Fixed bug #12132: Scheduler: Introduced proper handling of incomplete objects after...
authorFrancois Suter <francois.suter@typo3.org>
Fri, 16 Oct 2009 08:02:18 +0000 (08:02 +0000)
committerFrancois Suter <francois.suter@typo3.org>
Fri, 16 Oct 2009 08:02:18 +0000 (08:02 +0000)
git-svn-id: https://svn.typo3.org/TYPO3v4/Core/trunk@6157 709f56b5-9817-0410-a4d7-c38de5d9e867

ChangeLog
typo3/sysext/scheduler/class.tx_scheduler.php
typo3/sysext/scheduler/class.tx_scheduler_execution.php
typo3/sysext/scheduler/mod1/index.php
typo3/sysext/scheduler/mod1/locallang.xml
typo3/sysext/scheduler/res/tx_scheduler_be.css

index dfef810..454afbb 100755 (executable)
--- a/ChangeLog
+++ b/ChangeLog
@@ -4,6 +4,7 @@
        * Feature #12167: Reports: Enhanced report registration API to provide custom icons for reports
        * Feature #12178: Provide a report for installed services in the new Reports module
        * Cleanup #12249: Improved comments about service registration in EXT:sv/ext_tables.php and EXT:sv/ext_localconf.php
+       * Fixed bug #12132: Scheduler: Introduced proper handling of incomplete objects after unserialization from DB storage
 
 2009-10-15  Steffen Kamper  <info@sk-typo3.de>
 
index e43e48d..7888f91 100755 (executable)
@@ -323,9 +323,14 @@ class tx_scheduler implements t3lib_Singleton {
                } 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;
+                               // Return the task only if valid, otherwise throw an exception
+                       if ($this->isValidTaskObject($task)) {
+                               $task->setScheduler();
+                               $GLOBALS['TYPO3_DB']->sql_free_result($res);
+                               return $task;
+                       } else {
+                               throw new UnexpectedValueException('Could not unserialize task', 1255083671);
+                       }
                }
        }
 
@@ -375,13 +380,32 @@ class tx_scheduler implements t3lib_Singleton {
                if ($res) {
                        while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
                                $task = unserialize($row['serialized_task_object']);
-                               $task->setScheduler();
-                               $tasks[] = $task;
+                                       // Add the task to the list only if it is valid
+                               if ($this->isValidTaskObject($task)) {
+                                       $task->setScheduler();
+                                       $tasks[] = $task;
+                               }
                        }
                        $GLOBALS['TYPO3_DB']->sql_free_result($res);
                }
                return $tasks;
        }
+
+       /**
+        * This method encapsulates a very simple test for the purpose of clarity.
+        * Registered tasks are stored in the database along with a serialized task object.
+        * When a registered task is fetched, its object is unserialized.
+        * At that point, if the class corresponding to the object is not available anymore
+        * (e.g. because the extension providing it has been uninstalled),
+        * the unserialization will produce an incomplete object.
+        * This test checks whether the unserialized object is of the right (parent) class or not.
+        *
+        * @param       object          The object to test
+        * @return      boolean         True if object is a task, false otherwise
+        */
+       public function isValidTaskObject($task) {
+               return $task instanceof tx_scheduler_Task;
+       }
 }
 
 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/scheduler/class.tx_scheduler.php'])   {
index 674243c..311c581 100644 (file)
@@ -243,7 +243,7 @@ class tx_scheduler_Execution {
         * @return      boolean         True if the schedule is not active anymore, false otherwise
         */
        public function isEnded() {
-               if (empty($this->end))  {
+               if (empty($this->end)) {
                                // If no end is defined, the schedule never ends
                        $result = false;
                } else {
index ac72240..67cdd41 100755 (executable)
@@ -506,6 +506,14 @@ class tx_scheduler_Module extends t3lib_SCbase {
                                        $this->addMessage($GLOBALS['LANG']->getLL('msg.deleteError'), t3lib_FlashMessage::ERROR);
                                }
                        }
+               } catch (UnexpectedValueException $e) {
+                               // The could not be unserialized properly, simply delete the database record
+                       $result = $GLOBALS['TYPO3_DB']->exec_DELETEquery('tx_scheduler_task', 'uid = ' . intval($this->submittedData['uid']));
+                       if ($result) {
+                               $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);
@@ -521,6 +529,7 @@ class tx_scheduler_Module extends t3lib_SCbase {
                $registeredClasses = self::getRegisteredClasses();
                $content = '';
                $taskInfo = array();
+               $task = NULL;
 
                if ($this->submittedData['uid'] > 0) {
                                // If editing, retrieve data for existing task
@@ -531,42 +540,51 @@ class tx_scheduler_Module extends t3lib_SCbase {
                                        $this->addMessage($GLOBALS['LANG']->getLL('msg.maynotEditRunningTask'), 3);
                                        throw new LogicException('Runnings tasks cannot not be edited', 1251232849);
                                }
+                                       // Get the task object
                                $task = unserialize($taskRecord['serialized_task_object']);
 
                                        // Set some task information
+                               $class = $taskRecord['classname'];
                                $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])) {
+                                       // Check that the task object is valid
+                               if ($this->scheduler->isValidTaskObject($task)) {
+                                       // The task object is valid, process with fetching current data
+
                                        $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();
+                                               // 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
+                                       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'];
+                                                       // 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;
+                                       }
                                } else {
-                                               // It's not a recurring task
-                                               // Make sure interval and cron command are both empty
-                                       $taskInfo['type']      = 1;
-                                       $taskInfo['frequency'] = '';
+                                       // The task object is not valid
+
+                                               // Issue error message
+                                       $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.invalidTaskClassEdit'), $class), t3lib_FlashMessage::ERROR);
+                                               // Initialize empty values
+                                       $taskInfo['start']     = 0;
                                        $taskInfo['end']       = 0;
+                                       $taskInfo['frequency'] = '';
+                                       $taskInfo['multiple']  = false;
+                                       $taskInfo['type']      = 1;
                                }
                        } catch (OutOfBoundsException $e) {
                                        // Add a message and continue throwing the exception
@@ -850,21 +868,30 @@ class tx_scheduler_Module extends t3lib_SCbase {
                                        '<table border="0" cellspacing="1" cellpadding="2" class="tx_scheduler_task_list">', '</table>'
                                ),
                                '0'     => array(
-                                       'tr'     => array('<tr class="bgColor2" valign="top">', '</tr>'),
+                                       'tr'     => array('<tr class="bgColor2">', '</tr>'),
                                        'defCol' => array('<td class="cell">', '</td>'),
                                        '1'      => array('<td style="width: 36px;" class="cell">', '</td>')
                                ),
                                'defRow' => array(
                                        'tr'     => array('<tr class="bgColor3-20">', '</tr>'),
                                        'defCol' => array('<td class="cell">', '</td>'),
+                                       '1'              => array('<td class="cell right">', '</td>'),
                                        '2'              => array('<td class="cell right">', '</td>'),
                                )
                        );
                        $disabledTaskRow = array (
                                'tr'     => array('<tr class="bgColor3-20 disabled">', '</tr>'),
                                'defCol' => array('<td class="cell">', '</td>'),
+                               '1'              => array('<td class="cell right">', '</td>'),
                                '2'              => array('<td class="cell right">', '</td>'),
                        );
+                       $rowWithSpan = array (
+                               'tr'     => array('<tr class="bgColor3-20">', '</tr>'),
+                               'defCol' => array('<td class="cell">', '</td>'),
+                               '1'              => array('<td class="cell right">', '</td>'),
+                               '2'              => array('<td class="cell right">', '</td>'),
+                               '3'              => array('<td class="cell" colspan="6">', '</td>'),
+                       );
                        $table = array();
                        $tr = 0;
 
@@ -882,19 +909,27 @@ class tx_scheduler_Module extends t3lib_SCbase {
 
                                // 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
+                                       // Define action icons
+                               $editAction = '<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> ';
+                               $deleteAction = '<a href="' . $GLOBALS['MCONF']['_'] . '&CMD=delete&tx_scheduler[uid]=' . $schedulerRecord['uid'] . '" 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>';
+                                       // Define some default values
+                               $lastExecution = '-';
+                               $isRunning = false;
                                $executionStatus = 'scheduled';
                                $executionStatusDetail = '';
+                               $executionStatusOutput = '';
+                               $name = '';
+                               $nextDate = '-';
+                               $execType = '-';
+                               $frequency = '-';
+                               $multiple = '-';
+                               $startExecutionElement = '&nbsp;';
 
-                               $name = $registeredClasses[$schedulerRecord['classname']]['title']. ' (' . $registeredClasses[$schedulerRecord['classname']]['extension'] . ')';
-                               $additionalInformation = $task->getAdditionalInformation();
-                               if (!empty($additionalInformation)) {
-                                       $name .= ' [' . $additionalInformation . ']';
-                               }
+                                       // Restore the serialized task and pass it a reference to the scheduler object
+                               $task = unserialize($schedulerRecord['serialized_task_object']);
 
                                        // Assemble information about last execution
+                               $context = '';
                                if (!empty($schedulerRecord['lastexecution_time'])) {
                                        $lastExecution = date($dateFormat, $schedulerRecord['lastexecution_time']);
                                        if ($schedulerRecord['lastexecution_context'] == 'CLI') {
@@ -903,95 +938,114 @@ class tx_scheduler_Module extends t3lib_SCbase {
                                                $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';
-                               }
+                               if ($this->scheduler->isValidTaskObject($task)) {
+                                       // The task object is valid
 
-                                       // 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';
+                                       $name = $registeredClasses[$schedulerRecord['classname']]['title']. ' (' . $registeredClasses[$schedulerRecord['classname']]['extension'] . ')';
+                                       $additionalInformation = $task->getAdditionalInformation();
+                                       if (!empty($additionalInformation)) {
+                                               $name .= ' [' . $additionalInformation . ']';
                                        }
-                               }
 
-                                       // 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();
+                                               // Check if task currently has a running execution
+                                       if (!empty($schedulerRecord['serialized_executions'])) {
+                                               $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 {
-                                               $frequency = $task->getExecution()->getCronCmd();
+                                               $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');
-                               }
+                                               // 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" />';
+                                               // 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>';
-                               }
+                                               // Show no action links (edit, delete) if task is running
+                                       $actions = $editAction . $deleteAction;
+                                       if ($isRunning) {
+                                               $actions = '&nbsp;';
+                                       }
 
-                                       // Check the disable status
-                               if ($schedulerRecord['disable'] == 1) {
-                                       $tableLayout[$tr] = $disabledTaskRow;
-                                       $executionStatus  = 'disabled';
-                               }
+                                               // 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']);
+                                               // 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());
-                               }
+                                               $executionStatus       = 'failure';
+                                               $executionStatusDetail = sprintf($GLOBALS['LANG']->getLL('msg.executionFailureReport'), $exception->getCode(), $exception->getMessage());
+                                       }
+
+                                               // Format the execution status
+                                       $executionStatusOutput = '<img ' . t3lib_iconWorks::skinImg(t3lib_extMgm::extRelPath('scheduler'), 'res/gfx/status_' . $executionStatus . '.png') . ' alt="' . $GLOBALS['LANG']->getLL('status.' . $executionStatus) . '" title="' . $executionStatusDetail . '" />' . ' ' . $name;
 
+                                       $table[$tr][] = $startExecutionElement;
+                                       $table[$tr][] = $actions;
+                                       $table[$tr][] = $schedulerRecord['uid'];
+                                       $table[$tr][] = $executionStatusOutput;
+                                       $table[$tr][] = $execType;
+                                       $table[$tr][] = $frequency;
+                                       $table[$tr][] = $multiple;
+                                       $table[$tr][] = $lastExecution;
+                                       $table[$tr][] = $nextDate;
 
-                                       // 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 . '" />';
+                               } else {
+                                       // The task object is not valid
+                                       // Prepare to issue an error
+
+                                               /** @var t3lib_FlashMessage $flashMessage */
+                                       $flashMessage = t3lib_div::makeInstance('t3lib_FlashMessage',
+                                               sprintf($GLOBALS['LANG']->getLL('msg.invalidTaskClass'), $schedulerRecord['classname']),
+                                               '',
+                                               t3lib_FlashMessage::ERROR
+                                       );
+                                       $executionStatusOutput = $flashMessage->render();
 
-                               $table[$tr][] = $startExecutionElement;
-                               $table[$tr][] = $actions;
-                               $table[$tr][] = $schedulerRecord['uid'];
-                               $table[$tr][] = $executionStatus . ' ' . $name;
-                               $table[$tr][] = $execType;
-                               $table[$tr][] = $frequency;
-                               $table[$tr][] = $multiple;
-                               $table[$tr][] = $lastExecution;
-                               $table[$tr][] = $nextDate;
+                                       $tableLayout[$tr] = $rowWithSpan;
+                                       $table[$tr][] = $startExecutionElement;
+                                       $table[$tr][] = $deleteAction;
+                                       $table[$tr][] = $schedulerRecord['uid'];
+                                       $table[$tr][] = $executionStatusOutput;
+                               }
 
                                $tr++;
                        }
index a8961b1..03e3d85 100644 (file)
                        <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.noFrequency">No frequency was defined, either as an interval or as a cron command.</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>
@@ -70,7 +70,8 @@
                        <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.invalidTaskClass">Class %s of the registered task could not be found. You should re-install the extension that provided it or simply delete this task.</label>
+                       <label index="msg.invalidTaskClassEdit">The current class (%s) could not be found. Change it below or consider deleting this task altogether.</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>
index 96bfefb..4f04829 100644 (file)
@@ -18,7 +18,7 @@
 }
 
 .tx_scheduler_mod1 td {
-       vertical-align: middle;
+       vertical-align: top;
 }
 .tx_scheduler_mod1 td.cell {
        padding: 2px 4px;