Feature #11555: Integrated extension "gabriel" into TYPO3 Core as sysext "scheduler"
authorFrancois Suter <francois.suter@typo3.org>
Tue, 15 Sep 2009 21:19:39 +0000 (21:19 +0000)
committerFrancois Suter <francois.suter@typo3.org>
Tue, 15 Sep 2009 21:19:39 +0000 (21:19 +0000)
git-svn-id: https://svn.typo3.org/TYPO3v4/Core/trunk@5933 709f56b5-9817-0410-a4d7-c38de5d9e867

39 files changed:
ChangeLog
NEWS.txt
typo3/sysext/scheduler/ChangeLog [new file with mode: 0755]
typo3/sysext/scheduler/README.txt [new file with mode: 0755]
typo3/sysext/scheduler/class.tx_scheduler.php [new file with mode: 0755]
typo3/sysext/scheduler/class.tx_scheduler_croncmd.php [new file with mode: 0644]
typo3/sysext/scheduler/class.tx_scheduler_execution.php [new file with mode: 0644]
typo3/sysext/scheduler/class.tx_scheduler_failedexecutionexception.php [new file with mode: 0644]
typo3/sysext/scheduler/class.tx_scheduler_task.php [new file with mode: 0755]
typo3/sysext/scheduler/cli/scheduler_cli_dispatch.php [new file with mode: 0644]
typo3/sysext/scheduler/doc/manual.sxw [new file with mode: 0644]
typo3/sysext/scheduler/examples/class.tx_scheduler_sleeptask.php [new file with mode: 0644]
typo3/sysext/scheduler/examples/class.tx_scheduler_sleeptask_additionalfieldprovider.php [new file with mode: 0644]
typo3/sysext/scheduler/examples/class.tx_scheduler_testtask.php [new file with mode: 0644]
typo3/sysext/scheduler/examples/class.tx_scheduler_testtask_additionalfieldprovider.php [new file with mode: 0644]
typo3/sysext/scheduler/ext_autoload.php [new file with mode: 0644]
typo3/sysext/scheduler/ext_conf_template.txt [new file with mode: 0644]
typo3/sysext/scheduler/ext_emconf.php [new file with mode: 0755]
typo3/sysext/scheduler/ext_icon.gif [new file with mode: 0644]
typo3/sysext/scheduler/ext_localconf.php [new file with mode: 0644]
typo3/sysext/scheduler/ext_tables.php [new file with mode: 0755]
typo3/sysext/scheduler/ext_tables.sql [new file with mode: 0755]
typo3/sysext/scheduler/interfaces/interface.tx_scheduler_additionalfieldprovider.php [new file with mode: 0644]
typo3/sysext/scheduler/locallang.xml [new file with mode: 0644]
typo3/sysext/scheduler/mod1/clear.gif [new file with mode: 0755]
typo3/sysext/scheduler/mod1/conf.php [new file with mode: 0755]
typo3/sysext/scheduler/mod1/index.php [new file with mode: 0755]
typo3/sysext/scheduler/mod1/locallang.xml [new file with mode: 0644]
typo3/sysext/scheduler/mod1/locallang_csh_scheduler.xml [new file with mode: 0644]
typo3/sysext/scheduler/mod1/locallang_mod.xml [new file with mode: 0644]
typo3/sysext/scheduler/mod1/mod_template.html [new file with mode: 0644]
typo3/sysext/scheduler/mod1/moduleicon.gif [new file with mode: 0755]
typo3/sysext/scheduler/res/gfx/status_disabled.png [new file with mode: 0644]
typo3/sysext/scheduler/res/gfx/status_failure.png [new file with mode: 0644]
typo3/sysext/scheduler/res/gfx/status_late.png [new file with mode: 0644]
typo3/sysext/scheduler/res/gfx/status_running.png [new file with mode: 0644]
typo3/sysext/scheduler/res/gfx/status_scheduled.png [new file with mode: 0644]
typo3/sysext/scheduler/res/tx_scheduler_be.css [new file with mode: 0644]
typo3/sysext/scheduler/res/tx_scheduler_be.js [new file with mode: 0644]

index c5b09ed..a26e76f 100755 (executable)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2009-09-15  Francois Suter  <francois@typo3.org>
+
+       * Feature #11555: Integrated extension "gabriel" into TYPO3 Core as sysext "scheduler"
+
 2009-09-15  Stanislas Rolland  <typo3@sjbr.ca>
 
        * Fixed bug #11915: htmlArea RTE: superfluous span tags in content after server-based cleaning on paste operation
index 17f0064..d513ba1 100644 (file)
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -106,6 +106,13 @@ Backend
          and $TYPO3_CONF_VARS['BE']['XLLfile']['EXT:lang/locallang_login.xml']. These will only be used
          once you remove the altered "loginLabels" configuration option from typo3conf/localconf.php.
 
+       * The extension "gabriel" has been integrated into the TYPO3 core as system extension "scheduler".
+         It provides a centralized way of defining scheduled, recurring tasks, with a convenient
+         interface to manage them (BE module). The aim is to have a single cron job which just launches
+         the Scheduler, which in turn takes care of executing all due tasks.
+         Extension developers are strongly encouraged to turn their existing cron scripts
+         into Scheduler tasks.
+
 Frontend
 ========
 
diff --git a/typo3/sysext/scheduler/ChangeLog b/typo3/sysext/scheduler/ChangeLog
new file mode 100755 (executable)
index 0000000..46dcd5e
--- /dev/null
@@ -0,0 +1,163 @@
+2009-09-15  Francois Suter  <francois@typo3.org>
+
+       * Replaced usage of t3lib_div::getUserObj() by t3lib_div::makeInstance() in the CLI script, resolves #4644
+       * Harmonized the use of uppercase and lowercase letters in all class names, resolves #4645
+       * Committed to TYPO3 Core
+
+2009-09-14  Francois Suter  <francois@typo3.org>
+
+       * Replaced usage of cal_days_in_month() by date(), resolves #4618
+       * Completed the manual, resolves #4398
+
+2009-09-13  Francois Suter  <francois@typo3.org>
+
+       * Added link to create user when _cli_scheduler user doesn't exist, resolves #4571
+
+2009-09-11  Francois Suter  <francois@typo3.org>
+
+       * More improvements to check screen messages, resolves #4570
+       * Added test to catch empty tasks registration list, resolves #4572 (thanks to Georg Ringer)
+       * Corrected syntax of extension configuration file
+       * Changed the "check/uncheck all" checkbox in list view to use TYPO3's standard icon, resolves #4594
+
+2009-09-10  Francois Suter  <francois@typo3.org>
+
+       * Added ExtJS datepicker to edit form date fields, resolves #3663 (thanks to Steffen Kamper)
+
+2009-09-07  Francois Suter  <francois@typo3.org>
+
+       * Removed display of next execution date from list view when task is disabled, resolves #4480
+       * Added missing id attribute to checkbox fields in add/edit form, resolves #4468 (thanks to Georg Ringer)
+       * Improved wording of error message for invalid frequency, resolves #4474
+       * Removed unused variables and corrected one typo, resolves #4481
+
+2009-09-06  Francois Suter  <francois@typo3.org>
+
+       * Follow-up to #4397: changed registry usage to store also type of run. Registry is now used both by manual executions and CLI ones. Display of information moved to check screen in BE module.
+       * Changed BE module's check screen to use only flash messages, resolves #4465
+       * Made disabled status legend more explicit, resolves #4464
+       * Added t3lib_db::sql_free_result() calls wherever missing, resolves #4449
+       * Removed highlighting for late tasks (in list view) when taks is disabled, resolves #4466
+
+2009-09-03  Francois Suter  <francois@typo3.org>
+
+       * Follow-up to #4397: changed registry usage to store start and end of last automated run. Added display of said information in BE module.
+       * Follow-up to #4385: changed the example class names for additional providers to use "AdditionalFieldProvider" instead of just "provider"
+
+2009-08-31  Ingo Renner  <ingo@typo3.org>
+
+       * Fixed usage of t3lib_FlashMessage severity constants instead of magic numbers
+       * Renamed tx_scheduler_Task::displayAdditionalInformation() to getAdditionalInformation(), resolves #4404
+       * Forced the edit and delete icons to appear in one line
+
+2009-08-30  Ingo Renner  <ingo@typo3.org>
+
+       * Added storing the timestamp of the last run in a registry entry, resolves #4397
+
+2009-08-28  Francois Suter  <francois@typo3.org>
+
+       * Added "doNotLoadInFE" flag to extension configuration, resolves #4372
+       * Removed the whole CRID and additional parameters concept. In the DB, "crid" is replaced by "classname". Resolves #4302
+       * Cleaned up and corrected usage of listTasks() in BE module
+       * Added display of task UID in "task not found" message, resolves #4377
+       * Added check to hide status legend in BE module list view when there are no tasks, resolves #4374
+       * Removed a useless call to tx_scheduler_Task::setTask() in BE module, resolves #4376
+       * Added missing localized label for sleep time validation error
+       * Cleaned up task registration structure, resolves #4267
+       * Made some cosmetic clean up in phpDoc comments of interface tx_scheduler_AdditionalFieldProvider
+       * Renamed hook classes and files to use the word "provider" instead, resolves #4385
+       * Cleaned up BE module's locallang file: removed unused labels, renamed some labels for consistency, restored alphabetical sorting of labels, resolves #4386
+
+2009-08-27  Francois Suter  <francois@typo3.org>
+
+       * Follow-up to #4270: cleaned up code, CSS and images related to status messages (all part of core now)
+       * Follow-up to #4270: changed check screen to use flash message styles
+       * Reverted abusive usage of $GLOBALS['EXEC_TIME'] instead of time(), resolves #4363
+       * Hid next execution date from list view (BE module) when task is running, resolves #4364
+       * Follow-up to #4020: renamed some statuses for increased consistency and improved some legend labels
+
+2009-08-26  Ingo Renner  <ingo@typo3.org>
+
+       * Changed the current message implementation to use the core's t3lib_FlashMessages, resolves #4270
+
+2009-08-25  Francois Suter  <francois@typo3.org>
+
+       * Replaced usage of tx_scheduler_Task::isExecutionRunning() in BE module when information is already available in fetched DB record
+       * Disabled edit and delete actions when a task is running, resolves #4275
+
+2009-08-24  Francois Suter  <francois@typo3.org>
+
+       * Refactored the execution process for cleaner error reporting, resolves #4312
+       * Follow-up to #4019: cleaned up the display of execution failures
+
+2009-08-22  Francois Suter  <francois@typo3.org>
+
+       * Removed the execution pool from a task, replaced it with a single instance of an execution. Cleaned up the API as a consequence, fixed the calculation of the next execution date and marked task with a finished schedule as disabled. Resolves #4263, #4264 and #4308
+
+2009-08-21  Ingo Renner  <ingo@typo3.org>
+
+       * Added a way to detect whether a task ran successfully, resolves #4019
+
+2009-08-19  Francois Suter  <francois@typo3.org>
+
+       * Corrected tx_scheduler::fetchTask() to get tasks that need to run exactly now and not just in the past, should resolve #4224
+
+2009-08-17  Francois Suter  <francois@typo3.org>
+
+       * Added a check for selected task class when adding/editing a class, resolves #4047
+
+2009-08-17  Ingo Renner  <ingo@typo3.org>
+
+       * Added an indicator for disabled tasks, resolves #4261
+
+2009-08-16  Francois Suter  <francois@typo3.org>
+
+       * Dropped use of complete filename in task registration, since autoloading is used anyway, resolves #4039
+       * Completed Scheduler task API and cleaned up its usage, resolves #4255
+       * Renamed commands to parameters in Scheduler task for consistent naming, resolves #4254
+       * Added possibility to disable a scheduled task, resolves #4199
+       * Cleaned up the handling of values that are either task member variables or database fields. Necessary step for solving #4199
+       * Some CGL cleanups and comments corrections
+
+2009-08-11  Francois Suter  <francois@typo3.org>
+
+       * Corrected and completed description of CRID, resolves #4045
+
+2009-08-10  Francois Suter  <francois@typo3.org>
+
+       * Added description of CRID, resolves #4045
+
+2009-08-09  Francois Suter  <francois@typo3.org>
+
+       * Removed redundant link for adding new task, follow-up to #4043
+       * Solved bug #4152: Example task hooks do not keep their value when adding or editing (Thanks to Bastian Waidelich)
+       * Corrected all hook names according to new interface for additional fields, follow-up to #4048
+       * Solved bug #4134: Error in cron cmd calculation (Thanks for Markus Friedrich)
+       * Cleaned up BE module's CSS, resolves #4132
+       * Fixed bug #4194: Error running the test task
+       * Fixed bug #4195: Remove running flag from execution class
+       * Made the scheduler instance a member variable in the BE module, resolves #4133
+
+2009-08-05  Ingo Renner  <ingo@typo3.org>
+
+       * Added a title for the next execution column field when a task is late
+       * Changed the backend module icon
+       * Added an interface for classes that provide additional fields to the add/edit task form, resolves #4048
+
+2009-08-04  Ingo Renner  <ingo@typo3.org>
+
+       * Changed select for multiple executions to a checkbox, resolves #4126
+       * Added a link to add tasks, also when tasks have been configured, resolves #4043
+       * Added an indicator to show whether tasks are stopped, scheduled, or running, resolves #4020
+       * Added a refresh button to the docheader
+
+2009-08-03  Ingo Renner  <ingo@typo3.org>
+
+       * Changed blob fields to text, resolves #4046
+       * Renamed samples folder to examples, resolves #4040
+       * Renamed class tx_scheduler_event to tx_scheduler_Task, changed all occurrences of event to task, resolves #4038
+       * Renamed DB table tx_scheduler to tx_scheduler_task to respect namespacing, resolves #4044
+
+2009-07-17  Francois Suter  <francois@typo3.org>
+
+       * Submitted RFC for integration into TYPO3 Core
diff --git a/typo3/sysext/scheduler/README.txt b/typo3/sysext/scheduler/README.txt
new file mode 100755 (executable)
index 0000000..ef599d9
--- /dev/null
@@ -0,0 +1 @@
+The TYPO3 Scheduler makes it possible to manage cron jobs in a centralized way. Please refer to the manual for more details.
\ No newline at end of file
diff --git a/typo3/sysext/scheduler/class.tx_scheduler.php b/typo3/sysext/scheduler/class.tx_scheduler.php
new file mode 100755 (executable)
index 0000000..d603d6e
--- /dev/null
@@ -0,0 +1,388 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2005 Christian Jul Jensen <julle@typo3.org>
+*
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+/**
+ * TYPO3 Scheduler. This class handles scheduling and execution of tasks.
+ * Formerly known as "Gabriel TYPO3 arch angel"
+ *
+ * @author     Francois Suter <francois@typo3.org>
+ * @author     Christian Jul Jensen <julle@typo3.org>
+ *
+ * @package            TYPO3
+ * @subpackage tx_scheduler
+ *
+ * $Id: class.tx_scheduler.php 1198 2009-09-06 21:11:11Z francois $
+ */
+
+class tx_scheduler implements t3lib_Singleton {
+       /**
+        * @var array           $extConf: settings from the extension manager
+        */
+        var $extConf = array();
+
+       /**
+        * Constructor, makes sure all derived client classes are included
+        *
+        * @return      void
+        */
+       public function __construct() {
+                       // Get configuration from the extension manager
+               $this->extConf = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['scheduler']);
+               if (empty($this->extConf['maxLifetime'])) {
+                       $this->extConf['maxLifetime'] = 1440;
+               }
+
+                       // Clean up the serialized execution arrays
+               $this->cleanExecutionArrays();
+       }
+
+       /**
+        * Adds a task to the pool
+        *
+        * @param       tx_scheduler_Task       $task: the object representing the task to add
+        * @param       string                          $identifier: the identified of the task
+        * @return      boolean                         True if the task was successfully added, false otherwise
+        */
+       public function addTask(tx_scheduler_Task $task) {
+               $taskUid = $task->getTaskUid();
+               if (empty($taskUid)) {
+                       $fields = array(
+                               'crdate' => $GLOBALS['EXEC_TIME'],
+                               'classname' => get_class($task),
+                               'disable' => $task->isDisabled(),
+                               'serialized_task_object' => 'RESERVED'
+                       );
+                       $result = $GLOBALS['TYPO3_DB']->exec_INSERTquery('tx_scheduler_task', $fields);
+                       if ($result) {
+                               $task->setTaskUid($GLOBALS['TYPO3_DB']->sql_insert_id());
+                               $task->save();
+                               $result = true;
+                       } else {
+                               $result = false;
+                       }
+               } else {
+                       $result = false;
+               }
+               return $result;
+       }
+
+       /**
+        * Cleans the execution lists of the scheduled tasks, executions older than 24h are removed
+        * TODO: find a way to actually kill the job
+        *
+        * @return      void
+        */
+       protected function cleanExecutionArrays() {
+               $tstamp = $GLOBALS['EXEC_TIME'];
+
+                       // Select all tasks with executions
+                       // NOTE: this cleanup is done for disabled tasks too,
+                       // to avoid leaving old executions lying around
+               $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
+                       'uid, classname, serialized_executions',
+                       'tx_scheduler_task',
+                       'serialized_executions != \'\''
+               );
+
+               $maxDuration = $this->extConf['maxLifetime'] * 60;
+               while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
+                       if (($serialized_executions = unserialize($row['serialized_executions']))) {
+                               $executions = array();
+                               foreach ($serialized_executions AS $task) {
+                                       if (($tstamp - $task) < $maxDuration) {
+                                               $executions[] = $task;
+                                       } else {
+                                               $GLOBALS['BE_USER']->writelog(
+                                                       4,
+                                                       0,
+                                                       0,
+                                                       'scheduler',
+                                                       '[scheduler]: Removing logged execution, assuming that the process is dead. Execution of \'' . $row['classname'] . '\' (UID: ' . $row['uid']. ') was started at '.date('Y-m-d H:i:s', $task),
+                                                       array()
+                                               );
+                                       }
+                               }
+                       }
+
+                       if (count($serialized_executions) != count($executions)) {
+                               if (count($executions) == 0) {
+                                       $value = '';
+                               } else {
+                                       $value = serialize($executions);
+                               }
+
+                               $GLOBALS['TYPO3_DB']->exec_UPDATEquery(
+                                       'tx_scheduler_task',
+                                       'uid = ' . intval($row['uid']),
+                                       array('serialized_executions' => $value)
+                               );
+                       }
+               }
+               $GLOBALS['TYPO3_DB']->sql_free_result($res);
+       }
+
+       /**
+     * This method executes the given task and properly marks and records that execution
+        * It is expected to return false if the task was barred from running or if it was not saved properly
+        *
+        * @param       tx_scheduler_Task       $task: the task to execute
+        * @return      boolean                         Whether the task was saved succesfully to the database or not
+        */
+       public function executeTask(tx_scheduler_Task $task) {
+
+                       // Task is already running and multiple executions are not allowed
+               if (!$task->areMultipleExecutionsAllowed() && $task->isExecutionRunning()) {
+                               // Log multiple execution error
+                       $GLOBALS['BE_USER']->writelog(
+                               4,
+                               0,
+                               0,
+                               'scheduler',
+                               '[scheduler]: Task is already running and multiple executions are not allowed, skipping! Class: ' . get_class($task) . ', UID: ' . $task->getTaskUid(),
+                               array()
+                       );
+
+                       $result = false;
+
+                       // Task isn't running or multiple executions are allowed
+               } else {
+                               // Log scheduler invocation
+                       $GLOBALS['BE_USER']->writelog(
+                               4,
+                               0,
+                               0,
+                               'scheduler',
+                               '[scheduler]: Start execution. Class: ' . get_class($task) . ', UID: ' . $task->getTaskUid(),
+                               array()
+                       );
+
+                               // Register execution
+                       $executionID = $task->markExecution();
+
+                       $failure = null;
+                       try {
+                                       // Execute task
+                               $successfullyExecuted = $task->execute();
+
+                               if (!$successfullyExecuted) {
+                                       throw new tx_scheduler_FailedExecutionException(
+                                               'Task failed to execute successfully. Class: ' . get_class($task) . ', UID: ' . $task->getTaskUid(),
+                                               1250596541
+                                       );
+                               }
+                       } catch(Exception $e) {
+                                       // Store exception, so that it can be saved to database
+                               $failure = $e;
+                       }
+
+                               // Unregister excution
+                       $task->unmarkExecution($executionID, $failure);
+
+                               // Log completion of execution
+                       $GLOBALS['BE_USER']->writelog(
+                               4,
+                               0,
+                               0,
+                               'scheduler',
+                               '[scheduler]: Task executed. Class: ' . get_class($task). ', UID: ' . $task->getTaskUid(),
+                               array()
+                       );
+                       $result = $task->save();
+
+                               // Now that the result of the task execution has been handled,
+                               // throw the exception again, if any
+                       if ($failure instanceof Exception) {
+                               throw $failure;
+                       }
+               }
+
+               return $result;
+       }
+
+       /**
+        * This method stores information about the last run of the Scheduler into the system registry
+        * 
+        * @param       string  $type: Type of run (manual or command-line (assumed to be cron))
+        * @return      void
+        */
+       public function recordLastRun($type = 'cron') {
+                       // Validate input value
+               if ($type != 'manual') {
+                       $type = 'cron';
+               }
+
+               /**
+                * @var t3lib_Registry
+                */
+               $registry = t3lib_div::makeInstance('t3lib_Registry');
+               $runInformation = array('start' => $GLOBALS['EXEC_TIME'], 'end' => time(), 'type' => $type);
+               $registry->set('tx_scheduler', 'lastRun', $runInformation);
+       }
+
+       /**
+        * Removes a task completely from the system.
+        * TODO: find a way to actually kill the existing jobs
+        *
+        * @param       tx_scheduler_Task       $task: the object representing the task to delete
+        * @return      boolean                         True if task was successfully deleted, false otherwise
+        */
+       public function removeTask(tx_scheduler_Task $task) {
+               $taskUid = $task->getTaskUid();
+               if (!empty($taskUid)) {
+                       return $GLOBALS['TYPO3_DB']->exec_DELETEquery('tx_scheduler_task', 'uid = ' . $taskUid);
+               } else {
+                       return false;
+               }
+       }
+
+       /**
+        * Updates a task in the pool
+        *
+        * @param       tx_scheduler_Task       $task: Scheduler task object
+        * @return      boolean                         False if submitted task was not of proper class
+        */
+       public function saveTask(tx_scheduler_Task $task) {
+               $taskUid = $task->getTaskUid();
+               if (!empty($taskUid)) {
+                       try {
+                               $executionTime = $task->getNextDueExecution();
+                               $task->setExecutionTime($executionTime);
+                       }
+                       catch (Exception $e) {
+                               $task->setDisabled(true);
+                               $executionTime = 0;
+                       }
+                       $task->unsetScheduler();
+                       $fields = array(
+                               'nextexecution' => $executionTime,
+                               'classname' => get_class($task),
+                               'disable' => $task->isDisabled(),
+                               'serialized_task_object' => serialize($task)
+                       );
+                       return $GLOBALS['TYPO3_DB']->exec_UPDATEquery('tx_scheduler_task', "uid = '" . $taskUid . "'", $fields);
+               } else {
+                       return false;
+               }
+       }
+
+       /**
+        * Fetches and unserializes a task object from the db. If an uid is given the object
+        * with the uid is returned, else the object representing the next due task is returned.
+        * If there are no due tasks the method throws an exception.
+        *
+        * @param       integer                         $uid: primary key of a task
+        * @return      tx_scheduler_Task       The fetched task object
+        */
+       public function fetchTask($uid = 0) {
+                       // Define where clause
+                       // If no uid is given, take any non-disabled task which has a next execution time in the past
+               if (empty($uid)) {
+                       $whereClause = 'disable = 0 AND nextexecution != 0 AND nextexecution <= ' . $GLOBALS['EXEC_TIME'];
+               } else {
+                       $whereClause = 'uid = ' . intval($uid);
+               }
+
+               $queryArray = array(
+                       'SELECT'        => 'serialized_task_object',
+                       'FROM'          => 'tx_scheduler_task',
+                       'WHERE'         => $whereClause,
+                       'LIMIT'         => 1
+               );
+               $res = $GLOBALS['TYPO3_DB']->exec_SELECT_queryArray($queryArray);
+
+                       // If there are no available tasks, thrown an exception
+               if ($GLOBALS['TYPO3_DB']->sql_num_rows($res) == 0) {
+                       throw new OutOfBoundsException('No task', 1247827244);
+
+                       // Otherwise unserialize the task and return it
+               } else {
+                       $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
+                       $task = unserialize($row['serialized_task_object']);
+                       $task->setScheduler();
+                       $GLOBALS['TYPO3_DB']->sql_free_result($res);
+                       return $task;
+               }
+       }
+
+        /**
+         * This method is used to get the database record for a given task
+         * It returns the database record and not the task object
+         *
+         * @param      integer $uid: primary key of the task to get
+         * @return     array   Database record for the task
+         * @see        tx_scheduler::fetchTask()
+         */
+        public function fetchTaskRecord($uid) {
+               $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', 'tx_scheduler_task', "uid = '" . intval($uid) . "'");
+
+                       // If the task is not found, throw an exception
+               if ($GLOBALS['TYPO3_DB']->sql_num_rows($res) == 0) {
+                       throw new OutOfBoundsException('No task', 1247827244);
+
+                       // Otherwise get the task's record
+               } else {
+                       $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
+                       $GLOBALS['TYPO3_DB']->sql_free_result($res);
+               }
+               return $row;
+        }
+
+       /**
+        * Fetches and unserializes task objects selected with some (SQL) condition
+        * Objects are returned as an array
+        *
+        * @param       string          $where: part of a SQL where clause (without the "WHERE" keyword)
+        * @param       boolean         $includeDisabledTasks: true if disabled tasks should be fetched too, false otherwise
+        * @return      array           List of task objects
+        */
+       public function fetchTasksWithCondition($where, $includeDisabledTasks = false) {
+               $tasks = array();
+               if (!empty($where)) {
+                       $whereClause = $where;
+               }
+               if (!$includeDisabledTasks) {
+                       if (!empty($whereClause)) {
+                               $whereClause .= ' AND ';
+                       }
+                       $whereClause .= 'disable = 0';
+               }
+               $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('serialized_task_object', 'tx_scheduler_task', $whereClause);
+               if ($res) {
+                       while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
+                               $task = unserialize($row['serialized_task_object']);
+                               $task->setScheduler();
+                               $tasks[] = $task;
+                       }
+                       $GLOBALS['TYPO3_DB']->sql_free_result($res);
+               }
+               return $tasks;
+       }
+}
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/scheduler/class.tx_scheduler.php'])   {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/scheduler/class.tx_scheduler.php']);
+}
+
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/scheduler/class.tx_scheduler_croncmd.php b/typo3/sysext/scheduler/class.tx_scheduler_croncmd.php
new file mode 100644 (file)
index 0000000..d35d949
--- /dev/null
@@ -0,0 +1,306 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2008-2009 Markus Friedrich (markus.friedrich@dkd.de)
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+
+/**
+ * This class provides calulations for the cron command format
+ *
+ * @author             Markus Friedrich <markus.friedrich@dkd.de>
+ * @package            TYPO3
+ * @subpackage tx_scheduler
+ *
+ * $Id: class.tx_scheduler_croncmd.php 1262 2009-09-15 21:04:22Z francois $
+ */
+class tx_scheduler_CronCmd {
+
+       /**
+        * Sections of the cron command
+        *
+        *      field          allowed values
+        *      -----          --------------
+        *      minute         0-59
+        *      hour           0-23
+        *      day of month   1-31
+        *      month          1-12 (or names, see below)
+        *      day of week    0-7 (0 or 7 is Sun, or use names)
+        *
+        * @var array           $cmd_sections
+        */
+       public $cmd_sections;
+
+       /**
+        * Valid values for each part
+        *
+        * @var array           $valid_values
+        */
+       public $valid_values;
+
+       /**
+        * Array containing the values to build the new execution date
+        *
+        * 0    =>      minute
+        * 1    =>      hour
+        * 2    =>      day
+        * 3    =>      month
+        * 4    =>      year
+        *
+        * @var array           $values
+        */
+       public $values;
+
+       /**
+        * Constructor
+        *
+        * @param       string          $cmd: the cron command
+        * @return      void
+        */
+       public function __construct($cmd) {
+                       // Explode cmd in sections
+               $this->cmd_sections = t3lib_div::trimExplode(' ', $cmd);
+
+                       // Initialize the values with the starting time
+                       // This takes care that the calculated time is always in the future
+               $tstamp = strtotime('+1 minute');
+               $this->values = array(
+                               // Minute
+                       intval(date('i', $tstamp)),
+                               // Hour
+                       date('G', $tstamp),
+                               // Day
+                       date('j', $tstamp),
+                               // Month
+                       date('n', $tstamp),
+                               // Year
+                       date('Y', $tstamp)
+               );
+
+                       // Set valid values
+               $this->valid_values = array(
+                       $this->getList($this->cmd_sections[0], 0, 59),
+                       $this->getList($this->cmd_sections[1], 0, 23),
+                       $this->getDayList($this->values[3], $this->values[4]),
+                       $this->getList($this->cmd_sections[3], 1, 12),
+                       $this->getList('*', date('Y', $tstamp), intval(date('Y', $tstamp))+1)
+               );
+       }
+
+       /**
+        * Calulates the next execution
+        *
+        * @param       integer         $level: number of the current level, e.g. 2 is the day level
+        * @return      void
+        */
+       public function calculateNextValue($level) {
+               if (isset($this->values[$level])) {
+                       $current_value = &$this->values[$level];
+                       $next_level = $level + 1;
+
+                       if (in_array($current_value, $this->valid_values[$level])) {
+                               $this->calculateNextValue($next_level);
+                       } else {
+                               $next_value = $this->getNextValue($this->values[$level], $this->valid_values[$level]);
+                               if ($next_value === false) {
+                                               // Set this value and prior values to the start value
+                                       for ($i = $level; $i >= 0; $i--) {
+                                               $this->values[$i] = $this->valid_values[$i][0];
+
+                                                       // Update day list if month was changed
+                                               if ($i == 3) {
+                                                       $this->valid_values[2] = $this->getDayList($this->values[3], $this->values[4]);
+                                               }
+                                       }
+
+                                               // Calculate next value for the next value
+                                       for ($i = $next_level; $i <= count($this->values); $i++) {
+                                               if (isset($this->values[$i])) {
+                                                       $increased_value = $this->getNextValue($this->values[$i], $this->valid_values[$i]);
+
+                                                       if ($increased_value !== false) {
+                                                               $this->values[$i] = $increased_value;
+
+                                                                       // Update day list if month was changed
+                                                               if ($i == 3) {
+                                                                       $this->valid_values[2] = $this->getDayList($this->values[3], $this->values[4]);
+
+                                                                               // Check if day had already a valid start value, if not set a new one
+                                                                       if (!$this->values[2] || !in_array($this->values[2], $this->valid_values[2])) {
+                                                                               $this->values[2] = $this->valid_values[2][0];
+                                                                       }
+                                                               }
+
+                                                               break;
+                                                       } else {
+                                                               $this->values[$i] = $this->valid_values[$i][0];
+
+                                                                       // Update day list if month was changed
+                                                               if ($i == 3) {
+                                                                       $this->valid_values[2] = $this->getDayList($this->values[3], $this->values[4]+1);
+                                                               }
+                                                       }
+                                               }
+                                       }
+
+                                       $this->calculateNextValue($next_level);
+                               } else {
+                                       if ($level == 3) {
+                                                       // Update day list if month was changed
+                                               $this->valid_values[2] = $this->getDayList($this->values[3], $this->values[4]);
+                                       }
+
+                                       $current_value = $next_value;
+                                       $this->calculateNextValue($next_level);
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Builds a list of days for a certain month
+        *
+        * @param       integer         $currentMonth: number of a month
+        * @param       integer         $currentYear: a year
+        * @return      array           list of days
+        */
+       protected function getDayList($currentMonth, $currentYear) {
+                       // Create a dummy timestamp at 6:00 of the first day of the current month and year
+                       // to get the number of days in the month using date()
+               $dummyTimestamp = mktime(6, 0, 0, $currentMonth, 1, $currentYear);
+               $max_days = date('t', $dummyTimestamp);
+               $validDays = $this->getList($this->cmd_sections[2], 1, $max_days);
+
+                       // Consider special field 'day of week'
+               if ($this->cmd_sections[2] != '*' && (strpos($this->cmd_sections[4], '*') === false && preg_match('/[1-7]{1}/', $this->cmd_sections[4]) !== false)) {
+                               // Get list
+                       for ($i = 1; $i <= $max_days; $i++) {
+                               if (strftime('%u', mktime(0, 0, 0, $currentMonth, $i, $currentYear)) == $this->cmd_sections[4]) {
+                                       if (!in_array($i, $validDays)) {
+                                               $validDays[] = $i;
+                                       }
+                               }
+                       }
+               }
+               sort($validDays);
+
+               return $validDays;
+       }
+
+       /**
+        * Builds a list of possible values from a cron command
+        *
+        * @param       string          $definition: the command e.g. 2-8, *, 0-59/20
+        * @param       integer         $min: minimum allowed value
+        * @param       integer         $max: maximum allowed value
+        * @return      array           list with possible values
+        */
+       protected function getList($definition, $min, $max) {
+               $list = array();
+
+               if ($definition == '*') {
+                               // Get list for the asterix
+                       for ($tmp = $min; $tmp <= $max; $tmp++) {
+                               $list[] = $tmp;
+                       }
+               } else if (strpos($definition, '/') !== false) {
+                               // Get list for step values
+
+                               // Extract list part
+                       $defList = substr($definition, 0, strpos($definition, '/'));
+                       $stepDef = substr($definition, strpos($definition, '/') + 1);
+                       $tmpList = $this->getList($defList, $min, $max);
+
+                       for ($i=0; $i<count($tmpList); $i++) {
+                               if ($i % $stepDef == 0) {
+                                       $list[] = $tmpList[$i];
+                               }
+                       }
+               } else if (strpos($definition, ',') !== false) {
+                               // Get list for list definitions
+
+                       $defList = explode(',', $definition);
+                       foreach ($defList as $listItem) {
+                               $tmpList = $this->getList($listItem, $min, $max);
+                               $list = array_merge($list, $tmpList);
+                       }
+               } else if (strpos($definition, '-') !== false) {
+                               // Get list for range definitions
+
+                               // Get list definition parts
+                       list($def_min, $def_max) = t3lib::trimExplode('-', $definition);
+
+                       if ($def_min < $min) {
+                               $def_min = $min;
+                       }
+
+                       if ($def_max > $max) {
+                               $def_max = $max;
+                       }
+
+                       $list = $this->getList('*', $def_min, $def_max);
+               } else if (is_numeric($definition) && $definition >= $min && $definition <= $max) {
+                               // Get list for single values
+                       $list[] = intval($definition);
+               }
+
+                       // Sort the list and return it
+               sort($list);
+               return $list;
+       }
+
+       /**
+        * Returns the first value that is higher than the current value
+        * from a list of possible values
+        *
+        * @param       mixed   $currentValue: the value to be searched in the list
+        * @param       array   $listArray: the list of values
+        * @return      mixed   The value from the list right after the current value
+        */
+       public function getNextValue($currentValue, array $listArray) {
+               $next_value = false;
+
+               $numValues = count($listArray);
+               for ($i = 0; $i < $numValues; $i++) {
+                       if ($listArray[$i] > $currentValue) {
+                               $next_value = $listArray[$i];
+                               break;
+                       }
+               }
+
+               return $next_value;
+       }
+
+       /**
+        * Returns the timestamp for the value parts in $this->values
+        *
+        * @return      integer         unix timestamp
+        */
+       public function getTstamp() {
+               return mktime($this->values[1], $this->values[0], 0, $this->values[3], $this->values[2], $this->values[4]);
+       }
+}
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/scheduler/class.tx_scheduler_croncmd.php'])   {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/scheduler/class.tx_scheduler_croncmd.php']);
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/scheduler/class.tx_scheduler_execution.php b/typo3/sysext/scheduler/class.tx_scheduler_execution.php
new file mode 100644 (file)
index 0000000..674243c
--- /dev/null
@@ -0,0 +1,262 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2005 Christian Jul Jensen (julle@typo3.org)
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+
+/**
+ * This class manages the logic of a particular execution of a task
+ *
+ * @author     Francois Suter <francois@typo3.org>
+ * @author     Christian Jul Jensen <julle@typo3.org>
+ * @author     Markus Friedrich <markus.friedrich@dkd.de>
+ *
+ * @package            TYPO3
+ * @subpackage tx_scheduler
+ *
+ * $Id: class.tx_scheduler_execution.php 1262 2009-09-15 21:04:22Z francois $
+ */
+class tx_scheduler_Execution {
+
+       /**
+        * Start date of a task (timestamp)
+        *
+        * @var integer $start
+        */
+       protected $start;
+
+       /**
+        * End date of a task (timestamp)
+        *
+        * @var integer $end
+        */
+       protected $end;
+
+       /**
+        * Interval between executions (in seconds)
+        *
+        * @var integer $interval
+        */
+       protected $interval;
+
+       /**
+        * Flag for concurrent executions: true if allowed, false otherwise (default)
+        *
+        * @var boolean $multiple
+        */
+       protected $multiple = false;
+
+       /**
+        * The cron command string of this task,
+        *
+        * @var string          $cronCmd
+        */
+       protected $cronCmd;
+
+
+       /**********************************
+        * Setters and getters
+        **********************************/
+
+       /**
+        * This method is used to set the start date
+        *
+        * @param       integer         $start: start date (timestamp)
+        * @return      void
+        */
+       public function setStart($start) {
+               $this->start = $start;
+       }
+
+       /**
+        * This method is used to get the start date
+        *
+        * @return      integer         start date (timestamp)
+        */
+       public function getStart() {
+               return $this->start;
+       }
+
+       /**
+        * This method is used to set the end date
+        *
+        * @param       integer         $end: end date (timestamp)
+        * @return      void
+        */
+       public function setEnd($end) {
+               $this->end = $end;
+       }
+
+       /**
+        * This method is used to get the end date
+        *
+        * @return      integer         end date (timestamp)
+        */
+       public function getEnd() {
+               return $this->end;
+       }
+
+       /**
+        * This method is used to set the interval
+        *
+        * @param       integer         $interval: interval (in seconds)
+        * @return      void
+        */
+       public function setInterval($interval) {
+               $this->interval = $interval;
+       }
+
+       /**
+        * This method is used to get the interval
+        *
+        * @return      integer         interval (in seconds)
+        */
+       public function getInterval() {
+               return $this->interval;
+       }
+
+       /**
+        * This method is used to set the multiple execution flag
+        *
+        * @param       boolean         $multiple: true if concurrent executions are allowed, false otherwise
+        * @return      void
+        */
+       public function setMultiple($multiple) {
+               $this->multiple = $multiple;
+       }
+
+       /**
+        * This method is used to get the multiple execution flag
+        *
+        * @return      boolean         true if concurrent executions are allowed, false otherwise
+        */
+       public function getMultiple() {
+               return $this->multiple;
+       }
+
+       /**
+        * Set the value of the cron command
+        *
+        * @param       string          $cmd: cron command, using cron-like syntax
+        * @return      void
+        */
+       public function setCronCmd($cmd) {
+               $this->cronCmd = $cmd;
+       }
+
+       /**
+        * Get the value of the cron command
+        *
+        * @return      string          cron command, using cron-like syntax
+        */
+       public function getCronCmd() {
+               return $this->cronCmd;
+       }
+
+       /**********************************
+        * Execution calculations and logic
+        **********************************/
+
+       /**
+        * This method gets or calculates the next execution date
+        *
+        * @return      integer         Timestamp of the next execution
+        */
+       public function getNextExecution() {
+
+               if (!$this->isEnded()) {
+                               // If the schedule has not yet run out, find out the next date
+
+                       if (!$this->isStarted()) {
+                                       // If the schedule hasn't started yet, next date is start date
+                               $date = $this->start;
+                       } else {
+                                       // If the schedule has already started, calculate next date
+
+                               if ($this->cronCmd) {
+                                               // If it uses cron-like syntax, calculate next date
+                                       $date = $this->getNextCronExecution();
+                               } else if ($this->interval == 0) {
+                                               // If not and there's no interval either, it's a singe execution: use start date
+                                       $date = $this->start;
+                               } else {
+                                               // Otherwise calculate date based on interval
+                                       $now = time();
+                                       $date = $now + $this->interval - (($now - $this->start) % $this->interval);
+                               }
+                                       // If date is in the future, throw an exception
+                               if (!empty($this->end) && $date > $this->end) {
+                                       throw new OutOfBoundsException('Next execution date is past end date.', 1250715528);
+                               }
+                       }
+               } else {
+                               // The event has ended, throw an exception
+                       throw new OutOfBoundsException('Task is past end date.', 1250715544);
+               }
+
+               return $date;
+       }
+
+       /**
+        * Calulates the next execution from a cron command
+        *
+        * @return      integer         Next execution (timestamp)
+        */
+       public function getNextCronExecution() {
+               $cronCmd = t3lib_div::makeInstance('tx_scheduler_CronCmd', $this->getCronCmd());
+               $cronCmd->calculateNextValue(0);
+
+               return $cronCmd->getTstamp();
+       }
+
+       /**
+        * Checks if the schedule for a task is started or not
+        *
+        * @return      boolean         True if the schedule is already active, false otherwise
+        */
+       public function isStarted() {
+               return $this->start < time();
+       }
+
+       /**
+        * Checks if the schedule for a task is passed or not
+        *
+        * @return      boolean         True if the schedule is not active anymore, false otherwise
+        */
+       public function isEnded() {
+               if (empty($this->end))  {
+                               // If no end is defined, the schedule never ends
+                       $result = false;
+               } else {
+                               // Otherwise check if end is in the past
+                       $result = $this->end < time();
+               }
+
+               return $result;
+       }
+}
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/scheduler/class.tx_scheduler_execution.php']) {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/scheduler/class.tx_scheduler_execution.php']);
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/scheduler/class.tx_scheduler_failedexecutionexception.php b/typo3/sysext/scheduler/class.tx_scheduler_failedexecutionexception.php
new file mode 100644 (file)
index 0000000..776523c
--- /dev/null
@@ -0,0 +1,43 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Ingo Renner <ingo@typo3.org>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+
+/**
+ * Failed execution exception
+ *
+ * @author     Ingo Renner <ingo@typo3.org>
+ * @package TYPO3
+ * @subpackage scheduler
+ */
+class tx_scheduler_FailedExecutionException extends RuntimeException {
+
+
+}
+
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/scheduler/class.tx_scheduler_failedexecutionexception.php'])  {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/scheduler/class.tx_scheduler_failedexecutionexception.php']);
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/scheduler/class.tx_scheduler_task.php b/typo3/sysext/scheduler/class.tx_scheduler_task.php
new file mode 100755 (executable)
index 0000000..4358e8a
--- /dev/null
@@ -0,0 +1,441 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2005 Christian Jul Jensen (julle@typo3.org)
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+
+/**
+ * This is the base class for all Scheduler tasks
+ * It's an abstract class, not designed to be instantiated directly
+ * All Scheduler tasks should inherit from this class
+ *
+ * @author     Francois Suter <francois@typo3.org>
+ * @author     Christian Jul Jensen <julle@typo3.org>
+ *
+ * @package            TYPO3
+ * @subpackage tx_scheduler
+ *
+ * $Id: class.tx_scheduler_task.php 1262 2009-09-15 21:04:22Z francois $
+ */
+abstract class tx_scheduler_Task {
+
+       /**
+        * Reference to a scheduler object
+        *
+        * @var tx_scheduler
+        */
+       protected $scheduler;
+
+       /**
+        * The unique id of the task used to identify it in the database
+        *
+        * @var int
+        */
+       protected $taskUid;
+
+       /**
+        * Disable flag, true if task is disabled, false otherwise
+        *
+        * @var boolean
+        */
+       protected $disabled = false;
+
+       /**
+        * The execution object related to the task
+        *
+        * @var tx_scheduler_Execution
+        */
+       protected $execution;
+
+       /**
+        * This variable contains the time of next execution of the task
+        *
+        * @var timestamp
+        */
+       protected $executionTime = 0;
+
+       /**
+        * Constructor
+        */
+       public function __construct() {
+               $this->setScheduler();
+               $this->execution = t3lib_div::makeInstance('tx_scheduler_Execution');
+       }
+
+       /**
+        * This is the main method that is called when a task is executed
+        * It MUST be implemented by all classes inheriting from this one
+        * Note that there is no error handling, errors and failures are expected
+        * to be handled and logged by the client implementations.
+        * Should return true on successful execution, false on error.
+        *
+        * @return boolean      Returns true on successful execution, false on error
+        */
+       abstract public function execute();
+
+       /**
+        * This method is designed to return some additional information about the task,
+        * that may help to set it apart from other tasks from the same class
+        * This additional information is used - for example - in the Scheduler's BE module
+        * This method should be implemented in most task classes
+        *
+        * @return      string  Information to display
+        */
+       public function getAdditionalInformation() {
+               return '';
+       }
+
+       /**
+        * This method is used to set the unique id of the task
+        *
+        * @param       integer $id: primary key (from the database record) of the scheduled task
+        * @return      void
+        */
+       public function setTaskUid($id) {
+               $this->taskUid = intval($id);
+       }
+
+       /**
+        * This method returns the unique id of the task
+        *
+        * @return      integer         The id of the task
+        */
+       public function getTaskUid() {
+               return $this->taskUid;
+       }
+
+       /**
+        * This method returns the disable status of the task
+        *
+        * @return      boolean         True if task is disabled, false otherwise
+        */
+       public function isDisabled() {
+               return $this->disabled;
+       }
+
+       /**
+        * This method is used to set the disable status of the task
+        *
+        * @param       boolean $flag: true if task should be disabled, false otherwise
+        * @return      void
+        */
+       public function setDisabled($flag) {
+               if ($flag) {
+                       $this->disabled = true;
+               } else {
+                       $this->disabled = false;
+               }
+       }
+
+       /**
+        * This method is used to set the timestamp corresponding to the next execution time of the task
+        *
+        * @param       integer         $timestamp: timestamp of next execution
+        * @return      void
+        */
+       public function setExecutionTime($timestamp) {
+               $this->executionTime = intval($timestamp);
+       }
+
+       /**
+        * This method returns the timestamp corresponding to the next execution time of the task
+        * @return      integer         Timestamp of next execution
+        */
+       public function getExecutionTime() {
+               return $this->executionTime;
+       }
+
+       /**
+        * Sets the internal reference to the singleton instance of the Scheduler
+        *
+        * @return void
+        */
+       public function setScheduler() {
+               $this->scheduler = t3lib_div::makeInstance('tx_scheduler');
+       }
+
+       /**
+        * Unsets the internal reference to the singleton instance of the Scheduler
+        * This is done before a task is serialized, so that the scheduler instance
+        * is not saved to the database too
+        *
+        * @return void
+        */
+       public function unsetScheduler() {
+               unset($this->scheduler);
+       }
+
+       /**
+        * Registers a single execution of the task
+        *
+        * @param       integer $timestamp: Timestamp of the next execution
+        */
+       public function registerSingleExecution($timestamp) {
+               $execution = t3lib_div::makeInstance('tx_scheduler_Execution');
+               $execution->setStart($timestamp);
+               $execution->setInterval(0);
+               $execution->setEnd($timestamp);
+               $execution->setCronCmd('');
+               $execution->setMultiple(0);
+                       // Replace existing execution object
+               $this->execution = $execution;
+       }
+
+       /**
+        * Registers a reccuring excecution of the task
+        *
+        * @param       integer         $start: the first date/time where this execution should occur (timestamp)
+        * @param       string          $interval: execution interval in seconds
+        * @param       integer         $end: the last date/time where this execution should occur (timestamp)
+        * @param       boolean         $multiple: set to false if multiple executions of this task are not permitted in parallel
+        * @param       string          $croncmd: used like in crontab (minute hour day month weekday)
+        * @return      void
+        */
+       public function registerRecurringExecution($start, $interval, $end = 0, $multiple = false, $cron_cmd = '') {
+               $execution = t3lib_div::makeInstance('tx_scheduler_Execution');
+                       // Set general values
+               $execution->setStart($start);
+               $execution->setEnd($end);
+               $execution->setMultiple($multiple);
+
+               if (empty($cron_cmd)) {
+                               // Use interval
+                       $execution->setInterval($interval);
+                       $execution->setCronCmd('');
+               } else {
+                               // Use cron syntax
+                       $execution->setInterval(0);
+                       $execution->setCronCmd($cron_cmd);
+               }
+                       // Replace existing execution object
+               $this->execution = $execution;
+       }
+
+       /**
+        * Sets the internal execution object
+        *
+        * @param       tx_scheduler_Execution  $execution: the execution to add
+        */
+       public function setExecution(tx_scheduler_Execution $execution) {
+               $this->execution = $execution;
+       }
+
+       /**
+        * Returns the execution object
+        *
+        * @return      tx_scheduler_Execution  The internal execution object
+        */
+       public function getExecution() {
+               return $this->execution;
+       }
+
+       /**
+        * Returns the timestamp for next due execution of the task
+        *
+        * @return      integer         Date and time of the next execution as a timestamp
+        */
+       public function getNextDueExecution() {
+
+                       // NOTE: this call may throw an exception, but we let it bubble up
+               return $this->execution->getNextExecution();
+       }
+
+       /**
+        * Returns true if several runs of the task are allowed concurrently
+        *
+        * @return      boolean         True if concurrent executions are allowed, false otherwise
+        */
+       public function areMultipleExecutionsAllowed() {
+               return $this->execution->getMultiple();
+       }
+
+       /**
+        * Returns true if an instance of the task is already running
+        *
+        * @return      boolean         True if an instance is already running, false otherwise
+        */
+       public function isExecutionRunning() {
+               $isRunning = false;
+
+               $queryArr = array(
+                       'SELECT' => 'serialized_executions',
+                       'FROM'   => 'tx_scheduler_task',
+                       'WHERE'  => 'uid = ' . intval($this->taskUid),
+                       'LIMIT'  => 1
+               );
+               $res = $GLOBALS['TYPO3_DB']->exec_SELECT_queryArray($queryArr);
+
+               if (($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))) {
+                       if (strlen($row['serialized_executions']) > 0) {
+                               $isRunning = true;
+                       }
+               }
+               $GLOBALS['TYPO3_DB']->sql_free_result($res);
+
+               return $isRunning;
+       }
+
+       /**
+        * This method adds current execution to the execution list
+        * It also logs the execution time and mode
+        *
+        * @return      integer         Execution id
+        */
+       public function markExecution() {
+               $queryArr = array(
+                       'SELECT' => 'serialized_executions',
+                       'FROM'   => 'tx_scheduler_task',
+                       'WHERE'  => 'uid = ' . intval($this->taskUid),
+                       'LIMIT'  => 1
+               );
+               $res = $GLOBALS['TYPO3_DB']->exec_SELECT_queryArray($queryArr);
+
+               $runningExecutions = array();
+               if (($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))) {
+                       if (strlen($row['serialized_executions']) > 0) {
+                               $runningExecutions = unserialize($row['serialized_executions']);
+                       }
+               }
+               $GLOBALS['TYPO3_DB']->sql_free_result($res);
+
+                       // Count the number of existing executions and use that number as a key
+                       // (we need to know that number, because it is returned at the end of the method)
+               $numExecutions = count($runningExecutions);
+               $runningExecutions[$numExecutions] = time();
+
+                       // Define the context in which the script is running
+               $context = 'BE';
+               if (defined('TYPO3_cliMode') && TYPO3_cliMode) {
+                       $context = 'CLI';
+               }
+
+               $GLOBALS['TYPO3_DB']->exec_UPDATEquery(
+                       'tx_scheduler_task',
+                       'uid = ' . intval($this->taskUid),
+                       array(
+                               'serialized_executions' => serialize($runningExecutions),
+                               'lastexecution_time'    => time(),
+                               'lastexecution_context' => $context
+                       )
+               );
+
+               return $numExecutions;
+       }
+
+       /**
+        * Removes given execution from list
+        *
+        * @param       integer         Id of the execution to remove.
+        * @param       Exception       An exception to signal a failed execution
+        * @return      void
+        */
+       public function unmarkExecution($executionID, Exception $failure = null) {
+                       // Get the executions for the task
+               $queryArr = array(
+                       'SELECT' => 'serialized_executions',
+                       'FROM'   => 'tx_scheduler_task',
+                       'WHERE'  => 'uid = ' . intval($this->taskUid),
+                       'LIMIT'  => 1
+               );
+
+               $res = $GLOBALS['TYPO3_DB']->exec_SELECT_queryArray($queryArr);
+               if (($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))) {
+                       if (strlen($row['serialized_executions']) > 0) {
+                               $runningExecutions = unserialize($row['serialized_executions']);
+                                       // Remove the selected execution
+                               unset($runningExecutions[$executionID]);
+
+                               if (count($runningExecutions) > 0) {
+                                               // Re-serialize the updated executions list (if necessary)
+                                       $runningExecutionsSerialized = serialize($runningExecutions);
+                               } else {
+                                       $runningExecutionsSerialized = '';
+                               }
+
+                               if ($failure instanceof Exception) {
+                                               // Log failed execution
+                                       $GLOBALS['BE_USER']->writelog(
+                                               4,
+                                               0,
+                                               1,
+                                               $failure->getCode(),
+                                               '[scheduler]: Task failed to execute successfully. Class: '
+                                                       . get_class($this) . ', UID: '
+                                                       . $this->taskUid . '. '
+                                                       . $failure->getMessage(),
+                                               array()
+                                       );
+
+                                       $failure = serialize($failure);
+                               } else {
+                                       $failure = '';
+                               }
+
+                                       // Save the updated executions list
+                               $GLOBALS['TYPO3_DB']->exec_UPDATEquery(
+                                       'tx_scheduler_task',
+                                       'uid = ' . intval($this->taskUid),
+                                       array(
+                                               'serialized_executions' => $runningExecutionsSerialized,
+                                               'lastexecution_failure' => $failure
+                                       )
+                               );
+                       }
+               }
+               $GLOBALS['TYPO3_DB']->sql_free_result($res);
+       }
+
+       /**
+        * Saves the details of the task to the database.
+        *
+        * @return bool
+        */
+       public function save() {
+               return $this->scheduler->saveTask($this);
+       }
+
+       /**
+        * Stops the task, by replacing the execution object by an empty one
+        * NOTE: the task still needs to be saved after that
+        *
+        * @return      void
+        */
+       public function stop() {
+               $this->execution = t3lib_div::makeInstance('tx_scheduler_Execution');
+       }
+
+       /**
+        * Removes the task totally from the system.
+        *
+        * @return      void
+        */
+       public function remove() {
+               $this->scheduler->removeTask($this);
+       }
+}
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/scheduler/class.tx_scheduler_task.php'])      {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/scheduler/class.tx_scheduler_task.php']);
+}
+
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/scheduler/cli/scheduler_cli_dispatch.php b/typo3/sysext/scheduler/cli/scheduler_cli_dispatch.php
new file mode 100644 (file)
index 0000000..7ff4a6c
--- /dev/null
@@ -0,0 +1,71 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2008 Markus Friedrich (markus.friedrich@dkd.de>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+/**
+ * Starts all due tasks, used by the command line interface
+ * This script must be included by the "CLI module dispatcher"
+ *
+ * @author             Markus Friedrich <markus.friedrich@dkd.de>
+ * @package            TYPO3
+ * @subpackage tx_scheduler
+ *
+ * $Id: scheduler_cli_dispatch.php 1261 2009-09-15 20:22:45Z francois $
+ */
+if (defined('TYPO3_cliMode') && TYPO3_cliMode && basename(PATH_thisScript) == 'cli_dispatch.phpsh') {
+               // Create an instance of the scheduler object
+               /**
+                * @var tx_scheduler
+                */
+       $scheduler = t3lib_div::makeInstance('tx_scheduler');
+               // Loop as long as there are tasks
+       do {
+                       // Try getting the next task and execute it
+                       // If there are no more tasks to execute, an exception is thrown by tx_scheduler::fetchTask()
+               try {
+                               /**
+                                * @var tx_scheduler_Task
+                                */
+                       $task = $scheduler->fetchTask();
+                       $hasTask = true;
+                       try {
+                               $scheduler->executeTask($task);
+                       }
+                       catch (Exception $e) {
+                                       // We ignore any exception that may have been thrown during execution,
+                                       // as this is a background process.
+                                       // The exception message has been recorded to the database anyway
+                               continue;
+                       }
+               }
+               catch (OutOfBoundsException $e) {
+                       $hasTask = false;
+               }
+       } while ($hasTask);
+               // Record the run in the system registry
+       $scheduler->recordLastRun();
+} else {
+       die('This script must be included by the "CLI module dispatcher"');
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/scheduler/doc/manual.sxw b/typo3/sysext/scheduler/doc/manual.sxw
new file mode 100644 (file)
index 0000000..27ce26e
Binary files /dev/null and b/typo3/sysext/scheduler/doc/manual.sxw differ
diff --git a/typo3/sysext/scheduler/examples/class.tx_scheduler_sleeptask.php b/typo3/sysext/scheduler/examples/class.tx_scheduler_sleeptask.php
new file mode 100644 (file)
index 0000000..1581a3b
--- /dev/null
@@ -0,0 +1,76 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Francois Suter <francois@typo3.org>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+/**
+ * Class "tx_scheduler_SleepTask" provides a task that sleeps for some time
+ * This is useful for testing parallel executions
+ *
+ * @author             Francois Suter <francois@typo3.org>
+ * @package            TYPO3
+ * @subpackage tx_scheduler
+ *
+ * $Id: class.tx_scheduler_sleeptask.php 1262 2009-09-15 21:04:22Z francois $
+ */
+class tx_scheduler_SleepTask extends tx_scheduler_Task {
+
+       /**
+        * Number of seconds the task should be sleeping for
+        *
+        * @var integer         $sleepTime
+        */
+        public $sleepTime = 10;
+
+       /**
+        * Function executed from the Scheduler.
+        * Goes to sleep ;-)
+        *
+        * @return      void
+        */
+       public function execute() {
+               $time = 10;
+
+               if (!empty($this->sleepTime)) {
+                       $time = $this->sleepTime;
+               }
+
+               sleep($time);
+
+               return true;
+       }
+
+       /**
+        * This method returns the sleep duration as additional information
+        *
+        * @return      string  Information to display
+        */
+       public function getAdditionalInformation() {
+               return $GLOBALS['LANG']->sL('LLL:EXT:scheduler/mod1/locallang.xml:label.sleepTime') . ': ' . $this->sleepTime;
+       }
+}
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/scheduler/examples/class.tx_scheduler_sleeptask.php'])        {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/scheduler/examples/class.tx_scheduler_sleeptask.php']);
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/scheduler/examples/class.tx_scheduler_sleeptask_additionalfieldprovider.php b/typo3/sysext/scheduler/examples/class.tx_scheduler_sleeptask_additionalfieldprovider.php
new file mode 100644 (file)
index 0000000..8599be9
--- /dev/null
@@ -0,0 +1,118 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Francois Suter <francois@typo3.org>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+/**
+ * Aditional fields provider class for usage with the Scheduler's sleep task
+ *
+ * @author             Francois Suter <francois@typo3.org>
+ * @package            TYPO3
+ * @subpackage tx_scheduler
+ *
+ * $Id: class.tx_scheduler_sleeptask_additionalfieldprovider.php 1262 2009-09-15 21:04:22Z francois $
+ */
+class tx_scheduler_SleepTask_AdditionalFieldProvider implements tx_scheduler_AdditionalFieldProvider {
+
+       /**
+        * This method is used to define new fields for adding or editing a task
+        * In this case, it adds an sleep time field
+        *
+        * @param       array                                   $taskInfo: reference to the array containing the info used in the add/edit form
+        * @param       object                                  $task: when editing, reference to the current task object. Null when adding.
+        * @param       tx_scheduler_Module             $parentObject: reference to the calling object (Scheduler's BE module)
+        * @return      array                                   Array containg all the information pertaining to the additional fields
+        *                                                                      The array is multidimensional, keyed to the task class name and each field's id
+        *                                                                      For each field it provides an associative sub-array with the following:
+        *                                                                              ['code']                => The HTML code for the field
+        *                                                                              ['label']               => The label of the field (possibly localized)
+        *                                                                              ['cshKey']              => The CSH key for the field
+        *                                                                              ['cshLabel']    => The code of the CSH label
+        */
+       public function getAdditionalFields(array &$taskInfo, $task, tx_scheduler_Module $parentObject) {
+
+                       // Initialize extra field value
+               if (empty($taskInfo['sleepTime'])) {
+                       if ($parentObject->CMD == 'add') {
+                                       // In case of new task and if field is empty, set default sleep time
+                               $taskInfo['sleepTime'] = 30;
+                       } else if ($parentObject->CMD == 'edit') {
+                                       // In case of edit, set to internal value if no data was submitted already
+                               $taskInfo['sleepTime'] = $task->sleepTime;
+                       } else {
+                                       // Otherwise set an empty value, as it will not be used anyway
+                               $taskInfo['sleepTime'] = '';
+                       }
+               }
+
+                       // Write the code for the field
+               $fieldID = 'task_sleepTime';
+               $fieldCode = '<input type="text" name="tx_scheduler[sleepTime]" id="' . $fieldID . '" value="' . $taskInfo['sleepTime'] . '" size="10" />';
+               $additionalFields = array();
+               $additionalFields[$fieldID] = array(
+                       'code'     => $fieldCode,
+                       'label'    => 'LLL:EXT:scheduler/mod1/locallang.xml:label.sleepTime',
+                       'cshKey'   => '_MOD_tools_txschedulerM1',
+                       'cshLabel' => $fieldID
+               );
+
+               return $additionalFields;
+       }
+
+       /**
+        * This method checks any additional data that is relevant to the specific task
+        * If the task class is not relevant, the method is expected to return true
+        *
+        * @param       array                                   $submittedData: reference to the array containing the data submitted by the user
+        * @param       tx_scheduler_Module             $parentObject: reference to the calling object (Scheduler's BE module)
+        * @return      boolean                                 True if validation was ok (or selected class is not relevant), false otherwise
+        */
+       public function validateAdditionalFields(array &$submittedData, tx_scheduler_Module $parentObject) {
+               $submittedData['sleepTime'] = intval($submittedData['sleepTime']);
+
+               if ($submittedData['sleepTime'] < 0) {
+                       $parentObject->addMessage($GLOBALS['LANG']->sL('LLL:EXT:scheduler/mod1/locallang.xml:msg.invalidSleepTime'), t3lib_FlashMessage::ERROR);
+                       $result = false;
+               } else {
+                       $result = true;
+               }
+               return $result;
+       }
+
+       /**
+        * This method is used to save any additional input into the current task object
+        * if the task class matches
+        *
+        * @param       array                           $submittedData: array containing the data submitted by the user
+        * @param       tx_scheduler_Task       $task: reference to the current task object
+        * @return      void
+        */
+       public function saveAdditionalFields(array $submittedData, tx_scheduler_Task $task) {
+               $task->sleepTime = $submittedData['sleepTime'];
+       }
+}
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/scheduler/examples/class.tx_scheduler_sleeptask_additionalfieldprovider.php'])        {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/scheduler/examples/class.tx_scheduler_sleeptask_additionalfieldprovider.php']);
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/scheduler/examples/class.tx_scheduler_testtask.php b/typo3/sysext/scheduler/examples/class.tx_scheduler_testtask.php
new file mode 100644 (file)
index 0000000..4246f9d
--- /dev/null
@@ -0,0 +1,127 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2008 Markus Friedrich (markus.friedrich@dkd.de)
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+require_once(PATH_t3lib . 'class.t3lib_htmlmail.php');
+
+/**
+ * Class "tx_scheduler_TestTask" provides testing procedures
+ *
+ * @author             Markus Friedrich <markus.friedrich@dkd.de>
+ * @package            TYPO3
+ * @subpackage tx_scheduler
+ *
+ * $Id: class.tx_scheduler_testtask.php 1262 2009-09-15 21:04:22Z francois $
+ */
+class tx_scheduler_TestTask extends tx_scheduler_Task {
+
+       /**
+        * An email address to be used during the process
+        *
+        * @var string          $email
+        */
+        var $email;
+
+       /**
+        * Function executed from the Scheduler.
+        * Sends an email
+        *
+        * @return      void
+        */
+       public function execute() {
+               $success = false;
+
+               if (!empty($this->email)) {
+                               // If an email address is defined, send a message to it
+
+                               // NOTE: the TYPO3_DLOG constant is not used in this case, as this is a test task
+                               // and debugging is its main purpose anyway
+                       t3lib_div::devLog('[tx_scheduler_TestTask]: Test email sent to "' . $this->email . '"', 'scheduler', 0);
+
+                               // Get execution information
+                       $exec = $this->getExecution();
+
+                               // Get call method
+                       if (basename(PATH_thisScript) == 'cli_dispatch.phpsh') {
+                               $calledBy = 'CLI module dispatcher';
+                               $site = '-';
+                       } else {
+                               $calledBy = 'TYPO3 backend';
+                               $site = t3lib_div::getIndpEnv('TYPO3_SITE_URL');
+                       }
+
+                       $start = $exec->getStart();
+                       $end = $exec->getEnd();
+                       $interval = $exec->getInterval();
+                       $multiple = $exec->getMultiple();
+                       $cronCmd = $exec->getCronCmd();
+                       $mailBody =
+                               'SCHEDULER TEST-TASK' . chr(10)
+                               . '- - - - - - - - - - - - - - - -' . chr(10)
+                               . 'UID: ' . $this->taskUid . chr(10)
+                               . 'Sitename: ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . chr(10)
+                               . 'Site: ' . $site . chr(10)
+                               . 'Called by: ' . $calledBy . chr(10)
+                               . 'tstamp: ' . date('Y-m-d H:i:s') . ' [' . time() . ']' . chr(10)
+                               . 'maxLifetime: ' . $this->scheduler->extConf['maxLifetime'] . chr(10)
+                               . 'start: ' . date('Y-m-d H:i:s', $start) . ' [' . $start . ']' . chr(10)
+                               . 'end: ' . ((empty($end)) ? '-' : (date('Y-m-d H:i:s', $end) . ' [' . $end . ']')) . chr(10)
+                               . 'interval: ' . $interval . chr(10)
+                               . 'multiple: ' . ($multiple ? 'yes' : 'no') . chr(10)
+                               . 'cronCmd: ' . ($cronCmd ? $cronCmd : 'not used');
+
+                               // Prepare mailer and send the mail
+                       $mailer = t3lib_div::makeInstance('t3lib_htmlmail');
+                       $mailer->from_email = $this->email;
+                       $mailer->from_name = 'SCHEDULER TEST-TASK';
+                       $mailer->replyto_email = $this->email;
+                       $mailer->replyto_name = 'SCHEDULER TEST-TASK';
+                       $mailer->subject = 'SCHEDULER TEST-TASK';
+                       $mailer->setPlain($mailer->encodeMsg($mailBody));
+                       $mailer->setRecipient($this->email);
+                       $mailer->setHeaders();
+                       $mailer->setContent();
+                       $success = $mailer->sendtheMail();
+               } else {
+                               // No email defined, just log the task
+                       t3lib_div::devLog('[tx_scheduler_TestTask]: No email address given', 'scheduler', 2);
+               }
+
+               return $success;
+       }
+
+       /**
+        * This method returns the destination mail address as additional information
+        *
+        * @return      string  Information to display
+        */
+       public function getAdditionalInformation() {
+               return $GLOBALS['LANG']->sL('LLL:EXT:scheduler/mod1/locallang.xml:label.email') . ': ' . $this->email;
+       }
+}
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/scheduler/examples/class.tx_scheduler_testtask.php']) {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/scheduler/examples/class.tx_scheduler_testtask.php']);
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/scheduler/examples/class.tx_scheduler_testtask_additionalfieldprovider.php b/typo3/sysext/scheduler/examples/class.tx_scheduler_testtask_additionalfieldprovider.php
new file mode 100644 (file)
index 0000000..c47af55
--- /dev/null
@@ -0,0 +1,120 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Francois Suter <francois@typo3.org>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+/**
+ * Aditional fields provider class for usage with the Scheduler's test task
+ *
+ * @author             Francois Suter <francois@typo3.org>
+ * @package            TYPO3
+ * @subpackage tx_scheduler
+ *
+ * $Id: class.tx_scheduler_testtask_additionalfieldprovider.php 1262 2009-09-15 21:04:22Z francois $
+ */
+class tx_scheduler_TestTask_AdditionalFieldProvider implements tx_scheduler_AdditionalFieldProvider {
+
+       /**
+        * This method is used to define new fields for adding or editing a task
+        * In this case, it adds an email field
+        *
+        * @param       array                                   $taskInfo: reference to the array containing the info used in the add/edit form
+        * @param       object                                  $task: when editing, reference to the current task object. Null when adding.
+        * @param       tx_scheduler_Module             $parentObject: reference to the calling object (Scheduler's BE module)
+        * @return      array                                   Array containg all the information pertaining to the additional fields
+        *                                                                      The array is multidimensional, keyed to the task class name and each field's id
+        *                                                                      For each field it provides an associative sub-array with the following:
+        *                                                                              ['code']                => The HTML code for the field
+        *                                                                              ['label']               => The label of the field (possibly localized)
+        *                                                                              ['cshKey']              => The CSH key for the field
+        *                                                                              ['cshLabel']    => The code of the CSH label
+        */
+       public function getAdditionalFields(array &$taskInfo, $task, tx_scheduler_Module $parentObject) {
+
+                       // Initialize extra field value
+               if (empty($taskInfo['email'])) {
+                       if ($parentObject->CMD == 'add') {
+                                       // In case of new task and if field is empty, set default email address
+                               $taskInfo['email'] = $GLOBALS['BE_USER']->user['email'];
+
+                       } elseif ($parentObject->CMD == 'edit') {
+                                       // In case of edit, and editing a test task, set to internal value if not data was submitted already
+                               $taskInfo['email'] = $task->email;
+                       } else {
+                                       // Otherwise set an empty value, as it will not be used anyway
+                               $taskInfo['email'] = '';
+                       }
+               }
+
+                       // Write the code for the field
+               $fieldID = 'task_email';
+               $fieldCode = '<input type="text" name="tx_scheduler[email]" id="' . $fieldID . '" value="' . $taskInfo['email'] . '" size="30" />';
+               $additionalFields = array();
+               $additionalFields[$fieldID] = array(
+                       'code'     => $fieldCode,
+                       'label'    => 'LLL:EXT:scheduler/mod1/locallang.xml:label.email',
+                       'cshKey'   => '_MOD_tools_txschedulerM1',
+                       'cshLabel' => $fieldID
+               );
+
+               return $additionalFields;
+       }
+
+       /**
+        * This method checks any additional data that is relevant to the specific task
+        * If the task class is not relevant, the method is expected to return true
+        *
+        * @param       array                                   $submittedData: reference to the array containing the data submitted by the user
+        * @param       tx_scheduler_Module             $parentObject: reference to the calling object (Scheduler's BE module)
+        * @return      boolean                                 True if validation was ok (or selected class is not relevant), false otherwise
+        */
+       public function validateAdditionalFields(array &$submittedData, tx_scheduler_Module $parentObject) {
+               $submittedData['email'] = trim($submittedData['email']);
+
+               if (empty($submittedData['email'])) {
+                       $parentObject->addMessage($GLOBALS['LANG']->sL('LLL:EXT:scheduler/mod1/locallang.xml:msg.noEmail'), t3lib_FlashMessage::ERROR);
+                       $result = false;
+               } else {
+                       $result = true;
+               }
+
+               return $result;
+       }
+
+       /**
+        * This method is used to save any additional input into the current task object
+        * if the task class matches
+        *
+        * @param       array                           $submittedData: array containing the data submitted by the user
+        * @param       tx_scheduler_Task       $task: reference to the current task object
+        * @return      void
+        */
+       public function saveAdditionalFields(array $submittedData, tx_scheduler_Task $task) {
+               $task->email = $submittedData['email'];
+       }
+}
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/scheduler/examples/class.tx_scheduler_testtask_additionalfieldprovider.php']) {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/scheduler/examples/class.tx_scheduler_testtask_additionalfieldprovider.php']);
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/scheduler/ext_autoload.php b/typo3/sysext/scheduler/ext_autoload.php
new file mode 100644 (file)
index 0000000..49d57d9
--- /dev/null
@@ -0,0 +1,19 @@
+<?php
+/*
+ * Register necessary class names with autoloader
+ *
+ * $Id: ext_autoload.php 1190 2009-09-03 18:01:00Z francois $
+ */
+// TODO: document necessity of providing autoloader information
+return array(
+       'tx_scheduler'                                                                          => t3lib_extMgm::extPath('scheduler', 'class.tx_scheduler.php'),
+       'tx_scheduler_croncmd'                                                          => t3lib_extMgm::extPath('scheduler', 'class.tx_scheduler_croncmd.php'),
+       'tx_scheduler_task'                                                                     => t3lib_extMgm::extPath('scheduler', 'class.tx_scheduler_task.php'),
+       'tx_scheduler_execution'                                                        => t3lib_extMgm::extPath('scheduler', 'class.tx_scheduler_execution.php'),
+       'tx_scheduler_failedexecutionexception'                         => t3lib_extMgm::extPath('scheduler', 'class.tx_scheduler_failedexecutionexception.php'),
+       'tx_scheduler_testtask'                                                         => t3lib_extMgm::extPath('scheduler', 'examples/class.tx_scheduler_testtask.php'),
+       'tx_scheduler_testtask_additionalfieldprovider'         => t3lib_extMgm::extPath('scheduler', 'examples/class.tx_scheduler_testtask_additionalfieldprovider.php'),
+       'tx_scheduler_sleeptask'                                                        => t3lib_extMgm::extPath('scheduler', 'examples/class.tx_scheduler_sleeptask.php'),
+       'tx_scheduler_sleeptask_additionalfieldprovider'        => t3lib_extMgm::extPath('scheduler', 'examples/class.tx_scheduler_sleeptask_additionalfieldprovider.php')
+);
+?>
diff --git a/typo3/sysext/scheduler/ext_conf_template.txt b/typo3/sysext/scheduler/ext_conf_template.txt
new file mode 100644 (file)
index 0000000..f4e257d
--- /dev/null
@@ -0,0 +1,2 @@
+# cat=basic//40; type=string; label=Maximum lifetime: Enter the maximum lifetime (in minutes) of a scheduler task. If a task is still running after that time, it will be dropped from the execution list (but not stopped).
+maxLifetime = 1440
diff --git a/typo3/sysext/scheduler/ext_emconf.php b/typo3/sysext/scheduler/ext_emconf.php
new file mode 100755 (executable)
index 0000000..cf3de0c
--- /dev/null
@@ -0,0 +1,52 @@
+<?php
+
+########################################################################
+# Extension Manager/Repository config file for ext: "scheduler"
+#
+# Auto generated 03-08-2009 22:49
+#
+# Manual updates:
+# Only the data in the array - anything else is removed by next write.
+# "version" and "dependencies" must not be touched!
+########################################################################
+
+$EM_CONF[$_EXTKEY] = array(
+       'title' => 'Scheduler',
+       'description' => 'The TYPO3 Scheduler let\'s you register tasks to happen at a specific time',
+       'category' => 'misc',
+       'shy' => 0,
+       'version' => '1.0.0',
+       'dependencies' => '',
+       'conflicts' => 'gabriel',
+       'priority' => '',
+       'loadOrder' => '',
+       'module' => 'mod1',
+       'doNotLoadInFE' => 1,
+       'state' => 'stable',
+       'uploadfolder' => 0,
+       'createDirs' => '',
+       'modify_tables' => '',
+       'clearcacheonload' => 0,
+       'lockType' => '',
+       'author' => 'Francois Suter',
+       'author_email' => 'francois@typo3.org',
+       'author_company' => '',
+       'CGLcompliance' => '',
+       'CGLcompliance_note' => '',
+       'constraints' => array(
+               'depends' => array(
+                       'php' => '5.2.0-0.0.0',
+                       'typo3' => '4.3.0-0.0.0',
+               ),
+               'conflicts' => array(
+                       'gabriel' => '',
+               ),
+               'suggests' => array(
+               ),
+       ),
+       '_md5_values_when_last_written' => 'a:32:{s:9:"ChangeLog";s:4:"0f49";s:10:"README.txt";s:4:"022a";s:22:"class.tx_scheduler.php";s:4:"6605";s:30:"class.tx_scheduler_croncmd.php";s:4:"5ba8";s:32:"class.tx_scheduler_execution.php";s:4:"61ba";s:27:"class.tx_scheduler_task.php";s:4:"aa46";s:16:"ext_autoload.php";s:4:"e4c9";s:21:"ext_conf_template.txt";s:4:"07a2";s:12:"ext_icon.gif";s:4:"42ba";s:17:"ext_localconf.php";s:4:"c5e3";s:14:"ext_tables.php";s:4:"2ba4";s:14:"ext_tables.sql";s:4:"2185";s:13:"locallang.xml";s:4:"89d7";s:30:"cli/scheduler_cli_dispatch.php";s:4:"4e39";s:14:"doc/manual.sxw";s:4:"5629";s:41:"examples/class.tx_scheduler_sleeptask.php";s:4:"ee07";s:46:"examples/class.tx_scheduler_sleeptask_hook.php";s:4:"8738";s:40:"examples/class.tx_scheduler_testtask.php";s:4:"0030";s:45:"examples/class.tx_scheduler_testtask_hook.php";s:4:"ca4c";s:14:"mod1/clear.gif";s:4:"cc11";s:13:"mod1/conf.php";s:4:"ab0d";s:14:"mod1/index.php";s:4:"abe7";s:18:"mod1/locallang.xml";s:4:"e2eb";s:32:"mod1/locallang_csh_scheduler.xml";s:4:"668a";s:22:"mod1/locallang_mod.xml";s:4:"76ff";s:22:"mod1/mod_template.html";s:4:"7150";s:19:"mod1/moduleicon.gif";s:4:"42ba";s:23:"res/tx_scheduler_be.css";s:4:"6daa";s:22:"res/tx_scheduler_be.js";s:4:"2238";s:17:"res/gfx/error.png";s:4:"e4dd";s:14:"res/gfx/ok.png";s:4:"8bfe";s:19:"res/gfx/warning.png";s:4:"c847";}',
+       'suggests' => array(
+       ),
+);
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/scheduler/ext_icon.gif b/typo3/sysext/scheduler/ext_icon.gif
new file mode 100644 (file)
index 0000000..30dfa3f
Binary files /dev/null and b/typo3/sysext/scheduler/ext_icon.gif differ
diff --git a/typo3/sysext/scheduler/ext_localconf.php b/typo3/sysext/scheduler/ext_localconf.php
new file mode 100644 (file)
index 0000000..86a6232
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+/* $Id: ext_localconf.php 1262 2009-09-15 21:04:22Z francois $ */
+
+if (!defined ('TYPO3_MODE')) {
+       die ('Access denied.');
+}
+
+       // Register the Scheduler as a possible key for CLI calls
+$TYPO3_CONF_VARS['SC_OPTIONS']['GLOBAL']['cliKeys'][$_EXTKEY] = array(
+       'EXT:' . $_EXTKEY . '/cli/scheduler_cli_dispatch.php', '_CLI_scheduler'
+);
+
+       // Register information for the test and sleep tasks
+$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks']['tx_scheduler_TestTask'] = array(
+       'extension'        => $_EXTKEY,
+       'title'            => 'LLL:EXT:' . $_EXTKEY . '/locallang.xml:testTask.name',
+       'description'      => 'LLL:EXT:' . $_EXTKEY . '/locallang.xml:testTask.description',
+       'additionalFields' => 'tx_scheduler_TestTask_AdditionalFieldProvider'
+);
+$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks']['tx_scheduler_SleepTask'] = array(
+       'extension'        => $_EXTKEY,
+       'title'            => 'LLL:EXT:' . $_EXTKEY . '/locallang.xml:sleepTask.name',
+       'description'      => 'LLL:EXT:' . $_EXTKEY . '/locallang.xml:sleepTask.description',
+       'additionalFields' => 'tx_scheduler_SleepTask_AdditionalFieldProvider'
+);
+?>
\ No newline at end of file
diff --git a/typo3/sysext/scheduler/ext_tables.php b/typo3/sysext/scheduler/ext_tables.php
new file mode 100755 (executable)
index 0000000..035dfe5
--- /dev/null
@@ -0,0 +1,15 @@
+<?php
+/* $Id: ext_tables.php 802 2009-06-12 09:36:03Z francois $ */
+
+if (!defined('TYPO3_MODE')) {
+       die ('Access denied.');
+}
+
+if (TYPO3_MODE == 'BE') {
+               // Add module
+       t3lib_extMgm::addModule('tools', 'txschedulerM1', '', t3lib_extMgm::extPath($_EXTKEY) . 'mod1/');
+
+               // Add context sensitive help (csh) to the backend module
+       t3lib_extMgm::addLLrefForTCAdescr('_MOD_tools_txschedulerM1', 'EXT:' . $_EXTKEY . '/mod1/locallang_csh_scheduler.xml');
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/scheduler/ext_tables.sql b/typo3/sysext/scheduler/ext_tables.sql
new file mode 100755 (executable)
index 0000000..2c62dcc
--- /dev/null
@@ -0,0 +1,17 @@
+#
+# Table structure for table 'tx_scheduler_task'
+#
+CREATE TABLE tx_scheduler_task (
+       uid int(11) unsigned DEFAULT '0' NOT NULL auto_increment,
+       crdate int(11) unsigned DEFAULT '0' NOT NULL,
+       disable tinyint(4) unsigned DEFAULT '0' NOT NULL,
+       classname varchar(255) DEFAULT '' NOT NULL,
+       nextexecution int(11) unsigned DEFAULT '0' NOT NULL,
+       lastexecution_time int(11) unsigned DEFAULT '0' NOT NULL,
+       lastexecution_failure text NOT NULL,
+       lastexecution_context char(3) DEFAULT '' NOT NULL,
+       serialized_task_object text NOT NULL,
+       serialized_executions text NOT NULL,
+       PRIMARY KEY (uid),
+       KEY index_nextexecution (nextexecution)
+);
diff --git a/typo3/sysext/scheduler/interfaces/interface.tx_scheduler_additionalfieldprovider.php b/typo3/sysext/scheduler/interfaces/interface.tx_scheduler_additionalfieldprovider.php
new file mode 100644 (file)
index 0000000..49b6b6c
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Ingo Renner <ingo@typo3.org>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+
+/**
+ * Interface for classes who want to provide additional fields when adding a task
+ *
+ * @author             Ingo Renner <ingo@typo3.org>
+ * @package            TYPO3
+ * @subpackage tx_scheduler
+ *
+ * $Id: interface.tx_scheduler_additionalfieldprovider.php 1262 2009-09-15 21:04:22Z francois $
+ */
+interface tx_scheduler_AdditionalFieldProvider {
+
+       /**
+        * Gets additional fields to render in the form to add/edit a task
+        *
+        * @param       array                                   Values of the fields from the add/edit task form
+        * @param       tx_scheduler_Task               The task object being eddited. Null when adding a task!
+        * @param       tx_scheduler_Module             Reference to the scheduler backend module
+        * @return      array                                   A two dimensional array, array('Identifier' => array('fieldId' => array('code' => '', 'label' => '', 'cshKey' => '', 'cshLabel' => ''))
+        */
+       public function getAdditionalFields(array &$taskInfo, $task, tx_scheduler_Module $schedulerModule);
+
+       /**
+        * Validates the additional fields' values
+        *
+        * @param       array                                   An array containing the data submitted by the add/edit task form
+        * @param       tx_scheduler_Module             Reference to the scheduler backend module
+        * @return      boolean                                 True if validation was ok (or selected class is not relevant), false otherwise
+        */
+       public function validateAdditionalFields(array &$submittedData, tx_scheduler_Module $schedulerModule);
+
+       /**
+        * Takes care of saving the additional fields' values in the task's object
+        *
+        * @param       array                                   An array containing the data submitted by the add/edit task form
+        * @param       tx_scheduler_Module             Reference to the scheduler backend module
+        * @return      void
+        */
+       public function saveAdditionalFields(array $submittedData, tx_scheduler_Task $task);
+}
+
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/scheduler/locallang.xml b/typo3/sysext/scheduler/locallang.xml
new file mode 100644 (file)
index 0000000..ae5a0ea
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<T3locallang>
+       <meta type="array">
+               <type>module</type>
+               <description>General language labels for extension 'scheduler'</description>
+       </meta>
+       <data type="array">
+               <languageKey index="default" type="array">
+                       <label index="testTask.name">Scheduler test task</label>
+                       <label index="testTask.description">The Scheduler test task just sends a mail to a given email address. It is designed to be used for testing purposes.</label>
+                       <label index="sleepTask.name">Scheduler sleep task</label>
+                       <label index="sleepTask.description">This task does nothing but put PHP to sleep for a number of seconds. It is designed to test multiple executions.</label>
+               </languageKey>
+       </data>
+</T3locallang>
\ No newline at end of file
diff --git a/typo3/sysext/scheduler/mod1/clear.gif b/typo3/sysext/scheduler/mod1/clear.gif
new file mode 100755 (executable)
index 0000000..9ed1269
Binary files /dev/null and b/typo3/sysext/scheduler/mod1/clear.gif differ
diff --git a/typo3/sysext/scheduler/mod1/conf.php b/typo3/sysext/scheduler/mod1/conf.php
new file mode 100755 (executable)
index 0000000..6106e24
--- /dev/null
@@ -0,0 +1,9 @@
+<?php
+$MLANG['default']['tabs_images']['tab'] = 'moduleicon.gif';
+$MLANG['default']['ll_ref'] = 'LLL:EXT:scheduler/mod1/locallang_mod.xml';
+
+$MCONF['access'] = 'admin';
+$MCONF['script'] = '_DISPATCH';
+
+$MCONF['name'] = 'tools_txschedulerM1';
+?>
\ No newline at end of file
diff --git a/typo3/sysext/scheduler/mod1/index.php b/typo3/sysext/scheduler/mod1/index.php
new file mode 100755 (executable)
index 0000000..c104178
--- /dev/null
@@ -0,0 +1,1402 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Francois Suter <francois@typo3.org>
+*  (c) 2005 Christian Jul Jensen <julle@typo3.org>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+require_once (t3lib_extMgm::extPath('scheduler') . 'interfaces/interface.tx_scheduler_additionalfieldprovider.php');
+
+$LANG->includeLLFile('EXT:scheduler/mod1/locallang.xml');
+$BE_USER->modAccess($MCONF, 1); // This checks permissions and exits if the users has no permission for entry.
+
+/**
+ * Module 'TYPO3 Scheduler administration module' for the 'scheduler' extension.
+ *
+ * @author             Francois Suter <francois@typo3.org>
+ * @author             Christian Jul Jensen <julle@typo3.org>
+ * @author             Ingo Renner <ingo@typo3.org>
+ * @package            TYPO3
+ * @subpackage tx_scheduler
+ * @version            $Id: index.php 1262 2009-09-15 21:04:22Z francois $
+ */
+class tx_scheduler_Module extends t3lib_SCbase {
+
+       /**
+        * Back path to typo3 main dir
+        *
+        * @var string          $backPath
+        */
+       public $backPath;
+
+       /**
+        * Array containing submitted data when editing or adding a task
+        *
+        * @var array           $submittedData
+        */
+       protected $submittedData = array();
+
+       /**
+        * Array containing all messages issued by the application logic
+        * Contains the error's severity and the message itself
+        *
+        * @var array   $messages
+        */
+       protected $messages = array();
+
+       /**
+        * @var string  Key of the CSH file
+        */
+       protected $cshKey;
+
+       /**
+        *
+        * @var tx_scheduler    Local scheduler instance
+        */
+       protected $scheduler;
+
+       /**
+        * Constructor
+        *
+        * @return      void
+        */
+       public function __construct() {
+               $this->backPath = $GLOBALS['BACK_PATH'];
+                       // Set key for CSH
+               $this->cshKey = '_MOD_' . $GLOBALS['MCONF']['name'];
+       }
+
+       /**
+        * Initializes the backend module
+        *
+        * @return      void
+        */
+       public function init() {
+               parent::init();
+
+                       // Initialize document
+               $this->doc = t3lib_div::makeInstance('template');
+               $this->doc->setModuleTemplate(t3lib_extMgm::extPath('scheduler') . 'mod1/mod_template.html');
+               $this->doc->addStyleSheet('tx_scheduler', t3lib_extMgm::extRelPath('scheduler') . 'res/tx_scheduler_be.css');
+               $this->doc->backPath = $this->backPath;
+               $this->doc->bodyTagId = 'typo3-mod-php';
+               $this->doc->bodyTagAdditions = 'class="tx_scheduler_mod1"';
+
+                       // Create scheduler instance
+               $this->scheduler = t3lib_div::makeInstance('tx_scheduler');
+       }
+
+       /**
+        * Adds items to the ->MOD_MENU array. Used for the function menu selector.
+        *
+        * @return      void
+        */
+       public function menuConfig() {
+               $this->MOD_MENU = array(
+                       'function' => array(
+                               'scheduler' => $GLOBALS['LANG']->getLL('function.scheduler'),
+                               'check'     => $GLOBALS['LANG']->getLL('function.check'),
+                               'info'      => $GLOBALS['LANG']->getLL('function.info'),
+                       )
+               );
+
+               parent::menuConfig();
+       }
+
+       /**
+        * Main function of the module. Write the content to $this->content
+        *
+        * @return      void
+        */
+       public function main() {
+                       // Access check!
+                       // The page will show only if user has admin rights
+               if ($GLOBALS['BE_USER']->user['admin']) {
+
+                               // Set the form
+                       $this->doc->form = '<form name="tx_scheduler_form" id="tx_scheduler_form" method="post" action="">';
+
+                               // JavaScript for main function menu
+                       $this->doc->JScode = '
+                               <script language="javascript" type="text/javascript">
+                                       script_ended = 0;
+                                       function jumpToUrl(URL) {
+                                               document.location = URL;
+                                       }
+                               </script>
+                       ';
+
+                               // Prepare main content
+                       $this->content  = $this->doc->header(
+                               $GLOBALS['LANG']->getLL('function.' . $this->MOD_SETTINGS['function'])
+                       );
+                       $this->content .= $this->doc->spacer(5);
+                       $this->content .= $this->getModuleContent();
+               } else {
+                               // If no access, only display the module's title
+                       $this->content  = $this->doc->header($GLOBALS['LANG']->getLL('title'));
+                       $this->content .= $this->doc->spacer(5);
+               }
+
+                       // Place content inside template
+               $content  = $this->doc->startPage($GLOBALS['LANG']->getLL('title'));
+               $content .= $this->doc->moduleBody(
+                       array(),
+                       $this->getDocHeaderButtons(),
+                       $this->getTemplateMarkers()
+               );
+               $content .= $this->doc->endPage();
+
+                       // Replace content with templated content
+               $this->content = $content;
+       }
+
+       /**
+        * Generate the module's content
+        *
+        * @return      string  HTML of the module's main content
+        */
+       protected function getModuleContent() {
+               $content = '';
+
+                       // Get submitted data
+               $this->submittedData = t3lib_div::_GPmerged('tx_scheduler');
+
+                       // If a save command was submitted, handle saving now
+               if ($this->CMD == 'save') {
+                       $previousCMD = t3lib_div::_GP('previousCMD');
+                               // First check the submitted data
+                       $result = $this->preprocessData();
+
+                               // If result is ok, proceed with saving
+                       if ($result) {
+                               $this->saveTask();
+                                       // Unset command, so that default screen gets displayed
+                               unset($this->CMD);
+
+                               // Errors occurred
+                               // Go back to previous step
+                       } else {
+                               $this->CMD = $previousCMD;
+                       }
+               }
+
+                       // Handle chosen action
+               switch((string)$this->MOD_SETTINGS['function']) {
+                       case 'scheduler':
+                                       // Scheduler's main screen
+                               $content .= $this->executeTasks();
+
+                                       // Execute chosen action
+                               switch ($this->CMD) {
+                                       case 'add':
+                                       case 'edit':
+                                               try {
+                                                               // Try adding or editing
+                                                       $content .= $this->editTask();
+                                                       $sectionTitle = $GLOBALS['LANG']->getLL('action.' . $this->CMD);
+                                               } catch (Exception $e) {
+                                                               // An exception may happen when the task to
+                                                               // edit could not be found. In this case revert
+                                                               // to displaying the list of tasks
+                                                               // It can also happend when attempting to edit a running task
+                                                       $content .= $this->listTasks();
+                                               }
+                                               break;
+                                       case 'delete':
+                                               $this->deleteTask();
+                                               $content .= $this->listTasks();
+                                               break;
+                                       case 'list':
+                                       default:
+                                               $content .= $this->listTasks();
+                                               break;
+                               }
+                               break;
+                       case 'check':
+                                       // Setup check screen
+                                       // TODO: move check to the report module
+                               $content .= $this->displayCheckScreen();
+                               break;
+                       case 'info':
+                                       // Information screen
+                               $content .= $this->displayInfoScreen();
+                               break;
+               }
+
+                       // Wrap the content in a section
+               return $this->doc->section($sectionTitle, '<div class="tx_scheduler_mod1">' . $content . '</div>', 0, 1);
+       }
+
+       /**
+        * This method actually prints out the module's HTML content
+        *
+        * @return      void
+        */
+       public function render() {
+               $this->content .= $this->doc->endPage();
+
+               echo $this->content;
+       }
+
+       /**
+        * This method checks the status of the '_cli_scheduler' user
+        * It will differentiate between a non-existing user and an existing,
+        * but disabled user (as per enable fields)
+        *
+        * @return      integer         -1      if user doesn't exist
+        *                                               0      if user exists, but is disabled
+        *                                               1      if user exists and is not disabled
+        */
+       protected function checkSchedulerUser() {
+               $schedulerUserStatus = -1;
+                       // Assemble base WHERE clause
+               $where = 'username = \'_cli_scheduler\' AND admin = 0' . t3lib_BEfunc::deleteClause('be_users');
+                       // Check if user exists at all
+               $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
+                       '1',
+                       'be_users',
+                       $where
+               );
+               if ($GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
+                       $schedulerUserStatus = 0;
+                               // Check if user exists and is enabled
+                       $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
+                               '1',
+                               'be_users',
+                               $where . t3lib_BEfunc::BEenableFields('be_users')
+                       );
+                       if ($GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
+                               $schedulerUserStatus = 1;
+                       }
+               }
+               $GLOBALS['TYPO3_DB']->sql_free_result($res);
+
+               return $schedulerUserStatus;
+       }
+
+       /**
+        * This method creates the "cli_scheduler" BE user if it doesn't exist
+        * 
+        * @return      void
+        */
+       protected function createSchedulerUser() {
+                       // Check _cli_scheduler user status
+               $checkUser = $this->checkSchedulerUser();
+                       // Prepare default message
+               $message = $GLOBALS['LANG']->getLL('msg.userExists');
+               $severity = t3lib_FlashMessage::WARNING;
+                       // If the user does not exist, try creating it
+               if ($checkUser == -1) {
+                               // Prepare necessary data for _cli_scheduler user creation
+                       $password = md5(uniqid('scheduler', true));
+                       $data = array('be_users' => array('NEW' => array('username' => '_cli_scheduler', 'password' => $password, 'pid' => 0)));
+                               /**
+                                * Create an instance of TCEmain and execute user creation
+                                * 
+                                * @var t3lib_TCEmain
+                                */
+                       $tcemain = t3lib_div::makeInstance('t3lib_TCEmain');
+                       $tcemain->stripslashes_values = 0;
+                       $tcemain->start($data, array());
+                       $tcemain->process_datamap();
+                               // Check if a new uid was indeed generated (i.e. a new record was created)
+                               // (counting TCEmain errors doesn't work as some failures don't report errors)
+                       $numberOfNewIDs = count($tcemain->substNEWwithIDs);
+                       if ($numberOfNewIDs == 1) {
+                               $message = $GLOBALS['LANG']->getLL('msg.userCreated');
+                               $severity = t3lib_FlashMessage::OK;
+                       } else {
+                               $message = $GLOBALS['LANG']->getLL('msg.userNotCreated');
+                               $severity = t3lib_FlashMessage::ERROR;
+                       }
+               }
+               $this->addMessage($message, $severity);
+       }
+
+       /**
+        * This method displays the result of a number of checks
+        * on whether the Scheduler is ready to run or running properly
+        *
+        * @return      string  further information
+        */
+       protected function displayCheckScreen() {
+                       // First, check if cli_sceduler user creation was requested
+               if ($this->CMD == 'user') {
+                       $this->createSchedulerUser();
+               }
+
+                       // Start generating the content
+               $content = $GLOBALS['LANG']->getLL('msg.schedulerSetupCheck');
+
+                       // Display information about last automated run, as stored in the system registry
+                       /**
+                        * @var t3lib_Registry
+                        */
+               $registry = t3lib_div::makeInstance('t3lib_Registry');
+               $lastRun = $registry->get('tx_scheduler', 'lastRun');
+               if (!is_array($lastRun)) {
+                       $message = $GLOBALS['LANG']->getLL('msg.noLastRun');
+                       $severity = t3lib_FlashMessage::WARNING;
+               } else {
+                       if (empty($lastRun['end']) || empty($lastRun['start']) || empty($lastRun['type'])) {
+                               $message = $GLOBALS['LANG']->getLL('msg.incompleteLastRun');
+                               $severity = t3lib_FlashMessage::WARNING;
+                       } else {
+               $dateFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] . ' ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'];
+                               $startDate = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'], $lastRun['start']);
+                               $startTime = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'], $lastRun['start']);
+                               $endDate = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'], $lastRun['end']);
+                               $endTime = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'], $lastRun['end']);
+                               if ($lastRun['type'] == 'manual') {
+                                       $label = 'manually';
+                               } else {
+                                       $label = 'automatically';
+                               }
+                               $type = $GLOBALS['LANG']->getLL('label.' . $label);
+                               $message = sprintf($GLOBALS['LANG']->getLL('msg.lastRun'), $type, $startDate, $startTime, $endDate, $endTime);
+                               $severity = t3lib_FlashMessage::INFO;
+                       }
+               }
+               $flashMessage = t3lib_div::makeInstance(
+                       't3lib_FlashMessage',
+                       $message,
+                       '',
+                       $severity
+               );
+               $content .= '<div class="info-block">';
+               $content .= '<h3>' . $GLOBALS['LANG']->getLL('hdg.lastRun') . '</h3>';
+               $content .= $flashMessage->render();
+               $content .= '</div>';
+
+                       // Check CLI user
+               $content .= '<div class="info-block">';
+               $content .= '<h3>' . $GLOBALS['LANG']->getLL('hdg.schedulerUser') . '</h3>';
+               $content .= '<p>' . $GLOBALS['LANG']->getLL('msg.schedulerUser') . '</p>';
+
+               $checkUser = $this->checkSchedulerUser();
+               if ($checkUser == -1) {
+                       $link = $GLOBALS['MCONF']['_'] . '&SET[function]=check&CMD=user';
+                       $message = sprintf($GLOBALS['LANG']->getLL('msg.schedulerUserMissing'), $link);
+                       $severity = t3lib_FlashMessage::ERROR;
+               } elseif ($checkUser == 0) {
+                       $message = $GLOBALS['LANG']->getLL('msg.schedulerUserFoundButDisabled');
+                       $severity = t3lib_FlashMessage::WARNING;
+               } else {
+                       $message = $GLOBALS['LANG']->getLL('msg.schedulerUserFound');
+                       $severity = t3lib_FlashMessage::OK;
+               }
+               $flashMessage = t3lib_div::makeInstance(
+                       't3lib_FlashMessage',
+                       $message,
+                       '',
+                       $severity
+               );
+               $content .= $flashMessage->render() . '</div>';
+
+                       // Check CLI script
+               $script = PATH_typo3 . 'cli_dispatch.phpsh';
+               $content .= '<div class="info-block">';
+               $content .= '<h3>' . $GLOBALS['LANG']->getLL('hdg.cliScript') . '</h3>';
+               $content .= '<p>' . sprintf($GLOBALS['LANG']->getLL('msg.cliScript'), $script) . '</p>';
+
+               if (is_executable($script)) {
+                       $message = $GLOBALS['LANG']->getLL('msg.cliScriptExecutable');
+                       $severity = t3lib_FlashMessage::OK;
+               } else {
+                       $message = $GLOBALS['LANG']->getLL('msg.cliScriptNotExecutable');
+                       $severity = t3lib_FlashMessage::ERROR;
+               }
+               $flashMessage = t3lib_div::makeInstance(
+                       't3lib_FlashMessage',
+                       $message,
+                       '',
+                       $severity
+               );
+               $content .= $flashMessage->render() . '</div>';
+
+               return $content;
+       }
+
+       /**
+        * This method gathers information about all registered tasks and displays it
+        *
+        * @return      string  HTML content to display
+        */
+       protected function displayInfoScreen() {
+               $registeredClasses = self::getRegisteredClasses();
+
+                       // Initialise table layout
+               $tableLayout = array (
+                       'table' => array ('<table border="0" cellspacing="1" cellpadding="2" style="width:auto;">', '</table>'),
+                       '0' => array (
+                               'tr' => array('<tr class="bgColor2" valign="top">', '</tr>'),
+                               'defCol' => array('<td>', '</td>')
+                       ),
+                       'defRow' => array (
+                               'tr' => array('<tr class="bgColor3-20">', '</tr>'),
+                               'defCol' => array('<td>', '</td>')
+                       )
+               );
+               $table = array();
+               $tr = 0;
+
+                       // Header row
+               $table[$tr][] = $GLOBALS['LANG']->getLL('label.name');
+               $table[$tr][] = $GLOBALS['LANG']->getLL('label.extension');
+               $table[$tr][] = $GLOBALS['LANG']->getLL('label.description');
+               $table[$tr][] = '';
+               $tr++;
+
+                       // Display information about each service
+               foreach ($registeredClasses as $class => $classInfo) {
+                       $table[$tr][] = $classInfo['title'];
+                       $table[$tr][] = $classInfo['extension'];
+                       $table[$tr][] = $classInfo['description'];
+                       $link = $GLOBALS['MCONF']['_'] . '&SET[function]=list&CMD=add&tx_scheduler[class]=' . $class;
+                       $table[$tr][] = '<a href="' . $link . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:new') . '"><img ' . t3lib_iconWorks::skinImg($this->backPath, 'gfx/new_el.gif') . ' alt="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:new') . '" /></a>';
+                       $tr++;
+               }
+
+                       // Render the table and return it
+               $content  = '<div>' . $GLOBALS['LANG']->getLL('msg.infoScreenIntro') . '<div>';
+               $content .= $this->doc->spacer(5);
+               $content .= $this->doc->table($table, $tableLayout);
+
+               return $content;
+       }
+
+       /**
+        * Delete a task from the execution queue
+        *
+        * @return      void
+        */
+       protected function deleteTask() {
+               try {
+                               // Try to fetch the task and delete it
+                               /**
+                                * @var tx_scheduler_Task
+                                */
+                       $task = $this->scheduler->fetchTask($this->submittedData['uid']);
+                               // If the task is currently running, it may not be deleted
+                       if ($task->isExecutionRunning()) {
+                               $this->addMessage($GLOBALS['LANG']->getLL('msg.maynotDeleteRunningTask'), 3);
+                       } else {
+                               if ($this->scheduler->removeTask($task)) {
+                                       $this->addMessage($GLOBALS['LANG']->getLL('msg.deleteSuccess'));
+                               } else {
+                                       $this->addMessage($GLOBALS['LANG']->getLL('msg.deleteError'), t3lib_FlashMessage::ERROR);
+                               }
+                       }
+               } catch (OutOfBoundsException $e) {
+                               // The task was not found, for some reason
+                       $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.taskNotFound'), $this->submittedData['uid']), t3lib_FlashMessage::ERROR);
+               }
+       }
+
+       /**
+        * Return a form to add a new task or edit an existing one
+        *
+        * @return      string  HTML form to add or edit a task
+        */
+       protected function editTask() {
+               $registeredClasses = self::getRegisteredClasses();
+               $content = '';
+               $taskInfo = array();
+
+               if ($this->submittedData['uid'] > 0) {
+                               // If editing, retrieve data for existing task
+                       try {
+                               $taskRecord = $this->scheduler->fetchTaskRecord($this->submittedData['uid']);
+                                       // If there's a registered execution, the task should not be edited
+                               if (!empty($taskRecord['serialized_executions'])) {
+                                       $this->addMessage($GLOBALS['LANG']->getLL('msg.maynotEditRunningTask'), 3);
+                                       throw new LogicException('Runnings tasks cannot not be edited', 1251232849);
+                               }
+                               $task = unserialize($taskRecord['serialized_task_object']);
+
+                                       // Set some task information
+                               $taskInfo['disable'] = $taskRecord['disable'];
+
+                                       // Check that the task class still exists
+                               $class = $taskRecord['classname'];
+                                       // If no class was found, issue error message
+                                       // This can happen if the extension providing a given class was uninstalled,
+                                       // but the scheduler job was not cancelled
+                               if (isset($registeredClasses[$class])) {
+                                       $taskInfo['class'] = $class;
+                               } else {
+                                       $this->addMessage($GLOBALS['LANG']->getLL('msg.invalidTaskClass'), t3lib_FlashMessage::ERROR);
+                               }
+
+                                       // Get execution information
+                               $taskInfo['start']    = $task->getExecution()->getStart();
+                               $taskInfo['end']      = $task->getExecution()->getEnd();
+                               $taskInfo['interval'] = $task->getExecution()->getInterval();
+                               $taskInfo['croncmd']  = $task->getExecution()->getCronCmd();
+                               $taskInfo['multiple'] = $task->getExecution()->getMultiple();
+
+                               if (!empty($taskInfo['interval']) || !empty($taskInfo['croncmd'])) {
+                                               // Guess task type from the existing information
+                                               // If an interval or a cron command is defined, it's a recurring task
+
+                                               // FIXME remove magic numbers for the type, use class constants instead
+                                       $taskInfo['type']      = 2;
+                                       $taskInfo['frequency'] = (empty($taskInfo['interval'])) ? $taskInfo['croncmd'] : $taskInfo['interval'];
+                               } else {
+                                               // It's not a recurring task
+                                               // Make sure interval and cron command are both empty
+                                       $taskInfo['type']      = 1;
+                                       $taskInfo['frequency'] = '';
+                                       $taskInfo['end']       = 0;
+                               }
+                       } catch (OutOfBoundsException $e) {
+                                       // Add a message and continue throwing the exception
+                               $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.taskNotFound'), $this->submittedData['uid']), t3lib_FlashMessage::ERROR);
+                               throw $e;
+                       }
+               } else {
+                               // If adding a new object, set some default values
+                       $taskInfo['class'] = key($registeredClasses);
+                       $taskInfo['type'] = 2;
+                       $taskInfo['start'] = $GLOBALS['EXEC_TIME'];
+                       $taskInfo['end'] = '';
+                       $taskInfo['frequency'] = '';
+                       $taskInfo['multiple'] = 0;
+               }
+
+               if (count($this->submittedData) > 0) {
+                               // If some data was already submitted, use it to override
+                               // existing data
+                       $taskInfo = t3lib_div::array_merge_recursive_overrule($taskInfo, $this->submittedData);
+               }
+
+                       // Get the extra fields to display for each task that needs some
+               $allAdditionalFields = array();
+               foreach ($registeredClasses as $class => $registrationInfo) {
+                       if (!empty($registrationInfo['provider'])) {
+                               $providerObject = t3lib_div::getUserObj($registrationInfo['provider']);
+                               if ($providerObject instanceof tx_scheduler_AdditionalFieldProvider) {
+                                       $additionalFields = $providerObject->getAdditionalFields($taskInfo, isset($task) ? $task : null, $this);
+                                       $allAdditionalFields = array_merge($allAdditionalFields, array($class => $additionalFields));
+                               }
+                       }
+               }
+
+                       // Load necessary JavaScript
+               $this->doc->loadExtJS();
+               $this->doc->loadJavascriptLib(t3lib_extMgm::extRelPath('scheduler') . 'res/tx_scheduler_be.js');
+               $this->doc->addJsFile($this->backPath . '../t3lib/js/extjs/tceforms.js');
+
+                       // Define settings for Date Picker
+               $typo3Settings = array(
+                       'datePickerUSmode' => $GLOBALS['TYPO3_CONF_VARS']['SYS']['USdateFormat'] ? 1 : 0,
+                       'dateFormat'       => array('j-n-Y', 'G:i j-n-Y'),
+                       'dateFormatUS'     => array('n-j-Y', 'G:i n-j-Y'),
+               );
+               $this->doc->addInlineSettingArray('', $typo3Settings);
+
+                       // Define table layout for add/edit form
+               $tableLayout = array (
+                       'table' => array ('<table border="0" cellspacing="1" cellpadding="0" id="edit_form">', '</table>'),
+               );
+
+                       // Define a style for hiding
+                       // Some fields will be hidden when the task is not recurring
+               if ($taskInfo['type'] == 1) {
+                       $style = ' style="display: none"';
+               } else {
+                       $style = '';
+               }
+
+                       // Start rendering the add/edit form
+               $content .= '<input type="hidden" name="tx_scheduler[uid]" value="' . $this->submittedData['uid'] . '" />';
+               $content .= '<input type="hidden" name="previousCMD" value="' . $this->CMD . '" />';
+               $content .= '<input type="hidden" name="CMD" value="save" />';
+
+               $table = array();
+               $tr = 0;
+               $defaultCell = array('<td class="bgColor5">', '</td>');
+
+                       // Disable checkbox
+               $table[$tr][] = t3lib_BEfunc::cshItem($this->cshKey, 'task_disable', $this->backPath, '|', false, 'margin-bottom:0px;');
+               $table[$tr][] = '<label for="task_disable">' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:disable') . '</lang>';
+               $table[$tr][] =
+                       '<input type="hidden"   name="tx_scheduler[disable]" value="0" />
+                        <input type="checkbox" name="tx_scheduler[disable]" value="1" id="task_disable"' . ($taskInfo['disable'] == 1 ? ' checked="checked"' : '') . ' />';
+               $tableLayout[$tr] = array (
+                       'tr'     => array('<tr id="task_disable_row">', '</tr>'),
+                       'defCol' => $defaultCell
+               );
+               $tr++;
+
+                       // Task class selector
+               $table[$tr][] = t3lib_BEfunc::cshItem($this->cshKey, 'task_class', $this->backPath, '|', false, 'margin-bottom:0px;');
+               $table[$tr][] = '<label for="task_class">'.$GLOBALS['LANG']->getLL('label.class').'</label>';
+                       // On editing, don't allow changing of the task class, unless it was not valid
+               if ($this->submittedData['uid'] > 0 && !empty($taskInfo['class'])) {
+                       $cell = $registeredClasses[$taskInfo['class']]['title'] . ' (' . $registeredClasses[$taskInfo['class']]['extension'] . ')';
+                       $cell .= '<input type="hidden" name="tx_scheduler[class]" id="task_class" value="' . $taskInfo['class'] . '" />';
+               } else {
+                       $cell = '<select name="tx_scheduler[class]" id="task_class" class="wide" onchange="actOnChangedTaskClass(this)">';
+                               // Loop on all registered classes to display a selector
+                       foreach ($registeredClasses as $class => $classInfo) {
+                               $selected = ($class == $taskInfo['class']) ? ' selected="selected"' : '';
+                               $cell .= '<option value="' . $class . '"' . $selected . '>' . $classInfo['title'] . ' (' . $classInfo['extension'] . ')' . '</option>';
+                       }
+                       $cell .= '</select>';
+               }
+               $table[$tr][] = $cell;
+                       // Make sure each row has a unique id, for manipulation with JS
+               $tableLayout[$tr] = array (
+                       'tr'     => array('<tr id="task_class_row">', '</tr>'),
+                       'defCol' => $defaultCell
+               );
+               $tr++;
+
+                       // Task type selector
+               $table[$tr][] = t3lib_BEfunc::cshItem($this->cshKey, 'task_type', $this->backPath, '|', false, 'margin-bottom:0px;');
+               $table[$tr][] = '<label for="task_type">' . $GLOBALS['LANG']->getLL('label.type') . '</label>';
+               $table[$tr][] =
+                       '<select name="tx_scheduler[type]" id="task_type" onchange="actOnChangedTaskType(this)">' .
+                               '<option value="1"' . ($taskInfo['type'] == 1 ? ' selected="selected"' : '') . '>' . $GLOBALS['LANG']->getLL('label.type.single') . '</option>' .
+                               '<option value="2"' . ($taskInfo['type'] == 2 ? ' selected="selected"' : '') . '>' . $GLOBALS['LANG']->getLL('label.type.recurring') . '</option>' .
+                       '</select>';
+               $tableLayout[$tr] = array (
+                       'tr'     => array('<tr id="task_type_row">', '</tr>'),
+                       'defCol' => $defaultCell
+               );
+               $tr++;
+
+                       // Start date/time field
+                       // NOTE: datetime fields need a special id naming scheme
+               $table[$tr][] = t3lib_BEfunc::cshItem($this->cshKey, 'task_start', $this->backPath, '|', false, 'margin-bottom:0px;');
+               $table[$tr][] = '<label for="task_start">' . $GLOBALS['LANG']->getLL('label.start') . '</lang>';
+               $table[$tr][] = '<input name="tx_scheduler[start]" type="text" id="tceforms-datetimefield-task_start" value="' . strftime('%H:%M %d-%m-%Y', $taskInfo['start']) . '" />' .
+                       '<img' . t3lib_iconWorks::skinImg($this->backPath, 'gfx/datepicker.gif', '', 0) . ' style="cursor:pointer; vertical-align:middle;" alt=""' . ' id="picker-tceforms-datetimefield-task_start" />';;
+               $tableLayout[$tr] = array (
+                               'tr' => array('<tr id="task_start_row">', '</tr>'),
+                               'defCol' => $defaultCell
+                       );
+               $tr++;
+
+                       // End date/time field
+                       // NOTE: datetime fields need a special id naming scheme
+               $table[$tr][] = t3lib_BEfunc::cshItem($this->cshKey, 'task_end', $this->backPath, '|', false, 'margin-bottom:0px;');
+               $table[$tr][] = '<label for="task_end">' . $GLOBALS['LANG']->getLL('label.end') . '</lang>';
+               $table[$tr][] = '<input name="tx_scheduler[end]" type="text" id="tceforms-datetimefield-task_end" value="' . ((empty($taskInfo['end'])) ? '' : strftime('%H:%M %d-%m-%Y', $taskInfo['end'])) . '" />' .
+                       '<img' . t3lib_iconWorks::skinImg($this->backPath, 'gfx/datepicker.gif', '', 0) . ' style="cursor:pointer; vertical-align:middle;" alt=""' . ' id="picker-tceforms-datetimefield-task_end" />';
+               $tableLayout[$tr] = array (
+                       'tr'     => array('<tr id="task_end_row"' . $style . '>', '</tr>'),
+                       'defCol' => $defaultCell
+               );
+               $tr++;
+
+                       // Frequency input field
+               $table[$tr][] = t3lib_BEfunc::cshItem($this->cshKey, 'task_frequency', $this->backPath, '|', false, 'margin-bottom:0px;');
+               $table[$tr][] = '<label for="task_frequency">' . $GLOBALS['LANG']->getLL('label.frequency.long') . '</label>';
+               $cell = '<input type="text" name="tx_scheduler[frequency]" id="task_frequency" value="' . $taskInfo['frequency'] . '" />';
+               $table[$tr][] = $cell;
+               $tableLayout[$tr] = array (
+                       'tr'     => array('<tr id="task_frequency_row"' . $style . '>', '</tr>'),
+                       'defCol' => $defaultCell
+               );
+               $tr++;
+
+                       // Multiple execution selector
+               $table[$tr][] = t3lib_BEfunc::cshItem($this->cshKey, 'task_multiple', $this->backPath, '|', false, 'margin-bottom:0px;');
+               $table[$tr][] = '<label for="task_multiple">' . $GLOBALS['LANG']->getLL('label.parallel.long') . '</lang>';
+               $table[$tr][] =
+                       '<input type="hidden"   name="tx_scheduler[multiple]" value="0" />
+                        <input type="checkbox" name="tx_scheduler[multiple]" value="1" id="task_multiple"' . ($taskInfo['multiple'] == 1 ? ' checked="checked"' : '') . ' />';
+               $tableLayout[$tr] = array (
+                       'tr'     => array('<tr id="task_multiple_row"' . $style . '>', '</tr>'),
+                       'defCol' => $defaultCell
+               );
+               $tr++;
+
+                       // Display additional fields
+               foreach ($allAdditionalFields as $class => $fields) {
+                       if ($class == $taskInfo['class']) {
+                               $additionalFieldsStyle = '';
+                       } else {
+                               $additionalFieldsStyle = ' style="display: none"';
+                       }
+
+                       foreach ($fields as $fieldID => $fieldInfo) {
+                               $table[$tr][] = t3lib_BEfunc::cshItem($fieldInfo['cshKey'], $fieldInfo['cshLabel'], $this->backPath, '|', false, 'margin-bottom:0px;');
+                               $table[$tr][] = '<label for="' . $fieldID . '">' . $GLOBALS['LANG']->sL($fieldInfo['label']) . '</label>';
+                               $table[$tr][] = $fieldInfo['code'];
+                               $tableLayout[$tr] = array (
+                                       'tr'     => array('<tr id="' . $fieldID . '_row"' . $additionalFieldsStyle .' class="extraFields extra_fields_' . $class . '">', '</tr>'),
+                                       'defCol' => $defaultCell
+                               );
+                               $tr++;
+                       }
+               }
+
+                       // Render the add/edit task form
+               $content .= $this->doc->table($table, $tableLayout);
+
+               $content .= '<input type="submit" name="save" class="button" value="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:save') . '" /> '
+                       . '<input type="button" name="cancel" class="button" value="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:cancel') . '" onclick="document.location=\'' . $GLOBALS['MCONF']['_'] . '\'" />';
+
+               return $content;
+       }
+
+       /**
+        * Execute all selected tasks
+        *
+        * @return      void
+        */
+       protected function executeTasks() {
+                       // Continue if some elements have been chosen for execution
+               if (isset($this->submittedData['execute']) && count($this->submittedData['execute']) > 0) {
+
+                               // Get list of registered classes
+                       $registeredClasses = self::getRegisteredClasses();
+
+                               // Loop on all selected tasks
+                       foreach ($this->submittedData['execute'] as $uid) {
+
+                               try {
+                                               // Try fetching the task
+                                       $task = $this->scheduler->fetchTask($uid);
+                                       $class = get_class($task);
+                                       $name = $registeredClasses[$class]['title']. ' (' . $registeredClasses[$class]['extension'] . ')';
+                                               // Now try to execute it and report on outcome
+                                       try {
+                                               $result = $this->scheduler->executeTask($task);
+                                               if ($result) {
+                                                       $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.executed'), $name));
+                                               } else {
+                                                       $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.notExecuted'), $name), t3lib_FlashMessage::ERROR);
+                                               }
+                                       }
+                                       catch (Exception $e) {
+                                                       // An exception was thrown, display its message as an error
+                                               $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.executionFailed'), $name, $e->getMessage()), t3lib_FlashMessage::ERROR);
+                                       }
+                               }
+                                       // The task was not found, for some reason
+                               catch (OutOfBoundsException $e) {
+                                       $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.taskNotFound'), $uid), t3lib_FlashMessage::ERROR);
+                               }
+                       }
+                               // Record the run in the system registry
+                       $this->scheduler->recordLastRun('manual');
+                               // Make sure to switch to list view after execution
+                       $this->CMD = 'list';
+               }
+       }
+
+       /**
+        * Assemble display of list of scheduled tasks
+        *
+        * @return      string                  table of waiting schedulings
+        */
+       protected function listTasks() {
+                       // Define display format for dates
+               $dateFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] . ' ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'];
+               $content = '';
+
+                       // Get all registered tasks
+               $query = array(
+                       'SELECT'  => '*',
+                       'FROM'    => 'tx_scheduler_task',
+                       'ORDERBY' => 'nextexecution'
+               );
+
+               $res = $GLOBALS['TYPO3_DB']->exec_SELECT_queryArray($query);
+               $numRows = $GLOBALS['TYPO3_DB']->sql_num_rows($res);
+               if ($numRows == 0) {
+                       $content .= '<p>' . $GLOBALS['LANG']->getLL('msg.noTasks') . '</p>';
+               } else {
+                               // Get list of registered classes
+                       $registeredClasses = self::getRegisteredClasses();
+
+                               // Load ExtJS framework and specific JS library
+                       $this->doc->loadExtJS();
+                       $this->doc->loadJavascriptLib(t3lib_extMgm::extRelPath('scheduler') . 'res/tx_scheduler_be.js');
+
+                               // Initialise table layout
+                       $tableLayout = array(
+                               'table' => array(
+                                       '<table border="0" cellspacing="1" cellpadding="2" class="tx_scheduler_task_list">', '</table>'
+                               ),
+                               '0'     => array(
+                                       'tr'     => array('<tr class="bgColor2" valign="top">', '</tr>'),
+                                       'defCol' => array('<td>', '</td>'),
+                                       '1'      => array('<td style="width: 36px;">', '</td>')
+                               ),
+                               'defRow' => array(
+                                       'tr'     => array('<tr class="bgColor3-20">', '</tr>'),
+                                       'defCol' => array('<td>', '</td>')
+                               )
+                       );
+                       $disabledTaskRow = array (
+                               'tr'     => array('<tr class="bgColor3-20 disabled">', '</tr>'),
+                               'defCol' => array('<td>', '</td>')
+                       );
+                       $table = array();
+                       $tr = 0;
+
+                               // Header row
+                       $table[$tr][] = '<a href="#" onclick="toggleCheckboxes();" title="' . $GLOBALS['LANG']->getLL('label.checkAll') . '"><img ' . t3lib_iconWorks::skinImg($this->backPath, 'gfx/clip_select.gif') . ' alt="' . $GLOBALS['LANG']->getLL('label.checkAll') . '" /></a>';
+                       $table[$tr][] = '&nbsp;';
+                       $table[$tr][] = $GLOBALS['LANG']->getLL('task');
+                       $table[$tr][] = $GLOBALS['LANG']->getLL('label.type');
+                       $table[$tr][] = $GLOBALS['LANG']->getLL('label.frequency');
+                       $table[$tr][] = $GLOBALS['LANG']->getLL('label.parallel');
+                       $table[$tr][] = $GLOBALS['LANG']->getLL('label.lastExecution');
+                       $table[$tr][] = $GLOBALS['LANG']->getLL('label.nextExecution');
+                       $tr++;
+
+                               // Loop on all tasks
+                       while (($schedulerRecord = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))) {
+                                       // Restore the serialized task and pass it a reference to the scheduler object
+                               $task = $task = unserialize($schedulerRecord['serialized_task_object']);
+                                       // Set default execution status
+                               $executionStatus = 'scheduled';
+                               $executionStatusDetail = '';
+
+                               $name = $registeredClasses[$schedulerRecord['classname']]['title']. ' (' . $registeredClasses[$schedulerRecord['classname']]['extension'] . ')';
+                               $additionalInformation = $task->getAdditionalInformation();
+                               if (!empty($additionalInformation)) {
+                                       $name .= ' [' . $additionalInformation . ']';
+                               }
+
+                                       // Assemble information about last execution
+                               if (!empty($schedulerRecord['lastexecution_time'])) {
+                                       $lastExecution = date($dateFormat, $schedulerRecord['lastexecution_time']);
+                                       if ($schedulerRecord['lastexecution_context'] == 'CLI') {
+                                               $context = $GLOBALS['LANG']->getLL('label.cron');
+                                       } else {
+                                               $context = $GLOBALS['LANG']->getLL('label.manual');
+                                       }
+                                       $lastExecution .= ' (' . $context . ')';
+                               } else {
+                                       $lastExecution = '-';
+                               }
+
+                                       // Check if task currently has a running execution
+                               if (empty($schedulerRecord['serialized_executions'])) {
+                                       $isRunning = false;
+                               } else {
+                                       $isRunning = true;
+                                       $executionStatus = 'running';
+                               }
+
+                                       // Prepare display of next execution date
+                                       // If task is currently running, date is not displayed (as next hasn't been calculated yet)
+                                       // Also hide the date if task is disabled (the information doesn't make sense, as it will not run anyway)
+                               if ($isRunning || $schedulerRecord['disable'] == 1) {
+                                       $nextDate = '-';
+                               }
+                               else {
+                                       $nextDate = date($dateFormat, $schedulerRecord['nextexecution']);
+                                       if (empty($schedulerRecord['nextexecution'])) {
+                                               $nextDate = $GLOBALS['LANG']->getLL('none');
+                                       } elseif ($schedulerRecord['nextexecution'] < $GLOBALS['EXEC_TIME']) {
+                                                       // Next execution is overdue, highlight date
+                                               $nextDate = '<span class="late" title="' . $GLOBALS['LANG']->getLL('status.legend.scheduled') . '">' . $nextDate . '</span>';
+                                               $executionStatus = 'late';
+                                       }
+                               }
+
+                                       // Get execution type
+                               if ($task->getExecution()->getInterval() == 0 && $task->getExecution()->getCronCmd() == '') {
+                                       $execType = $GLOBALS['LANG']->getLL('label.type.single');
+                                       $frequency = '-';
+                               } else {
+                                       $execType = $GLOBALS['LANG']->getLL('label.type.recurring');
+                                       if ($task->getExecution()->getCronCmd() == '') {
+                                               $frequency = $task->getExecution()->getInterval();
+                                       } else {
+                                               $frequency = $task->getExecution()->getCronCmd();
+                                       }
+                               }
+
+                                       // Get multiple executions setting
+                               if ($task->getExecution()->getMultiple()) {
+                                       $multiple = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:yes');
+                               } else {
+                                       $multiple = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:no');
+                               }
+
+                                       // Define checkbox
+                               $startExecutionElement = '<input type="checkbox" name="tx_scheduler[execute][]" value="' . $schedulerRecord['uid'] . '" id="task_' . $schedulerRecord['uid'] . '" class="checkboxes" />';
+
+                                       // Assemble links for actions (edit, delete),
+                                       // only if task is not running
+                               if ($isRunning) {
+                                       $actions = '&nbsp;';
+                               } else {
+                                       $actions = '<a href="' . $GLOBALS['MCONF']['_'] . '&CMD=edit&tx_scheduler[uid]=' . $schedulerRecord['uid'] . '" title="'.$GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:edit') . '"><img ' . t3lib_iconWorks::skinImg($this->backPath, 'gfx/edit2.gif') . ' alt="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:edit') . '" /></a> ';
+                                       $deleteLink = $GLOBALS['MCONF']['_'] . '&CMD=delete&tx_scheduler[uid]=' . $schedulerRecord['uid'];
+                                       $actions .= '<a href="' . $deleteLink . '" onclick="return confirm(\'' . $GLOBALS['LANG']->getLL('msg.delete') . '\');" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:delete') . '"><img ' . t3lib_iconWorks::skinImg($this->backPath, 'gfx/garbage.gif') . ' alt="'.$GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:delete') . '" /></a>';
+                               }
+
+                                       // Check the disable status
+                               if ($schedulerRecord['disable'] == 1) {
+                                       $tableLayout[$tr] = $disabledTaskRow;
+                                       $executionStatus  = 'disabled';
+                               }
+
+                                       // A failure is the worst thing that could happen, so it must overwrite all other statuses
+                               if (!empty($schedulerRecord['lastexecution_failure'])) {
+                                       $exception = unserialize($schedulerRecord['lastexecution_failure']);
+
+                                       $executionStatus       = 'failure';
+                                       $executionStatusDetail = sprintf($GLOBALS['LANG']->getLL('msg.executionFailureReport'), $exception->getCode(), $exception->getMessage());
+                               }
+
+
+                                       // Format the execution status
+                               $executionStatus = '<img ' . t3lib_iconWorks::skinImg(t3lib_extMgm::extRelPath('scheduler'), 'res/gfx/status_' . $executionStatus . '.png') . ' alt="' . $GLOBALS['LANG']->getLL('status.' . $executionStatus) . '" title="' . $executionStatusDetail . '" />';
+
+                               $table[$tr][] = $startExecutionElement;
+                               $table[$tr][] = $actions;
+                               $table[$tr][] = $executionStatus . ' ' . $name;
+                               $table[$tr][] = $execType;
+                               $table[$tr][] = $frequency;
+                               $table[$tr][] = $multiple;
+                               $table[$tr][] = $lastExecution;
+                               $table[$tr][] = $nextDate;
+
+                               $tr++;
+                       }
+                               // Render table
+                       $content .= $this->doc->table($table, $tableLayout);
+
+                       $content .= '<input type="submit" class="button" name="go" value="' . $GLOBALS['LANG']->getLL('label.executeSelected') . '" />';
+               }
+
+                       // Display add new task link
+               $link = $GLOBALS['MCONF']['_'] . '&CMD=add';
+               $content .= '<p><a href="' . $link .'"><img '
+                       . t3lib_iconWorks::skinImg($this->backPath, 'gfx/new_el.gif')
+                       . ' alt="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:new')
+                       . '" /> ' . $GLOBALS['LANG']->getLL('action.add') . '</a></p>';
+
+                       // Display legend, if there's at least one registered task
+               if ($numRows > 0) {
+                       $content .= '<p class="status-legend">' . $GLOBALS['LANG']->getLL('status.legend') . ':</p>
+                       <ul>
+                               <li><img ' . t3lib_iconWorks::skinImg(t3lib_extMgm::extRelPath('scheduler'), 'res/gfx/status_failure.png') . ' alt="' . $GLOBALS['LANG']->getLL('status.failure') . '" /> ' . $GLOBALS['LANG']->getLL('status.legend.failure') . '</li>
+                               <li><img ' . t3lib_iconWorks::skinImg(t3lib_extMgm::extRelPath('scheduler'), 'res/gfx/status_late.png') . ' alt="' . $GLOBALS['LANG']->getLL('status.late') . '" /> ' . $GLOBALS['LANG']->getLL('status.legend.late') . '</li>
+                               <li><img ' . t3lib_iconWorks::skinImg(t3lib_extMgm::extRelPath('scheduler'), 'res/gfx/status_running.png') . ' alt="' . $GLOBALS['LANG']->getLL('status.running') . '" /> ' . $GLOBALS['LANG']->getLL('status.legend.running') . '</li>
+                               <li><img ' . t3lib_iconWorks::skinImg(t3lib_extMgm::extRelPath('scheduler'), 'res/gfx/status_scheduled.png') . ' alt="' . $GLOBALS['LANG']->getLL('status.scheduled') . '" /> ' . $GLOBALS['LANG']->getLL('status.legend.scheduled') . '</li>
+                               <li><img ' . t3lib_iconWorks::skinImg(t3lib_extMgm::extRelPath('scheduler'), 'res/gfx/status_disabled.png') . ' alt="' . $GLOBALS['LANG']->getLL('status.disabled') . '" /> ' . $GLOBALS['LANG']->getLL('status.legend.disabled') . '</li>
+                       </ul>';
+               }
+
+
+               $GLOBALS['TYPO3_DB']->sql_free_result($res);
+
+               return $content;
+       }
+
+       /**
+        * Saves a task specified in the backend form to the database
+        *
+        * @return      void
+        */
+       protected function saveTask() {
+
+                       // If a task is being edited fetch old task data
+               if (!empty($this->submittedData['uid'])) {
+                       try {
+                               $taskRecord = $this->scheduler->fetchTaskRecord($this->submittedData['uid']);
+                               /**
+                                * @var tx_scheduler_Task
+                                */
+                               $task = unserialize($taskRecord['serialized_task_object']);
+                       } catch (OutOfBoundsException $e) {
+                                       // If the task could not be fetched, issue an error message
+                                       // and exit early
+                               $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.taskNotFound'), $this->submittedData['uid']), t3lib_FlashMessage::ERROR);
+                               return;
+                       }
+
+                               // Register single execution
+                       if ($this->submittedData['type'] == 1) {
+                               $task->registerSingleExecution($this->submittedData['start']);
+
+                               // Else, it's a recurring task
+                       } else {
+                               if (!empty($this->submittedData['croncmd'])) {
+                                               // Definition by cron-like syntax
+
+                                       $interval = 0;
+                                       $cronCmd = $this->submittedData['croncmd'];
+                               } else {
+                                               // Definition by interval
+
+                                       $interval = $this->submittedData['interval'];
+                                       $cronCmd = '';
+                               }
+
+                                       // Register recurring execution
+                               $task->registerRecurringExecution($this->submittedData['start'], $interval, $this->submittedData['end'], $this->submittedData['multiple'], $cronCmd);
+                       }
+
+                               // Set disable flag
+                       $task->setDisabled($this->submittedData['disable']);
+
+                               // Save additional input values
+                       if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$this->submittedData['class']]['additionalFields'])) {
+                               $providerObject = t3lib_div::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$this->submittedData['class']]['additionalFields']);
+                               if ($providerObject instanceof tx_scheduler_AdditionalFieldProvider) {
+                                       $providerObject->saveAdditionalFields($this->submittedData, $task);
+                               }
+                       }
+
+                               // Save to database
+                       $result = $this->scheduler->saveTask($task);
+                       if ($result) {
+                               $this->addMessage($GLOBALS['LANG']->getLL('msg.updateSuccess'));
+                       } else {
+                               $this->addMessage($GLOBALS['LANG']->getLL('msg.updateError'), t3lib_FlashMessage::ERROR);
+                       }
+               } else {
+                               // A new task is being created
+
+                               // Create an instance of chosen class
+                       $task = t3lib_div::makeInstance($this->submittedData['class']);
+
+                       if ($this->submittedData['type'] == 1) {
+                                       // Set up single execution
+                               $task->registerSingleExecution($this->submittedData['start']);
+                       } else {
+                                       // Set up recurring execution
+                               $task->registerRecurringExecution($this->submittedData['start'], $this->submittedData['interval'], $this->submittedData['end'], $this->submittedData['multiple'], $this->submittedData['croncmd']);
+                       }
+
+                               // Save additional input values
+                       if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$this->submittedData['class']]['additionalFields'])) {
+                               $providerObject = t3lib_div::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$this->submittedData['class']]['additionalFields']);
+                               if ($providerObject instanceof tx_scheduler_AdditionalFieldProvider) {
+                                       $providerObject->saveAdditionalFields($this->submittedData, $task);
+                               }
+                       }
+
+                               // Set disable flag
+                       $task->setDisabled($this->submittedData['disable']);
+
+                               // Add to database
+                       $result = $this->scheduler->addTask($task);
+                       if ($result) {
+                               $this->addMessage($GLOBALS['LANG']->getLL('msg.addSuccess'));
+                       } else {
+                               $this->addMessage($GLOBALS['LANG']->getLL('msg.addError'), t3lib_FlashMessage::ERROR);
+                       }
+               }
+       }
+
+
+       /*************************
+        *
+        * INPUT PROCESSING UTILITIES
+        *
+        *************************/
+
+       /**
+        * Checks the submitted data and performs some preprocessing on it
+        *
+        * @return      boolean         True if everything was ok, false otherwise
+        */
+       protected function preprocessData() {
+               $result = true;
+
+                       // Validate id
+               $this->submittedData['uid'] = (empty($this->submittedData['uid'])) ? 0 : intval($this->submittedData['uid']);
+
+                       // Validate selected task class
+               if (!class_exists($this->submittedData['class'])) {
+                       $this->addMessage($GLOBALS['LANG']->getLL('msg.noTaskClassFound'), t3lib_FlashMessage::ERROR);
+               }
+
+                       // Check start date
+               if (empty($this->submittedData['start'])) {
+                       $this->addMessage($GLOBALS['LANG']->getLL('msg.noStartDate'), t3lib_FlashMessage::ERROR);
+                       $result = false;
+               } else {
+                       try {
+                               $timestamp = $this->checkDate($this->submittedData['start']);
+                               $this->submittedData['start'] = $timestamp;
+                       } catch (Exception $e) {
+                               $this->addMessage($GLOBALS['LANG']->getLL('msg.invalidStartDate'), t3lib_FlashMessage::ERROR);
+                               $result = false;
+                       }
+               }
+
+                       // Check end date, if recurring task
+               if ($this->submittedData['type'] == 2 && !empty($this->submittedData['end'])) {
+                       try {
+                               $timestamp = $this->checkDate($this->submittedData['end']);
+                               $this->submittedData['end'] = $timestamp;
+
+                               if ($this->submittedData['end'] < $this->submittedData['start']) {
+                                       $this->addMessage($GLOBALS['LANG']->getLL('msg.endDateSmallerThanStartDate'), t3lib_FlashMessage::ERROR);
+                                       $result = false;
+                               }
+                       } catch (Exception $e) {
+                               $this->addMessage($GLOBALS['LANG']->getLL('msg.invalidEndDate'), t3lib_FlashMessage::ERROR);
+                               $result = false;
+                       }
+               }
+
+                       // Set default values for interval and cron command
+               $this->submittedData['interval'] = 0;
+               $this->submittedData['croncmd'] = '';
+
+                       // Check type and validity of frequency, if recurring
+               if ($this->submittedData['type'] == 2) {
+                       $parts = t3lib_div::trimExplode(' ', $this->submittedData['frequency']);
+                       $numParts = count($parts);
+
+                       if ($numParts == 0) {
+                                       // No parts, empty frequency, not valid
+
+                               $this->addMessage($GLOBALS['LANG']->getLL('msg.noFrequency'), t3lib_FlashMessage::ERROR);
+                               $result = false;
+                       } else if ($numParts == 1) {
+                                       // One part, assume it is an interval
+                                       // Make sure it has a valid value
+                               $interval = intval($this->submittedData['frequency']);
+                               if ($interval > 0) {
+                                       $this->submittedData['interval'] = $interval;
+                               } else {
+                                       $this->addMessage($GLOBALS['LANG']->getLL('msg.invalidFrequency'), t3lib_FlashMessage::ERROR);
+                                       $result = false;
+                               }
+                       } else if ($numParts == 5) {
+                                       // Five parts, assume it is a valid cron command
+                               $this->submittedData['croncmd'] = $this->submittedData['frequency'];
+                       } else {
+                                       // Some other number of parts, assume it is an invalid cron command
+
+                               $this->addMessage($GLOBALS['LANG']->getLL('msg.invalidFrequency'), t3lib_FlashMessage::ERROR);
+                               $result = false;
+                       }
+               }
+
+                       // Validate additional input fields
+               if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$this->submittedData['class']]['additionalFields'])) {
+                       $providerObject = t3lib_div::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$this->submittedData['class']]['additionalFields']);
+                       if ($providerObject instanceof tx_scheduler_AdditionalFieldProvider) {
+                                       // The validate method will return true if all went well, but that must not
+                                       // override previous false values => AND the returned value with the existing one
+                               $result &= $providerObject->validateAdditionalFields($this->submittedData, $this);
+                       }
+               }
+
+               return $result;
+       }
+
+       /**
+        * This method checks whether the given string can be considered a valid date or not
+        * Allowed values are anything that matches natural language (see PHP function strtotime())
+        * or TYPO3's date syntax: HH:ii yyyy-mm-dd
+        * If the string is a valid date, the corresponding timestamp is returned.
+        * Otherwise an exception is thrown
+        *
+        * @param       string          $string: string to check
+        * @return      integer         Unix timestamp
+        */
+       protected function checkDate($string) {
+                       // Try with strtotime
+               $timestamp = strtotime($string);
+
+                       // That failed. Try TYPO3's standard date/time input format
+               if ($timestamp === false) {
+                               // Split time and date
+                       $dateParts = t3lib_div::trimExplode(' ', $string, true);
+                               // Proceed if there are indeed two parts
+                               // Extract each component of date and time
+                       if (count($dateParts) == 2) {
+                               list($time, $date) = $dateParts;
+                               list($hour, $minutes) = t3lib_div::trimExplode(':', $time, true);
+                               list($day, $month, $year) = t3lib_div::trimExplode('-', $date, true);
+                                       // Get a timestamp from all these parts
+                               $timestamp = mktime($hour, $minutes, 0, $month, $day, $year);
+                       }
+                               // If the timestamp is still false, throw an exception
+                       if ($timestamp === false) {
+                               throw new Exception;
+                       }
+               }
+               return $timestamp;
+       }
+
+       /*************************
+        *
+        * APPLICATION LOGIC UTILITIES
+        *
+        *************************/
+
+       /**
+        * This method is used to add a message to the internal queue
+        *
+        * @param       string  the message itself
+        * @param       integer message level (-1 = success (default), 0 = info, 1 = notice, 2 = warning, 3 = error)
+        * @return      void
+        */
+       public function addMessage($message, $severity = t3lib_FlashMessage::OK) {
+               $message = t3lib_div::makeInstance(
+                       't3lib_FlashMessage',
+                       $message,
+                       '',
+                       $severity
+               );
+
+               $this->doc->pushFlashMessage($message);
+       }
+
+       /**
+        * This method a list of all classes that have been registered with the Scheduler
+        * For each item the following information is provided, as an associative array:
+        *
+        * ['extension']        =>      Key of the extension which provides the class
+        * ['filename']         =>      Path to the file containing the class
+        * ['title']            =>      String (possibly localized) containing a human-readable name for the class
+        * ['provider']         =>      Name of class that implements the interface for additional fields, if necessary
+        *
+        * The name of the class itself is used as the key of the list array
+        *
+        * @return      array   List of registered classes
+        */
+       protected static function getRegisteredClasses() {
+               $list = array();
+               if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'])) {
+                       foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'] as $class => $registrationInformation) {
+
+                               $title         = isset($registrationInformation['title'])         ? $GLOBALS['LANG']->sL($registrationInformation['title'])         : '';
+                               $description   = isset($registrationInformation['description'])   ? $GLOBALS['LANG']->sL($registrationInformation['description'])   : '';
+
+                               $list[$class] = array(
+                                       'extension'     => $registrationInformation['extension'],
+                                       'title'         => $title,
+                                       'description'   => $description,
+                                       'provider'              => isset($registrationInformation['additionalFields']) ? $registrationInformation['additionalFields'] : ''
+                               );
+                       }
+               }
+
+               return $list;
+       }
+
+
+       /*************************
+        *
+        * RENDERING UTILITIES
+        *
+        *************************/
+
+       /**
+        * Gets the filled markers that are used in the HTML template.
+        *
+        * @return      array           The filled marker array
+        */
+       protected function getTemplateMarkers() {
+               $markers = array(
+                       'FUNC_MENU' => $this->getFunctionMenu(),
+                       'CONTENT'   => $this->content,
+                       'TITLE'     => $GLOBALS['LANG']->getLL('title'),
+               );
+
+               return $markers;
+       }
+
+       /**
+        * Gets the function menu selector for this backend module.
+        *
+        * @return      string          The HTML representation of the function menu selector
+        */
+       protected function getFunctionMenu() {
+               $functionMenu = t3lib_BEfunc::getFuncMenu(
+                       0,
+                       'SET[function]',
+                       $this->MOD_SETTINGS['function'],
+                       $this->MOD_MENU['function']
+               );
+
+               return $functionMenu;
+       }
+
+       /**
+        * Gets the buttons that shall be rendered in the docHeader.
+        *
+        * @return      array           Available buttons for the docHeader
+        */
+       protected function getDocHeaderButtons() {
+               $buttons = array(
+                       'csh'      => t3lib_BEfunc::cshItem('_MOD_tools_txschedulerM1', '', $this->backPath),
+                       'reload'   => '',
+                       'shortcut' => $this->getShortcutButton(),
+               );
+
+               if (empty($this->CMD) || $this->CMD == 'list') {
+                       $buttons['reload'] = '<a href="' . $GLOBALS['MCONF']['_'] . '">' .
+                               '<img' . t3lib_iconWorks::skinImg($this->backPath, 'gfx/refresh_n.gif') . ' title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:labels.reload', 1) . '" alt="" />' .
+                               '</a>';
+               }
+
+               return $buttons;
+       }
+
+       /**
+        * Gets the button to set a new shortcut in the backend (if current user is allowed to).
+        *
+        * @return      string          HTML representiation of the shortcut button
+        */
+       protected function getShortcutButton() {
+               $result = '';
+               if ($GLOBALS['BE_USER']->mayMakeShortcut()) {
+                       $result = $this->doc->makeShortcutIcon('', 'function', $this->MCONF['name']);
+               }
+
+               return $result;
+       }
+}
+
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/scheduler/mod1/index.php']) {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/scheduler/mod1/index.php']);
+}
+
+
+
+
+// Make instance:
+$SOBE = t3lib_div::makeInstance('tx_scheduler_Module');
+$SOBE->init();
+
+// Include files?
+foreach($SOBE->include_once as $INC_FILE) {
+       include_once($INC_FILE);
+}
+
+$SOBE->main();
+$SOBE->render();
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/scheduler/mod1/locallang.xml b/typo3/sysext/scheduler/mod1/locallang.xml
new file mode 100644 (file)
index 0000000..7adc48c
--- /dev/null
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<T3locallang>
+       <meta type="array">
+               <type>module</type>
+               <description>Language labels for module 'tools_txschedulerM1'</description>
+       </meta>
+       <data type="array">
+               <languageKey index="default" type="array">
+                       <label index="action.add">Add task</label>
+                       <label index="action.edit">Edit task</label>
+                       <label index="button.cancel">Cancel</label>
+                       <label index="function.scheduler">Scheduled tasks</label>
+                       <label index="function.check">Setup check</label>
+                       <label index="function.info">Information</label>
+                       <label index="hdg.cliScript">CLI script</label>
+                       <label index="hdg.lastRun">Last run</label>
+                       <label index="hdg.schedulerUser">TYPO3 Scheduler backend user</label>
+                       <label index="label.automatically">automatically</label>
+                       <label index="label.checkAll">Check/uncheck all</label>
+                       <label index="label.class">Class</label>
+                       <label index="label.cron">Cron</label>
+                       <label index="label.defType">Definition type</label>
+                       <label index="label.defType.interval">Interval</label>
+                       <label index="label.defType.cron">Cron command</label>
+                       <label index="label.description">Description</label>
+                       <label index="label.email">Email</label>
+                       <label index="label.end">End (HH:MM DD-MM-YYYY)</label>
+                       <label index="label.executeSelected">Execute selected tasks</label>
+                       <label index="label.extension">Extension</label>
+                       <label index="label.frequency">Frequency</label>
+                       <label index="label.frequency.long">Frequency (seconds or cron command)</label>
+                       <label index="label.lastExecution">Last Execution</label>
+                       <label index="label.manual">Manual</label>
+                       <label index="label.manually">manually</label>
+                       <label index="label.name">Name</label>
+                       <label index="label.nextExecution">Next Execution</label>
+                       <label index="label.parallel">Parallel Execution</label>
+                       <label index="label.parallel.long">Allow Parallel Execution</label>
+                       <label index="label.sleepTime">Sleep time</label>
+                       <label index="label.start">Start (HH:MM DD-MM-YYYY)</label>
+                       <label index="label.type">Type</label>
+                       <label index="label.type.recurring">Recurring</label>
+                       <label index="label.type.single">Single</label>
+                       <label index="msg.addError">The task could not be added.</label>
+                       <label index="msg.addSuccess">The task was added successfully.</label>
+                       <label index="msg.cliScript">The script to execute to run the Scheduler from the command line is: &lt;strong&gt;"%s scheduler"&lt;/strong&gt;.</label>
+                       <label index="msg.cliScriptExecutable">The webserver user is allowed execute this script.</label>
+                       <label index="msg.cliScriptNotExecutable">The webserver user is not allowed execute this script.</label>
+                       <label index="msg.delete">Are you sure you want to delete this task?</label>
+                       <label index="msg.deleteError">The task could not be deleted.</label>
+                       <label index="msg.deleteSuccess">The task was successfully deleted.</label>
+                       <label index="msg.endDateSmallerThanStartDate">The end date is before the start date.</label>
+                       <label index="msg.executed">Executed: %s</label>
+                       <label index="msg.incompleteLastRun">The information about the last execution of the Scheduler is incomplete.</label>
+                       <label index="msg.infoScreenIntro">This is the list of all available tasks in this TYPO3 installation. Click on the icon at the far right to directly create a new scheduled task of the chosen type.</label>
+                       <label index="msg.noFrequency">No frequency was defined, either as an interval or as a cron command.</label>
+                       <label index="msg.invalidFrequency">Invalid frequency. Please enter either a number of seconds or a valid cron command.</label>
+                       <label index="msg.invalidSleepTime">Please enter a sleep time greater or equal to 0 (zero).</label>
+                       <label index="msg.lastRun">The Scheduler was last started (%1$s) on %2$s at %3$s and ended on %4$s at %5$s.</label>
+                       <label index="msg.maynotDeleteRunningTask">A running task may not be deleted.</label>
+                       <label index="msg.maynotEditRunningTask">A running task may not be edited.</label>
+                       <label index="msg.noEmail">Please enter an email address</label>
+                       <label index="msg.noLastRun">The Scheduler has never yet run or the information about the last run has been lost.</label>
+                       <label index="msg.noTaskClassFound">The selected task class could not be found. You should probably contact the task's developers.</label>
+                       <label index="msg.noTasks">No tasks defined yet.</label>
+                       <label index="msg.notExecuted">Not executed: %s</label>
+                       <label index="msg.executionFailed">Execution of task "%1$s" failed with the following message: %2$s</label>
+                       <label index="msg.executionFailureReport">Execution failed: %1$d, %2$s</label>
+                       <label index="msg.noStartDate">Please define a start date</label>
+                       <label index="msg.invalidStartDate">Start date is invalid</label>
+                       <label index="msg.invalidEndDate">End date is invalid</label>
+                       <label index="msg.invalidTaskClass">The class of the registered task could not be found. Change class or consider deleting this task.</label>
+                       <label index="msg.schedulerSetupCheck">This screen checks if the requisites for running the Scheduler as a cron job are fulfilled. It also displays information about the last run of the Scheduler.</label>
+                       <label index="msg.schedulerUser">The backend user "_cli_scheduler" is used by the CLI-script to log into TYPO3. This user may not have administrator privileges. The user's password is not important but should be set to a secure value for security reasons.</label>
+                       <label index="msg.schedulerUserFound">The backend user "_cli_scheduler" was found.</label>
+                       <label index="msg.schedulerUserFoundButDisabled">The backend user "_cli_scheduler" exists but is currently disabled.</label>
+                       <label index="msg.schedulerUserMissing">The backend user "_cli_scheduler" was not found. &lt;a href="%s" title="Click to create the missing user"&gt;Create the user now&lt;/a&gt;.</label>
+                       <label index="msg.singleExecutionMustBeInFuture">The execution time of single execution tasks has to be in the future.</label>
+                       <label index="msg.taskNotFound">The requested task (UID: %d) was not found.</label>
+                       <label index="msg.updateSuccess">The task was updated successfully.</label>
+                       <label index="msg.updateError">The task could not be updated.</label>
+                       <label index="msg.userCreated">The "_cli_scheduler" user was created successfully.</label>
+                       <label index="msg.userExists">The "_cli_scheduler" user already exists. It was not created.</label>
+                       <label index="msg.userNotCreated">The creation of the "_cli_scheduler" user failed.</label>
+                       <label index="none">None</label>
+                       <label index="status">Status</label>
+                       <label index="status.legend">Status Legend</label>
+                       <label index="status.legend.disabled">Disabled, will not be executed, except manually</label>
+                       <label index="status.legend.failure">Failure! An error occured during the last execution (move over bullet for more details)</label>
+                       <label index="status.legend.late">Late, will run with next execution</label>
+                       <label index="status.legend.running">Currently running</label>
+                       <label index="status.legend.scheduled">Scheduled, will run on next possible execution</label>
+                       <label index="status.disabled">disabled</label>
+                       <label index="status.failure">failure</label>
+                       <label index="status.late">late</label>
+                       <label index="status.running">running</label>
+                       <label index="status.scheduled">scheduled</label>
+                       <label index="task">Task</label>
+                       <label index="title">TYPO3 Scheduler administration module</label>
+               </languageKey>
+       </data>
+</T3locallang>
\ No newline at end of file
diff --git a/typo3/sysext/scheduler/mod1/locallang_csh_scheduler.xml b/typo3/sysext/scheduler/mod1/locallang_csh_scheduler.xml
new file mode 100644 (file)
index 0000000..039fdda
--- /dev/null
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<T3locallang>
+       <meta type="array">
+               <description>Contents of scheduler csh</description>
+               <type>CSH</type>
+               <csh_table>_MOD_tools_txschedulerM1</csh_table>
+               <fileId>EXT:scheduler/mod1/locallang_csh_scheduler.xml</fileId>
+               <labelContext type="array">
+               </labelContext>
+       </meta>
+       <data type="array">
+               <languageKey index="default" type="array">
+                       <label index=".alttitle">Scheduler</label>
+                       <label index=".description">The Scheduler provides an interface to register and manage recurring or one-time tasks.</label>
+                       <label index=".syntax">
+                               The Scheduler uses a single cron job which handles all registered tasks. It is thus very convenient as a single cron job needs to be set up and all task frequencies can be managed from the BE module.
+                       </label>
+                       <label index="task_class.alttitle">Class of the task</label>
+                       <label index="task_class.description">Choose a class from the list of available tasks</label>
+                       <label index="task_class.details">
+                               The tasks are provided by various extensions. See the manual to learn how to create your own tasks and register them with the Scheduler.
+                       </label>
+                       <label index="task_disable.alttitle">Disable task</label>
+                       <label index="task_disable.description">Check this box to disable the automatic, scheduled execution of this task. It can still be launched manually from the BE module.</label>
+                       <label index="task_arguments.alttitle">Additional call arguments</label>
+                       <label index="task_arguments.description">Enter whatever arguments exist for the chosen task class</label>
+                       <label index="task_arguments.details">
+                               Call arguments are entered using a syntax similar to HTTP query strings, i.e. &amp;arg1=value1&amp;arg2=value2 etc.
+                       </label>
+                       <label index="task_type.alttitle">Type of task</label>
+                       <label index="task_type.description">Choose whether the task must happen a single time or will be repeated</label>
+                       <label index="task_start.alttitle">Start of task</label>
+                       <label index="task_start.description">Enter a start date and time for the task</label>
+                       <label index="task_start.details">
+                               Date must be entered in a standard format like HH:mm dd-mm-YYYY or YYYY-mm-dd HH:mm. Natural keywords can also be used, like "now" or "tomorrow".
+                       </label>
+                       <label index="task_end.alttitle">Start of task</label>
+                       <label index="task_end.description">Enter an end date and time for the task, can be blank</label>
+                       <label index="task_end.details">
+                               Date must be entered in a standard format like HH:mm dd-mm-YYYY or YYYY-mm-dd HH:mm. Natural keywords can also be used, like "now" or "tomorrow".
+
+                               Can be left blank, if task is not supposed to end at any point.
+                       </label>
+                       <label index="task_frequency.alttitle">Frequency of task</label>
+                       <label index="task_frequency.description">Enter a frequency, either in seconds or using a cron-like syntax</label>
+                       <label index="task_frequency.details">
+                               The frequency of the task can be simply entered as a number of seconds.
+
+                               Alternately it is possible to use the same syntax as for cron jobs. This is simply a list of five numbers or the wildcard "*", separated by blanks. The five numbers are (in order) minutes, hours, day of month, months and day of week. Examples:
+
+                               */5 * * * * means every 5 minutes
+                               30 22 * * * means every day at 22:30
+                       </label>
+                       <label index="task_frequency.seeAlso">
+                               Wikipedia reference|http://en.wikipedia.org/wiki/Cron
+                       </label>
+                       <label index="task_multiple.alttitle">Multiple executions</label>
+                       <label index="task_multiple.description">Choose whether a task can be running several times simultaneously or not</label>
+                       <label index="task_email.alttitle">Email address</label>
+                       <label index="task_email.description">Enter a recipient for the mails sent by the test task</label>
+                       <label index="task_sleepTime.alttitle">Sleep time</label>
+                       <label index="task_sleepTime.description">Enter a number of seconds during which the task will just sleep.</label>
+               </languageKey>
+       </data>
+</T3locallang>
diff --git a/typo3/sysext/scheduler/mod1/locallang_mod.xml b/typo3/sysext/scheduler/mod1/locallang_mod.xml
new file mode 100644 (file)
index 0000000..9f35204
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<T3locallang>
+       <meta type="array">
+               <type>module</type>
+               <description>Language labels for module 'tools_txschedulerM1' - header, description</description>
+       </meta>
+       <data type="array">
+               <languageKey index="default" type="array">
+                       <label index="mlang_tabs_tab">Scheduler</label>
+                       <label index="mlang_labels_tabdescr">Scheduler administration module. Check all registered tasks. Add, modify or delete tasks.</label>
+                       <label index="mlang_labels_tablabel">TYPO3 Scheduler</label>
+               </languageKey>
+       </data>
+</T3locallang>
\ No newline at end of file
diff --git a/typo3/sysext/scheduler/mod1/mod_template.html b/typo3/sysext/scheduler/mod1/mod_template.html
new file mode 100644 (file)
index 0000000..3e99e07
--- /dev/null
@@ -0,0 +1,35 @@
+<!-- ###FULLDOC### begin -->
+<div class="typo3-fullDoc">
+       <!-- Page header with buttons, path details and csh -->
+       <div id="typo3-docheader">
+               <div id="typo3-docheader-row1">
+                       <div class="buttonsleft">###BUTTONLIST_LEFT###</div>
+                       <div class="buttonsright">###BUTTONLIST_RIGHT###</div>
+               </div>
+               <div id="typo3-docheader-row2">
+                       <div class="docheader-row2-left"><div class="docheader-funcmenu">###FUNC_MENU###</div></div>
+                       <div class="docheader-row2-right"></div>
+               </div>
+       </div>
+       <!-- Content of module, for instance listing, info or editing -->
+       <div id="typo3-docbody">
+               <div id="typo3-inner-docbody">
+                       ###CONTENT###
+               </div>
+       </div>
+</div>
+<!-- ###FULLDOC### end -->
+
+<!-- Grouping the icons on top -->
+
+<!-- ###BUTTON_GROUP_WRAP### -->
+       <div class="buttongroup">###BUTTONS###</div>
+<!-- ###BUTTON_GROUP_WRAP### -->
+
+<!-- ###BUTTON_GROUPS_LEFT### -->
+<!-- ###BUTTON_GROUP1### -->###CSH###<!-- ###BUTTON_GROUP1### -->
+<!-- ###BUTTON_GROUPS_LEFT### -->
+
+<!-- ###BUTTON_GROUPS_RIGHT### -->
+<!-- ###BUTTON_GROUP1### -->###RELOAD######SHORTCUT###<!-- ###BUTTON_GROUP1### -->
+<!-- ###BUTTON_GROUPS_RIGHT### -->
diff --git a/typo3/sysext/scheduler/mod1/moduleicon.gif b/typo3/sysext/scheduler/mod1/moduleicon.gif
new file mode 100755 (executable)
index 0000000..30dfa3f
Binary files /dev/null and b/typo3/sysext/scheduler/mod1/moduleicon.gif differ
diff --git a/typo3/sysext/scheduler/res/gfx/status_disabled.png b/typo3/sysext/scheduler/res/gfx/status_disabled.png
new file mode 100644 (file)
index 0000000..79f0d77
Binary files /dev/null and b/typo3/sysext/scheduler/res/gfx/status_disabled.png differ
diff --git a/typo3/sysext/scheduler/res/gfx/status_failure.png b/typo3/sysext/scheduler/res/gfx/status_failure.png
new file mode 100644 (file)
index 0000000..feb1f07
Binary files /dev/null and b/typo3/sysext/scheduler/res/gfx/status_failure.png differ
diff --git a/typo3/sysext/scheduler/res/gfx/status_late.png b/typo3/sysext/scheduler/res/gfx/status_late.png
new file mode 100644 (file)
index 0000000..87053e0
Binary files /dev/null and b/typo3/sysext/scheduler/res/gfx/status_late.png differ
diff --git a/typo3/sysext/scheduler/res/gfx/status_running.png b/typo3/sysext/scheduler/res/gfx/status_running.png
new file mode 100644 (file)
index 0000000..aec59d9
Binary files /dev/null and b/typo3/sysext/scheduler/res/gfx/status_running.png differ
diff --git a/typo3/sysext/scheduler/res/gfx/status_scheduled.png b/typo3/sysext/scheduler/res/gfx/status_scheduled.png
new file mode 100644 (file)
index 0000000..611fc88
Binary files /dev/null and b/typo3/sysext/scheduler/res/gfx/status_scheduled.png differ
diff --git a/typo3/sysext/scheduler/res/tx_scheduler_be.css b/typo3/sysext/scheduler/res/tx_scheduler_be.css
new file mode 100644 (file)
index 0000000..3bcdea2
--- /dev/null
@@ -0,0 +1,76 @@
+/* Scheduler BE-Styles */
+/* $Id: tx_scheduler_be.css 1146 2009-08-27 20:26:30Z francois $ */
+
+.tx_scheduler_mod1 a {
+       text-decoration: underline;
+}
+
+.tx_scheduler_mod1 #typo3-inner-docbody {
+       padding-right: 10px;
+}
+
+.tx_scheduler_mod1 a.typo3-csh-link {
+       text-decoration: none;
+}
+
+.tx_scheduler_mod1 .tx_scheduler_task_list {
+       width: 100%;
+}
+
+.tx_scheduler_mod1 td {
+       padding: 2px 4px;
+       vertical-align: middle;
+}
+.tx_scheduler_mod1 tr.disabled td {
+       color: #666;
+}
+.tx_scheduler_mod1 .bgColor2 td {
+       text-align: left;
+       font-weight: bold;
+       color: #fff;
+}
+.tx_scheduler_mod1 .late {
+       color: #f00;
+       font-weight: bold;
+}
+.tx_scheduler_mod1 label {
+       display: block;
+       width: 200px;
+}
+.tx_scheduler_mod1 input{}
+.tx_scheduler_mod1 select {
+       margin-bottom: 1.2em;
+       width: 200px;
+}
+.tx_scheduler_mod1 input.wide,
+.tx_scheduler_mod1 select.wide {
+       width: 400px;
+}
+.tx_scheduler_mod1 input.checkboxes {
+       width: auto;
+}
+
+.tx_scheduler_mod1 input.button {
+       padding: 1px 4px 1px 4px;
+       line-height: 14px;
+       margin: 5px 0px;
+}
+
+.tx_scheduler_mod1 .status-legend {
+       padding-top: 10px;
+       padding-bottom: 5px;
+       font-weight: bold;
+}
+
+.tx_scheduler_mod1 th input,
+.tx_scheduler_mod1 td input {
+       margin-bottom: 2px;
+}
+
+/* Definition for BE function "Setup check" */
+.info-block {
+       margin: 25px 0px 0px 0px;
+}
+.info-block p {
+       margin: 8px 5px 8px 5px;
+}
diff --git a/typo3/sysext/scheduler/res/tx_scheduler_be.js b/typo3/sysext/scheduler/res/tx_scheduler_be.js
new file mode 100644 (file)
index 0000000..f4d6c7a
--- /dev/null
@@ -0,0 +1,102 @@
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Francois Suter <francois@typo3.org>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+/**
+ * This javascript file is used in the Scheduler's backend module
+ * It relies on ExtJS core being loaded
+ *
+ * @author     Francois Suter <francois@typo3.org>
+ *
+ * $Id: tx_scheduler_be.js 1220 2009-09-11 20:45:35Z francois $
+ */
+
+/**
+ * Global variable to keep track of checked/unchecked status of all
+ * checkboxes for execution selection
+ * 
+ * @var        boolean
+ */
+var allCheckedStatus = false;
+
+/**
+ * This method reacts on changes to the task class
+ * It switches on or off the relevant extra fields
+ *
+ * @param      theSelector: select form item where the selection was made
+ * @return     void
+ */
+function actOnChangedTaskClass(theSelector) {
+       var taskClass = theSelector.options[theSelector.selectedIndex].value;
+               // Hide all extra fields
+               // Show only relevant extra fields
+       Ext.select('.extraFields').setDisplayed(false);
+       Ext.select('.extra_fields_' + taskClass).setDisplayed(true);
+}
+
+/**
+ * This method reacts on changes to the type of a task, i.e. single or recurring,
+ * by showing or hiding the relevant form fields
+ *
+ * @param      theSelector: select form item where the selection was made
+ * @return     void
+ */
+function actOnChangedTaskType(theSelector) {
+               // Get task type from selected value, or set default value
+       var taskType;
+       if (theSelector.selectedIndex) {
+               taskType = theSelector.options[theSelector.selectedIndex].value;
+       } else {
+               taskType = 1;
+       }
+               // Single task
+               // Hide all fields related to recurring tasks
+       if (taskType == 1) {
+               Ext.fly('task_end_row').setDisplayed(false);
+               Ext.fly('task_frequency_row').setDisplayed(false);
+               Ext.fly('task_multiple_row').setDisplayed(false);
+
+               // Recurring task
+               // Show all fields related to recurring tasks
+       } else {
+               Ext.fly('task_end_row').setDisplayed(true);
+               Ext.fly('task_frequency_row').setDisplayed(true);
+               Ext.fly('task_multiple_row').setDisplayed(true);
+       }
+}
+
+/**
+ * This method reacts on the checking of a toggle,
+ * activating or not the check of all other checkboxes
+ *
+ * @return     void
+ */
+function toggleCheckboxes() {
+               // Toggle status of global variable
+       allCheckedStatus = !allCheckedStatus;
+               // Get all checkboxes with proper class
+       var checkboxes = Ext.select('.checkboxes');
+       var count = checkboxes.getCount();
+               // Set them all to same status as main checkbox
+       for (var i = 0; i < count; i++) {
+               checkboxes.item(i).dom.checked = allCheckedStatus;
+       }
+}