[FEATURE] Add table garbage collection scheduler task
authorChristian Kuhn <lolli@schwarzbu.ch>
Thu, 31 Mar 2011 21:44:19 +0000 (23:44 +0200)
committerChristian Kuhn <lolli@schwarzbu.ch>
Sun, 19 Jun 2011 21:07:35 +0000 (23:07 +0200)
This feature was implemented during T3BOARD11 bug auction. It is
kindly sponsored with 100 beers by jweiland.net and is called the
'Schnaps Hans scheduler task'

Change-Id: Id7c58d28e4f6e82a708513a3ce9761baf36c06cd
Resolves: #25343
Releases: 4.6
Reviewed-on: http://review.typo3.org/1376
Reviewed-by: Christian Kuhn
Tested-by: Christian Kuhn
NEWS.txt
typo3/sysext/scheduler/ext_autoload.php
typo3/sysext/scheduler/ext_localconf.php
typo3/sysext/scheduler/locallang.xml
typo3/sysext/scheduler/mod1/locallang.xml
typo3/sysext/scheduler/mod1/locallang_csh_scheduler.xml
typo3/sysext/scheduler/res/tx_scheduler_be.js
typo3/sysext/scheduler/tasks/class.tx_scheduler_tablegarbagecollection.php [new file with mode: 0644]
typo3/sysext/scheduler/tasks/class.tx_scheduler_tablegarbagecollection_additionalfieldprovider.php [new file with mode: 0644]

index 2068e44..2e56a71 100644 (file)
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -51,7 +51,14 @@ Workspaces
 Other general improvements
 -------------------------------------------------------------------------------
 
-...
+* New scheduler task to delete old data from tables
+
+The scheduler task 'Table garbage collection' can be used to delete old data
+from growing tables like log tables on a regular basis. Additional tables that
+can be cleaned up must be registered in $TYPO3_CONF_VARS['SC_OPTIONS']
+['scheduler']['tasks']['tx_scheduler_TableGarbageCollection']['options']
+['tables'] with its table name and a field. After that they can
+be selected in the scheduler task.
 
 -------------------------------------------------------------------------------
 Backend
@@ -135,4 +142,4 @@ Security
 Performance
 -------------------------------------------------------------------------------
 
-...
+...
\ No newline at end of file
index 6c3ada4..b0bef11 100644 (file)
@@ -19,5 +19,7 @@ return array(
        'tx_scheduler_croncmdtest' => $extensionPath . 'tests/tx_scheduler_croncmdTest.php',
        'tx_scheduler_cachingframeworkgarbagecollection' => $extensionPath . 'tasks/class.tx_scheduler_cachingframeworkgarbagecollection.php',
        'tx_scheduler_cachingframeworkgarbagecollection_additionalfieldprovider' => $extensionPath . 'tasks/class.tx_scheduler_cachingframeworkgarbagecollection_additionalfieldprovider.php',
+       'tx_scheduler_tablegarbagecollection' => $extensionPath . 'tasks/class.tx_scheduler_tablegarbagecollection.php',
+       'tx_scheduler_tablegarbagecollection_additionalfieldprovider' => $extensionPath . 'tasks/class.tx_scheduler_tablegarbagecollection_additionalfieldprovider.php',
 );
 ?>
\ No newline at end of file
index 54e4c13..185107b 100644 (file)
@@ -31,10 +31,37 @@ if (!empty($extConf['showSampleTasks'])) {
 
        // Add caching framework garbage collection task
 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks']['tx_scheduler_CachingFrameworkGarbageCollection'] = array(
-               'extension'        => $_EXTKEY,
-               'title'            => 'LLL:EXT:' . $_EXTKEY . '/locallang.xml:cachingFrameworkGarbageCollection.name',
-               'description'      => 'LLL:EXT:' . $_EXTKEY . '/locallang.xml:cachingFrameworkGarbageCollection.description',
-               'additionalFields' => 'tx_scheduler_CachingFrameworkGarbageCollection_AdditionalFieldProvider',
-       );
+       'extension'        => $_EXTKEY,
+       'title'            => 'LLL:EXT:' . $_EXTKEY . '/locallang.xml:cachingFrameworkGarbageCollection.name',
+       'description'      => 'LLL:EXT:' . $_EXTKEY . '/locallang.xml:cachingFrameworkGarbageCollection.description',
+       'additionalFields' => 'tx_scheduler_CachingFrameworkGarbageCollection_AdditionalFieldProvider',
+);
 
+       // Add table garbage collection task
+$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks']['tx_scheduler_TableGarbageCollection'] = array(
+       'extension'        => $_EXTKEY,
+       'title'            => 'LLL:EXT:' . $_EXTKEY . '/locallang.xml:tableGarbageCollection.name',
+       'description'      => 'LLL:EXT:' . $_EXTKEY . '/locallang.xml:tableGarbageCollection.description',
+       'additionalFields' => 'tx_scheduler_TableGarbageCollection_AdditionalFieldProvider',
+);
+       // Initialize option array of table garbage collection task if not already done by some other extension or localconf.php
+if (!is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks']['tx_scheduler_TableGarbageCollection']['options'])) {
+       $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks']['tx_scheduler_TableGarbageCollection']['options'] = array();
+}
+if (!is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks']['tx_scheduler_TableGarbageCollection']['options']['tables'])) {
+       $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks']['tx_scheduler_TableGarbageCollection']['options']['tables'] = array();
+}
+       // Register sys_log table in table garbage collection task
+if (!is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks']['tx_scheduler_TableGarbageCollection']['options']['tables']['sys_log'])) {
+       $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks']['tx_scheduler_TableGarbageCollection']['options']['tables']['sys_log'] = array(
+               'dateField' => 'tstamp',
+               'expirePeriod' => 180,
+       );
+}
+       // Register cache_pages table in table garbage collection task
+if (!is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks']['tx_scheduler_TableGarbageCollection']['options']['tables']['cache_pages'])) {
+       $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks']['tx_scheduler_TableGarbageCollection']['options']['tables']['cache_pages'] = array(
+               'expireField' => 'expires',
+       );
+}
 ?>
\ No newline at end of file
index 5279180..5efc6c9 100644 (file)
@@ -12,6 +12,8 @@
                        <label index="sleepTask.description">This task does nothing but put PHP to sleep for a number of seconds. It is designed to test multiple executions.</label>
                        <label index="cachingFrameworkGarbageCollection.name">Caching framework garbage collection</label>
                        <label index="cachingFrameworkGarbageCollection.description">This task calls the garbage collection of configured caching framework caches which use one of the selected backends. This will free some space in cache backends which do not have an internal garbage collection. In case of the default database backend it is advisable to run this task once a day when the database is mostly idle.</label>
+                       <label index="tableGarbageCollection.name">Table garbage collection</label>
+                       <label index="tableGarbageCollection.description">Task to delete old entries from specific tables like sys_log. Use $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks']['tx_scheduler_TableGarbageCollection']['options']['tables'] to configure tables. Refer to the manual for more details.</label>
                </languageKey>
        </data>
 </T3locallang>
\ No newline at end of file
index f079737..74f8db5 100644 (file)
                        <label index="label.serverTime">Server time</label>
                        <label index="label.sleepTime">Sleep time</label>
                        <label index="label.start">Start (HH:MM DD-MM-YYYY)</label>
+                       <label index="label.tableGarbageCollection.allTables">Clean all available tables</label>
+                       <label index="label.tableGarbageCollection.table">Table to clean up</label>
+                       <label index="label.tableGarbageCollection.additionalInformationTable">Table: %s</label>
+                       <label index="label.tableGarbageCollection.additionalInformationAllTables">All tables</label>
+                       <label index="label.tableGarbageCollection.numberOfDays">Delete entries older than given number of days</label>
                        <label index="label.type">Type</label>
                        <label index="label.type.recurring">Recurring</label>
                        <label index="label.type.single">Single</label>
@@ -76,6 +81,7 @@
                        <label index="msg.noStartDate">Please define a start date.</label>
                        <label index="msg.invalidStartDate">Start date is invalid.</label>
                        <label index="msg.invalidEndDate">End date is invalid.</label>
+                       <label index="msg.invalidNumberOfDays">Number of days is invalid.</label>
                        <label index="msg.invalidTaskClass">Class %s of the registered task could not be found. You should re-install the extension that provided it or simply delete this task.</label>
                        <label index="msg.invalidTaskClassEdit">The current class (%s) could not be found. Change it below or consider deleting this task altogether.</label>
                        <label index="msg.schedulerSetupCheck">This screen checks if the requisites for running the Scheduler as a cron job are fulfilled. It also displays information about the last run of the Scheduler.</label>
index a7c8d19..9ecc43a 100644 (file)
                        <label index="task_sleepTime.description">Enter a number of seconds during which the task will just sleep.</label>
                        <label index="task_cachingFrameworkGarbageCollection_selectedBackends.alttitle">Select backends</label>
                        <label index="task_cachingFrameworkGarbageCollection_selectedBackends.description">If a cache uses one of the selected backends, the garbage collection will be called for this cache.</label>
+                       <label index="task_tableGarbageCollection_numberOfDays.alttitle">Number of days</label>
+                       <label index="task_tableGarbageCollection_numberOfDays.description">Delete rows from selected table that are older than given number of days.</label>
+                       <label index="task_tableGarbageCollection_table.alttitle">Select table</label>
+                       <label index="task_tableGarbageCollection_table.description">Select one of the registered tables to be cleaned up with this task.</label>
+                       <label index="task_tableGarbageCollection_allTables.alttitle">Clean up all tables</label>
+                       <label index="task_tableGarbageCollection_allTables.description">If selected, all configured tables are cleaned. The selection below is ignored.</label>
                </languageKey>
        </data>
-</T3locallang>
+</T3locallang>
\ No newline at end of file
index d348479..cd8d668 100644 (file)
@@ -82,6 +82,42 @@ function actOnChangedTaskType(theSelector) {
 }
 
 /**
+ * This method reacts on field changes of all table field for
+ * table garbage collection task
+ *
+ * @param theCheckbox: The selected checkbox
+ * @return void
+ */
+function actOnChangeSchedulerTableGarbageCollectionAllTables(theCheckbox) {
+       if (theCheckbox.checked) {
+               Ext.fly('task_tableGarbageCollection_table').set({disabled: 'disabled'});
+               Ext.fly('task_tableGarbageCollection_numberOfDays').set({disabled: 'disabled'});
+       } else {
+               Ext.fly('task_tableGarbageCollection_table').dom.removeAttribute('disabled');
+               if (Ext.fly('task_tableGarbageCollection_numberOfDays').getValue() > 0) {
+                       Ext.fly('task_tableGarbageCollection_numberOfDays').dom.removeAttribute('disabled');
+               }
+       }
+}
+
+/**
+ * This methods set the 'number of days' field to the default expire period
+ * of the selected table
+ *
+ * @param theSelector: select form item where the table selection was made
+ * @return void
+ */
+function actOnChangeSchedulerTableGarbageCollectionTable(theSelector) {
+       if (defaultNumberOfDays[theSelector.options[theSelector.selectedIndex].value] > 0) {
+               Ext.fly('task_tableGarbageCollection_numberOfDays').dom.removeAttribute('disabled');
+               Ext.fly('task_tableGarbageCollection_numberOfDays').set({value: defaultNumberOfDays[theSelector.options[theSelector.selectedIndex].value]});
+       } else {
+               Ext.fly('task_tableGarbageCollection_numberOfDays').set({disabled: 'disabled'});
+               Ext.fly('task_tableGarbageCollection_numberOfDays').set({value: 0});
+       }
+}
+
+/**
  * This method reacts on the checking of a toggle,
  * activating or not the check of all other checkboxes
  *
@@ -120,4 +156,4 @@ Ext.onReady(function(){
                        }
                }
        });
-});
+});
\ No newline at end of file
diff --git a/typo3/sysext/scheduler/tasks/class.tx_scheduler_tablegarbagecollection.php b/typo3/sysext/scheduler/tasks/class.tx_scheduler_tablegarbagecollection.php
new file mode 100644 (file)
index 0000000..99dbda1
--- /dev/null
@@ -0,0 +1,156 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2011 Christian Kuhn <lolli@schwarzbu.ch>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+/**
+ * Remove old entries from tables.
+ *
+ * This task deletes rows from tables older than the given number of days.
+ *
+ * Available tables must be registered in
+ * $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks']['tx_scheduler_TableGarbageCollection']['options']['tables']
+ * See ext_localconf.php of scheduler extension for an example
+ *
+ * @author Christian Kuhn <lolli@schwarzbu.ch>
+ * @package TYPO3
+ * @subpackage scheduler
+ */
+class tx_scheduler_TableGarbageCollection extends tx_scheduler_Task {
+
+       /**
+        * @var boolean True if all tables should be cleaned up
+        */
+       public $allTables = FALSE;
+
+       /**
+        * @var integer Number of days
+        */
+       public $numberOfDays = 180;
+
+       /**
+        * @var string Table to clean up
+        */
+       public $table = '';
+
+       /**
+        * Execute garbage collection, called by scheduler.
+        *
+        * @throws RuntimeException if configured table was not cleaned up
+        * @return boolean TRUE if task run was successful
+        */
+       public function execute() {
+               $tableConfigurations = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks']['tx_scheduler_TableGarbageCollection']['options']['tables'];
+               $tableHandled = FALSE;
+               foreach ($tableConfigurations as $tableName => $configuration) {
+                       if ($this->allTables || $tableName === $this->table) {
+                               $this->handleTable($tableName, $configuration);
+                               $tableHandled = TRUE;
+                       }
+               }
+
+               if (!$tableHandled) {
+                       throw new RuntimeException(
+                               'tx_scheduler_TableGarbageCollection misconfiguration: ' . $this->table . ' does not exist in configuration',
+                               1308354399
+                       );
+               }
+
+               return TRUE;
+       }
+
+       /**
+        * Execute clean up of a specific table
+        *
+        * @throws RuntimeException If table configuration is broken
+        * @param string $table The table to handle
+        * @param array $configuration Clean up configuration
+        * @return void
+        */
+       protected function handleTable($table, array $configuration) {
+               if (isset($configuration['expireField'])) {
+                       $field = $configuration['expireField'];
+                       $dateLimit = $GLOBALS['EXEC_TIME'];
+                               // If expire field value is 0, do not delete
+                               // Expire field = 0 means no expiration
+                       $where = $field . " <= '" . $dateLimit . "' AND " . $field . " > '0'";
+               } elseif (isset($configuration['dateField'])) {
+                       $field = $configuration['dateField'];
+                       if (!$this->allTables) {
+                               $deleteTimestamp = strtotime('-' . $this->numberOfDays . 'days');
+                       } else {
+                               if (!isset($configuration['expirePeriod'])) {
+                                       throw new RuntimeException(
+                                               'tx_scheduler_TableGarbageCollection misconfiguration: No expirePeriod defined for table ' . $table,
+                                               1308355095
+                                       );
+                               }
+                               $deleteTimestamp = strtotime('-' . $configuration['expirePeriod'] . 'days');
+                       }
+                       $where = $configuration['dateField'] . ' < ' . $deleteTimestamp;
+               } else {
+                       throw new RuntimeException(
+                               'tx_scheduler_TableGarbageCollection misconfiguration: Either expireField or dateField must be defined for table ' . $table,
+                               1308355268
+                       );
+               }
+
+               $GLOBALS['TYPO3_DB']->exec_DELETEquery(
+                       $table,
+                       $where
+               );
+
+               $error = $GLOBALS['TYPO3_DB']->sql_error();
+               if ($error) {
+                       throw new Exception(
+                               'tx_scheduler_TableGarbageCollection failed for table ' . $this->table . ' with error: ' . $error,
+                               1308255491
+                       );
+               }
+
+               return TRUE;
+       }
+
+       /**
+        * This method returns the selected table as additional information
+        *
+        * @return string Information to display
+        */
+       public function getAdditionalInformation() {
+               if ($this->allTables) {
+                       $message = $GLOBALS['LANG']->sL('LLL:EXT:scheduler/mod1/locallang.xml:label.tableGarbageCollection.additionalInformationAllTables');
+               } else {
+                       $message = sprintf(
+                               $GLOBALS['LANG']->sL('LLL:EXT:scheduler/mod1/locallang.xml:label.tableGarbageCollection.additionalInformationTable'),
+                               $this->table
+                       );
+               }
+
+               return $message;
+       }
+}
+
+if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/scheduler/tasks/class.tx_scheduler_tablegarbagecollection.php'])) {
+       include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/scheduler/tasks/class.tx_scheduler_tablegarbagecollection.php']);
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/scheduler/tasks/class.tx_scheduler_tablegarbagecollection_additionalfieldprovider.php b/typo3/sysext/scheduler/tasks/class.tx_scheduler_tablegarbagecollection_additionalfieldprovider.php
new file mode 100644 (file)
index 0000000..a72e28e
--- /dev/null
@@ -0,0 +1,291 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2011 Christian Kuhn <lolli@schwarzbu.ch>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+/**
+ * Additional BE fields for sys log table garbage collection task.
+ *
+ * @author Christian Kuhn <lolli@schwarzbu.ch>
+ * @package TYPO3
+ * @subpackage scheduler
+ */
+class tx_scheduler_TableGarbageCollection_AdditionalFieldProvider implements tx_scheduler_AdditionalFieldProvider {
+
+       /**
+        * Add addional fields
+        *
+        * @param array $taskInfo Reference to the array containing the info used in the add/edit form
+        * @param object $task When editing, reference to the current task object. Null when adding.
+        * @param tx_scheduler_Module $parentObject Reference to the calling object (Scheduler's BE module)
+        * @return array Array containing all the information pertaining to the additional fields
+        */
+       public function getAdditionalFields(array &$taskInfo, $task, tx_scheduler_Module $parentObject) {
+               $additionalFields['task_tableGarbageCollection_allTables'] = $this->getAllTablesAdditionalField($taskInfo, $task, $parentObject);
+               $additionalFields['task_tableGarbageCollection_table'] = $this->getTableAdditionalField($taskInfo, $task, $parentObject);
+               $additionalFields['task_tableGarbageCollection_numberOfDays'] = $this->getNumberOfDaysAdditionalField($taskInfo, $task, $parentObject);
+
+               return $additionalFields;
+       }
+
+       /**
+        * Add a select field of available tables.
+        *
+        * @param array $taskInfo Reference to the array containing the info used in the add/edit form
+        * @param object $task When editing, reference to the current task object. Null when adding.
+        * @param tx_scheduler_Module $parentObject Reference to the calling object (Scheduler's BE module)
+        * @return array Array containing all the information pertaining to the additional fields
+        */
+       protected function getAllTablesAdditionalField(array &$taskInfo, $task, tx_scheduler_Module $parentObject) {
+               if ($parentObject->CMD === 'edit') {
+                       $checked = $task->allTables === TRUE ? 'checked="checked" ' : '';
+               } else {
+                       $checked = '';
+               }
+
+               $fieldName = 'tx_scheduler[scheduler_tableGarbageCollection_allTables]';
+               $fieldId = 'task_tableGarbageCollection_allTables';
+               $fieldHtml = '<input type="checkbox" ' .
+                       $checked .
+                       'onChange="actOnChangeSchedulerTableGarbageCollectionAllTables(this)" ' .
+                       'name="' . $fieldName . '" ' .
+                       'id="' . $fieldId . '" />';
+
+               $fieldConfiguration = array(
+                       'code' => $fieldHtml,
+                       'label' => 'LLL:EXT:scheduler/mod1/locallang.xml:label.tableGarbageCollection.allTables',
+                       'cshKey' => '_MOD_tools_txschedulerM1',
+                       'cshLabel' => $fieldId,
+               );
+
+               return $fieldConfiguration;
+       }
+
+       /**
+        * Add a select field of available tables.
+        *
+        * @param array $taskInfo Reference to the array containing the info used in the add/edit form
+        * @param object $task When editing, reference to the current task object. Null when adding.
+        * @param tx_scheduler_Module $parentObject Reference to the calling object (Scheduler's BE module)
+        * @return array Array containing all the information pertaining to the additional fields
+        */
+       protected function getTableAdditionalField(array &$taskInfo, $task, tx_scheduler_Module $parentObject) {
+               $tableConfiguration = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks']['tx_scheduler_TableGarbageCollection']['options']['tables'];
+
+               $options = array();
+
+                               // Add an empty option on top if an existing task is configured
+                               // with a table that can not be found in configuration anymore
+               if ($parentObject->CMD === 'edit' && !array_key_exists($task->table, $tableConfiguration)) {
+                       $options[] = '<option value="" selected="selected"></option>';
+               }
+
+               $defaultNumberOfDays = array();
+               foreach ($tableConfiguration as $tableName => $configuration) {
+                       if ($parentObject->CMD === 'add' && count($options) === 0) {
+                                       // Select first table by default if adding a new task
+                               $options[] = '<option value="' . $tableName . '" selected="selected">' . $tableName . '</option>';
+                               if (isset($configuration['expirePeriod'])) {
+                                       $defaultNumberOfDays[$tableName] = $configuration['expirePeriod'];
+                               }
+                       } elseif ($task->table === $tableName) {
+                                       // Select currently selected table
+                               $options[] = '<option value="' . $tableName . '" selected="selected">' . $tableName . '</option>';
+                               if (isset($configuration['expirePeriod'])) {
+                                       $defaultNumberOfDays[$tableName] = $task->numberOfDays;
+                               }
+                       } else {
+                               $options[] = '<option value="' . $tableName . '">' . $tableName . '</option>';
+                               if (isset($configuration['expirePeriod'])) {
+                                       $defaultNumberOfDays[$tableName] = $configuration['expirePeriod'];
+                               }
+                       }
+               }
+
+               $disabled = $task->allTables === TRUE ? ' disabled="disabled"' : '';
+
+               $fieldName = 'tx_scheduler[scheduler_tableGarbageCollection_table]';
+               $fieldId = 'task_tableGarbageCollection_table';
+
+               $fieldHtml = array();
+                       // Add table drop down html
+               $fieldHtml[] = '<select ' .
+                       'name="' . $fieldName . '" ' .
+                       $disabled .
+                       'onChange="actOnChangeSchedulerTableGarbageCollectionTable(this)"' .
+                       'id="' . $fieldId . '">' .
+                       implode(LF, $options) .
+                       '</select>';
+                       // Add js array for default 'number of days' values
+               $fieldHtml[] = '<script type="text/javascript">/*<![CDATA[*/<!--';
+               $fieldHtml[] = 'var defaultNumberOfDays = ' . json_encode($defaultNumberOfDays) . ';';
+               $fieldHtml[] = '// -->/*]]>*/</script>';
+
+               $fieldConfiguration = array(
+                       'code' => implode(LF, $fieldHtml),
+                       'label' => 'LLL:EXT:scheduler/mod1/locallang.xml:label.tableGarbageCollection.table',
+                       'cshKey' => '_MOD_tools_txschedulerM1',
+                       'cshLabel' => $fieldId,
+               );
+
+               return $fieldConfiguration;
+       }
+
+       /**
+        * Add a input field to get the number of days.
+        *
+        * @param array $taskInfo Reference to the array containing the info used in the add/edit form
+        * @param object $task When editing, reference to the current task object. Null when adding.
+        * @param tx_scheduler_Module $parentObject Reference to the calling object (Scheduler's BE module)
+        * @return array Array containing all the information pertaining to the additional fields
+        */
+       protected function getNumberOfDaysAdditionalField(array &$taskInfo, $task, tx_scheduler_Module $parentObject) {
+                       // Initialize selected fields
+               $disabled = '';
+               if (empty($taskInfo['scheduler_tableGarbageCollection_numberOfDays'])) {
+                       if ($parentObject->CMD === 'add') {
+                                       // In case of new task, set to 180 days
+                               $taskInfo['scheduler_tableGarbageCollection_numberOfDays'] = 180;
+                       } elseif ($parentObject->CMD === 'edit') {
+                                       // In case of editing the task, set to currently selected value
+                               $taskInfo['scheduler_tableGarbageCollection_numberOfDays'] = $task->numberOfDays;
+                               if ($task->numberOfDays === 0) {
+                                       $disabled = ' disabled="disabled"';
+                               }
+                       }
+               }
+
+               if ($task->allTables === TRUE) {
+                       $disabled = ' disabled="disabled"';
+               }
+
+               $fieldName = 'tx_scheduler[scheduler_tableGarbageCollection_numberOfDays]';
+               $fieldId = 'task_tableGarbageCollection_numberOfDays';
+               $fieldHtml = '<input type="text" ' .
+                       'name="' . $fieldName . '" ' .
+                       'id="' . $fieldId . '" ' .
+                       $disabled .
+                       'value="' . intval($taskInfo['scheduler_tableGarbageCollection_numberOfDays']) . '" ' .
+                       'size="4" />';
+
+               $fieldConfiguration = array(
+                       'code' => $fieldHtml,
+                       'label' => 'LLL:EXT:scheduler/mod1/locallang.xml:label.tableGarbageCollection.numberOfDays',
+                       'cshKey' => '_MOD_tools_txschedulerM1',
+                       'cshLabel' => $fieldId,
+               );
+
+               return $fieldConfiguration;
+       }
+
+       /**
+        * Validate additional fields
+        *
+        * @param array $submittedData Reference to the array containing the data submitted by the user
+        * @param tx_scheduler_Module $parentObject Reference to the calling object (Scheduler's BE module)
+        * @return boolean True if validation was ok (or selected class is not relevant), false otherwise
+        */
+       public function validateAdditionalFields(array &$submittedData, tx_scheduler_Module $parentObject) {
+               $validData = $this->validateAllTablesAdditionalField($submittedData, $parentObject);
+               $validData &= $this->validateTableAdditionalField($submittedData, $parentObject);
+               $validData &= $this->validateNumberOfDaysAdditionalField($submittedData, $parentObject);
+
+               return $validData;
+       }
+
+       /**
+        * Checks if all table field is correct
+        *
+        * @param array $submittedData Reference to the array containing the data submitted by the user
+        * @param tx_scheduler_Module $parentObject Reference to the calling object (Scheduler's BE module)
+        * @return boolean True if data is valid
+        */
+       public function validateAllTablesAdditionalField(array &$submittedData, tx_scheduler_Module $parentObject) {
+               $validData = FALSE;
+               if (!isset($submittedData['scheduler_tableGarbageCollection_allTables'])) {
+                       $validData = TRUE;
+               } elseif ($submittedData['scheduler_tableGarbageCollection_allTables'] === 'on') {
+                       $validData = TRUE;
+               }
+
+               return $validData;
+       }
+
+       /**
+        * Checks given table for existence in configuration array
+        *
+        * @param array $submittedData Reference to the array containing the data submitted by the user
+        * @param tx_scheduler_Module $parentObject Reference to the calling object (Scheduler's BE module)
+        * @return boolean True if table exists in configuration, false otherwise
+        */
+       public function validateTableAdditionalField(array &$submittedData, tx_scheduler_Module $parentObject) {
+               $validData = FALSE;
+               $tableConfiguration = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks']['tx_scheduler_TableGarbageCollection']['options']['tables'];
+               if (!isset($submittedData['scheduler_tableGarbageCollection_table'])) {
+                       $validData = TRUE;
+               } elseif (array_key_exists($submittedData['scheduler_tableGarbageCollection_table'], $tableConfiguration)) {
+                       $validData = TRUE;
+               }
+
+               return $validData;
+       }
+
+       /**
+        * Checks if given number of days is a positive integer
+        *
+        * @param array $submittedData Reference to the array containing the data submitted by the user
+        * @param tx_scheduler_Module $parentObject Reference to the calling object (Scheduler's BE module)
+        * @return boolean True if validation was ok (or selected class is not relevant), false otherwise
+        */
+       public function validateNumberOfDaysAdditionalField(array &$submittedData, tx_scheduler_Module $parentObject) {
+               $validData = FALSE;
+               if (!isset($submittedData['scheduler_tableGarbageCollection_numberOfDays'])) {
+                       $validData = TRUE;
+               } elseif (intval($submittedData['scheduler_tableGarbageCollection_numberOfDays']) >= 0) {
+                       $validData = TRUE;
+               } else {
+                               // Issue error message
+                       $parentObject->addMessage($GLOBALS['LANG']->sL('LLL:EXT:scheduler/mod1/locallang.xml:msg.invalidNumberOfDays'), t3lib_FlashMessage::ERROR);
+               }
+
+               return $validData;
+       }
+
+       /**
+        * Save additional field in task
+        *
+        * @param array $submittedData Contains data submitted by the user
+        * @param tx_scheduler_Task $task Reference to the current task object
+        * @return void
+        */
+       public function saveAdditionalFields(array $submittedData, tx_scheduler_Task $task) {
+               $task->allTables = $submittedData['scheduler_tableGarbageCollection_allTables'] === 'on' ? TRUE : FALSE;
+               $task->table = $submittedData['scheduler_tableGarbageCollection_table'];
+               $task->numberOfDays = intval($submittedData['scheduler_tableGarbageCollection_numberOfDays']);
+       }
+}
+
+if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/scheduler/tasks/class.tx_scheduler_tablegarbagecollection_additionalfieldprovider.php'])) {
+       include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/scheduler/tasks/class.tx_scheduler_tablegarbagecollection_additionalfieldprovider.php']);
+}
+
+?>
\ No newline at end of file