summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/controllers/admin_maintenance.php159
-rw-r--r--core/helpers/access.php3
-rw-r--r--core/helpers/core_installer.php5
-rw-r--r--core/helpers/graphics.php21
-rw-r--r--core/models/task.php7
-rw-r--r--core/views/admin_maintenance.html.php138
-rw-r--r--core/views/admin_maintenance_task.html.php2
7 files changed, 302 insertions, 33 deletions
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"),
+ "<a href=\"" . url::site("admin/maintenance") . "\">",
+ "</a>"));
+ }
+
+ $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;
}
- public function run($task_id) {
+ /**
+ * 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();
- switch($task->name) {
- case "rebuild_images":
- graphics::rebuild_dirty_images($task);
+ $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");
}
+ $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
@@ -129,10 +129,17 @@ class graphics_Core {
}
/**
+ * 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, "<a href=\"" .
- url::site("admin/maintenance/start/rebuild_images") .
+ url::site("admin/maintenance/start/rebuild_images?csrf=" . access::csrf_token()) .
"\" class=\"gDialogLink\">", "</a>"),
"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 @@
<div id="gMaintenance">
<h1> <?= _("Maintenance Tasks") ?> </h1>
<p>
- <?= _("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.") ?>
</p>
<div id="gAvailableTasks">
<h2> <?= _("Available Tasks") ?> </h2>
- <table style="width: 400px">
- <? foreach ($available_tasks as $task) ?>
+ <table style="width: 680px" border="1">
<tr>
+ <th>
+ <?= _("Name") ?>
+ </th>
+ <th>
+ <?= _("Description") ?>
+ </th>
+ <th>
+ <?= _("Action") ?>
+ </th>
+ </tr>
+ <? foreach ($task_definitions as $task) ?>
+ <tr class="<?= log::severity_class($task->severity) ?>">
+ <td>
+ <?= $task->name ?>
+ </td>
<td>
<?= $task->description ?>
</td>
<td>
- <a href="<?= url::site("admin/maintenance/start/$task->name") ?>" class="gDialogLink">
+ <a href="<?= url::site("admin/maintenance/start/$task->callback?csrf=$csrf") ?>"
+ class="gDialogLink">
<?= _("run") ?>
</a>
</td>
@@ -25,6 +40,119 @@
<div id="gRunningTasks">
<h2> <?= _("Running Tasks") ?> </h2>
- <i> Task list goes here </i>
+ <table style="width: 680px" border="1">
+ <tr>
+ <th>
+ <?= _("Last Updated") ?>
+ </th>
+ <th>
+ <?= _("Name") ?>
+ </th>
+ <th>
+ <?= _("Status") ?>
+ </th>
+ <th>
+ <?= _("Info") ?>
+ </th>
+ <th>
+ <?= _("Action") ?>
+ </th>
+ </tr>
+ <? foreach ($running_tasks as $task): ?>
+ <tr class="<?= $task->state == "stalled" ? "gWarning" : "" ?>">
+ <td>
+ <?= date("M j, Y H:i:s", $task->updated) ?>
+ </td>
+ <td>
+ <?= $task->name ?>
+ </td>
+ <td>
+ <? if ($task->done): ?>
+ <? if ($task->state == "cancelled"): ?>
+ <?= _("Cancelled") ?>
+ <? endif ?>
+ <?= _("Done") ?>
+ <? elseif ($task->state == "stalled"): ?>
+ <?= _("Stalled") ?>
+ <? else: ?>
+ <?= sprintf(_("%d%% Complete"), $task->percent_complete) ?>
+ <? endif ?>
+ </td>
+ <td>
+ <?= $task->status ?>
+ </td>
+ <td>
+ <? if ($task->state == "stalled"): ?>
+ <a href="<?= url::site("admin/maintenance/resume/$task->id?csrf=$csrf") ?>" class="gDialogLink">
+ <?= _("resume") ?>
+ </a>
+ <? endif ?>
+ <a href="<?= url::site("admin/maintenance/cancel/$task->id?csrf=$csrf") ?>">
+ <?= _("cancel") ?>
+ </a>
+ </td>
+ </tr>
+ <? endforeach ?>
+ </table>
+ </div>
+
+ <div id="gFinishedTasks">
+ <h2> <?= _("Finished Tasks") ?> </h2>
+
+ <table style="width: 680px" border="1">
+ <tr>
+ <th>
+ <?= _("Last Updated") ?>
+ </th>
+ <th>
+ <?= _("Name") ?>
+ </th>
+ <th>
+ <?= _("Status") ?>
+ </th>
+ <th>
+ <?= _("Info") ?>
+ </th>
+ <th>
+ <?= _("Action") ?>
+ </th>
+ </tr>
+ <? foreach ($finished_tasks as $task): ?>
+ <tr class="<?= $task->state == "success" ? "gSuccess" : "gError" ?>">
+ <td>
+ <?= date("M j, Y H:i:s", $task->updated) ?>
+ </td>
+ <td>
+ <?= $task->name ?>
+ </td>
+ <td>
+ <? if ($task->state == "success"): ?>
+ <?= _("Success") ?>
+ <? elseif ($task->state == "error"): ?>
+ <?= _("Failed") ?>
+ <? elseif ($task->state == "cancelled"): ?>
+ <?= _("Cancelled") ?>
+ <? endif ?>
+ </td>
+ <td>
+ <?= $task->status ?>
+ </td>
+ <td>
+ <? if ($task->done): ?>
+ <a href="<?= url::site("admin/maintenance/remove/$task->id?csrf=$csrf") ?>">
+ <?= _("remove") ?>
+ </a>
+ <? else: ?>
+ <a href="<?= url::site("admin/maintenance/resume/$task->id?csrf=$csrf") ?>">
+ <?= _("resume") ?>
+ </a>
+ <a href="<?= url::site("admin/maintenance/cancel/$task->id?csrf=$csrf") ?>">
+ <?= _("cancel") ?>
+ </a>
+ <? endif ?>
+ </td>
+ </tr>
+ <? endforeach ?>
+ </table>
</div>
</div>
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 @@
<script type="text/javascript">
update = function() {
$.ajax({
- url: "<?= url::site("admin/maintenance/run/$task->id") ?>",
+ url: "<?= url::site("admin/maintenance/run/$task->id?csrf=$csrf") ?>",
dataType: "json",
success: function(data) {
$("#gStatus").html("" + data.task.status);