TYPO3 CMS  TYPO3_6-2
SchedulerModuleController.php
Go to the documentation of this file.
1 <?php
3 
28 
37 
43  public $backPath;
44 
50  protected $submittedData = array();
51 
58  protected $messages = array();
59 
63  protected $cshKey;
64 
68  protected $scheduler;
69 
73  protected $pageRenderer;
74 
80  public function __construct() {
81  $GLOBALS['LANG']->includeLLFile('EXT:scheduler/mod1/locallang.xlf');
82  $GLOBALS['BE_USER']->modAccess($GLOBALS['MCONF'], TRUE);
83  $this->backPath = $GLOBALS['BACK_PATH'];
84  // Set key for CSH
85  $this->cshKey = '_MOD_' . $GLOBALS['MCONF']['name'];
86  }
87 
93  public function init() {
94  parent::init();
95  // Initialize document
96  $this->doc = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\Template\\DocumentTemplate');
97  $this->doc->setModuleTemplate(ExtensionManagementUtility::extPath('scheduler') . 'mod1/mod_template.html');
98  $this->pageRenderer = $this->doc->getPageRenderer();
99  $this->pageRenderer->addCssFile(ExtensionManagementUtility::extRelPath('scheduler') . 'res/tx_scheduler_be.css');
100  $this->doc->backPath = $this->backPath;
101  $this->doc->bodyTagId = 'typo3-mod-php';
102  $this->doc->bodyTagAdditions = 'class="tx_scheduler_mod1"';
103  // Create scheduler instance
104  $this->scheduler = GeneralUtility::makeInstance('TYPO3\\CMS\\Scheduler\\Scheduler');
105  }
106 
112  public function menuConfig() {
113  $this->MOD_MENU = array(
114  'function' => array(
115  'scheduler' => $GLOBALS['LANG']->getLL('function.scheduler'),
116  'check' => $GLOBALS['LANG']->getLL('function.check'),
117  'info' => $GLOBALS['LANG']->getLL('function.info')
118  )
119  );
120  parent::menuConfig();
121  }
122 
128  public function main() {
129  // Access check!
130  // The page will show only if user has admin rights
131  if ($GLOBALS['BE_USER']->user['admin']) {
132  // Set the form
133  $this->doc->form = '<form name="tx_scheduler_form" id="tx_scheduler_form" method="post" action="">';
134  $this->pageRenderer->addInlineSetting('scheduler', 'runningIcon', ExtensionManagementUtility::extRelPath('scheduler') . 'res/gfx/status_running.png');
135  // Prepare main content
136  $this->content = $this->doc->header($GLOBALS['LANG']->getLL('function.' . $this->MOD_SETTINGS['function']));
137  $this->content .= $this->getModuleContent();
138  } else {
139  // If no access, only display the module's title
140  $this->content = $this->doc->header($GLOBALS['LANG']->getLL('title'));
141  $this->content .= $this->doc->spacer(5);
142  }
143  // Place content inside template
144  $content = $this->doc->moduleBody(array(), $this->getDocHeaderButtons(), $this->getTemplateMarkers());
145  // Renders the module page
146  $this->content = $this->doc->render($GLOBALS['LANG']->getLL('title'), $content);
147  }
148 
154  protected function getModuleContent() {
155  $content = '';
156  $sectionTitle = '';
157  // Get submitted data
158  $this->submittedData = GeneralUtility::_GPmerged('tx_scheduler');
159  $this->submittedData['uid'] = (int)$this->submittedData['uid'];
160  // If a save command was submitted, handle saving now
161  if ($this->CMD === 'save' || $this->CMD === 'saveclose' || $this->CMD === 'savenew') {
162  $previousCMD = GeneralUtility::_GP('previousCMD');
163  // First check the submitted data
164  $result = $this->preprocessData();
165  // If result is ok, proceed with saving
166  if ($result) {
167  $this->saveTask();
168  if ($this->CMD == 'saveclose') {
169  // Unset command, so that default screen gets displayed
170  unset($this->CMD);
171  } elseif ($this->CMD == 'save') {
172  // After saving a "add form", return to edit
173  $this->CMD = 'edit';
174  } elseif ($this->CMD === 'savenew') {
175  // Unset submitted data, so that empty form gets displayed
176  unset($this->submittedData);
177  // After saving a "add/edit form", return to add
178  $this->CMD = 'add';
179  } else {
180  // Return to edit form
181  $this->CMD = $previousCMD;
182  }
183  } else {
184  $this->CMD = $previousCMD;
185  }
186  }
187  // Handle chosen action
188  switch ((string) $this->MOD_SETTINGS['function']) {
189  case 'scheduler':
190  // Scheduler's main screen
191  $this->executeTasks();
192  // Execute chosen action
193  switch ($this->CMD) {
194  case 'add':
195 
196  case 'edit':
197  try {
198  // Try adding or editing
199  $content .= $this->editTask();
200  $sectionTitle = $GLOBALS['LANG']->getLL('action.' . $this->CMD);
201  } catch (\Exception $e) {
202  if ($e->getCode() === 1305100019) {
203  // Invalid controller class name exception
204  $this->addMessage($e->getMessage(), FlashMessage::ERROR);
205  }
206  // An exception may also happen when the task to
207  // edit could not be found. In this case revert
208  // to displaying the list of tasks
209  // It can also happen when attempting to edit a running task
210  $content .= $this->listTasks();
211  }
212  break;
213  case 'delete':
214  $this->deleteTask();
215  $content .= $this->listTasks();
216  break;
217  case 'stop':
218  $this->stopTask();
219  $content .= $this->listTasks();
220  break;
221  case 'list':
222 
223  default:
224  $content .= $this->listTasks();
225  }
226  break;
227  case 'check':
228  // Setup check screen
229  // TODO: move check to the report module
230  $content .= $this->displayCheckScreen();
231  break;
232  case 'info':
233  // Information screen
234  $content .= $this->displayInfoScreen();
235  break;
236  }
237  // Wrap the content in a section
238  return $this->doc->section($sectionTitle, '<div class="tx_scheduler_mod1">' . $content . '</div>', 0, 1);
239  }
240 
246  public function render() {
247  echo $this->content;
248  }
249 
257  protected function checkSchedulerUser() {
258  $schedulerUserStatus = -1;
259  // Assemble base WHERE clause
260  $where = 'username = \'_cli_scheduler\' AND admin = 0' . BackendUtility::deleteClause('be_users');
261  // Check if user exists at all
262  $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('1', 'be_users', $where);
263  if ($GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
264  $schedulerUserStatus = 0;
265  // Check if user exists and is enabled
266  $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('1', 'be_users', $where . BackendUtility::BEenableFields('be_users'));
267  if ($GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
268  $schedulerUserStatus = 1;
269  }
270  }
271  $GLOBALS['TYPO3_DB']->sql_free_result($res);
272  return $schedulerUserStatus;
273  }
274 
280  protected function createSchedulerUser() {
281  // Check _cli_scheduler user status
282  $checkUser = $this->checkSchedulerUser();
283  // Prepare default message
284  $message = $GLOBALS['LANG']->getLL('msg.userExists');
286  // If the user does not exist, try creating it
287  if ($checkUser == -1) {
288  // Prepare necessary data for _cli_scheduler user creation
289  $password = uniqid('scheduler', TRUE);
291  $objInstanceSaltedPW = SaltFactory::getSaltingInstance();
292  $password = $objInstanceSaltedPW->getHashedPassword($password);
293  }
294  $data = array('be_users' => array('NEW' => array('username' => '_cli_scheduler', 'password' => $password, 'pid' => 0)));
296  $tcemain = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\DataHandling\\DataHandler');
297  $tcemain->stripslashes_values = 0;
298  $tcemain->start($data, array());
299  $tcemain->process_datamap();
300  // Check if a new uid was indeed generated (i.e. a new record was created)
301  // (counting TCEmain errors doesn't work as some failures don't report errors)
302  $numberOfNewIDs = count($tcemain->substNEWwithIDs);
303  if ($numberOfNewIDs == 1) {
304  $message = $GLOBALS['LANG']->getLL('msg.userCreated');
306  } else {
307  $message = $GLOBALS['LANG']->getLL('msg.userNotCreated');
309  }
310  }
311  $this->addMessage($message, $severity);
312  }
313 
320  protected function displayCheckScreen() {
321  $message = '';
323  // First, check if _cli_scheduler user creation was requested
324  if ($this->CMD == 'user') {
325  $this->createSchedulerUser();
326  }
327 
328  // Start generating the content
329  $content = '<p class="lead">' . $GLOBALS['LANG']->getLL('msg.schedulerSetupCheck') . '</p>';
330 
331  // Display information about last automated run, as stored in the system registry
333  $registry = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Registry');
334  $lastRun = $registry->get('tx_scheduler', 'lastRun');
335  if (!is_array($lastRun)) {
336  $message = $GLOBALS['LANG']->getLL('msg.noLastRun');
338  } else {
339  if (empty($lastRun['end']) || empty($lastRun['start']) || empty($lastRun['type'])) {
340  $message = $GLOBALS['LANG']->getLL('msg.incompleteLastRun');
342  } else {
343  $startDate = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'], $lastRun['start']);
344  $startTime = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'], $lastRun['start']);
345  $endDate = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'], $lastRun['end']);
346  $endTime = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'], $lastRun['end']);
347  $label = 'automatically';
348  if ($lastRun['type'] == 'manual') {
349  $label = 'manually';
350  }
351  $type = $GLOBALS['LANG']->getLL('label.' . $label);
352  $message = sprintf($GLOBALS['LANG']->getLL('msg.lastRun'), $type, $startDate, $startTime, $endDate, $endTime);
354  }
355  }
357  $flashMessage = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Messaging\\FlashMessage', $message, '', $severity);
358 
359  $content .= '<div class="info-block">';
360  $content .= '<h2>' . $GLOBALS['LANG']->getLL('hdg.lastRun') . '</h2>';
361  $content .= $flashMessage->render();
362  $content .= '</div>';
363 
364  // Check CLI user
365  $content .= '<div class="info-block">';
366  $content .= '<h2>' . $GLOBALS['LANG']->getLL('hdg.schedulerUser') . '</h2>';
367  $content .= '<p>' . $GLOBALS['LANG']->getLL('msg.schedulerUser') . '</p>';
368  $checkUser = $this->checkSchedulerUser();
369  if ($checkUser == -1) {
370  $link = $GLOBALS['MCONF']['_'] . '&SET[function]=check&CMD=user';
371  $message = sprintf($GLOBALS['LANG']->getLL('msg.schedulerUserMissing'), htmlspecialchars($link));
373  } elseif ($checkUser == 0) {
374  $message = $GLOBALS['LANG']->getLL('msg.schedulerUserFoundButDisabled');
376  } else {
377  $message = $GLOBALS['LANG']->getLL('msg.schedulerUserFound');
379  }
380  $flashMessage = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Messaging\\FlashMessage', $message, '', $severity);
381  $content .= $flashMessage->render() . '</div>';
382 
383  // Check if CLI script is executable or not
384  $script = PATH_typo3 . 'cli_dispatch.phpsh';
385  $isExecutable = FALSE;
386  // Skip this check if running Windows, as rights do not work the same way on this platform
387  // (i.e. the script will always appear as *not* executable)
388  if (TYPO3_OS === 'WIN') {
389  $isExecutable = TRUE;
390  } else {
391  $isExecutable = is_executable($script);
392  }
393  $content .= '<div class="info-block">';
394  $content .= '<h2>' . $GLOBALS['LANG']->getLL('hdg.cliScript') . '</h2>';
395  $content .= '<p>' . sprintf($GLOBALS['LANG']->getLL('msg.cliScript'), $script) . '</p>';
396  if ($isExecutable) {
397  $message = $GLOBALS['LANG']->getLL('msg.cliScriptExecutable');
399  } else {
400  $message = $GLOBALS['LANG']->getLL('msg.cliScriptNotExecutable');
402  }
403  $flashMessage = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Messaging\\FlashMessage', $message, '', $severity);
404  $content .= $flashMessage->render() . '</div>';
405  return $content;
406  }
407 
413  protected function displayInfoScreen() {
414  $content = '';
415  $registeredClasses = self::getRegisteredClasses();
416  // No classes available, display information message
417  if (count($registeredClasses) == 0) {
419  $flashMessage = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Messaging\\FlashMessage', $GLOBALS['LANG']->getLL('msg.noTasksDefined'), '', \TYPO3\CMS\Core\Messaging\FlashMessage::INFO);
420  $content .= $flashMessage->render();
421  } else {
422  // Initialise table layout
423  $tableLayout = array(
424  'table' => array('<table class="t3-table">', '</table>'),
425  '0' => array(
426  'tr' => array('<thead><tr>', '</tr></thead>'),
427  'defCol' => array('<td>', '</td>')
428  ),
429  'defRow' => array(
430  'tr' => array('<tr>', '</tr>'),
431  'defCol' => array('<td>', '</td>')
432  )
433  );
434  $table = array();
435  $tr = 0;
436  // Header row
437  $table[$tr][] = $GLOBALS['LANG']->getLL('label.name');
438  $table[$tr][] = $GLOBALS['LANG']->getLL('label.extension');
439  $table[$tr][] = $GLOBALS['LANG']->getLL('label.description');
440  $table[$tr][] = '';
441  $tr++;
442  // Display information about each service
443  foreach ($registeredClasses as $class => $classInfo) {
444  $table[$tr][] = htmlspecialchars($classInfo['title']);
445  $table[$tr][] = htmlspecialchars($classInfo['extension']);
446  $table[$tr][] = htmlspecialchars($classInfo['description']);
447  $link = $GLOBALS['MCONF']['_'] . '&SET[function]=list&CMD=add&tx_scheduler[class]=' . $class;
448  $table[$tr][] = '<a href="' . htmlspecialchars($link) . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xlf:new', TRUE) . '" class="icon">' . IconUtility::getSpriteIcon('actions-document-new') . '</a>';
449  $tr++;
450  }
451  // Render the table and return it
452  $content = '<p class="lead">' . $GLOBALS['LANG']->getLL('msg.infoScreenIntro') . '</p>';
453  $content .= $this->doc->table($table, $tableLayout);
454  }
455  return $content;
456  }
457 
464  protected function displayServerTime() {
465  // Get the current time, formatted
466  $dateFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] . ' ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'] . ' T (e';
467  $now = date($dateFormat) . ', GMT ' . date('P') . ')';
468  // Display the help text
469  $serverTime = '<h3>' . $GLOBALS['LANG']->getLL('label.serverTime') . '</h3>';
470  $serverTime .= '<p>' . $GLOBALS['LANG']->getLL('msg.serverTimeHelp') . '</p>';
471  $serverTime .= '<p>' . sprintf($GLOBALS['LANG']->getLL('msg.serverTime'), $now) . '</p>';
472  return $serverTime;
473  }
474 
481  protected function renderTaskProgressBar($progress) {
482  $progressText = $GLOBALS['LANG']->getLL('status.progress') . ':&nbsp;' . $progress . '%';
483  $progressBar = '<div class="progress"> <div class="bar" style="width: ' . $progress . '%;">' . $progressText . '</div> </div>';
484  return $progressBar;
485  }
486 
492  protected function deleteTask() {
493  try {
494  // Try to fetch the task and delete it
495  $task = $this->scheduler->fetchTask($this->submittedData['uid']);
496  // If the task is currently running, it may not be deleted
497  if ($task->isExecutionRunning()) {
498  $this->addMessage($GLOBALS['LANG']->getLL('msg.maynotDeleteRunningTask'), \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
499  } else {
500  if ($this->scheduler->removeTask($task)) {
501  $GLOBALS['BE_USER']->writeLog(4, 0, 0, 0, 'Scheduler task "%s" (UID: %s, Class: "%s") was deleted', array($task->getTaskTitle(), $task->getTaskUid(), $task->getTaskClassName()));
502  $this->addMessage($GLOBALS['LANG']->getLL('msg.deleteSuccess'));
503  } else {
504  $this->addMessage($GLOBALS['LANG']->getLL('msg.deleteError'), \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
505  }
506  }
507  } catch (\UnexpectedValueException $e) {
508  // The task could not be unserialized properly, simply delete the database record
509  $result = $GLOBALS['TYPO3_DB']->exec_DELETEquery('tx_scheduler_task', 'uid = ' . (int)$this->submittedData['uid']);
510  if ($result) {
511  $this->addMessage($GLOBALS['LANG']->getLL('msg.deleteSuccess'));
512  } else {
513  $this->addMessage($GLOBALS['LANG']->getLL('msg.deleteError'), \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
514  }
515  } catch (\OutOfBoundsException $e) {
516  // The task was not found, for some reason
517  $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.taskNotFound'), $this->submittedData['uid']), \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
518  }
519  }
520 
529  protected function stopTask() {
530  try {
531  // Try to fetch the task and stop it
532  $task = $this->scheduler->fetchTask($this->submittedData['uid']);
533  if ($task->isExecutionRunning()) {
534  // If the task is indeed currently running, clear marked executions
535  $result = $task->unmarkAllExecutions();
536  if ($result) {
537  $this->addMessage($GLOBALS['LANG']->getLL('msg.stopSuccess'));
538  } else {
539  $this->addMessage($GLOBALS['LANG']->getLL('msg.stopError'), \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
540  }
541  } else {
542  // The task is not running, nothing to unmark
543  $this->addMessage($GLOBALS['LANG']->getLL('msg.maynotStopNonRunningTask'), \TYPO3\CMS\Core\Messaging\FlashMessage::WARNING);
544  }
545  } catch (\Exception $e) {
546  // The task was not found, for some reason
547  $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.taskNotFound'), $this->submittedData['uid']), \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
548  }
549  }
550 
556  protected function editTask() {
557  $registeredClasses = self::getRegisteredClasses();
558  $registeredTaskGroups = self::getRegisteredTaskGroups();
559  $content = '';
560  $taskInfo = array();
561  $task = NULL;
562  $process = 'edit';
563  if ($this->submittedData['uid'] > 0) {
564  // If editing, retrieve data for existing task
565  try {
566  $taskRecord = $this->scheduler->fetchTaskRecord($this->submittedData['uid']);
567  // If there's a registered execution, the task should not be edited
568  if (!empty($taskRecord['serialized_executions'])) {
569  $this->addMessage($GLOBALS['LANG']->getLL('msg.maynotEditRunningTask'), \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
570  throw new \LogicException('Runnings tasks cannot not be edited', 1251232849);
571  }
572  // Get the task object
574  $task = unserialize($taskRecord['serialized_task_object']);
575  // Set some task information
576  $taskInfo['disable'] = $taskRecord['disable'];
577  $taskInfo['description'] = $taskRecord['description'];
578  $taskInfo['task_group'] = $taskRecord['task_group'];
579  // Check that the task object is valid
580  if ($this->scheduler->isValidTaskObject($task)) {
581  // The task object is valid, process with fetching current data
582  $taskInfo['class'] = get_class($task);
583  // Get execution information
584  $taskInfo['start'] = $task->getExecution()->getStart();
585  $taskInfo['end'] = $task->getExecution()->getEnd();
586  $taskInfo['interval'] = $task->getExecution()->getInterval();
587  $taskInfo['croncmd'] = $task->getExecution()->getCronCmd();
588  $taskInfo['multiple'] = $task->getExecution()->getMultiple();
589  if (!empty($taskInfo['interval']) || !empty($taskInfo['croncmd'])) {
590  // Guess task type from the existing information
591  // If an interval or a cron command is defined, it's a recurring task
592  // FIXME: remove magic numbers for the type, use class constants instead
593  $taskInfo['type'] = 2;
594  $taskInfo['frequency'] = $taskInfo['interval'] ?: $taskInfo['croncmd'];
595  } else {
596  // It's not a recurring task
597  // Make sure interval and cron command are both empty
598  $taskInfo['type'] = 1;
599  $taskInfo['frequency'] = '';
600  $taskInfo['end'] = 0;
601  }
602  } else {
603  // The task object is not valid
604  // Issue error message
605  $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.invalidTaskClassEdit'), get_class($task)), \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
606  // Initialize empty values
607  $taskInfo['start'] = 0;
608  $taskInfo['end'] = 0;
609  $taskInfo['frequency'] = '';
610  $taskInfo['multiple'] = FALSE;
611  $taskInfo['type'] = 1;
612  }
613  } catch (\OutOfBoundsException $e) {
614  // Add a message and continue throwing the exception
615  $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.taskNotFound'), $this->submittedData['uid']), \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
616  throw $e;
617  }
618  } else {
619  // If adding a new object, set some default values
620  $taskInfo['class'] = key($registeredClasses);
621  $taskInfo['type'] = 2;
622  $taskInfo['start'] = $GLOBALS['EXEC_TIME'];
623  $taskInfo['end'] = '';
624  $taskInfo['frequency'] = '';
625  $taskInfo['multiple'] = 0;
626  $process = 'add';
627  }
628  if (count($this->submittedData) > 0) {
629  // If some data was already submitted, use it to override
630  // existing data
632  }
633  // Get the extra fields to display for each task that needs some
634  $allAdditionalFields = array();
635  if ($process == 'add') {
636  foreach ($registeredClasses as $class => $registrationInfo) {
637  if (!empty($registrationInfo['provider'])) {
639  $providerObject = GeneralUtility::getUserObj($registrationInfo['provider']);
640  if ($providerObject instanceof \TYPO3\CMS\Scheduler\AdditionalFieldProviderInterface) {
641  $additionalFields = $providerObject->getAdditionalFields($taskInfo, NULL, $this);
642  $allAdditionalFields = array_merge($allAdditionalFields, array($class => $additionalFields));
643  }
644  }
645  }
646  } else {
647  if (!empty($registeredClasses[$taskInfo['class']]['provider'])) {
648  $providerObject = GeneralUtility::getUserObj($registeredClasses[$taskInfo['class']]['provider']);
649  if ($providerObject instanceof \TYPO3\CMS\Scheduler\AdditionalFieldProviderInterface) {
650  $allAdditionalFields[$taskInfo['class']] = $providerObject->getAdditionalFields($taskInfo, $task, $this);
651  }
652  }
653  }
654  // Load necessary JavaScript
655  $this->pageRenderer->loadExtJS();
656  $this->pageRenderer->addJsFile(ExtensionManagementUtility::extRelPath('scheduler') . 'res/tx_scheduler_be.js');
657  $this->pageRenderer->addJsFile($this->backPath . 'sysext/backend/Resources/Public/JavaScript/tceforms.js');
658  $this->pageRenderer->addJsFile($this->backPath . 'js/extjs/ux/Ext.ux.DateTimePicker.js');
659  // Define settings for Date Picker
660  $typo3Settings = array(
661  'datePickerUSmode' => $GLOBALS['TYPO3_CONF_VARS']['SYS']['USdateFormat'] ? 1 : 0,
662  'dateFormat' => array('j-n-Y', 'G:i j-n-Y'),
663  'dateFormatUS' => array('n-j-Y', 'G:i n-j-Y')
664  );
665  $this->pageRenderer->addInlineSettingArray('', $typo3Settings);
666  // Define table layout for add/edit form
667  $tableLayout = array(
668  'table' => array('<table border="0" cellspacing="0" cellpadding="0" id="edit_form" class="typo3-usersettings">', '</table>')
669  );
670  // Define a style for hiding
671  // Some fields will be hidden when the task is not recurring
672  $style = '';
673  if ($taskInfo['type'] == 1) {
674  $style = ' style="display: none"';
675  }
676  // Start rendering the add/edit form
677  $content .= '<input type="hidden" name="tx_scheduler[uid]" value="' . htmlspecialchars($this->submittedData['uid']) . '" />';
678  $content .= '<input type="hidden" name="previousCMD" value="' . htmlspecialchars($this->CMD) . '" />';
679  $table = array();
680  $tr = 0;
681  $defaultCell = array('<td class="td-input">', '</td>');
682  // Disable checkbox
683  $label = '<label for="task_disable">' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xlf:disable') . '</label>';
684  $table[$tr][] = BackendUtility::wrapInHelp($this->cshKey, 'task_disable', $label);
685  $table[$tr][] = '<input type="hidden" name="tx_scheduler[disable]" value="0" />
686  <input type="checkbox" name="tx_scheduler[disable]" value="1" id="task_disable"' . ($taskInfo['disable'] == 1 ? ' checked="checked"' : '') . ' />';
687  $tableLayout[$tr] = array(
688  'tr' => array('<tr id="task_disable_row">', '</tr>'),
689  'defCol' => $defaultCell,
690  '0' => array('<td class="td-label">', '</td>')
691  );
692  $tr++;
693  // Task class selector
694  $label = '<label for="task_class">' . $GLOBALS['LANG']->getLL('label.class') . '</label>';
695  $table[$tr][] = BackendUtility::wrapInHelp($this->cshKey, 'task_class', $label);
696  // On editing, don't allow changing of the task class, unless it was not valid
697  if ($this->submittedData['uid'] > 0 && !empty($taskInfo['class'])) {
698  $cell = $registeredClasses[$taskInfo['class']]['title'] . ' (' . $registeredClasses[$taskInfo['class']]['extension'] . ')';
699  $cell .= '<input type="hidden" name="tx_scheduler[class]" id="task_class" value="' . htmlspecialchars($taskInfo['class']) . '" />';
700  } else {
701  $cell = '<select name="tx_scheduler[class]" id="task_class" class="wide" onchange="actOnChangedTaskClass(this)">';
702  // Group registered classes by classname
703  $groupedClasses = array();
704  foreach ($registeredClasses as $class => $classInfo) {
705  $groupedClasses[$classInfo['extension']][$class] = $classInfo;
706  }
707  ksort($groupedClasses);
708  // Loop on all grouped classes to display a selector
709  foreach ($groupedClasses as $extension => $class) {
710  $cell .= '<optgroup label="' . htmlspecialchars($extension) . '">';
711  foreach ($groupedClasses[$extension] as $class => $classInfo) {
712  $selected = $class == $taskInfo['class'] ? ' selected="selected"' : '';
713  $cell .= '<option value="' . htmlspecialchars($class) . '"' . 'title="' . htmlspecialchars($classInfo['description']) . '"' . $selected . '>' . htmlspecialchars($classInfo['title']) . '</option>';
714  }
715  $cell .= '</optgroup>';
716  }
717  $cell .= '</select>';
718  }
719  $table[$tr][] = $cell;
720  // Make sure each row has a unique id, for manipulation with JS
721  $tableLayout[$tr] = array(
722  'tr' => array('<tr id="task_class_row">', '</tr>'),
723  'defCol' => $defaultCell,
724  '0' => array('<td class="td-label">', '</td>')
725  );
726  $tr++;
727  // Task type selector
728  $label = '<label for="task_type">' . $GLOBALS['LANG']->getLL('label.type') . '</label>';
729  $table[$tr][] = BackendUtility::wrapInHelp($this->cshKey, 'task_type', $label);
730  $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>';
731  $tableLayout[$tr] = array(
732  'tr' => array('<tr id="task_type_row">', '</tr>'),
733  'defCol' => $defaultCell,
734  '0' => array('<td class="td-label">', '</td>')
735  );
736  $tr++;
737  // Task group selector
738  $label = '<label for="task_group">' . $GLOBALS['LANG']->getLL('label.group') . '</label>';
739  $table[$tr][] = BackendUtility::wrapInHelp($this->cshKey, 'task_group', $label);
740  $cell = '<select name="tx_scheduler[task_group]" id="task_class" class="wide">';
741  // Loop on all groups to display a selector
742  $cell .= '<option value="0" title=""></option>';
743  foreach ($registeredTaskGroups as $taskGroup) {
744  $selected = $taskGroup['uid'] == $taskInfo['task_group'] ? ' selected="selected"' : '';
745  $cell .= '<option value="' . $taskGroup['uid'] . '"' . 'title="';
746  $cell .= htmlspecialchars($taskGroup['groupName']) . '"' . $selected . '>';
747  $cell .= htmlspecialchars($taskGroup['groupName']) . '</option>';
748  }
749  $cell .= '</select>';
750  $table[$tr][] = $cell;
751  $tableLayout[$tr] = array(
752  'tr' => array('<tr id="task_group_row">', '</tr>'),
753  'defCol' => $defaultCell,
754  '0' => array('<td class="td-label">', '</td>')
755  );
756  $tr++;
757  // Start date/time field
758  // NOTE: datetime fields need a special id naming scheme
759  $label = '<label for="tceforms-datetimefield-task_start">' . $GLOBALS['LANG']->getLL('label.start') . '</label>';
760  $table[$tr][] = BackendUtility::wrapInHelp($this->cshKey, 'task_start', $label);
761  $table[$tr][] = '<input name="tx_scheduler[start]" type="text" id="tceforms-datetimefield-task_start" value="' . (empty($taskInfo['start']) ? '' : strftime('%H:%M %d-%m-%Y', $taskInfo['start'])) . '" />' . IconUtility::getSpriteIcon('actions-edit-pick-date', array(
762  'style' => 'cursor:pointer;',
763  'id' => 'picker-tceforms-datetimefield-task_start'
764  ));
765  $tableLayout[$tr] = array(
766  'tr' => array('<tr id="task_start_row">', '</tr>'),
767  'defCol' => $defaultCell,
768  '0' => array('<td class="td-label">', '</td>')
769  );
770  $tr++;
771  // End date/time field
772  // NOTE: datetime fields need a special id naming scheme
773  $label = '<label for="tceforms-datetimefield-task_end">' . $GLOBALS['LANG']->getLL('label.end') . '</label>';
774  $table[$tr][] = BackendUtility::wrapInHelp($this->cshKey, 'task_end', $label);
775  $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'])) . '" />' . IconUtility::getSpriteIcon('actions-edit-pick-date', array(
776  'style' => 'cursor:pointer;',
777  'id' => 'picker-tceforms-datetimefield-task_end'
778  ));
779  $tableLayout[$tr] = array(
780  'tr' => array('<tr id="task_end_row"' . $style . '>', '</tr>'),
781  'defCol' => $defaultCell,
782  '0' => array('<td class="td-label">', '</td>')
783  );
784  $tr++;
785  // Frequency input field
786  $label = '<label for="task_frequency">' . $GLOBALS['LANG']->getLL('label.frequency.long') . '</label>';
787  $table[$tr][] = BackendUtility::wrapInHelp($this->cshKey, 'task_frequency', $label);
788  $cell = '<input type="text" name="tx_scheduler[frequency]" id="task_frequency" value="' . htmlspecialchars($taskInfo['frequency']) . '" />';
789  $table[$tr][] = $cell;
790  $tableLayout[$tr] = array(
791  'tr' => array('<tr id="task_frequency_row"' . $style . '>', '</tr>'),
792  'defCol' => $defaultCell,
793  '0' => array('<td class="td-label">', '</td>')
794  );
795  $tr++;
796  // Multiple execution selector
797  $label = '<label for="task_multiple">' . $GLOBALS['LANG']->getLL('label.parallel.long') . '</label>';
798  $table[$tr][] = BackendUtility::wrapInHelp($this->cshKey, 'task_multiple', $label);
799  $table[$tr][] = '<input type="hidden" name="tx_scheduler[multiple]" value="0" />
800  <input type="checkbox" name="tx_scheduler[multiple]" value="1" id="task_multiple"' . ($taskInfo['multiple'] == 1 ? ' checked="checked"' : '') . ' />';
801  $tableLayout[$tr] = array(
802  'tr' => array('<tr id="task_multiple_row"' . $style . '>', '</tr>'),
803  'defCol' => $defaultCell,
804  '0' => array('<td class="td-label">', '</td>')
805  );
806  $tr++;
807  // Description
808  $label = '<label for="task_description">' . $GLOBALS['LANG']->getLL('label.description') . '</label>';
809  $table[$tr][] = BackendUtility::wrapInHelp($this->cshKey, 'task_description', $label);
810  $table[$tr][] = '<textarea name="tx_scheduler[description]">' . htmlspecialchars($taskInfo['description']) . '</textarea>';
811  $tableLayout[$tr] = array(
812  'tr' => array('<tr id="task_description_row">', '</tr>'),
813  'defCol' => $defaultCell,
814  '0' => array('<td class="td-label">', '</td>')
815  );
816  $tr++;
817  // Display additional fields
818  foreach ($allAdditionalFields as $class => $fields) {
819  if ($class == $taskInfo['class']) {
820  $additionalFieldsStyle = '';
821  } else {
822  $additionalFieldsStyle = ' style="display: none"';
823  }
824  // Add each field to the display, if there are indeed any
825  if (isset($fields) && is_array($fields)) {
826  foreach ($fields as $fieldID => $fieldInfo) {
827  $label = '<label for="' . $fieldID . '">' . $GLOBALS['LANG']->sL($fieldInfo['label']) . '</label>';
828  $table[$tr][] = BackendUtility::wrapInHelp($fieldInfo['cshKey'], $fieldInfo['cshLabel'], $label);
829  $table[$tr][] = $fieldInfo['code'];
830  $htmlClassName = strtolower(str_replace('\\', '-', $class));
831  $tableLayout[$tr] = array(
832  'tr' => array('<tr id="' . $fieldID . '_row"' . $additionalFieldsStyle . ' class="extraFields extra_fields_' . $htmlClassName . '">', '</tr>'),
833  'defCol' => $defaultCell,
834  '0' => array('<td class="td-label">', '</td>')
835  );
836  $tr++;
837  }
838  }
839  }
840  // Render the add/edit task form
841  $content .= '<div style="float: left;"><div class="typo3-dyntabmenu-divs">';
842  $content .= $this->doc->table($table, $tableLayout);
843  $content .= '</div></div>';
844  $content .= '<div style="padding-top: 20px; clear: both;"></div>';
845  // Display information about server time usage
846  $content .= $this->displayServerTime();
847  return $content;
848  }
849 
855  protected function executeTasks() {
856  // Make sure next automatic scheduler-run is scheduled
857  if (GeneralUtility::_POST('go') !== NULL) {
858  $this->scheduler->scheduleNextSchedulerRunUsingAtDaemon();
859  }
860  // Continue if some elements have been chosen for execution
861  if (isset($this->submittedData['execute']) && count($this->submittedData['execute']) > 0) {
862  // Get list of registered classes
863  $registeredClasses = self::getRegisteredClasses();
864  // Loop on all selected tasks
865  foreach ($this->submittedData['execute'] as $uid) {
866  try {
867  // Try fetching the task
868  $task = $this->scheduler->fetchTask($uid);
869  $class = get_class($task);
870  $name = $registeredClasses[$class]['title'] . ' (' . $registeredClasses[$class]['extension'] . ')';
871  // Now try to execute it and report on outcome
872  try {
873  $result = $this->scheduler->executeTask($task);
874  if ($result) {
875  $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.executed'), $name));
876  } else {
877  $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.notExecuted'), $name), \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
878  }
879  } catch (\Exception $e) {
880  // An exception was thrown, display its message as an error
881  $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.executionFailed'), $name, $e->getMessage()), \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
882  }
883  } catch (\OutOfBoundsException $e) {
884  $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.taskNotFound'), $uid), \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
885  } catch (\UnexpectedValueException $e) {
886  $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.executionFailed'), $uid, $e->getMessage()), \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
887  }
888  }
889  // Record the run in the system registry
890  $this->scheduler->recordLastRun('manual');
891  // Make sure to switch to list view after execution
892  $this->CMD = 'list';
893  }
894  }
895 
901  protected function listTasks() {
902  // Define display format for dates
903  $dateFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] . ' ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'];
904  $content = '';
905  // Get list of registered classes
906  $registeredClasses = self::getRegisteredClasses();
907  // Get list of registered task groups
908  $registeredTaskGroups = self::getRegisteredTaskGroups();
909 
910  // add an empty entry for non-grouped tasks
911  // add in front of list
912  array_unshift($registeredTaskGroups, array('uid' => 0, 'groupName' => ''));
913 
914  // Get all registered tasks
915  // Just to get the number of entries
916  $query = array(
917  'SELECT' => '
918  tx_scheduler_task.*,
919  tx_scheduler_task_group.groupName as taskGroupName,
920  tx_scheduler_task_group.description as taskGroupDescription,
921  tx_scheduler_task_group.deleted as isTaskGroupDeleted
922  ',
923  'FROM' => '
924  tx_scheduler_task
925  LEFT JOIN tx_scheduler_task_group ON tx_scheduler_task_group.uid = tx_scheduler_task.task_group
926  ',
927  'WHERE' => '1=1',
928  'ORDERBY' => 'tx_scheduler_task_group.sorting'
929  );
930  $res = $this->getDatabaseConnection()->exec_SELECT_queryArray($query);
931  $numRows = $this->getDatabaseConnection()->sql_num_rows($res);
932 
933  // No tasks defined, display information message
934  if ($numRows == 0) {
936  $flashMessage = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Messaging\\FlashMessage', $GLOBALS['LANG']->getLL('msg.noTasks'), '', \TYPO3\CMS\Core\Messaging\FlashMessage::INFO);
937  $content .= $flashMessage->render();
938  } else {
939  // Load ExtJS framework and specific JS library
940  $this->pageRenderer->loadExtJS();
941  $this->pageRenderer->addJsFile(ExtensionManagementUtility::extRelPath('scheduler') . 'res/tx_scheduler_be.js');
942  // Initialise table layout
943  $tableLayout = array(
944  'table' => array(
945  '<table class="t3-table">',
946  '</table>'
947  ),
948  '0' => array(
949  'tr' => array('<thead><tr>', '</tr></thead>'),
950  'defCol' => array('<td>', '</td>'),
951  '1' => array('<td style="width: 56px;">', '</td>'),
952  '3' => array('<td colspan="2">', '</td>')
953  ),
954  'defRow' => array(
955  'tr' => array('<tr class="db_list_normal">', '</tr>'),
956  'defCol' => array('<td>', '</td>'),
957  '1' => array('<td class="right">', '</td>'),
958  '2' => array('<td class="right">', '</td>')
959  )
960  );
961  $disabledTaskRow = array(
962  'tr' => array('<tr class="db_list_normal disabled">', '</tr>'),
963  'defCol' => array('<td>', '</td>'),
964  '1' => array('<td class="right">', '</td>'),
965  '2' => array('<td class="right">', '</td>')
966  );
967  $rowWithSpan = array(
968  'tr' => array('<tr class="db_list_normal">', '</tr>'),
969  'defCol' => array('<td>', '</td>'),
970  '1' => array('<td class="right">', '</td>'),
971  '2' => array('<td class="right">', '</td>'),
972  '3' => array('<td colspan="6">', '</td>')
973  );
974  $taskGroupRow = array(
975  'tr' => array('<tr class="db_list_normal">', '</tr>'),
976  'defCol' => array('<td>', '</td>'),
977  '0' => array('<td colspan="10">', '</td>')
978  );
979  $table = array();
980  $tr = 0;
981  // Header row
982  $table[$tr][] = '<a href="#" onclick="toggleCheckboxes();" title="' . $GLOBALS['LANG']->getLL('label.checkAll', TRUE) . '" class="icon">' . IconUtility::getSpriteIcon('actions-document-select') . '</a>';
983  $table[$tr][] = '&nbsp;';
984  $table[$tr][] = $GLOBALS['LANG']->getLL('label.id');
985  $table[$tr][] = $GLOBALS['LANG']->getLL('task');
986  $table[$tr][] = $GLOBALS['LANG']->getLL('label.type');
987  $table[$tr][] = $GLOBALS['LANG']->getLL('label.frequency');
988  $table[$tr][] = $GLOBALS['LANG']->getLL('label.parallel');
989  $table[$tr][] = $GLOBALS['LANG']->getLL('label.lastExecution');
990  $table[$tr][] = $GLOBALS['LANG']->getLL('label.nextExecution');
991  $tr++;
992 
993  // Loop on all tasks
994  $temporaryResult = array();
995  while ($row = $this->getDatabaseConnection()->sql_fetch_assoc($res)) {
996  if ($row['taskGroupName'] === NULL || $row['isTaskGroupDeleted'] === '1') {
997  $row['taskGroupName'] = '';
998  $row['taskGroupDescription'] = '';
999  $row['task_group'] = 0;
1000  }
1001  $temporaryResult[$row['task_group']]['groupName'] = $row['taskGroupName'];
1002  $temporaryResult[$row['task_group']]['groupDescription'] = $row['taskGroupDescription'];
1003  $temporaryResult[$row['task_group']]['tasks'][] = $row;
1004  }
1005  foreach ($temporaryResult as $taskGroup) {
1006  if (!empty($taskGroup['groupName'])) {
1007  $groupText = '<strong>' . htmlspecialchars($taskGroup['groupName']) . ' </strong>';
1008  if (!empty($taskGroup['groupDescription'])) {
1009  $groupText .= '<br />' . nl2br(htmlspecialchars($taskGroup['groupDescription']));
1010  }
1011  $table[$tr++][] = '<tr><td colspan="10">' . $groupText . '</td></tr>';
1012  }
1013 
1014 
1015 
1016  foreach ($taskGroup['tasks'] as $schedulerRecord) {// Define action icons
1017  $editAction = '<a href="' . $GLOBALS['MCONF']['_'] . '&CMD=edit&tx_scheduler[uid]=' . $schedulerRecord['uid'] . '" title="' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_common.xlf:edit', TRUE) . '" class="icon">' .
1018  IconUtility::getSpriteIcon('actions-document-open') . '</a>';
1019  $deleteAction = '<a href="' . $GLOBALS['MCONF']['_'] . '&CMD=delete&tx_scheduler[uid]=' . $schedulerRecord['uid'] . '" onclick="return confirm(\'' . $this->getLanguageService()->getLL('msg.delete') . '\');" title="' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_common.xlf:delete', TRUE) . '" class="icon">' .
1020  IconUtility::getSpriteIcon('actions-edit-delete') . '</a>';
1021  $stopAction = '<a href="' . $GLOBALS['MCONF']['_'] . '&CMD=stop&tx_scheduler[uid]=' . $schedulerRecord['uid'] . '" onclick="return confirm(\'' . $this->getLanguageService()->getLL('msg.stop') . '\');" title="' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_common.xlf:stop', TRUE) . '" class="icon">' .
1022  '<img ' . IconUtility::skinImg($this->backPath, (ExtensionManagementUtility::extRelPath('scheduler') . 'res/gfx/stop.png')) . ' alt="' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_common.xlf:stop') . '" /></a>';
1023  $runAction = '<a href="' . $GLOBALS['MCONF']['_'] . '&tx_scheduler[execute][]=' . $schedulerRecord['uid'] . '" title="' . $this->getLanguageService()->getLL('action.run_task') . '" class="icon">' .
1024  IconUtility::getSpriteIcon('extensions-scheduler-run-task') . '</a>';
1025 
1026  // Define some default values
1027  $lastExecution = '-';
1028  $isRunning = FALSE;
1029  $executionStatus = 'scheduled';
1030  $executionStatusOutput = '';
1031  $name = '';
1032  $nextDate = '-';
1033  $execType = '-';
1034  $frequency = '-';
1035  $multiple = '-';
1036  $startExecutionElement = '&nbsp;';
1037  // Restore the serialized task and pass it a reference to the scheduler object
1039  $task = unserialize($schedulerRecord['serialized_task_object']);
1040  $class = get_class($task);
1041  if ($class === '__PHP_Incomplete_Class' && preg_match('/^O:[0-9]+:"(?P<classname>.+?)"/', $schedulerRecord['serialized_task_object'], $matches) === 1) {
1042  $class = $matches['classname'];
1043  }
1044  // Assemble information about last execution
1045  $context = '';
1046  if (!empty($schedulerRecord['lastexecution_time'])) {
1047  $lastExecution = date($dateFormat, $schedulerRecord['lastexecution_time']);
1048  if ($schedulerRecord['lastexecution_context'] == 'CLI') {
1049  $context = $GLOBALS['LANG']->getLL('label.cron');
1050  } else {
1051  $context = $GLOBALS['LANG']->getLL('label.manual');
1052  }
1053  $lastExecution .= ' (' . $context . ')';
1054  }
1055 
1056  if ($this->scheduler->isValidTaskObject($task)) {
1057  // The task object is valid
1058  $name = htmlspecialchars($registeredClasses[$class]['title'] . ' (' . $registeredClasses[$class]['extension'] . ')');
1059  $additionalInformation = $task->getAdditionalInformation();
1060  if ($task instanceof \TYPO3\CMS\Scheduler\ProgressProviderInterface) {
1061  $progress = round(floatval($task->getProgress()), 2);
1062  $name .= '<br />' . $this->renderTaskProgressBar($progress);
1063  }
1064  if (!empty($additionalInformation)) {
1065  $name .= '<br />[' . htmlspecialchars($additionalInformation) . ']';
1066  }
1067  // Check if task currently has a running execution
1068  if (!empty($schedulerRecord['serialized_executions'])) {
1069  $isRunning = TRUE;
1070  $executionStatus = 'running';
1071  }
1072 
1073  // Prepare display of next execution date
1074  // If task is currently running, date is not displayed (as next hasn't been calculated yet)
1075  // Also hide the date if task is disabled (the information doesn't make sense, as it will not run anyway)
1076  if ($isRunning || $schedulerRecord['disable'] == 1) {
1077  $nextDate = '-';
1078  } else {
1079  $nextDate = date($dateFormat, $schedulerRecord['nextexecution']);
1080  if (empty($schedulerRecord['nextexecution'])) {
1081  $nextDate = $GLOBALS['LANG']->getLL('none');
1082  } elseif ($schedulerRecord['nextexecution'] < $GLOBALS['EXEC_TIME']) {
1083  // Next execution is overdue, highlight date
1084  $nextDate = '<span class="late" title="' . $GLOBALS['LANG']->getLL('status.legend.scheduled') . '">' . $nextDate . '</span>';
1085  $executionStatus = 'late';
1086  }
1087  }
1088  // Get execution type
1089  if ($task->getExecution()->getInterval() == 0 && $task->getExecution()->getCronCmd() == '') {
1090  $execType = $GLOBALS['LANG']->getLL('label.type.single');
1091  $frequency = '-';
1092  } else {
1093  $execType = $GLOBALS['LANG']->getLL('label.type.recurring');
1094  if ($task->getExecution()->getCronCmd() == '') {
1095  $frequency = $task->getExecution()->getInterval();
1096  } else {
1097  $frequency = $task->getExecution()->getCronCmd();
1098  }
1099  }
1100  // Get multiple executions setting
1101  if ($task->getExecution()->getMultiple()) {
1102  $multiple = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xlf:yes');
1103  } else {
1104  $multiple = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xlf:no');
1105  }
1106  // Define checkbox
1107  $startExecutionElement = '<input type="checkbox" name="tx_scheduler[execute][]" value="' . $schedulerRecord['uid'] . '" id="task_' . $schedulerRecord['uid'] . '" class="checkboxes" />';
1108 
1109  $actions = $editAction . $deleteAction;
1110 
1111  // Check the disable status
1112  // Row is shown dimmed if task is disabled, unless it is still running
1113  if ($schedulerRecord['disable'] == 1 && !$isRunning) {
1114  $tableLayout[$tr] = $disabledTaskRow;
1115  $executionStatus = 'disabled';
1116  }
1117 
1118  // Show no action links (edit, delete) if task is running
1119  if ($isRunning) {
1120  $actions = $stopAction;
1121  } else {
1122  $actions .= $runAction;
1123  }
1124 
1125  // Check if the last run failed
1126  $failureOutput = '';
1127  if (!empty($schedulerRecord['lastexecution_failure'])) {
1128  // Try to get the stored exception array
1130  $exceptionArray = @unserialize($schedulerRecord['lastexecution_failure']);
1131  if (!is_array($exceptionArray) || empty($exceptionArray)) {
1132  $failureDetail = $GLOBALS['LANG']->getLL('msg.executionFailureDefault');
1133  } else {
1134  $failureDetail = sprintf($GLOBALS['LANG']->getLL('msg.executionFailureReport'), $exceptionArray['code'], $exceptionArray['message']);
1135  }
1136  $failureOutput = ' <img ' . IconUtility::skinImg(ExtensionManagementUtility::extRelPath('scheduler'), 'res/gfx/status_failure.png') . ' alt="' . htmlspecialchars($GLOBALS['LANG']->getLL('status.failure')) . '" title="' . htmlspecialchars($failureDetail) . '" />';
1137  }
1138  // Format the execution status,
1139  // including failure feedback, if any
1140  $executionStatusOutput = '<img ' . IconUtility::skinImg(ExtensionManagementUtility::extRelPath('scheduler'), ('res/gfx/status_' . $executionStatus . '.png')) . ' id="executionstatus_' . $schedulerRecord['uid'] . '" alt="' . htmlspecialchars($GLOBALS['LANG']->getLL(('status.' . $executionStatus))) . '" title="' . htmlspecialchars($GLOBALS['LANG']->getLL(('status.legend.' . $executionStatus))) . '" />' . $failureOutput;
1141  $table[$tr][] = $startExecutionElement;
1142  $table[$tr][] = $actions;
1143  $table[$tr][] = $schedulerRecord['uid'];
1144  $table[$tr][] = $executionStatusOutput;
1145  if ($schedulerRecord['description'] !== '') {
1146  if (!empty($this->scheduler->extConf['listShowTaskDescriptionAsHover'])) {
1147  $table[$tr][] = '<span title="' . htmlspecialchars($schedulerRecord['description']) . '">' . $name . '</span>';
1148  } else {
1149  $table[$tr][] = $name . '<br />' . nl2br(htmlspecialchars($schedulerRecord['description'])) . '<br />';
1150  }
1151  } else {
1152  $table[$tr][] = $name;
1153  }
1154  $table[$tr][] = $execType;
1155  $table[$tr][] = $frequency;
1156  $table[$tr][] = $multiple;
1157  $table[$tr][] = $lastExecution;
1158  $table[$tr][] = $nextDate;
1159  } else {
1160  // The task object is not valid
1161  // Prepare to issue an error
1163  $flashMessage = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Messaging\\FlashMessage', sprintf($GLOBALS['LANG']->getLL('msg.invalidTaskClass'), $class), '', \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
1164  $executionStatusOutput = $flashMessage->render();
1165  $tableLayout[$tr] = $rowWithSpan;
1166  $table[$tr][] = $startExecutionElement;
1167  $table[$tr][] = $deleteAction;
1168  $table[$tr][] = $schedulerRecord['uid'];
1169  $table[$tr][] = $executionStatusOutput;
1170  }
1171  $tr++;
1172  }
1173  }
1174  $this->getDatabaseConnection()->sql_free_result($res);
1175 
1176  // Render table
1177  $content .= $this->doc->table($table, $tableLayout);
1178 
1179  $content .= '<button name="go" id="scheduler_executeselected">' .
1180  IconUtility::getSpriteIcon('extensions-scheduler-run-task') . ' ' .
1181  $GLOBALS['LANG']->getLL('label.executeSelected') . '</button>';
1182  }
1183  if (!count($registeredClasses) > 0) {
1185  $flashMessage = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Messaging\\FlashMessage', $GLOBALS['LANG']->getLL('msg.noTasksDefined'), '', \TYPO3\CMS\Core\Messaging\FlashMessage::INFO);
1186  $content .= $flashMessage->render();
1187  }
1188  // Display legend, if there's at least one registered task
1189  // Also display information about the usage of server time
1190  if ($numRows > 0) {
1191  $content .= $this->doc->spacer(20);
1192  $content .= '<h3>' . $GLOBALS['LANG']->getLL('status.legend') . '</h3>
1193  <ul>
1194  <li><img ' . IconUtility::skinImg(ExtensionManagementUtility::extRelPath('scheduler'), 'res/gfx/status_failure.png') . ' alt="' . htmlspecialchars($GLOBALS['LANG']->getLL('status.failure')) . '" title="' . htmlspecialchars($GLOBALS['LANG']->getLL('status.failure')) . '" /> ' . $GLOBALS['LANG']->getLL('status.legend.failure') . '</li>
1195  <li><img ' . IconUtility::skinImg(ExtensionManagementUtility::extRelPath('scheduler'), 'res/gfx/status_late.png') . ' alt="' . htmlspecialchars($GLOBALS['LANG']->getLL('status.late')) . '" title="' . htmlspecialchars($GLOBALS['LANG']->getLL('status.late')) . '" /> ' . $GLOBALS['LANG']->getLL('status.legend.late') . '</li>
1196  <li><img ' . IconUtility::skinImg(ExtensionManagementUtility::extRelPath('scheduler'), 'res/gfx/status_running.png') . ' alt="' . htmlspecialchars($GLOBALS['LANG']->getLL('status.running')) . '" title="' . htmlspecialchars($GLOBALS['LANG']->getLL('status.running')) . '" /> ' . $GLOBALS['LANG']->getLL('status.legend.running') . '</li>
1197  <li><img ' . IconUtility::skinImg(ExtensionManagementUtility::extRelPath('scheduler'), 'res/gfx/status_scheduled.png') . ' alt="' . htmlspecialchars($GLOBALS['LANG']->getLL('status.scheduled')) . '" title="' . htmlspecialchars($GLOBALS['LANG']->getLL('status.scheduled')) . '" /> ' . $GLOBALS['LANG']->getLL('status.legend.scheduled') . '</li>
1198  <li><img ' . IconUtility::skinImg(ExtensionManagementUtility::extRelPath('scheduler'), 'res/gfx/status_disabled.png') . ' alt="' . htmlspecialchars($GLOBALS['LANG']->getLL('status.disabled')) . '" title="' . htmlspecialchars($GLOBALS['LANG']->getLL('status.disabled')) . '" /> ' . $GLOBALS['LANG']->getLL('status.legend.disabled') . '</li>
1199  </ul>';
1200  $content .= $this->displayServerTime();
1201  }
1202  return $content;
1203  }
1204 
1210  protected function saveTask() {
1211  // If a task is being edited fetch old task data
1212  if (!empty($this->submittedData['uid'])) {
1213  try {
1214  $taskRecord = $this->scheduler->fetchTaskRecord($this->submittedData['uid']);
1216  $task = unserialize($taskRecord['serialized_task_object']);
1217  } catch (\OutOfBoundsException $e) {
1218  // If the task could not be fetched, issue an error message
1219  // and exit early
1220  $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.taskNotFound'), $this->submittedData['uid']), \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
1221  return;
1222  }
1223  // Register single execution
1224  if ($this->submittedData['type'] == 1) {
1225  $task->registerSingleExecution($this->submittedData['start']);
1226  } else {
1227  if (!empty($this->submittedData['croncmd'])) {
1228  // Definition by cron-like syntax
1229  $interval = 0;
1230  $cronCmd = $this->submittedData['croncmd'];
1231  } else {
1232  // Definition by interval
1233  $interval = $this->submittedData['interval'];
1234  $cronCmd = '';
1235  }
1236  // Register recurring execution
1237  $task->registerRecurringExecution($this->submittedData['start'], $interval, $this->submittedData['end'], $this->submittedData['multiple'], $cronCmd);
1238  }
1239  // Set disable flag
1240  $task->setDisabled($this->submittedData['disable']);
1241  // Set description
1242  $task->setDescription($this->submittedData['description']);
1243  // Set task group
1244  $task->setTaskGroup($this->submittedData['task_group']);
1245  // Save additional input values
1246  if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$this->submittedData['class']]['additionalFields'])) {
1248  $providerObject = GeneralUtility::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$this->submittedData['class']]['additionalFields']);
1249  if ($providerObject instanceof \TYPO3\CMS\Scheduler\AdditionalFieldProviderInterface) {
1250  $providerObject->saveAdditionalFields($this->submittedData, $task);
1251  }
1252  }
1253  // Save to database
1254  $result = $this->scheduler->saveTask($task);
1255  if ($result) {
1256  $GLOBALS['BE_USER']->writeLog(4, 0, 0, 0, 'Scheduler task "%s" (UID: %s, Class: "%s") was updated', array($task->getTaskTitle(), $task->getTaskUid(), $task->getTaskClassName()));
1257  $this->addMessage($GLOBALS['LANG']->getLL('msg.updateSuccess'));
1258  } else {
1259  $this->addMessage($GLOBALS['LANG']->getLL('msg.updateError'), \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
1260  }
1261  } else {
1262  // A new task is being created
1263  // Create an instance of chosen class
1264  $task = GeneralUtility::makeInstance($this->submittedData['class']);
1265  if ($this->submittedData['type'] == 1) {
1266  // Set up single execution
1267  $task->registerSingleExecution($this->submittedData['start']);
1268  } else {
1269  // Set up recurring execution
1270  $task->registerRecurringExecution($this->submittedData['start'], $this->submittedData['interval'], $this->submittedData['end'], $this->submittedData['multiple'], $this->submittedData['croncmd']);
1271  }
1272  // Save additional input values
1273  if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$this->submittedData['class']]['additionalFields'])) {
1275  $providerObject = GeneralUtility::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$this->submittedData['class']]['additionalFields']);
1276  if ($providerObject instanceof \TYPO3\CMS\Scheduler\AdditionalFieldProviderInterface) {
1277  $providerObject->saveAdditionalFields($this->submittedData, $task);
1278  }
1279  }
1280  // Set disable flag
1281  $task->setDisabled($this->submittedData['disable']);
1282  // Set description
1283  $task->setDescription($this->submittedData['description']);
1284  // Set description
1285  $task->setTaskGroup($this->submittedData['task_group']);
1286  // Add to database
1287  $result = $this->scheduler->addTask($task);
1288  if ($result) {
1289  $GLOBALS['BE_USER']->writeLog(4, 0, 0, 0, 'Scheduler task "%s" (UID: %s, Class: "%s") was added', array($task->getTaskTitle(), $task->getTaskUid(), $task->getTaskClassName()));
1290  $this->addMessage($GLOBALS['LANG']->getLL('msg.addSuccess'));
1291 
1292  // set the uid of the just created task so that we
1293  // can continue editing after initial saving
1294  $this->submittedData['uid'] = $task->getTaskUid();
1295  } else {
1296  $this->addMessage($GLOBALS['LANG']->getLL('msg.addError'), \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
1297  }
1298  }
1299  }
1300 
1301  /*************************
1302  *
1303  * INPUT PROCESSING UTILITIES
1304  *
1305  *************************/
1311  protected function preprocessData() {
1312  $result = TRUE;
1313  // Validate id
1314  $this->submittedData['uid'] = empty($this->submittedData['uid']) ? 0 : (int)$this->submittedData['uid'];
1315  // Validate selected task class
1316  if (!class_exists($this->submittedData['class'])) {
1317  $this->addMessage($GLOBALS['LANG']->getLL('msg.noTaskClassFound'), \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
1318  }
1319  // Check start date
1320  if (empty($this->submittedData['start'])) {
1321  $this->addMessage($GLOBALS['LANG']->getLL('msg.noStartDate'), \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
1322  $result = FALSE;
1323  } else {
1324  try {
1325  $timestamp = $this->checkDate($this->submittedData['start']);
1326  $this->submittedData['start'] = $timestamp;
1327  } catch (\Exception $e) {
1328  $this->addMessage($GLOBALS['LANG']->getLL('msg.invalidStartDate'), \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
1329  $result = FALSE;
1330  }
1331  }
1332  // Check end date, if recurring task
1333  if ($this->submittedData['type'] == 2 && !empty($this->submittedData['end'])) {
1334  try {
1335  $timestamp = $this->checkDate($this->submittedData['end']);
1336  $this->submittedData['end'] = $timestamp;
1337  if ($this->submittedData['end'] < $this->submittedData['start']) {
1338  $this->addMessage($GLOBALS['LANG']->getLL('msg.endDateSmallerThanStartDate'), \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
1339  $result = FALSE;
1340  }
1341  } catch (\Exception $e) {
1342  $this->addMessage($GLOBALS['LANG']->getLL('msg.invalidEndDate'), \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
1343  $result = FALSE;
1344  }
1345  }
1346  // Set default values for interval and cron command
1347  $this->submittedData['interval'] = 0;
1348  $this->submittedData['croncmd'] = '';
1349  // Check type and validity of frequency, if recurring
1350  if ($this->submittedData['type'] == 2) {
1351  $frequency = trim($this->submittedData['frequency']);
1352  if (empty($frequency)) {
1353  // Empty frequency, not valid
1354  $this->addMessage($GLOBALS['LANG']->getLL('msg.noFrequency'), \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
1355  $result = FALSE;
1356  } else {
1357  $cronErrorCode = 0;
1358  $cronErrorMessage = '';
1359  // Try interpreting the cron command
1360  try {
1361  \TYPO3\CMS\Scheduler\CronCommand\NormalizeCommand::normalize($frequency);
1362  $this->submittedData['croncmd'] = $frequency;
1363  } catch (\Exception $e) {
1364  // Store the exception's result
1365  $cronErrorMessage = $e->getMessage();
1366  $cronErrorCode = $e->getCode();
1367  // Check if the frequency is a valid number
1368  // If yes, assume it is a frequency in seconds, and unset cron error code
1369  if (is_numeric($frequency)) {
1370  $this->submittedData['interval'] = (int)$frequency;
1371  unset($cronErrorCode);
1372  }
1373  }
1374  // If there's a cron error code, issue validation error message
1375  if (!empty($cronErrorCode)) {
1376  $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.frequencyError'), $cronErrorMessage, $cronErrorCode), \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
1377  $result = FALSE;
1378  }
1379  }
1380  }
1381  // Validate additional input fields
1382  if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$this->submittedData['class']]['additionalFields'])) {
1384  $providerObject = GeneralUtility::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$this->submittedData['class']]['additionalFields']);
1385  if ($providerObject instanceof \TYPO3\CMS\Scheduler\AdditionalFieldProviderInterface) {
1386  // The validate method will return TRUE if all went well, but that must not
1387  // override previous FALSE values => AND the returned value with the existing one
1388  $result &= $providerObject->validateAdditionalFields($this->submittedData, $this);
1389  }
1390  }
1391  return $result;
1392  }
1393 
1405  public function checkDate($string) {
1406  // Try with strtotime
1407  $timestamp = strtotime($string);
1408  // That failed. Try TYPO3's standard date/time input format
1409  if ($timestamp === FALSE) {
1410  // Split time and date
1411  $dateParts = GeneralUtility::trimExplode(' ', $string, TRUE);
1412  // Proceed if there are indeed two parts
1413  // Extract each component of date and time
1414  if (count($dateParts) == 2) {
1415  list($time, $date) = $dateParts;
1416  list($hour, $minutes) = GeneralUtility::trimExplode(':', $time, TRUE);
1417  list($day, $month, $year) = GeneralUtility::trimExplode('-', $date, TRUE);
1418  // Get a timestamp from all these parts
1419  $timestamp = @mktime($hour, $minutes, 0, $month, $day, $year);
1420  }
1421  // If the timestamp is still FALSE, throw an exception
1422  if ($timestamp === FALSE) {
1423  throw new \InvalidArgumentException('"' . $string . '" seems not to be a correct date.', 1294587694);
1424  }
1425  }
1426  return $timestamp;
1427  }
1428 
1429  /*************************
1430  *
1431  * APPLICATION LOGIC UTILITIES
1432  *
1433  *************************/
1441  public function addMessage($message, $severity = \TYPO3\CMS\Core\Messaging\FlashMessage::OK) {
1442  $flashMessage = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Messaging\\FlashMessage', $message, '', $severity);
1444  $flashMessageService = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Messaging\\FlashMessageService');
1446  $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
1447  $defaultFlashMessageQueue->enqueue($flashMessage);
1448  }
1449 
1463  static protected function getRegisteredClasses() {
1464  $list = array();
1465  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'])) {
1466  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'] as $class => $registrationInformation) {
1467  $title = isset($registrationInformation['title']) ? $GLOBALS['LANG']->sL($registrationInformation['title']) : '';
1468  $description = isset($registrationInformation['description']) ? $GLOBALS['LANG']->sL($registrationInformation['description']) : '';
1469  $list[$class] = array(
1470  'extension' => $registrationInformation['extension'],
1471  'title' => $title,
1472  'description' => $description,
1473  'provider' => isset($registrationInformation['additionalFields']) ? $registrationInformation['additionalFields'] : ''
1474  );
1475  }
1476  }
1477  return $list;
1478  }
1479 
1485  static protected function getRegisteredTaskGroups() {
1486  $list = array();
1487 
1488  // Get all registered task groups
1489  $query = array(
1490  'SELECT' => '*',
1491  'FROM' => 'tx_scheduler_task_group',
1492  'WHERE' => '1=1'
1493  . BackendUtility::BEenableFields('tx_scheduler_task_group')
1494  . BackendUtility::deleteClause('tx_scheduler_task_group'),
1495  'ORDERBY' => 'sorting'
1496  );
1497  $res = $GLOBALS['TYPO3_DB']->exec_SELECT_queryArray($query);
1498 
1499  while (($groupRecord = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) !== FALSE) {
1500  $list[] = $groupRecord;
1501  }
1502  $GLOBALS['TYPO3_DB']->sql_free_result($res);
1503 
1504  return $list;
1505  }
1506 
1507  /*************************
1508  *
1509  * RENDERING UTILITIES
1510  *
1511  *************************/
1517  protected function getTemplateMarkers() {
1518  $markers = array(
1519  'CSH' => BackendUtility::wrapInHelp('_MOD_system_txschedulerM1', ''),
1520  'FUNC_MENU' => $this->getFunctionMenu(),
1521  'CONTENT' => $this->content,
1522  'TITLE' => $GLOBALS['LANG']->getLL('title')
1523  );
1524  return $markers;
1525  }
1526 
1532  protected function getFunctionMenu() {
1533  $functionMenu = BackendUtility::getFuncMenu(0, 'SET[function]', $this->MOD_SETTINGS['function'], $this->MOD_MENU['function']);
1534  return $functionMenu;
1535  }
1536 
1542  protected function getDocHeaderButtons() {
1543  $buttons = array(
1544  'addtask' => '',
1545  'close' => '',
1546  'save' => '',
1547  'saveclose' => '',
1548  'savenew' => '',
1549  'delete' => '',
1550  'reload' => '',
1551  'shortcut' => $this->getShortcutButton()
1552  );
1553  if (empty($this->CMD) || $this->CMD == 'list' || $this->CMD == 'delete' || $this->CMD == 'stop') {
1554  $buttons['reload'] = '<a href="' . $GLOBALS['MCONF']['_'] . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.reload', TRUE) . '">' . IconUtility::getSpriteIcon('actions-system-refresh') . '</a>';
1555  if ($this->MOD_SETTINGS['function'] === 'scheduler' && count(self::getRegisteredClasses())) {
1556  $link = $GLOBALS['MCONF']['_'] . '&CMD=add';
1557  $image = IconUtility::getSpriteIcon('actions-document-new', array('alt' => $GLOBALS['LANG']->getLL('action.add')));
1558  $buttons['addtask'] = '<a href="' . htmlspecialchars($link) . '" ' . 'title="' . $GLOBALS['LANG']->getLL('action.add') . '">' . $image . '</a>';
1559  }
1560  }
1561  if ($this->CMD === 'add' || $this->CMD === 'edit') {
1562  $buttons['close'] = '<a href="#" onclick="document.location=\'' . $GLOBALS['MCONF']['_'] . '\'" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xlf:cancel', TRUE) . '">' . IconUtility::getSpriteIcon('actions-document-close') . '</a>';
1563  $buttons['save'] = '<button style="padding: 0; margin: 0; cursor: pointer;" type="submit" name="CMD" value="save" class="c-inputButton" src="clear.gif" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xlf:save', TRUE) . '" />' . IconUtility::getSpriteIcon('actions-document-save') . '</button>';
1564  $buttons['saveclose'] = '<button style="padding: 0; margin: 0; cursor: pointer;" type="submit" name="CMD" value="saveclose" class="c-inputButton" src="clear.gif" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xlf:saveAndClose', TRUE) . '" />' . IconUtility::getSpriteIcon('actions-document-save-close') . '</button>';
1565  $buttons['savenew'] = '<button style="padding: 0; margin: 0; cursor: pointer;" type="submit" name="CMD" value="savenew" class="c-inputButton" src="clear.gif" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xlf:saveAndCreateNewDoc', TRUE) . '" />' . IconUtility::getSpriteIcon('actions-document-save-new') . '</button>';
1566  }
1567  if ($this->CMD === 'edit') {
1568  $buttons['delete'] = '<button style="padding: 0; margin: 0; cursor: pointer;" type="submit" name="CMD" value="delete" class="c-inputButton" src="clear.gif" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xlf:delete', TRUE) . '" />' . IconUtility::getSpriteIcon('actions-edit-delete') . '</button>';
1569  }
1570  return $buttons;
1571  }
1572 
1578  protected function getShortcutButton() {
1579  $result = '';
1580  if ($GLOBALS['BE_USER']->mayMakeShortcut()) {
1581  $result = $this->doc->makeShortcutIcon('', 'function', $this->MCONF['name']);
1582  }
1583  return $result;
1584  }
1585 
1591  protected function getBackendUserAuthentication() {
1592  return $GLOBALS['BE_USER'];
1593  }
1594 
1600  protected function getDatabaseConnection() {
1601  return $GLOBALS['TYPO3_DB'];
1602  }
1603 
1604 }
static mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys=TRUE, $includeEmptyValues=TRUE, $enableUnsetFeature=TRUE)
static getSaltingInstance($saltedHash='', $mode=TYPO3_MODE)
Definition: SaltFactory.php:83
$uid
Definition: server.php:36
static getUserObj($classRef, $checkPrefix='', $silent=FALSE)
$registry
Definition: ext_tables.php:46
static wrapInHelp($table, $field, $text='', array $overloadHelpText=array())
if($list_of_literals) if(!empty($literals)) if(!empty($literals)) $result
Analyse literals to prepend the N char to them if their contents aren&#39;t numeric.
static getSpriteIcon($iconName, array $options=array(), array $overlays=array())
if(!defined('TYPO3_MODE')) $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_pre_processing'][]
static deleteClause($table, $tableAlias='')