diff options
24 files changed, 481 insertions, 231 deletions
@@ -36,7 +36,7 @@ # block below. You just need to change RewriteBase line to match your # Gallery 3 URL. Here are some examples: # -# Gallery3 URL RewriteBase line +# Gallery 3 URL RewriteBase line # ============= ==================== # http://example.com/gallery3 RewriteBase /gallery3 # http://example.com/~bob/photos RewriteBase /~bob/photos diff --git a/installer/cli.php b/installer/cli.php index e2fdffce..50845ea4 100644 --- a/installer/cli.php +++ b/installer/cli.php @@ -69,7 +69,7 @@ function oops($message) { print "==> " . $message; print "\n"; print "For help you can try:\n"; - print " * The Gallery3 FAQ - http://codex.gallery2.org/Gallery3:FAQ\n"; + print " * The Gallery 3 FAQ - http://codex.gallery2.org/Gallery3:FAQ\n"; print " * The Gallery Forums - http://gallery.menalto.com/forum\n"; print "\n\n** INSTALLATION FAILED **\n"; exit(1); diff --git a/installer/views/already_installed.html.php b/installer/views/already_installed.html.php index 0d7fc193..f6ac1bff 100644 --- a/installer/views/already_installed.html.php +++ b/installer/views/already_installed.html.php @@ -1,5 +1,5 @@ <?php defined("SYSPATH") or die("No direct script access.") ?> <p class="success"> - Your Gallery3 install is complete. + Your Gallery 3 install is complete. </p> diff --git a/installer/views/install.html.php b/installer/views/install.html.php index 18060219..a0eddaf3 100644 --- a/installer/views/install.html.php +++ b/installer/views/install.html.php @@ -1,7 +1,7 @@ <?php defined("SYSPATH") or die("No direct script access.") ?> <html> <head> - <title>Gallery3 Installer</title> + <title>Gallery 3 Installer</title> <link rel="stylesheet" type="text/css" href="install.css"/> </head> <body> diff --git a/installer/views/success.html.php b/installer/views/success.html.php index 4bca2fb1..e9ee9818 100644 --- a/installer/views/success.html.php +++ b/installer/views/success.html.php @@ -1,7 +1,7 @@ <?php defined("SYSPATH") or die("No direct script access.") ?> <h1> Success! </h1> <p class="success"> - Your Gallery3 install is complete! + Your Gallery 3 install is complete! </p> <?php if (!empty($user)): ?> diff --git a/modules/comment/views/comment.mrss.php b/modules/comment/views/comment.mrss.php index e27bc44f..2b5b13c1 100644 --- a/modules/comment/views/comment.mrss.php +++ b/modules/comment/views/comment.mrss.php @@ -5,7 +5,7 @@ xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:fh="http://purl.org/syndication/history/1.0"> <channel> - <generator>gallery3</generator> + <generator>Gallery 3</generator> <title><?= p::clean($feed->title) ?></title> <link><?= $feed->uri ?></link> <description><?= p::clean($feed->description) ?></description> diff --git a/modules/exif/helpers/exif_task.php b/modules/exif/helpers/exif_task.php index 375503e3..649ad092 100644 --- a/modules/exif/helpers/exif_task.php +++ b/modules/exif/helpers/exif_task.php @@ -38,37 +38,48 @@ class exif_task_Core { } static function update_index($task) { - $completed = $task->get("completed", 0); + try { + $completed = $task->get("completed", 0); - $start = microtime(true); - foreach (ORM::factory("item") - ->join("exif_records", "items.id", "exif_records.item_id", "left") - ->where("type", "photo") - ->open_paren() - ->where("exif_records.item_id", null) - ->orwhere("exif_records.dirty", 1) - ->close_paren() - ->find_all() as $item) { - if (microtime(true) - $start > 1.5) { - break; - } + $start = microtime(true); + $message = array(); + foreach (ORM::factory("item") + ->join("exif_records", "items.id", "exif_records.item_id", "left") + ->where("type", "photo") + ->open_paren() + ->where("exif_records.item_id", null) + ->orwhere("exif_records.dirty", 1) + ->close_paren() + ->find_all() as $item) { + if (microtime(true) - $start > 1.5) { + break; + } - $completed++; - exif::extract($item); - } + $completed++; + exif::extract($item); + $message[] = t("Updated Exif meta data for '%title'", + array("title" => p::purify($item->title))); + } - list ($remaining, $total, $percent) = exif::stats(); - $task->set("completed", $completed); - if ($remaining == 0 || !($remaining + $completed)) { + $task->log($message); + list ($remaining, $total, $percent) = exif::stats(); + $task->set("completed", $completed); + if ($remaining == 0 || !($remaining + $completed)) { + $task->done = true; + $task->state = "success"; + site_status::clear("exif_index_out_of_date"); + $task->percent_complete = 100; + } else { + $task->percent_complete = round(100 * $completed / ($remaining + $completed)); + } + $task->status = t2("one record updated, index is %percent% up-to-date", + "%count records updated, index is %percent% up-to-date", + $completed, array("percent" => $percent)); + } catch (Exception $e) { $task->done = true; - $task->state = "success"; - site_status::clear("exif_index_out_of_date"); - $task->percent_complete = 100; - } else { - $task->percent_complete = round(100 * $completed / ($remaining + $completed)); + $task->state = "error"; + $task->status = $e->getMessage(); + $task->log($e->__toString()); } - $task->status = t2("one record updated, index is %percent% up-to-date", - "%count records updated, index is %percent% up-to-date", - $completed, array("percent" => $percent)); } } diff --git a/modules/g2_import/helpers/g2_import.php b/modules/g2_import/helpers/g2_import.php index 2e40eb7f..d67d4c04 100644 --- a/modules/g2_import/helpers/g2_import.php +++ b/modules/g2_import/helpers/g2_import.php @@ -54,14 +54,14 @@ class g2_import_Core { } /** - * Initialize the embedded Gallery2 instance. Call this before any other Gallery2 calls. + * Initialize the embedded Gallery 2 instance. Call this before any other Gallery 2 calls. */ static function init_embed($embed_path) { if (!is_file($embed_path)) { return false; } - // Gallery2 defines a class called Gallery. So does Gallery 3. They don't get along. So do + // Gallery 2 defines a class called Gallery. So does Gallery 3. They don't get along. So do // a total hack here and copy over a few critical files (embed.php, main.php, bootstrap.inc // and Gallery.class) and munge them so that we can rename the Gallery class to be // G2_Gallery. Is this retarded? Why yes it is. @@ -217,15 +217,14 @@ class g2_import_Core { static function import_group(&$queue) { $g2_group_id = array_shift($queue); if (self::map($g2_group_id)) { - return; + return t("Group with id: %id already imported, skipping", array("id" => $g2_group_id)); } try { $g2_group = g2(GalleryCoreApi::loadEntitiesById($g2_group_id)); } catch (Exception $e) { - g2_import::log( - t("Failed to import Gallery 2 group with id: %id", array("id" => $g2_group_id))); - return; + return t("Failed to import Gallery 2 group with id: %id\n%exception", + array("id" => $g2_group_id, "exception" => $e->__toString())); } switch ($g2_group->getGroupType()) { @@ -236,24 +235,29 @@ class g2_import_Core { // @todo For now we assume this is a "duplicate group" exception $group = group::lookup_by_name($g2_group->getGroupname()); } - + $message = t("Group '%name' was imported", array("name" => $g2_group->getGroupname())); break; case GROUP_ALL_USERS: $group = group::registered_users(); + $message = t("Group 'Registered' was converted to '%name'", array("name" => $group->name)); break; case GROUP_SITE_ADMINS: + $message = t("Group 'Admin' does not exist in Gallery 3, skipping"); break; // This is not a group in G3 case GROUP_EVERYBODY: $group = group::everybody(); + $message = t("Group 'Everybody' was converted to '%name'", array("name" => $group->name)); break; } if (isset($group)) { self::set_map($g2_group->getId(), $group->id); } + + return $message; } /** @@ -262,12 +266,12 @@ class g2_import_Core { static function import_user(&$queue) { $g2_user_id = array_shift($queue); if (self::map($g2_user_id)) { - return; + return t("User with id: %id already imported, skipping", array("id" => $g2_user_id)); } if (g2(GalleryCoreApi::isAnonymousUser($g2_user_id))) { self::set_map($g2_user_id, user::guest()->id); - return; + return t("Skipping Anonymous User"); } $g2_admin_group_id = @@ -275,17 +279,18 @@ class g2_import_Core { try { $g2_user = g2(GalleryCoreApi::loadEntitiesById($g2_user_id)); } catch (Exception $e) { - g2_import::log( - t("Failed to import Gallery 2 user with id: %id", array("id" => $g2_user_id))); - return; + return t("Failed to import Gallery 2 user with id: %id\n%exception", + array("id" => $g2_user_id, "exception" => $e->__toString())); } $g2_groups = g2(GalleryCoreApi::fetchGroupsForUser($g2_user->getId())); try { $user = user::create($g2_user->getUsername(), $g2_user->getfullname(), ""); + $message = t("Created user: '%name'.", array("name" => $user->name)); } catch (Exception $e) { // @todo For now we assume this is a "duplicate user" exception $user = user::lookup_by_name($g2_user->getUsername()); + $message = t("Loaded existing user: '%name'.", array("name" => $user->name)); } $user->hashed_password = $g2_user->getHashedPassword(); @@ -294,13 +299,18 @@ class g2_import_Core { foreach ($g2_groups as $g2_group_id => $g2_group_name) { if ($g2_group_id == $g2_admin_group_id) { $user->admin = true; + $message .= t("\n\tAdded 'admin' flag to user"); } else { - $user->add(ORM::factory("group", self::map($g2_group_id))); + $group = ORM::factory("group", self::map($g2_group_id)); + $user->add($group); + $message .= t("\n\tAdded user to group '%group'.", array("group" => $group->name)); } } $user->save(); self::set_map($g2_user->getId(), $user->id); + + return $message; } @@ -321,20 +331,19 @@ class g2_import_Core { } if (self::map($g2_album_id)) { - return; + return t("Album with id: %id already imported, skipping", array("id" => $g2_album_id)); } try { // Load the G2 album item, and figure out its parent in G3. $g2_album = g2(GalleryCoreApi::loadEntitiesById($g2_album_id)); } catch (Exception $e) { - g2_import::log( - t("Failed to import Gallery 2 album with id: %id", array("id" => $g2_album_id))); - return; + return t("Failed to import Gallery 2 album with id: %id\n%exception", + array("id" => $g2_album_id, "exception" => $e->__toString())); } if ($g2_album->getParentId() == null) { - return; + return t("Skipping Gallery 2 root album"); } $parent_album = ORM::factory("item", self::map($g2_album->getParentId())); @@ -367,10 +376,12 @@ class g2_import_Core { } $album->save(); - self::import_keywords_as_tags($g2_album->getKeywords(), $album); + $message[] = t("Album '%name' imported.", array("name" => $album->name)); + $message[] = self::import_keywords_as_tags($g2_album->getKeywords(), $album); self::set_map($g2_album_id, $album->id); // @todo import album highlights + return $message; } /** @@ -386,10 +397,11 @@ class g2_import_Core { $g3_album_id = self::map($g2_album_id); if (!$g3_album_id) { - return; + return t("Album with id: %id not imported", array("id" => $g3_album_id)); } $table = g2(GalleryCoreApi::fetchThumbnailsByItemIds(array($g2_album_id))); + $message = ""; if (isset($table[$g2_album_id])) { // Backtrack the source id to an item $g2_source = $table[$g2_album_id]; @@ -405,8 +417,10 @@ class g2_import_Core { $g2_album->view_count = g2(GalleryCoreApi::fetchItemViewCount($g2_album_id)); $g2_album->save(); graphics::generate($g2_album); + $message = t("Highlight created for album '%name'", array("name" => $g2_album->name)); } } + return $message; } /** @@ -416,16 +430,15 @@ class g2_import_Core { $g2_item_id = array_shift($queue); if (self::map($g2_item_id)) { - return; + return t("Item with id: %id already imported, skipping", array("id" => $g2_item_id)); } try { self::$current_g2_item = $g2_item = g2(GalleryCoreApi::loadEntitiesById($g2_item_id)); $g2_path = g2($g2_item->fetchPath()); } catch (Exception $e) { - g2_import::log( - t("Failed to import Gallery 2 item with id: %id", array("id" => $g2_item_id))); - return; + return t("Failed to import Gallery 2 item with id: %id\n%exception", + array("id" => $g2_item_id, "exception" => $e->__toString())); } $parent = ORM::factory("item", self::map($g2_item->getParentId())); @@ -433,7 +446,7 @@ class g2_import_Core { $g2_type = $g2_item->getEntityType(); $corrupt = 0; if (!file_exists($g2_path)) { - // If the Gallery2 source image isn't available, this operation is going to fail. That can + // If the Gallery 2 source image isn't available, this operation is going to fail. That can // happen in cases where there's corruption in the source Gallery 2. In that case, fall // back on using a broken image. It's important that we import *something* otherwise // anything that refers to this item in Gallery 2 will have a dangling pointer in Gallery 3 @@ -447,10 +460,13 @@ class g2_import_Core { $corrupt = 1; } + $message = array(); switch ($g2_type) { case "GalleryPhotoItem": if (!in_array($g2_item->getMimeType(), array("image/jpeg", "image/gif", "image/png"))) { Kohana::log("alert", "$g2_path is an unsupported image type; using a placeholder gif"); + $message[] = t("'%path' is an unsupported image type, using a placeholder", + array("path" => $g2_path)); $g2_path = MODPATH . "g2_import/data/broken-image.gif"; $corrupt = 1; } @@ -464,9 +480,12 @@ class g2_import_Core { self::_decode_html_special_chars($g2_item->getTitle()), self::_decode_html_special_chars(self::extract_description($g2_item)), self::map($g2_item->getOwnerId())); + $message[].= t("Imported photo: '%title'", array("title" => p::purify($item->title))); } catch (Exception $e) { Kohana::log( - "alert", "Corrupt image $g2_path\n" . $e->getMessage() . "\n" . $e->getTraceAsString()); + "alert", "Corrupt image $g2_path\n" . $e->__toString()); + $message[] = t("Corrupt image '%path'", array("path" => $g2_path)); + $message[] = $e->__toString(); $corrupt = 1; } break; @@ -482,12 +501,19 @@ class g2_import_Core { self::_decode_html_special_chars($g2_item->getTitle()), self::_decode_html_special_chars(self::extract_description($g2_item)), self::map($g2_item->getOwnerId())); + $message[] = t("Imported movie: '%title'", array("title" => p::purify($item->title))); } catch (Exception $e) { - Kohana::log("alert", "Corrupt movie $g2_path\n" . - $e->getMessage() . "\n" . $e->getTraceAsString()); + Kohana::log("alert", "Corrupt movie $g2_path\n" . $e->__toString()); + $message[] = t("Corrupt movie '%path'", array("path" => $g2_path)); + $message[] = $e->__toString(); $corrupt = 1; } + } else { + Kohana::log("alert", "$g2_path is an unsupported movie type"); + $message[] = t("'%path' is an unsupported movie type", array("path" => $g2_path)); + $corrupt = 1; } + break; default: @@ -496,13 +522,14 @@ class g2_import_Core { } if (!empty($item)) { - self::import_keywords_as_tags($g2_item->getKeywords(), $item); + $message[] = self::import_keywords_as_tags($g2_item->getKeywords(), $item); } if (isset($item)) { self::set_map($g2_item_id, $item->id); $item->view_count = g2(GalleryCoreApi::fetchItemViewCount($g2_item_id)); $item->save(); + $message[] = t("View count updated: %count", array("count" => $item->view_count)); } if ($corrupt) { @@ -513,21 +540,21 @@ class g2_import_Core { $g2_item_url = str_replace('&g2_GALLERYSID=TMP_SESSION_ID_DI_NOISSES_PMT', '', $g2_item_url); if (!empty($item)) { - $warning = + $message[] = t("<a href=\"%g2_url\">%title</a> from Gallery 2 could not be processed; " . "(imported as <a href=\"%g3_url\">%title</a>)", array("g2_url" => $g2_item_url, "g3_url" => $item->url(), "title" => $g2_item->getTitle())); } else { - $warning = + $message[] = t("<a href=\"%g2_url\">%title</a> from Gallery 2 could not be processed", array("g2_url" => $g2_item_url, "title" => $g2_item->getTitle())); } - g2_import::log($warning); } self::$current_g2_item = null; + return $message; } /** @@ -548,9 +575,8 @@ class g2_import_Core { try { $g2_comment = g2(GalleryCoreApi::loadEntitiesById($g2_comment_id)); } catch (Exception $e) { - g2_import::log("Failed to import Gallery 2 comment with id: %id", - array("id" => $g2_comment_id)); - return; + return t("Failed to import Gallery 2 comment with id: %id\%exception", + array("id" => $g2_comment_id, "exception" => $e->__toString())); } $text = $g2_comment->getSubject(); @@ -572,12 +598,19 @@ class g2_import_Core { $comment->save(); self::map($g2_comment->getId(), $comment->id); + return t("Imported comment '%comment' for item with id: %id", + array("id" => $comment->item_id, + "comment" => text::limit_words(nl2br(p::purify($comment->text)), 50))); } /** * Import all the tags for a single item */ static function import_tags_for_item(&$queue) { + if (!module::is_active("tag")) { + return t("Gallery 3 tag module is inactive, no tags will be imported"); + } + GalleryCoreApi::requireOnce("modules/tags/classes/TagsHelper.class"); $g2_item_id = array_shift($queue); $g3_item = ORM::factory("item", self::map($g2_item_id)); @@ -585,22 +618,24 @@ class g2_import_Core { try { $tag_names = array_values(g2(TagsHelper::getTagsByItemId($g2_item_id))); } catch (Exception $e) { - g2_import::log("Failed to import tags for Gallery 2 item with id: %id", - array("id" => $g2_item_id)); - return; + return t("Failed to import Gallery 2 tags for item with id: %id\n%exception", + array("id" => $g2_item_id, "exception" => $e->__toString())); } + $tags = ""; foreach ($tag_names as $tag_name) { - $tag = tag::add($g3_item, $tag_name); + $tags .= (strlen($tags) ? ", " : "") . tag::add($g3_item, $tag_name); } // Tag operations are idempotent so we don't need to map them. Which is good because we don't // have an id for each individual tag mapping anyway so it'd be hard to set up the mapping. + return t("Added '%tags' to '%title'", array("tags" => $tags, + "title" => p::purify($item->title))); } static function import_keywords_as_tags($keywords, $item) { if (!module::is_active("tag")) { - return; + return t("Gallery 3 tag module is inactive, no keywords will be imported"); } // Keywords in G2 are free form. So we don't know what our user used as a separator. Try to @@ -614,17 +649,20 @@ class g2_import_Core { $delim = " "; } + $tags = ""; foreach (preg_split("/$delim/", $keywords) as $keyword) { $keyword = trim($keyword); if ($keyword) { - tag::add($item, $keyword); + $tags .= (strlen($tags) ? ", " : "") . tag::add($item, $keyword); } } + return strlen($tags) ? t("Added '%keywords' to '%title'", + array("keywords" => $tags, "title" => p::purify($item->title))) : ""; } /** - * If the thumbnails and resizes created for the Gallery2 photo match the dimensions of the - * ones we expect to create for Gallery3, then copy the files over instead of recreating them. + * If the thumbnails and resizes created for the Gallery 2 photo match the dimensions of the + * ones we expect to create for Gallery 3, then copy the files over instead of recreating them. */ static function copy_matching_thumbnails_and_resizes($item) { // We only operate on items that are being imported @@ -632,7 +670,7 @@ class g2_import_Core { return; } - // Precaution: if the Gallery2 item was watermarked, or we have the Gallery3 watermark module + // Precaution: if the Gallery 2 item was watermarked, or we have the Gallery 3 watermark module // active then we'd have to do something a lot more sophisticated here. For now, just skip // this step in those cases. // @todo we should probably use an API here, eventually. @@ -885,7 +923,7 @@ function g2() { $args = func_get_arg(0); $ret = array_shift($args); if ($ret) { - Kohana::log("error", "Gallery2 call failed with: " . $ret->getAsText()); + Kohana::log("error", "Gallery 2 call failed with: " . $ret->getAsText()); throw new Exception("@todo G2_FUNCTION_FAILED"); } if (count($args) == 1) { diff --git a/modules/g2_import/helpers/g2_import_task.php b/modules/g2_import/helpers/g2_import_task.php index 3961097d..47a205bd 100644 --- a/modules/g2_import/helpers/g2_import_task.php +++ b/modules/g2_import/helpers/g2_import_task.php @@ -94,7 +94,10 @@ class g2_import_task_Core { if (empty($queue)) { $task->set("queue", $queue = array_keys(g2(GalleryCoreApi::fetchGroupNames()))); } - g2_import::import_group($queue); + $log_message = g2_import::import_group($queue); + if ($log_message) { + $task->log($log_message); + } $task->status = t( "Importing groups (%count of %total)", array("count" => $done["groups"] + 1, "total" => $stats["groups"])); @@ -105,7 +108,10 @@ class g2_import_task_Core { $task->set( "queue", $queue = array_keys(g2(GalleryCoreApi::fetchUsersForGroup(GROUP_EVERYBODY)))); } - g2_import::import_user($queue); + $log_message = g2_import::import_user($queue); + if ($log_message) { + $task->log($log_message); + } $task->status = t( "Importing users (%count of %total)", array("count" => $done["users"] + 1, "total" => $stats["users"])); @@ -115,7 +121,10 @@ class g2_import_task_Core { if (empty($queue)) { $task->set("queue", $queue = g2(GalleryCoreApi::fetchAlbumTree())); } - g2_import::import_album($queue); + $log_message = g2_import::import_album($queue); + if ($log_message) { + $task->log($log_message); + } $task->status = t( "Importing albums (%count of %total)", array("count" => $done["albums"] + 1, "total" => $stats["albums"])); @@ -127,7 +136,10 @@ class g2_import_task_Core { $task->set("last_id", end($queue)); } - g2_import::import_item($queue); + $log_message = g2_import::import_item($queue); + if ($log_message) { + $task->log($log_message); + } $task->status = t( "Importing photos (%count of %total)", array("count" => $done["items"] + 1, "total" => $stats["items"])); @@ -138,7 +150,10 @@ class g2_import_task_Core { $task->set("queue", $queue = g2_import::get_comment_ids($task->get("last_id", 0))); $task->set("last_id", end($queue)); } - g2_import::import_comment($queue); + $log_message = g2_import::import_comment($queue); + if ($log_message) { + $task->log($log_message); + } $task->status = t( "Importing comments (%count of %total)", array("count" => $done["comments"] + 1, "total" => $stats["comments"])); @@ -150,7 +165,10 @@ class g2_import_task_Core { $task->set("queue", $queue = g2_import::get_tag_item_ids($task->get("last_id", 0))); $task->set("last_id", end($queue)); } - g2_import::import_tags_for_item($queue); + $log_message = g2_import::import_tags_for_item($queue); + if ($log_message) { + $task->log($log_message); + } $task->status = t( "Importing tags (%count of %total)", array("count" => $done["tags"] + 1, "total" => $stats["tags"])); @@ -161,7 +179,10 @@ class g2_import_task_Core { if (empty($queue)) { $task->set("queue", $queue = g2(GalleryCoreApi::fetchAlbumTree())); } - g2_import::set_album_highlight($queue); + $log_message = g2_import::set_album_highlight($queue); + if ($log_message) { + $task->log($log_message); + } $task->status = t( "Album highlights (%count of %total)", array("count" => $done["tags"] + 1, "total" => $stats["albums"])); diff --git a/modules/gallery/controllers/admin_maintenance.php b/modules/gallery/controllers/admin_maintenance.php index 7c5934a3..543961a1 100644 --- a/modules/gallery/controllers/admin_maintenance.php +++ b/modules/gallery/controllers/admin_maintenance.php @@ -59,6 +59,8 @@ class Admin_Maintenance_Controller extends Admin_Controller { $view = new View("admin_maintenance_task.html"); $view->task = $task; + $task->log(t("Task %task_name started (task id %task_id)", + array("task_name" => $task->name, "task_id" => $task->id))); log::info("tasks", t("Task %task_name started (task id %task_id)", array("task_name" => $task->name, "task_id" => $task->id)), html::anchor("admin/maintenance", t("maintenance"))); @@ -79,6 +81,8 @@ class Admin_Maintenance_Controller extends Admin_Controller { $view = new View("admin_maintenance_task.html"); $view->task = $task; + $task->log(t("Task %task_name resumed (task id %task_id)", + array("task_name" => $task->name, "task_id" => $task->id))); log::info("tasks", t("Task %task_name resumed (task id %task_id)", array("task_name" => $task->name, "task_id" => $task->id)), html::anchor("admin/maintenance", t("maintenance"))); @@ -86,6 +90,40 @@ class Admin_Maintenance_Controller extends Admin_Controller { } /** + * Show the task log + * @param string $task_id + */ + public function show_log($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_show_log.html"); + $view->task = $task; + + print $view; + } + + /** + * Save the task log + * @param string $task_id + */ + public function save_log($task_id) { + access::verify_csrf(); + + $task = ORM::factory("task", $task_id); + if (!$task->loaded) { + throw new Exception("@todo MISSING_TASK"); + } + + header("Content-Type: application/text"); + header("Content-Disposition: filename=gallery3_task_log.txt"); + print $task->get_log(); + } + + /** * Cancel a task. * @param string $task_id */ @@ -123,7 +161,14 @@ class Admin_Maintenance_Controller extends Admin_Controller { public function remove_finished_tasks() { access::verify_csrf(); - Database::instance()->delete("tasks", array("done" => 1)); + + // Do it the long way so we can call delete and remove the cache. + $finished = ORM::factory("task") + ->where(array("done" => 1)) + ->find_all(); + foreach ($finished as $task) { + task::remove($task->id); + } message::success(t("All finished tasks removed")); url::redirect("admin/maintenance"); } diff --git a/modules/gallery/helpers/gallery_block.php b/modules/gallery/helpers/gallery_block.php index a10f2bbf..b7816954 100644 --- a/modules/gallery/helpers/gallery_block.php +++ b/modules/gallery/helpers/gallery_block.php @@ -33,7 +33,7 @@ class gallery_block_Core { switch($block_id) { case "welcome": $block->css_id = "gWelcome"; - $block->title = t("Welcome to Gallery3"); + $block->title = t("Welcome to Gallery 3"); $block->content = new View("admin_block_welcome.html"); break; diff --git a/modules/gallery/helpers/gallery_task.php b/modules/gallery/helpers/gallery_task.php index 1152cda2..9ce2c4a0 100644 --- a/modules/gallery/helpers/gallery_task.php +++ b/modules/gallery/helpers/gallery_task.php @@ -45,131 +45,154 @@ class gallery_task_Core { * @param Task_Model the task */ static function rebuild_dirty_images($task) { - $result = graphics::find_dirty_images_query(); - $completed = $task->get("completed", 0); - $ignored = $task->get("ignored", array()); - $remaining = $result->count() - count($ignored); - - $i = 0; - foreach ($result as $row) { - if (array_key_exists($row->id, $ignored)) { - continue; - } + $message = array(); + try { + $result = graphics::find_dirty_images_query(); + $completed = $task->get("completed", 0); + $ignored = $task->get("ignored", array()); + $remaining = $result->count() - count($ignored); + + $i = 0; + foreach ($result as $row) { + if (array_key_exists($row->id, $ignored)) { + continue; + } - $item = ORM::factory("item", $row->id); - if ($item->loaded) { - $success = graphics::generate($item); - if (!$success) { - $ignored[$item->id] = 1; + $item = ORM::factory("item", $row->id); + if ($item->loaded) { + $success = graphics::generate($item); + if (!$success) { + $ignored[$item->id] = 1; + $message[] = t("Unable to rebuild images for '%title'", + array("title" => p::purify($item->title))); + } else { + $message[] = t("Successfully rebuilt images for '%title'", + array("title" => p::purify($item->title))); + } } - } - $completed++; - $remaining--; + $completed++; + $remaining--; - if (++$i == 2) { - break; + if (++$i == 2) { + break; + } } - } - $task->status = t2("Updated: 1 image. Total: %total_count.", - "Updated: %count images. Total: %total_count.", - $completed, - array("total_count" => ($remaining + $completed))); + $task->status = t2("Updated: 1 image. Total: %total_count.", + "Updated: %count images. Total: %total_count.", + $completed, + array("total_count" => ($remaining + $completed))); - if ($completed + $remaining > 0) { - $task->percent_complete = (int)(100 * $completed / ($completed + $remaining)); - } else { - $task->percent_complete = 100; - } + if ($completed + $remaining > 0) { + $task->percent_complete = (int)(100 * $completed / ($completed + $remaining)); + } else { + $task->percent_complete = 100; + } - $task->set("completed", $completed); - $task->set("ignored", $ignored); - if ($remaining == 0) { + $task->set("completed", $completed); + $task->set("ignored", $ignored); + if ($remaining == 0) { + $task->done = true; + $task->state = "success"; + site_status::clear("graphics_dirty"); + } + } catch (Exception $e) { $task->done = true; - $task->state = "success"; - site_status::clear("graphics_dirty"); + $task->state = "error"; + $task->status = $e->getMessage(); + $message[] = $e->__toString(); } + $task->log($message); } static function update_l10n(&$task) { - $start = microtime(true); - $dirs = $task->get("dirs"); - $files = $task->get("files"); - $cache = $task->get("cache", array()); - $i = 0; - - switch ($task->get("mode", "init")) { - case "init": // 0% - $dirs = array("gallery", "modules", "themes", "installer"); - $files = array(); - $task->set("mode", "find_files"); - $task->status = t("Finding files"); - break; - - case "find_files": // 0% - 10% - while (($dir = array_pop($dirs)) && microtime(true) - $start < 0.5) { - if (in_array(basename($dir), array("tests", "lib"))) { - continue; - } + $message = array(); + try { + $start = microtime(true); + $dirs = $task->get("dirs"); + $files = $task->get("files"); + $cache = $task->get("cache", array()); + $i = 0; + + switch ($task->get("mode", "init")) { + case "init": // 0% + $dirs = array("gallery", "modules", "themes", "installer"); + $files = array(); + $task->set("mode", "find_files"); + $task->status = t("Finding files"); + break; - foreach (glob(DOCROOT . "$dir/*") as $path) { - $relative_path = str_replace(DOCROOT, "", $path); - if (is_dir($path)) { - $dirs[] = $relative_path; - } else { - $files[] = $relative_path; + case "find_files": // 0% - 10% + while (($dir = array_pop($dirs)) && microtime(true) - $start < 0.5) { + if (in_array(basename($dir), array("tests", "lib"))) { + continue; + } + + foreach (glob(DOCROOT . "$dir/*") as $path) { + $relative_path = str_replace(DOCROOT, "", $path); + if (is_dir($path)) { + $dirs[] = $relative_path; + } else { + $files[] = $relative_path; + } } } - } - $task->status = t2("Finding files: found 1 file", - "Finding files: found %count files", count($files)); + $message[] = $task->status = t2("Finding files: found 1 file", + "Finding files: found %count files", count($files)); - if (!$dirs) { - $task->set("mode", "scan_files"); - $task->set("total_files", count($files)); - $task->status = t("Scanning files"); - $task->percent_complete = 10; - } - break; - - case "scan_files": // 10% - 90% - while (($file = array_pop($files)) && microtime(true) - $start < 0.5) { - $file = DOCROOT . $file; - switch (pathinfo($file, PATHINFO_EXTENSION)) { - case "php": - l10n_scanner::scan_php_file($file, $cache); - break; + if (!$dirs) { + $task->set("mode", "scan_files"); + $task->set("total_files", count($files)); + $task->status = t("Scanning files"); + $task->percent_complete = 10; + } + break; - case "info": - l10n_scanner::scan_info_file($file, $cache); - break; + case "scan_files": // 10% - 90% + while (($file = array_pop($files)) && microtime(true) - $start < 0.5) { + $file = DOCROOT . $file; + switch (pathinfo($file, PATHINFO_EXTENSION)) { + case "php": + l10n_scanner::scan_php_file($file, $cache); + break; + + case "info": + l10n_scanner::scan_info_file($file, $cache); + break; + } } - } - $total_files = $task->get("total_files"); - $task->status = t2("Scanning files: scanned 1 file", - "Scanning files: scanned %count files", $total_files - count($files)); + $total_files = $task->get("total_files"); + $message[] = $task->status = t2("Scanning files: scanned 1 file", + "Scanning files: scanned %count files", $total_files - count($files)); - $task->percent_complete = 10 + 80 * ($total_files - count($files)) / $total_files; - if (empty($files)) { - $task->set("mode", "fetch_updates"); - $task->status = t("Fetching updates"); - $task->percent_complete = 90; + $task->percent_complete = 10 + 80 * ($total_files - count($files)) / $total_files; + if (empty($files)) { + $task->set("mode", "fetch_updates"); + $task->status = t("Fetching updates"); + $task->percent_complete = 90; + } + break; + + case "fetch_updates": // 90% - 100% + $message = array_merge($message, l10n_client::fetch_updates()); + $task->done = true; + $task->state = "success"; + $task->status = t("Translations installed/updated"); + $task->percent_complete = 100; } - break; - case "fetch_updates": // 90% - 100% - l10n_client::fetch_updates(); + $task->set("files", $files); + $task->set("dirs", $dirs); + $task->set("cache", $cache); + } catch (Exception $e) { $task->done = true; - $task->state = "success"; - $task->status = t("Translations installed/updated"); - $task->percent_complete = 100; + $task->state = "error"; + $task->status = $e->getMessage(); + $message[] = $e->__toString(); } - - $task->set("files", $files); - $task->set("dirs", $dirs); - $task->set("cache", $cache); + $task->log($message); } }
\ No newline at end of file diff --git a/modules/gallery/helpers/l10n_client.php b/modules/gallery/helpers/l10n_client.php index e153532c..6d4da0eb 100644 --- a/modules/gallery/helpers/l10n_client.php +++ b/modules/gallery/helpers/l10n_client.php @@ -67,6 +67,9 @@ class l10n_client_Core { return true; } + /** + * @return an array of messages that will be written to the task log + */ static function fetch_updates() { $request->locales = array(); $request->messages = new stdClass(); @@ -101,8 +104,7 @@ class l10n_client_Core { throw new Exception("@todo TRANSLATIONS_FETCH_REQUEST_FAILED " . $response_status); } if (empty($response_data)) { - log::info("translations", "Translations fetch request resulted in an empty response"); - return; + return array(t("Translations fetch request resulted in an empty response")); } $response = json_decode($response_data); @@ -112,9 +114,8 @@ class l10n_client_Core { // {key:<key_2>, ...} // ] $count = count($response); - log::info("translations", - t2("Installed 1 new / updated translation message", - "Installed %count new / updated translation messages", $count)); + $message[] = t2("Installed 1 new / updated translation message", + "Installed %count new / updated translation messages", $count); foreach ($response as $message_data) { // @todo Better input validation @@ -152,6 +153,7 @@ class l10n_client_Core { $entry->translation = $translation; $entry->save(); } + return $message; } static function submit_translations() { diff --git a/modules/gallery/helpers/task.php b/modules/gallery/helpers/task.php index a8a004ab..6a9f63c2 100644 --- a/modules/gallery/helpers/task.php +++ b/modules/gallery/helpers/task.php @@ -56,6 +56,8 @@ class task_Core { } $task->done = 1; $task->state = "cancelled"; + $task->log(t("Task %task_name cancelled (task id %task_id)", + array("task_name" => $task->name, "task_id" => $task->id))); $task->save(); return $task; @@ -74,9 +76,20 @@ class task_Core { throw new Exception("@todo MISSING_TASK"); } - $task->state = "running"; - call_user_func_array($task->callback, array(&$task)); - $task->save(); + try { + $task->state = "running"; + call_user_func_array($task->callback, array(&$task)); + if ($task->done) { + $task->log($task->status); + } + $task->save(); + } catch (Exception $e) { + $task->log($e->__toString()); + $task->state = "error"; + $task->done = true; + $task->status = $e->getMessage(); + $task->save(); + } return $task; } diff --git a/modules/gallery/models/task.php b/modules/gallery/models/task.php index 9e3ae5c6..b7e255a2 100644 --- a/modules/gallery/models/task.php +++ b/modules/gallery/models/task.php @@ -40,7 +40,47 @@ class Task_Model extends ORM { return parent::save(); } + public function delete() { + Cache::instance()->delete($this->_cache_key()); + return parent::delete(); + } + public function owner() { return user::lookup($this->owner_id); } + + /** + * Log a message to the task log. + * @params $msg mixed a string or array of strings + */ + public function log($msg) { + $key = $this->_cache_key(); + $log = Cache::instance()->get($key); + + if (is_array($msg)) { + $msg = implode("\n", $msg); + } + + // Save for 30 days. + $log .= !empty($log) ? "\n" : ""; + Cache::instance()->set($key, "$log{$msg}", + array("task", "log", "import"), 2592000); + } + + /** + * Retrieve the cached log information for this task. + * @returns the log data or null if there is no log data + */ + public function get_log() { + $log_data = Cache::instance()->get($this->_cache_key()); + return $log_data !== null ? $log_data : false; + } + + /** + * Build the task cache key + * @returns the key to use in access the cache + */ + private function _cache_key() { + return md5("$this->id; $this->name; $this->callback"); + } }
\ No newline at end of file diff --git a/modules/gallery/views/admin_maintenance.html.php b/modules/gallery/views/admin_maintenance.html.php index c47f77f8..cd1cc02e 100644 --- a/modules/gallery/views/admin_maintenance.html.php +++ b/modules/gallery/views/admin_maintenance.html.php @@ -29,7 +29,7 @@ </td> <td> <a href="<?= url::site("admin/maintenance/start/$task->callback?csrf=$csrf") ?>" - class="gDialogLink"> + class="gDialogLink gButtonLink ui-icon-left ui-state-default ui-corner-all"> <?= t("run") ?> </a> </td> @@ -94,11 +94,13 @@ </td> <td> <? if ($task->state == "stalled"): ?> - <a class="gDialogLink" href="<?= url::site("admin/maintenance/resume/$task->id?csrf=$csrf") ?>"> + <a class="gDialogLink gButtonLink ui-icon-left ui-state-default ui-corner-all" + href="<?= url::site("admin/maintenance/resume/$task->id?csrf=$csrf") ?>"> <?= t("resume") ?> </a> <? endif ?> - <a href="<?= url::site("admin/maintenance/cancel/$task->id?csrf=$csrf") ?>"> + <a href="<?= url::site("admin/maintenance/cancel/$task->id?csrf=$csrf") ?>" + class="gButtonLink ui-icon-left ui-state-default ui-corner-all right"> <?= t("cancel") ?> </a> </td> @@ -161,17 +163,23 @@ </td> <td> <? if ($task->done): ?> - <a href="<?= url::site("admin/maintenance/remove/$task->id?csrf=$csrf") ?>"> + <a href="<?= url::site("admin/maintenance/remove/$task->id?csrf=$csrf") ?>" class="gButtonLink ui-state-default ui-corner-all"> <?= t("remove") ?> </a> + <? if ($task->get_log()): ?> + <a href="<?= url::site("admin/maintenance/show_log/$task->id?csrf=$csrf") ?>" class="gDialogLink gButtonLink ui-state-default ui-corner-all"> + <?= t("browse log") ?> + </a> + <? endif ?> <? else: ?> - <a class="gDialogLink" href="<?= url::site("admin/maintenance/resume/$task->id?csrf=$csrf") ?>"> + <a href="<?= url::site("admin/maintenance/resume/$task->id?csrf=$csrf") ?>" class="gDialogLink gButtonLink" ui-state-default ui-corner-all> <?= t("resume") ?> </a> - <a href="<?= url::site("admin/maintenance/cancel/$task->id?csrf=$csrf") ?>"> + <a href="<?= url::site("admin/maintenance/cancel/$task->id?csrf=$csrf") ?>" class="gButtonLink ui-state-default ui-corner-all"> <?= t("cancel") ?> </a> <? endif ?> + </ul> </td> </tr> <? endforeach ?> diff --git a/modules/gallery/views/admin_maintenance_show_log.html.php b/modules/gallery/views/admin_maintenance_show_log.html.php new file mode 100644 index 00000000..9d850986 --- /dev/null +++ b/modules/gallery/views/admin_maintenance_show_log.html.php @@ -0,0 +1,19 @@ +<?php defined("SYSPATH") or die("No direct script access.") ?> +<script type="text/javascript"> + dismiss = function() { + window.location.reload(); + } + download = function(){ + // send request + $('<form action="<?= url::site("admin/maintenance/save_log/$task->id?csrf=$csrf") ?>" method="post"></form>'). +appendTo('body').submit().remove(); + }; +</script> +<div id="gTaskLogDialog"> + <h1> <?= $task->name ?> </h1> + <div class="gTaskLog"> + <pre><?= p::purify($task->get_log()) ?></pre> + </div> + <button id="gCloseButton" class="ui-state-default ui-corner-all" onclick="dismiss()"><?= t("Close") ?></button> + <button id="gSaveButton" class="ui-state-default ui-corner-all" onclick="download()"><?= t("Save") ?></button> +</div> diff --git a/modules/gallery/views/kohana_error_page.php b/modules/gallery/views/kohana_error_page.php index 6bf48549..9361514d 100644 --- a/modules/gallery/views/kohana_error_page.php +++ b/modules/gallery/views/kohana_error_page.php @@ -53,7 +53,6 @@ margin: 0px; } </style> - <script src="<?= url::file("lib/jquery.js") ?>" type="text/javascript"></script> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title><?= t("Something went wrong!") ?></title> </head> @@ -78,8 +77,13 @@ <h2> <?= t("Hey wait, you're an admin! We can tell you stuff.") ?> </h2> - <a id="toggle" href="" - onclick="javascript:$('#stuff').slideDown('slow'); $('#toggle').slideUp(); return false"> + <script type="text/javascript"> + var show_details = function() { + document.getElementById("stuff").style.display = "block"; + document.getElementById("toggle").style.display = "none"; + } + </script> + <a id="toggle" href="#" onclick="javascript:show_details(); return false;"> <b><?= t("Ok.. tell me stuff!") ?></b> </a> <div id="stuff" style="display: none"> diff --git a/modules/gallery/views/upgrader.html.php b/modules/gallery/views/upgrader.html.php index f9e242a8..37578855 100644 --- a/modules/gallery/views/upgrader.html.php +++ b/modules/gallery/views/upgrader.html.php @@ -1,7 +1,7 @@ <?php defined("SYSPATH") or die("No direct script access.") ?> <html> <head> - <title><?= t("Gallery3 Upgrader") ?></title> + <title><?= t("Gallery 3 Upgrader") ?></title> <link rel="stylesheet" type="text/css" href="<?= url::file("modules/gallery/css/upgrader.css") ?>" media="screen,print,projection" /> <script src="<?= url::file("lib/jquery.js") ?>" type="text/javascript"></script> diff --git a/modules/recaptcha/libraries/Form_Recaptcha.php b/modules/recaptcha/libraries/Form_Recaptcha.php index 4c0c4997..f4e9d4ac 100644 --- a/modules/recaptcha/libraries/Form_Recaptcha.php +++ b/modules/recaptcha/libraries/Form_Recaptcha.php @@ -28,7 +28,7 @@ class Form_Recaptcha_Core extends Form_Input { public function __construct($name) { parent::__construct($name); $this->error_messages("incorrect-captcha-sol", - t("The values supplied to recaptcha are incorrect.")); + t("The values supplied to reCAPTCHA are incorrect.")); $this->error_messages("invalid-site-private-key", t("The site private key is incorrect.")); } diff --git a/modules/recaptcha/views/admin_recaptcha.html.php b/modules/recaptcha/views/admin_recaptcha.html.php index 9c2911ef..43b4da8a 100644 --- a/modules/recaptcha/views/admin_recaptcha.html.php +++ b/modules/recaptcha/views/admin_recaptcha.html.php @@ -14,7 +14,7 @@ <div id="gAdminRecaptchaTest" class="gBlock"> <h2> <?= t("reCAPTCHA Test") ?> </h2> <p> - <?= t("If you see a captcha form below, then reCAPTCHA is functioning properly.") ?> + <?= t("If you see a CAPTCHA form below, then reCAPTCHA is functioning properly.") ?> </p> <div id="gRecaptcha"/> diff --git a/modules/search/helpers/search.php b/modules/search/helpers/search.php index ea8dad81..34eaecbd 100644 --- a/modules/search/helpers/search.php +++ b/modules/search/helpers/search.php @@ -50,6 +50,9 @@ class search_Core { return array($count, new ORM_Iterator(ORM::factory("item"), $db->query($query))); } + /** + * @return string An error message suitable for inclusion in the task log + */ static function check_index() { list ($remaining) = search::stats(); if ($remaining) { @@ -76,6 +79,7 @@ class search_Core { $record->data = join(" ", $data); $record->dirty = 0; $record->save(); + return t("Search index updated for '%title'", array("title" => p::purify($item->title))); } static function stats() { diff --git a/modules/search/helpers/search_task.php b/modules/search/helpers/search_task.php index 876661e4..395bcd98 100644 --- a/modules/search/helpers/search_task.php +++ b/modules/search/helpers/search_task.php @@ -39,34 +39,42 @@ class search_task_Core { } static function update_index($task) { - $completed = $task->get("completed", 0); + try { + $completed = $task->get("completed", 0); - $start = microtime(true); - foreach (ORM::factory("item") - ->join("search_records", "items.id", "search_records.item_id", "left") - ->where("search_records.item_id", null) - ->orwhere("search_records.dirty", 1) - ->find_all() as $item) { - if (microtime(true) - $start > 1.5) { - break; - } + $start = microtime(true); + foreach (ORM::factory("item") + ->join("search_records", "items.id", "search_records.item_id", "left") + ->where("search_records.item_id", null) + ->orwhere("search_records.dirty", 1) + ->find_all() as $item) { + if (microtime(true) - $start > 1.5) { + break; + } - search::update($item); - $completed++; - } + $message[] = search::update($item); + $completed++; + } - list ($remaining, $total, $percent) = search::stats(); - $task->set("completed", $completed); - if ($remaining == 0 || !($remaining + $completed)) { + list ($remaining, $total, $percent) = search::stats(); + $task->set("completed", $completed); + if ($remaining == 0 || !($remaining + $completed)) { + $task->done = true; + $task->state = "success"; + site_status::clear("search_index_out_of_date"); + $task->percent_complete = 100; + } else { + $task->percent_complete = round(100 * $completed / ($remaining + $completed)); + } + $task->status = t2("one record updated, index is %percent% up-to-date", + "%count records updated, index is %percent% up-to-date", + $completed, array("percent" => $percent)); + } catch (Exception $e) { $task->done = true; - $task->state = "success"; - site_status::clear("search_index_out_of_date"); - $task->percent_complete = 100; - } else { - $task->percent_complete = round(100 * $completed / ($remaining + $completed)); + $task->state = "error"; + $task->status = $e->getMessage(); + $message[] = $e->__toString(); } - $task->status = t2("one record updated, index is %percent% up-to-date", - "%count records updated, index is %percent% up-to-date", - $completed, array("percent" => $percent)); + $task->log($message); } } diff --git a/themes/admin_default/css/screen.css b/themes/admin_default/css/screen.css index e68620a5..d408acf0 100644 --- a/themes/admin_default/css/screen.css +++ b/themes/admin_default/css/screen.css @@ -400,6 +400,20 @@ li.gDefaultGroup h4, li.gDefaultGroup .gUser { float: left; } +#gTaskLogDialog h1 { + font-size: 1.1em; +} + +.gTaskLog { + border: 1pt solid; + font-size: .9em; + height: 400px; + margin: .5em 0; + overflow: auto; + padding: .5em +} + + /** ******************************************************************* * 7) Server Add *********************************************************************/ |