diff --git a/core/controllers/admin_maintenance.php b/core/controllers/admin_maintenance.php
index b695cfcb..fba78a40 100644
--- a/core/controllers/admin_maintenance.php
+++ b/core/controllers/admin_maintenance.php
@@ -18,49 +18,172 @@
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
class Admin_Maintenance_Controller extends Admin_Controller {
- public function index() {
- $view = new Admin_View("admin.html");
-
- $available_tasks = array(
- new ArrayObject(
- array("name" => "rebuild_images",
- "description" => _("Rebuild out of date thumbnails and resizes")),
+ /**
+ * Get all available tasks
+ * @todo move task definition out into the modules
+ */
+ private function _get_task_definitions() {
+ $dirty_count = graphics::find_dirty_images_query()->count();
+ return array(
+ "graphics::rebuild_dirty_images" => new ArrayObject(
+ array("name" => _("Rebuild Images"),
+ "callback" => "graphics::rebuild_dirty_images",
+ "description" => (
+ $dirty_count ?
+ sprintf(
+ _("You have %d out-of-date images"), $dirty_count)
+ : _("All your images are up to date")),
+ "severity" => $dirty_count ? log::WARNING : log::SUCCESS),
ArrayObject::ARRAY_AS_PROPS));
+ }
+ /**
+ * Show a list of all available, running and finished tasks.
+ */
+ public function index() {
+ $query = Database::instance()->query(
+ "UPDATE `tasks` SET `state` = 'stalled' " .
+ "WHERE done = 0 " .
+ "AND state <> 'stalled' " .
+ "AND unix_timestamp(now()) - updated > 120");
+ $stalled_count = $query->count();
+ if ($stalled_count) {
+ log::warning("tasks",
+ sprintf(_("%d tasks are stalled"), $stalled_count),
+ sprintf(_("%sview%s"),
+ "",
+ ""));
+ }
+
+ $view = new Admin_View("admin.html");
$view->content = new View("admin_maintenance.html");
- $view->content->available_tasks = $available_tasks;
- $view->content->running_tasks = ORM::factory("task")->find_all();
+ $view->content->task_definitions = $this->_get_task_definitions();
+ $view->content->running_tasks = ORM::factory("task")->where("done", 0)->find_all();
+ $view->content->finished_tasks = ORM::factory("task")->where("done", 1)->find_all();
+ $view->content->csrf = access::csrf_token();
print $view;
}
- public function start($task_name) {
+ /**
+ * Start a new task
+ * @param string $task_callback
+ */
+ public function start($task_callback) {
+ access::verify_csrf();
+
+ $task_definitions = $this->_get_task_definitions();
+
$task = ORM::factory("task");
- $task->name = $task_name;
+ $task->callback = $task_callback;
+ $task->name = $task_definitions[$task_callback]->name;
$task->percent_complete = 0;
$task->status = "";
+ $task->state = "started";
$task->context = serialize(array());
$task->save();
$view = new View("admin_maintenance_task.html");
+ $view->csrf = access::csrf_token();
$view->task = $task;
+
+ log::info("tasks", sprintf(_("Task %s started (task id %d)"), $task->name, $task->id),
+ html::anchor(url::site("admin/maintenance"), _("maintenance")));
print $view;
}
+ /**
+ * Resume a stalled task
+ * @param string $task_id
+ */
+ public function resume($task_id) {
+ access::verify_csrf();
+
+ $task = ORM::factory("task", $task_id);
+ if (!$task->loaded) {
+ throw new Exception("@todo MISSING_TASK");
+ }
+ $view = new View("admin_maintenance_task.html");
+ $view->csrf = access::csrf_token();
+ $view->task = $task;
+
+ log::info("tasks", sprintf(_("Task %s resumed (task id %d)"), $task->name, $task->id),
+ html::anchor(url::site("admin/maintenance"), _("maintenance")));
+ print $view;
+ }
+
+ /**
+ * Cancel a task.
+ * @param string $task_id
+ */
+ public function cancel($task_id) {
+ access::verify_csrf();
+
+ $task = ORM::factory("task", $task_id);
+ if (!$task->loaded) {
+ throw new Exception("@todo MISSING_TASK");
+ }
+ $task->done = 1;
+ $task->state = "cancelled";
+ $task->save();
+
+ message::success(_("Task cancelled"));
+ url::redirect("admin/maintenance");
+ }
+
+ /**
+ * Remove a task.
+ * @param string $task_id
+ */
+ public function remove($task_id) {
+ access::verify_csrf();
+
+ $task = ORM::factory("task", $task_id);
+ if (!$task->loaded) {
+ throw new Exception("@todo MISSING_TASK");
+ }
+ $task->delete();
+ message::success(_("Task removed"));
+ url::redirect("admin/maintenance");
+ }
+
+ /**
+ * Run a task. This will trigger the task to do a small amount of work, then it will report
+ * back with status on the task.
+ * @param string $task_id
+ */
public function run($task_id) {
+ access::verify_csrf();
+
$task = ORM::factory("task", $task_id);
if (!$task->loaded) {
throw new Exception("@todo MISSING_TASK");
}
- switch($task->name) {
- case "rebuild_images":
- graphics::rebuild_dirty_images($task);
- }
-
+ $task->state = "running";
+ call_user_func_array($task->callback, array(&$task));
$task->save();
- print json_encode(
- array("status" => "success",
- "task" => $task->as_array()));
+ if ($task->done) {
+ switch ($task->state) {
+ case "success":
+ log::success("tasks", sprintf(_("Task %s completed (task id %d)"), $task->name, $task->id),
+ html::anchor(url::site("admin/maintenance"), _("maintenance")));
+ message::success(_("Task completed successfully"));
+ break;
+
+ case "error":
+ log::error("tasks", sprintf(_("Task %s failed (task id %d)"), $task->name, $task->id),
+ html::anchor(url::site("admin/maintenance"), _("maintenance")));
+ message::success(_("Task failed"));
+ break;
+ }
+ print json_encode(
+ array("result" => "success",
+ "location" => url::site("admin/maintenance")));
+ } else {
+ print json_encode(
+ array("result" => "in_progress",
+ "task" => $task->as_array()));
+ }
}
}
diff --git a/core/helpers/access.php b/core/helpers/access.php
index c6ee1fcc..d05f3df0 100644
--- a/core/helpers/access.php
+++ b/core/helpers/access.php
@@ -305,7 +305,8 @@ class access_Core {
* Verify our Cross Site Request Forgery token is valid, else throw an exception.
*/
public static function verify_csrf() {
- if (Input::instance()->post("csrf") !== Session::instance()->get("csrf")) {
+ $input = Input::instance();
+ if ($input->post("csrf", $input->get("csrf", null)) !== Session::instance()->get("csrf")) {
access::forbidden();
}
}
diff --git a/core/helpers/core_installer.php b/core/helpers/core_installer.php
index 46eb24c6..c83d9bcb 100644
--- a/core/helpers/core_installer.php
+++ b/core/helpers/core_installer.php
@@ -128,11 +128,14 @@ class core_installer {
ENGINE=InnoDB DEFAULT CHARSET=utf8;");
$db->query("CREATE TABLE `tasks` (
+ `callback` varchar(255) default NULL,
`context` text NOT NULL,
- `done` boolean DEFAULT 0,
+ `done` boolean default 0,
`id` int(9) NOT NULL auto_increment,
+ `updated` int(9) default NULL,
`name` varchar(255) default NULL,
`percent_complete` int(9) default 0,
+ `state` varchar(32) default NULL,
`status` varchar(255) default NULL,
PRIMARY KEY (`id`))
ENGINE=InnoDB DEFAULT CHARSET=utf8;");
diff --git a/core/helpers/graphics.php b/core/helpers/graphics.php
index 68aacaca..62bde88a 100644
--- a/core/helpers/graphics.php
+++ b/core/helpers/graphics.php
@@ -128,11 +128,18 @@ class graphics_Core {
->save($output_file);
}
+ /**
+ * Stub.
+ * @todo implement this
+ */
+ public static function compose($input_file, $output_file, $other_args) {
+ }
+
/**
* Return a query result that locates all items with dirty images.
* @return Database_Result Query result
*/
- private static function _find_dirty_images_query() {
+ public static function find_dirty_images_query() {
return Database::instance()->query(
"SELECT `id` FROM `items` " .
"WHERE (`thumb_dirty` = 1 AND (`type` <> 'album' OR `right` - `left` > 1))" .
@@ -147,12 +154,12 @@ class graphics_Core {
$db = Database::instance();
$db->query("UPDATE `items` SET `thumb_dirty` = 1, `resize_dirty` = 1");
- $count = self::_find_dirty_images_query()->count();
+ $count = self::find_dirty_images_query()->count();
if ($count) {
message::warning(
sprintf(_("%d of your photos are out of date. %sClick here to fix them%s"),
$count, "", ""),
"graphics_dirty");
}
@@ -165,7 +172,7 @@ class graphics_Core {
public static function rebuild_dirty_images($task) {
$db = Database::instance();
- $result = self::_find_dirty_images_query();
+ $result = self::find_dirty_images_query();
$remaining = $result->count();
$completed = $task->get("completed", 0);
@@ -194,9 +201,9 @@ class graphics_Core {
}
$task->set("completed", $completed);
- $task->done = ($remaining == 0);
-
- if ($task->done) {
+ if ($remaining == 0) {
+ $task->done = true;
+ $task->state = "success";
message::clear_permanent("graphics_dirty");
}
}
diff --git a/core/models/task.php b/core/models/task.php
index 697ab7bc..b88e34b7 100644
--- a/core/models/task.php
+++ b/core/models/task.php
@@ -33,4 +33,11 @@ class Task_Model extends ORM {
$context[$key] = $value;
$this->context = serialize($context);
}
+
+ public function save() {
+ if (!empty($this->changed)) {
+ $this->updated = time();
+ }
+ return parent::save();
+ }
}
\ No newline at end of file
diff --git a/core/views/admin_maintenance.html.php b/core/views/admin_maintenance.html.php
index 0d9f6adb..263fea10 100644
--- a/core/views/admin_maintenance.html.php
+++ b/core/views/admin_maintenance.html.php
@@ -2,19 +2,34 @@
= _("Maintenance Tasks") ?>
- = _("Occasionally your Gallery will require some maintenance. Here are some tasks you can run to keep it running smoothly.") ?>
+ = _("Occasionally your Gallery will require some maintenance. Here are some tasks you can use to keep it running smoothly.") ?>
= _("Available Tasks") ?>
-
- foreach ($available_tasks as $task) ?>
+
+ |
+ = _("Name") ?>
+ |
+
+ = _("Description") ?>
+ |
+
+ = _("Action") ?>
+ |
+
+ foreach ($task_definitions as $task) ?>
+
+ |
+ = $task->name ?>
+ |
= $task->description ?>
|
- name") ?>" class="gDialogLink">
+ callback?csrf=$csrf") ?>"
+ class="gDialogLink">
= _("run") ?>
|
@@ -25,6 +40,119 @@
= _("Running Tasks") ?>
-
Task list goes here
+
+
+ |
+ = _("Last Updated") ?>
+ |
+
+ = _("Name") ?>
+ |
+
+ = _("Status") ?>
+ |
+
+ = _("Info") ?>
+ |
+
+ = _("Action") ?>
+ |
+
+ foreach ($running_tasks as $task): ?>
+ ">
+ |
+ = date("M j, Y H:i:s", $task->updated) ?>
+ |
+
+ = $task->name ?>
+ |
+
+ if ($task->done): ?>
+ if ($task->state == "cancelled"): ?>
+ = _("Cancelled") ?>
+ endif ?>
+ = _("Done") ?>
+ elseif ($task->state == "stalled"): ?>
+ = _("Stalled") ?>
+ else: ?>
+ = sprintf(_("%d%% Complete"), $task->percent_complete) ?>
+ endif ?>
+ |
+
+ = $task->status ?>
+ |
+
+ if ($task->state == "stalled"): ?>
+ id?csrf=$csrf") ?>" class="gDialogLink">
+ = _("resume") ?>
+
+ endif ?>
+ id?csrf=$csrf") ?>">
+ = _("cancel") ?>
+
+ |
+
+ endforeach ?>
+
+
+
+
+
= _("Finished Tasks") ?>
+
+
+
+ |
+ = _("Last Updated") ?>
+ |
+
+ = _("Name") ?>
+ |
+
+ = _("Status") ?>
+ |
+
+ = _("Info") ?>
+ |
+
+ = _("Action") ?>
+ |
+
+ foreach ($finished_tasks as $task): ?>
+ ">
+ |
+ = date("M j, Y H:i:s", $task->updated) ?>
+ |
+
+ = $task->name ?>
+ |
+
+ if ($task->state == "success"): ?>
+ = _("Success") ?>
+ elseif ($task->state == "error"): ?>
+ = _("Failed") ?>
+ elseif ($task->state == "cancelled"): ?>
+ = _("Cancelled") ?>
+ endif ?>
+ |
+
+ = $task->status ?>
+ |
+
+ if ($task->done): ?>
+ id?csrf=$csrf") ?>">
+ = _("remove") ?>
+
+ else: ?>
+ id?csrf=$csrf") ?>">
+ = _("resume") ?>
+
+ id?csrf=$csrf") ?>">
+ = _("cancel") ?>
+
+ endif ?>
+ |
+
+ endforeach ?>
+
diff --git a/core/views/admin_maintenance_task.html.php b/core/views/admin_maintenance_task.html.php
index 4776ecaa..c31de876 100644
--- a/core/views/admin_maintenance_task.html.php
+++ b/core/views/admin_maintenance_task.html.php
@@ -3,7 +3,7 @@