summaryrefslogtreecommitdiff
path: root/modules/gallery/helpers
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gallery/helpers')
-rw-r--r--modules/gallery/helpers/access.php17
-rw-r--r--modules/gallery/helpers/album.php2
-rw-r--r--modules/gallery/helpers/gallery.php14
-rw-r--r--modules/gallery/helpers/gallery_event.php66
-rw-r--r--modules/gallery/helpers/gallery_installer.php10
-rw-r--r--modules/gallery/helpers/gallery_task.php287
-rw-r--r--modules/gallery/helpers/gallery_theme.php3
-rw-r--r--modules/gallery/helpers/graphics.php13
-rw-r--r--modules/gallery/helpers/item.php14
-rw-r--r--modules/gallery/helpers/json.php33
-rw-r--r--modules/gallery/helpers/l10n_client.php16
-rw-r--r--modules/gallery/helpers/movie.php2
12 files changed, 387 insertions, 90 deletions
diff --git a/modules/gallery/helpers/access.php b/modules/gallery/helpers/access.php
index 87b6b313..f1ea00c0 100644
--- a/modules/gallery/helpers/access.php
+++ b/modules/gallery/helpers/access.php
@@ -222,7 +222,7 @@ class access_Core {
self::_update_access_non_view_cache($group, $perm_name, $album);
}
- self::_update_htaccess_files($album, $group, $perm_name, $value);
+ self::update_htaccess_files($album, $group, $perm_name, $value);
model_cache::clear();
}
@@ -623,11 +623,18 @@ class access_Core {
}
/**
- * Maintain .htacccess files to prevent direct access to albums, resizes and thumbnails when we
- * apply the view and view_full permissions to guest users.
+ * Rebuild the .htaccess files that prevent direct access to albums, resizes and thumbnails. We
+ * call this internally any time we change the view or view_full permissions for guest users.
+ * This function is only public because we use it in maintenance tasks.
+ *
+ * @param Item_Model the album
+ * @param Group_Model the group whose permission is changing
+ * @param string the permission name
+ * @param string the new permission value (eg access::DENY)
*/
- private static function _update_htaccess_files($album, $group, $perm_name, $value) {
- if ($group->id != 1 || !($perm_name == "view" || $perm_name == "view_full")) {
+ static function update_htaccess_files($album, $group, $perm_name, $value) {
+ if ($group->id != identity::everybody()->id ||
+ !($perm_name == "view" || $perm_name == "view_full")) {
return;
}
diff --git a/modules/gallery/helpers/album.php b/modules/gallery/helpers/album.php
index 0baae631..0ac5e8b0 100644
--- a/modules/gallery/helpers/album.php
+++ b/modules/gallery/helpers/album.php
@@ -58,7 +58,7 @@ class album_Core {
static function get_edit_form($parent) {
$form = new Forge(
"albums/update/{$parent->id}", "", "post", array("id" => "g-edit-album-form"));
- $form->hidden("from_id");
+ $form->hidden("from_id")->value($parent->id);
$group = $form->group("edit_item")->label(t("Edit Album"));
$group->input("title")->label(t("Title"))->value($parent->title)
diff --git a/modules/gallery/helpers/gallery.php b/modules/gallery/helpers/gallery.php
index d4078209..54d16322 100644
--- a/modules/gallery/helpers/gallery.php
+++ b/modules/gallery/helpers/gallery.php
@@ -25,12 +25,14 @@ class gallery_Core {
* down for maintenance" page.
*/
static function maintenance_mode() {
- $maintenance_mode = Kohana::config("core.maintenance_mode", false, false);
-
- if (Router::$controller != "login" && !empty($maintenance_mode) && !identity::active_user()->admin) {
- Router::$controller = "maintenance";
- Router::$controller_path = MODPATH . "gallery/controllers/maintenance.php";
- Router::$method = "index";
+ if (Router::$controller != "login" &&
+ Router::$controller != "combined" &&
+ module::get_var("gallery", "maintenance_mode", 0) &&
+ !identity::active_user()->admin) {
+ Session::instance()->set("continue_url", url::abs_site("admin/maintenance"));
+ Router::$controller = "login";
+ Router::$controller_path = MODPATH . "gallery/controllers/login.php";
+ Router::$method = "html";
}
}
diff --git a/modules/gallery/helpers/gallery_event.php b/modules/gallery/helpers/gallery_event.php
index 272fd205..e3fa5e08 100644
--- a/modules/gallery/helpers/gallery_event.php
+++ b/modules/gallery/helpers/gallery_event.php
@@ -109,18 +109,44 @@ class gallery_event_Core {
$parent = $item->parent();
if (!$parent->album_cover_item_id) {
- // Assume we deleted the album cover and pick a new one. Choosing the first photo in the
- // album is logical, but it's not the most efficient in the case where we're deleting all
- // the photos in the album one at a time since we'll probably delete them in order which
- // means that we'll be resetting the album cover each time.
- if ($child = $parent->children(1)->current()) {
- item::make_album_cover($child);
+ // Assume that we deleted the album cover
+ if (batch::in_progress()) {
+ // Remember that this parent is missing an album cover, for later.
+ $batch_missing_album_cover = Session::instance()->get("batch_missing_album_cover", array());
+ $batch_missing_album_cover[$parent->id] = 1;
+ Session::instance()->set("batch_missing_album_cover", $batch_missing_album_cover);
+ } else {
+ // Choose the first child as the new cover.
+ if ($child = $parent->children(1)->current()) {
+ item::make_album_cover($child);
+ }
}
}
}
+ static function batch_complete() {
+ // Set the album covers for any items that where we probably deleted the album cover during
+ // this batch. The item may have been deleted, so don't count on it being around. Choose the
+ // first child as the new album cover.
+ // NOTE: if the first child doesn't have an album cover, then this won't work.
+ foreach (array_keys(Session::instance()->get("batch_missing_album_cover", array())) as $id) {
+ $item = ORM::factory("item", $id);
+ if ($item->loaded() && !$item->album_cover_item_id) {
+ if ($child = $item->children(1)->current()) {
+ item::make_album_cover($child);
+ }
+ }
+ }
+ Session::instance()->delete("batch_missing_album_cover");
+ }
+
static function item_moved($item, $old_parent) {
access::recalculate_permissions($item->parent());
+
+ // If the new parent doesn't have an album cover, make this it.
+ if (!$item->parent()->album_cover_item_id) {
+ item::make_album_cover($item);
+ }
}
static function user_login($user) {
@@ -167,7 +193,9 @@ class gallery_event_Core {
->url(user_profile::url($user->id))
->label($user->display_name()));
- if (isset($theme->item)) {
+ if (Router::$controller == "admin") {
+ $continue_url = url::site("");
+ } else if (isset($theme->item)) {
if (access::user_can(identity::guest(), "view", $theme->item)) {
$continue_url = $theme->item->abs_url();
} else {
@@ -180,8 +208,7 @@ class gallery_event_Core {
$menu->append(Menu::factory("link")
->id("user_menu_logout")
->css_id("g-logout-link")
- ->url(url::site("logout?csrf=$csrf&continue_url=" .
- urlencode($continue_url)))
+ ->url(url::site("logout?csrf=$csrf&continue_url=" . urlencode($continue_url)))
->label(t("Logout")));
}
}
@@ -211,7 +238,7 @@ class gallery_event_Core {
$add_menu->append(Menu::factory("dialog")
->id("add_photos_item")
->label(t("Add photos"))
- ->url(url::site("flash_uploader/app/$item->id")));
+ ->url(url::site("uploader/index/$item->id")));
if ($item->is_album()) {
$add_menu->append(Menu::factory("dialog")
->id("add_album_item")
@@ -249,7 +276,7 @@ class gallery_event_Core {
$options_menu->append(Menu::factory("dialog")
->id("edit_item")
->label($edit_text)
- ->url(url::site("form/edit/{$item->type}s/$item->id")));
+ ->url(url::site("form/edit/{$item->type}s/$item->id?from_id={$item->id}")));
}
if ($item->is_album()) {
@@ -263,7 +290,6 @@ class gallery_event_Core {
}
$csrf = access::csrf_token();
- $theme_item = $theme->item();
$page_type = $theme->page_type();
if ($can_edit && $item->is_photo() && graphics::can("rotate")) {
$options_menu
@@ -274,7 +300,7 @@ class gallery_event_Core {
->css_class("ui-icon-rotate-ccw")
->ajax_handler("function(data) { " .
"\$.gallery_replace_image(data, \$('$item_css_selector')) }")
- ->url(url::site("quick/rotate/$item->id/ccw?csrf=$csrf&from_id=$theme_item->id&page_type=$page_type")))
+ ->url(url::site("quick/rotate/$item->id/ccw?csrf=$csrf&from_id={$item->id}&page_type=$page_type")))
->append(
Menu::factory("ajax_link")
->id("rotate_cw")
@@ -282,7 +308,7 @@ class gallery_event_Core {
->css_class("ui-icon-rotate-cw")
->ajax_handler("function(data) { " .
"\$.gallery_replace_image(data, \$('$item_css_selector')) }")
- ->url(url::site("quick/rotate/$item->id/cw?csrf=$csrf&from_id=$theme_item->id&page_type=$page_type")));
+ ->url(url::site("quick/rotate/$item->id/cw?csrf=$csrf&from_id={$item->id}&page_type=$page_type")));
}
if ($item->id != item::root()->id) {
@@ -315,7 +341,7 @@ class gallery_event_Core {
->label($delete_text)
->css_class("ui-icon-trash")
->css_class("g-quick-delete")
- ->url(url::site("quick/form_delete/$item->id?csrf=$csrf&from_id=$theme_item->id&page_type=$page_type")));
+ ->url(url::site("quick/form_delete/$item->id?csrf=$csrf&from_id={$item->id}&page_type=$page_type")));
}
}
}
@@ -416,7 +442,7 @@ class gallery_event_Core {
->id("edit")
->label($edit_title)
->css_class("ui-icon-pencil")
- ->url(url::site("quick/form_edit/$item->id?from_id=$theme_item->id")));
+ ->url(url::site("quick/form_edit/$item->id?from_id={$theme_item->id}")));
if ($item->is_photo() && graphics::can("rotate")) {
$options_menu
@@ -427,7 +453,7 @@ class gallery_event_Core {
->css_class("ui-icon-rotate-ccw")
->ajax_handler("function(data) { " .
"\$.gallery_replace_image(data, \$('$thumb_css_selector')) }")
- ->url(url::site("quick/rotate/$item->id/ccw?csrf=$csrf&from_id=$theme_item->id&page_type=$page_type")))
+ ->url(url::site("quick/rotate/$item->id/ccw?csrf=$csrf&from_id={$theme_item->id}&page_type=$page_type")))
->append(
Menu::factory("ajax_link")
->id("rotate_cw")
@@ -435,7 +461,7 @@ class gallery_event_Core {
->css_class("ui-icon-rotate-cw")
->ajax_handler("function(data) { " .
"\$.gallery_replace_image(data, \$('$thumb_css_selector')) }")
- ->url(url::site("quick/rotate/$item->id/cw?csrf=$csrf&from_id=$theme_item->id&page_type=$page_type")));
+ ->url(url::site("quick/rotate/$item->id/cw?csrf=$csrf&from_id={$theme_item->id}&page_type=$page_type")));
}
// @todo Don't move photos from the photo page; we don't yet have a good way of redirecting
@@ -474,7 +500,7 @@ class gallery_event_Core {
->id("delete")
->label($delete_title)
->css_class("ui-icon-trash")
- ->url(url::site("quick/form_delete/$item->id?csrf=$csrf&from_id=$theme_item->id&page_type=$page_type")));
+ ->url(url::site("quick/form_delete/$item->id?csrf=$csrf&from_id={$theme_item->id}&page_type=$page_type")));
}
if ($item->is_album()) {
@@ -483,7 +509,7 @@ class gallery_event_Core {
->id("add_item")
->label(t("Add a photo"))
->css_class("ui-icon-plus")
- ->url(url::site("flash_uploader/app/$item->id")))
+ ->url(url::site("uploader/index/$item->id")))
->append(Menu::factory("dialog")
->id("add_album")
->label(t("Add an album"))
diff --git a/modules/gallery/helpers/gallery_installer.php b/modules/gallery/helpers/gallery_installer.php
index 39c35711..f5589618 100644
--- a/modules/gallery/helpers/gallery_installer.php
+++ b/modules/gallery/helpers/gallery_installer.php
@@ -295,7 +295,8 @@ class gallery_installer {
module::set_var("gallery", "credits", (string) $powered_by_string);
module::set_var("gallery", "simultaneous_upload_limit", 5);
module::set_var("gallery", "admin_area_timeout", 90 * 60);
- module::set_version("gallery", 30);
+ module::set_var("gallery", "maintenance_mode", 0);
+ module::set_version("gallery", 31);
}
static function upgrade($version) {
@@ -554,7 +555,12 @@ class gallery_installer {
if ($version == 29) {
$db->query("ALTER TABLE {caches} ADD KEY (`key`);");
module::set_version("gallery", $version = 30);
- }
+ }
+
+ if ($version == 30) {
+ module::set_var("gallery", "maintenance_mode", 0);
+ module::set_version("gallery", $version = 31);
+ }
}
static function uninstall() {
diff --git a/modules/gallery/helpers/gallery_task.php b/modules/gallery/helpers/gallery_task.php
index bc128b3e..bf1355b8 100644
--- a/modules/gallery/helpers/gallery_task.php
+++ b/modules/gallery/helpers/gallery_task.php
@@ -18,8 +18,15 @@
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
class gallery_task_Core {
- const MPTT_LEFT = 0;
- const MPTT_RIGHT = 1;
+ const FIX_STATE_START_MPTT = 0;
+ const FIX_STATE_RUN_MPTT = 1;
+ const FIX_STATE_START_ALBUMS = 2;
+ const FIX_STATE_RUN_ALBUMS = 3;
+ const FIX_STATE_START_DUPE_SLUGS = 4;
+ const FIX_STATE_RUN_DUPE_SLUGS = 5;
+ const FIX_STATE_START_DUPE_NAMES = 6;
+ const FIX_STATE_RUN_DUPE_NAMES = 7;
+ const FIX_STATE_DONE = 8;
static function available_tasks() {
$dirty_count = graphics::find_dirty_images_query()->count_records();
@@ -47,10 +54,10 @@ class gallery_task_Core {
->severity(log::SUCCESS);
$tasks[] = Task_Definition::factory()
- ->callback("gallery_task::fix_mptt")
- ->name(t("Fix Album/Photo hierarchy"))
- ->description(t("Fix problems where your album/photo breadcrumbs are out of " .
- "sync with your actual hierarchy."))
+ ->callback("gallery_task::fix")
+ ->name(t("Fix your Gallery"))
+ ->description(t("Fix a variety of problems that might cause your Gallery to act " .
+ "strangely. Requires maintenance mode."))
->severity(log::SUCCESS);
return $tasks;
@@ -310,78 +317,274 @@ class gallery_task_Core {
}
}
- static function fix_mptt($task) {
+ static function fix($task) {
$start = microtime(true);
$total = $task->get("total");
if (empty($total)) {
- $task->set("total", $total = db::build()->count_records("items"));
- $task->set("stack", "1:" . self::MPTT_LEFT);
+ // mptt: 2 operations for every item
+ // album audit (permissions and bogus album covers): 1 operation for every album
+ // dupe slugs: 1 operation for each unique conflicted slug
+ $total = 2 * db::build()->count_records("items");
+ $total += db::build()->where("type", "=", "album")->count_records("items");
+ foreach (self::find_dupe_slugs() as $row) {
+ $total++;
+ }
+ foreach (self::find_dupe_names() as $row) {
+ $total++;
+ }
+
+ $task->set("total", $total);
+ $task->set("state", $state = self::FIX_STATE_START_MPTT);
$task->set("ptr", 1);
$task->set("completed", 0);
}
- $ptr = $task->get("ptr");
- $stack = explode(" ", $task->get("stack"));
$completed = $task->get("completed");
+ $state = $task->get("state");
- // Implement a depth-first tree walk using a stack. Not the most efficient, but it's simple.
- while ($stack && microtime(true) - $start < 1.5) {
- list($id, $state) = explode(":", array_pop($stack));
+ if (!module::get_var("gallery", "maintenance_mode")) {
+ module::set_var("gallery", "maintenance_mode", 1);
+ }
+
+ // This is a state machine that checks each item in the database. It verifies the following
+ // attributes for an item.
+ // 1. Left and right MPTT pointers are correct
+ // 2. The .htaccess permission files for restricted items exist and are well formed.
+ // 3. The relative_path_cache and relative_url_cache values are set to null.
+ // 4. there are no album_cover_item_ids pointing to missing items
+ //
+ // We'll do a depth-first tree walk over our hierarchy using only the adjacency data because
+ // we don't trust MPTT here (that might be what we're here to fix!). Avoid avoid using ORM
+ // calls as much as possible since they're expensive.
+ //
+ // NOTE: the MPTT check will only traverse items that have valid parents. It's possible that
+ // we have some tree corruption where there are items with parent ids to non-existent items.
+ // We should probably do something about that.
+ while ($state != self::FIX_STATE_DONE && microtime(true) - $start < 1.5) {
switch ($state) {
- case self::MPTT_LEFT:
- self::fix_mptt_set_left($id, $ptr++);
- $item = ORM::factory("item", $id);
- array_push($stack, $id . ":" . self::MPTT_RIGHT);
- foreach (self::fix_mptt_children($id) as $child) {
- array_push($stack, $child->id . ":" . self::MPTT_LEFT);
+ case self::FIX_STATE_START_MPTT:
+ $task->set("ptr", $ptr = 1);
+ $task->set("stack", item::root()->id . ":L");
+ $state = self::FIX_STATE_RUN_MPTT;
+ break;
+
+ case self::FIX_STATE_RUN_MPTT:
+ $ptr = $task->get("ptr");
+ $stack = explode(" ", $task->get("stack"));
+ list ($id, $ptr_mode) = explode(":", array_pop($stack));
+ if ($ptr_mode == "L") {
+ $stack[] = "$id:R";
+ db::build()
+ ->update("items")
+ ->set("left_ptr", $ptr++)
+ ->where("id", "=", $id)
+ ->execute();
+
+ foreach (db::build()
+ ->select(array("id"))
+ ->from("items")
+ ->where("parent_id", "=", $id)
+ ->order_by("left_ptr", "ASC")
+ ->execute() as $child) {
+ array_push($stack, "{$child->id}:L");
+ }
+ } else if ($ptr_mode == "R") {
+ db::build()
+ ->update("items")
+ ->set("right_ptr", $ptr++)
+ ->set("relative_path_cache", null)
+ ->set("relative_url_cache", null)
+ ->where("id", "=", $id)
+ ->execute();
+ }
+ $task->set("ptr", $ptr);
+ $task->set("stack", implode(" ", $stack));
+ $completed++;
+
+ if (empty($stack)) {
+ $state = self::FIX_STATE_START_DUPE_SLUGS;
+ }
+ break;
+
+
+ case self::FIX_STATE_START_DUPE_SLUGS:
+ $stack = array();
+ foreach (self::find_dupe_slugs() as $row) {
+ list ($parent_id, $slug) = explode(":", $row->parent_slug, 2);
+ $stack[] = join(":", array($parent_id, $slug));
+ }
+ if ($stack) {
+ $task->set("stack", implode(" ", $stack));
+ $state = self::FIX_STATE_RUN_DUPE_SLUGS;
+ } else {
+ $state = self::FIX_STATE_START_DUPE_NAMES;
}
break;
- case self::MPTT_RIGHT:
- self::fix_mptt_set_right($id, $ptr++);
+ case self::FIX_STATE_RUN_DUPE_SLUGS:
+ $stack = explode(" ", $task->get("stack"));
+ list ($parent_id, $slug) = explode(":", array_pop($stack));
+
+ // We want to leave the first one alone and update all conflicts to be random values.
+ $fixed = 0;
+ $conflicts = ORM::factory("item")
+ ->where("parent_id", "=", $parent_id)
+ ->where("slug", "=", $slug)
+ ->find_all(1, 1);
+ if ($conflicts->count() && $conflict = $conflicts->current()) {
+ $task->log("Fixing conflicting slug for item id {$conflict->id}");
+ db::build()
+ ->update("items")
+ ->set("slug", $slug . "-" . (string)rand(1000, 9999))
+ ->where("id", "=", $conflict->id)
+ ->execute();
+
+ // We fixed one conflict, but there might be more so put this parent back on the stack
+ // and try again. We won't consider it completed when we don't fix a conflict. This
+ // guarantees that we won't spend too long fixing one set of conflicts, and that we
+ // won't stop before all are fixed.
+ $stack[] = "$parent_id:$slug";
+ break;
+ }
+ $task->set("stack", implode(" ", $stack));
$completed++;
+
+ if (empty($stack)) {
+ $state = self::FIX_STATE_START_DUPE_NAMES;
+ }
+ break;
+
+ case self::FIX_STATE_START_DUPE_NAMES:
+ $stack = array();
+ foreach (self::find_dupe_names() as $row) {
+ list ($parent_id, $name) = explode(":", $row->parent_name, 2);
+ $stack[] = join(":", array($parent_id, $name));
+ }
+ if ($stack) {
+ $task->set("stack", implode(" ", $stack));
+ $state = self::FIX_STATE_RUN_DUPE_NAMES;
+ } else {
+ $state = self::FIX_STATE_START_ALBUMS;
+ }
+ break;
+
+ case self::FIX_STATE_RUN_DUPE_NAMES:
+ $stack = explode(" ", $task->get("stack"));
+ list ($parent_id, $name) = explode(":", array_pop($stack));
+
+ $fixed = 0;
+ // We want to leave the first one alone and update all conflicts to be random values.
+ $conflicts = ORM::factory("item")
+ ->where("parent_id", "=", $parent_id)
+ ->where("name", "=", $name)
+ ->find_all(1, 1);
+ if ($conflicts->count() && $conflict = $conflicts->current()) {
+ $task->log("Fixing conflicting name for item id {$conflict->id}");
+ db::build()
+ ->update("items")
+ ->set("name", $name . "-" . (string)rand(1000, 9999))
+ ->where("id", "=", $conflict->id)
+ ->execute();
+
+ // We fixed one conflict, but there might be more so put this parent back on the stack
+ // and try again. We won't consider it completed when we don't fix a conflict. This
+ // guarantees that we won't spend too long fixing one set of conflicts, and that we
+ // won't stop before all are fixed.
+ $stack[] = "$parent_id:$name";
+ break;
+ }
+ $task->set("stack", implode(" ", $stack));
+ $completed++;
+
+ if (empty($stack)) {
+ $state = self::FIX_STATE_START_ALBUMS;
+ }
+ break;
+
+ case self::FIX_STATE_START_ALBUMS:
+ $stack = array();
+ foreach (db::build()
+ ->select("id")
+ ->from("items")
+ ->where("type", "=", "album")
+ ->execute() as $row) {
+ $stack[] = $row->id;
+ }
+ $task->set("stack", implode(" ", $stack));
+ $state = self::FIX_STATE_RUN_ALBUMS;
+ break;
+
+ case self::FIX_STATE_RUN_ALBUMS:
+ $stack = explode(" ", $task->get("stack"));
+ $id = array_pop($stack);
+
+ $item = ORM::factory("item", $id);
+ if ($item->album_cover_item_id) {
+ $album_cover_item = ORM::factory("item", $item->album_cover_item_id);
+ if (!$album_cover_item->loaded()) {
+ $item->album_cover_item_id = null;
+ $item->save();
+ }
+ }
+
+ $everybody = identity::everybody();
+ $view_col = "view_{$everybody->id}";
+ $view_full_col = "view_full_{$everybody->id}";
+ $intent = ORM::factory("access_intent")->where("item_id", "=", $id)->find();
+ if ($intent->$view_col === access::DENY) {
+ access::update_htaccess_files($item, $everybody, "view", access::DENY);
+ }
+ if ($intent->$view_full_col === access::DENY) {
+ access::update_htaccess_files($item, $everybody, "view_full", access::DENY);
+ }
+ $task->set("stack", implode(" ", $stack));
+ $completed++;
+
+ if (empty($stack)) {
+ $state = self::FIX_STATE_DONE;
+ }
break;
}
}
- $task->set("stack", implode(" ", $stack));
- $task->set("ptr", $ptr);
+ $task->set("state", $state);
$task->set("completed", $completed);
- if ($total == $completed) {
+ if ($state == self::FIX_STATE_DONE) {
$task->done = true;
$task->state = "success";
$task->percent_complete = 100;
+ module::set_var("gallery", "maintenance_mode", 0);
} else {
$task->percent_complete = round(100 * $completed / $total);
}
- $task->status = t2("One row updated", "%count / %total rows updated", $completed,
+ $task->status = t2("One operation complete", "%count / %total operations complete", $completed,
array("total" => $total));
}
- static function fix_mptt_children($parent_id) {
+ static function find_dupe_slugs() {
return db::build()
+ ->select_distinct(
+ array("parent_slug" => new Database_Expression("CONCAT(`parent_id`, ':', `slug`)")))
->select("id")
+ ->select(array("C" => "COUNT(\"*\")"))
->from("items")
- ->where("parent_id", "=", $parent_id)
- ->order_by("left_ptr", "ASC")
+ ->having("C", ">", 1)
+ ->group_by("parent_slug")
->execute();
}
- static function fix_mptt_set_left($id, $value) {
- db::build()
- ->update("items")
- ->set("left_ptr", $value)
- ->where("id", "=", $id)
- ->execute();
- }
-
- static function fix_mptt_set_right($id, $value) {
- db::build()
- ->update("items")
- ->set("right_ptr", $value)
- ->where("id", "=", $id)
+ static function find_dupe_names() {
+ return db::build()
+ ->select_distinct(
+ array("parent_name" => new Database_Expression("CONCAT(`parent_id`, ':', `name`)")))
+ ->select("id")
+ ->select(array("C" => "COUNT(\"*\")"))
+ ->from("items")
+ ->where("type", "<>", "album")
+ ->having("C", ">", 1)
+ ->group_by("parent_name")
->execute();
}
} \ No newline at end of file
diff --git a/modules/gallery/helpers/gallery_theme.php b/modules/gallery/helpers/gallery_theme.php
index 62deb831..0d7cc44a 100644
--- a/modules/gallery/helpers/gallery_theme.php
+++ b/modules/gallery/helpers/gallery_theme.php
@@ -79,8 +79,7 @@ class gallery_theme_Core {
return L10n_Client_Controller::l10n_form();
}
- if ($session->get("after_install")) {
- $session->delete("after_install");
+ if ($session->get_once("after_install")) {
return new View("welcome_message_loader.html");
}
}
diff --git a/modules/gallery/helpers/graphics.php b/modules/gallery/helpers/graphics.php
index 7ff09d13..cc4d2e76 100644
--- a/modules/gallery/helpers/graphics.php
+++ b/modules/gallery/helpers/graphics.php
@@ -117,7 +117,18 @@ class graphics_Core {
static function generate($item) {
if ($item->is_album()) {
if (!$cover = $item->album_cover()) {
- // This album has no cover; there's nothing to generate.
+ // This album has no cover; there's nothing to generate. Because of an old bug, it's
+ // possible that there's an album cover item id that points to an invalid item. In that
+ // case, just null out the album cover item id. It's not optimal to do that at this low
+ // level, but it's not trivial to find these cases quickly in an upgrade script and if we
+ // don't do this, the album may be permanently marked as "needs rebuilding"
+ //
+ // ref: http://sourceforge.net/apps/trac/gallery/ticket/1172
+ // http://gallery.menalto.com/node/96926
+ if ($item->album_cover_item_id) {
+ $item->album_cover_item_id = null;
+ $item->save();
+ }
return;
}
$input_file = $cover->file_path();
diff --git a/modules/gallery/helpers/item.php b/modules/gallery/helpers/item.php
index aef68c6e..092904a5 100644
--- a/modules/gallery/helpers/item.php
+++ b/modules/gallery/helpers/item.php
@@ -105,9 +105,15 @@ class item_Core {
model_cache::clear();
$parent->album_cover_item_id = $item->is_album() ? $item->album_cover_item_id : $item->id;
- $parent->thumb_dirty = 1;
+ if ($item->thumb_dirty) {
+ $parent->thumb_dirty = 1;
+ graphics::generate($parent);
+ } else {
+ copy($item->thumb_path(), $parent->thumb_path());
+ $parent->thumb_width = $item->thumb_width;
+ $parent->thumb_height = $item->thumb_height;
+ }
$parent->save();
- graphics::generate($parent);
$grand_parent = $parent->parent();
if ($grand_parent && access::can("edit", $grand_parent) &&
$grand_parent->album_cover_item_id == null) {
@@ -158,8 +164,10 @@ class item_Core {
*/
static function get_delete_form($item) {
$page_type = Input::instance()->get("page_type");
+ $from_id = Input::instance()->get("from_id");
$form = new Forge(
- "quick/delete/$item->id?page_type=$page_type", "", "post", array("id" => "g-confirm-delete"));
+ "quick/delete/$item->id?page_type=$page_type&from_id=$from_id", "",
+ "post", array("id" => "g-confirm-delete"));
$group = $form->group("confirm_delete")->label(t("Confirm Deletion"));
$group->submit("")->value(t("Delete"));
$form->script("")
diff --git a/modules/gallery/helpers/json.php b/modules/gallery/helpers/json.php
new file mode 100644
index 00000000..a39db27a
--- /dev/null
+++ b/modules/gallery/helpers/json.php
@@ -0,0 +1,33 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2010 Bharat Mediratta
+ *
+ * This program 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.
+ *
+ * This program 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+class json_Core {
+ /**
+ * JSON Encode a reply to the browser and set the content type to specify that it's a JSON
+ * payload.
+ *
+ * @param mixed $message string or object to json encode and print
+ */
+ static function reply($message) {
+ if (!headers_sent()) {
+ header("Content-Type: application/json; charset=" . Kohana::CHARSET);
+ }
+ print json_encode($message);
+ }
+} \ No newline at end of file
diff --git a/modules/gallery/helpers/l10n_client.php b/modules/gallery/helpers/l10n_client.php
index f45f502d..43cc2036 100644
--- a/modules/gallery/helpers/l10n_client.php
+++ b/modules/gallery/helpers/l10n_client.php
@@ -20,13 +20,12 @@
class l10n_client_Core {
- private static function _server_url() {
- return "http://gallery.menalto.com/index.php";
+ private static function _server_url($path) {
+ return "http://gallery.menalto.com/translations/$path";
}
static function server_api_key_url() {
- return self::_server_url() . "?q=translations/userkey/" .
- self::client_token();
+ return self::_server_url("userkey/" . self::client_token());
}
static function client_token() {
@@ -53,7 +52,7 @@ class l10n_client_Core {
static function validate_api_key($api_key) {
$version = "1.0";
- $url = self::_server_url() . "?q=translations/status";
+ $url = self::_server_url("status");
$signature = self::_sign($version, $api_key);
list ($response_data, $response_status) = remote::post(
@@ -123,7 +122,7 @@ class l10n_client_Core {
}
$request_data = json_encode($request);
- $url = self::_server_url() . "?q=translations/fetch";
+ $url = self::_server_url("fetch");
list ($response_data, $response_status) = remote::post($url, array("data" => $request_data));
if (!remote::success($response_status)) {
throw new Exception("@todo TRANSLATIONS_FETCH_REQUEST_FAILED " . $response_status);
@@ -195,12 +194,15 @@ class l10n_client_Core {
// @todo Batch requests (max request size)
// @todo include base_revision in submission / how to handle resubmissions / edit fights?
+ $request = new stdClass();
foreach (db::build()
->select("key", "message", "locale", "base_revision", "translation")
->from("outgoing_translations")
->execute() as $row) {
$key = $row->key;
if (!isset($request->{$key})) {
+ $request->{$key} = new stdClass();
+ $request->{$key}->translations = new stdClass();
$request->{$key}->message = json_encode(unserialize($row->message));
}
$request->{$key}->translations->{$row->locale} = json_encode(unserialize($row->translation));
@@ -208,7 +210,7 @@ class l10n_client_Core {
// @todo reduce memory consumption, e.g. free $request
$request_data = json_encode($request);
- $url = self::_server_url() . "?q=translations/submit";
+ $url = self::_server_url("submit");
$signature = self::_sign($request_data);
list ($response_data, $response_status) = remote::post(
diff --git a/modules/gallery/helpers/movie.php b/modules/gallery/helpers/movie.php
index 6333eaf2..bbb5b66c 100644
--- a/modules/gallery/helpers/movie.php
+++ b/modules/gallery/helpers/movie.php
@@ -26,7 +26,7 @@
class movie_Core {
static function get_edit_form($movie) {
$form = new Forge("movies/update/$movie->id", "", "post", array("id" => "g-edit-movie-form"));
- $form->hidden("from_id");
+ $form->hidden("from_id")->value($movie->id);
$group = $form->group("edit_item")->label(t("Edit Movie"));
$group->input("title")->label(t("Title"))->value($movie->title)
->error_messages("required", t("You must provide a title"))