diff options
Diffstat (limited to 'modules/gallery/helpers')
23 files changed, 966 insertions, 608 deletions
diff --git a/modules/gallery/helpers/MY_html.php b/modules/gallery/helpers/MY_html.php new file mode 100644 index 00000000..b29f287f --- /dev/null +++ b/modules/gallery/helpers/MY_html.php @@ -0,0 +1,91 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2009 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 html extends html_Core { + /** + * Returns a string that is safe to be used in HTML (XSS protection). + * + * If $html is a string, the returned string will be HTML escaped. + * If $html is a SafeString instance, the returned string may contain + * unescaped HTML which is assumed to be safe. + * + * Example:<pre> + * <div><?= html::clean($php_var) ?> + * </pre> + */ + static function clean($html) { + return new SafeString($html); + } + + /** + * Returns a string that is safe to be used in HTML (XSS protection), + * purifying (filtering) the given HTML to ensure that the result contains + * only non-malicious HTML. + * + * Example:<pre> + * <div><?= html::purify($item->title) ?> + * </pre> + */ + static function purify($html) { + return SafeString::purify($html); + } + + /** + * Flags the given string as safe to be used in HTML (free of malicious HTML/JS). + * + * Example:<pre> + * // Parameters to t() are automatically escaped by default. + * // If the parameter is marked as clean, it won't get escaped. + * t('Go <a href="%url">there</a>', + * array("url" => html::mark_clean(url::current()))) + * </pre> + */ + static function mark_clean($html) { + return SafeString::of_safe_html($html); + } + + /** + * Escapes the given string for use in JavaScript. + * + * Example:<pre> + * <script type="text/javascript>" + * var some_js_string = <?= html::js_string($php_string) ?>; + * </script> + * </pre> + */ + static function js_string($string) { + return SafeString::of($string)->for_js(); + } + + /** + * Returns a string safe for use in HTML element attributes. + * + * Assumes that the HTML element attribute is already + * delimited by single or double quotes + * + * Example:<pre> + * <a title="<?= html::clean_for_attribute($php_var) ?>">; + * </script> + * </pre> + * @return the string escaped for use in HTML attributes. + */ + static function clean_attribute($string) { + return self::clean($string)->for_html_attr(); + } +} diff --git a/modules/gallery/helpers/MY_url.php b/modules/gallery/helpers/MY_url.php index c4967c52..368c947e 100644 --- a/modules/gallery/helpers/MY_url.php +++ b/modules/gallery/helpers/MY_url.php @@ -18,21 +18,6 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ class url extends url_Core { - static function site($uri, $protocol=false) { - if (($pos = strpos($uri, "?")) !== false) { - list ($uri, $query) = explode("?", $uri, 2); - $query = "?$query"; - } else { - $query = ""; - } - - $parts = explode("/", $uri, 3); - if ($parts[0] == "albums" || $parts[0] == "photos") { - $uri = model_cache::get("item", $parts[1])->relative_path(); - } - return parent::site($uri . $query, $protocol); - } - static function parse_url() { if (Router::$controller) { return; @@ -55,20 +40,24 @@ class url extends url_Core { } /** - * Return the item that the uri is referencing + * Locate an item using the URI. We assume that the uri is in the form /a/b/c where each + * component matches up with an item slug. + * @param string $uri the uri fragment + * @return Item_Model */ static function get_item_from_uri($uri) { $current_uri = html_entity_decode($uri, ENT_QUOTES); - $item = ORM::factory("item")->where("relative_path_cache", $current_uri)->find(); + // In most cases, we'll have an exact match in the relative_url_cache item field. + // but failing that, walk down the tree until we find it. The fallback code will fix caches + // as it goes, so it'll never be run frequently. + $item = ORM::factory("item")->where("relative_url_cache", $current_uri)->find(); if (!$item->loaded) { - // It's possible that the relative path cache for the item we're looking for is out of date, - // so find it the hard way. $count = count(Router::$segments); foreach (ORM::factory("item") - ->where("name", html_entity_decode(Router::$segments[$count - 1], ENT_QUOTES)) + ->where("slug", html_entity_decode(Router::$segments[$count - 1], ENT_QUOTES)) ->where("level", $count + 1) ->find_all() as $match) { - if ($match->relative_path() == $current_uri) { + if ($match->relative_url() == $current_uri) { $item = $match; } } diff --git a/modules/gallery/helpers/access.php b/modules/gallery/helpers/access.php index c48f0b79..949aea84 100644 --- a/modules/gallery/helpers/access.php +++ b/modules/gallery/helpers/access.php @@ -66,9 +66,10 @@ * the Access_Intent_Model */ class access_Core { - const DENY = 0; - const ALLOW = 1; - const UNKNOWN = 2; + const DENY = false; + const ALLOW = true; + const INHERIT = null; // access_intent + const UNKNOWN = null; // cache (access_cache, items) /** * Does the active user have this permission on this item? @@ -78,18 +79,30 @@ class access_Core { * @return boolean */ static function can($perm_name, $item) { + return self::user_can(user::active(), $perm_name, $item); + } + + /** + * Does the user have this permission on this item? + * + * @param User_Model $user + * @param string $perm_name + * @param Item_Model $item + * @return boolean + */ + static function user_can($user, $perm_name, $item) { if (!$item->loaded) { return false; } - if (user::active()->admin) { + if ($user->admin) { return true; } $resource = $perm_name == "view" ? $item : model_cache::get("access_cache", $item->id, "item_id"); - foreach (user::group_ids() as $id) { - if ($resource->__get("{$perm_name}_$id") === self::ALLOW) { + foreach ($user->groups as $group) { + if ($resource->__get("{$perm_name}_{$group->id}") === self::ALLOW) { return true; } } @@ -129,7 +142,7 @@ class access_Core { * @param Group_Model $group * @param string $perm_name * @param Item_Model $item - * @return integer access::ALLOW, access::DENY or null for no intent + * @return boolean access::ALLOW, access::DENY or access::INHERIT (null) for no intent */ static function group_intent($group, $perm_name, $item) { $intent = model_cache::get("access_intent", $item->id, "item_id"); @@ -153,11 +166,11 @@ class access_Core { // For view permissions, if any parent is self::DENY, then those parents lock this one. // Return $lock = ORM::factory("item") - ->where("`left` <= $item->left") - ->where("`right` >= $item->right") + ->where("`left_ptr` <= $item->left_ptr") + ->where("`right_ptr` >= $item->right_ptr") ->where("items.id <> $item->id") ->join("access_intents", "items.id", "access_intents.item_id") - ->where("access_intents.view_$group->id", 0) + ->where("access_intents.view_$group->id", self::DENY) ->orderby("level", "DESC") ->limit(1) ->find(); @@ -205,6 +218,7 @@ class access_Core { } self::_update_htaccess_files($album, $group, $perm_name, $value); + model_cache::clear(); } /** @@ -240,7 +254,23 @@ class access_Core { if ($item->id == 1) { throw new Exception("@todo CANT_RESET_ROOT_PERMISSION"); } - self::_set($group, $perm_name, $item, null); + self::_set($group, $perm_name, $item, self::INHERIT); + } + + /** + * Recalculate the permissions for a given item and its hierarchy. $item must be an album. + */ + static function recalculate_permissions($item) { + foreach (self::_get_all_groups() as $group) { + foreach (ORM::factory("permission")->find_all() as $perm) { + if ($perm->name == "view") { + self::_update_access_view_cache($group, $item); + } else { + self::_update_access_non_view_cache($group, $perm->name, $item); + } + } + } + model_cache::clear(); } /** @@ -411,6 +441,7 @@ class access_Core { $cache_table = $perm_name == "view" ? "items" : "access_caches"; $db->query("ALTER TABLE {{$cache_table}} DROP `$field`"); $db->query("ALTER TABLE {access_intents} DROP `$field`"); + model_cache::clear(); ORM::factory("access_intent")->clear_cache(); } @@ -425,9 +456,11 @@ class access_Core { $db = Database::instance(); $field = "{$perm_name}_{$group->id}"; $cache_table = $perm_name == "view" ? "items" : "access_caches"; - $db->query("ALTER TABLE {{$cache_table}} ADD `$field` SMALLINT NOT NULL DEFAULT 0"); - $db->query("ALTER TABLE {access_intents} ADD `$field` BOOLEAN DEFAULT NULL"); - $db->update("access_intents", array($field => 0), array("item_id" => 1)); + $not_null = $cache_table == "items" ? "" : "NOT NULL"; + $db->query("ALTER TABLE {{$cache_table}} ADD `$field` BINARY $not_null DEFAULT FALSE"); + $db->query("ALTER TABLE {access_intents} ADD `$field` BINARY DEFAULT NULL"); + $db->update("access_intents", array($field => self::DENY), array("item_id" => 1)); + model_cache::clear(); ORM::factory("access_intent")->clear_cache(); } @@ -457,11 +490,11 @@ class access_Core { // item, then its safe to propagate from here. if ($access->$field !== self::DENY) { $tmp_item = ORM::factory("item") - ->where("left <", $item->left) - ->where("right >", $item->right) + ->where("left_ptr <", $item->left_ptr) + ->where("right_ptr >", $item->right_ptr) ->join("access_intents", "access_intents.item_id", "items.id") ->where("access_intents.$field", self::DENY) - ->orderby("left", "DESC") + ->orderby("left_ptr", "DESC") ->limit(1) ->find(); if ($tmp_item->loaded) { @@ -474,26 +507,26 @@ class access_Core { // them according the rule above. So mark every permission below this level as UNKNOWN so // that we can tell which permissions have been changed, and which ones need to be updated. $db->update("items", array($field => self::UNKNOWN), - array("left >=" => $item->left, "right <=" => $item->right)); + array("left_ptr >=" => $item->left_ptr, "right_ptr <=" => $item->right_ptr)); $query = ORM::factory("access_intent") - ->select(array("access_intents.$field", "items.left", "items.right", "items.id")) + ->select(array("access_intents.$field", "items.left_ptr", "items.right_ptr", "items.id")) ->join("items", "items.id", "access_intents.item_id") - ->where("left >=", $item->left) - ->where("right <=", $item->right) + ->where("left_ptr >=", $item->left_ptr) + ->where("right_ptr <=", $item->right_ptr) ->where("type", "album") - ->where("access_intents.$field IS NOT", null) + ->where("access_intents.$field IS NOT", self::INHERIT) ->orderby("level", "DESC") ->find_all(); foreach ($query as $row) { if ($row->$field == self::ALLOW) { // Propagate ALLOW for any row that is still UNKNOWN. $db->update("items", array($field => $row->$field), - array($field => self::UNKNOWN, "left >=" => $row->left, "right <=" => $row->right)); + array($field => self::UNKNOWN, "left_ptr >=" => $row->left_ptr, "right_ptr <=" => $row->right_ptr)); } else if ($row->$field == self::DENY) { // DENY overwrites everything below it $db->update("items", array($field => $row->$field), - array("left >=" => $row->left, "right <=" => $row->right)); + array("left_ptr >=" => $row->left_ptr, "right_ptr <=" => $row->right_ptr)); } } @@ -501,7 +534,7 @@ class access_Core { // DENY parent in the hierarchy to propagate from. So we'll still have a UNKNOWN values in // the hierarchy, and all of those are safe to change to ALLOW. $db->update("items", array($field => self::ALLOW), - array($field => self::UNKNOWN, "left >=" => $item->left, "right <=" => $item->right)); + array($field => self::UNKNOWN, "left_ptr >=" => $item->left_ptr, "right_ptr <=" => $item->right_ptr)); } /** @@ -526,13 +559,13 @@ class access_Core { // // @todo To optimize this, we wouldn't need to propagate from the parent, we could just // propagate from here with the parent's intent. - if ($access->$field === null) { + if ($access->$field === self::INHERIT) { $tmp_item = ORM::factory("item") ->join("access_intents", "items.id", "access_intents.item_id") - ->where("left <", $item->left) - ->where("right >", $item->right) - ->where("$field IS NOT", null) - ->orderby("left", "DESC") + ->where("left_ptr <", $item->left_ptr) + ->where("right_ptr >", $item->right_ptr) + ->where("$field IS NOT", self::UNKNOWN) + ->orderby("left_ptr", "DESC") ->limit(1) ->find(); if ($tmp_item->loaded) { @@ -543,20 +576,21 @@ class access_Core { // With non-view permissions, each level can override any permissions that came above it // so start at the top and work downwards, overlaying permissions as we go. $query = ORM::factory("access_intent") - ->select(array("access_intents.$field", "items.left", "items.right")) + ->select(array("access_intents.$field", "items.left_ptr", "items.right_ptr")) ->join("items", "items.id", "access_intents.item_id") - ->where("left >=", $item->left) - ->where("right <=", $item->right) - ->where("$field IS NOT", null) + ->where("left_ptr >=", $item->left_ptr) + ->where("right_ptr <=", $item->right_ptr) + ->where("$field IS NOT", self::INHERIT) ->orderby("level", "ASC") ->find_all(); foreach ($query as $row) { + $value = ($row->$field === self::ALLOW) ? "TRUE" : "FALSE"; $db->query( - "UPDATE {access_caches} SET `$field` = {$row->$field} " . + "UPDATE {access_caches} SET `$field` = $value " . "WHERE `item_id` IN " . " (SELECT `id` FROM {items} " . - " WHERE `left` >= $row->left " . - " AND `right` <= $row->right)"); + " WHERE `left_ptr` >= $row->left_ptr " . + " AND `right_ptr` <= $row->right_ptr)"); } } diff --git a/modules/gallery/helpers/album.php b/modules/gallery/helpers/album.php index 1197f243..dfb1e66d 100644 --- a/modules/gallery/helpers/album.php +++ b/modules/gallery/helpers/album.php @@ -30,9 +30,10 @@ class album_Core { * @param string $name the name of this new album (it will become the directory name on disk) * @param integer $title the title of the new album * @param string $description (optional) the longer description of this album + * @param string $slug (optional) the url component for this photo * @return Item_Model */ - static function create($parent, $name, $title, $description=null, $owner_id=null) { + static function create($parent, $name, $title, $description=null, $owner_id=null, $slug=null) { if (!$parent->loaded || !$parent->is_album()) { throw new Exception("@todo INVALID_PARENT"); } @@ -47,6 +48,10 @@ class album_Core { throw new Exception("@todo NAME_CANNOT_END_IN_PERIOD"); } + if (empty($slug)) { + $slug = item::convert_filename_to_slug($name); + } + $album = ORM::factory("item"); $album->type = "album"; $album->title = $title; @@ -55,15 +60,23 @@ class album_Core { $album->owner_id = $owner_id; $album->thumb_dirty = 1; $album->resize_dirty = 1; + $album->slug = $slug; $album->rand_key = ((float)mt_rand()) / (float)mt_getrandmax(); - $album->sort_column = "weight"; + $album->sort_column = "created"; $album->sort_order = "ASC"; + // Randomize the name or slug if there's a conflict + // @todo Improve this. Random numbers are not user friendly while (ORM::factory("item") ->where("parent_id", $parent->id) + ->open_paren() ->where("name", $album->name) + ->orwhere("slug", $album->slug) + ->close_paren() ->find()->id) { - $album->name = "{$name}-" . rand(); + $rand = rand(); + $album->name = "{$name}-$rand"; + $album->slug = "{$slug}-$rand"; } $album = $album->add_to_parent($parent); @@ -71,6 +84,8 @@ class album_Core { mkdir(dirname($album->thumb_path())); mkdir(dirname($album->resize_path())); + // @todo: publish this from inside Item_Model::save() when we refactor to the point where + // there's only one save() happening here. module::event("item_created", $album); return $album; @@ -82,28 +97,44 @@ class album_Core { ->label(t("Add an album to %album_title", array("album_title" => $parent->title))); $group->input("title")->label(t("Title")); $group->textarea("description")->label(t("Description")); - $group->input("name")->label(t("Directory Name")) + $group->input("name")->label(t("Directory name")) ->callback("item::validate_no_slashes") ->error_messages("no_slashes", t("The directory name can't contain the \"/\" character")); + $group->input("slug")->label(t("Internet Address")) + ->callback("item::validate_url_safe") + ->error_messages( + "not_url_safe", + t("The internet address should contain only letters, numbers, hyphens and underscores")); $group->hidden("type")->value("album"); $group->submit("")->value(t("Create")); $form->add_rules_from(ORM::factory("item")); + $form->script("") + ->url(url::abs_file("modules/gallery/js/albums_form_add.js")); return $form; } static function get_edit_form($parent) { $form = new Forge("albums/{$parent->id}", "", "post", array("id" => "gEditAlbumForm")); $form->hidden("_method")->value("put"); - $group = $form->group("edit_album")->label(t("Edit Album")); + $group = $form->group("edit_item")->label(t("Edit Album")); $group->input("title")->label(t("Title"))->value($parent->title); $group->textarea("description")->label(t("Description"))->value($parent->description); if ($parent->id != 1) { $group->input("dirname")->label(t("Directory Name"))->value($parent->name) + ->rules("required") + ->error_messages("name_conflict", t("There is already a photo or album with this name")) ->callback("item::validate_no_slashes") ->error_messages("no_slashes", t("The directory name can't contain a \"/\"")) ->callback("item::validate_no_trailing_period") ->error_messages("no_trailing_period", t("The directory name can't end in \".\"")); + $group->input("slug")->label(t("Internet Address"))->value($parent->slug) + ->error_messages( + "slug_conflict", t("There is already a photo or album with this internet address")) + ->callback("item::validate_url_safe") + ->error_messages( + "not_url_safe", + t("The internet address should contain only letters, numbers, hyphens and underscores")); } $sort_order = $group->group("sort_order", array("id" => "gAlbumSortOrder")) @@ -111,22 +142,33 @@ class album_Core { $sort_order->dropdown("column", array("id" => "gAlbumSortColumn")) ->label(t("Sort by")) - ->options(array("weight" => t("Order Added"), - "captured" => t("Capture Date"), - "created" => t("Creation Date"), - "title" => t("Title"), - "updated" => t("Updated Date"), - "view_count" => t("Number of views"), - "rand_key" => t("Random"))) + ->options(album::get_sort_order_options()) ->selected($parent->sort_column); $sort_order->dropdown("direction", array("id" => "gAlbumSortDirection")) ->label(t("Order")) ->options(array("ASC" => t("Ascending"), "DESC" => t("Descending"))) ->selected($parent->sort_order); + + module::event("item_edit_form", $parent, $form); + + $group = $form->group("buttons")->label(""); $group->hidden("type")->value("album"); $group->submit("")->value(t("Modify")); $form->add_rules_from(ORM::factory("item")); return $form; } + + /** + * Return a structured set of all the possible sort orders. + */ + static function get_sort_order_options() { + return array("weight" => t("Manual"), + "captured" => t("Date captured"), + "created" => t("Date uploaded"), + "title" => t("Title"), + "updated" => t("Date modified"), + "view_count" => t("Number of views"), + "rand_key" => t("Random")); + } } diff --git a/modules/gallery/helpers/gallery.php b/modules/gallery/helpers/gallery.php index a32ac484..813134eb 100644 --- a/modules/gallery/helpers/gallery.php +++ b/modules/gallery/helpers/gallery.php @@ -18,7 +18,7 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ class gallery_Core { - const VERSION = "3.0 beta 2"; + const VERSION = "3.0 git (pre-beta3)"; /** * If Gallery is in maintenance mode, then force all non-admins to get routed to a "This site is @@ -78,4 +78,232 @@ class gallery_Core { static function time($timestamp) { return date(module::get_var("gallery", "time_format", "H:i:s"), $timestamp); } + + static function site_menu($menu, $theme) { + if ($theme->page_type != "login") { + $menu->append(Menu::factory("link") + ->id("home") + ->label(t("Home")) + ->url(item::root()->url())); + + $item = $theme->item(); + + $can_edit = $item && access::can("edit", $item); + $can_add = $item && access::can("add", $item); + + if ($can_add) { + $menu->append($add_menu = Menu::factory("submenu") + ->id("add_menu") + ->label(t("Add"))); + $add_menu->append(Menu::factory("dialog") + ->id("add_photos_item") + ->label(t("Add photos")) + ->url(url::site("simple_uploader/app/$item->id"))); + if ($item->is_album()) { + $add_menu->append(Menu::factory("dialog") + ->id("add_album_item") + ->label(t("Add an album")) + ->url(url::site("form/add/albums/$item->id?type=album"))); + } + } + + $menu->append($options_menu = Menu::factory("submenu") + ->id("options_menu") + ->label(t("Photo options"))); + if ($item && ($can_edit || $can_add)) { + if ($can_edit) { + $options_menu->append(Menu::factory("dialog") + ->id("edit_item") + ->label($item->is_album() ? t("Edit album") : t("Edit photo")) + ->url(url::site("form/edit/{$item->type}s/$item->id"))); + } + + if ($item->is_album()) { + $options_menu->label(t("Album options")); + if ($can_edit) { + $options_menu->append(Menu::factory("dialog") + ->id("edit_permissions") + ->label(t("Edit permissions")) + ->url(url::site("permissions/browse/$item->id"))); + } + } + } + + if (user::active()->admin) { + $menu->append($admin_menu = Menu::factory("submenu") + ->id("admin_menu") + ->label(t("Admin"))); + gallery::admin_menu($admin_menu, $theme); + module::event("admin_menu", $admin_menu, $theme); + } + + module::event("site_menu", $menu, $theme); + } + } + + static function admin_menu($menu, $theme) { + $menu + ->append(Menu::factory("link") + ->id("dashboard") + ->label(t("Dashboard")) + ->url(url::site("admin"))) + ->append(Menu::factory("submenu") + ->id("settings_menu") + ->label(t("Settings")) + ->append(Menu::factory("link") + ->id("graphics_toolkits") + ->label(t("Graphics")) + ->url(url::site("admin/graphics"))) + ->append(Menu::factory("link") + ->id("languages") + ->label(t("Languages")) + ->url(url::site("admin/languages"))) + ->append(Menu::factory("link") + ->id("advanced") + ->label(t("Advanced")) + ->url(url::site("admin/advanced_settings")))) + ->append(Menu::factory("link") + ->id("modules") + ->label(t("Modules")) + ->url(url::site("admin/modules"))) + ->append(Menu::factory("submenu") + ->id("content_menu") + ->label(t("Content"))) + ->append(Menu::factory("submenu") + ->id("appearance_menu") + ->label(t("Appearance")) + ->append(Menu::factory("link") + ->id("themes") + ->label(t("Theme Choice")) + ->url(url::site("admin/themes"))) + ->append(Menu::factory("link") + ->id("theme_options") + ->label(t("Theme Options")) + ->url(url::site("admin/theme_options")))) + ->append(Menu::factory("submenu") + ->id("statistics_menu") + ->label(t("Statistics"))) + ->append(Menu::factory("link") + ->id("maintenance") + ->label(t("Maintenance")) + ->url(url::site("admin/maintenance"))); + return $menu; + } + + static function context_menu($menu, $theme, $item, $thumb_css_selector) { + $menu->append($options_menu = Menu::factory("submenu") + ->id("options_menu") + ->label(t("Options")) + ->css_class("ui-icon-carat-1-n")); + + if (access::can("edit", $item)) { + $page_type = $theme->page_type(); + switch ($item->type) { + case "movie": + $edit_title = t("Edit this movie"); + $delete_title = t("Delete this movie"); + break; + + case "album": + $edit_title = t("Edit this album"); + $delete_title = t("Delete this album"); + break; + + default: + $edit_title = t("Edit this photo"); + $delete_title = t("Delete this photo"); + break; + } + $cover_title = t("Choose as the album cover"); + $move_title = t("Move to another album"); + + $csrf = access::csrf_token(); + + $options_menu->append(Menu::factory("dialog") + ->id("edit") + ->label($edit_title) + ->css_class("ui-icon-pencil") + ->url(url::site("quick/form_edit/$item->id?page_type=$page_type"))); + + + if ($item->is_photo() && graphics::can("rotate")) { + $options_menu + ->append( + Menu::factory("ajax_link") + ->id("rotate_ccw") + ->label(t("Rotate 90° counter clockwise")) + ->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&page_type=$page_type"))) + ->append( + Menu::factory("ajax_link") + ->id("rotate_cw") + ->label(t("Rotate 90° clockwise")) + ->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&page_type=$page_type"))); + } + + // Don't move photos from the photo page; we don't yet have a good way of redirecting after + // move + if ($page_type == "album") { + $options_menu + ->append(Menu::factory("dialog") + ->id("move") + ->label($move_title) + ->css_class("ui-icon-folder-open") + ->url(url::site("move/browse/$item->id"))); + } + + $parent = $item->parent(); + if (access::can("edit", $parent)) { + // We can't make this item the highlight if it's an album with no album cover, or if it's + // already the album cover. + if (($item->type == "album" && empty($item->album_cover_item_id)) || + ($item->type == "album" && $parent->album_cover_item_id == $item->album_cover_item_id) || + $parent->album_cover_item_id == $item->id) { + $disabledState = " ui-state-disabled"; + } else { + $disabledState = " "; + } + if ($item->parent()->id != 1) { + $options_menu + ->append(Menu::factory("ajax_link") + ->id("make_album_cover") + ->label($cover_title) + ->css_class("ui-icon-star") + ->ajax_handler("function(data) { window.location.reload() }") + ->url(url::site("quick/make_album_cover/$item->id?csrf=$csrf"))); + } + $options_menu + ->append(Menu::factory("dialog") + ->id("delete") + ->label($delete_title) + ->css_class("ui-icon-trash") + ->css_id("gQuickDelete") + ->url(url::site("quick/form_delete/$item->id?csrf=$csrf&page_type=$page_type"))); + } + + if ($item->is_album()) { + $options_menu + ->append(Menu::factory("dialog") + ->id("add_item") + ->label(t("Add a photo")) + ->css_class("ui-icon-plus") + ->url(url::site("simple_uploader/app/$item->id"))) + ->append(Menu::factory("dialog") + ->id("add_album") + ->label(t("Add an album")) + ->css_class("ui-icon-note") + ->url(url::site("form/add/albums/$item->id?type=album"))) + ->append(Menu::factory("dialog") + ->id("edit_permissions") + ->label(t("Edit permissions")) + ->css_class("ui-icon-key") + ->url(url::site("permissions/browse/$item->id"))); + } + } + } }
\ No newline at end of file diff --git a/modules/gallery/helpers/gallery_event.php b/modules/gallery/helpers/gallery_event.php index aa11b7c0..64f2a9ff 100644 --- a/modules/gallery/helpers/gallery_event.php +++ b/modules/gallery/helpers/gallery_event.php @@ -23,7 +23,7 @@ class gallery_event_Core { access::add_group($group); } - static function group_before_delete($group) { + static function group_deleted($group) { access::delete_group($group); } @@ -31,10 +31,14 @@ class gallery_event_Core { access::add_item($item); } - static function item_before_delete($item) { + static function item_deleted($item) { access::delete_item($item); } + static function item_moved($item, $old_parent) { + access::recalculate_permissions($item->parent()); + } + static function user_login($user) { // If this user is an admin, check to see if there are any post-install tasks that we need // to run and take care of those now. @@ -43,4 +47,10 @@ class gallery_event_Core { module::clear_var("gallery", "choose_default_tookit"); } } + + static function item_index_data($item, $data) { + $data[] = $item->description; + $data[] = $item->name; + $data[] = $item->title; + } } diff --git a/modules/gallery/helpers/gallery_installer.php b/modules/gallery/helpers/gallery_installer.php index 28c1990f..a1856424 100644 --- a/modules/gallery/helpers/gallery_installer.php +++ b/modules/gallery/helpers/gallery_installer.php @@ -24,13 +24,13 @@ class gallery_installer { `id` int(9) NOT NULL auto_increment, `item_id` int(9), PRIMARY KEY (`id`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + DEFAULT CHARSET=utf8;"); $db->query("CREATE TABLE {access_intents} ( `id` int(9) NOT NULL auto_increment, `item_id` int(9), PRIMARY KEY (`id`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + DEFAULT CHARSET=utf8;"); $db->query("CREATE TABLE {caches} ( `id` int(9) NOT NULL auto_increment, @@ -40,7 +40,7 @@ class gallery_installer { `cache` longblob, PRIMARY KEY (`id`), KEY (`tags`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + DEFAULT CHARSET=utf8;"); $db->query("CREATE TABLE {graphics_rules} ( `id` int(9) NOT NULL auto_increment, @@ -51,7 +51,7 @@ class gallery_installer { `priority` int(9) NOT NULL, `target` varchar(32) NOT NULL, PRIMARY KEY (`id`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + DEFAULT CHARSET=utf8;"); $db->query("CREATE TABLE {incoming_translations} ( `id` int(9) NOT NULL auto_increment, @@ -63,7 +63,7 @@ class gallery_installer { PRIMARY KEY (`id`), UNIQUE KEY(`key`, `locale`), KEY `locale_key` (`locale`, `key`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + DEFAULT CHARSET=utf8;"); $db->query("CREATE TABLE {items} ( `id` int(9) NOT NULL auto_increment, @@ -72,7 +72,7 @@ class gallery_installer { `created` int(9) default NULL, `description` varchar(2048) default NULL, `height` int(9) default NULL, - `left` int(9) NOT NULL, + `left_ptr` int(9) NOT NULL, `level` int(9) NOT NULL, `mime_type` varchar(64) default NULL, `name` varchar(255) default NULL, @@ -80,10 +80,12 @@ class gallery_installer { `parent_id` int(9) NOT NULL, `rand_key` float default NULL, `relative_path_cache` varchar(255) default NULL, + `relative_url_cache` varchar(255) default NULL, `resize_dirty` boolean default 1, `resize_height` int(9) default NULL, `resize_width` int(9) default NULL, - `right` int(9) NOT NULL, + `right_ptr` int(9) NOT NULL, + `slug` varchar(255) default NULL, `sort_column` varchar(64) default NULL, `sort_order` char(4) default 'ASC', `thumb_dirty` boolean default 1, @@ -98,8 +100,9 @@ class gallery_installer { PRIMARY KEY (`id`), KEY `parent_id` (`parent_id`), KEY `type` (`type`), - KEY `random` (`rand_key`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + KEY `random` (`rand_key`), + KEY `weight` (`weight` DESC)) + DEFAULT CHARSET=utf8;"); $db->query("CREATE TABLE {logs} ( `id` int(9) NOT NULL auto_increment, @@ -112,7 +115,7 @@ class gallery_installer { `url` varchar(255) default NULL, `user_id` int(9) default 0, PRIMARY KEY (`id`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + DEFAULT CHARSET=utf8;"); $db->query("CREATE TABLE {messages} ( `id` int(9) NOT NULL auto_increment, @@ -121,7 +124,7 @@ class gallery_installer { `value` varchar(255) default NULL, PRIMARY KEY (`id`), UNIQUE KEY(`key`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + DEFAULT CHARSET=utf8;"); $db->query("CREATE TABLE {modules} ( `id` int(9) NOT NULL auto_increment, @@ -130,7 +133,7 @@ class gallery_installer { `version` int(9) default NULL, PRIMARY KEY (`id`), UNIQUE KEY(`name`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + DEFAULT CHARSET=utf8;"); $db->query("CREATE TABLE {outgoing_translations} ( `id` int(9) NOT NULL auto_increment, @@ -142,7 +145,7 @@ class gallery_installer { PRIMARY KEY (`id`), UNIQUE KEY(`key`, `locale`), KEY `locale_key` (`locale`, `key`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + DEFAULT CHARSET=utf8;"); $db->query("CREATE TABLE {permissions} ( `id` int(9) NOT NULL auto_increment, @@ -150,14 +153,14 @@ class gallery_installer { `name` varchar(64) default NULL, PRIMARY KEY (`id`), UNIQUE KEY(`name`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + DEFAULT CHARSET=utf8;"); $db->query("CREATE TABLE {sessions} ( `session_id` varchar(127) NOT NULL, `data` text NOT NULL, `last_activity` int(10) UNSIGNED NOT NULL, PRIMARY KEY (`session_id`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + DEFAULT CHARSET=utf8;"); $db->query("CREATE TABLE {tasks} ( `id` int(9) NOT NULL auto_increment, @@ -172,7 +175,7 @@ class gallery_installer { `updated` int(9) default NULL, PRIMARY KEY (`id`), KEY (`owner_id`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + DEFAULT CHARSET=utf8;"); $db->query("CREATE TABLE {themes} ( `id` int(9) NOT NULL auto_increment, @@ -180,7 +183,7 @@ class gallery_installer { `version` int(9) default NULL, PRIMARY KEY (`id`), UNIQUE KEY(`name`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + DEFAULT CHARSET=utf8;"); $db->query("CREATE TABLE {vars} ( `id` int(9) NOT NULL auto_increment, @@ -189,7 +192,7 @@ class gallery_installer { `value` text, PRIMARY KEY (`id`), UNIQUE KEY(`module_name`, `name`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + DEFAULT CHARSET=utf8;"); foreach (array("albums", "logs", "modules", "resizes", "thumbs", "tmp", "uploads") as $dir) { @mkdir(VARPATH . $dir); @@ -204,8 +207,8 @@ class gallery_installer { $root->type = "album"; $root->title = "Gallery"; $root->description = ""; - $root->left = 1; - $root->right = 2; + $root->left_ptr = 1; + $root->right_ptr = 2; $root->parent_id = 0; $root->level = 1; $root->thumb_dirty = 1; @@ -222,6 +225,7 @@ class gallery_installer { module::set_var("gallery", "resize_size", 640); module::set_var("gallery", "default_locale", "en_US"); module::set_var("gallery", "image_quality", 75); + module::set_var("gallery", "image_sharpen", 15); // Add rules for generating our thumbnails and resizes graphics::add_rule( @@ -258,7 +262,7 @@ class gallery_installer { module::set_var("gallery", "show_credits", 1); // @todo this string needs to be picked up by l10n_scanner module::set_var("gallery", "credits", "Powered by <a href=\"%url\">Gallery %version</a>"); - module::set_version("gallery", 7); + module::set_version("gallery", 12); } static function upgrade($version) { @@ -283,7 +287,7 @@ class gallery_installer { `cache` text, PRIMARY KEY (`id`), KEY (`tags`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + DEFAULT CHARSET=utf8;"); module::set_version("gallery", $version = 4); } @@ -305,6 +309,55 @@ class gallery_installer { module::clear_var("gallery", "version"); module::set_version("gallery", $version = 7); } + + if ($version == 7) { + $groups = ORM::factory("group")->find_all(); + $permissions = ORM::factory("permission")->find_all(); + foreach($groups as $group) { + foreach($permissions as $permission) { + // Update access intents + $db->query("ALTER TABLE {access_intents} MODIFY COLUMN {$permission->name}_{$group->id} BINARY(1) DEFAULT NULL"); + // Update access cache + if ($permission->name === "view") { + $db->query("ALTER TABLE {items} MODIFY COLUMN {$permission->name}_{$group->id} BINARY(1) DEFAULT FALSE"); + } else { + $db->query("ALTER TABLE {access_caches} MODIFY COLUMN {$permission->name}_{$group->id} BINARY(1) NOT NULL DEFAULT FALSE"); + } + } + } + module::set_version("gallery", $version = 8); + } + + if ($version == 8) { + $db->query("ALTER TABLE {items} CHANGE COLUMN `left` `left_ptr` INT(9) NOT NULL;"); + $db->query("ALTER TABLE {items} CHANGE COLUMN `right` `right_ptr` INT(9) NOT NULL;"); + module::set_version("gallery", $version = 9); + } + + if ($version == 9) { + $db->query("ALTER TABLE {items} ADD KEY `weight` (`weight` DESC);"); + + module::set_version("gallery", $version = 10); + } + + if ($version == 10) { + module::set_var("gallery", "image_sharpen", 15); + + module::set_version("gallery", $version = 11); + } + + if ($version == 11) { + $db->query("ALTER TABLE {items} ADD COLUMN `relative_url_cache` varchar(255) DEFAULT NULL"); + $db->query("ALTER TABLE {items} ADD COLUMN `slug` varchar(255) DEFAULT NULL"); + + // This is imperfect since some of the slugs may contain invalid characters, but it'll do + // for now because we don't want a lengthy operation here. + $db->query("UPDATE {items} SET `slug` = `name`"); + + // Flush all path caches becuase we're going to start urlencoding them. + $db->query("UPDATE {items} SET `relative_url_cache` = NULL, `relative_path_cache` = NULL"); + module::set_version("gallery", $version = 12); + } } static function uninstall() { diff --git a/modules/gallery/helpers/gallery_menu.php b/modules/gallery/helpers/gallery_menu.php deleted file mode 100644 index b6f763b8..00000000 --- a/modules/gallery/helpers/gallery_menu.php +++ /dev/null @@ -1,167 +0,0 @@ -<?php defined("SYSPATH") or die("No direct script access."); -/** - * Gallery - a web based photo album viewer and editor - * Copyright (C) 2000-2009 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 gallery_menu_Core { - static function site($menu, $theme) { - $is_admin = user::active()->admin; - - $menu->append(Menu::factory("link") - ->id("home") - ->label(t("Home")) - ->url(url::site("albums/1"))); - - $item = $theme->item(); - - $can_edit = $item && access::can("edit", $item) || $is_admin; - $can_add = $item && (access::can("add", $item) || $is_admin); - - if ($can_add) { - $menu->append(Menu::factory("dialog") - ->id("add_photos_item") - ->label(t("Add photos")) - ->url(url::site("simple_uploader/app/$item->id"))); - } - - if ($item && $can_edit || $can_add) { - $menu->append($options_menu = Menu::factory("submenu") - ->id("options_menu") - ->label(t("Options"))); - - if ($can_edit) { - $options_menu - ->append(Menu::factory("dialog") - ->id("edit_item") - ->label($item->is_album() ? t("Edit album") : t("Edit photo")) - ->url(url::site("form/edit/{$item->type}s/$item->id"))); - } - - // @todo Move album options menu to the album quick edit pane - if ($item->is_album()) { - if ($can_add) { - $options_menu - ->append(Menu::factory("dialog") - ->id("add_album") - ->label(t("Add an album")) - ->url(url::site("form/add/albums/$item->id?type=album"))); - } - - if ($can_edit) { - $options_menu - ->append(Menu::factory("dialog") - ->id("edit_permissions") - ->label(t("Edit permissions")) - ->url(url::site("permissions/browse/$item->id"))); - } - } - } - - if ($is_admin) { - $menu->append($admin_menu = Menu::factory("submenu") - ->id("admin_menu") - ->label(t("Admin"))); - self::admin($admin_menu, $theme); - foreach (module::active() as $module) { - if ($module->name == "gallery") { - continue; - } - $class = "{$module->name}_menu"; - if (method_exists($class, "admin")) { - call_user_func_array(array($class, "admin"), array(&$admin_menu, $theme)); - } - } - } - } - - static function album($menu, $theme) { - } - - static function tag($menu, $theme) { - } - - static function thumb($menu, $theme, $item) { - $menu->append(Menu::factory("submenu") - ->id("options_menu") - ->label(t("Options")) - ->css_class("gThumbMenu")); - } - - static function photo($menu, $theme) { - if (access::can("view_full", $theme->item())) { - $menu->append(Menu::factory("link") - ->id("fullsize") - ->label(t("View full size")) - ->url($theme->item()->file_url()) - ->css_class("gFullSizeLink")); - } - } - - static function admin($menu, $theme) { - $menu - ->append(Menu::factory("link") - ->id("dashboard") - ->label(t("Dashboard")) - ->url(url::site("admin"))) - ->append(Menu::factory("submenu") - ->id("settings_menu") - ->label(t("Settings")) - ->append(Menu::factory("link") - ->id("graphics_toolkits") - ->label(t("Graphics")) - ->url(url::site("admin/graphics"))) - ->append(Menu::factory("link") - ->id("languages") - ->label(t("Languages")) - ->url(url::site("admin/languages"))) - ->append(Menu::factory("link") - ->id("l10n_mode") - ->label(Session::instance()->get("l10n_mode", false) - ? t("Stop translating") : t("Start translating")) - ->url(url::site("l10n_client/toggle_l10n_mode?csrf=" . - access::csrf_token()))) - ->append(Menu::factory("link") - ->id("advanced") - ->label(t("Advanced")) - ->url(url::site("admin/advanced_settings")))) - ->append(Menu::factory("link") - ->id("modules") - ->label(t("Modules")) - ->url(url::site("admin/modules"))) - ->append(Menu::factory("submenu") - ->id("content_menu") - ->label(t("Content"))) - ->append(Menu::factory("submenu") - ->id("appearance_menu") - ->label(t("Appearance")) - ->append(Menu::factory("link") - ->id("themes") - ->label(t("Theme Choice")) - ->url(url::site("admin/themes"))) - ->append(Menu::factory("link") - ->id("theme_options") - ->label(t("Theme Options")) - ->url(url::site("admin/theme_options")))) - ->append(Menu::factory("submenu") - ->id("statistics_menu") - ->label(t("Statistics"))) - ->append(Menu::factory("link") - ->id("maintenance") - ->label(t("Maintenance")) - ->url(url::site("admin/maintenance"))); - } -} diff --git a/modules/gallery/helpers/gallery_quick.php b/modules/gallery/helpers/gallery_quick.php deleted file mode 100644 index d0ffc584..00000000 --- a/modules/gallery/helpers/gallery_quick.php +++ /dev/null @@ -1,144 +0,0 @@ -<?php defined("SYSPATH") or die("No direct script access."); -/** - * Gallery - a web based photo album viewer and editor - * Copyright (C) 2000-2009 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 gallery_quick_Core { - static function get_quick_buttons($item, $page_type) { - $buttons = self::buttons($item, $page_type); - foreach (module::active() as $module) { - if ($module->name == "gallery") { - continue; - } - $class_name = "{$module->name}_quick"; - if (method_exists($class_name, "buttons")) { - $module_buttons = call_user_func(array($class_name, "buttons"), $item, $page_type); - foreach (array("left", "center", "right", "additional") as $position) { - if (!empty($module_buttons[$position])) { - $buttons[$position] = array_merge($buttons[$position], $module_buttons[$position]); - } - } - } - } - - $sorted_buttons->main = array(); - foreach (array("left", "center", "right") as $position) { - $sorted_buttons->main = array_merge($sorted_buttons->main, $buttons[$position]); - } - - $sorted_buttons->additional = $buttons["additional"]; - $max_display = empty($sorted_buttons->additional) ? 6 : 5; - if (count($sorted_buttons->main) >= $max_display) { - $to_move = array_slice($sorted_buttons->main, 5); - $sorted_buttons->additional = array_merge($to_move, $sorted_buttons->additional); - for ($i = count($sorted_buttons->main); $i >= 5; $i--) { - unset($sorted_buttons->main[$i]); - } - } - - return $sorted_buttons; - } - - static function buttons($item, $page_type) { - $elements = array("left" => array(), "center" => array(), "right" => array(), - "additional" => array()); - switch ($item->type) { - case "movie": - $edit_title = t("Edit this movie"); - $move_title = t("Move this movie to another album"); - $cover_title = t("Choose this movie as the album cover"); - $delete_title = t("Delete this movie"); - break; - case "album": - $edit_title = t("Edit this album"); - $move_title = t("Move this album to another album"); - $cover_title = t("Choose this album as the album cover"); - $delete_title = t("Delete this album"); - break; - default: - $edit_title = t("Edit this photo"); - $move_title = t("Move this photo to another album"); - $cover_title = t("Choose this photo as the album cover"); - $delete_title = t("Delete this photo"); - break; - } - - $csrf = access::csrf_token(); - $elements["left"][] = (object)array( - "title" => $edit_title, - "class" => "gDialogLink gButtonLink", - "icon" => "ui-icon-pencil", - "href" => url::site("quick/form_edit/$item->id?page_type=$page_type")); - - if ($item->is_photo() && graphics::can("rotate")) { - $elements["left"][] = - (object)array( - "title" => t("Rotate 90 degrees counter clockwise"), - "class" => "gButtonLink", - "icon" => "ui-icon-rotate-ccw", - "href" => url::site("quick/rotate/$item->id/ccw?csrf=$csrf&page_type=$page_type")); - $elements["left"][] = - (object)array( - "title" => t("Rotate 90 degrees clockwise"), - "class" => "gButtonLink", - "icon" => "ui-icon-rotate-cw", - "href" => url::site("quick/rotate/$item->id/cw?csrf=$csrf&page_type=$page_type")); - } - - // Don't move photos from the photo page; we don't yet have a good way of redirecting after move - if ($page_type == "album") { - $elements["left"][] = (object)array( - "title" => $move_title, - "class" => "gDialogLink gButtonLink", - "icon" => "ui-icon-folder-open", - "href" => url::site("move/browse/$item->id")); - } - - if (access::can("edit", $item->parent())) { - $disabledState = - $item->type == "album" && empty($item->album_cover_item_id) ? " ui-state-disabled" : ""; - $elements["right"][] = (object)array( - "title" => $cover_title, - "class" => "gButtonLink{$disabledState}", - "icon" => "ui-icon-star", - "href" => url::site("quick/make_album_cover/$item->id?csrf=$csrf&page_type=$page_type")); - - $elements["right"][] = (object)array( - "title" => $delete_title, - "class" => "gDialogLink gButtonLink", - "icon" => "ui-icon-trash", - "id" => "gQuickDelete", - "href" => url::site("quick/form_delete/$item->id?csrf=$csrf&page_type=$page_type")); - } - - if ($item->is_album()) { - $elements["additional"][] = (object)array( - "title" => t("Add a photo"), - "class" => "add_item gDialogLink", - "href" => url::site("simple_uploader/app/$item->id")); - $elements["additional"][] = (object)array( - "title" => t("Add an album"), - "class" => "add_album gDialogLink", - "href" => url::site("form/add/albums/$item->id?type=album")); - $elements["additional"][] = (object)array( - "title" => t("Edit permissions"), - "class" => "permissions gDialogLink", - "href" => url::site("permissions/browse/$item->id")); - } - return $elements; - } -} diff --git a/modules/gallery/helpers/gallery_rss.php b/modules/gallery/helpers/gallery_rss.php index 7daf6170..feeab88a 100644 --- a/modules/gallery/helpers/gallery_rss.php +++ b/modules/gallery/helpers/gallery_rss.php @@ -40,7 +40,6 @@ class gallery_rss_Core { $feed->max_pages = ceil($all_children->find_all()->count() / $limit); $feed->title = t("Recent Updates"); - $feed->link = url::abs_site("albums/1"); $feed->description = t("Recent Updates"); return $feed; @@ -50,11 +49,11 @@ class gallery_rss_Core { $feed->children = $item ->viewable() - ->descendants($limit, $offset, "photo"); - $feed->max_pages = ceil($item->viewable()->descendants_count("photo") / $limit); - $feed->title = p::purify($item->title); - $feed->link = url::abs_site("albums/{$item->id}"); - $feed->description = nl2br(p::purify($item->description)); + ->descendants($limit, $offset, array("type" => "photo")); + $feed->max_pages = ceil( + $item->viewable()->descendants_count(array("type" => "photo")) / $limit); + $feed->title = html::purify($item->title); + $feed->description = nl2br(html::purify($item->description)); return $feed; } diff --git a/modules/gallery/helpers/gallery_search.php b/modules/gallery/helpers/gallery_search.php deleted file mode 100644 index 2a4029d3..00000000 --- a/modules/gallery/helpers/gallery_search.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php defined("SYSPATH") or die("No direct script access."); -/** - * Gallery - a web based photo album viewer and editor - * Copyright (C) 2000-2009 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 gallery_search_Core { - static function item_index_data($item) { - return join(" ", array($item->description, $item->name, $item->title)); - } -} diff --git a/modules/gallery/helpers/gallery_task.php b/modules/gallery/helpers/gallery_task.php index 9ce2c4a0..1b56ab97 100644 --- a/modules/gallery/helpers/gallery_task.php +++ b/modules/gallery/helpers/gallery_task.php @@ -45,7 +45,7 @@ class gallery_task_Core { * @param Task_Model the task */ static function rebuild_dirty_images($task) { - $message = array(); + $errors = array(); try { $result = graphics::find_dirty_images_query(); $completed = $task->get("completed", 0); @@ -60,14 +60,15 @@ class gallery_task_Core { $item = ORM::factory("item", $row->id); if ($item->loaded) { - $success = graphics::generate($item); - if (!$success) { + try { + graphics::generate($item); $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))); + $errors[] = t("Successfully rebuilt images for '%title'", + array("title" => html::purify($item->title))); + } catch (Exception $e) { + $errors[] = t("Unable to rebuild images for '%title'", + array("title" => html::purify($item->title))); + $errors[] = $e->__toString(); } } @@ -101,13 +102,15 @@ class gallery_task_Core { $task->done = true; $task->state = "error"; $task->status = $e->getMessage(); - $message[] = $e->__toString(); + $errors[] = $e->__toString(); + } + if ($errors) { + $task->log($errors); } - $task->log($message); } static function update_l10n(&$task) { - $message = array(); + $errors = array(); try { $start = microtime(true); $dirs = $task->get("dirs"); @@ -139,8 +142,8 @@ class gallery_task_Core { } } - $message[] = $task->status = t2("Finding files: found 1 file", - "Finding files: found %count files", count($files)); + $task->status = t2("Finding files: found 1 file", + "Finding files: found %count files", count($files)); if (!$dirs) { $task->set("mode", "scan_files"); @@ -165,8 +168,8 @@ class gallery_task_Core { } $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->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)) { @@ -177,7 +180,7 @@ class gallery_task_Core { break; case "fetch_updates": // 90% - 100% - $message = array_merge($message, l10n_client::fetch_updates()); + l10n_client::fetch_updates(); $task->done = true; $task->state = "success"; $task->status = t("Translations installed/updated"); @@ -191,8 +194,10 @@ class gallery_task_Core { $task->done = true; $task->state = "error"; $task->status = $e->getMessage(); - $message[] = $e->__toString(); + $errors[] = $e->__toString(); + } + if ($errors) { + $task->log($errors); } - $task->log($message); } }
\ No newline at end of file diff --git a/modules/gallery/helpers/gallery_theme.php b/modules/gallery/helpers/gallery_theme.php index 226b8a42..20dfeb04 100644 --- a/modules/gallery/helpers/gallery_theme.php +++ b/modules/gallery/helpers/gallery_theme.php @@ -22,67 +22,41 @@ class gallery_theme_Core { $session = Session::instance(); $buf = ""; if ($session->get("debug")) { - $theme->css("modules/gallery/css/debug.css"); - } - if (($theme->page_type == "album" || $theme->page_type == "photo") - && access::can("edit", $theme->item())) { - $theme->css("modules/gallery/css/quick.css"); - $theme->script("modules/gallery/js/quick.js"); + $theme->css("debug.css"); } if (module::is_active("rss")) { if ($item = $theme->item()) { - $buf .= rss::feed_link("gallery/album/{$item->id}"); + if ($item->is_album()) { + $buf .= rss::feed_link("gallery/album/{$item->id}"); + } else { + $buf .= rss::feed_link("gallery/album/{$item->parent()->id}"); + } } else if ($tag = $theme->tag()) { $buf .= rss::feed_link("tag/tag/{$tag->id}"); } } if ($session->get("l10n_mode", false)) { - $theme->css("modules/gallery/css/l10n_client.css"); - $theme->script("lib/jquery.cookie.js"); - $theme->script("modules/gallery/js/l10n_client.js"); + $theme->css("l10n_client.css"); + $theme->script("jquery.cookie.js"); + $theme->script("l10n_client.js"); } return $buf; } - static function resize_top($theme, $item) { - if (access::can("edit", $item)) { - $edit_link = url::site("quick/pane/$item->id?page_type=photo"); - return "<div class=\"gQuick\" href=\"$edit_link\">"; - } - } - - static function resize_bottom($theme, $item) { - if (access::can("edit", $item)) { - return "</div>"; - } - } - - static function thumb_top($theme, $child) { - if (access::can("edit", $child)) { - $edit_link = url::site("quick/pane/$child->id?page_type=album"); - return "<div class=\"gQuick\" href=\"$edit_link\">"; - } - } - - static function thumb_bottom($theme, $child) { - if (access::can("edit", $child)) { - return "</div>"; - } - } - static function admin_head($theme) { + $theme->script("gallery.panel.js"); $session = Session::instance(); if ($session->get("debug")) { - $theme->css("modules/gallery/css/debug.css"); + $theme->css("debug.css"); } if ($session->get("l10n_mode", false)) { - $theme->css("modules/gallery/css/l10n_client.css"); - $theme->script("lib/jquery.cookie.js"); - $theme->script("modules/gallery/js/l10n_client.js"); + $theme->css("l10n_client.css"); + $theme->script("jquery.cookie.js"); + $theme->script("l10n_client.js"); } } @@ -98,7 +72,7 @@ class gallery_theme_Core { if ($session->get("after_install")) { $session->delete("after_install"); - return new View("after_install_loader.html"); + return new View("welcome_message_loader.html"); } } @@ -123,4 +97,10 @@ class gallery_theme_Core { static function admin_credits() { return gallery_theme::credits(); } + + static function body_attributes() { + if (locales::is_rtl()) { + return 'class="rtl"'; + } + } }
\ No newline at end of file diff --git a/modules/gallery/helpers/graphics.php b/modules/gallery/helpers/graphics.php index db9b2ef5..78812794 100644 --- a/modules/gallery/helpers/graphics.php +++ b/modules/gallery/helpers/graphics.php @@ -102,12 +102,12 @@ class graphics_Core { /** * Rebuild the thumb and resize for the given item. * @param Item_Model $item - * @return true on successful generation */ static function generate($item) { if ($item->is_album()) { if (!$cover = $item->album_cover()) { - return false; + // This album has no cover; there's nothing to generate. + return; } $input_file = $cover->file_path(); $input_item = $cover; @@ -127,7 +127,7 @@ class graphics_Core { $item->thumb_dirty = 0; $item->resize_dirty = 0; $item->save(); - return true; + return; } try { @@ -176,10 +176,8 @@ class graphics_Core { // @todo we should handle this better. Kohana::log("error", "Caught exception rebuilding image: {$item->title}\n" . $e->getMessage() . "\n" . $e->getTraceAsString()); - return false; + throw $e; } - - return true; } /** @@ -195,6 +193,8 @@ class graphics_Core { self::init_toolkit(); } + module::event("graphics_resize", $input_file, $output_file, $options); + if (@filesize($input_file) == 0) { throw new Exception("@todo EMPTY_INPUT_FILE"); } @@ -204,11 +204,16 @@ class graphics_Core { // Image would get upscaled; do nothing copy($input_file, $output_file); } else { - Image::factory($input_file) + $image = Image::factory($input_file) ->resize($options["width"], $options["height"], $options["master"]) - ->quality(module::get_var("gallery", "image_quality")) - ->save($output_file); + ->quality(module::get_var("gallery", "image_quality")); + if (graphics::can("sharpen")) { + $image->sharpen(module::get_var("gallery", "image_sharpen")); + } + $image->save($output_file); } + + module::event("graphics_resize_completed", $input_file, $output_file, $options); } /** @@ -223,10 +228,14 @@ class graphics_Core { self::init_toolkit(); } + module::event("graphics_rotate", $input_file, $output_file, $options); + Image::factory($input_file) ->quality(module::get_var("gallery", "image_quality")) ->rotate($options["degrees"]) ->save($output_file); + + module::event("graphics_rotate_completed", $input_file, $output_file, $options); } /** @@ -249,6 +258,8 @@ class graphics_Core { self::init_toolkit(); } + module::event("graphics_composite", $input_file, $output_file, $options); + list ($width, $height) = getimagesize($input_file); list ($w_width, $w_height) = getimagesize($options["file"]); @@ -276,6 +287,9 @@ class graphics_Core { ->composite($options["file"], $x, $y, $options["transparency"]) ->quality(module::get_var("gallery", "image_quality")) ->save($output_file); + + + module::event("graphics_composite_completed", $input_file, $output_file, $options); } /** @@ -312,9 +326,9 @@ class graphics_Core { t2("One of your photos is out of date. <a %attrs>Click here to fix it</a>", "%count of your photos are out of date. <a %attrs>Click here to fix them</a>", $count, - array("attrs" => sprintf( + array("attrs" => html::mark_clean(sprintf( 'href="%s" class="gDialogLink"', - url::site("admin/maintenance/start/gallery_task::rebuild_dirty_images?csrf=__CSRF__")))), + url::site("admin/maintenance/start/gallery_task::rebuild_dirty_images?csrf=__CSRF__"))))), "graphics_dirty"); } } @@ -326,15 +340,101 @@ class graphics_Core { * GraphicsMagick we return the path to the directory containing the appropriate binaries. */ static function detect_toolkits() { + $toolkits = new stdClass(); + + // GD is special, it doesn't use exec() $gd = function_exists("gd_info") ? gd_info() : array(); - $exec = function_exists("exec"); + $toolkits->gd->name = "GD"; if (!isset($gd["GD Version"])) { - $gd["GD Version"] = false; + $toolkits->gd->installed = false; + $toolkits->gd->error = t("GD is not installed"); + } else { + $toolkits->gd->installed = true; + $toolkits->gd->version = $gd["GD Version"]; + $toolkits->gd->rotate = function_exists("imagerotate"); + $toolkits->gd->sharpen = function_exists("imageconvolution"); + $toolkits->gd->binary = ""; + $toolkits->gd->dir = ""; + + if (!$toolkits->gd->rotate && !$toolkits->gd->sharpen) { + $toolkits->gd->error = + t("You have GD version %version, but it lacks image rotation and sharpening.", + array("version" => $gd["GD Version"])); + } else if (!$toolkits->gd->rotate) { + $toolkits->gd->error = + t("You have GD version %version, but it lacks image rotation.", + array("version" => $gd["GD Version"])); + } else if (!$toolkits->gd->sharpen) { + $toolkits->gd->error = + t("You have GD version %version, but it lacks image sharpening.", + array("version" => $gd["GD Version"])); + } + } + + if (!function_exists("exec")) { + $toolkits->imagemagick->installed = false; + $toolkits->imagemagick->error = t("ImageMagick requires the <b>exec</b> function"); + + $toolkits->graphicsmagick->installed = false; + $toolkits->graphicsmagick->error = t("GraphicsMagick requires the <b>exec</b> function"); + } else { + putenv("PATH=" . getenv("PATH") . ":/usr/local/bin:/opt/local/bin:/opt/bin"); + + // @todo: consider refactoring the two segments below into a loop since they are so + // similar. + + // ImageMagick + $path = exec("which convert"); + $toolkits->imagemagick->name = "ImageMagick"; + if ($path) { + if (@is_file($path)) { + preg_match('/Version: \S+ (\S+)/', `convert -v`, $matches); + $version = $matches[1]; + + $toolkits->imagemagick->installed = true; + $toolkits->imagemagick->version = $version; + $toolkits->imagemagick->binary = $path; + $toolkits->imagemagick->dir = dirname($path); + $toolkits->imagemagick->rotate = true; + $toolkits->imagemagick->sharpen = true; + } else { + $toolkits->imagemagick->installed = false; + $toolkits->imagemagick->error = + t("ImageMagick is installed, but PHP's open_basedir restriction " . + "prevents Gallery from using it."); + } + } else { + $toolkits->imagemagick->installed = false; + $toolkits->imagemagick->error = t("We could not locate ImageMagick on your system."); + } + + // GraphicsMagick + $path = exec("which gm"); + $toolkits->graphicsmagick->name = "GraphicsMagick"; + if ($path) { + if (@is_file($path)) { + preg_match('/\S+ (\S+)/', `gm version`, $matches); + $version = $matches[1]; + + $toolkits->graphicsmagick->installed = true; + $toolkits->graphicsmagick->version = $version; + $toolkits->graphicsmagick->binary = $path; + $toolkits->graphicsmagick->dir = dirname($path); + $toolkits->graphicsmagick->rotate = true; + $toolkits->graphicsmagick->sharpen = true; + } else { + $toolkits->graphicsmagick->installed = false; + $toolkits->graphicsmagick->error = + t("GraphicsMagick is installed, but PHP's open_basedir restriction " . + "prevents Gallery from using it."); + } + } else { + $toolkits->graphicsmagick->installed = false; + $toolkits->graphicsmagick->error = t("We could not locate GraphicsMagick on your system."); + } } - putenv("PATH=" . getenv("PATH") . ":/usr/local/bin:/opt/local/bin:/opt/bin"); - return array("gd" => $gd, - "imagemagick" => $exec ? dirname(exec("which convert")) : false, - "graphicsmagick" => $exec ? dirname(exec("which gm")) : false); + + return $toolkits; } /** @@ -344,16 +444,17 @@ class graphics_Core { // Detect a graphics toolkit $toolkits = graphics::detect_toolkits(); foreach (array("imagemagick", "graphicsmagick", "gd") as $tk) { - if ($toolkits[$tk]) { + if ($toolkits->$tk->installed) { module::set_var("gallery", "graphics_toolkit", $tk); - module::set_var("gallery", "graphics_toolkit_path", $tk == "gd" ? "" : $toolkits[$tk]); + module::set_var("gallery", "graphics_toolkit_path", $toolkits->$tk->dir); break; } } + if (!module::get_var("gallery", "graphics_toolkit")) { site_status::warning( t("Graphics toolkit missing! Please <a href=\"%url\">choose a toolkit</a>", - array("url" => url::site("admin/graphics"))), + array("url" => html::mark_clean(url::site("admin/graphics")))), "missing_graphics_toolkit"); } } @@ -385,14 +486,18 @@ class graphics_Core { /** * Verify that a specific graphics function is available with the active toolkit. - * @param string $func (eg rotate, resize) + * @param string $func (eg rotate, sharpen) * @return boolean */ static function can($func) { - if (module::get_var("gallery", "graphics_toolkit") == "gd" && - $func == "rotate" && - !function_exists("imagerotate")) { - return false; + if (module::get_var("gallery", "graphics_toolkit") == "gd") { + switch ($func) { + case "rotate": + return function_exists("imagerotate"); + + case "sharpen": + return function_exists("imageconvolution"); + } } return true; diff --git a/modules/gallery/helpers/item.php b/modules/gallery/helpers/item.php index f40b5c97..588c08d4 100644 --- a/modules/gallery/helpers/item.php +++ b/modules/gallery/helpers/item.php @@ -53,7 +53,7 @@ class item_Core { access::required("view", $parent); access::required("edit", $parent); - model_cache::clear("item", $parent->album_cover_item_id); + model_cache::clear(); $parent->album_cover_item_id = $item->is_album() ? $item->album_cover_item_id : $item->id; $parent->thumb_dirty = 1; $parent->save(); @@ -69,7 +69,7 @@ class item_Core { access::required("edit", $album); @unlink($album->thumb_path()); - model_cache::clear("item", $album->album_cover_item_id) ; + model_cache::clear(); $album->album_cover_item_id = null; $album->thumb_width = 0; $album->thumb_height = 0; @@ -90,21 +90,9 @@ class item_Core { } } - static function validate_no_name_conflict($input) { - $itemid = Input::instance()->post("item"); - if (is_array($itemid)) { - $itemid = $itemid[0]; - } - $item = ORM::factory("item") - ->in("id", $itemid) - ->find(); - if (Database::instance() - ->from("items") - ->where("parent_id", $item->parent_id) - ->where("id <>", $item->id) - ->where("name", $input->value) - ->count_records()) { - $input->add_error("conflict", 1); + static function validate_url_safe($input) { + if (preg_match("/[^A-Za-z0-9-_]/", $input->value)) { + $input->add_error("not_url_safe", 1); } } @@ -121,6 +109,16 @@ class item_Core { } /** + * Convert a filename into something we can use as a url component. + * @param string $filename + */ + static function convert_filename_to_slug($filename) { + $result = pathinfo($filename, PATHINFO_FILENAME); + $result = preg_replace("/[^A-Za-z0-9-_]+/", "-", $result); + return trim($result, "-"); + } + + /** * Display delete confirmation message and form * @param object $item * @return string form @@ -129,12 +127,72 @@ class item_Core { if (Input::instance()->get("page_type") == "album") { $page_type = "album"; } else { - $page_type = "item"; + $page_type = "photo"; } - $form = new Forge("quick/delete/$item->id?page_type=$page_type", "", "post", array("id" => "gConfirmDelete")); + $form = new Forge( + "quick/delete/$item->id?page_type=$page_type", "", "post", array("id" => "gConfirmDelete")); $form->hidden("_method")->value("put"); $group = $form->group("confirm_delete")->label(t("Confirm Deletion")); $group->submit("")->value(t("Delete")); return $form; } + + /** + * Get the next weight value + */ + static function get_max_weight() { + // Guard against an empty result when we create the first item. It's unfortunate that we + // have to check this every time. + // @todo: figure out a better way to bootstrap the weight. + $result = Database::instance() + ->select("weight")->from("items") + ->orderby("weight", "desc")->limit(1) + ->get()->current(); + return ($result ? $result->weight : 0) + 1; + } + + /** + * Add a set of restrictions to any following queries to restrict access only to items + * viewable by the active user. + * @chainable + */ + static function viewable($model) { + $view_restrictions = array(); + if (!user::active()->admin) { + foreach (user::group_ids() as $id) { + // Separate the first restriction from the rest to make it easier for us to formulate + // our where clause below + if (empty($view_restrictions)) { + $view_restrictions[0] = "items.view_$id"; + } else { + $view_restrictions[1]["items.view_$id"] = access::ALLOW; + } + } + } + switch (count($view_restrictions)) { + case 0: + break; + + case 1: + $model->where($view_restrictions[0], access::ALLOW); + break; + + default: + $model->open_paren(); + $model->where($view_restrictions[0], access::ALLOW); + $model->orwhere($view_restrictions[1]); + $model->close_paren(); + break; + } + + return $model; + } + + /** + * Return the root Item_Model + * @return Item_Model + */ + static function root() { + return model_cache::get("item", 1); + } }
\ No newline at end of file diff --git a/modules/gallery/helpers/l10n_client.php b/modules/gallery/helpers/l10n_client.php index 6d4da0eb..3460cc65 100644 --- a/modules/gallery/helpers/l10n_client.php +++ b/modules/gallery/helpers/l10n_client.php @@ -74,7 +74,7 @@ class l10n_client_Core { $request->locales = array(); $request->messages = new stdClass(); - $locales = locale::installed(); + $locales = locales::installed(); foreach ($locales as $locale => $locale_data) { $request->locales[] = $locale; } @@ -113,10 +113,6 @@ class l10n_client_Core { // [{key:<key_1>, translation: <JSON encoded translation>, rev:<rev>, locale:<locale>}, // {key:<key_2>, ...} // ] - $count = count($response); - $message[] = t2("Installed 1 new / updated translation message", - "Installed %count new / updated translation messages", $count); - foreach ($response as $message_data) { // @todo Better input validation if (empty($message_data->key) || empty($message_data->translation) || @@ -153,7 +149,6 @@ class l10n_client_Core { $entry->translation = $translation; $entry->save(); } - return $message; } static function submit_translations() { diff --git a/modules/gallery/helpers/locale.php b/modules/gallery/helpers/locales.php index 41b78834..16dda2d7 100644 --- a/modules/gallery/helpers/locale.php +++ b/modules/gallery/helpers/locales.php @@ -21,8 +21,9 @@ /** * This is the API for handling locales. */ -class locale_Core { +class locales_Core { private static $locales; + private static $language_subtag_to_locale; /** * Return the list of available locales. @@ -55,6 +56,9 @@ class locale_Core { : array_merge($locales, array($default)); module::set_var("gallery", "installed_locales", join("|", $locales)); + + // Clear the cache + self::$locales = null; } // @todo Might want to add a localizable language name as well. @@ -105,6 +109,16 @@ class locale_Core { $l["zh_TW"] = "繁體中文"; // Chinese (TW) asort($l, SORT_LOCALE_STRING); self::$locales = $l; + + // Language subtag to (default) locale mapping + foreach ($l as $locale => $name) { + list ($language) = explode("_", $locale . "_"); + // The first one mentioned is the default + if (!isset($d[$language])) { + $d[$language] = $locale; + } + } + self::$language_subtag_to_locale = $d; } static function display_name($locale=null) { @@ -121,4 +135,80 @@ class locale_Core { list ($language, $territory) = explode('_', $locale . "_"); return in_array($language, array("he", "fa", "ar")); } + + /** + * Returns the best match comparing the HTTP accept-language header + * with the installed locales. + * @todo replace this with request::accepts_language() when we upgrade to Kohana 2.4 + */ + static function locale_from_http_request() { + $http_accept_language = Input::instance()->server("HTTP_ACCEPT_LANGUAGE"); + if ($http_accept_language) { + // Parse the HTTP header and build a preference list + // Example value: "de,en-us;q=0.7,en-uk,fr-fr;q=0.2" + $locale_preferences = array(); + foreach (explode(",", $http_accept_language) as $code) { + list ($requested_locale, $qvalue) = explode(";", $code . ";"); + $requested_locale = trim($requested_locale); + $qvalue = trim($qvalue); + if (preg_match("/^([a-z]{2,3})(?:[_-]([a-zA-Z]{2}))?/", $requested_locale, $matches)) { + $requested_locale = strtolower($matches[1]); + if (!empty($matches[2])) { + $requested_locale .= "_" . strtoupper($matches[2]); + } + $requested_locale = trim(str_replace("-", "_", $requested_locale)); + if (!strlen($qvalue)) { + // If not specified, default to 1. + $qvalue = 1; + } else { + // qvalue is expected to be something like "q=0.7" + list ($ignored, $qvalue) = explode("=", $qvalue . "=="); + $qvalue = floatval($qvalue); + } + $locale_preferences[] = array($requested_locale, $qvalue); + } + } + + // Compare and score requested locales with installed ones + $scored_locales = array(); + foreach ($locale_preferences as $requested_value) { + $scored_locale_match = self::_locale_match_score($requested_value); + if ($scored_locale_match) { + $scored_locales[] = $scored_locale_match; + } + } + + usort($scored_locales, array("locales", "_compare_locale_by_qvalue")); + + $best_match = array_shift($scored_locales); + if ($best_match) { + return $best_match[0]; + } + } + + return null; + } + + static function _compare_locale_by_qvalue($a, $b) { + $a = $a[1]; + $b = $b[1]; + if ($a == $b) { + return 0; + } + return $a < $b ? 1 : -1; + } + + private static function _locale_match_score($requested_locale_and_qvalue) { + list ($requested_locale, $qvalue) = $requested_locale_and_qvalue; + $installed = self::installed(); + if (isset($installed[$requested_locale])) { + return $requested_locale_and_qvalue; + } + list ($language) = explode("_", $requested_locale . "_"); + if (isset(self::$language_subtag_to_locale[$language]) && + isset($installed[self::$language_subtag_to_locale[$language]])) { + return array(self::$language_subtag_to_locale[$language], $qvalue * 0.66); + } + return null; + } }
\ No newline at end of file diff --git a/modules/gallery/helpers/model_cache.php b/modules/gallery/helpers/model_cache.php index 2649fdbd..a3e09862 100644 --- a/modules/gallery/helpers/model_cache.php +++ b/modules/gallery/helpers/model_cache.php @@ -32,10 +32,8 @@ class model_cache_Core { return self::$cache->$model_name->$field_name->$id; } - static function clear($model_name, $id, $field_name="id") { - if (!empty(self::$cache->$model_name->$field_name->$id)) { - unset(self::$cache->$model_name->$field_name->$id); - } + static function clear() { + self::$cache = new stdClass(); } static function set($model) { diff --git a/modules/gallery/helpers/module.php b/modules/gallery/helpers/module.php index 0d483206..a3088c38 100644 --- a/modules/gallery/helpers/module.php +++ b/modules/gallery/helpers/module.php @@ -119,6 +119,8 @@ class module_Core { $installer_class = "{$module_name}_installer"; if (method_exists($installer_class, "install")) { call_user_func_array(array($installer_class, "install"), array()); + } else { + module::set_version($module_name, 1); } module::load_modules(); @@ -145,6 +147,13 @@ class module_Core { $installer_class = "{$module_name}_installer"; if (method_exists($installer_class, "upgrade")) { call_user_func_array(array($installer_class, "upgrade"), array($version_before)); + } else { + $available = module::available(); + if (isset($available->$module_name->code_version)) { + module::set_version($module_name, $available->$module_name->code_version); + } else { + throw new Exception("@todo UNKNOWN_MODULE"); + } } module::load_modules(); @@ -274,11 +283,9 @@ class module_Core { array_shift($args); $function = str_replace(".", "_", $name); - foreach (self::$modules as $module) { - if (!$module->active) { - continue; - } - + // @todo: consider calling gallery_event first, since for things menus we need it to do some + // setup + foreach (self::$active as $module) { $class = "{$module->name}_event"; if (method_exists($class, $function)) { call_user_func_array(array($class, $function), $args); diff --git a/modules/gallery/helpers/movie.php b/modules/gallery/helpers/movie.php index d62ead76..59bf5c19 100644 --- a/modules/gallery/helpers/movie.php +++ b/modules/gallery/helpers/movie.php @@ -31,10 +31,11 @@ class movie_Core { * @param string $name the filename to use for this photo in the album * @param integer $title the title of the new photo * @param string $description (optional) the longer description of this photo + * @param string $slug (optional) the url component for this photo * @return Item_Model */ static function create($parent, $filename, $name, $title, - $description=null, $owner_id=null) { + $description=null, $owner_id=null, $slug=null) { if (!$parent->loaded || !$parent->is_album()) { throw new Exception("@todo INVALID_PARENT"); } @@ -67,6 +68,10 @@ class movie_Core { $name .= "." . $pi["extension"]; } + if (empty($slug)) { + $slug = item::convert_filename_to_slug($name); + } + $movie = ORM::factory("item"); $movie->type = "movie"; $movie->title = $title; @@ -79,15 +84,21 @@ class movie_Core { $movie->thumb_dirty = 1; $movie->resize_dirty = 1; $movie->sort_column = "weight"; + $movie->slug = $slug; $movie->rand_key = ((float)mt_rand()) / (float)mt_getrandmax(); // Randomize the name if there's a conflict + // @todo Improve this. Random numbers are not user friendly while (ORM::factory("item") ->where("parent_id", $parent->id) + ->open_paren() ->where("name", $movie->name) + ->orwhere("slug", $movie->slug) + ->close_paren() ->find()->id) { - // @todo Improve this. Random numbers are not user friendly - $movie->name = rand() . "." . $pi["extension"]; + $rand = rand(); + $movie->name = "{$name}.$rand.{$pi['extension']}"; + $movie->slug = "{$slug}-$rand"; } // This saves the photo @@ -102,6 +113,8 @@ class movie_Core { copy($filename, $movie->file_path()); + // @todo: publish this from inside Item_Model::save() when we refactor to the point where + // there's only one save() happening here. module::event("item_created", $movie); // Build our thumbnail @@ -139,7 +152,7 @@ class movie_Core { $cmd = escapeshellcmd($ffmpeg) . " -i " . escapeshellarg($input_file) . " -an -ss 00:00:03 -an -r 1 -vframes 1" . - " -y -f mjpeg " . escapeshellarg($output_file); + " -y -f mjpeg " . escapeshellarg($output_file) . " 2>&1"; exec($cmd); } diff --git a/modules/gallery/helpers/p.php b/modules/gallery/helpers/p.php deleted file mode 100644 index 862c769b..00000000 --- a/modules/gallery/helpers/p.php +++ /dev/null @@ -1,39 +0,0 @@ -<?php defined("SYSPATH") or die("No direct script access."); -/** - * Gallery - a web based photo album viewer and editor - * Copyright (C) 2000-2009 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 p_Core { - private static $_purifier = null; - static function clean($dirty_html) { - return html::specialchars($dirty_html); - } - - static function purify($dirty_html) { - if (empty(self::$_purifier)) { - require_once(dirname(__file__) . "/../lib/HTMLPurifier/HTMLPurifier.auto.php"); - $config = HTMLPurifier_Config::createDefault(); - foreach (Kohana::config('purifier') as $category => $key_value) { - foreach ($key_value as $key => $value) { - $config->set("$category.$key", $value); - } - } - self::$_purifier = new HTMLPurifier($config); - } - return self::$_purifier->purify($dirty_html); - } -} diff --git a/modules/gallery/helpers/photo.php b/modules/gallery/helpers/photo.php index e8a4f357..3d9fbe69 100644 --- a/modules/gallery/helpers/photo.php +++ b/modules/gallery/helpers/photo.php @@ -31,10 +31,11 @@ class photo_Core { * @param string $name the filename to use for this photo in the album * @param integer $title the title of the new photo * @param string $description (optional) the longer description of this photo + * @param string $slug (optional) the url component for this photo * @return Item_Model */ static function create($parent, $filename, $name, $title, - $description=null, $owner_id=null) { + $description=null, $owner_id=null, $slug=null) { if (!$parent->loaded || !$parent->is_album()) { throw new Exception("@todo INVALID_PARENT"); } @@ -66,6 +67,10 @@ class photo_Core { $name .= "." . $pi["extension"]; } + if (empty($slug)) { + $slug = item::convert_filename_to_slug($name); + } + $photo = ORM::factory("item"); $photo->type = "photo"; $photo->title = $title; @@ -78,15 +83,21 @@ class photo_Core { $photo->thumb_dirty = 1; $photo->resize_dirty = 1; $photo->sort_column = "weight"; + $photo->slug = $slug; $photo->rand_key = ((float)mt_rand()) / (float)mt_getrandmax(); - // Randomize the name if there's a conflict + // Randomize the name or slug if there's a conflict + // @todo Improve this. Random numbers are not user friendly while (ORM::factory("item") ->where("parent_id", $parent->id) + ->open_paren() ->where("name", $photo->name) + ->orwhere("slug", $photo->slug) + ->close_paren() ->find()->id) { - // @todo Improve this. Random numbers are not user friendly - $photo->name = rand() . "." . $pi["extension"]; + $rand = rand(); + $photo->name = "{$name}.$rand.{$pi['extension']}"; + $photo->slug = "{$slug}-$rand"; } // This saves the photo @@ -105,10 +116,18 @@ class photo_Core { copy($filename, $photo->file_path()); + // @todo: publish this from inside Item_Model::save() when we refactor to the point where + // there's only one save() happening here. module::event("item_created", $photo); - // Build our thumbnail/resizes - graphics::generate($photo); + // Build our thumbnail/resizes. If we fail to build thumbnail/resize we assume that the image + // is bad in some way and discard it. + try { + graphics::generate($photo); + } catch (Exception $e) { + $photo->delete(); + throw $e; + } // If the parent has no cover item, make this it. if (access::can("edit", $parent) && $parent->album_cover_item_id == null) { @@ -121,10 +140,15 @@ class photo_Core { static function get_add_form($parent) { $form = new Forge("albums/{$parent->id}", "", "post", array("id" => "gAddPhotoForm")); $group = $form->group("add_photo")->label( - t("Add Photo to %album_title", array("album_title" =>$parent->title))); + t("Add Photo to %album_title", array("album_title" => $parent->title))); $group->input("title")->label(t("Title")); $group->textarea("description")->label(t("Description")); $group->input("name")->label(t("Filename")); + $group->input("slug")->label(t("Internet Address"))->value($photo->slug) + ->callback("item::validate_url_safe") + ->error_messages( + "not_url_safe", + t("The internet address should contain only letters, numbers, hyphens and underscores")); $group->upload("file")->label(t("File"))->rules("required|allow[jpg,png,gif,flv,mp4]"); $group->hidden("type")->value("photo"); $group->submit("")->value(t("Upload")); @@ -135,16 +159,26 @@ class photo_Core { static function get_edit_form($photo) { $form = new Forge("photos/$photo->id", "", "post", array("id" => "gEditPhotoForm")); $form->hidden("_method")->value("put"); - $group = $form->group("edit_photo")->label(t("Edit Photo")); + $group = $form->group("edit_item")->label(t("Edit Photo")); $group->input("title")->label(t("Title"))->value($photo->title); $group->textarea("description")->label(t("Description"))->value($photo->description); $group->input("filename")->label(t("Filename"))->value($photo->name) - ->error_messages("conflict", t("There is already a file with this name")) + ->error_messages("name_conflict", t("There is already a photo or album with this name")) ->callback("item::validate_no_slashes") ->error_messages("no_slashes", t("The photo name can't contain a \"/\"")) ->callback("item::validate_no_trailing_period") ->error_messages("no_trailing_period", t("The photo name can't end in \".\"")); + $group->input("slug")->label(t("Internet Address"))->value($photo->slug) + ->callback("item::validate_url_safe") + ->error_messages( + "slug_conflict", t("There is already a photo or album with this internet address")) + ->error_messages( + "not_url_safe", + t("The internet address should contain only letters, numbers, hyphens and underscores")); + + module::event("item_edit_form", $photo, $form); + $group = $form->group("buttons")->label(""); $group->submit("")->value(t("Modify")); $form->add_rules_from(ORM::factory("item")); return $form; diff --git a/modules/gallery/helpers/task.php b/modules/gallery/helpers/task.php index 6a9f63c2..9fa04305 100644 --- a/modules/gallery/helpers/task.php +++ b/modules/gallery/helpers/task.php @@ -84,10 +84,11 @@ class task_Core { } $task->save(); } catch (Exception $e) { + Kohana::log("error", $e->__toString()); $task->log($e->__toString()); $task->state = "error"; $task->done = true; - $task->status = $e->getMessage(); + $task->status = substr($e->getMessage(), 0, 255); $task->save(); } |