├── Resources ├── Public │ ├── Images │ │ ├── hour.png │ │ ├── gradient.png │ │ ├── moduleicon.png │ │ ├── bg_notifications.png │ │ └── be_module_icon_v7.png │ ├── StyleSheet │ │ ├── bars.css │ │ └── timeline.css │ ├── Icons │ │ └── Extension.svg │ └── JavaScript │ │ ├── common.js │ │ ├── tooltip.dynamic.js │ │ └── tooltip.js └── Private │ ├── Language │ ├── locallang_tca.xlf │ └── locallang_mod.xlf │ ├── Layouts │ └── Module.html │ ├── Partials │ └── Timeline │ │ └── Detail.html │ └── Templates │ └── Timeline │ └── Timeline.html ├── Documentation ├── Images │ └── UserManual │ │ ├── BE_Module.png │ │ ├── TaskHover.png │ │ ├── BE_Module_Icon.png │ │ └── BE_Module_Icon_v7.png ├── _make │ ├── _not_versioned │ │ └── _.gitignore │ ├── make-html.bat │ ├── make.bat │ ├── Makefile │ └── conf.py ├── Pages │ ├── Screenshots.rst │ ├── Introduction.rst │ ├── Links.rst │ ├── UserManual.rst │ └── Aoe.rst ├── Includes.txt ├── Index.rst └── LICENSE ├── .gitignore ├── Tests ├── Unit │ ├── Domain │ │ ├── Model │ │ │ ├── Fixtures │ │ │ │ ├── EmptyTask.php │ │ │ │ └── TaskWithLogFilePath.php │ │ │ ├── TaskTest.php │ │ │ └── LogTest.php │ │ └── Repository │ │ │ └── LogRepositoryTest.php │ └── Converter │ │ └── FormValueTimestampConverterTest.php ├── Functional │ └── Domain │ │ └── Repository │ │ ├── Fixtures │ │ ├── tx_scheduler_task.xml │ │ └── tx_schedulertimeline_domain_model_log.xml │ │ └── LogRepositoryTest.php └── Linting │ └── Resources │ └── Xliff.xsd ├── ext_conf_template.txt ├── ext_tables.php ├── README.md ├── ext_tables.sql ├── ext_localconf.php ├── .scrutinizer.yml ├── ext_emconf.php ├── Configuration └── TypoScript │ └── Backend │ └── setup.txt ├── .github └── workflows │ ├── deploy.yml │ └── ci.yml ├── Classes ├── Interfaces │ └── ReturnMessage.php ├── Converter │ └── FormValueTimestampConverter.php ├── Domain │ ├── Repository │ │ ├── TaskRepository.php │ │ └── LogRepository.php │ └── Model │ │ ├── Log.php │ │ └── Task.php ├── ViewHelpers │ ├── DurationViewHelper.php │ ├── IncreaseViewHelper.php │ ├── StatusViewHelper.php │ ├── Format │ │ └── SpeakingDateViewHelper.php │ └── GanttViewHelper.php ├── Controller │ └── TimelineController.php └── XClass │ └── Scheduler.php ├── composer.json └── disabled.travis.yml /Resources/Public/Images/hour.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AOEpeople/scheduler_timeline/HEAD/Resources/Public/Images/hour.png -------------------------------------------------------------------------------- /Resources/Public/Images/gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AOEpeople/scheduler_timeline/HEAD/Resources/Public/Images/gradient.png -------------------------------------------------------------------------------- /Resources/Public/Images/moduleicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AOEpeople/scheduler_timeline/HEAD/Resources/Public/Images/moduleicon.png -------------------------------------------------------------------------------- /Resources/Public/Images/bg_notifications.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AOEpeople/scheduler_timeline/HEAD/Resources/Public/Images/bg_notifications.png -------------------------------------------------------------------------------- /Documentation/Images/UserManual/BE_Module.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AOEpeople/scheduler_timeline/HEAD/Documentation/Images/UserManual/BE_Module.png -------------------------------------------------------------------------------- /Documentation/Images/UserManual/TaskHover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AOEpeople/scheduler_timeline/HEAD/Documentation/Images/UserManual/TaskHover.png -------------------------------------------------------------------------------- /Resources/Public/Images/be_module_icon_v7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AOEpeople/scheduler_timeline/HEAD/Resources/Public/Images/be_module_icon_v7.png -------------------------------------------------------------------------------- /Documentation/Images/UserManual/BE_Module_Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AOEpeople/scheduler_timeline/HEAD/Documentation/Images/UserManual/BE_Module_Icon.png -------------------------------------------------------------------------------- /Documentation/Images/UserManual/BE_Module_Icon_v7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AOEpeople/scheduler_timeline/HEAD/Documentation/Images/UserManual/BE_Module_Icon_v7.png -------------------------------------------------------------------------------- /Documentation/_make/_not_versioned/_.gitignore: -------------------------------------------------------------------------------- 1 | # this is file .gitignore 2 | 3 | # ignore everything in this directory 4 | * 5 | 6 | # but do not ignore this this file 7 | !.gitignore -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IDE 2 | .idea 3 | 4 | # Build folder 5 | .Build 6 | 7 | # PHP Dependencies + Dev 8 | /composer.lock 9 | /index.php 10 | /typo3 11 | /typo3_src 12 | /typo3conf 13 | /vendor 14 | -------------------------------------------------------------------------------- /Tests/Unit/Domain/Model/Fixtures/EmptyTask.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | Scheduler Timeline Log 8 | 9 | 10 | Scheduler Task 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Documentation/Pages/Links.rst: -------------------------------------------------------------------------------- 1 | .. ================================================== 2 | .. FOR YOUR INFORMATION 3 | .. -------------------------------------------------- 4 | .. -*- coding: utf-8 -*- with BOM. 5 | 6 | .. include:: ../Includes.txt 7 | 8 | 9 | .. _links: 10 | 11 | 12 | Links 13 | ----- 14 | 15 | :TER: 16 | https://typo3.org/extensions/repository/view/scheduler_timeline 17 | 18 | :Bug Tracker: 19 | https://github.com/AOEpeople/scheduler_timeline/issues 20 | 21 | :Git Repository: 22 | https://github.com/AOEpeople/scheduler_timeline.git 23 | 24 | :Contact: 25 | dev@aoe.com 26 | -------------------------------------------------------------------------------- /Documentation/Includes.txt: -------------------------------------------------------------------------------- 1 | .. ================================================== 2 | .. FOR YOUR INFORMATION 3 | .. -------------------------------------------------- 4 | .. -*- coding: utf-8 -*- with BOM. 5 | 6 | .. This is 'Includes.txt'. It is included at the very top of each and 7 | every ReST source file in this documentation project (= manual). 8 | 9 | 10 | .. ================================================== 11 | .. DEFINE SOME TEXT ROLES 12 | .. -------------------------------------------------- 13 | 14 | .. role:: typoscript(code) 15 | 16 | .. role:: ts(typoscript) 17 | :class: typoscript 18 | 19 | .. role:: php(code) 20 | 21 | .. highlight:: php 22 | -------------------------------------------------------------------------------- /Resources/Private/Language/locallang_mod.xlf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | TYPO3 Scheduler Timeline 8 | 9 | 10 | Shows a timeline of processed tasks. 11 | 12 | 13 | Scheduler Timeline 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /ext_conf_template.txt: -------------------------------------------------------------------------------- 1 | # cat=basic//; type=boolean; label=Enable sample tasks: When turned on, you can use the sample, test tasks provided by the scheduler. Before turning this off, make sure you don't have any of those sample tasks currently scheduled. You will also need to clear the configuration cache. 2 | showSampleTasks = 0 3 | 4 | # cat=basic//; type=int+; label=Clean log entries: Clean log entries, that are older that the configured time (in minutes). 5 | cleanLogEntriesOlderThan = 10080 6 | 7 | # cat=basic//; type=int+; label=Check processes: Check processes that are running longer as the configured time (in minutes) if there are alive. If empty (or zero) no checks are done. 8 | checkProcessesAfter = 15 9 | -------------------------------------------------------------------------------- /ext_tables.php: -------------------------------------------------------------------------------- 1 | 'timeline', 13 | ], 14 | [ 15 | 'access' => 'user,group', 16 | 'icon' => 'EXT:' . $_EXTKEY . '/Resources/Public/Images/be_module_icon_v7.png', 17 | 'labels' => 'LLL:EXT:' . $_EXTKEY . '/Resources/Private/Language/locallang_mod.xlf', 18 | ] 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Scheduler Timeline 2 | ================== 3 | 4 | Description 5 | ----------- 6 | A TYPO3 backend module providing a graphical timeline of running and finished scheduler tasks 7 | 8 | Screenshots 9 | ----------- 10 | ![Timeline View](/Documentation/Images/UserManual/BE_Module.png?raw=true "Timeline View") 11 | 12 | Build Status 13 | ------------ 14 | [![Build Status](https://travis-ci.org/AOEpeople/scheduler_timeline.svg)](https://travis-ci.org/AOEpeople/scheduler_timeline) 15 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/AOEpeople/scheduler_timeline/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/AOEpeople/scheduler_timeline/?branch=master) 16 | [![Code Coverage](https://scrutinizer-ci.com/g/AOEpeople/scheduler_timeline/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/AOEpeople/scheduler_timeline/?branch=master) 17 | -------------------------------------------------------------------------------- /ext_tables.sql: -------------------------------------------------------------------------------- 1 | # 2 | # Table structure for table 'tx_schedulertimeline_domain_model_log' 3 | # 4 | CREATE TABLE tx_schedulertimeline_domain_model_log ( 5 | uid INT(11) NOT NULL auto_increment, 6 | pid INT(11) DEFAULT '0' NOT NULL, 7 | tstamp INT(11) DEFAULT '0' NOT NULL, 8 | crdate INT(11) DEFAULT '0' NOT NULL, 9 | cruser_id INT(11) DEFAULT '0' NOT NULL, 10 | 11 | task INT(11) DEFAULT '0' NOT NULL, 12 | starttime INT(11) DEFAULT '0' NOT NULL, 13 | endtime INT(11) DEFAULT '0' NOT NULL, 14 | exception TEXT, 15 | returnmessage TEXT, 16 | processid INT(11) DEFAULT '0' NOT NULL, 17 | 18 | PRIMARY KEY (uid), 19 | KEY parent (pid) 20 | ); 21 | 22 | # 23 | # Table structure for table 'tx_scheduler_task' 24 | # 25 | CREATE TABLE tx_scheduler_task ( 26 | pid INT(11) DEFAULT '0' NOT NULL 27 | ); 28 | -------------------------------------------------------------------------------- /ext_localconf.php: -------------------------------------------------------------------------------- 1 | \AOE\SchedulerTimeline\XClass\Scheduler::class 7 | ]; 8 | 9 | // Get the extensions's configuration 10 | $extConf = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['scheduler_timeline']); 11 | 12 | if ((TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_AJAX) || ('BE' === TYPO3_MODE)) { 13 | /** @noinspection PhpUndefinedVariableInspection */ 14 | $extPath = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath($_EXTKEY); 15 | 16 | \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTypoScriptSetup( 17 | file_get_contents($extPath . 'Configuration/TypoScript/Backend/setup.txt') 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | filter: 2 | excluded_paths: 3 | - 'Configuration/*' 4 | - 'Resources/*' 5 | paths: 6 | - 'Classes/*' 7 | 8 | tools: 9 | 10 | external_code_coverage: 11 | timeout: 1800 12 | runs: 1 13 | 14 | php_cpd: 15 | enabled: true 16 | 17 | php_code_sniffer: 18 | enabled: true 19 | config: 20 | standard: TYPO3CMS 21 | 22 | php_cs_fixer: 23 | enabled: true 24 | 25 | php_hhvm: 26 | enabled: true 27 | config: 28 | use_undeclared_constant: false 29 | 30 | php_mess_detector: 31 | enabled: true 32 | config: 33 | controversial_rules: 34 | superglobals: false 35 | 36 | php_pdepend: 37 | enabled: true 38 | 39 | php_analyzer: 40 | enabled: true 41 | 42 | sensiolabs_security_checker: true 43 | -------------------------------------------------------------------------------- /Documentation/Pages/UserManual.rst: -------------------------------------------------------------------------------- 1 | .. ================================================== 2 | .. FOR YOUR INFORMATION 3 | .. -------------------------------------------------- 4 | .. -*- coding: utf-8 -*- with BOM. 5 | 6 | .. include:: ../Includes.txt 7 | 8 | 9 | .. _users-manual: 10 | 11 | Users manual 12 | ============ 13 | 14 | All you need to do to enable this extension is to install it. There is no configuration needed, when installed you will have a new backend module in the backend under "Admin Tools". 15 | 16 | .. image:: /Images/UserManual/BE_Module_Icon.png 17 | .. image:: /Images/UserManual/BE_Module_Icon_v7.png 18 | 19 | When you click "Scheduler Timeline" you will see a screen similar to: 20 | 21 | .. image:: /Images/UserManual/BE_Module.png 22 | 23 | You can see more information about the task when hovering the entry on the timeline. 24 | 25 | .. image:: /Images/UserManual/TaskHover.png 26 | -------------------------------------------------------------------------------- /ext_emconf.php: -------------------------------------------------------------------------------- 1 | 'Scheduler Timeline', 4 | 'description' => 'Logs information about scheduler task execution and displays them in a graphical timeline', 5 | 'version' => '8.1.1', 6 | 'category' => 'module', 7 | 'author' => 'Fabrizio Branca, Erik Frister, Thomas Layh, Tomas Norre Mikkelsen, Stefan Rotsch, Nikola Stojiljković', 8 | 'author_company' => 'AOE GmbH', 9 | 'author_email' => 'dev@aoe.com', 10 | 'state' => 'stable', 11 | 'uploadfolder' => 0, 12 | 'createDirs' => '', 13 | 'clearCacheOnLoad' => 0, 14 | 'constraints' => [ 15 | 'depends' => [ 16 | 'php' => '7.0.0-7.2.99', 17 | 'typo3' => '8.7.0-8.7.99', 18 | 'extbase' => '', 19 | 'fluid' => '', 20 | 'scheduler' => '', 21 | ], 22 | 'conflicts' => [], 23 | 'suggests' => [], 24 | ], 25 | ]; 26 | -------------------------------------------------------------------------------- /Configuration/TypoScript/Backend/setup.txt: -------------------------------------------------------------------------------- 1 | config.tx_extbase.persistence.classes { 2 | AOE\SchedulerTimeline\Domain\Model\Log { 3 | mapping { 4 | tableName = tx_schedulertimeline_domain_model_log 5 | recordType = AOE\SchedulerTimeline\Domain\Model\Log 6 | columns { 7 | task.mapOnProperty = task 8 | starttime.mapOnProperty = starttime 9 | endtime.mapOnProperty = endtime 10 | exception.mapOnProperty = exception 11 | returnmessage.mapOnProperty = returnmessage 12 | processid.mapOnProperty = processid 13 | } 14 | } 15 | } 16 | 17 | AOE\SchedulerTimeline\Domain\Model\Task { 18 | mapping { 19 | tableName = tx_scheduler_task 20 | columns { 21 | uid.mapOnProperty = uid 22 | serialized_task_object.mapOnProperty = serializedTaskObject 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Resources/Private/Layouts/Module.html: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 |
7 | 8 |
9 |
10 |
11 | 12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | 21 |
22 |
23 | 24 | 25 | 26 | 27 |
28 |
29 |
30 | -------------------------------------------------------------------------------- /Tests/Functional/Domain/Repository/Fixtures/tx_scheduler_task.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 131 5 | 345 6 | Not important for the test 7 | 1507140146 8 | 9 | 10 | 132 11 | 345 12 | Not important for the test 13 | 1507140147 14 | 15 | 16 | 133 17 | 345 18 | Not important for the test 19 | 1507140148 20 | 21 | 22 | 134 23 | 345 24 | Not important for the test 25 | 1507140149 26 | 27 | 28 | 135 29 | 345 30 | Not important for the test 31 | 1507140150 32 | 33 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: DEPLOY 2 | 3 | on: 4 | push: 5 | tags: 6 | - "**" 7 | 8 | jobs: 9 | TER: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | 14 | - name: "Determine tag" 15 | id: "determine-tag" 16 | run: "echo \"::set-output name=tag::${GITHUB_REF#refs/tags/}\"" 17 | 18 | - name: Deploy to TER 19 | run: | 20 | if [ -n "${{ secrets.TYPO3_ORG_USERNAME }}" ] && [ -n "${{ secrets.TYPO3_ORG_PASSWORD }}" ]; then 21 | echo -e "Preparing upload of release ${{ steps.determine-tag.outputs.tag }} to TER\n"; 22 | 23 | # Install ter client 24 | composer global require helhum/ter-client 25 | 26 | # Build extension files 27 | composer extension-release 28 | 29 | # Upload 30 | TAG_MESSAGE=`git log -1 --pretty=%B` 31 | echo "Tag-Message: ${TAG_MESSAGE}" 32 | echo "Uploading release ${{ steps.determine-tag.outputs.tag }} to TER" 33 | $HOME/.composer/vendor/helhum/ter-client/ter-client upload scheduler_timeline . -u "${{ secrets.TYPO3_ORG_USERNAME }}" -p "${{ secrets.TYPO3_ORG_PASSWORD }}" -m "$TAG_MESSAGE" 34 | fi; -------------------------------------------------------------------------------- /Classes/Interfaces/ReturnMessage.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * All rights reserved 11 | * 12 | * This script is part of the TYPO3 project. The TYPO3 project is 13 | * free software; you can redistribute it and/or modify 14 | * it under the terms of the GNU General Public License as published by 15 | * the Free Software Foundation; either version 3 of the License, or 16 | * (at your option) any later version. 17 | * 18 | * The GNU General Public License can be found at 19 | * http://www.gnu.org/copyleft/gpl.html. 20 | * 21 | * This script is distributed in the hope that it will be useful, 22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 | * GNU General Public License for more details. 25 | * 26 | * This copyright notice MUST APPEAR in all copies of the script! 27 | ***************************************************************/ 28 | 29 | /** 30 | * Interface ReturnMessage 31 | * 32 | * @package AOE\SchedulerTimeline\Interfaces 33 | */ 34 | interface ReturnMessage 35 | { 36 | 37 | /** 38 | * @var string return message 39 | */ 40 | public function getReturnMessage(); 41 | } 42 | -------------------------------------------------------------------------------- /Documentation/Pages/Aoe.rst: -------------------------------------------------------------------------------- 1 | .. ================================================== 2 | .. FOR YOUR INFORMATION 3 | .. -------------------------------------------------- 4 | .. -*- coding: utf-8 -*- with BOM. 5 | 6 | .. include:: ../Includes.txt 7 | 8 | 9 | .. _aoe: 10 | 11 | 12 | The AOE Way 13 | ==================== 14 | 15 | | AOE ( `www.aoe.com `_ ) is a leading provider of Enterprise Open Source web solutions. 16 | | Using current agile development methods, more than 180 developers and consultants in 8 global locations 17 | | develop customized Open Source solutions for global Fortune 500 companies and corporations. 18 | | 19 | | For more than 15 years we have been designing and developing complex Enterprise Web Content Management 20 | | Systems, E-Commerce and Mobile Applications as well as Web Portal solutions based on Open Source 21 | | technology and successfully launched over 1,400 projects worldwide. 22 | | 23 | | The integration of Open Source software is a central element of our 24 | | corporate philosophy and key driver of our growth. 25 | | 26 | | **The sourcecode of this Extension is continuously monitored by a** `Jenkins `_ **job.** 27 | | **Checking for** `checkstyles `_, `mess `_, `duplicate code `_ **and** `code coverage `_ 28 | 29 | -------------------------------------------------------------------------------- /Resources/Private/Partials/Timeline/Detail.html: -------------------------------------------------------------------------------- 1 | {namespace s=AOE\SchedulerTimeline\ViewHelpers} 2 | 3 |
4 | 5 |
6 |

{log.task.title}

7 |
8 | 9 |
10 | 11 |

[{log.task.taskObject.additionalInformation}]

12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 49 | 50 | 51 |
Status:
Timespan:{log.timespan}
Duration [h:m:s]:
Process id:{log.processid}

Exception: {log.exception.message}

33 |
34 |
{log.exception.stacktrace}
35 |
36 |

Return message:

45 |
46 |
{log.returnmessage}
47 |
48 |
52 |
53 | 54 |
-------------------------------------------------------------------------------- /Classes/Converter/FormValueTimestampConverter.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This file is part of the TYPO3 Scheduler Timeline Extension. 9 | * 10 | * It is free software; you can redistribute it and/or modify it under 11 | * the terms of the GNU General Public License, either version 2 12 | * of the License, or any later version. 13 | * 14 | * For the full copyright and license information, please read the 15 | * LICENSE.txt file that was distributed with this source code. 16 | * 17 | * The TYPO3 project - inspiring people to share! 18 | */ 19 | 20 | class FormValueTimestampConverter 21 | { 22 | /** 23 | * @param string $value 24 | * @return string 25 | */ 26 | public static function convertValueToStrToTimeValue(string $value) 27 | { 28 | $timeArray = preg_split('#(?<=\d)(?=[a-z])#i', $value); 29 | $timeArray[0] = $timeArray[0] ?? '1'; 30 | $timeArray[1] = $timeArray[1] ?? 'hour'; 31 | return $timeArray[0] . ' ' . self::getFullLabel($timeArray[1]); 32 | } 33 | 34 | /** 35 | * @param string $label 36 | * @return string 37 | */ 38 | private function getFullLabel(string $label) 39 | { 40 | switch ($label) { 41 | case 'd': 42 | return 'day'; 43 | case 'w': 44 | return 'week'; 45 | case 'y': 46 | return 'year'; 47 | case 'h': 48 | default: 49 | return 'hour'; 50 | 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /Resources/Public/StyleSheet/bars.css: -------------------------------------------------------------------------------- 1 | .bar-red, 2 | .bar-red span, 3 | .bar-orange, 4 | .bar-orange span, 5 | .bar-yellow, 6 | .bar-yellow span, 7 | .bar-green, 8 | .bar-green span, 9 | .bar-lightblue, 10 | .bar-lightblue span, 11 | .bar-blue, 12 | .bar-blue span, 13 | .bar-lightgray, 14 | .bar-lightgray span, 15 | .bar-gray, 16 | .bar-gray span { display:block; height:16px; background-image:url(../Images/bg_notifications.png); background-repeat:no-repeat; font:bold 10px/16px Arial, Helvetica, sans-serif; text-transform:uppercase; text-align:center; padding:0 0 0 7px; margin:1px 0; white-space:nowrap; color:#fff; } 17 | 18 | .bar-red { background-position:0 0; } 19 | .bar-red span { background-position:100% 0; padding:0 7px 0 0; } 20 | 21 | .bar-orange { background-position:0 -16px; } 22 | .bar-orange span { background-position:100% -16px; padding:0 7px 0 0; } 23 | 24 | .bar-yellow { background-position:0 -32px; } 25 | .bar-yellow span { background-position:100% -32px; padding:0 7px 0 0; } 26 | 27 | .bar-green { background-position:0 -48px; } 28 | .bar-green span { background-position:100% -48px; padding:0 7px 0 0; } 29 | 30 | .bar-lightblue { background-position:0 -64px; } 31 | .bar-lightblue span { background-position:100% -64px; padding:0 7px 0 0; } 32 | 33 | .bar-blue { background-position:0 -80px; } 34 | .bar-blue span { background-position:100% -80px; padding:0 7px 0 0; } 35 | 36 | .bar-lightgray { background-position:0 -96px; } 37 | .bar-lightgray span { background-position:100% -96px; padding:0 7px 0 0; } 38 | 39 | .bar-gray { background-position:0 -112px; } 40 | .bar-gray span { background-position:100% -112px; padding:0 7px 0 0; } -------------------------------------------------------------------------------- /Classes/Domain/Repository/TaskRepository.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * All rights reserved 11 | * 12 | * This script is part of the TYPO3 project. The TYPO3 project is 13 | * free software; you can redistribute it and/or modify 14 | * it under the terms of the GNU General Public License as published by 15 | * the Free Software Foundation; either version 3 of the License, or 16 | * (at your option) any later version. 17 | * 18 | * The GNU General Public License can be found at 19 | * http://www.gnu.org/copyleft/gpl.html. 20 | * 21 | * This script is distributed in the hope that it will be useful, 22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 | * GNU General Public License for more details. 25 | * 26 | * This copyright notice MUST APPEAR in all copies of the script! 27 | ***************************************************************/ 28 | 29 | /** 30 | * Class TaskRepository 31 | * 32 | * @package AOE\SchedulerTimeline\Domain\Repository 33 | */ 34 | class TaskRepository extends \TYPO3\CMS\Extbase\Persistence\Repository 35 | { 36 | 37 | /** 38 | * Initialize object 39 | * Ignore storage pid 40 | * 41 | * @return void 42 | */ 43 | public function initializeObject() 44 | { 45 | $querySettings = $this->objectManager->get('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Typo3QuerySettings'); 46 | $querySettings->setRespectStoragePage(false); 47 | $this->setDefaultQuerySettings($querySettings); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Tests/Linting/Resources/Xliff.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /Resources/Public/Icons/Extension.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 21 | 22 | -------------------------------------------------------------------------------- /Tests/Functional/Domain/Repository/Fixtures/tx_schedulertimeline_domain_model_log.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1 5 | 345 6 | 7 | 1445191476 8 | 0 9 | 131 10 | 11 | 12 | 13 | 14 | 2 15 | 345 16 | 17 | 1445191514 18 | 19 | 1445192788 20 | 132 21 | 22 | 23 | 24 | 25 | 3 26 | 345 27 | 28 | 1445106499 29 | 30 | 1445110099 31 | 133 32 | 33 | 34 | 35 | 36 | 4 37 | 345 38 | 39 | 1445282899 40 | 41 | 1445286499 42 | 134 43 | 44 | 45 | 46 | 47 | 5 48 | 345 49 | 50 | 1445113699 51 | 0 52 | 134 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /Documentation/Index.rst: -------------------------------------------------------------------------------- 1 | .. ================================================== 2 | .. FOR YOUR INFORMATION 3 | .. -------------------------------------------------- 4 | .. -*- coding: utf-8 -*- with BOM. 5 | 6 | .. include:: Includes.txt 7 | 8 | 9 | .. _start: 10 | 11 | ============================================================= 12 | Scheduler Timeline 13 | ============================================================= 14 | 15 | .. only:: html 16 | 17 | :Classification: 18 | scheduler_timeline 19 | 20 | :Version: 21 | |release| 22 | 23 | :Language: 24 | en 25 | 26 | :Description: 27 | Logs information about scheduler task execution and displays them in a graphical timeline 28 | 29 | :Keywords: 30 | Scheduler,Timeline 31 | 32 | :Copyright: 33 | 2016 34 | 35 | :Author: 36 | AOE GmbH 37 | 38 | :Email: 39 | dev@aoe.com 40 | 41 | :License: 42 | Copyright 2016 AOE GmbH 43 | 44 | All rights reserved 45 | 46 | This Extension is part of the TYPO3 project. The TYPO3 project is 47 | free software; you can redistribute it and/or modify 48 | it under the terms of the GNU General Public License as published by 49 | the Free Software Foundation; either version 3 of the License, or 50 | (at your option) any later version. 51 | 52 | The GNU General Public License can be found at 53 | http://www.gnu.org/copyleft/gpl.html. 54 | 55 | This Extension is distributed in the hope that it will be useful, 56 | but WITHOUT ANY WARRANTY; without even the implied warranty of 57 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 58 | GNU General Public License for more details. 59 | 60 | :Rendered: 61 | |today| 62 | 63 | The content of this document is related to TYPO3, 64 | a GNU/GPL CMS/Framework available from `www.typo3.org `_. 65 | 66 | 67 | **Table of Contents** 68 | 69 | .. toctree:: 70 | :maxdepth: 5 71 | :titlesonly: 72 | :glob: 73 | 74 | Pages/Introduction 75 | Pages/Screenshots 76 | Pages/UserManual 77 | Pages/Links 78 | Pages/Aoe 79 | -------------------------------------------------------------------------------- /Resources/Public/JavaScript/common.js: -------------------------------------------------------------------------------- 1 | $.noConflict(); 2 | jQuery(function () { 3 | jQuery('.timeline-box').scrollLeft(jQuery('.timeline-panel').width()); 4 | 5 | jQuery('.task, .task-title').tooltip({ 6 | offsetParent: 'body', 7 | predelay: 100, 8 | position: 'bottom center', 9 | onShow: function () { 10 | this.getTrigger().addClass('active'); 11 | }, 12 | onHide: function () { 13 | this.getTrigger().removeClass('active'); 14 | } 15 | }).dynamic(); 16 | 17 | // collision detection 18 | function getPositions(box) { 19 | var $box = jQuery(box); 20 | var pos = $box.position(); 21 | var width = $box.width(); 22 | var height = $box.height(); 23 | return [[pos.left, pos.left + width], [pos.top, pos.top + height]]; 24 | } 25 | 26 | function comparePositions(p1, p2) { 27 | var x1 = p1[0] < p2[0] ? p1 : p2; 28 | var x2 = p1[0] < p2[0] ? p2 : p1; 29 | return x1[1] > x2[0] || x1[0] === x2[0] ? true : false; 30 | } 31 | 32 | function collision(a, b) { 33 | var posA = getPositions(a); 34 | var posB = getPositions(b); 35 | 36 | return (posA[1][0] == posB[1][0]) && comparePositions(posA[0], posB[0]); 37 | } 38 | 39 | jQuery('.timeline').each(function () { 40 | var $tasks = jQuery('.task', jQuery(this)); 41 | var numberOfTasks = $tasks.length; 42 | for (var i = 0; i < numberOfTasks; i++) { 43 | var u = Math.min(i + 10, numberOfTasks); 44 | for (var j = i + 1; j < u; j++) { 45 | if (collision($tasks[i], $tasks[j])) { 46 | var $subject = jQuery($tasks[i]); 47 | var $object = jQuery($tasks[j]); 48 | 49 | var objectTop = parseInt($subject.css('top')); 50 | 51 | $object.css('top', (objectTop + 4) + 'px'); 52 | 53 | $subject.css('height', 18); 54 | $object.css('height', 18); 55 | } 56 | } 57 | } 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /Tests/Unit/Converter/FormValueTimestampConverterTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This file is part of the TYPO3 Scheduler Timeline Extension. 9 | * 10 | * It is free software; you can redistribute it and/or modify it under 11 | * the terms of the GNU General Public License, either version 2 12 | * of the License, or any later version. 13 | * 14 | * For the full copyright and license information, please read the 15 | * LICENSE.txt file that was distributed with this source code. 16 | * 17 | * The TYPO3 project - inspiring people to share! 18 | */ 19 | 20 | use AOE\SchedulerTimeline\Converter\FormValueTimestampConverter; 21 | use Nimut\TestingFramework\TestCase\UnitTestCase; 22 | 23 | 24 | class formValueTimestampConverterTest extends UnitTestCase 25 | { 26 | /** 27 | * @test 28 | * @dataProvider convertValueToStrToTimeValueDataProvider 29 | * @return void 30 | */ 31 | public function convertValueToStrToTimeValueTest(string $label, string $expected) 32 | { 33 | self::assertSame( 34 | $expected, 35 | FormValueTimestampConverter::convertValueToStrToTimeValue($label) 36 | ); 37 | 38 | } 39 | 40 | /** 41 | * @return array 42 | */ 43 | public function convertValueToStrToTimeValueDataProvider() 44 | { 45 | return [ 46 | '1 Hours' => [ 47 | 'label' => '1h', 48 | 'expected' => '1 hour' 49 | ], 50 | '3 Hours' => [ 51 | 'label' => '3h', 52 | 'expected' => '3 hour' 53 | ], 54 | '1 Day' => [ 55 | 'label' => '1d', 56 | 'expected' => '1 day' 57 | ], 58 | '3 Days' => [ 59 | 'label' => '3d', 60 | 'expected' => '3 day' 61 | ], 62 | '1 Week' => [ 63 | 'label' => '1w', 64 | 'expected' => '1 week' 65 | ], 66 | '2 Weeks' => [ 67 | 'label' => '2w', 68 | 'expected' => '2 week' 69 | ], 70 | '1 Year' => [ 71 | 'label' => '1y', 72 | 'expected' => '1 year' 73 | ], 74 | ]; 75 | } 76 | } -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aoe/scheduler_timeline", 3 | "description": "Logs information about scheduler task execution and displays them in a graphical timeline", 4 | "type": "typo3-cms-extension", 5 | "keywords": ["TYPO3 CMS", "Scheduler", "Timeline"], 6 | "license": ["GPL-2.0+"], 7 | "homepage": "https://github.com/AOEpeople/scheduler_timeline", 8 | "support": { 9 | "issues": "https://github.com/AOEpeople/scheduler_timeline/issues" 10 | }, 11 | "require": { 12 | "php": ">=7.0", 13 | "typo3/cms-core": "^8.7", 14 | "typo3/cms-extbase": "^8.7", 15 | "typo3/cms-scheduler": "^8.7", 16 | "typo3fluid/fluid": "^2.6" 17 | }, 18 | "require-dev": { 19 | "nimut/testing-framework": "^4.1" 20 | }, 21 | "autoload": { 22 | "psr-4": { 23 | "AOE\\SchedulerTimeline\\": "Classes" 24 | } 25 | }, 26 | "autoload-dev": { 27 | "psr-4": { 28 | "AOE\\SchedulerTimeline\\Tests\\": "Tests" 29 | } 30 | }, 31 | "config": { 32 | "vendor-dir": ".Build/vendor", 33 | "bin-dir": ".Build/bin" 34 | }, 35 | "scripts": { 36 | "post-autoload-dump": [ 37 | "mkdir -p .Build/Web/typo3conf/ext/", 38 | "[ -L .Build/Web/typo3conf/ext/scheduler_timeline ] || ln -snvf ../../../../. .Build/Web/typo3conf/ext/scheduler_timeline" 39 | ], 40 | "test:unit": [ 41 | "[ -e .Build/bin/phpunit ] || composer update", 42 | "TYPO3_PATH_ROOT=$(pwd)/.Build/Web .Build/bin/phpunit -c .Build/vendor/nimut/testing-framework/res/Configuration/UnitTests.xml Tests/Unit/" 43 | ], 44 | "test:functional": [ 45 | "[ -e .Build/bin/phpunit ] || composer update", 46 | "TYPO3_PATH_ROOT=$(pwd)/.Build/Web .Build/bin/phpunit -c .Build/vendor/nimut/testing-framework/res/Configuration/FunctionalTests.xml Tests/Functional", 47 | "[ -L .Build/Web/typo3conf/ext/scheduler_timeline ] || ln -snvf ../../../../. .Build/Web/typo3conf/ext/scheduler_timeline" 48 | ], 49 | "extension-release": [ 50 | "rm -rf Tests/", 51 | "rm .gitignore", 52 | "rm .scrutinizer.yml", 53 | "rm disabled.travis.yml" 54 | ] 55 | }, 56 | "extra": { 57 | "branch-alias": { 58 | "dev-master": "8.1.0-dev" 59 | }, 60 | "typo3/cms": { 61 | "cms-package-dir": "{$vendor-dir}/typo3/cms", 62 | "web-dir": ".Build/Web" 63 | }, 64 | "helhum/typo3-console": { 65 | "install-extension-dummy": false 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [ push, pull_request ] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | typo3: [ ^8.7 ] 14 | php: [ '7.2', '7.4' ] 15 | 16 | steps: 17 | - name: Start database server 18 | run: sudo /etc/init.d/mysql start 19 | 20 | - name: Checkout 21 | uses: actions/checkout@v2 22 | 23 | - name: Set up PHP Version ${{ matrix.php }} 24 | uses: shivammathur/setup-php@v2 25 | with: 26 | php-version: ${{ matrix.php }} 27 | tools: composer:v2 28 | 29 | - name: Environment Check 30 | run: | 31 | php --version 32 | composer --version 33 | 34 | - name: Validate composer.json and composer.lock 35 | run: composer validate 36 | 37 | - name: Install dependencies with nimut/typo3-complete:${{ matrix.typo3 }} 38 | run: | 39 | composer require --dev nimut/typo3-complete:${{ matrix.typo3 }} --no-progress 40 | git checkout composer.json 41 | ln -nfs .Build/vendor/typo3/cms/typo3 typo3 42 | 43 | - name: Lint PHP 44 | run: find . -name \*.php ! -path "./.Build/*" ! -path "./scripts/*" ! -path "./typo3_src/*" | parallel --gnu php -d display_errors=stderr -l {} > /dev/null \; 45 | 46 | - name: Unit Tests with coverage 47 | if: matrix.typo3 == '^8.7' 48 | run: | 49 | export "UNIT_XML"=.Build/vendor/nimut/testing-framework/res/Configuration/UnitTests.xml 50 | .Build/bin/phpunit --coverage-clover=unittest-coverage.clover --colors -c $UNIT_XML Tests/Unit/ 51 | 52 | - name: Functional Tests with coverage 53 | run: | 54 | export "FUNCTIONAL_XML"=.Build/vendor/nimut/testing-framework/res/Configuration/FunctionalTests.xml 55 | find 'Tests/Functional' -wholename '*Test.php' | parallel --gnu 'echo; echo "Running functional test suite {}"; .Build/bin/phpunit --coverage-clover={}functionaltest-coverage.clover --colors -c $FUNCTIONAL_XML {}' 56 | if: matrix.typo3 == '^8.7' 57 | env: 58 | typo3DatabaseHost: 127.0.0.1 59 | typo3DatabaseName: typo3 60 | typo3DatabasePassword: root 61 | typo3DatabaseUsername: root 62 | 63 | - name: Upload coverage results to Scrutinizer 64 | if: matrix.typo3 == '^8.7' 65 | run: | 66 | wget https://scrutinizer-ci.com/ocular.phar 67 | php ocular.phar code-coverage:upload --format=php-clover unittest-coverage.clover 68 | find 'Tests/Functional' -wholename '*Test.php' -exec php ocular.phar code-coverage:upload --format=php-clover {}functionaltest-coverage.clover \; -------------------------------------------------------------------------------- /Classes/ViewHelpers/DurationViewHelper.php: -------------------------------------------------------------------------------- 1 | 8 | * 9 | * All rights reserved 10 | * 11 | * This script is part of the TYPO3 project. The TYPO3 project is 12 | * free software; you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation; either version 3 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * The GNU General Public License can be found at 18 | * http://www.gnu.org/copyleft/gpl.html. 19 | * 20 | * This script is distributed in the hope that it will be useful, 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | * GNU General Public License for more details. 24 | * 25 | * This copyright notice MUST APPEAR in all copies of the script! 26 | ***************************************************************/ 27 | 28 | use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; 29 | use TYPO3\CMS\Fluid\Core\ViewHelper\Facets\CompilableInterface; 30 | use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; 31 | use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic; 32 | 33 | /** 34 | * Class DurationViewHelper 35 | * 36 | * @package AOE\SchedulerTimeline\ViewHelpers 37 | */ 38 | class DurationViewHelper extends AbstractViewHelper implements CompilableInterface 39 | { 40 | 41 | use CompileWithRenderStatic; 42 | 43 | /** 44 | * Initializes the arguments 45 | */ 46 | public function initializeArguments() 47 | { 48 | parent::initializeArguments(); 49 | $this->registerArgument('duration', 'string', 'Duration of the process', true); 50 | } 51 | 52 | /** 53 | * 54 | * @param array $arguments 55 | * @param \Closure $renderChildrenClosure 56 | * @param RenderingContextInterface $renderingContext 57 | * 58 | * @return string 59 | */ 60 | public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext) 61 | { 62 | $duration = $arguments['duration']; 63 | 64 | $hours = intval(intval($duration) / 3600); 65 | $hours = str_pad($hours, 2, '0', STR_PAD_LEFT); 66 | $minutes = intval(($duration / 60) % 60); 67 | $seconds = intval($duration % 60); 68 | 69 | $result = $seconds; 70 | if (intval($minutes) || intval($hours)) { 71 | $seconds = str_pad($seconds, 2, '0', STR_PAD_LEFT); 72 | $result = $minutes . ':' . $seconds; 73 | } 74 | if (intval($hours)) { 75 | $minutes = str_pad($minutes, 2, '0', STR_PAD_LEFT); 76 | $result = $hours . ':' . $minutes . ':' . $seconds; 77 | } 78 | return $result; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Resources/Public/StyleSheet/timeline.css: -------------------------------------------------------------------------------- 1 | #contentwrapper { 2 | float: left; 3 | width: 100%; 4 | } 5 | 6 | #contentcolumn { 7 | margin-left: 300px; /* Set left margin to LeftColumnWidth */ 8 | } 9 | 10 | #leftcolumn { 11 | float: left; 12 | width: 300px; /* Width of left column */ 13 | margin-left: -100%; 14 | } 15 | 16 | .configuration { 17 | padding: 5px; 18 | } 19 | 20 | #leftcolumn .row { 21 | background-color: #F6F6F6; 22 | border-right: 1px solid #DADFE0; 23 | } 24 | 25 | #leftcolumn .row.hours { 26 | background-color: transparent; 27 | text-align: right; 28 | padding-right: 5px; 29 | font-size: 10px; 30 | } 31 | 32 | #now { 33 | position: absolute; 34 | width: 1px; 35 | height: 100%; 36 | background-color: red; 37 | } 38 | 39 | div.row { 40 | border-bottom: 1px solid #DADFE0; 41 | padding: 0; 42 | margin: 0; 43 | height: 40px; 44 | overflow: hidden; 45 | } 46 | 47 | div.timeline-box { 48 | overflow: auto; 49 | } 50 | 51 | div.timeline-panel { 52 | background-image: url(../Images/hour.png); 53 | border-right: 1px solid black; 54 | position: relative; 55 | } 56 | 57 | div.timeline { 58 | position: relative; 59 | } 60 | 61 | div.hours { 62 | height: 20px; 63 | } 64 | 65 | .detailwrap { 66 | width: 100%; 67 | height: 100%; 68 | } 69 | 70 | .details { 71 | display: none; 72 | background-color: white; 73 | border: 1px solid black; 74 | z-index: 1000; 75 | max-width: 400px; 76 | } 77 | 78 | .details pre { 79 | background-color: white; 80 | overflow: auto; 81 | max-width: 385px; 82 | font-size: 8pt; 83 | line-height: 9pt; 84 | } 85 | 86 | .hour { 87 | width: 240px; 88 | float: left; 89 | text-align: center; 90 | } 91 | 92 | .details-headline { 93 | width: 100%; 94 | height: 28px; 95 | } 96 | 97 | .task-info-details .details-headline { 98 | width: 100%; 99 | height: auto; 100 | background-color: grey; 101 | } 102 | 103 | .details-headline h3 { 104 | padding: 5px; 105 | color: white; 106 | margin: 0; 107 | } 108 | 109 | .details-content { 110 | padding: 5px; 111 | } 112 | 113 | .details-content td.label { 114 | font-weight: bold; 115 | color: black; 116 | } 117 | 118 | .caption-container { 119 | width: 500px; 120 | overflow: hidden; 121 | } 122 | 123 | .task { 124 | position: absolute; 125 | height: 24px; 126 | top: 8px; 127 | cursor: default; 128 | } 129 | 130 | .task-title { 131 | line-height: 16px; 132 | cursor: default; 133 | } 134 | 135 | .status { 136 | width: 100px; 137 | } 138 | 139 | .details-headline, .task { background-image: url(../Images/gradient.png); } 140 | 141 | .details-headline.success, .task.success { background-color: #36B963; } 142 | .details-headline.pending, .task.pending { background-color: #A9ABA8; } 143 | .details-headline.error, .task.error { background-color: #E41300; } 144 | .details-headline.missed, .task.missed { background-color: #F75300; } 145 | .details-headline.running, .task.running { background-color: #FE9D00; } 146 | 147 | div.task.active, div.task:hover { background-color: #00A2FA; } -------------------------------------------------------------------------------- /Classes/ViewHelpers/IncreaseViewHelper.php: -------------------------------------------------------------------------------- 1 | 8 | * 9 | * All rights reserved 10 | * 11 | * This script is part of the TYPO3 project. The TYPO3 project is 12 | * free software; you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation; either version 3 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * The GNU General Public License can be found at 18 | * http://www.gnu.org/copyleft/gpl.html. 19 | * 20 | * This script is distributed in the hope that it will be useful, 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | * GNU General Public License for more details. 24 | * 25 | * This copyright notice MUST APPEAR in all copies of the script! 26 | ***************************************************************/ 27 | 28 | use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; 29 | use TYPO3\CMS\Fluid\Core\ViewHelper\Facets\CompilableInterface; 30 | use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; 31 | use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic; 32 | 33 | /** 34 | * Class IncreaseViewHelper 35 | * 36 | * @package AOE\SchedulerTimeline\ViewHelpers 37 | */ 38 | class IncreaseViewHelper extends AbstractViewHelper implements CompilableInterface 39 | { 40 | use CompileWithRenderStatic; 41 | 42 | /** 43 | * View helper returns HTML, thus we need to disable output escaping 44 | * 45 | * @var bool 46 | */ 47 | protected $escapeOutput = false; 48 | 49 | /** 50 | * Initializes the arguments 51 | */ 52 | public function initializeArguments() 53 | { 54 | parent::initializeArguments(); 55 | $this->registerArgument('start', 'string', 'Start time', true); 56 | $this->registerArgument('end', 'string', 'End time', true); 57 | $this->registerArgument('interval', 'string', 'Interval of hours', false, '1'); 58 | $this->registerArgument('iterator', 'string', 'Iterator of hours', false, 'i'); 59 | } 60 | 61 | /** 62 | * 63 | * @param array $arguments 64 | * @param \Closure $renderChildrenClosure 65 | * @param RenderingContextInterface $renderingContext 66 | * 67 | * @return string 68 | */ 69 | public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext) 70 | { 71 | $templateVariableContainer = $renderingContext->getVariableProvider(); 72 | 73 | $start = $arguments['start']; 74 | $end = $arguments['end']; 75 | $interval = $arguments['interval']; 76 | $iterator = $arguments['iterator']; 77 | 78 | $result = ''; 79 | for ($i = $start; $i < $end; $i += $interval) { 80 | $templateVariableContainer->add($iterator, $i); 81 | $result .= $renderChildrenClosure(); 82 | $templateVariableContainer->remove($iterator); 83 | } 84 | return $result; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Classes/ViewHelpers/StatusViewHelper.php: -------------------------------------------------------------------------------- 1 | 8 | * 9 | * All rights reserved 10 | * 11 | * This script is part of the TYPO3 project. The TYPO3 project is 12 | * free software; you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation; either version 3 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * The GNU General Public License can be found at 18 | * http://www.gnu.org/copyleft/gpl.html. 19 | * 20 | * This script is distributed in the hope that it will be useful, 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | * GNU General Public License for more details. 24 | * 25 | * This copyright notice MUST APPEAR in all copies of the script! 26 | ***************************************************************/ 27 | 28 | use AOE\SchedulerTimeline\Domain\Model\Log; 29 | use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; 30 | use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper; 31 | use TYPO3\CMS\Fluid\Core\ViewHelper\Facets\CompilableInterface; 32 | use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic; 33 | 34 | /** 35 | * Class StatusViewHelper 36 | * 37 | * @package AOE\SchedulerTimeline\ViewHelpers 38 | */ 39 | class StatusViewHelper extends AbstractViewHelper implements CompilableInterface 40 | { 41 | 42 | use CompileWithRenderStatic; 43 | 44 | /** 45 | * View helper returns HTML, thus we need to disable output escaping 46 | * 47 | * @var bool 48 | */ 49 | protected $escapeOutput = false; 50 | 51 | /** 52 | * Initializes the arguments 53 | */ 54 | public function initializeArguments() 55 | { 56 | parent::initializeArguments(); 57 | $this->registerArgument('status', 'string', 'the status of the process', true); 58 | } 59 | 60 | /** 61 | * Prints status html for process 62 | * 63 | * @param array $arguments 64 | * @param \Closure $renderChildrenClosure 65 | * @param RenderingContextInterface $renderingContext 66 | * 67 | * @return string 68 | */ 69 | public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext) 70 | { 71 | 72 | $status = $arguments['status']; 73 | 74 | switch ($status) { 75 | case Log::STATUS_SUCCESS: 76 | $result = '' . $status . ''; 77 | break; 78 | case Log::STATUS_PENDING: 79 | $result = '' . $status . ''; 80 | break; 81 | case Log::STATUS_RUNNING: 82 | $result = '' . $status . ''; 83 | break; 84 | case Log::STATUS_MISSED: 85 | $result = '' . $status . ''; 86 | break; 87 | case Log::STATUS_ERROR: 88 | $result = '' . $status . ''; 89 | break; 90 | default: 91 | $result = $status; 92 | } 93 | return $result; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Classes/ViewHelpers/Format/SpeakingDateViewHelper.php: -------------------------------------------------------------------------------- 1 | 8 | * 9 | * All rights reserved 10 | * 11 | * This script is part of the TYPO3 project. The TYPO3 project is 12 | * free software; you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation; either version 3 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * The GNU General Public License can be found at 18 | * http://www.gnu.org/copyleft/gpl.html. 19 | * 20 | * This script is distributed in the hope that it will be useful, 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | * GNU General Public License for more details. 24 | * 25 | * This copyright notice MUST APPEAR in all copies of the script! 26 | ***************************************************************/ 27 | 28 | use TYPO3\CMS\Fluid\Core\Rendering\RenderingContextInterface; 29 | use TYPO3\CMS\Fluid\Core\ViewHelper\Facets\CompilableInterface; 30 | use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; 31 | use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic; 32 | 33 | /** 34 | * Class SpeakingDateViewHelper 35 | * 36 | * @package AOE\SchedulerTimeline\ViewHelpers\Format 37 | */ 38 | class SpeakingDateViewHelper extends AbstractViewHelper implements CompilableInterface 39 | { 40 | use CompileWithRenderStatic; 41 | 42 | /** 43 | * Initializes the arguments 44 | */ 45 | public function initializeArguments() 46 | { 47 | parent::initializeArguments(); 48 | $this->registerArgument('timestamp', 'string', 'Timestamp', true); 49 | $this->registerArgument('defaultFormat', 'string', 'Default time format', false, 'Y.m.d H:i'); 50 | $this->registerArgument('todayFormat', 'string', 'Time format for today', false, 'H:i'); 51 | $this->registerArgument('tomorrowFormat', 'string', 'Time format for tomorrow', false, '\T\o\m\m\o\r\o\w, H:i'); 52 | $this->registerArgument('yesterdayFormat', 'string', 'Time format for yesterday', false, '\Y\e\s\t\e\r\d\a\y, H:i'); 53 | } 54 | 55 | /** 56 | * 57 | * @param array $arguments 58 | * @param \Closure $renderChildrenClosure 59 | * @param RenderingContextInterface $renderingContext 60 | * @return string 61 | */ 62 | public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext) 63 | { 64 | 65 | $timestamp = $arguments['timestamp']; 66 | $defaultFormat = $arguments['defaultFormat']; 67 | $todayFormat = $arguments['todayFormat']; 68 | $tomorrowFormat = $arguments['tomorrowFormat']; 69 | $yesterdayFormat = $arguments['yesterdayFormat']; 70 | 71 | $day = date('Ymd', $timestamp); 72 | if ($todayFormat && (date('Ymd') == $day)) { 73 | $result = date($todayFormat, $timestamp); 74 | } elseif ($tomorrowFormat && (date('Ymd', strtotime('+1 day')) == $day)) { 75 | $result = date($tomorrowFormat, $timestamp); 76 | } elseif ($yesterdayFormat && (date('Ymd', strtotime('-1 day')) == $day)) { 77 | $result = date($yesterdayFormat, $timestamp); 78 | } else { 79 | $result = date($defaultFormat, $timestamp); 80 | } 81 | return $result; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Classes/ViewHelpers/GanttViewHelper.php: -------------------------------------------------------------------------------- 1 | 8 | * 9 | * All rights reserved 10 | * 11 | * This script is part of the TYPO3 project. The TYPO3 project is 12 | * free software; you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation; either version 3 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * The GNU General Public License can be found at 18 | * http://www.gnu.org/copyleft/gpl.html. 19 | * 20 | * This script is distributed in the hope that it will be useful, 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | * GNU General Public License for more details. 24 | * 25 | * This copyright notice MUST APPEAR in all copies of the script! 26 | ***************************************************************/ 27 | 28 | use AOE\SchedulerTimeline\Domain\Model\Log; 29 | use TYPO3\CMS\Core\Utility\GeneralUtility; 30 | use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; 31 | use TYPO3\CMS\Fluid\Core\ViewHelper\Facets\CompilableInterface; 32 | use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper; 33 | use TYPO3Fluid\Fluid\Core\ViewHelper\TagBuilder; 34 | use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic; 35 | 36 | /** 37 | * Class GanttViewHelper 38 | * 39 | * @package AOE\SchedulerTimeline\ViewHelpers 40 | */ 41 | class GanttViewHelper extends AbstractTagBasedViewHelper implements CompilableInterface 42 | { 43 | 44 | use CompileWithRenderStatic; 45 | 46 | /** 47 | * Initializes the arguments 48 | */ 49 | public function initializeArguments() 50 | { 51 | parent::initializeArguments(); 52 | $this->registerArgument('log', '\AOE\SchedulerTimeline\Domain\Model\Log', 'Log Record', true); 53 | $this->registerArgument('starttime', 'string', 'Start time', true); 54 | $this->registerArgument('zoom', 'string', 'Zoom level', true); 55 | } 56 | 57 | /** 58 | * 59 | * @param array $arguments 60 | * @param \Closure $renderChildrenClosure 61 | * @param RenderingContextInterface $renderingContext 62 | * 63 | * @return string 64 | */ 65 | public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext) 66 | { 67 | /** @var TagBuilder $tag */ 68 | $tag = GeneralUtility::makeInstance(TagBuilder::class); 69 | 70 | /** @var Log $log */ 71 | $log = $arguments['log']; 72 | $zoom = $arguments['zoom']; 73 | $startTime = $arguments['starttime']; 74 | 75 | $duration = $log->getDuration() / $zoom; 76 | $duration = ceil($duration / 4) * 4 - 1; // round to numbers dividable by 4, then remove 1 px border 77 | $duration = max($duration, 3); 78 | 79 | $offset = ($log->getStarttime() - $startTime) / $zoom; 80 | if ($offset < 0) { // cut bar 81 | $duration += $offset; 82 | $offset = 0; 83 | } 84 | $tag->setTagName('div'); 85 | $tag->addAttribute('style', sprintf('width: %spx; left: %spx;', $duration, $offset)); 86 | $tag->addAttribute('class', 'task ' . $log->getStatus()); 87 | $tag->addAttribute('id', 'uid_' . $log->getUid()); 88 | $tag->setContent($renderChildrenClosure()); 89 | return $tag->render(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Tests/Unit/Domain/Repository/LogRepositoryTest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * All rights reserved 11 | * 12 | * This script is part of the TYPO3 project. The TYPO3 project is 13 | * free software; you can redistribute it and/or modify 14 | * it under the terms of the GNU General Public License as published by 15 | * the Free Software Foundation; either version 3 of the License, or 16 | * (at your option) any later version. 17 | * 18 | * The GNU General Public License can be found at 19 | * http://www.gnu.org/copyleft/gpl.html. 20 | * 21 | * This script is distributed in the hope that it will be useful, 22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 | * GNU General Public License for more details. 25 | * 26 | * This copyright notice MUST APPEAR in all copies of the script! 27 | ***************************************************************/ 28 | 29 | use AOE\SchedulerTimeline\Domain\Model\Log; 30 | use AOE\SchedulerTimeline\Domain\Model\Task; 31 | use AOE\SchedulerTimeline\Domain\Repository\LogRepository; 32 | use TYPO3\CMS\Extbase\Object\ObjectManager; 33 | 34 | /** 35 | * Class LogRepositoryTest 36 | * @package AOE\SchedulerTimeline\Tests\Unit\Domain\Repository 37 | */ 38 | class LogRepositoryTest extends \Nimut\TestingFramework\TestCase\UnitTestCase 39 | { 40 | 41 | public function setUp() 42 | { 43 | // Make sure that timezone is always the same in all testing environments 44 | date_default_timezone_set('UTC'); 45 | } 46 | 47 | /** 48 | * @test 49 | */ 50 | public function findGroupedByTaskIgnoresLogEntriesForDeletedTasks() 51 | { 52 | $logCollection = self::getLogCollection(); 53 | $logRepository = $this->getMockBuilder(LogRepository::class) 54 | ->setConstructorArgs([new ObjectManager()]) 55 | ->setMethods(['findByTime']) 56 | ->getMock(); 57 | $logRepository->expects(self::any())->method('findByTime')->willReturn($logCollection); 58 | 59 | $logs = $logRepository->findGroupedByTask(0, time()); 60 | 61 | self::assertCount(3, $logs); 62 | self::assertSame($logCollection[0]->getTask(), $logs[1]['task']); 63 | self::assertSame($logCollection[1]->getTask(), $logs[2]['task']); 64 | // The third log collection item is not returned as it has no Task object attached 65 | self::assertSame($logCollection[3]->getTask(), $logs[4]['task']); 66 | } 67 | 68 | /** 69 | * Gets a collection of Log objects, one without an attached Task object 70 | * 71 | * @return array 72 | */ 73 | private static function getLogCollection() 74 | { 75 | return array( 76 | self::getLogItem(self::getTaskItem(1)), 77 | self::getLogItem(self::getTaskItem(2)), 78 | self::getLogItem(), 79 | self::getLogItem(self::getTaskItem(4)) 80 | ); 81 | } 82 | 83 | /** 84 | * Returns a Log object 85 | * 86 | * @param Task $task 87 | * @return Log 88 | */ 89 | private static function getLogItem(Task $task = null) 90 | { 91 | $log = new Log(); 92 | $log->_setProperty('task', $task); 93 | 94 | return $log; 95 | } 96 | 97 | /** 98 | * Returns a Task object 99 | * 100 | * @param integer $uid 101 | * @return Task 102 | */ 103 | private static function getTaskItem($uid = 0) 104 | { 105 | $task = new Task(); 106 | $task->_setProperty('uid', $uid); 107 | 108 | return $task; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Resources/Private/Templates/Timeline/Timeline.html: -------------------------------------------------------------------------------- 1 | {namespace s=AOE\SchedulerTimeline\ViewHelpers} 2 | 3 | 4 | 5 | 6 | 7 |

Scheduler Timeline

8 | 9 | 10 | 11 | 12 | 13 | 14 | {label} 15 | 16 | 17 | {label} 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | No logs found! 28 | 29 | 30 |
31 | 32 |
33 |
34 |
35 |
36 | 37 |
38 | 39 |
40 |
41 | 42 |
43 |
44 |
45 |
46 | 47 | 48 |
49 |
50 | 51 |   52 | 53 | 54 |
55 |
56 |
57 | 58 |
59 |
60 |
61 |
62 | 63 |
64 | 65 |
66 |
-
67 |
68 | 69 | 70 |
71 |
72 |
73 |
74 |
75 | {group.task.title}
({group.task.extension}) 76 |
77 |
78 |
79 |

{group.task.title} ({group.task.extension})

80 |
81 | 82 |
83 | 84 | 85 |

86 | {group.task.taskObject.additionalInformation} 87 |

88 |
89 | 90 |

91 | No additional information provided. 92 |

93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 | 103 |
104 | 105 |
106 | 107 |
108 |
109 | 110 |
111 | -------------------------------------------------------------------------------- /Tests/Unit/Domain/Model/TaskTest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * All rights reserved 11 | * 12 | * This script is part of the TYPO3 project. The TYPO3 project is 13 | * free software; you can redistribute it and/or modify 14 | * it under the terms of the GNU General Public License as published by 15 | * the Free Software Foundation; either version 3 of the License, or 16 | * (at your option) any later version. 17 | * 18 | * The GNU General Public License can be found at 19 | * http://www.gnu.org/copyleft/gpl.html. 20 | * 21 | * This script is distributed in the hope that it will be useful, 22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 | * GNU General Public License for more details. 25 | * 26 | * This copyright notice MUST APPEAR in all copies of the script! 27 | ***************************************************************/ 28 | 29 | use AOE\SchedulerTimeline\Domain\Model\Task; 30 | use Nimut\TestingFramework\TestCase\UnitTestCase; 31 | 32 | /** 33 | * Class TaskTest 34 | * 35 | * @package AOE\SchedulerTimeline\Tests\Unit\Domain\Model 36 | */ 37 | class TaskTest extends UnitTestCase 38 | { 39 | 40 | /** 41 | * @var Task 42 | */ 43 | protected $subject; 44 | 45 | /** 46 | * Sets up the test case 47 | */ 48 | public function setUp() 49 | { 50 | $this->subject = $this->getAccessibleMock('AOE\\SchedulerTimeline\\Domain\\Model\\Task', array('dummy')); 51 | } 52 | 53 | /** 54 | * @test 55 | * @return void 56 | */ 57 | public function getClassReturnsClassNameOfSerializedTaskObject() 58 | { 59 | $taskFixture = new \AOE\SchedulerTimeline\Tests\Unit\Domain\Model\Fixtures\EmptyTask(); 60 | $serializedTaskFixture = serialize($taskFixture); 61 | 62 | $this->subject->_set('serializedTaskObject', $serializedTaskFixture); 63 | $this->assertEquals('AOE\\SchedulerTimeline\\Tests\\Unit\\Domain\\Model\\Fixtures\\EmptyTask', $this->subject->getClassname()); 64 | } 65 | 66 | /** 67 | * @test 68 | * @return void 69 | */ 70 | public function getTaskObjectReturnsClassOfSerializedTaksObject() 71 | { 72 | $taskFixture = new \AOE\SchedulerTimeline\Tests\Unit\Domain\Model\Fixtures\EmptyTask(); 73 | $serializedTaskFixture = serialize($taskFixture); 74 | 75 | $this->subject->_set('serializedTaskObject', $serializedTaskFixture); 76 | $this->assertEquals($taskFixture, $this->subject->getTaskObject()); 77 | } 78 | 79 | /** 80 | * @test 81 | * @return void 82 | */ 83 | public function getLogFilePathReturnsFalseIfSerializedTaskObjectDoesntHaveMethodGetLogFilePath() 84 | { 85 | $taskFixture = new \AOE\SchedulerTimeline\Tests\Unit\Domain\Model\Fixtures\EmptyTask(); 86 | $serializedTaskFixture = serialize($taskFixture); 87 | 88 | $this->subject->_set('serializedTaskObject', $serializedTaskFixture); 89 | $this->assertFalse($this->subject->getLogFilePath()); 90 | } 91 | 92 | /** 93 | * @test 94 | * @return void 95 | */ 96 | public function getLogFilePathReturnsPathFromSerializedTaskObject() 97 | { 98 | $taskFixture = new \AOE\SchedulerTimeline\Tests\Unit\Domain\Model\Fixtures\TaskWithLogFilePath(); 99 | $serializedTaskFixture = serialize($taskFixture); 100 | 101 | $this->subject->_set('serializedTaskObject', $serializedTaskFixture); 102 | $this->assertEquals('some/file/path', $this->subject->getLogFilePath()); 103 | } 104 | 105 | /** 106 | * @test 107 | * @expectedException \Exception 108 | * @expectedExceptionCode 1450187123 109 | * @return void 110 | */ 111 | public function getTaskObjectThrowsExceptionIfSerializedTaskObjectIsNotSet() 112 | { 113 | $this->subject->getTaskObject(); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Classes/Domain/Repository/LogRepository.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * All rights reserved 11 | * 12 | * This script is part of the TYPO3 project. The TYPO3 project is 13 | * free software; you can redistribute it and/or modify 14 | * it under the terms of the GNU General Public License as published by 15 | * the Free Software Foundation; either version 3 of the License, or 16 | * (at your option) any later version. 17 | * 18 | * The GNU General Public License can be found at 19 | * http://www.gnu.org/copyleft/gpl.html. 20 | * 21 | * This script is distributed in the hope that it will be useful, 22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 | * GNU General Public License for more details. 25 | * 26 | * This copyright notice MUST APPEAR in all copies of the script! 27 | ***************************************************************/ 28 | 29 | /** 30 | * Class LogRepository 31 | * 32 | * @package AOE\SchedulerTimeline\Domain\Repository 33 | */ 34 | class LogRepository extends \TYPO3\CMS\Extbase\Persistence\Repository 35 | { 36 | 37 | /** 38 | * @var int min date timestamp 39 | */ 40 | protected $minDate; 41 | 42 | /** 43 | * @var int max date timestamp 44 | */ 45 | protected $maxDate; 46 | 47 | /** 48 | * Initialize object 49 | * Ignore storage pid 50 | * 51 | * @return void 52 | */ 53 | public function initializeObject() 54 | { 55 | $querySettings = $this->objectManager->get('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Typo3QuerySettings'); 56 | $querySettings->setRespectStoragePage(false); 57 | $this->setDefaultQuerySettings($querySettings); 58 | } 59 | 60 | /** 61 | * Find logs by time 62 | * 63 | * @param int $starttime 64 | * @param int $endtime 65 | * @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface 66 | */ 67 | public function findByTime($starttime, $endtime) 68 | { 69 | $query = $this->createQuery(); 70 | $query->matching( 71 | $query->logicalAnd( 72 | $query->logicalOr( 73 | $query->greaterThanOrEqual('endtime', $starttime), 74 | $query->equals('endtime', 0) 75 | ), 76 | $query->lessThanOrEqual('starttime', $endtime) 77 | ) 78 | ); 79 | $query->setOrderings(array('starttime' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING)); 80 | return $query->execute(); 81 | } 82 | 83 | /** 84 | * Find logs grouped by task 85 | * 86 | * @return array array( => array('task' => , 'logs' => array(, ...) ), ...) 87 | */ 88 | public function findGroupedByTask(int $startTime, int $endTime) 89 | { 90 | $logs = $this->findByTime($startTime, $endTime); 91 | $result = array(); 92 | foreach ($logs as $log) { /* @var $log \AOE\SchedulerTimeline\Domain\Model\Log */ 93 | $task = $log->getTask(); 94 | if (NULL === $task) { 95 | continue; 96 | } 97 | 98 | // min/max 99 | $startTime = $log->getStarttime(); 100 | $this->minDate = is_null($this->minDate) ? $startTime : min($this->minDate, $startTime); 101 | $this->maxDate = is_null($this->maxDate) ? $startTime : max($this->maxDate, $startTime); 102 | 103 | $result[$task->getUid()]['task'] = $task; 104 | $result[$task->getUid()]['logs'][] = $log; 105 | } 106 | return $result; 107 | } 108 | 109 | /** 110 | * Get min date (from findGroupedByTask) 111 | * 112 | * @return int 113 | */ 114 | public function getMinDate() 115 | { 116 | return $this->minDate; 117 | } 118 | 119 | /** 120 | * Get max date (from findGroupedByTask) 121 | * 122 | * @return int 123 | */ 124 | public function getMaxDate() 125 | { 126 | return $this->maxDate; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Resources/Public/JavaScript/tooltip.dynamic.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * jQuery Tools @VERSION / Tooltip Dynamic Positioning 4 | * 5 | * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE. 6 | * 7 | * http://flowplayer.org/tools/tooltip/dynamic.html 8 | * 9 | * Since: July 2009 10 | * Date: @DATE 11 | */ 12 | (function($) { 13 | 14 | // version number 15 | var t = $.tools.tooltip; 16 | 17 | t.dynamic = { 18 | conf: { 19 | classNames: "top right bottom left" 20 | } 21 | }; 22 | 23 | /* 24 | * See if element is on the viewport. Returns an boolean array specifying which 25 | * edges are hidden. Edges are in following order: 26 | * 27 | * [top, right, bottom, left] 28 | * 29 | * For example following return value means that top and right edges are hidden 30 | * 31 | * [true, true, false, false] 32 | * 33 | */ 34 | function getCropping(el) { 35 | 36 | var w = $(window); 37 | var right = w.width() + w.scrollLeft(); 38 | var bottom = w.height() + w.scrollTop(); 39 | 40 | return [ 41 | el.offset().top <= w.scrollTop(), // top 42 | right <= el.offset().left + el.width(), // right 43 | bottom <= el.offset().top + el.height(), // bottom 44 | w.scrollLeft() >= el.offset().left // left 45 | ]; 46 | } 47 | 48 | /* 49 | Returns true if all edges of an element are on viewport. false if not 50 | 51 | @param crop the cropping array returned by getCropping function 52 | */ 53 | function isVisible(crop) { 54 | var i = crop.length; 55 | while (i--) { 56 | if (crop[i]) { return false; } 57 | } 58 | return true; 59 | } 60 | 61 | // dynamic plugin 62 | $.fn.dynamic = function(conf) { 63 | 64 | if (typeof conf == 'number') { conf = {speed: conf}; } 65 | 66 | conf = $.extend({}, t.dynamic.conf, conf); 67 | 68 | var cls = conf.classNames.split(/\s/), orig; 69 | 70 | this.each(function() { 71 | 72 | var api = $(this).tooltip().onBeforeShow(function(e, pos) { 73 | 74 | // get nessessary variables 75 | var tip = this.getTip(), tipConf = this.getConf(); 76 | 77 | /* 78 | We store the original configuration and use it to restore back to the original state. 79 | */ 80 | if (!orig) { 81 | orig = [ 82 | tipConf.position[0], 83 | tipConf.position[1], 84 | tipConf.offset[0], 85 | tipConf.offset[1], 86 | $.extend({}, tipConf) 87 | ]; 88 | } 89 | 90 | /* 91 | display tip in it's default position and by setting visibility to hidden. 92 | this way we can check whether it will be on the viewport 93 | */ 94 | $.extend(tipConf, orig[4]); 95 | tipConf.position = [orig[0], orig[1]]; 96 | tipConf.offset = [orig[2], orig[3]]; 97 | 98 | tip.css({ 99 | visibility: 'hidden', 100 | position: 'absolute', 101 | top: pos.top, 102 | left: pos.left 103 | }).show(); 104 | 105 | // now let's see for hidden edges 106 | var crop = getCropping(tip); 107 | 108 | // possibly alter the configuration 109 | if (!isVisible(crop)) { 110 | 111 | // change the position and add class 112 | if (crop[2]) { $.extend(tipConf, conf.top); tipConf.position[0] = 'top'; tip.addClass(cls[0]); } 113 | if (crop[3]) { $.extend(tipConf, conf.right); tipConf.position[1] = 'right'; tip.addClass(cls[1]); } 114 | if (crop[0]) { $.extend(tipConf, conf.bottom); tipConf.position[0] = 'bottom'; tip.addClass(cls[2]); } 115 | if (crop[1]) { $.extend(tipConf, conf.left); tipConf.position[1] = 'left'; tip.addClass(cls[3]); } 116 | 117 | // vertical offset 118 | if (crop[0] || crop[2]) { tipConf.offset[0] *= -1; } 119 | 120 | // horizontal offset 121 | if (crop[1] || crop[3]) { tipConf.offset[1] *= -1; } 122 | } 123 | 124 | tip.css({visibility: 'visible'}).hide(); 125 | 126 | }); 127 | 128 | // restore positioning as soon as possible 129 | api.onBeforeShow(function() { 130 | var c = this.getConf(), tip = this.getTip(); 131 | setTimeout(function() { 132 | c.position = [orig[0], orig[1]]; 133 | c.offset = [orig[2], orig[3]]; 134 | }, 0); 135 | }); 136 | 137 | // remove custom class names and restore original effect 138 | api.onHide(function() { 139 | var tip = this.getTip(); 140 | tip.removeClass(conf.classNames); 141 | }); 142 | 143 | ret = api; 144 | 145 | }); 146 | 147 | return conf.api ? ret : this; 148 | }; 149 | 150 | }) (jQuery); 151 | -------------------------------------------------------------------------------- /disabled.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | notifications: 4 | email: 5 | on_success: never 6 | on_failure: never 7 | 8 | sudo: false 9 | 10 | addons: 11 | apt: 12 | packages: 13 | - parallel 14 | - libxml2-utils 15 | - nodejs 16 | - npm 17 | 18 | services: 19 | - mysql 20 | 21 | cache: 22 | directories: 23 | - $HOME/.composer/cache 24 | 25 | before_install: 26 | - composer self-update 27 | - composer --version 28 | 29 | before_script: 30 | # Enables us to test dev-master 31 | - composer config minimum-stability dev 32 | - composer config prefer-stable true 33 | # Installs nimut/typo3-complete 34 | - travis_wait 30 composer require --dev nimut/typo3-complete=$TYPO3_VERSION 35 | - export "TYPO3_PATH_WEB"=$PWD/.Build/Web; 36 | # Locating UnitTests.xml 37 | - export "UNIT_XML"='.Build/vendor/nimut/testing-framework/res/Configuration/UnitTests.xml' 38 | # Location FunctionalTests.xml 39 | - export "FUNCTIONAL_XML"='.Build/vendor/nimut/testing-framework/res/Configuration/FunctionalTests.xml' 40 | # Symlink TYPO3-Core 41 | - ln -nfs .Build/vendor/typo3/cms/typo3 typo3 42 | 43 | script: 44 | - > 45 | echo; 46 | echo "Running xmllint (Xliff)"; 47 | find Resources/Private/Language/ -name '*.xlf' -type f | xargs xmllint --noout --schema Tests/Linting/Resources/Xliff.xsd 48 | - > 49 | echo; 50 | echo "Running PHP lint"; 51 | find . -name \*.php ! -path "./.Build/*" ! -path "./scripts/*" ! -path "./typo3_src/*" | parallel --gnu php -d display_errors=stderr -l {} > /dev/null \; 52 | # - > 53 | # echo; 54 | # echo "Running JavaScript lint"; 55 | # npm install -g jslint; 56 | # find Resources/Public/JavaScript/ -name '*.js' ! -name 'jquery*' ! -name 'tooltip*' -type f | xargs jslint 57 | - > 58 | if [[ "$COVERAGE" == "0" ]]; then 59 | echo; 60 | echo "Running unit tests"; 61 | .Build/bin/phpunit --colors -c $UNIT_XML Tests/Unit/ 62 | fi 63 | - > 64 | if [[ "$COVERAGE" == "1" ]]; then 65 | echo; 66 | echo "Running unit tests"; 67 | .Build/bin/phpunit --coverage-clover=unittest-coverage.clover --colors -c $UNIT_XML Tests/Unit/ 68 | fi 69 | - > 70 | if [[ "$FUNCTIONAL" == "1" ]]; then 71 | echo; 72 | export typo3DatabaseName="typo3"; 73 | export typo3DatabaseHost="localhost"; 74 | export typo3DatabaseUsername="root"; 75 | export typo3DatabasePassword=""; 76 | find 'Tests/Functional' -wholename '*Test.php' | parallel --gnu 'echo; echo "Running functional test suite {}"; .Build/bin/phpunit --coverage-clover={}functionaltest-coverage.clover --colors -c $FUNCTIONAL_XML {}' 77 | fi 78 | - > 79 | if [[ "$COVERAGE" == "1" ]]; then 80 | echo; 81 | echo "Uploading code coverage results"; 82 | wget https://scrutinizer-ci.com/ocular.phar 83 | php ocular.phar code-coverage:upload --format=php-clover unittest-coverage.clover 84 | find 'Tests/Functional' -wholename '*Test.php' -exec php ocular.phar code-coverage:upload --format=php-clover {}functionaltest-coverage.clover \; 85 | fi 86 | 87 | jobs: 88 | allow_failures: 89 | - env: TYPO3_VERSION=^9.5 COVERAGE=0 FUNCTIONAL=1 90 | - env: TYPO3_VERSION=^10.4 COVERAGE=0 FUNCTIONAL=1 91 | - env: TYPO3_VERSION=dev-master COVERAGE=0 FUNCTIONAL=1 92 | include: 93 | - stage: test 94 | env: TYPO3_VERSION=^8.7 COVERAGE=0 FUNCTIONAL=0 95 | php: 7.0 96 | - stage: test 97 | env: TYPO3_VERSION=^8.7 COVERAGE=0 FUNCTIONAL=1 98 | php: 7.0 99 | - stage: test 100 | env: TYPO3_VERSION=^8.7 COVERAGE=0 FUNCTIONAL=0 101 | php: 7.2 102 | - stage: test 103 | env: TYPO3_VERSION=^8.7 COVERAGE=1 FUNCTIONAL=1 104 | php: 7.2 105 | - stage: test 106 | env: TYPO3_VERSION=^9.5 COVERAGE=0 FUNCTIONAL=1 107 | php: 7.2 108 | - stage: test 109 | env: TYPO3_VERSION=^10.4 COVERAGE=0 FUNCTIONAL=1 110 | php: 7.2 111 | - stage: test 112 | env: TYPO3_VERSION=dev-master COVERAGE=0 FUNCTIONAL=1 113 | php: 7.2 114 | 115 | - stage: ship to ter 116 | if: tag IS present 117 | php: 7.2 118 | install: skip 119 | before_script: skip 120 | script: 121 | - | 122 | if [ -n "$TYPO3_ORG_USERNAME" ] && [ -n "$TYPO3_ORG_PASSWORD" ]; then 123 | echo -e "Preparing upload of release ${TRAVIS_TAG} to TER\n"; 124 | # Install ter client 125 | composer global require helhum/ter-client 126 | # Build extension files 127 | composer extension-release 128 | # Upload 129 | TAG_MESSAGE=`git log -1 --pretty=%B` 130 | echo "Tag-Message: ${TAG_MESSAGE}" 131 | echo "Uploading release ${TRAVIS_TAG} to TER" 132 | $HOME/.composer/vendor/bin/ter-client upload scheduler_timeline . -u "$TYPO3_ORG_USERNAME" -p "$TYPO3_ORG_PASSWORD" -m "$TAG_MESSAGE" 133 | fi; -------------------------------------------------------------------------------- /Tests/Functional/Domain/Repository/LogRepositoryTest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * All rights reserved 11 | * 12 | * This script is part of the TYPO3 project. The TYPO3 project is 13 | * free software; you can redistribute it and/or modify 14 | * it under the terms of the GNU General Public License as published by 15 | * the Free Software Foundation; either version 3 of the License, or 16 | * (at your option) any later version. 17 | * 18 | * The GNU General Public License can be found at 19 | * http://www.gnu.org/copyleft/gpl.html. 20 | * 21 | * This script is distributed in the hope that it will be useful, 22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 | * GNU General Public License for more details. 25 | * 26 | * This copyright notice MUST APPEAR in all copies of the script! 27 | ***************************************************************/ 28 | 29 | use Nimut\TestingFramework\TestCase\FunctionalTestCase; 30 | 31 | /** 32 | * Class LogRepositoryTest 33 | * 34 | * @package AOE\SchedulerTimeline\Tests\Functional\Domain\Repository 35 | */ 36 | class LogRepositoryTest extends FunctionalTestCase 37 | { 38 | 39 | /** 40 | * @var \AOE\SchedulerTimeline\Domain\Repository\LogRepository 41 | */ 42 | protected $logRepository; 43 | 44 | /** 45 | * @var \TYPO3\CMS\Extbase\Object\ObjectManager 46 | */ 47 | protected $objectManager; 48 | 49 | /** 50 | * @var array 51 | */ 52 | protected $coreExtensionsToLoad = array('scheduler'); 53 | 54 | /** 55 | * @var array 56 | */ 57 | protected $testExtensionsToLoad = array('typo3conf/ext/scheduler_timeline'); 58 | 59 | /** 60 | * SetUp 61 | */ 62 | public function setUp() 63 | { 64 | parent::setUp(); 65 | $this->importDataSet(__DIR__ . '/Fixtures/tx_schedulertimeline_domain_model_log.xml'); 66 | $this->importDataSet(__DIR__ . '/Fixtures/tx_scheduler_task.xml'); 67 | $this->objectManager = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Object\\ObjectManager'); 68 | $this->logRepository = $this->objectManager->get('AOE\\SchedulerTimeline\\Domain\\Repository\\LogRepository'); 69 | } 70 | 71 | /** 72 | * Tears down the fixture 73 | * 74 | * @return void 75 | */ 76 | public function tearDown() 77 | { 78 | parent::tearDown(); 79 | } 80 | 81 | /** 82 | * @test 83 | */ 84 | public function findByTimeReturnsQueryResultInterface() 85 | { 86 | // Order by starttime 87 | $expectedArray = array(5,1,2); 88 | 89 | $startTime = 1445191476; // 18.10.15 18:04 90 | $endTime = 1445191876; // 18.10.15 18:11 91 | $logs = $this->logRepository->findByTime($startTime, $endTime); 92 | 93 | /** @var \AOE\SchedulerTimeline\Domain\Model\Log $log */ 94 | foreach ($logs as $log) { 95 | $actualArray[] = $log->getUid(); 96 | } 97 | 98 | $this->assertSame( 99 | $expectedArray, 100 | $actualArray 101 | ); 102 | } 103 | 104 | /** 105 | * @test 106 | */ 107 | public function findGroupedByTaskReturnsMultidimensionArray() 108 | { 109 | $actualArray = array(); 110 | $expectedArray = array( 111 | 131 => array( 112 | 'task' => 131, 113 | 'logs' => array(1) 114 | ), 115 | 132 => array( 116 | 'task' => 132, 117 | 'logs' => array(2) 118 | ), 119 | 133 => array( 120 | 'task' => 133, 121 | 'logs' => array(3) 122 | ), 123 | 134 => array( 124 | 'task' => 134, 125 | 'logs' => array(4,5) 126 | ), 127 | ); 128 | 129 | $startTime = 1445191476; // 18.10.15 18:04 130 | $endTime = 1445191876; // 18.10.15 18:11 131 | $logsGroupedByTask = $this->logRepository->findGroupedByTask($startTime, $endTime); 132 | 133 | foreach ($logsGroupedByTask as $taskUid => $taskObject) { 134 | $actualArray[$taskUid]['task'] = $taskUid; 135 | 136 | /** @var \AOE\SchedulerTimeline\Domain\Model\Task $task */ 137 | foreach ($taskObject as $task) { 138 | /** @var \AOE\SchedulerTimeline\Domain\Model\Log $log */ 139 | foreach ($task as $log) { 140 | $actualArray[$taskUid]['logs'][] = $log->getUid(); 141 | } 142 | } 143 | } 144 | 145 | $this->assertSame( 146 | sort($expectedArray), 147 | sort($actualArray) 148 | ); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /Documentation/_make/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXOPTS%" == "" ( 6 | set SPHINXOPTS=-c . -a -E -w ./_not_versioned/warnings.txt 7 | ) 8 | if "%SPHINXBUILD%" == "" ( 9 | set SPHINXBUILD=/var/www/congstar-congo/devbox/htdocs/typo3temp/tx_sphinx/sphinx-doc/bin/sphinx-build 10 | ) 11 | set PAPER=a4 12 | set BUILDDIR=build 13 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .. 14 | set I18NSPHINXOPTS=%SPHINXOPTS% .. 15 | if NOT "%PAPER%" == "" ( 16 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 17 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 18 | ) 19 | 20 | if "%1" == "" goto help 21 | 22 | if "%1" == "help" ( 23 | :help 24 | echo.Please use `make ^` where ^ is one of 25 | echo. html to make standalone HTML files 26 | echo. dirhtml to make HTML files named index.html in directories 27 | echo. singlehtml to make a single large HTML file 28 | echo. pickle to make pickle files 29 | echo. json to make JSON files 30 | echo. htmlhelp to make HTML files and a HTML help project 31 | echo. qthelp to make HTML files and a qthelp project 32 | echo. devhelp to make HTML files and a Devhelp project 33 | echo. epub to make an epub 34 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 35 | echo. text to make text files 36 | echo. man to make manual pages 37 | echo. texinfo to make Texinfo files 38 | echo. gettext to make PO message catalogs 39 | echo. changes to make an overview over all changed/added/deprecated items 40 | echo. linkcheck to check all external links for integrity 41 | echo. doctest to run all doctests embedded in the documentation if enabled 42 | goto end 43 | ) 44 | 45 | if "%1" == "clean" ( 46 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 47 | del /q /s %BUILDDIR%\* 48 | goto end 49 | ) 50 | 51 | if "%1" == "html" ( 52 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 53 | if errorlevel 1 exit /b 1 54 | echo. 55 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 56 | goto end 57 | ) 58 | 59 | if "%1" == "dirhtml" ( 60 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 61 | if errorlevel 1 exit /b 1 62 | echo. 63 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 64 | goto end 65 | ) 66 | 67 | if "%1" == "singlehtml" ( 68 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 69 | if errorlevel 1 exit /b 1 70 | echo. 71 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 72 | goto end 73 | ) 74 | 75 | if "%1" == "pickle" ( 76 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 77 | if errorlevel 1 exit /b 1 78 | echo. 79 | echo.Build finished; now you can process the pickle files. 80 | goto end 81 | ) 82 | 83 | if "%1" == "json" ( 84 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 85 | if errorlevel 1 exit /b 1 86 | echo. 87 | echo.Build finished; now you can process the JSON files. 88 | goto end 89 | ) 90 | 91 | if "%1" == "htmlhelp" ( 92 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 93 | if errorlevel 1 exit /b 1 94 | echo. 95 | echo.Build finished; now you can run HTML Help Workshop with the ^ 96 | .hhp project file in %BUILDDIR%/htmlhelp. 97 | goto end 98 | ) 99 | 100 | if "%1" == "qthelp" ( 101 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 102 | if errorlevel 1 exit /b 1 103 | echo. 104 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 105 | .qhcp project file in %BUILDDIR%/qthelp, like this: 106 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Projectname.qhcp 107 | echo.To view the help file: 108 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Projectname.ghc 109 | goto end 110 | ) 111 | 112 | if "%1" == "devhelp" ( 113 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 114 | if errorlevel 1 exit /b 1 115 | echo. 116 | echo.Build finished. 117 | goto end 118 | ) 119 | 120 | if "%1" == "epub" ( 121 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 122 | if errorlevel 1 exit /b 1 123 | echo. 124 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 125 | goto end 126 | ) 127 | 128 | if "%1" == "latex" ( 129 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 130 | if errorlevel 1 exit /b 1 131 | echo. 132 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 133 | goto end 134 | ) 135 | 136 | if "%1" == "text" ( 137 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 138 | if errorlevel 1 exit /b 1 139 | echo. 140 | echo.Build finished. The text files are in %BUILDDIR%/text. 141 | goto end 142 | ) 143 | 144 | if "%1" == "man" ( 145 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 146 | if errorlevel 1 exit /b 1 147 | echo. 148 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 149 | goto end 150 | ) 151 | 152 | if "%1" == "texinfo" ( 153 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 154 | if errorlevel 1 exit /b 1 155 | echo. 156 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 157 | goto end 158 | ) 159 | 160 | if "%1" == "gettext" ( 161 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 162 | if errorlevel 1 exit /b 1 163 | echo. 164 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 165 | goto end 166 | ) 167 | 168 | if "%1" == "changes" ( 169 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 170 | if errorlevel 1 exit /b 1 171 | echo. 172 | echo.The overview file is in %BUILDDIR%/changes. 173 | goto end 174 | ) 175 | 176 | if "%1" == "linkcheck" ( 177 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 178 | if errorlevel 1 exit /b 1 179 | echo. 180 | echo.Link check complete; look for any errors in the above output ^ 181 | or in %BUILDDIR%/linkcheck/output.txt. 182 | goto end 183 | ) 184 | 185 | if "%1" == "doctest" ( 186 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 187 | if errorlevel 1 exit /b 1 188 | echo. 189 | echo.Testing of doctests in the sources finished, look at the ^ 190 | results in %BUILDDIR%/doctest/output.txt. 191 | goto end 192 | ) 193 | 194 | :end 195 | -------------------------------------------------------------------------------- /Classes/Domain/Model/Log.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * All rights reserved 11 | * 12 | * This script is part of the TYPO3 project. The TYPO3 project is 13 | * free software; you can redistribute it and/or modify 14 | * it under the terms of the GNU General Public License as published by 15 | * the Free Software Foundation; either version 3 of the License, or 16 | * (at your option) any later version. 17 | * 18 | * The GNU General Public License can be found at 19 | * http://www.gnu.org/copyleft/gpl.html. 20 | * 21 | * This script is distributed in the hope that it will be useful, 22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 | * GNU General Public License for more details. 25 | * 26 | * This copyright notice MUST APPEAR in all copies of the script! 27 | ***************************************************************/ 28 | 29 | /** 30 | * Class Log 31 | * 32 | * @package AOE\SchedulerTimeline\Domain\Model 33 | */ 34 | class Log extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity 35 | { 36 | 37 | const STATUS_PENDING = 'pending'; 38 | const STATUS_RUNNING = 'running'; 39 | const STATUS_SUCCESS = 'success'; 40 | const STATUS_MISSED = 'missed'; 41 | const STATUS_ERROR = 'error'; 42 | 43 | /** 44 | * @var \AOE\SchedulerTimeline\Domain\Model\Task 45 | */ 46 | protected $task; 47 | 48 | /** 49 | * @var int 50 | */ 51 | protected $starttime = 0; 52 | 53 | /** 54 | * @var int 55 | */ 56 | protected $endtime = 0; 57 | 58 | /** 59 | * @var string 60 | */ 61 | protected $exception = ''; 62 | 63 | /** 64 | * @var string 65 | */ 66 | protected $returnmessage = ''; 67 | 68 | /** 69 | * @var int 70 | */ 71 | protected $processid = 0; 72 | 73 | /** 74 | * @param \AOE\SchedulerTimeline\Domain\Model\Task $task 75 | */ 76 | public function setTask(Task $task) 77 | { 78 | $this->task = $task; 79 | } 80 | 81 | /** 82 | * @param int $starttime 83 | */ 84 | public function setStarttime(int $starttime) 85 | { 86 | $this->starttime = $starttime; 87 | } 88 | 89 | /** 90 | * @param int $endtime 91 | */ 92 | public function setEndtime(int $endtime) 93 | { 94 | $this->endtime = $endtime; 95 | } 96 | 97 | /** 98 | * @param string $exception 99 | */ 100 | public function setException(string $exception) 101 | { 102 | $this->exception = $exception; 103 | } 104 | 105 | /** 106 | * @param string $returnmessage 107 | */ 108 | public function setReturnmessage(string $returnmessage) 109 | { 110 | $this->returnmessage = $returnmessage; 111 | } 112 | 113 | /** 114 | * @param int $processid 115 | */ 116 | public function setProcessid(int $processid) 117 | { 118 | $this->processid = $processid; 119 | } 120 | 121 | /** 122 | * Get task 123 | * 124 | * @return \AOE\SchedulerTimeline\Domain\Model\Task 125 | */ 126 | public function getTask() 127 | { 128 | return $this->task; 129 | } 130 | 131 | /** 132 | * @return the $starttime 133 | */ 134 | public function getStarttime() 135 | { 136 | return $this->starttime; 137 | } 138 | 139 | /** 140 | * @return the $endtime 141 | */ 142 | public function getEndtime() 143 | { 144 | return $this->endtime; 145 | } 146 | 147 | /** 148 | * @return the $exception 149 | */ 150 | public function getException() 151 | { 152 | return unserialize($this->exception); 153 | } 154 | 155 | /** 156 | * @return the $returnmessage 157 | */ 158 | public function getReturnmessage() 159 | { 160 | return $this->returnmessage; 161 | } 162 | 163 | /** 164 | * Get the process id 165 | * 166 | * @return int 167 | */ 168 | public function getProcessid() 169 | { 170 | return $this->processid; 171 | } 172 | 173 | /** 174 | * @return int duration (in sec) 175 | */ 176 | public function getDuration() 177 | { 178 | $endtime = $this->getEndtime() ? $this->getEndtime() : $this->getCurrentTime(); 179 | return $endtime - $this->getStarttime(); 180 | } 181 | 182 | /** 183 | * Get formatted timespan 184 | * 185 | * @return string 186 | */ 187 | public function getTimespan() 188 | { 189 | $timespan = $this->getFormattedDateFromTimestamp($this->getStarttime()); 190 | if ($this->isRunning()) { 191 | $timespan .= ' - (still running)'; 192 | } else { 193 | $timespan .= ' - ' . $this->getFormattedDateFromTimestamp($this->getEndtime()); 194 | } 195 | return $timespan; 196 | } 197 | 198 | /** 199 | * Is this task running? 200 | * 201 | * @return bool 202 | */ 203 | public function isRunning() 204 | { 205 | return (!$this->getEndtime()); 206 | } 207 | 208 | /** 209 | * Get status 210 | * 211 | * @return string see class constants STATUS_* 212 | */ 213 | public function getStatus() 214 | { 215 | if ($this->getException()) { 216 | return self::STATUS_ERROR; 217 | } elseif ($this->isRunning()) { 218 | return self::STATUS_RUNNING; 219 | } else { 220 | return self::STATUS_SUCCESS; 221 | } 222 | } 223 | 224 | /** 225 | * Kill this process 226 | * 227 | * @return void 228 | */ 229 | public function kill() 230 | { 231 | if ($this->isRunning()) { 232 | shell_exec('kill '. intval($this->processid)); 233 | } 234 | } 235 | 236 | /** 237 | * Returns the current time 238 | * 239 | * Mockable getter for time() 240 | * 241 | * @return int 242 | */ 243 | protected function getCurrentTime() 244 | { 245 | return time(); 246 | } 247 | 248 | /** 249 | * @param $timestamp 250 | * 251 | * @return bool|string 252 | */ 253 | protected function getFormattedDateFromTimestamp($timestamp) 254 | { 255 | return date('H:i', $timestamp); 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /Classes/Domain/Model/Task.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * All rights reserved 11 | * 12 | * This script is part of the TYPO3 project. The TYPO3 project is 13 | * free software; you can redistribute it and/or modify 14 | * it under the terms of the GNU General Public License as published by 15 | * the Free Software Foundation; either version 3 of the License, or 16 | * (at your option) any later version. 17 | * 18 | * The GNU General Public License can be found at 19 | * http://www.gnu.org/copyleft/gpl.html. 20 | * 21 | * This script is distributed in the hope that it will be useful, 22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 | * GNU General Public License for more details. 25 | * 26 | * This copyright notice MUST APPEAR in all copies of the script! 27 | ***************************************************************/ 28 | 29 | /** 30 | * Class Task 31 | * 32 | * @package AOE\SchedulerTimeline\Domain\Model 33 | */ 34 | class Task extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity 35 | { 36 | /** 37 | * @var string 38 | */ 39 | protected $serializedTaskObject; 40 | 41 | /** 42 | * @var string 43 | */ 44 | protected $logFileContent; 45 | 46 | /** 47 | * @return string 48 | */ 49 | public function getSerializedTaskObject(): string 50 | { 51 | return $this->serializedTaskObject; 52 | } 53 | 54 | /** 55 | * @param string $serializedTaskObject 56 | */ 57 | public function setSerializedTaskObject(string $serializedTaskObject) 58 | { 59 | $this->serializedTaskObject = $serializedTaskObject; 60 | } 61 | 62 | /** 63 | * Get classname 64 | * 65 | * @return string 66 | */ 67 | public function getClassname() 68 | { 69 | return get_class($this->getTaskObject()); 70 | } 71 | 72 | /** 73 | * Get task object 74 | * 75 | * @return \TYPO3\CMS\Scheduler\Task\AbstractTask 76 | * @throws \Exception 77 | */ 78 | public function getTaskObject() 79 | { 80 | $taskObject = unserialize($this->serializedTaskObject); 81 | if (!is_object($taskObject)) { 82 | throw new \Exception('Inconsitent data: There is no serialized task object in the Task', 1450187123); 83 | } 84 | return $taskObject; 85 | } 86 | 87 | /** 88 | * Get log file path 89 | * Expects $taskObject->getLogFilePath() to return the path 90 | * 91 | * @return string|bool 92 | */ 93 | public function getLogFilePath() 94 | { 95 | $taskObject = $this->getTaskObject(); 96 | if ($taskObject && is_callable(array($taskObject, 'getLogFilePath'))) { 97 | return $taskObject->getLogFilePath(); 98 | } 99 | return false; 100 | } 101 | 102 | /** 103 | * Get log file content 104 | * (If a log file is available. See $this->getLogFilePath()) 105 | * 106 | * @return string 107 | */ 108 | public function getLogFileContent() 109 | { 110 | if (is_null($this->logFileContent)) { 111 | $this->logFileContent = ''; 112 | $logFilePath = $this->getLogFilePath(); 113 | if ($logFilePath && is_file($logFilePath)) { 114 | $this->logFileContent = shell_exec('tail -n 20 ' . escapeshellarg($logFilePath)); 115 | } 116 | } 117 | return $this->logFileContent; 118 | } 119 | 120 | /** 121 | * Get info for this task 122 | * 123 | * @return array 124 | */ 125 | public function getInfo() 126 | { 127 | $registeredClasses = $this->getRegisteredClasses(); 128 | return $registeredClasses[$this->getClassname()]; 129 | } 130 | 131 | /** 132 | * Get extension 133 | * 134 | * @return string 135 | */ 136 | public function getExtension() 137 | { 138 | $info = $this->getInfo(); 139 | return $info['extension']; 140 | } 141 | 142 | /** 143 | * Get filename 144 | * 145 | * @return string 146 | */ 147 | public function getFilename() 148 | { 149 | $info = $this->getInfo(); 150 | return $info['filename']; 151 | } 152 | 153 | /** 154 | * Get title 155 | * 156 | * @return string 157 | */ 158 | public function getTitle() 159 | { 160 | $info = $this->getInfo(); 161 | return $info['title']; 162 | } 163 | 164 | /** 165 | * Get provider 166 | * 167 | * @return string 168 | */ 169 | public function getProvider() 170 | { 171 | $info = $this->getInfo(); 172 | return $info['provider']; 173 | } 174 | 175 | /** 176 | * This method a list of all classes that have been registered with the Scheduler 177 | * For each item the following information is provided, as an associative array: 178 | * 179 | * ['extension'] => Key of the extension which provides the class 180 | * ['filename'] => Path to the file containing the class 181 | * ['title'] => String (possibly localized) containing a human-readable name for the class 182 | * ['provider'] => Name of class that implements the interface for additional fields, if necessary 183 | * 184 | * The name of the class itself is used as the key of the list array 185 | * 186 | * @return array List of registered classes 187 | */ 188 | protected function getRegisteredClasses() 189 | { 190 | $list = array(); 191 | if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'])) { 192 | foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'] as $class => $registrationInformation) { 193 | $title = isset($registrationInformation['title']) ? $GLOBALS['LANG']->sL($registrationInformation['title']) : ''; 194 | $description = isset($registrationInformation['description']) ? $GLOBALS['LANG']->sL($registrationInformation['description']) : ''; 195 | 196 | $list[$class] = array( 197 | 'extension' => $registrationInformation['extension'], 198 | 'title' => $title, 199 | 'description' => $description, 200 | 'provider' => isset($registrationInformation['additionalFields']) ? $registrationInformation['additionalFields'] : '' 201 | ); 202 | } 203 | } 204 | 205 | return $list; 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /Documentation/_make/Makefile: -------------------------------------------------------------------------------- 1 | # You can set these variables from the command line. 2 | SPHINXOPTS = -c . -a -E -w ./_not_versioned/warnings.txt 3 | SPHINXBUILD = sphinx-build 4 | PAPER = a4 5 | BUILDDIR = build 6 | 7 | # User-friendly check for sphinx-build 8 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 9 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 10 | endif 11 | 12 | # Internal variables. 13 | PAPEROPT_a4 = -D latex_paper_size=a4 14 | PAPEROPT_letter = -D latex_paper_size=letter 15 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .. 16 | # the i18n builder cannot share the environment and doctrees with the others 17 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .. 18 | 19 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 20 | 21 | help: 22 | @echo "Please use \`make ' where is one of" 23 | @echo " html to make standalone HTML files" 24 | @echo " dirhtml to make HTML files named index.html in directories" 25 | @echo " singlehtml to make a single large HTML file" 26 | @echo " pickle to make pickle files" 27 | @echo " json to make JSON files" 28 | @echo " htmlhelp to make HTML files and a HTML help project" 29 | @echo " qthelp to make HTML files and a qthelp project" 30 | @echo " devhelp to make HTML files and a Devhelp project" 31 | @echo " epub to make an epub" 32 | @echo " pdf to make PDF using rst2pdf" 33 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 34 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 35 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 36 | @echo " text to make text files" 37 | @echo " man to make manual pages" 38 | @echo " texinfo to make Texinfo files" 39 | @echo " info to make Texinfo files and run them through makeinfo" 40 | @echo " gettext to make PO message catalogs" 41 | @echo " changes to make an overview of all changed/added/deprecated items" 42 | @echo " xml to make Docutils-native XML files" 43 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 44 | @echo " linkcheck to check all external links for integrity" 45 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 46 | 47 | clean: 48 | rm -rf $(BUILDDIR)/* 49 | 50 | html: 51 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 52 | @echo 53 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 54 | 55 | dirhtml: 56 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 57 | @echo 58 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 59 | 60 | singlehtml: 61 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 62 | @echo 63 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 64 | 65 | pickle: 66 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 67 | @echo 68 | @echo "Build finished; now you can process the pickle files." 69 | 70 | json: 71 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 72 | @echo 73 | @echo "Build finished; now you can process the JSON files." 74 | 75 | htmlhelp: 76 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 77 | @echo 78 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 79 | ".hhp project file in $(BUILDDIR)/htmlhelp." 80 | 81 | qthelp: 82 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 83 | @echo 84 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 85 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 86 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Brute Force Protection.qhcp" 87 | @echo "To view the help file:" 88 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Brute Force Protection.qhc" 89 | 90 | devhelp: 91 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 92 | @echo 93 | @echo "Build finished." 94 | @echo "To view the help file:" 95 | @echo "# mkdir -p $$HOME/.local/share/devhelp/Brute Force Protection" 96 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Brute Force Protection" 97 | @echo "# devhelp" 98 | 99 | epub: 100 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 101 | @echo 102 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 103 | 104 | pdf: 105 | $(SPHINXBUILD) -b pdf $(ALLSPHINXOPTS) $(BUILDDIR)/pdf 106 | @echo 107 | @echo "Build finished. The PDF is in $(BUILDDIR)/pdf." 108 | 109 | latex: 110 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 111 | @echo 112 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 113 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 114 | "(use \`make latexpdf' here to do that automatically)." 115 | 116 | latexpdf: 117 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 118 | @echo "Running LaTeX files through pdflatex..." 119 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 120 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 121 | 122 | latexpdfja: 123 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 124 | @echo "Running LaTeX files through platex and dvipdfmx..." 125 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 126 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 127 | 128 | text: 129 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 130 | @echo 131 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 132 | 133 | man: 134 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 135 | @echo 136 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 137 | 138 | texinfo: 139 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 140 | @echo 141 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 142 | @echo "Run \`make' in that directory to run these through makeinfo" \ 143 | "(use \`make info' here to do that automatically)." 144 | 145 | info: 146 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 147 | @echo "Running Texinfo files through makeinfo..." 148 | make -C $(BUILDDIR)/texinfo info 149 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 150 | 151 | gettext: 152 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 153 | @echo 154 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 155 | 156 | changes: 157 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 158 | @echo 159 | @echo "The overview file is in $(BUILDDIR)/changes." 160 | 161 | linkcheck: 162 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 163 | @echo 164 | @echo "Link check complete; look for any errors in the above output " \ 165 | "or in $(BUILDDIR)/linkcheck/output.txt." 166 | 167 | doctest: 168 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 169 | @echo "Testing of doctests in the sources finished, look at the " \ 170 | "results in $(BUILDDIR)/doctest/output.txt." 171 | 172 | xml: 173 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 174 | @echo 175 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 176 | 177 | pseudoxml: 178 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 179 | @echo 180 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 181 | -------------------------------------------------------------------------------- /Classes/Controller/TimelineController.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * All rights reserved 11 | * 12 | * This script is part of the TYPO3 project. The TYPO3 project is 13 | * free software; you can redistribute it and/or modify 14 | * it under the terms of the GNU General Public License as published by 15 | * the Free Software Foundation; either version 3 of the License, or 16 | * (at your option) any later version. 17 | * 18 | * The GNU General Public License can be found at 19 | * http://www.gnu.org/copyleft/gpl.html. 20 | * 21 | * This script is distributed in the hope that it will be useful, 22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 | * GNU General Public License for more details. 25 | * 26 | * This copyright notice MUST APPEAR in all copies of the script! 27 | ***************************************************************/ 28 | 29 | use AOE\SchedulerTimeline\Converter\FormValueTimestampConverter; 30 | use TYPO3\CMS\Core\Utility\GeneralUtility; 31 | use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; 32 | 33 | /** 34 | * Class TimelineController 35 | * 36 | * @package AOE\SchedulerTimeline\Controller 37 | */ 38 | class TimelineController extends ActionController 39 | { 40 | 41 | /** 42 | * @var string Key of the extension this controller belongs to 43 | */ 44 | protected $extensionName = 'SchedulerTimeline'; 45 | 46 | /** 47 | * @var \TYPO3\CMS\Core\Page\PageRenderer 48 | */ 49 | protected $pageRenderer; 50 | 51 | /** 52 | * @var \AOE\SchedulerTimeline\Domain\Repository\LogRepository 53 | */ 54 | protected $logRepository; 55 | 56 | /** 57 | * @var \AOE\SchedulerTimeline\Domain\Repository\TaskRepository 58 | */ 59 | protected $taskRepository; 60 | 61 | /** 62 | * @var \TYPO3\CMS\Backend\Template\DocumentTemplate 63 | */ 64 | protected $template; 65 | 66 | /** 67 | * Initializes the controller before invoking an action method. 68 | * 69 | * @return void 70 | */ 71 | protected function initializeAction() 72 | { 73 | $this->pageRenderer->addInlineLanguageLabelFile('EXT:scheduler_timeline/Resources/Private/Language/locallang.xml'); 74 | } 75 | 76 | /** 77 | * @param \TYPO3\CMS\Core\Page\PageRenderer $pageRenderer 78 | * @return void 79 | */ 80 | public function injectPageRenderer(\TYPO3\CMS\Core\Page\PageRenderer $pageRenderer) 81 | { 82 | $this->pageRenderer = $pageRenderer; 83 | } 84 | 85 | /** 86 | * @param \AOE\SchedulerTimeline\Domain\Repository\LogRepository $logRepository 87 | * @return void 88 | */ 89 | public function injectLogRepository(\AOE\SchedulerTimeline\Domain\Repository\LogRepository $logRepository) 90 | { 91 | $this->logRepository = $logRepository; 92 | } 93 | 94 | /** 95 | * @param \AOE\SchedulerTimeline\Domain\Repository\TaskRepository $taskRepository 96 | * @return void 97 | */ 98 | public function injectTaskRepository(\AOE\SchedulerTimeline\Domain\Repository\TaskRepository $taskRepository) 99 | { 100 | $this->taskRepository = $taskRepository; 101 | } 102 | 103 | /** 104 | * Simple action to list some stuff 105 | */ 106 | public function timelineAction() 107 | { 108 | $zoom = 15; // amount of seconds per pixel 109 | 110 | $startTime = strtoTime('-1 hour'); 111 | $endTime = time(); 112 | $postVars = GeneralUtility::_POST('tx_schedulertimeline_tools_schedulertimelineschedulertimeline'); 113 | $timespan = $postVars['timespan'] ?? null; 114 | if ($timespan !== null) { 115 | $startTime = $this->getStartTime($postVars['timespan']); 116 | } 117 | $groupedLogs = $this->logRepository->findGroupedByTask($startTime, $endTime); 118 | 119 | // To have logs that was added this second included, 120 | $startTimeFloor = $this->hourFloor($startTime); 121 | $endTimeCeil = $this->hourCeil($endTime); 122 | 123 | $this->view->assign('groupedLogs', $groupedLogs); 124 | $this->view->assign('starttime', $startTimeFloor); 125 | $this->view->assign('endtime', $endTimeCeil); 126 | $this->view->assign('zoom', $zoom); 127 | $this->view->assign('now', ($GLOBALS['EXEC_TIME'] - $startTimeFloor) / $zoom); 128 | $this->view->assign('timelinePanelWidth', ($endTimeCeil - $startTimeFloor) / $zoom); 129 | $this->view->assign('actionMenuValues', $this->getActionMenuValues()); 130 | $this->view->assign('actionMenuSelected', $timespan); 131 | } 132 | 133 | /** 134 | * @param string $timespan 135 | * @return int 136 | */ 137 | private function getStartTime(string $timespan) 138 | { 139 | $strToTimeValue = FormValueTimestampConverter::convertValueToStrToTimeValue($timespan); 140 | return strtotime('-' . $strToTimeValue); 141 | } 142 | 143 | /** 144 | * @return array 145 | */ 146 | private function getActionMenuValues() 147 | { 148 | return [ 149 | '1h' => '1 Hour', 150 | '3h' => '3 Hours', 151 | '1d' => '1 Day', 152 | '3d' => '3 Days', 153 | '1w' => '1 Week', 154 | '2w' => '2 Weeks', 155 | '3w' => '3 Weeks', 156 | '4w' => '4 Weeks', 157 | ]; 158 | } 159 | 160 | /** 161 | * Return the last full houd 162 | * 163 | * @param int $timestamp 164 | * @return int 165 | */ 166 | protected function hourFloor($timestamp) 167 | { 168 | return mktime(date('H', $timestamp), 0, 0, date('n', $timestamp), date('j', $timestamp), date('Y', $timestamp)); 169 | } 170 | 171 | /** 172 | * Returns the next full hour 173 | * 174 | * @param int $timestamp 175 | * @return int 176 | */ 177 | protected function hourCeil($timestamp) 178 | { 179 | return mktime(date('H', $timestamp)+1, 0, 0, date('n', $timestamp), date('j', $timestamp), date('Y', $timestamp)); 180 | } 181 | 182 | 183 | /** 184 | * Processes a general request. The result can be returned by altering the given response. 185 | * 186 | * @param \TYPO3\CMS\Extbase\Mvc\RequestInterface $request The request object 187 | * @param \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response The response, modified by this handler 188 | * @throws \TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException if the controller doesn't support the current request type 189 | * @return void 190 | */ 191 | public function processRequest(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request, \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response) 192 | { 193 | $this->template = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\Template\\DocumentTemplate'); 194 | 195 | $GLOBALS['SOBE'] = new \stdClass(); 196 | $GLOBALS['SOBE']->doc = $this->template; 197 | 198 | parent::processRequest($request, $response); 199 | 200 | $pageHeader = $this->template->startpage( 201 | $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xml:module.title') 202 | ); 203 | $pageEnd = $this->template->endPage(); 204 | 205 | $response->setContent($pageHeader . $response->getContent() . $pageEnd); 206 | } 207 | 208 | 209 | /** 210 | * Wrapper for t3lib_PageRenderer->addJsFile. Excludes $jsFile from concatenation on TYPO3 4.6+. 211 | * 212 | * @param string $jsFile 213 | * @return void 214 | */ 215 | protected function addJsFileToPageRenderer($jsFile) 216 | { 217 | if (version_compare(TYPO3_version, '4.6', '>=')) { 218 | $this->pageRenderer->addJsFile($jsFile, 'text/javascript', true, false, '', true); 219 | } else { 220 | $this->pageRenderer->addJsFile($jsFile); 221 | } 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /Resources/Public/JavaScript/tooltip.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * jQuery Tools @VERSION Tooltip - UI essentials 4 | * 5 | * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE. 6 | * 7 | * http://flowplayer.org/tools/tooltip/ 8 | * 9 | * Since: November 2008 10 | * Date: @DATE 11 | */ 12 | (function($) { 13 | // static constructs 14 | $.tools = $.tools || {version: '@VERSION'}; 15 | 16 | $.tools.tooltip = { 17 | 18 | conf: { 19 | 20 | // default effect variables 21 | effect: 'toggle', 22 | fadeOutSpeed: "fast", 23 | predelay: 0, 24 | delay: 30, 25 | opacity: 1, 26 | tip: 0, 27 | 28 | // 'top', 'bottom', 'right', 'left', 'center' 29 | position: ['top', 'center'], 30 | offset: [0, 0], 31 | relative: false, 32 | offsetParent: null, 33 | cancelDefault: true, 34 | 35 | // type to event mapping 36 | events: { 37 | def: "mouseenter,mouseleave", 38 | input: "focus,blur", 39 | widget: "focus mouseenter,blur mouseleave", 40 | tooltip: "mouseenter,mouseleave" 41 | }, 42 | 43 | // 1.2 44 | layout: '
', 45 | tipClass: 'tooltip' 46 | }, 47 | 48 | addEffect: function(name, loadFn, hideFn) { 49 | effects[name] = [loadFn, hideFn]; 50 | } 51 | }; 52 | 53 | 54 | var effects = { 55 | toggle: [ 56 | function(done) { 57 | var conf = this.getConf(), tip = this.getTip(), o = conf.opacity; 58 | if (o < 1) { tip.css({opacity: o}); } 59 | tip.show(); 60 | done.call(); 61 | }, 62 | 63 | function(done) { 64 | this.getTip().hide(); 65 | done.call(); 66 | } 67 | ], 68 | 69 | fade: [ 70 | function(done) { 71 | var conf = this.getConf(); 72 | this.getTip().fadeTo(conf.fadeInSpeed, conf.opacity, done); 73 | }, 74 | function(done) { 75 | this.getTip().fadeOut(this.getConf().fadeOutSpeed, done); 76 | } 77 | ] 78 | }; 79 | 80 | 81 | /* calculate tip position relative to the trigger */ 82 | function getPosition(trigger, tip, conf) { 83 | 84 | 85 | // get origin top/left position 86 | var top = conf.relative ? trigger.position().top : trigger.offset().top, 87 | left = conf.relative ? trigger.position().left : trigger.offset().left, 88 | pos = conf.position[0]; 89 | 90 | top -= tip.outerHeight() - conf.offset[0]; 91 | left += trigger.outerWidth() + conf.offset[1]; 92 | 93 | // iPad position fix 94 | if (/iPad/i.test(navigator.userAgent)) { 95 | top -= $(window).scrollTop(); 96 | } 97 | 98 | // adjust Y 99 | var height = tip.outerHeight() + trigger.outerHeight(); 100 | if (pos == 'center') { top += height / 2; } 101 | if (pos == 'bottom') { top += height; } 102 | 103 | 104 | // adjust X 105 | pos = conf.position[1]; 106 | var width = tip.outerWidth() + trigger.outerWidth(); 107 | if (pos == 'center') { left -= width / 2; } 108 | if (pos == 'left') { left -= width; } 109 | 110 | return {top: top, left: left}; 111 | } 112 | 113 | 114 | 115 | function Tooltip(trigger, conf) { 116 | 117 | var self = this, 118 | fire = trigger.add(self), 119 | tip, 120 | timer = 0, 121 | pretimer = 0, 122 | title = trigger.attr("title"), 123 | tipAttr = trigger.attr("data-tooltip"), 124 | effect = effects[conf.effect], 125 | shown, 126 | 127 | // get show/hide configuration 128 | isInput = trigger.is(":input"), 129 | isWidget = isInput && trigger.is(":checkbox, :radio, select, :button, :submit"), 130 | type = trigger.attr("type"), 131 | evt = conf.events[type] || conf.events[isInput ? (isWidget ? 'widget' : 'input') : 'def']; 132 | 133 | 134 | // check that configuration is sane 135 | if (!effect) { throw "Nonexistent effect \"" + conf.effect + "\""; } 136 | 137 | evt = evt.split(/,\s*/); 138 | if (evt.length != 2) { throw "Tooltip: bad events configuration for " + type; } 139 | 140 | 141 | // trigger --> show 142 | trigger.bind(evt[0], function(e) { 143 | 144 | clearTimeout(timer); 145 | if (conf.predelay) { 146 | pretimer = setTimeout(function() { self.show(e); }, conf.predelay); 147 | 148 | } else { 149 | self.show(e); 150 | } 151 | 152 | // trigger --> hide 153 | }).bind(evt[1], function(e) { 154 | clearTimeout(pretimer); 155 | if (conf.delay) { 156 | timer = setTimeout(function() { self.hide(e); }, conf.delay); 157 | 158 | } else { 159 | self.hide(e); 160 | } 161 | 162 | }); 163 | 164 | 165 | // remove default title 166 | if (title && conf.cancelDefault) { 167 | trigger.removeAttr("title"); 168 | trigger.data("title", title); 169 | } 170 | 171 | $.extend(self, { 172 | 173 | show: function(e) { 174 | 175 | // tip not initialized yet 176 | if (!tip) { 177 | 178 | // data-tooltip 179 | if (tipAttr) { 180 | tip = $(tipAttr); 181 | 182 | // single tip element for all 183 | } else if (conf.tip) { 184 | tip = $(conf.tip).eq(0); 185 | 186 | // autogenerated tooltip 187 | } else if (title) { 188 | tip = $(conf.layout).addClass(conf.tipClass).appendTo(document.body) 189 | .hide().append(title); 190 | 191 | // manual tooltip 192 | } else { 193 | tip = trigger.next(); 194 | if (!tip.length) { tip = trigger.parent().next(); } 195 | 196 | } 197 | 198 | if (!tip.length) { throw "Cannot find tooltip for " + trigger; } 199 | 200 | if (conf.offsetParent) { 201 | tip.appendTo($(conf.offsetParent)); 202 | } 203 | } 204 | 205 | if (self.isShown()) { return self; } 206 | 207 | // stop previous animation 208 | tip.stop(true, true); 209 | 210 | // get position 211 | var pos = getPosition(trigger, tip, conf); 212 | 213 | // restore title for single tooltip element 214 | if (conf.tip) { 215 | tip.html(trigger.data("title")); 216 | } 217 | 218 | // onBeforeShow 219 | e = e || $.Event(); 220 | e.type = "onBeforeShow"; 221 | fire.trigger(e, [pos]); 222 | if (e.isDefaultPrevented()) { return self; } 223 | 224 | 225 | // onBeforeShow may have altered the configuration 226 | pos = getPosition(trigger, tip, conf); 227 | 228 | // set position 229 | tip.css({position:'absolute', top: pos.top, left: pos.left}); 230 | 231 | shown = true; 232 | 233 | // invoke effect 234 | effect[0].call(self, function() { 235 | e.type = "onShow"; 236 | shown = 'full'; 237 | fire.trigger(e); 238 | }); 239 | 240 | 241 | // tooltip events 242 | var event = conf.events.tooltip.split(/,\s*/); 243 | 244 | if (!tip.data("__set")) { 245 | 246 | tip.bind(event[0], function() { 247 | clearTimeout(timer); 248 | clearTimeout(pretimer); 249 | }); 250 | 251 | if (event[1] && !trigger.is("input:not(:checkbox, :radio), textarea")) { 252 | tip.bind(event[1], function(e) { 253 | 254 | // being moved to the trigger element 255 | if (e.relatedTarget != trigger[0]) { 256 | trigger.trigger(evt[1].split(" ")[0]); 257 | } 258 | }); 259 | } 260 | 261 | tip.data("__set", true); 262 | } 263 | 264 | return self; 265 | }, 266 | 267 | hide: function(e) { 268 | 269 | if (!tip || !self.isShown()) { return self; } 270 | 271 | // onBeforeHide 272 | e = e || $.Event(); 273 | e.type = "onBeforeHide"; 274 | fire.trigger(e); 275 | if (e.isDefaultPrevented()) { return; } 276 | 277 | shown = false; 278 | 279 | effects[conf.effect][1].call(self, function() { 280 | e.type = "onHide"; 281 | fire.trigger(e); 282 | }); 283 | 284 | return self; 285 | }, 286 | 287 | isShown: function(fully) { 288 | return fully ? shown == 'full' : shown; 289 | }, 290 | 291 | getConf: function() { 292 | return conf; 293 | }, 294 | 295 | getTip: function() { 296 | return tip; 297 | }, 298 | 299 | getTrigger: function() { 300 | return trigger; 301 | } 302 | 303 | }); 304 | 305 | // callbacks 306 | $.each("onHide,onBeforeShow,onShow,onBeforeHide".split(","), function(i, name) { 307 | 308 | // configuration 309 | if ($.isFunction(conf[name])) { 310 | $(self).bind(name, conf[name]); 311 | } 312 | 313 | // API 314 | self[name] = function(fn) { 315 | if (fn) { $(self).bind(name, fn); } 316 | return self; 317 | }; 318 | }); 319 | 320 | } 321 | 322 | 323 | // jQuery plugin implementation 324 | $.fn.tooltip = function(conf) { 325 | 326 | // return existing instance 327 | var api = this.data("tooltip"); 328 | if (api) { return api; } 329 | 330 | conf = $.extend(true, {}, $.tools.tooltip.conf, conf); 331 | 332 | // position can also be given as string 333 | if (typeof conf.position == 'string') { 334 | conf.position = conf.position.split(/,?\s/); 335 | } 336 | 337 | // install tooltip for each entry in jQuery object 338 | this.each(function() { 339 | api = new Tooltip($(this), conf); 340 | $(this).data("tooltip", api); 341 | }); 342 | 343 | return conf.api ? api: this; 344 | }; 345 | 346 | }) (jQuery); 347 | 348 | 349 | 350 | -------------------------------------------------------------------------------- /Classes/XClass/Scheduler.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * All rights reserved 11 | * 12 | * This script is part of the TYPO3 project. The TYPO3 project is 13 | * free software; you can redistribute it and/or modify 14 | * it under the terms of the GNU General Public License as published by 15 | * the Free Software Foundation; either version 3 of the License, or 16 | * (at your option) any later version. 17 | * 18 | * The GNU General Public License can be found at 19 | * http://www.gnu.org/copyleft/gpl.html. 20 | * 21 | * This script is distributed in the hope that it will be useful, 22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 | * GNU General Public License for more details. 25 | * 26 | * This copyright notice MUST APPEAR in all copies of the script! 27 | ***************************************************************/ 28 | 29 | use TYPO3\CMS\Core\Utility\GeneralUtility; 30 | 31 | /** 32 | * Class Scheduler 33 | * 34 | * @package AOE\SchedulerTimeline\XClass 35 | */ 36 | class Scheduler extends \TYPO3\CMS\Scheduler\Scheduler 37 | { 38 | 39 | /** 40 | * Wraps the executeTask method 41 | * 42 | * @param \TYPO3\CMS\Scheduler\Task\AbstractTask $task The task to execute 43 | * @return boolean Whether the task was saved successfully to the database or not 44 | * @throws \Exception 45 | */ 46 | public function executeTask(\TYPO3\CMS\Scheduler\Task\AbstractTask $task) 47 | { 48 | $taskUid = $task->getTaskUid(); 49 | 50 | // log start 51 | $logUid = $this->logStart($taskUid); 52 | 53 | $failure = null; 54 | try { 55 | $result = parent::executeTask($task); 56 | } catch (\Exception $e) { 57 | $failure = $e; 58 | } 59 | 60 | if ($result || $failure) { 61 | $returnMessage = ''; 62 | if ($task instanceof \AOE\SchedulerTimeline\Interfaces\ReturnMessage || is_callable(array($task, 'getReturnMessage'))) { 63 | $returnMessage = $task->getReturnMessage(); 64 | } 65 | 66 | // log end 67 | $this->logEnd($logUid, $failure, $returnMessage); 68 | } else { 69 | // task was not executed, because another task was running 70 | // and multiple execution is not allowed for this task 71 | $this->removeLog($logUid); 72 | } 73 | 74 | if ($failure instanceof \Exception) { 75 | throw $failure; 76 | } 77 | 78 | return $result; 79 | } 80 | 81 | /** 82 | * Extend the method to cleanup up the log table aswell 83 | * 84 | * @see tx_scheduler::cleanExecutionArrays() 85 | */ 86 | protected function cleanExecutionArrays() 87 | { 88 | parent::cleanExecutionArrays(); 89 | $this->cleanupLog(); 90 | } 91 | 92 | /** 93 | * Cleanup log 94 | * 95 | * @return void 96 | * @throws \Exception 97 | */ 98 | protected function cleanupLog() 99 | { 100 | $extConf = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['scheduler_timeline']); 101 | 102 | // clean old log entries 103 | $dbObj = $GLOBALS['TYPO3_DB']; /* @var $dbObj \TYPO3\CMS\Core\Database\DatabaseConnection */ 104 | $res = $dbObj->exec_DELETEquery('tx_schedulertimeline_domain_model_log', 'endtime > 0 AND endtime <'.(time()- $extConf['cleanLogEntriesOlderThan'] * 60)); 105 | if ($res === false) { 106 | GeneralUtility::sysLog('Error while cleaning log', 'scheduler_timeline', 0); 107 | } 108 | 109 | // clean tasks, that exceeded the maxLifetime 110 | $maxDuration = $this->extConf['maxLifetime'] * 60; 111 | $dbObj = $GLOBALS['TYPO3_DB']; /* @var $dbObj \TYPO3\CMS\Core\Database\DatabaseConnection */ 112 | $res = $dbObj->exec_UPDATEquery( 113 | 'tx_schedulertimeline_domain_model_log', 114 | 'endtime = 0 AND starttime < ' . (time() - $maxDuration), 115 | array( 116 | 'endtime' => time(), 117 | 'exception' => serialize(array('message' => 'Task was cleaned up, because it exceeded maxLifetime.')) 118 | ) 119 | ); 120 | if ($res === false) { 121 | GeneralUtility::sysLog('Error while cleaning tasks', 'scheduler_timeline', 0); 122 | } 123 | 124 | // check if process are still alive that have been started more than x minutes ago 125 | $checkProcessesAfter = intval($extConf['checkProcessesAfter']) * 60; 126 | if ($checkProcessesAfter) { 127 | $res = $dbObj->exec_SELECTquery('uid, processid, task', 'tx_schedulertimeline_domain_model_log', 'endtime = 0 AND starttime < ' . (time() - $checkProcessesAfter)); 128 | if (is_resource($res)) { 129 | while (($row = $dbObj->sql_fetch_assoc($res)) !== false) { 130 | $processId = $row['processid']; 131 | if (!$this->checkProcess($processId)) { 132 | 133 | // update log 134 | $res2 = $dbObj->exec_UPDATEquery( 135 | 'tx_schedulertimeline_domain_model_log', 136 | 'uid = '.intval($row['uid']), 137 | array( 138 | 'endtime' => time(), 139 | 'exception' => serialize(array('message' => 'Task was cleaned up, because it seems to be dead.')) 140 | ) 141 | ); 142 | if ($res2 === false) { 143 | throw new \Exception('Error while cleaning tasks'); 144 | } 145 | 146 | $exception = new \TYPO3\CMS\Scheduler\FailedExecutionException('Task was cleaned up, because it seems to be dead.'); 147 | 148 | // update scheduler task 149 | $res3 = $dbObj->exec_UPDATEquery( 150 | 'tx_scheduler_task', 151 | 'uid = '.intval($row['task']), 152 | array( 153 | 'serialized_executions' => '', 154 | 'lastexecution_failure' => serialize($exception) 155 | ) 156 | ); 157 | 158 | if ($res3 === false) { 159 | GeneralUtility::sysLog('Error while cleaning tasks', 'scheduler_timeline', 0); 160 | } 161 | } 162 | } 163 | } 164 | } 165 | } 166 | 167 | /** 168 | * Log the start of a task 169 | * 170 | * @param int $taskUid 171 | * @throws \Exception 172 | * @return int 173 | */ 174 | protected function logStart($taskUid) 175 | { 176 | $now = time(); 177 | $dbObj = $GLOBALS['TYPO3_DB']; /* @var $dbObj \TYPO3\CMS\Core\Database\DatabaseConnection */ 178 | $res = $dbObj->exec_INSERTquery('tx_schedulertimeline_domain_model_log', array( 179 | 'pid' => '0', 180 | 'tstamp' => $now, 181 | 'starttime' => $now, 182 | 'task' => $taskUid, 183 | 'processid' => getmypid() 184 | )); 185 | if ($res === false) { 186 | GeneralUtility::sysLog('Error while inserting log entry', 'scheduler_timeline', 0); 187 | } 188 | return $dbObj->sql_insert_id(); 189 | } 190 | 191 | /** 192 | * Remove a log entry 193 | * 194 | * @param int $logUid 195 | * @throws \Exception 196 | * @return void 197 | */ 198 | protected function removeLog($logUid) 199 | { 200 | $dbObj = $GLOBALS['TYPO3_DB']; /* @var $dbObj \TYPO3\CMS\Core\Database\DatabaseConnection */ 201 | $res = $dbObj->exec_DELETEquery('tx_schedulertimeline_domain_model_log', 'uid='.intval($logUid)); 202 | if ($res === false) { 203 | GeneralUtility::sysLog('Error while deleting log entry', 'scheduler_timeline', 0); 204 | } 205 | } 206 | 207 | /** 208 | * Log the end of a task 209 | * 210 | * @param int $logUid 211 | * @param \Exception $failure 212 | * @param string $returnMessage 213 | * @throws \Exception 214 | */ 215 | protected function logEnd($logUid, $failure, $returnMessage) 216 | { 217 | $exception = ''; 218 | if ($failure instanceof \Exception) { /* @var $failure \Exception */ 219 | $exception = serialize(array( 220 | 'message' => $failure->getMessage(), 221 | 'stacktrace' => $failure->getTraceAsString(), 222 | 'endtime' => time(), 223 | 'class' => get_class($failure) 224 | )); 225 | } 226 | 227 | $dbObj = $GLOBALS['TYPO3_DB']; /* @var $dbObj \TYPO3\CMS\Core\Database\DatabaseConnection */ 228 | $res = $dbObj->exec_UPDATEquery('tx_schedulertimeline_domain_model_log', 'uid='.intval($logUid), array( 229 | 'endtime' => time(), 230 | 'exception' => $exception, 231 | 'returnmessage' => $returnMessage 232 | )); 233 | if ($res === false) { 234 | GeneralUtility::sysLog('Error while updating log entry', 'scheduler_timeline', 0); 235 | } 236 | } 237 | 238 | /** 239 | * Check process 240 | * 241 | * @param int $pid 242 | * @return bool 243 | */ 244 | protected function checkProcess($pid) 245 | { 246 | // form the filename to search for 247 | $file = '/proc/' . (int) $pid . '/cmdline'; 248 | $fp = false; 249 | if (file_exists($file)) { 250 | $fp = @fopen($file, 'rb'); 251 | } 252 | 253 | if (!$fp) { // if file does not exist or cannot be opened, return false 254 | return false; 255 | } 256 | $buf = fgets($fp); 257 | fclose($fp); 258 | 259 | if ($buf === false) { // if we failed to read from file, return false 260 | return false; 261 | } 262 | return true; 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /Tests/Unit/Domain/Model/LogTest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * All rights reserved 11 | * 12 | * This script is part of the TYPO3 project. The TYPO3 project is 13 | * free software; you can redistribute it and/or modify 14 | * it under the terms of the GNU General Public License as published by 15 | * the Free Software Foundation; either version 3 of the License, or 16 | * (at your option) any later version. 17 | * 18 | * The GNU General Public License can be found at 19 | * http://www.gnu.org/copyleft/gpl.html. 20 | * 21 | * This script is distributed in the hope that it will be useful, 22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 | * GNU General Public License for more details. 25 | * 26 | * This copyright notice MUST APPEAR in all copies of the script! 27 | ***************************************************************/ 28 | 29 | use AOE\SchedulerTimeline\Domain\Model\Log; 30 | use Nimut\TestingFramework\TestCase\UnitTestCase; 31 | 32 | /** 33 | * Class LogTest 34 | * @package AOE\SchedulerTimeline\Tests\Unit\Domain\Model 35 | */ 36 | class LogTest extends UnitTestCase 37 | { 38 | 39 | /** 40 | * @var Log 41 | */ 42 | protected $subject; 43 | 44 | public function setUp() 45 | { 46 | // Make sure that timezone is always the same in all testing environments 47 | date_default_timezone_set('UTC'); 48 | $this->subject = new \AOE\SchedulerTimeline\Domain\Model\Log(); 49 | } 50 | 51 | /** 52 | * @test 53 | */ 54 | public function checkInitiationOfSubjectReturnObjectOfDomainModelLog() 55 | { 56 | $this->assertInstanceOf( 57 | 'AOE\SchedulerTimeline\Domain\Model\Log', 58 | $this->subject 59 | ); 60 | } 61 | 62 | /** 63 | * @test 64 | */ 65 | public function getStarttimeReturnsInitialValueForInteger() 66 | { 67 | $this->assertSame( 68 | 0, 69 | $this->subject->getStarttime() 70 | ); 71 | } 72 | 73 | /** 74 | * @test 75 | */ 76 | public function getEndtimeReturnsInitialValueForInteger() 77 | { 78 | $this->assertSame( 79 | 0, 80 | $this->subject->getEndtime() 81 | ); 82 | } 83 | 84 | /** 85 | * @test 86 | */ 87 | public function getExceptionReturnsInitialValueForString() 88 | { 89 | $this->assertSame( 90 | false, 91 | $this->subject->getException() 92 | ); 93 | } 94 | 95 | /** 96 | * @test 97 | */ 98 | public function getReturnmessageReturnsInitialValueForString() 99 | { 100 | $this->assertSame( 101 | '', 102 | $this->subject->getReturnmessage() 103 | ); 104 | } 105 | 106 | /** 107 | * @test 108 | */ 109 | public function getProcessIdReturnsInitialValueForInteger() 110 | { 111 | $this->assertSame( 112 | 0, 113 | $this->subject->getProcessid() 114 | ); 115 | } 116 | 117 | /** 118 | * @test 119 | */ 120 | public function getTaskReturnsNull() 121 | { 122 | $this->assertNull($this->subject->getTask()); 123 | } 124 | 125 | /** 126 | * @test 127 | * @dataProvider getDurationReturnsDurationInSecondsDataProvider() 128 | */ 129 | public function getDurationReturnsDurationInSeconds($starttime, $endtime, $expectedDuration) 130 | { 131 | $stub = $this->getAccessibleMock( 132 | 'AOE\\SchedulerTimeline\\Domain\\Model\\Log', 133 | array('getStarttime', 'getEndtime', 'getCurrentTime') 134 | ); 135 | $stub->expects($this->any())->method('getStarttime')->will($this->returnValue($starttime)); 136 | $stub->expects($this->any())->method('getEndtime')->will($this->returnValue($endtime)); 137 | $stub->expects($this->any())->method('getCurrentTime')->will($this->returnValue(1000)); 138 | 139 | $this->assertSame($expectedDuration, $stub->getDuration()); 140 | } 141 | 142 | /** 143 | * @return array 144 | */ 145 | public function getDurationReturnsDurationInSecondsDataProvider() 146 | { 147 | return array( 148 | 'With start- and endtime' => array( 149 | 'starttime' => 0, 150 | 'endtime' => 10, 151 | 'expectedDuration' => 10 152 | ), 153 | 'With starttime but no endtime' => array( 154 | 'starttime' => 0, 155 | 'endtime' => null, 156 | 'expectedDuration' => 1000 157 | ), 158 | 'With same start- and endtime' => array( 159 | 'starttime' => 100, 160 | 'endtime' => 100, 161 | 'expectedDuration' => 0 162 | ), 163 | 'With both start- and endtime being zero' => array( 164 | 'starttime' => 0, 165 | 'endtime' => 0, 166 | 'expectedDuration' => 1000 167 | ), 168 | 'With endtime lower than starttime' => array( 169 | 'starttime' => 100, 170 | 'endtime' => 10, 171 | 'expectedDuration' => -90 172 | ), 173 | ); 174 | } 175 | 176 | /** 177 | * @test 178 | * @dataProvider isRunningReturnsBooleanDataProvider() 179 | */ 180 | public function isRunningReturnsBoolean($endtime, $isRunning) 181 | { 182 | $stub = $this->getMockBuilder('AOE\\SchedulerTimeline\\Domain\\Model\\Log') 183 | ->setMethods(['getEndtime']) 184 | ->disableOriginalConstructor() 185 | ->getMock(); 186 | $stub->expects($this->any())->method('getEndtime')->will($this->returnValue($endtime)); 187 | $this->assertSame( 188 | $isRunning, 189 | $stub->isRunning() 190 | ); 191 | } 192 | 193 | /** 194 | * @return array 195 | */ 196 | public function isRunningReturnsBooleanDataProvider() 197 | { 198 | return array( 199 | 'Endtime set to zero' => array( 200 | 'endtime' => 0, 201 | 'isRunning' => true 202 | ), 203 | 'Endtime set to null' => array( 204 | 'entime' => null, 205 | 'isRunning' => true 206 | ), 207 | 'Endtime set' => array( 208 | 'endtime' => '10', 209 | 'isRunning' => false 210 | ) 211 | ); 212 | } 213 | 214 | /** 215 | * @test 216 | * @dataProvider getFormattedDateFromTimestampReturnsStringDataProvider() 217 | */ 218 | public function getFormattedDateFromTimestampReturnsString($timestamp, $expectedFormattedDate) 219 | { 220 | $stub = $this->getAccessibleMock('AOE\\SchedulerTimeline\\Domain\\Model\\Log', array('dummy')); 221 | 222 | $this->assertSame( 223 | $expectedFormattedDate, 224 | $stub->_call('getFormattedDateFromTimestamp', $timestamp) 225 | ); 226 | } 227 | 228 | /** 229 | * @return array 230 | */ 231 | public function getFormattedDateFromTimestampReturnsStringDataProvider() 232 | { 233 | return array( 234 | 'Empty timestamp' => array( 235 | 'timestamp' => 0, 236 | 'expectedFormattedDate' => '00:00' 237 | ), 238 | 'Timestamp 2015-10-13 16:26' => array( 239 | 'timestamp' => strtotime('2015-10-13 16:26 UTC'), 240 | 'expectedFormattedDate' => '16:26' 241 | ), 242 | 'Timestamp 2015-11-13 10:13' => array( 243 | 'timestamp' => strtotime('2015-11-13 10:13 UTC'), 244 | 'expectedFormattedDate' => '10:13' 245 | ), 246 | ); 247 | } 248 | 249 | /** 250 | * @test 251 | * 252 | * @param $isRunning 253 | * @param $starttime 254 | * @param $starttimeFormatted 255 | * @param $endtime 256 | * @param $endtimeFormatted 257 | * @param $expectedString 258 | * 259 | * @dataProvider getTimespanReturnsStringDataProvider() 260 | */ 261 | public function getTimespanReturnsString($isRunning, $starttime, $starttimeFormatted, $endtime, $endtimeFormatted, $expectedString) 262 | { 263 | $stub = $this->getAccessibleMock( 264 | 'AOE\\SchedulerTimeline\\Domain\\Model\\Log', 265 | array('isRunning', 'getStarttime', 'getEndtime', 'getFormattedDateFromTimestamp') 266 | ); 267 | $stub->expects($this->any())->method('isRunning')->will($this->returnValue($isRunning)); 268 | $stub->expects($this->any())->method('getStarttime')->will($this->returnValue($starttime)); 269 | $stub->expects($this->any())->method('getEndtime')->will($this->returnValue($endtime)); 270 | 271 | $returnMap = array( 272 | array($starttime, $starttimeFormatted), 273 | array($endtime, $endtimeFormatted) 274 | ); 275 | 276 | $stub->expects($this->any())->method('getFormattedDateFromTimestamp')->will($this->returnValueMap($returnMap)); 277 | 278 | $this->assertSame( 279 | $expectedString, 280 | $stub->getTimespan() 281 | ); 282 | } 283 | 284 | /** 285 | * @return array 286 | */ 287 | public function getTimespanReturnsStringDataProvider() 288 | { 289 | return array( 290 | 'Task zero as starttime, task still running no endtime' => array( 291 | 'isRunning' => true, 292 | 'starttime' => 0, 293 | 'starttimeFormatted' => '00:00', 294 | 'endtime' => null, 295 | 'endtimeFormatted' => null, 296 | 'expectedString' => '00:00 - (still running)' 297 | ), 298 | 'Task with starttime 2015-10-13 16:26 and no endtime' => array( 299 | 'isRunning' => true, 300 | 'starttime' => strtotime('2015-10-13 16:26'), 301 | 'starttimeFormatted' => '16:26', 302 | 'endtime' => null, 303 | 'endtimeFormatted' => null, 304 | 'expectedString' => '16:26 - (still running)' 305 | ), 306 | 'Task with starttime 2015-11-13 9:26 and endtime 2015-11-13 10:13' => array( 307 | 'isRunning' => false, 308 | 'starttime' => strtotime('2015-11-13 9:26'), 309 | 'starttimeFormatted' => '09:26', 310 | 'endtime' => strtotime('2015-11-13 10:13'), 311 | 'endtimeFormatted' => '10:13', 312 | 'expectedString' => '09:26 - 10:13' 313 | ), 314 | ); 315 | } 316 | 317 | /** 318 | * @test 319 | * @dataProvider getStatusReturnStringDataProvider() 320 | */ 321 | public function getStatusReturnString($exception, $isRunning, $expectedStatus) 322 | { 323 | $stub = $this->getAccessibleMock('AOE\\SchedulerTimeline\\Domain\\Model\\Log', array('isRunning', 'getException')); 324 | $stub->expects($this->any())->method('isRunning')->will($this->returnValue($isRunning)); 325 | $stub->expects($this->any())->method('getException')->will($this->returnValue($exception)); 326 | 327 | $this->assertSame( 328 | $expectedStatus, 329 | $stub->getStatus() 330 | ); 331 | } 332 | 333 | /** 334 | * @return array 335 | */ 336 | public function getStatusReturnStringDataProvider() 337 | { 338 | return array( 339 | 'Task is still running' => array( 340 | 'exception' => null, 341 | 'isRunning' => true, 342 | 'expectedStatus' => \AOE\SchedulerTimeline\Domain\Model\Log::STATUS_RUNNING 343 | ), 344 | 'Task is done' => array( 345 | 'exception' => null, 346 | 'isRunning' => false, 347 | 'expectedStatus' => \AOE\SchedulerTimeline\Domain\Model\Log::STATUS_SUCCESS 348 | ), 349 | 'Task failed with exception' => array( 350 | 'exception' => 's:19:"NotWorkingException"', 351 | 'isRunning' => false, 352 | 'expectedStatus' => \AOE\SchedulerTimeline\Domain\Model\Log::STATUS_ERROR 353 | ) 354 | ); 355 | } 356 | } 357 | -------------------------------------------------------------------------------- /Documentation/_make/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Note that not all possible configuration values are present in this 4 | # autogenerated file. 5 | # 6 | # All configuration values have a default; values that are commented out 7 | # serve to show the default. 8 | 9 | import sys 10 | import os 11 | import json 12 | from datetime import date 13 | 14 | # import composer.json 15 | 16 | with open('../../composer.json') as data_file: 17 | composer = json.load(data_file) 18 | 19 | # If extensions (or modules to document with autodoc) are in another directory, 20 | # add these directories to sys.path here. If the directory is relative to the 21 | # documentation root, use os.path.abspath to make it absolute, like shown here. 22 | #sys.path.insert(0, os.path.abspath('.')) 23 | 24 | # -- PHP highlighting configuration -------------------------------------------- 25 | 26 | from sphinx.highlighting import lexers 27 | if lexers: 28 | from pygments.lexers.web import PhpLexer 29 | lexers['php'] = PhpLexer(startinline=True) 30 | 31 | # -- General configuration ----------------------------------------------------- 32 | 33 | # If your documentation needs a minimal Sphinx version, state it here. 34 | #needs_sphinx = '1.0' 35 | 36 | # Add any Sphinx extension module names here, as strings. They can be 37 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 38 | # ones. 39 | extensions = ['sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.ifconfig'] 40 | 41 | # Add any paths that contain templates here, relative to this directory. 42 | templates_path = ['../_templates'] 43 | 44 | # The suffix of source filenames. 45 | source_suffix = '.rst' 46 | 47 | # The encoding of source files. 48 | #source_encoding = 'utf-8-sig' 49 | 50 | # The master toctree document. 51 | master_doc = 'Index' 52 | 53 | # General information about the project. 54 | project = composer["title"] 55 | copyright = str(date.today().year) + ' AOE GmbH' 56 | 57 | # The version info for the project you're documenting, acts as replacement for 58 | # |version| and |release|, also used in various other places throughout the 59 | # built documents. 60 | # 61 | # The short X.Y version. 62 | version = '0.4' 63 | # The full version, including alpha/beta/rc tags. 64 | release = composer["version"] 65 | 66 | # The language for content autogenerated by Sphinx. Refer to documentation 67 | # for a list of supported languages. 68 | #language = None 69 | 70 | # There are two options for replacing |today|: either, you set today to some 71 | # non-false value, then it is used: 72 | #today = '' 73 | # Else, today_fmt is used as the format for a strftime call. 74 | today_fmt = '%Y-%m-%d %H:%M' 75 | 76 | # List of patterns, relative to source directory, that match files and 77 | # directories to ignore when looking for source files. 78 | exclude_patterns = ['_make'] 79 | exclude_trees = ['_make'] 80 | 81 | # The reST default role (used for this markup: `text`) to use for all 82 | # documents. 83 | #default_role = None 84 | 85 | # If true, '()' will be appended to :func: etc. cross-reference text. 86 | #add_function_parentheses = True 87 | 88 | # If true, the current module name will be prepended to all description 89 | # unit titles (such as .. function::). 90 | #add_module_names = True 91 | 92 | # If true, sectionauthor and moduleauthor directives will be shown in the 93 | # output. They are ignored by default. 94 | #show_authors = False 95 | 96 | # The name of the Pygments (syntax highlighting) style to use. 97 | pygments_style = 'sphinx' 98 | 99 | # A list of ignored prefixes for module index sorting. 100 | #modindex_common_prefix = [] 101 | 102 | # If true, keep warnings as "system message" paragraphs in the built documents. 103 | #keep_warnings = False 104 | 105 | 106 | # -- Options for HTML output ---------------------------------------------- 107 | 108 | # The theme to use for HTML and HTML Help pages. See the documentation for 109 | # a list of builtin themes. 110 | html_theme = 'default' 111 | 112 | # Theme options are theme-specific and customize the look and feel of a theme 113 | # further. For a list of options available for each theme, see the 114 | # documentation. 115 | #html_theme_options = {} 116 | 117 | # Add any paths that contain custom themes here, relative to this directory. 118 | html_theme_path = [] 119 | 120 | # The name for this set of Sphinx documents. If None, it defaults to 121 | # " v documentation". 122 | #html_title = None 123 | 124 | # A shorter title for the navigation bar. Default is the same as html_title. 125 | #html_short_title = None 126 | 127 | # The name of an image file (relative to this directory) to place at the top 128 | # of the sidebar. 129 | #html_logo = None 130 | 131 | # The name of an image file (within the static path) to use as favicon of the 132 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 133 | # pixels large. 134 | #html_favicon = None 135 | 136 | # Add any paths that contain custom static files (such as style sheets) here, 137 | # relative to this directory. They are copied after the builtin static files, 138 | # so a file named "default.css" will overwrite the builtin "default.css". 139 | html_static_path = ['../_static'] 140 | 141 | # Add any extra paths that contain custom files (such as robots.txt or 142 | # .htaccess) here, relative to this directory. These files are copied 143 | # directly to the root of the documentation. 144 | #html_extra_path = [] 145 | 146 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 147 | # using the given strftime format. 148 | #html_last_updated_fmt = '%b %d, %Y' 149 | 150 | # If true, SmartyPants will be used to convert quotes and dashes to 151 | # typographically correct entities. 152 | #html_use_smartypants = True 153 | 154 | # Custom sidebar templates, maps document names to template names. 155 | #html_sidebars = {} 156 | 157 | # Additional templates that should be rendered to pages, maps page names to 158 | # template names. 159 | #html_additional_pages = {} 160 | 161 | # If false, no module index is generated. 162 | #html_domain_indices = True 163 | 164 | # If false, no index is generated. 165 | #html_use_index = True 166 | 167 | # If true, the index is split into individual pages for each letter. 168 | #html_split_index = False 169 | 170 | # If true, links to the reST sources are added to the pages. 171 | #html_show_sourcelink = True 172 | 173 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 174 | #html_show_sphinx = True 175 | 176 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 177 | #html_show_copyright = True 178 | 179 | # If true, an OpenSearch description file will be output, and all pages will 180 | # contain a tag referring to it. The value of this option must be the 181 | # base URL from which the finished HTML is served. 182 | #html_use_opensearch = '' 183 | 184 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 185 | #html_file_suffix = None 186 | 187 | # Output file base name for HTML help builder. 188 | htmlhelp_basename = 'felogin_bruteforce_protectiondoc' 189 | 190 | 191 | # -- Options for LaTeX output -------------------------------------------------- 192 | 193 | latex_elements = { 194 | # The paper size ('letterpaper' or 'a4paper'). 195 | #'papersize': 'letterpaper', 196 | 197 | # The font size ('10pt', '11pt' or '12pt'). 198 | #'pointsize': '10pt', 199 | 200 | # Additional stuff for the LaTeX preamble. 201 | 'preamble': '\\usepackage{typo3}' 202 | } 203 | 204 | # Grouping the document tree into LaTeX files. List of tuples 205 | # (source start file, target name, title, 206 | # author, documentclass [howto, manual, or own class]). 207 | latex_documents = [ 208 | ('Index', 'felogin_bruteforce_protection.tex', u'Brute Force Protection', 209 | u'Kevin Schu, Andre Wuttig', 'manual'), 210 | ] 211 | 212 | # The name of an image file (relative to this directory) to place at the top of 213 | # the title page. 214 | #latex_logo = None 215 | 216 | # For "manual" documents, if this is true, then toplevel headings are parts, 217 | # not chapters. 218 | #latex_use_parts = False 219 | 220 | # If true, show page references after internal links. 221 | #latex_show_pagerefs = False 222 | 223 | # If true, show URL addresses after external links. 224 | #latex_show_urls = False 225 | 226 | # Documents to append as an appendix to all manuals. 227 | #latex_appendices = [] 228 | 229 | # If false, no module index is generated. 230 | #latex_domain_indices = True 231 | 232 | 233 | # -- Options for rst2pdf output ------------------------------------------------ 234 | 235 | # Grouping the document tree into PDF files. List of tuples 236 | # (source start file, target name, title, author, options). 237 | # 238 | # If there is more than one author, separate them with \\. 239 | # For example: r'Guido van Rossum\\Fred L. Drake, Jr., editor' 240 | # 241 | # The options element is a dictionary that lets you override 242 | # this config per-document. 243 | # For example, 244 | # ('index', u'MyProject', u'My Project', u'Author Name', 245 | # dict(pdf_compressed = True)) 246 | # would mean that specific document would be compressed 247 | # regardless of the global pdf_compressed setting. 248 | pdf_documents = [ 249 | ('Index', 'felogin_bruteforce_protection', u'Brute Force Protection', 250 | u'Kevin Schu, Andre Wuttig'), 251 | ] 252 | 253 | # A comma-separated list of custom stylesheets. Example: 254 | pdf_stylesheets = ['sphinx','kerning','a4'] 255 | 256 | # A list of folders to search for stylesheets. Example: 257 | pdf_style_path = ['.', '_styles'] 258 | 259 | # Create a compressed PDF 260 | # Use True/False or 1/0 261 | # Example: compressed=True 262 | #pdf_compressed = False 263 | 264 | # A colon-separated list of folders to search for fonts. Example: 265 | # pdf_font_path = ['/usr/share/fonts', '/usr/share/texmf-dist/fonts/'] 266 | 267 | # Language to be used for hyphenation support 268 | #pdf_language = "en_US" 269 | 270 | # Mode for literal blocks wider than the frame. Can be 271 | # overflow, shrink or truncate 272 | #pdf_fit_mode = "shrink" 273 | 274 | # Section level that forces a break page. 275 | # For example: 1 means top-level sections start in a new page 276 | # 0 means disabled 277 | #pdf_break_level = 0 278 | 279 | # When a section starts in a new page, force it to be 'even', 'odd', 280 | # or just use 'any' 281 | #pdf_breakside = 'any' 282 | 283 | # Insert footnotes where they are defined instead of 284 | # at the end. 285 | #pdf_inline_footnotes = True 286 | 287 | # verbosity level. 0 1 or 2 288 | #pdf_verbosity = 0 289 | 290 | # If false, no index is generated. 291 | #pdf_use_index = True 292 | 293 | # If false, no modindex is generated. 294 | #pdf_use_modindex = True 295 | 296 | # If false, no coverpage is generated. 297 | #pdf_use_coverpage = True 298 | 299 | # Name of the cover page template to use 300 | #pdf_cover_template = 'sphinxcover.tmpl' 301 | 302 | # Documents to append as an appendix to all manuals. 303 | #pdf_appendices = [] 304 | 305 | # Enable experimental feature to split table cells. Use it 306 | # if you get "DelayedTable too big" errors 307 | #pdf_splittables = False 308 | 309 | # Set the default DPI for images 310 | #pdf_default_dpi = 72 311 | 312 | # Enable rst2pdf extension modules (default is only vectorpdf) 313 | # you need vectorpdf if you want to use sphinx's graphviz support 314 | #pdf_extensions = ['vectorpdf'] 315 | 316 | # Page template name for "regular" pages 317 | #pdf_page_template = 'cutePage' 318 | 319 | # Show Table Of Contents at the beginning? 320 | #pdf_use_toc = True 321 | 322 | # How many levels deep should the table of contents be? 323 | pdf_toc_depth = 9999 324 | 325 | # Add section number to section references 326 | pdf_use_numbered_links = False 327 | 328 | # Background images fitting mode 329 | pdf_fit_background_mode = 'scale' 330 | 331 | 332 | # -- Options for manual page output --------------------------------------- 333 | 334 | # One entry per manual page. List of tuples 335 | # (source start file, name, description, authors, manual section). 336 | man_pages = [ 337 | ('Index', 'felogin_bruteforce_protection', u'Brute Force Protection', 338 | [u'Kevin Schu, Andre Wuttig'], 1) 339 | ] 340 | 341 | # If true, show URL addresses after external links. 342 | #man_show_urls = False 343 | 344 | 345 | # -- Options for Texinfo output ------------------------------------------- 346 | 347 | # Grouping the document tree into Texinfo files. List of tuples 348 | # (source start file, target name, title, author, 349 | # dir menu entry, description, category) 350 | texinfo_documents = [ 351 | ('Index', 'felogin_bruteforce_protection', u'Brute Force Protection', 352 | u'Kevin Schu, Andre Wuttig', 'Brute Force Protection', 'One line description of project.', 353 | 'Miscellaneous'), 354 | ] 355 | 356 | # Documents to append as an appendix to all manuals. 357 | #texinfo_appendices = [] 358 | 359 | # If false, no module index is generated. 360 | #texinfo_domain_indices = True 361 | 362 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 363 | #texinfo_show_urls = 'footnote' 364 | 365 | # If true, do not generate a @detailmenu in the "Top" node's menu. 366 | #texinfo_no_detailmenu = False 367 | 368 | 369 | #================================================= 370 | # 371 | # TYPO3 codeblock BEGIN: 372 | # 373 | # Insert this codeblock at the end of your Sphinx 374 | # builder configuration file 'conf.py'. 375 | # This may enable TYPO3 specific features like 376 | # TYPO3 themes. It makes Yaml settings files work. 377 | # 378 | #------------------------------------------------- 379 | 380 | if 1 and "TYPO3 specific": 381 | 382 | try: 383 | t3DocTeam 384 | except NameError: 385 | t3DocTeam = {} 386 | 387 | try: 388 | import t3sphinx 389 | html_theme_path.insert(0, t3sphinx.themes_dir) 390 | html_theme = 'typo3sphinx' 391 | except: 392 | html_theme = 'default' 393 | 394 | t3DocTeam['conf_py_file'] = None 395 | try: 396 | t3DocTeam['conf_py_file'] = __file__ 397 | except: 398 | import inspect 399 | t3DocTeam['conf_py_file'] = inspect.getfile( 400 | inspect.currentframe()) 401 | 402 | t3DocTeam['conf_py_package_dir'] = os.path.abspath(os.path.dirname( 403 | t3DocTeam['conf_py_file'])) 404 | t3DocTeam['relpath_to_master_doc'] = '..' 405 | t3DocTeam['relpath_to_logdir'] = '_not_versioned' 406 | t3DocTeam['path_to_logdir'] = os.path.join( 407 | t3DocTeam['conf_py_package_dir'], 408 | t3DocTeam['relpath_to_logdir']) 409 | t3DocTeam['pathToYamlSettings'] = os.path.join( 410 | t3DocTeam['conf_py_package_dir'], 411 | t3DocTeam['relpath_to_master_doc'], 'Settings.yml') 412 | try: 413 | t3DocTeam['pathToGlobalYamlSettings'] = \ 414 | t3sphinx.pathToGlobalYamlSettings 415 | except: 416 | t3DocTeam['pathToGlobalYamlSettings'] = None 417 | if not t3DocTeam['pathToGlobalYamlSettings']: 418 | t3DocTeam['pathToGlobalYamlSettings'] = os.path.join( 419 | t3DocTeam['conf_py_package_dir'], 'GlobalSettings.yml') 420 | try: 421 | __function = t3sphinx.yamlsettings.processYamlSettings 422 | except: 423 | __function = None 424 | if not __function: 425 | try: 426 | import yamlsettings 427 | __function = yamlsettings.processYamlSettings 428 | except: 429 | __function = None 430 | if __function: 431 | __function(globals(), t3DocTeam) 432 | 433 | #------------------------------------------------- 434 | # 435 | # TYPO3 codeblock END. 436 | # 437 | #================================================= -------------------------------------------------------------------------------- /Documentation/LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | --------------------------------------------------------------------------------