From 7558feb6cade7cebe7c061fde7fe4807ab7f88da Mon Sep 17 00:00:00 2001 From: Tim Almdal Date: Tue, 2 Feb 2010 15:02:46 -0800 Subject: [PATCH] Move the scheduler module to the contrib repository for this release. --- .../scheduler/controllers/admin_schedule.php | 105 ++++++++++++++ modules/scheduler/helpers/scheduler.php | 133 ++++++++++++++++++ modules/scheduler/helpers/scheduler_event.php | 73 ++++++++++ .../scheduler/helpers/scheduler_installer.php | 42 ++++++ modules/scheduler/models/schedule.php | 33 +++++ modules/scheduler/module.info | 3 + .../scheduler/views/admin_schedule.html.php | 11 ++ .../views/admin_schedule_confirm.html.php | 4 + .../views/scheduler_definitions.html.php | 49 +++++++ 9 files changed, 453 insertions(+) create mode 100644 modules/scheduler/controllers/admin_schedule.php create mode 100644 modules/scheduler/helpers/scheduler.php create mode 100644 modules/scheduler/helpers/scheduler_event.php create mode 100644 modules/scheduler/helpers/scheduler_installer.php create mode 100644 modules/scheduler/models/schedule.php create mode 100644 modules/scheduler/module.info create mode 100644 modules/scheduler/views/admin_schedule.html.php create mode 100644 modules/scheduler/views/admin_schedule_confirm.html.php create mode 100644 modules/scheduler/views/scheduler_definitions.html.php diff --git a/modules/scheduler/controllers/admin_schedule.php b/modules/scheduler/controllers/admin_schedule.php new file mode 100644 index 00000000..6911cf86 --- /dev/null +++ b/modules/scheduler/controllers/admin_schedule.php @@ -0,0 +1,105 @@ +task_callback = $task_callback; + $schedule->next_run_datetime = time(); + $v = new View("admin_schedule.html"); + $v->form = scheduler::get_form("define", $schedule); + $v->method = "define"; + print $v; + } + + public function update_form($id) { + access::verify_csrf(); + + + $schedule = ORM::factory("schedule", $id); + $v = new View("admin_schedule.html"); + $v->form = scheduler::get_form("update", $schedule); + $v->method = "update"; + print $v; + } + + public function remove_form($id) { + access::verify_csrf(); + + $schedule = ORM::factory("schedule", $id); + + $v = new View("admin_schedule_confirm.html"); + $v->name = $schedule->name; + $v->form = new Forge("admin/schedule/remove/{$id}", "", "post", + array("id" => "g-remove-schedule")); + $group = $v->form->group("remove"); + $group->submit("")->value(t("Continue")); + print $v; + } + + public function remove($id) { + access::verify_csrf(); + $schedule = ORM::factory("schedule", $id); + $schedule->delete(); + + message::success(t("Removed scheduled task: %name", array("name" => $schedule->name))); + print json_encode(array("result" => "success", "reload" => 1)); + } + + public function define() { + $this->_handle_request("define"); + } + + public function update($id=null) { + $this->_handle_request("update", $id); + } + + private function _handle_request($method, $id=null) { + $schedule = ORM::factory("schedule", $id); + $form = scheduler::get_form($method, $schedule); + $valid = $form->validate(); + if ($valid) { + $schedule->name = $form->schedule_group->schedule_name->value; + $schedule->interval = $form->schedule_group->interval->value; + $schedule->next_run_datetime = + $this->_start_date($form->schedule_group->run_date->dow->selected, + $form->schedule_group->run_date->time->value); + $schedule->task_callback = $form->schedule_group->callback->value; + $schedule->save(); + if ($method == "define") { + message::success(t("Added scheduled task: %name", array("name" => $schedule->name))); + } else { + message::success(t("Updated scheduled task: %name", array("name" => $schedule->name))); + } + print json_encode(array("result" => "success", "reload" => 1)); + } else { + print json_encode(array("result" => "error", "form" => (string) $form)); + } + } + + private function _start_date($dow, $time) { + list ($hour, $minutes) = explode(":", $time); + $local_time = localtime(); + $days = ($dow < $local_time[6] ? 7 : 0) + $dow - $local_time[6]; + return + mktime($hour, $minutes, 0, $local_time[4] + 1, $local_time[3] + $days, 1900 + $local_time[5]); + } +} diff --git a/modules/scheduler/helpers/scheduler.php b/modules/scheduler/helpers/scheduler.php new file mode 100644 index 00000000..987b1b32 --- /dev/null +++ b/modules/scheduler/helpers/scheduler.php @@ -0,0 +1,133 @@ + t("Hourly"), "86400" => t("Daily"), + "604800" => t("Weekly"), "2419200" => t("Monthly")); + } + return $intervals; + } + + static function get_form($method, $schedule) { + if ($method == "define") { + $title = t("Create a scheduled event"); + $button_text = t("Create"); + } else { + $title = t("Update a scheduled event"); + $button_text = t("Update"); + } + + $id = empty($schedule->id) ? "" : "/$schedule->id"; + $form = new Forge("admin/schedule/$method{$id}", "", "post", + array("id" => "g-{$method}-schedule")); + $group = $form->group("schedule_group")->label($title); + $group->input("schedule_name") + ->label(t("Description")) + ->id("g-schedule-name") + ->rules("required|length[0, 128]") + ->error_messages("required", t("You must provide a description")) + ->error_messages("length", t("Your description is too long")) + ->value(!empty($schedule->name) ? $schedule->name : ""); + + list ($dow, $display_time) = scheduler::format_time($schedule->next_run_datetime); + $next = $group->group("run_date")->label(t("Scheduled Date")); + $next->dropdown("dow") + ->label(t("Day")) + ->id("g-schedule-day") + ->rules("required") + ->options(array(t("Sunday"), t("Monday"), t("Tuesday"), t("Wednesday"), + t("Thursday"), t("Friday"), t("Saturday"))) + ->selected($dow); + + $next->input("time") + ->label(t("Hour")) + ->id("g-schedule-time") + ->rules("required") + ->error_messages("required", t("You must provide a time")) + ->error_messages("time_invalid", t("Invalid time")) + ->callback("scheduler::valid_time") + ->value($display_time); + + // need to set the top padding to zero on g-define-schedule li.g-error + $group->dropdown("interval")->label(t("How often"))->id("g-schedule-frequency") + ->options(scheduler::intervals()) + ->rules("required") + ->error_messages("required", t("You must provide an interval")) + ->selected(!empty($schedule->interval) ? $schedule->interval : "2419200"); + $group->hidden("callback")->value($schedule->task_callback); + $group->submit("")->value($button_text); + + return $form; + } + + static function format_time($time) { + $local_time = localtime($time); + $display_time = str_pad($local_time[2], 2, "0", STR_PAD_LEFT) . ":" . + str_pad($local_time[1], 2, "0", STR_PAD_LEFT); + return array($local_time[6], $display_time); + } + + static function valid_time($field) { + if (preg_match("/([0-9]{1,2}):([0-9]{2})/", $field->value, $matches)) { + $hour = (int)$matches[1]; + $minutes = (int)$matches[2]; + if (!(0 <= $hour && $hour<= 23 || 0 <= $minutes && $minutes <= 59)) { + $field->add_error("time_invalid", 1); + } + } else { + $field->add_error("time_invalid", 1); + } + } + + static function get_definitions() { + $v = ""; + $events = ORM::factory("schedule") + ->order_by("next_run_datetime", "asc") + ->find_all(); + if ($events->count()) { + $v = new View("scheduler_definitions.html"); + $v->schedule_definitions = array(); + foreach ($events as $schedule) { + $entry[] = $schedule->id; + $entry[] = $schedule->name; + $run_date = strftime("%A, %b %e, %Y %H:%M ", $schedule->next_run_datetime); + $intervals = scheduler::intervals(); + $interval = $intervals[$schedule->interval]; + if (!empty($schedule->task_id)) { + $status = t("Running"); + } else if ($schedule->next_run_datetime < time()) { + $status = t("Overdue"); + } else { + $status = t("Scheduled"); + } + + $v->schedule_definitions[] = (object)array("id" => $schedule->id, + "name" => $schedule->name, + "run_date" => $run_date, + "interval" => $interval, + "status" => $status); + } + } + return $v; + } +} \ No newline at end of file diff --git a/modules/scheduler/helpers/scheduler_event.php b/modules/scheduler/helpers/scheduler_event.php new file mode 100644 index 00000000..7d00841a --- /dev/null +++ b/modules/scheduler/helpers/scheduler_event.php @@ -0,0 +1,73 @@ + t("schedule"), + "url" =>url::site("form/add/admin/schedule")); + } + + /** + * At this point, the output has been sent to the browser, so we can take some time + * to run a schedule task. + */ + static function gallery_shutdown() { + try { + $schedule = ORM::factory("schedule") + ->where("next_run_datetime", "<=", time()) + ->where("busy", "!=", 1) + ->order_by("next_run_datetime") + ->find_all(1); + + if ($schedule->count()) { + $schedule = $schedule->current(); + $schedule->busy = true; + $schedule->save(); + + try { + if (empty($schedule->task_id)) { + $task = task::start($schedule->task_callback); + $schedule->task_id = $task->id; + } + + $task = task::run($schedule->task_id); + + if ($task->done) { + $schedule->next_run_datetime += $schedule->interval; + $schedule->task_id = null; + } + + $schedule->busy = false; + $schedule->save(); + } catch (Exception $e) { + $schedule->busy = false; + $schedule->save(); + throw $e; + } + } + } catch (Exception $e) { + Kohana_Log::add("error", (string)$e); + } + } +} diff --git a/modules/scheduler/helpers/scheduler_installer.php b/modules/scheduler/helpers/scheduler_installer.php new file mode 100644 index 00000000..46be4a81 --- /dev/null +++ b/modules/scheduler/helpers/scheduler_installer.php @@ -0,0 +1,42 @@ +query("CREATE TABLE {schedules} ( + `id` int(9) NOT NULL auto_increment, + `name` varchar(128) NOT NULL, + `task_callback` varchar(128) NOT NULL, + `task_id` int(9) NULL, + `next_run_datetime` int(9) NOT NULL, + `interval` int(9) NOT NULL, + `busy` bool NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `run_date` (`next_run_datetime`, `busy`), + UNIQUE KEY (`name`)) + DEFAULT CHARSET=utf8;"); + module::set_version("scheduler", $version = 1); + } + + static function uninstall() { + $db = Database::instance(); + $db->query("DROP TABLE IF EXISTS {schedules}"); + } +} diff --git a/modules/scheduler/models/schedule.php b/modules/scheduler/models/schedule.php new file mode 100644 index 00000000..b83b5738 --- /dev/null +++ b/modules/scheduler/models/schedule.php @@ -0,0 +1,33 @@ +join("schedules_tasks", "task.id", "schedules_tasks.task_id") + ->where("schedule_id", "=", $this->id) + ->find_all(); + } + + public function add_task($task) { + db::build() + ->insert("schedules_tasks", array("schedule_id" => $this->id,"task_id" => $task->id)) + ->execute(); + } +} diff --git a/modules/scheduler/module.info b/modules/scheduler/module.info new file mode 100644 index 00000000..15355dfb --- /dev/null +++ b/modules/scheduler/module.info @@ -0,0 +1,3 @@ +name = "Scheduler" +description = "Schedule tasks to run at specific times and intervals" +version = 1 diff --git a/modules/scheduler/views/admin_schedule.html.php b/modules/scheduler/views/admin_schedule.html.php new file mode 100644 index 00000000..3d45dc53 --- /dev/null +++ b/modules/scheduler/views/admin_schedule.html.php @@ -0,0 +1,11 @@ + + + diff --git a/modules/scheduler/views/admin_schedule_confirm.html.php b/modules/scheduler/views/admin_schedule_confirm.html.php new file mode 100644 index 00000000..5d09654d --- /dev/null +++ b/modules/scheduler/views/admin_schedule_confirm.html.php @@ -0,0 +1,4 @@ + +

+

$name)) ?>

+ diff --git a/modules/scheduler/views/scheduler_definitions.html.php b/modules/scheduler/views/scheduler_definitions.html.php new file mode 100644 index 00000000..0ab46f2b --- /dev/null +++ b/modules/scheduler/views/scheduler_definitions.html.php @@ -0,0 +1,49 @@ + +
+

+ + + + + + + + + + "> + + + + + + + +
+ + + + + + + + + +
+ name) ?> + + run_date) ?> + + interval) ?> + + status) ?> + + id?csrf=$csrf") ?>" + class="g-dialog-link g-button ui-icon-left ui-state-default ui-corner-all"> + + + id?csrf=$csrf") ?>" + class="g-dialog-link g-button ui-icon-left ui-state-default ui-corner-all"> + + +
+