PageRenderTime 73ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/typo3/sysext/scheduler/mod1/index.php

https://github.com/foxsoft/typo3v4core
PHP | 1608 lines | 994 code | 180 blank | 434 comment | 168 complexity | dfa977c8396aa086f16d90b68af2b9e2 MD5 | raw file
Possible License(s): Apache-2.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /***************************************************************
  3. * Copyright notice
  4. *
  5. * (c) 2009-2010 Francois Suter <francois@typo3.org>
  6. * (c) 2005 Christian Jul Jensen <julle@typo3.org>
  7. * All rights reserved
  8. *
  9. * This script is part of the TYPO3 project. The TYPO3 project is
  10. * free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License as published by
  12. * the Free Software Foundation; either version 2 of the License, or
  13. * (at your option) any later version.
  14. *
  15. * The GNU General Public License can be found at
  16. * http://www.gnu.org/copyleft/gpl.html.
  17. *
  18. * This script is distributed in the hope that it will be useful,
  19. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. * GNU General Public License for more details.
  22. *
  23. * This copyright notice MUST APPEAR in all copies of the script!
  24. ***************************************************************/
  25. require_once (t3lib_extMgm::extPath('scheduler') . 'interfaces/interface.tx_scheduler_additionalfieldprovider.php');
  26. $LANG->includeLLFile('EXT:scheduler/mod1/locallang.xml');
  27. $BE_USER->modAccess($MCONF, 1); // This checks permissions and exits if the users has no permission for entry.
  28. /**
  29. * Module 'TYPO3 Scheduler administration module' for the 'scheduler' extension.
  30. *
  31. * @author Francois Suter <francois@typo3.org>
  32. * @author Christian Jul Jensen <julle@typo3.org>
  33. * @author Ingo Renner <ingo@typo3.org>
  34. * @package TYPO3
  35. * @subpackage tx_scheduler
  36. * @version $Id$
  37. */
  38. class tx_scheduler_Module extends t3lib_SCbase {
  39. /**
  40. * Back path to typo3 main dir
  41. *
  42. * @var string $backPath
  43. */
  44. public $backPath;
  45. /**
  46. * Array containing submitted data when editing or adding a task
  47. *
  48. * @var array $submittedData
  49. */
  50. protected $submittedData = array();
  51. /**
  52. * Array containing all messages issued by the application logic
  53. * Contains the error's severity and the message itself
  54. *
  55. * @var array $messages
  56. */
  57. protected $messages = array();
  58. /**
  59. * @var string Key of the CSH file
  60. */
  61. protected $cshKey;
  62. /**
  63. *
  64. * @var tx_scheduler Local scheduler instance
  65. */
  66. protected $scheduler;
  67. /**
  68. * Constructor
  69. *
  70. * @return void
  71. */
  72. public function __construct() {
  73. $this->backPath = $GLOBALS['BACK_PATH'];
  74. // Set key for CSH
  75. $this->cshKey = '_MOD_' . $GLOBALS['MCONF']['name'];
  76. }
  77. /**
  78. * Initializes the backend module
  79. *
  80. * @return void
  81. */
  82. public function init() {
  83. parent::init();
  84. // Initialize document
  85. $this->doc = t3lib_div::makeInstance('template');
  86. $this->doc->setModuleTemplate(t3lib_extMgm::extPath('scheduler') . 'mod1/mod_template.html');
  87. $this->doc->getPageRenderer()->addCssFile(t3lib_extMgm::extRelPath('scheduler') . 'res/tx_scheduler_be.css');
  88. $this->doc->backPath = $this->backPath;
  89. $this->doc->bodyTagId = 'typo3-mod-php';
  90. $this->doc->bodyTagAdditions = 'class="tx_scheduler_mod1"';
  91. // Create scheduler instance
  92. $this->scheduler = t3lib_div::makeInstance('tx_scheduler');
  93. }
  94. /**
  95. * Adds items to the ->MOD_MENU array. Used for the function menu selector.
  96. *
  97. * @return void
  98. */
  99. public function menuConfig() {
  100. $this->MOD_MENU = array(
  101. 'function' => array(
  102. 'scheduler' => $GLOBALS['LANG']->getLL('function.scheduler'),
  103. 'check' => $GLOBALS['LANG']->getLL('function.check'),
  104. 'info' => $GLOBALS['LANG']->getLL('function.info'),
  105. )
  106. );
  107. parent::menuConfig();
  108. }
  109. /**
  110. * Main function of the module. Write the content to $this->content
  111. *
  112. * @return void
  113. */
  114. public function main() {
  115. // Access check!
  116. // The page will show only if user has admin rights
  117. if ($GLOBALS['BE_USER']->user['admin']) {
  118. // Set the form
  119. $this->doc->form = '<form name="tx_scheduler_form" id="tx_scheduler_form" method="post" action="">';
  120. // JavaScript for main function menu
  121. $this->doc->JScode = '
  122. <script language="javascript" type="text/javascript">
  123. script_ended = 0;
  124. function jumpToUrl(URL) {
  125. document.location = URL;
  126. }
  127. </script>
  128. ';
  129. // Prepare main content
  130. $this->content = $this->doc->header(
  131. $GLOBALS['LANG']->getLL('function.' . $this->MOD_SETTINGS['function'])
  132. );
  133. $this->content .= $this->doc->spacer(5);
  134. $this->content .= $this->getModuleContent();
  135. } else {
  136. // If no access, only display the module's title
  137. $this->content = $this->doc->header($GLOBALS['LANG']->getLL('title'));
  138. $this->content .= $this->doc->spacer(5);
  139. }
  140. // Place content inside template
  141. $content = $this->doc->startPage($GLOBALS['LANG']->getLL('title'));
  142. $content .= $this->doc->moduleBody(
  143. array(),
  144. $this->getDocHeaderButtons(),
  145. $this->getTemplateMarkers()
  146. );
  147. $content .= $this->doc->endPage();
  148. // Replace content with templated content
  149. $this->content = $content;
  150. }
  151. /**
  152. * Generate the module's content
  153. *
  154. * @return string HTML of the module's main content
  155. */
  156. protected function getModuleContent() {
  157. $content = '';
  158. $sectionTitle = '';
  159. // Get submitted data
  160. $this->submittedData = t3lib_div::_GPmerged('tx_scheduler');
  161. // If a save command was submitted, handle saving now
  162. if ($this->CMD == 'save') {
  163. $previousCMD = t3lib_div::_GP('previousCMD');
  164. // First check the submitted data
  165. $result = $this->preprocessData();
  166. // If result is ok, proceed with saving
  167. if ($result) {
  168. $this->saveTask();
  169. // Unset command, so that default screen gets displayed
  170. unset($this->CMD);
  171. // Errors occurred
  172. // Go back to previous step
  173. } else {
  174. $this->CMD = $previousCMD;
  175. }
  176. }
  177. // Handle chosen action
  178. switch((string)$this->MOD_SETTINGS['function']) {
  179. case 'scheduler':
  180. // Scheduler's main screen
  181. $content .= $this->executeTasks();
  182. // Execute chosen action
  183. switch ($this->CMD) {
  184. case 'add':
  185. case 'edit':
  186. try {
  187. // Try adding or editing
  188. $content .= $this->editTask();
  189. $sectionTitle = $GLOBALS['LANG']->getLL('action.' . $this->CMD);
  190. } catch (Exception $e) {
  191. // An exception may happen when the task to
  192. // edit could not be found. In this case revert
  193. // to displaying the list of tasks
  194. // It can also happend when attempting to edit a running task
  195. $content .= $this->listTasks();
  196. }
  197. break;
  198. case 'delete':
  199. $this->deleteTask();
  200. $content .= $this->listTasks();
  201. break;
  202. case 'stop':
  203. $this->stopTask();
  204. $content .= $this->listTasks();
  205. break;
  206. case 'list':
  207. default:
  208. $content .= $this->listTasks();
  209. break;
  210. }
  211. break;
  212. case 'check':
  213. // Setup check screen
  214. // TODO: move check to the report module
  215. $content .= $this->displayCheckScreen();
  216. break;
  217. case 'info':
  218. // Information screen
  219. $content .= $this->displayInfoScreen();
  220. break;
  221. }
  222. // Wrap the content in a section
  223. return $this->doc->section($sectionTitle, '<div class="tx_scheduler_mod1">' . $content . '</div>', 0, 1);
  224. }
  225. /**
  226. * This method actually prints out the module's HTML content
  227. *
  228. * @return void
  229. */
  230. public function render() {
  231. echo $this->content;
  232. }
  233. /**
  234. * This method checks the status of the '_cli_scheduler' user
  235. * It will differentiate between a non-existing user and an existing,
  236. * but disabled user (as per enable fields)
  237. *
  238. * @return integer -1 if user doesn't exist
  239. * 0 if user exists, but is disabled
  240. * 1 if user exists and is not disabled
  241. */
  242. protected function checkSchedulerUser() {
  243. $schedulerUserStatus = -1;
  244. // Assemble base WHERE clause
  245. $where = 'username = \'_cli_scheduler\' AND admin = 0' . t3lib_BEfunc::deleteClause('be_users');
  246. // Check if user exists at all
  247. $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
  248. '1',
  249. 'be_users',
  250. $where
  251. );
  252. if ($GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
  253. $schedulerUserStatus = 0;
  254. // Check if user exists and is enabled
  255. $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
  256. '1',
  257. 'be_users',
  258. $where . t3lib_BEfunc::BEenableFields('be_users')
  259. );
  260. if ($GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
  261. $schedulerUserStatus = 1;
  262. }
  263. }
  264. $GLOBALS['TYPO3_DB']->sql_free_result($res);
  265. return $schedulerUserStatus;
  266. }
  267. /**
  268. * This method creates the "cli_scheduler" BE user if it doesn't exist
  269. *
  270. * @return void
  271. */
  272. protected function createSchedulerUser() {
  273. // Check _cli_scheduler user status
  274. $checkUser = $this->checkSchedulerUser();
  275. // Prepare default message
  276. $message = $GLOBALS['LANG']->getLL('msg.userExists');
  277. $severity = t3lib_FlashMessage::WARNING;
  278. // If the user does not exist, try creating it
  279. if ($checkUser == -1) {
  280. // Prepare necessary data for _cli_scheduler user creation
  281. $password = md5(uniqid('scheduler', true));
  282. $data = array('be_users' => array('NEW' => array('username' => '_cli_scheduler', 'password' => $password, 'pid' => 0)));
  283. /**
  284. * Create an instance of TCEmain and execute user creation
  285. *
  286. * @var t3lib_TCEmain
  287. */
  288. $tcemain = t3lib_div::makeInstance('t3lib_TCEmain');
  289. $tcemain->stripslashes_values = 0;
  290. $tcemain->start($data, array());
  291. $tcemain->process_datamap();
  292. // Check if a new uid was indeed generated (i.e. a new record was created)
  293. // (counting TCEmain errors doesn't work as some failures don't report errors)
  294. $numberOfNewIDs = count($tcemain->substNEWwithIDs);
  295. if ($numberOfNewIDs == 1) {
  296. $message = $GLOBALS['LANG']->getLL('msg.userCreated');
  297. $severity = t3lib_FlashMessage::OK;
  298. } else {
  299. $message = $GLOBALS['LANG']->getLL('msg.userNotCreated');
  300. $severity = t3lib_FlashMessage::ERROR;
  301. }
  302. }
  303. $this->addMessage($message, $severity);
  304. }
  305. /**
  306. * This method displays the result of a number of checks
  307. * on whether the Scheduler is ready to run or running properly
  308. *
  309. * @return string further information
  310. */
  311. protected function displayCheckScreen() {
  312. $message = '';
  313. $severity = t3lib_FlashMessage::OK;
  314. // First, check if cli_sceduler user creation was requested
  315. if ($this->CMD == 'user') {
  316. $this->createSchedulerUser();
  317. }
  318. // Start generating the content
  319. $content = $GLOBALS['LANG']->getLL('msg.schedulerSetupCheck');
  320. // Display information about last automated run, as stored in the system registry
  321. /**
  322. * @var t3lib_Registry
  323. */
  324. $registry = t3lib_div::makeInstance('t3lib_Registry');
  325. $lastRun = $registry->get('tx_scheduler', 'lastRun');
  326. if (!is_array($lastRun)) {
  327. $message = $GLOBALS['LANG']->getLL('msg.noLastRun');
  328. $severity = t3lib_FlashMessage::WARNING;
  329. } else {
  330. if (empty($lastRun['end']) || empty($lastRun['start']) || empty($lastRun['type'])) {
  331. $message = $GLOBALS['LANG']->getLL('msg.incompleteLastRun');
  332. $severity = t3lib_FlashMessage::WARNING;
  333. } else {
  334. $startDate = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'], $lastRun['start']);
  335. $startTime = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'], $lastRun['start']);
  336. $endDate = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'], $lastRun['end']);
  337. $endTime = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'], $lastRun['end']);
  338. $label = 'automatically';
  339. if ($lastRun['type'] == 'manual') {
  340. $label = 'manually';
  341. }
  342. $type = $GLOBALS['LANG']->getLL('label.' . $label);
  343. $message = sprintf($GLOBALS['LANG']->getLL('msg.lastRun'), $type, $startDate, $startTime, $endDate, $endTime);
  344. $severity = t3lib_FlashMessage::INFO;
  345. }
  346. }
  347. $flashMessage = t3lib_div::makeInstance(
  348. 't3lib_FlashMessage',
  349. $message,
  350. '',
  351. $severity
  352. );
  353. $content .= '<div class="info-block">';
  354. $content .= '<h3>' . $GLOBALS['LANG']->getLL('hdg.lastRun') . '</h3>';
  355. $content .= $flashMessage->render();
  356. $content .= '</div>';
  357. // Check CLI user
  358. $content .= '<div class="info-block">';
  359. $content .= '<h3>' . $GLOBALS['LANG']->getLL('hdg.schedulerUser') . '</h3>';
  360. $content .= '<p>' . $GLOBALS['LANG']->getLL('msg.schedulerUser') . '</p>';
  361. $checkUser = $this->checkSchedulerUser();
  362. if ($checkUser == -1) {
  363. $link = $GLOBALS['MCONF']['_'] . '&SET[function]=check&CMD=user';
  364. $message = sprintf($GLOBALS['LANG']->getLL('msg.schedulerUserMissing'), htmlspecialchars($link));
  365. $severity = t3lib_FlashMessage::ERROR;
  366. } elseif ($checkUser == 0) {
  367. $message = $GLOBALS['LANG']->getLL('msg.schedulerUserFoundButDisabled');
  368. $severity = t3lib_FlashMessage::WARNING;
  369. } else {
  370. $message = $GLOBALS['LANG']->getLL('msg.schedulerUserFound');
  371. $severity = t3lib_FlashMessage::OK;
  372. }
  373. $flashMessage = t3lib_div::makeInstance(
  374. 't3lib_FlashMessage',
  375. $message,
  376. '',
  377. $severity
  378. );
  379. $content .= $flashMessage->render() . '</div>';
  380. // Check if CLI script is executable or not
  381. $script = PATH_typo3 . 'cli_dispatch.phpsh';
  382. $isExecutable = FALSE;
  383. // Skip this check if running Windows, as rights do not work the same way on this platform
  384. // (i.e. the script will always appear as *not* executable)
  385. if (TYPO3_OS === 'WIN') {
  386. $isExecutable = TRUE;
  387. } else {
  388. $isExecutable = is_executable($script);
  389. }
  390. $content .= '<div class="info-block">';
  391. $content .= '<h3>' . $GLOBALS['LANG']->getLL('hdg.cliScript') . '</h3>';
  392. $content .= '<p>' . sprintf($GLOBALS['LANG']->getLL('msg.cliScript'), $script) . '</p>';
  393. if ($isExecutable) {
  394. $message = $GLOBALS['LANG']->getLL('msg.cliScriptExecutable');
  395. $severity = t3lib_FlashMessage::OK;
  396. } else {
  397. $message = $GLOBALS['LANG']->getLL('msg.cliScriptNotExecutable');
  398. $severity = t3lib_FlashMessage::ERROR;
  399. }
  400. $flashMessage = t3lib_div::makeInstance(
  401. 't3lib_FlashMessage',
  402. $message,
  403. '',
  404. $severity
  405. );
  406. $content .= $flashMessage->render() . '</div>';
  407. return $content;
  408. }
  409. /**
  410. * This method gathers information about all available task classes and displays it
  411. *
  412. * @return string HTML content to display
  413. */
  414. protected function displayInfoScreen() {
  415. $content = '';
  416. $registeredClasses = self::getRegisteredClasses();
  417. // No classes available, display information message
  418. if (count($registeredClasses) == 0) {
  419. /** @var t3lib_FlashMessage $flashMessage */
  420. $flashMessage = t3lib_div::makeInstance('t3lib_FlashMessage',
  421. $GLOBALS['LANG']->getLL('msg.noTasksDefined'),
  422. '',
  423. t3lib_FlashMessage::INFO
  424. );
  425. $content .= $flashMessage->render();
  426. // Display the list of all available classes
  427. } else {
  428. // Initialise table layout
  429. $tableLayout = array (
  430. 'table' => array ('<table border="0" cellspacing="1" cellpadding="2" style="width:auto;">', '</table>'),
  431. '0' => array (
  432. 'tr' => array('<tr class="bgColor2" valign="top">', '</tr>'),
  433. 'defCol' => array('<td class="cell">', '</td>')
  434. ),
  435. 'defRow' => array (
  436. 'tr' => array('<tr class="bgColor3-20">', '</tr>'),
  437. 'defCol' => array('<td class="cell">', '</td>')
  438. )
  439. );
  440. $table = array();
  441. $tr = 0;
  442. // Header row
  443. $table[$tr][] = $GLOBALS['LANG']->getLL('label.name');
  444. $table[$tr][] = $GLOBALS['LANG']->getLL('label.extension');
  445. $table[$tr][] = $GLOBALS['LANG']->getLL('label.description');
  446. $table[$tr][] = '';
  447. $tr++;
  448. // Display information about each service
  449. foreach ($registeredClasses as $class => $classInfo) {
  450. $table[$tr][] = $classInfo['title'];
  451. $table[$tr][] = $classInfo['extension'];
  452. $table[$tr][] = $classInfo['description'];
  453. $link = $GLOBALS['MCONF']['_'] . '&SET[function]=list&CMD=add&tx_scheduler[class]=' . $class;
  454. $table[$tr][] = '<a href="' . htmlspecialchars($link) . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:new', TRUE) . '">' . t3lib_iconWorks::getSpriteIcon('actions-document-new') . '</a>';
  455. $tr++;
  456. }
  457. // Render the table and return it
  458. $content = '<div>' . $GLOBALS['LANG']->getLL('msg.infoScreenIntro') . '</div>';
  459. $content .= $this->doc->spacer(5);
  460. $content .= $this->doc->table($table, $tableLayout);
  461. }
  462. return $content;
  463. }
  464. /**
  465. * Display the current server's time along with a help text about server time
  466. * usage in the Scheduler
  467. *
  468. * @return string HTML to display
  469. */
  470. protected function displayServerTime() {
  471. // Get the current time, formatted
  472. $dateFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] . ' ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'] . ' T (e';
  473. $now = date($dateFormat) . ', GMT ' . date('P') . ')';
  474. // Display the help text
  475. $serverTime = '<h4>' . $GLOBALS['LANG']->getLL('label.serverTime') . '</h4>';
  476. $serverTime .= '<p>' . $GLOBALS['LANG']->getLL('msg.serverTimeHelp') . '</p>';
  477. $serverTime .= '<p>' . sprintf($GLOBALS['LANG']->getLL('msg.serverTime'), $now) . '</p>';
  478. return $serverTime;
  479. }
  480. /**
  481. * Delete a task from the execution queue
  482. *
  483. * @return void
  484. */
  485. protected function deleteTask() {
  486. try {
  487. // Try to fetch the task and delete it
  488. /**
  489. * @var tx_scheduler_Task
  490. */
  491. $task = $this->scheduler->fetchTask($this->submittedData['uid']);
  492. // If the task is currently running, it may not be deleted
  493. if ($task->isExecutionRunning()) {
  494. $this->addMessage($GLOBALS['LANG']->getLL('msg.maynotDeleteRunningTask'), t3lib_FlashMessage::ERROR);
  495. } else {
  496. if ($this->scheduler->removeTask($task)) {
  497. $this->addMessage($GLOBALS['LANG']->getLL('msg.deleteSuccess'));
  498. } else {
  499. $this->addMessage($GLOBALS['LANG']->getLL('msg.deleteError'), t3lib_FlashMessage::ERROR);
  500. }
  501. }
  502. } catch (UnexpectedValueException $e) {
  503. // The task could not be unserialized properly, simply delete the database record
  504. $result = $GLOBALS['TYPO3_DB']->exec_DELETEquery('tx_scheduler_task', 'uid = ' . intval($this->submittedData['uid']));
  505. if ($result) {
  506. $this->addMessage($GLOBALS['LANG']->getLL('msg.deleteSuccess'));
  507. } else {
  508. $this->addMessage($GLOBALS['LANG']->getLL('msg.deleteError'), t3lib_FlashMessage::ERROR);
  509. }
  510. } catch (OutOfBoundsException $e) {
  511. // The task was not found, for some reason
  512. $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.taskNotFound'), $this->submittedData['uid']), t3lib_FlashMessage::ERROR);
  513. }
  514. }
  515. /**
  516. * Clears the registered running executions from the task
  517. * Note that this doesn't actually stop the running script. It just unmarks
  518. * all executions.
  519. * TODO: find a way to really kill the running task
  520. *
  521. * @return void
  522. */
  523. protected function stopTask() {
  524. try {
  525. // Try to fetch the task and stop it
  526. /**
  527. * @var tx_scheduler_Task
  528. */
  529. $task = $this->scheduler->fetchTask($this->submittedData['uid']);
  530. if ($task->isExecutionRunning()) {
  531. // If the task is indeed currently running, clear marked executions
  532. $result = $task->unmarkAllExecutions();
  533. if ($result) {
  534. $this->addMessage($GLOBALS['LANG']->getLL('msg.stopSuccess'));
  535. } else {
  536. $this->addMessage($GLOBALS['LANG']->getLL('msg.stopError'), t3lib_FlashMessage::ERROR);
  537. }
  538. } else {
  539. // The task is not running, nothing to unmark
  540. $this->addMessage($GLOBALS['LANG']->getLL('msg.maynotStopNonRunningTask'), t3lib_FlashMessage::WARNING);
  541. }
  542. } catch (Exception $e) {
  543. // The task was not found, for some reason
  544. $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.taskNotFound'), $this->submittedData['uid']), t3lib_FlashMessage::ERROR);
  545. }
  546. }
  547. /**
  548. * Return a form to add a new task or edit an existing one
  549. *
  550. * @return string HTML form to add or edit a task
  551. */
  552. protected function editTask() {
  553. $registeredClasses = self::getRegisteredClasses();
  554. $content = '';
  555. $taskInfo = array();
  556. $task = NULL;
  557. $process = 'edit';
  558. if ($this->submittedData['uid'] > 0) {
  559. // If editing, retrieve data for existing task
  560. try {
  561. $taskRecord = $this->scheduler->fetchTaskRecord($this->submittedData['uid']);
  562. // If there's a registered execution, the task should not be edited
  563. if (!empty($taskRecord['serialized_executions'])) {
  564. $this->addMessage($GLOBALS['LANG']->getLL('msg.maynotEditRunningTask'), t3lib_FlashMessage::ERROR);
  565. throw new LogicException('Runnings tasks cannot not be edited', 1251232849);
  566. }
  567. // Get the task object
  568. $task = unserialize($taskRecord['serialized_task_object']);
  569. // Set some task information
  570. $class = $taskRecord['classname'];
  571. $taskInfo['disable'] = $taskRecord['disable'];
  572. // Check that the task object is valid
  573. if ($this->scheduler->isValidTaskObject($task)) {
  574. // The task object is valid, process with fetching current data
  575. $taskInfo['class'] = $class;
  576. // Get execution information
  577. $taskInfo['start'] = $task->getExecution()->getStart();
  578. $taskInfo['end'] = $task->getExecution()->getEnd();
  579. $taskInfo['interval'] = $task->getExecution()->getInterval();
  580. $taskInfo['croncmd'] = $task->getExecution()->getCronCmd();
  581. $taskInfo['multiple'] = $task->getExecution()->getMultiple();
  582. if (!empty($taskInfo['interval']) || !empty($taskInfo['croncmd'])) {
  583. // Guess task type from the existing information
  584. // If an interval or a cron command is defined, it's a recurring task
  585. // FIXME remove magic numbers for the type, use class constants instead
  586. $taskInfo['type'] = 2;
  587. $taskInfo['frequency'] = (empty($taskInfo['interval'])) ? $taskInfo['croncmd'] : $taskInfo['interval'];
  588. } else {
  589. // It's not a recurring task
  590. // Make sure interval and cron command are both empty
  591. $taskInfo['type'] = 1;
  592. $taskInfo['frequency'] = '';
  593. $taskInfo['end'] = 0;
  594. }
  595. } else {
  596. // The task object is not valid
  597. // Issue error message
  598. $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.invalidTaskClassEdit'), $class), t3lib_FlashMessage::ERROR);
  599. // Initialize empty values
  600. $taskInfo['start'] = 0;
  601. $taskInfo['end'] = 0;
  602. $taskInfo['frequency'] = '';
  603. $taskInfo['multiple'] = false;
  604. $taskInfo['type'] = 1;
  605. }
  606. } catch (OutOfBoundsException $e) {
  607. // Add a message and continue throwing the exception
  608. $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.taskNotFound'), $this->submittedData['uid']), t3lib_FlashMessage::ERROR);
  609. throw $e;
  610. }
  611. } else {
  612. // If adding a new object, set some default values
  613. $taskInfo['class'] = key($registeredClasses);
  614. $taskInfo['type'] = 2;
  615. $taskInfo['start'] = $GLOBALS['EXEC_TIME'];
  616. $taskInfo['end'] = '';
  617. $taskInfo['frequency'] = '';
  618. $taskInfo['multiple'] = 0;
  619. $process = 'add';
  620. }
  621. if (count($this->submittedData) > 0) {
  622. // If some data was already submitted, use it to override
  623. // existing data
  624. $taskInfo = t3lib_div::array_merge_recursive_overrule($taskInfo, $this->submittedData);
  625. }
  626. // Get the extra fields to display for each task that needs some
  627. $allAdditionalFields = array();
  628. if ($process == 'add') {
  629. foreach ($registeredClasses as $class => $registrationInfo) {
  630. if (!empty($registrationInfo['provider'])) {
  631. $providerObject = t3lib_div::getUserObj($registrationInfo['provider']);
  632. if ($providerObject instanceof tx_scheduler_AdditionalFieldProvider) {
  633. $additionalFields = $providerObject->getAdditionalFields($taskInfo, NULL, $this);
  634. $allAdditionalFields = array_merge($allAdditionalFields, array($class => $additionalFields));
  635. }
  636. }
  637. }
  638. // In case of edit, get only the extra fields for the current task class
  639. } else {
  640. if (!empty($registeredClasses[$taskInfo['class']]['provider'])) {
  641. $providerObject = t3lib_div::getUserObj($registeredClasses[$taskInfo['class']]['provider']);
  642. if ($providerObject instanceof tx_scheduler_AdditionalFieldProvider) {
  643. $allAdditionalFields[$taskInfo['class']] = $providerObject->getAdditionalFields($taskInfo, $task, $this);
  644. }
  645. }
  646. }
  647. // Load necessary JavaScript
  648. /** @var $pageRenderer t3lib_PageRenderer */
  649. $pageRenderer = $this->doc->getPageRenderer();
  650. $pageRenderer->loadExtJS();
  651. $pageRenderer->addJsFile(t3lib_extMgm::extRelPath('scheduler') . 'res/tx_scheduler_be.js');
  652. $pageRenderer->addJsFile($this->backPath . '../t3lib/js/extjs/tceforms.js');
  653. // Define settings for Date Picker
  654. $typo3Settings = array(
  655. 'datePickerUSmode' => $GLOBALS['TYPO3_CONF_VARS']['SYS']['USdateFormat'] ? 1 : 0,
  656. 'dateFormat' => array('j-n-Y', 'G:i j-n-Y'),
  657. 'dateFormatUS' => array('n-j-Y', 'G:i n-j-Y'),
  658. );
  659. $pageRenderer->addInlineSettingArray('', $typo3Settings);
  660. // Define table layout for add/edit form
  661. $tableLayout = array (
  662. 'table' => array ('<table border="0" cellspacing="1" cellpadding="0" id="edit_form">', '</table>'),
  663. );
  664. // Define a style for hiding
  665. // Some fields will be hidden when the task is not recurring
  666. $style = '';
  667. if ($taskInfo['type'] == 1) {
  668. $style = ' style="display: none"';
  669. }
  670. // Start rendering the add/edit form
  671. $content .= '<input type="hidden" name="tx_scheduler[uid]" value="' . $this->submittedData['uid'] . '" />';
  672. $content .= '<input type="hidden" name="previousCMD" value="' . $this->CMD . '" />';
  673. $content .= '<input type="hidden" name="CMD" value="save" />';
  674. $table = array();
  675. $tr = 0;
  676. $defaultCell = array('<td class="bgColor5 cell">', '</td>');
  677. // Disable checkbox
  678. $table[$tr][] = t3lib_BEfunc::cshItem($this->cshKey, 'task_disable', $this->backPath, '|', false, 'margin-bottom:0px;');
  679. $table[$tr][] = '<label for="task_disable">' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:disable') . '</label>';
  680. $table[$tr][] =
  681. '<input type="hidden" name="tx_scheduler[disable]" value="0" />
  682. <input type="checkbox" name="tx_scheduler[disable]" value="1" id="task_disable"' . ($taskInfo['disable'] == 1 ? ' checked="checked"' : '') . ' />';
  683. $tableLayout[$tr] = array (
  684. 'tr' => array('<tr id="task_disable_row">', '</tr>'),
  685. 'defCol' => $defaultCell
  686. );
  687. $tr++;
  688. // Task class selector
  689. $table[$tr][] = t3lib_BEfunc::cshItem($this->cshKey, 'task_class', $this->backPath, '|', false, 'margin-bottom:0px;');
  690. $table[$tr][] = '<label for="task_class">'.$GLOBALS['LANG']->getLL('label.class').'</label>';
  691. // On editing, don't allow changing of the task class, unless it was not valid
  692. if ($this->submittedData['uid'] > 0 && !empty($taskInfo['class'])) {
  693. $cell = $registeredClasses[$taskInfo['class']]['title'] . ' (' . $registeredClasses[$taskInfo['class']]['extension'] . ')';
  694. $cell .= '<input type="hidden" name="tx_scheduler[class]" id="task_class" value="' . $taskInfo['class'] . '" />';
  695. } else {
  696. $cell = '<select name="tx_scheduler[class]" id="task_class" class="wide" onchange="actOnChangedTaskClass(this)">';
  697. // Loop on all registered classes to display a selector
  698. foreach ($registeredClasses as $class => $classInfo) {
  699. $selected = ($class == $taskInfo['class']) ? ' selected="selected"' : '';
  700. $cell .= '<option value="' . $class . '"' . $selected . '>' . $classInfo['title'] . ' (' . $classInfo['extension'] . ')' . '</option>';
  701. }
  702. $cell .= '</select>';
  703. }
  704. $table[$tr][] = $cell;
  705. // Make sure each row has a unique id, for manipulation with JS
  706. $tableLayout[$tr] = array (
  707. 'tr' => array('<tr id="task_class_row">', '</tr>'),
  708. 'defCol' => $defaultCell
  709. );
  710. $tr++;
  711. // Task type selector
  712. $table[$tr][] = t3lib_BEfunc::cshItem($this->cshKey, 'task_type', $this->backPath, '|', false, 'margin-bottom:0px;');
  713. $table[$tr][] = '<label for="task_type">' . $GLOBALS['LANG']->getLL('label.type') . '</label>';
  714. $table[$tr][] =
  715. '<select name="tx_scheduler[type]" id="task_type" onchange="actOnChangedTaskType(this)">' .
  716. '<option value="1"' . ($taskInfo['type'] == 1 ? ' selected="selected"' : '') . '>' . $GLOBALS['LANG']->getLL('label.type.single') . '</option>' .
  717. '<option value="2"' . ($taskInfo['type'] == 2 ? ' selected="selected"' : '') . '>' . $GLOBALS['LANG']->getLL('label.type.recurring') . '</option>' .
  718. '</select>';
  719. $tableLayout[$tr] = array (
  720. 'tr' => array('<tr id="task_type_row">', '</tr>'),
  721. 'defCol' => $defaultCell
  722. );
  723. $tr++;
  724. // Start date/time field
  725. // NOTE: datetime fields need a special id naming scheme
  726. $table[$tr][] = t3lib_BEfunc::cshItem($this->cshKey, 'task_start', $this->backPath, '|', false, 'margin-bottom:0px;');
  727. $table[$tr][] = '<label for="tceforms-datetimefield-task_start">' . $GLOBALS['LANG']->getLL('label.start') . '</label>';
  728. $table[$tr][] = '<input name="tx_scheduler[start]" type="text" id="tceforms-datetimefield-task_start" value="' . strftime('%H:%M %d-%m-%Y', $taskInfo['start']) . '" />' .
  729. t3lib_iconWorks::getSpriteIcon(
  730. 'actions-edit-pick-date',
  731. array(
  732. 'style' => 'cursor:pointer;',
  733. 'id' => 'picker-tceforms-datetimefield-task_start'
  734. ));
  735. $tableLayout[$tr] = array (
  736. 'tr' => array('<tr id="task_start_row">', '</tr>'),
  737. 'defCol' => $defaultCell
  738. );
  739. $tr++;
  740. // End date/time field
  741. // NOTE: datetime fields need a special id naming scheme
  742. $table[$tr][] = t3lib_BEfunc::cshItem($this->cshKey, 'task_end', $this->backPath, '|', false, 'margin-bottom:0px;');
  743. $table[$tr][] = '<label for="tceforms-datetimefield-task_end">' . $GLOBALS['LANG']->getLL('label.end') . '</label>';
  744. $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'])) . '" />' .
  745. t3lib_iconWorks::getSpriteIcon(
  746. 'actions-edit-pick-date',
  747. array(
  748. 'style' => 'cursor:pointer;',
  749. 'id' => 'picker-tceforms-datetimefield-task_end'
  750. ));
  751. $tableLayout[$tr] = array (
  752. 'tr' => array('<tr id="task_end_row"' . $style . '>', '</tr>'),
  753. 'defCol' => $defaultCell
  754. );
  755. $tr++;
  756. // Frequency input field
  757. $table[$tr][] = t3lib_BEfunc::cshItem($this->cshKey, 'task_frequency', $this->backPath, '|', false, 'margin-bottom:0px;');
  758. $table[$tr][] = '<label for="task_frequency">' . $GLOBALS['LANG']->getLL('label.frequency.long') . '</label>';
  759. $cell = '<input type="text" name="tx_scheduler[frequency]" id="task_frequency" value="' . $taskInfo['frequency'] . '" />';
  760. $table[$tr][] = $cell;
  761. $tableLayout[$tr] = array (
  762. 'tr' => array('<tr id="task_frequency_row"' . $style . '>', '</tr>'),
  763. 'defCol' => $defaultCell
  764. );
  765. $tr++;
  766. // Multiple execution selector
  767. $table[$tr][] = t3lib_BEfunc::cshItem($this->cshKey, 'task_multiple', $this->backPath, '|', false, 'margin-bottom:0px;');
  768. $table[$tr][] = '<label for="task_multiple">' . $GLOBALS['LANG']->getLL('label.parallel.long') . '</label>';
  769. $table[$tr][] =
  770. '<input type="hidden" name="tx_scheduler[multiple]" value="0" />
  771. <input type="checkbox" name="tx_scheduler[multiple]" value="1" id="task_multiple"' . ($taskInfo['multiple'] == 1 ? ' checked="checked"' : '') . ' />';
  772. $tableLayout[$tr] = array (
  773. 'tr' => array('<tr id="task_multiple_row"' . $style . '>', '</tr>'),
  774. 'defCol' => $defaultCell
  775. );
  776. $tr++;
  777. // Display additional fields
  778. foreach ($allAdditionalFields as $class => $fields) {
  779. if ($class == $taskInfo['class']) {
  780. $additionalFieldsStyle = '';
  781. } else {
  782. $additionalFieldsStyle = ' style="display: none"';
  783. }
  784. // Add each field to the display, if there are indeed any
  785. if (isset($fields) && is_array($fields)) {
  786. foreach ($fields as $fieldID => $fieldInfo) {
  787. $table[$tr][] = t3lib_BEfunc::cshItem($fieldInfo['cshKey'], $fieldInfo['cshLabel'], $this->backPath, '|', false, 'margin-bottom:0px;');
  788. $table[$tr][] = '<label for="' . $fieldID . '">' . $GLOBALS['LANG']->sL($fieldInfo['label']) . '</label>';
  789. $table[$tr][] = $fieldInfo['code'];
  790. $tableLayout[$tr] = array (
  791. 'tr' => array('<tr id="' . $fieldID . '_row"' . $additionalFieldsStyle .' class="extraFields extra_fields_' . $class . '">', '</tr>'),
  792. 'defCol' => $defaultCell
  793. );
  794. $tr++;
  795. }
  796. }
  797. }
  798. // Render the add/edit task form
  799. $content .= $this->doc->table($table, $tableLayout);
  800. $content .= '<input type="submit" name="save" class="button" value="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:save', TRUE) . '" /> '
  801. . '<input type="button" name="cancel" class="button" value="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:cancel', TRUE) . '" onclick="document.location=\'' . $GLOBALS['MCONF']['_'] . '\'" />';
  802. // Display information about server time usage
  803. $content .= $this->displayServerTime();
  804. return $content;
  805. }
  806. /**
  807. * Execute all selected tasks
  808. *
  809. * @return void
  810. */
  811. protected function executeTasks() {
  812. // Continue if some elements have been chosen for execution
  813. if (isset($this->submittedData['execute']) && count($this->submittedData['execute']) > 0) {
  814. // Get list of registered classes
  815. $registeredClasses = self::getRegisteredClasses();
  816. // Loop on all selected tasks
  817. foreach ($this->submittedData['execute'] as $uid) {
  818. try {
  819. // Try fetching the task
  820. $task = $this->scheduler->fetchTask($uid);
  821. $class = get_class($task);
  822. $name = $registeredClasses[$class]['title']. ' (' . $registeredClasses[$class]['extension'] . ')';
  823. // Now try to execute it and report on outcome
  824. try {
  825. $result = $this->scheduler->executeTask($task);
  826. if ($result) {
  827. $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.executed'), $name));
  828. } else {
  829. $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.notExecuted'), $name), t3lib_FlashMessage::ERROR);
  830. }
  831. }
  832. catch (Exception $e) {
  833. // An exception was thrown, display its message as an error
  834. $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.executionFailed'), $name, $e->getMessage()), t3lib_FlashMessage::ERROR);
  835. }
  836. }
  837. // The task was not found, for some reason
  838. catch (OutOfBoundsException $e) {
  839. $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.taskNotFound'), $uid), t3lib_FlashMessage::ERROR);
  840. }
  841. // The task object was not valid
  842. catch (UnexpectedValueException $e) {
  843. $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.executionFailed'), $name, $e->getMessage()), t3lib_FlashMessage::ERROR);
  844. }
  845. }
  846. // Record the run in the system registry
  847. $this->scheduler->recordLastRun('manual');
  848. // Make sure to switch to list view after execution
  849. $this->CMD = 'list';
  850. }
  851. }
  852. /**
  853. * Assemble display of list of scheduled tasks
  854. *
  855. * @return string table of waiting schedulings
  856. */
  857. protected function listTasks() {
  858. // Define display format for dates
  859. $dateFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] . ' ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'];
  860. $content = '';
  861. // Get list of registered classes
  862. $registeredClasses = self::getRegisteredClasses();
  863. // Get all registered tasks
  864. $query = array(
  865. 'SELECT' => '*',
  866. 'FROM' => 'tx_scheduler_task',
  867. 'ORDERBY' => 'nextexecution'
  868. );
  869. $res = $GLOBALS['TYPO3_DB']->exec_SELECT_queryArray($query);
  870. $numRows = $GLOBALS['TYPO3_DB']->sql_num_rows($res);
  871. // No tasks defined, display information message
  872. if ($numRows == 0) {
  873. /** @var t3lib_FlashMessage $flashMessage */
  874. $flashMessage = t3lib_div::makeInstance('t3lib_FlashMessage',
  875. $GLOBALS['LANG']->getLL('msg.noTasks'),
  876. '',
  877. t3lib_FlashMessage::INFO
  878. );
  879. $content .= $flashMessage->render();
  880. } else {
  881. // Load ExtJS framework and specific JS library
  882. /** @var $pageRenderer t3lib_PageRenderer */
  883. $pageRenderer = $this->doc->getPageRenderer();
  884. $pageRenderer->loadExtJS();
  885. $pageRenderer->addJsFile(t3lib_extMgm::extRelPath('scheduler') . 'res/tx_scheduler_be.js');
  886. // Initialise table layout
  887. $tableLayout = array(
  888. 'table' => array(
  889. '<table border="0" cellspacing="1" cellpadding="2" class="tx_scheduler_task_list">', '</table>'
  890. ),
  891. '0' => array(
  892. 'tr' => array('<tr class="bgColor2">', '</tr>'),
  893. 'defCol' => array('<td class="cell">', '</td>'),
  894. '1' => array('<td style="width: 36px;" class="cell">', '</td>')
  895. ),
  896. 'defRow' => array(
  897. 'tr' => array('<tr class="bgColor3-20">', '</tr>'),
  898. 'defCol' => array('<td class="cell">', '</td>'),
  899. '1' => array('<td class="cell right">', '</td>'),
  900. '2' => array('<td class="cell right">', '</td>'),
  901. )
  902. );
  903. $disabledTaskRow = array (
  904. 'tr' => array('<tr class="bgColor3-20 disabled">', '</tr>'),
  905. 'defCol' => array('<td class="cell">', '</td>'),
  906. '1' => array('<td class="cell right">', '</td>'),
  907. '2' => array('<td class="cell right">', '</td>'),
  908. );
  909. $rowWithSpan = array (
  910. 'tr' => array('<tr class="bgColor3-20">', '</tr>'),
  911. 'defCol' => array('<td class="cell">', '</td>'),
  912. '1' => array('<td class="cell right">', '</td>'),
  913. '2' => array('<td class="cell right">', '</td>'),
  914. '3' => array('<td class="cell" colspan="6">', '</td>'),
  915. );
  916. $table = array();
  917. $tr = 0;
  918. // Header row
  919. $table[$tr][] = '<a href="#" onclick="toggleCheckboxes();" title="' . $GLOBALS['LANG']->getLL('label.checkAll', TRUE) . '">' .
  920. t3lib_iconWorks::getSpriteIcon('actions-document-select') .
  921. '</a>';
  922. $table[$tr][] = '&nbsp;';
  923. $table[$tr][] = $GLOBALS['LANG']->getLL('label.id');
  924. $table[$tr][] = $GLOBALS['LANG']->getLL('task');
  925. $table[$tr][] = $GLOBALS['LANG']->getLL('label.type');
  926. $table[$tr][] = $GLOBALS['LANG']->getLL('label.frequency');
  927. $table[$tr][] = $GLOBALS['LANG']->getLL('label.parallel');
  928. $table[$tr][] = $GLOBALS['LANG']->getLL('label.lastExecution');
  929. $table[$tr][] = $GLOBALS['LANG']->getLL('label.nextExecution');
  930. $tr++;
  931. // Loop on all tasks
  932. while (($schedulerRecord = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))) {
  933. // Define action icons
  934. $editAction = '<a href="' . $GLOBALS['MCONF']['_'] . '&CMD=edit&tx_scheduler[uid]=' . $schedulerRecord['uid'] . '" title="'.$GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:edit', TRUE) . '">' . t3lib_iconWorks::getSpriteIcon('actions-document-open') . '</a>';
  935. $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', TRUE) . '">' . t3lib_iconWorks::getSpriteIcon('actions-edit-delete') . '</a>';
  936. $stopAction = '<a href="' . $GLOBALS['MCONF']['_'] . '&CMD=stop&tx_scheduler[uid]=' . $schedulerRecord['uid'] . '" onclick="return confirm(\'' . $GLOBALS['LANG']->getLL('msg.stop') . '\');" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:stop', TRUE) . '"><img ' . t3lib_iconWorks::skinImg($this->backPath, t3lib_extMgm::extRelPath('scheduler') . '/res/gfx/stop.png') . ' alt="'.$GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:stop') . '" /></a>';
  937. // Define some default values
  938. $lastExecution = '-';
  939. $isRunning = false;
  940. $executionStatus = 'scheduled';
  941. $executionStatusDetail = '';
  942. $executionStatusOutput = '';
  943. $name = '';
  944. $nextDate = '-';
  945. $execType = '-';
  946. $frequency = '-';
  947. $multiple = '-';
  948. $startExecutionElement = '&nbsp;';
  949. // Restore the serialized task and pass it a reference to the scheduler object
  950. $task = unserialize($schedulerRecord['serialized_task_object']);
  951. // Assemble information about last execution
  952. $context = '';
  953. if (!empty($schedulerRecord['lastexecution_time'])) {
  954. $lastExecution = date($dateFormat, $schedulerRecord['lastexecution_time']);
  955. if ($schedulerRecord['lastexecution_context'] == 'CLI') {
  956. $context = $GLOBALS['LANG']->getLL('label.cron');
  957. } else {
  958. $context = $GLOBALS['LANG']->getLL('label.manual');
  959. }
  960. $lastExecution .= ' (' . $context . ')';
  961. }
  962. if ($this->scheduler->isValidTaskObject($task)) {
  963. // The task object is valid
  964. $name = $registeredClasses[$schedulerRecord['classname']]['title']. ' (' . $registeredClasses[$schedulerRecord['classname']]['extension'] . ')';
  965. $additionalInformation = $task->getAdditionalInformation();
  966. if (!empty($additionalInformation)) {
  967. $name .= ' [' . $additionalInformation . ']';
  968. }
  969. // Check if task currently has a running execution
  970. if (!empty($schedulerRecord['serialized_executions'])) {
  971. $isRunning = true;
  972. $executionStatus = 'running';
  973. }
  974. // Prepare display of next execution date
  975. // If task is currently running, date is not displayed (as next hasn't been calculated yet)
  976. // Also hide the date if task is disabled (the information doesn't make sense, as it will not run anyway)
  977. if ($isRunning || $schedulerRecord['disable'] == 1) {
  978. $nextDate = '-';
  979. }
  980. else {
  981. $nextDate = date($dateFormat, $schedulerRecord['nextexecution']);
  982. if (empty($schedulerRecord['nextexecution'])) {
  983. $nextDate = $GLOBALS['LANG']->getLL('none');
  984. } elseif ($schedulerRecord['nextexecution'] < $GLOBALS['EXEC_TIME']) {
  985. // Next execution is overdue, highlight date
  986. $nextDate = '<span class="late" title="' . $GLOBALS['LANG']->getLL('status.legend.scheduled') . '">' . $nextDate . '</span>';
  987. $executionStatus = 'late';
  988. }
  989. }
  990. // Get execution type
  991. if ($task->getExecution()->getInterval() == 0 && $task->getExecution()->getCronCmd() == '') {
  992. $execType = $GLOBALS['LANG']->getLL('label.type.single');
  993. $frequency = '-';
  994. } else {
  995. $execType = $GLOBALS['LANG']->getLL('label.type.recurring');
  996. if ($task->getExecution()->getCronCmd() == '') {
  997. $frequency = $task->getExecution()->getInterval();
  998. } else {
  999. $frequency = $task->getExecution()->getCronCmd();
  1000. }
  1001. }
  1002. // Get multiple executions setting
  1003. if ($task->getExecution()->getMultiple()) {
  1004. $multiple = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:yes');
  1005. } else {
  1006. $multiple = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:no');
  1007. }
  1008. // Define checkbox
  1009. $startExecutionElement = '<input type="checkbox" name="tx_scheduler[execute][]" value="' . $schedulerRecord['uid'] . '" id="task_' . $schedulerRecord['uid'] . '" class="checkboxes" />';
  1010. // Show no action links (edit, delete) if task is running
  1011. $actions = $editAction . $deleteAction;
  1012. if ($isRunning) {
  1013. $actions = $stopAction;
  1014. }
  1015. // Check the disable status
  1016. // Row is shown dimmed if task is disabled, unless it is still running
  1017. if ($schedulerRecord['disable'] == 1 && !$isRunning) {
  1018. $tableLayout[$tr] = $disabledTaskRow;
  1019. $executionStatus = 'disabled';
  1020. }
  1021. // Check if the last run failed
  1022. $failureOutput = '';
  1023. if (!empty($schedulerRecord['lastexecution_failure'])) {
  1024. // Try to get the stored exception object
  1025. $exception = unserialize($schedulerRecord['lastexecution_failure']);
  1026. // If the exception could not be unserialized, issue a default error message
  1027. if ($exception === FALSE) {
  1028. $failureDetail = $GLOBALS['LANG']->getLL('msg.executionFailureDefault');
  1029. } else {
  1030. $failureDetail = sprintf($GLOBALS['LANG']->getLL('msg.executionFailureReport'), $exception->getCode(), $exception->getMessage());
  1031. }
  1032. $failureOutput = ' <img ' . t3lib_iconWorks::skinImg(t3lib_extMgm::extRelPath('scheduler'), 'res/gfx/status_failure.png') . ' alt="' . htmlspecialchars($GLOBALS['LANG']->getLL('status.failure')) . '" title="' . htmlspecialchars($failureDetail) . '" />';
  1033. }
  1034. // Format the execution status,
  1035. // including failure feedback, if any
  1036. $executionStatusOutput = '<img ' . t3lib_iconWorks::skinImg(t3lib_extMgm::extRelPath('scheduler'), 'res/gfx/status_' . $executionStatus . '.png') . ' alt="' . htmlspecialchars($GLOBALS['LANG']->getLL('status.' . $executionStatus)) . '" title="' . htmlspecialchars($executionStatusDetail) . '" />' . $failureOutput . ' ' . htmlspecialchars($name);
  1037. $table[$tr][] = $startExecutionElement;
  1038. $table[$tr][] = $actions;
  1039. $table[$tr][] = $schedulerRecord['uid'];
  1040. $table[$tr][] = $executionStatusOutput;
  1041. $table[$tr][] = $execType;
  1042. $table[$tr][] = $frequency;
  1043. $table[$tr][] = $multiple;
  1044. $table[$tr][] = $lastExecution;
  1045. $table[$tr][] = $nextDate;
  1046. } else {
  1047. // The task object is not valid
  1048. // Prepare to issue an error
  1049. /** @var t3lib_FlashMessage $flashMessage */
  1050. $flashMessage = t3lib_div::makeInstance('t3lib_FlashMessage',
  1051. sprintf($GLOBALS['LANG']->getLL('msg.invalidTaskClass'), $schedulerRecord['classname']),
  1052. '',
  1053. t3lib_FlashMessage::ERROR
  1054. );
  1055. $executionStatusOutput = $flashMessage->render();
  1056. $tableLayout[$tr] = $rowWithSpan;
  1057. $table[$tr][] = $startExecutionElement;
  1058. $table[$tr][] = $deleteAction;
  1059. $table[$tr][] = $schedulerRecord['uid'];
  1060. $table[$tr][] = $executionStatusOutput;
  1061. }
  1062. $tr++;
  1063. }
  1064. // Render table
  1065. $content .= $this->doc->table($table, $tableLayout);
  1066. $content .= '<input type="submit" class="button" name="go" value="' . $GLOBALS['LANG']->getLL('label.executeSelected') . '" />';
  1067. }
  1068. if (count($registeredClasses) > 0) {
  1069. // Display add new task link
  1070. $link = $GLOBALS['MCONF']['_'] . '&CMD=add';
  1071. $content .= '<p><a href="' . htmlspecialchars($link) .'"><img '
  1072. . t3lib_iconWorks::skinImg($this->backPath, 'gfx/new_el.gif')
  1073. . ' alt="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:new', TRUE)
  1074. . '" /> ' . $GLOBALS['LANG']->getLL('action.add') . '</a></p>';
  1075. } else {
  1076. /** @var t3lib_FlashMessage $flashMessage */
  1077. $flashMessage = t3lib_div::makeInstance('t3lib_FlashMessage',
  1078. $GLOBALS['LANG']->getLL('msg.noTasksDefined'),
  1079. '',
  1080. t3lib_FlashMessage::INFO
  1081. );
  1082. $content .= $flashMessage->render();
  1083. }
  1084. // Display legend, if there's at least one registered task
  1085. // Also display information about the usage of server time
  1086. if ($numRows > 0) {
  1087. $content .= $this->doc->spacer(20);
  1088. $content .= '<h4>' . $GLOBALS['LANG']->getLL('status.legend') . '</h4>
  1089. <ul>
  1090. <li><img ' . t3lib_iconWorks::skinImg(t3lib_extMgm::extRelPath('scheduler'), 'res/gfx/status_failure.png') . ' alt="' . htmlspecialchars($GLOBALS['LANG']->getLL('status.failure')) . '" /> ' . $GLOBALS['LANG']->getLL('status.legend.failure') . '</li>
  1091. <li><img ' . t3lib_iconWorks::skinImg(t3lib_extMgm::extRelPath('scheduler'), 'res/gfx/status_late.png') . ' alt="' . htmlspecialchars($GLOBALS['LANG']->getLL('status.late')) . '" /> ' . $GLOBALS['LANG']->getLL('status.legend.late') . '</li>
  1092. <li><img ' . t3lib_iconWorks::skinImg(t3lib_extMgm::extRelPath('scheduler'), 'res/gfx/status_running.png') . ' alt="' . htmlspecialchars($GLOBALS['LANG']->getLL('status.running')) . '" /> ' . $GLOBALS['LANG']->getLL('status.legend.running') . '</li>
  1093. <li><img ' . t3lib_iconWorks::skinImg(t3lib_extMgm::extRelPath('scheduler'), 'res/gfx/status_scheduled.png') . ' alt="' . htmlspecialchars($GLOBALS['LANG']->getLL('status.scheduled')) . '" /> ' . $GLOBALS['LANG']->getLL('status.legend.scheduled') . '</li>
  1094. <li><img ' . t3lib_iconWorks::skinImg(t3lib_extMgm::extRelPath('scheduler'), 'res/gfx/status_disabled.png') . ' alt="' . htmlspecialchars($GLOBALS['LANG']->getLL('status.disabled')) . '" /> ' . $GLOBALS['LANG']->getLL('status.legend.disabled') . '</li>
  1095. </ul>';
  1096. $content .= $this->doc->spacer(10);
  1097. $content .= $this->displayServerTime();
  1098. }
  1099. $GLOBALS['TYPO3_DB']->sql_free_result($res);
  1100. return $content;
  1101. }
  1102. /**
  1103. * Saves a task specified in the backend form to the database
  1104. *
  1105. * @return void
  1106. */
  1107. protected function saveTask() {
  1108. // If a task is being edited fetch old task data
  1109. if (!empty($this->submittedData['uid'])) {
  1110. try {
  1111. $taskRecord = $this->scheduler->fetchTaskRecord($this->submittedData['uid']);
  1112. /**
  1113. * @var tx_scheduler_Task
  1114. */
  1115. $task = unserialize($taskRecord['serialized_task_object']);
  1116. } catch (OutOfBoundsException $e) {
  1117. // If the task could not be fetched, issue an error message
  1118. // and exit early
  1119. $this->addMessage(sprintf($GLOBALS['LANG']->getLL('msg.taskNotFound'), $this->submittedData['uid']), t3lib_FlashMessage::ERROR);
  1120. return;
  1121. }
  1122. // Register single execution
  1123. if ($this->submittedData['type'] == 1) {
  1124. $task->registerSingleExecution($this->submittedData['start']);
  1125. // Else, it's a recurring task
  1126. } else {
  1127. if (!empty($this->submittedData['croncmd'])) {
  1128. // Definition by cron-like syntax
  1129. $interval = 0;
  1130. $cronCmd = $this->submittedData['croncmd'];
  1131. } else {
  1132. // Definition by interval
  1133. $interval = $this->submittedData['interval'];
  1134. $cronCmd = '';
  1135. }
  1136. // Register recurring execution
  1137. $task->registerRecurringExecution($this->submittedData['start'], $interval, $this->submittedData['end'], $this->submittedData['multiple'], $cronCmd);
  1138. }
  1139. // Set disable flag
  1140. $task->setDisabled($this->submittedData['disable']);
  1141. // Save additional input values
  1142. if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$this->submittedData['class']]['additionalFields'])) {
  1143. $providerObject = t3lib_div::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][$this->submittedData['class']]['additionalFields']);
  1144. if ($providerObject instanceof tx_scheduler_AdditionalFieldProvider) {
  1145. $providerObject->saveAdditionalFields($this->submittedData, $task);
  1146. }
  1147. }
  1148. // Save to database
  1149. $result = $this->scheduler->saveTask($task);
  1150. if ($result) {
  1151. $this->addMessage($GLOBALS['LANG']->getLL('msg.updateSuccess'));
  1152. } else {
  1153. $this->addMessage($GLOBALS['LANG']->getLL('msg.updateError'), t3lib_FlashMessage::ERROR);
  1154. }
  1155. } else {
  1156. // A new task is being created
  1157. // Create an instance of chosen class
  1158. $task = t3lib_div::makeInstance($this->submittedData['class']);
  1159. if ($this->submittedData['type'] == 1) {
  1160. // Set up single execution
  1161. $task->registerSingleExecution($this->submittedData['start']);
  1162. } else {
  1163. // Set up recurring execution
  1164. $task->registerRecurringExecution(

Large files files are truncated, but you can click here to view the full file