diff options
Diffstat (limited to 'modules')
36 files changed, 635 insertions, 182 deletions
diff --git a/modules/comment/helpers/comment_theme.php b/modules/comment/helpers/comment_theme.php index 38a00b5c..e9b402f6 100644 --- a/modules/comment/helpers/comment_theme.php +++ b/modules/comment/helpers/comment_theme.php @@ -27,6 +27,7 @@ class comment_theme_Core { $block = new Block; $block->css_id = "gComments"; $block->title = t("Comments"); + $block->anchor = "comments"; $view = new View("comments.html"); $view->comments = ORM::factory("comment") diff --git a/modules/comment/js/comment.js b/modules/comment/js/comment.js index 9fd63c1a..bff83770 100644 --- a/modules/comment/js/comment.js +++ b/modules/comment/js/comment.js @@ -1,13 +1,15 @@ $("document").ready(function() { $("#gAddCommentButton").click(function(event) { event.preventDefault(); - $.get($(this).attr("href"), - {}, - function(data) { - $("#gCommentDetail").append(data); - ajaxify_comment_form(); - }); - }); + if (!$("#gAddCommentForm").length) { + $.get($(this).attr("href"), + {}, + function(data) { + $("#gCommentDetail").append(data); + ajaxify_comment_form(); + }); + } + }); }); function ajaxify_comment_form() { @@ -22,7 +24,7 @@ function ajaxify_comment_form() { $.get(data.resource, function(data, textStatus) { $("#gComments .gBlockContent ul:first").append("<li>"+data+"</li>"); $("#gComments .gBlockContent ul:first li:last").effect("highlight", {color: "#cfc"}, 8000); - $("#gAddCommentForm").hide(2000); + $("#gAddCommentForm").hide(2000).remove(); $("#gNoCommentsYet").hide(2000); }); } diff --git a/modules/exif/helpers/exif_event.php b/modules/exif/helpers/exif_event.php index 826ec959..81ac5f44 100644 --- a/modules/exif/helpers/exif_event.php +++ b/modules/exif/helpers/exif_event.php @@ -18,6 +18,14 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ class exif_event_Core { + static function gallery_ready() { + if (!function_exists("gettext")) { + function gettext($message) { + return (string) t($message); + } + } + } + static function item_created($item) { if (!$item->is_album()) { exif::extract($item); diff --git a/modules/gallery/controllers/packager.php b/modules/gallery/controllers/packager.php index fbb1d07d..ae87d74b 100644 --- a/modules/gallery/controllers/packager.php +++ b/modules/gallery/controllers/packager.php @@ -114,18 +114,24 @@ class Packager_Controller extends Controller { $root = ORM::factory("item", 1); $root_created_timestamp = $root->created; $root_updated_timestamp = $root->updated; + $table_name = ""; foreach (file($sql_file) as $line) { // Prefix tables $line = preg_replace( "/(CREATE TABLE|IF EXISTS|INSERT INTO) `{$dbconfig['table_prefix']}(\w+)`/", "\\1 {\\2}", $line); + if (preg_match("/CREATE TABLE {(\w+)}/", $line, $matches)) { + $table_name = $matches[1]; + } // Normalize dates $line = preg_replace("/,$root_created_timestamp,/", ",UNIX_TIMESTAMP(),", $line); $line = preg_replace("/,$root_updated_timestamp,/", ",UNIX_TIMESTAMP(),", $line); - // Remove ENGINE= specifications - $line = preg_replace("/ENGINE=\S+ /", "", $line); + // Remove ENGINE= specifications execpt for search records, it always needs to be MyISAM + if ($table_name != "search_records") { + $line = preg_replace("/ENGINE=\S+ /", "", $line); + } $buf .= $line; } diff --git a/modules/gallery/controllers/permissions.php b/modules/gallery/controllers/permissions.php index 5f4620b2..8d75862e 100644 --- a/modules/gallery/controllers/permissions.php +++ b/modules/gallery/controllers/permissions.php @@ -81,7 +81,7 @@ class Permissions_Controller extends Controller { } } - function _get_form($item) { + private function _get_form($item) { $view = new View("permissions_form.html"); $view->item = $item; $view->groups = ORM::factory("group")->find_all(); diff --git a/modules/gallery/controllers/simple_uploader.php b/modules/gallery/controllers/simple_uploader.php index 156d18ac..bc508319 100644 --- a/modules/gallery/controllers/simple_uploader.php +++ b/modules/gallery/controllers/simple_uploader.php @@ -32,6 +32,7 @@ class Simple_Uploader_Controller extends Controller { } public function start() { + access::verify_csrf(); batch::start(); } diff --git a/modules/gallery/helpers/album.php b/modules/gallery/helpers/album.php index dfb1e66d..9cd746d7 100644 --- a/modules/gallery/helpers/album.php +++ b/modules/gallery/helpers/album.php @@ -135,6 +135,9 @@ class album_Core { ->error_messages( "not_url_safe", t("The internet address should contain only letters, numbers, hyphens and underscores")); + } else { + $group->hidden("dirname")->value($parent->name); + $group->hidden("slug")->value($parent->slug); } $sort_order = $group->group("sort_order", array("id" => "gAlbumSortOrder")) diff --git a/modules/gallery/helpers/gallery.php b/modules/gallery/helpers/gallery.php index 813134eb..80ae65bd 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 git (pre-beta3)"; + const VERSION = "3.0 git (pre-RC1)"; /** * If Gallery is in maintenance mode, then force all non-admins to get routed to a "This site is @@ -95,15 +95,22 @@ class gallery_Core { $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()) { + $is_album_writable = + is_writable($item->is_album() ? $item->file_path() : $item->parent()->file_path()); + if ($is_album_writable) { $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"))); + ->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"))); + } + } else { + message::warning(t("The album '%album_name' is not writable.", + array("album_name" => $item->title))); } } diff --git a/modules/gallery/helpers/gallery_installer.php b/modules/gallery/helpers/gallery_installer.php index a1856424..6500482b 100644 --- a/modules/gallery/helpers/gallery_installer.php +++ b/modules/gallery/helpers/gallery_installer.php @@ -203,6 +203,12 @@ class gallery_installer { access::register_permission("edit", "Edit"); access::register_permission("add", "Add"); + // Mark for translation (must be the same strings as used above) + t("View Full Size"); + t("View"); + t("Edit"); + t("Add"); + $root = ORM::factory("item"); $root->type = "album"; $root->title = "Gallery"; diff --git a/modules/gallery/helpers/locales.php b/modules/gallery/helpers/locales.php index 16dda2d7..ab7f7526 100644 --- a/modules/gallery/helpers/locales.php +++ b/modules/gallery/helpers/locales.php @@ -165,50 +165,58 @@ class locales_Core { list ($ignored, $qvalue) = explode("=", $qvalue . "=="); $qvalue = floatval($qvalue); } - $locale_preferences[] = array($requested_locale, $qvalue); + // Group by language to boost inexact same-language matches + list ($language) = explode("_", $requested_locale . "_"); + if (!isset($locale_preferences[$language])) { + $locale_preferences[$language] = array(); + } + $locale_preferences[$language][$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; + foreach ($locale_preferences as $language => $requested_locales) { + // Inexact match adjustment (same language, different region) + $fallback_adjustment_factor = 0.95; + if (count($requested_locales) > 1) { + // Sort by qvalue, descending + $qvalues = array_values($requested_locales); + rsort($qvalues); + // Ensure inexact match scores worse than 2nd preference in same language. + $fallback_adjustment_factor *= $qvalues[1]; + } + foreach ($requested_locales as $requested_locale => $qvalue) { + list ($matched_locale, $match_score) = + self::_locale_match_score($requested_locale, $qvalue, $fallback_adjustment_factor); + if ($matched_locale && + (!isset($scored_locales[$matched_locale]) || + $match_score > $scored_locales[$matched_locale])) { + $scored_locales[$matched_locale] = $match_score; + } } } - usort($scored_locales, array("locales", "_compare_locale_by_qvalue")); + arsort($scored_locales); - $best_match = array_shift($scored_locales); - if ($best_match) { - return $best_match[0]; - } + list ($locale) = each($scored_locales); + return $locale; } 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; + private static function _locale_match_score($requested_locale, $qvalue, $adjustment_factor) { $installed = self::installed(); if (isset($installed[$requested_locale])) { - return $requested_locale_and_qvalue; + return array($requested_locale, $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); + $score = $adjustment_factor * $qvalue; + return array(self::$language_subtag_to_locale[$language], $score); } - return null; + return array(null, 0); } }
\ No newline at end of file diff --git a/modules/gallery/libraries/Theme_View.php b/modules/gallery/libraries/Theme_View.php index 541bce88..130e2dce 100644 --- a/modules/gallery/libraries/Theme_View.php +++ b/modules/gallery/libraries/Theme_View.php @@ -111,6 +111,12 @@ class Theme_View_Core extends Gallery_View { return $menu->compact(); } + public function movie_menu() { + $menu = Menu::factory("root"); + module::event("movie_menu", $menu, $this); + return $menu->compact(); + } + public function context_menu($item, $thumbnail_css_selector) { $menu = Menu::factory("root") ->append(Menu::factory("submenu") diff --git a/modules/gallery/models/item.php b/modules/gallery/models/item.php index d1c6feb9..ff02daf8 100644 --- a/modules/gallery/models/item.php +++ b/modules/gallery/models/item.php @@ -284,8 +284,8 @@ class Item_Model extends ORM_MPTT { ->where("id <>", 1) ->orderby("left_ptr", "ASC") ->get() as $row) { - $names[] = urlencode($row->name); - $slugs[] = urlencode($row->slug); + $names[] = rawurlencode($row->name); + $slugs[] = rawurlencode($row->slug); } $this->relative_path_cache = implode($names, "/"); $this->relative_url_cache = implode($slugs, "/"); diff --git a/modules/gallery/tests/Controller_Auth_Test.php b/modules/gallery/tests/Controller_Auth_Test.php new file mode 100644 index 00000000..caf6d8f2 --- /dev/null +++ b/modules/gallery/tests/Controller_Auth_Test.php @@ -0,0 +1,252 @@ +<?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 Controller_Auth_Test extends Unit_Test_Case { + static $rest_methods = array("_index", "_show", "_form_edit", "_form_add", "_create", + "_update", "_delete"); + + static $rest_methods_with_csrf_check = array("_update", "_delete", "_create"); + + public function find_missing_auth_test() { + $found = array(); + $controllers = glob("*/*/controllers/*.php"); + $feeds = glob("*/*/helpers/*_rss.php"); + foreach (array_merge($controllers, $feeds) as $controller) { + if (preg_match("{modules/(gallery_)?unit_test/}", $controller)) { + continue; + } + + // List of all tokens without whitespace, simplifying parsing. + $tokens = array(); + foreach (token_get_all(file_get_contents($controller)) as $token) { + if (!is_array($token) || $token[0] != T_WHITESPACE) { + $tokens[] = $token; + } + } + + $is_admin_controller = false; + $is_rest_controller = false; + + $open_braces = 0; + $function = null; + for ($token_number = 0; $token_number < count($tokens); $token_number++) { + $token = $tokens[$token_number]; + + // Count braces. + // 1 open brace = in class context. + // 2 open braces = in function. + if (!is_array($token)) { + if ($token == "}") { + $open_braces--; + if ($open_braces == 1 && $function) { + $found[$controller][] = $function; + $function = null; + } else if ($open_braces == 0) { + $is_admin_controller = false; + $is_rest_controller = false; + } + } else if ($token == "{") { + $open_braces++; + } + } else { + // An array token + + if ($open_braces == 0 && $token[0] == T_EXTENDS) { + if (self::_token_matches(array(T_STRING, "Admin_Controller"), $tokens, $token_number + 1)) { + $is_admin_controller = true; + } else if (self::_token_matches(array(T_STRING, "REST_Controller"), $tokens, $token_number + 1)) { + $is_rest_controller = true; + } + } else if ($open_braces == 1 && $token[0] == T_FUNCTION) { + $line = $token[2]; + $name = ""; + // Search backwards to check visibility, + // "private function", or "private static function" + $previous = $tokens[$token_number - 1][0]; + $previous_2 = $tokens[$token_number - 2][0]; + $is_private = in_array($previous, array(T_PRIVATE, T_PROTECTED)) || + in_array($previous_2, array(T_PRIVATE, T_PROTECTED)); + $is_static = $previous == T_STATIC || $previous_2 == T_STATIC; + + // Search forward to get function name + do { + $token_number++; + if (self::_token_matches(array(T_STRING), $tokens, $token_number)) { + $token = $tokens[$token_number]; + $name = $token[1]; + break; + } + } while ($token_number < count($tokens)); + + $is_rss_feed = $name == "feed" && strpos(basename($controller), "_rss.php"); + + if ((!$is_static || $is_rss_feed) && + (!$is_private || + ($is_rest_controller && in_array($name, self::$rest_methods)))) { + $function = self::_function($name, $line, $is_admin_controller); + if ($is_rest_controller && in_array($name, self::$rest_methods_with_csrf_check)) { + $function->checks_csrf(true); + } + } + } + + // Check body of all public functions + // + // Authorization + // Require: access::required\( + // Authentication (CSRF token) + // [When using Input, $this->input, Forge] + // Require: ->validate() or access::verify_csrf\( + if ($function && $open_braces >= 2) { + if ($token[0] == T_STRING) { + if ($token[1] == "access" && + self::_token_matches(array(T_DOUBLE_COLON, "::"), $tokens, $token_number + 1) && + self::_token_matches(array(T_STRING), $tokens, $token_number + 2) && + in_array($tokens[$token_number + 2][1], array("forbidden", "required")) && + self::_token_matches("(", $tokens, $token_number + 3)) { + $token_number += 3; + $function->checks_authorization(true); + } else if ($token[1] == "access" && + self::_token_matches(array(T_DOUBLE_COLON, "::"), $tokens, $token_number + 1) && + self::_token_matches(array(T_STRING, "verify_csrf"), $tokens, $token_number + 2) && + self::_token_matches("(", $tokens, $token_number + 3)) { + $token_number += 3; + $function->checks_csrf(true); + } else if (in_array($token[1], array("Input", "Forge")) && + self::_token_matches(array(T_DOUBLE_COLON, "::"), $tokens, $token_number + 1)) { + $token_number++; + $function->uses_input(true); + } + } else if ($token[0] == T_VARIABLE) { + if ($token[1] == '$this' && + self::_token_matches(array(T_OBJECT_OPERATOR), $tokens, $token_number + 1) && + self::_token_matches(array(T_STRING, "input"), $tokens, $token_number + 2)) { + $token_number += 2; + $function->uses_input(true); + } + } else if ($token[0] == T_OBJECT_OPERATOR) { + if (self::_token_matches(array(T_STRING, "validate"), $tokens, $token_number + 1) && + self::_token_matches("(", $tokens, $token_number + 2)) { + $token_number += 2; + $function->checks_csrf(true); + } + } + } + } + } + } + + // Generate the report + $new = TMPPATH . "controller_auth_data.txt"; + $fd = fopen($new, "wb"); + ksort($found); + foreach ($found as $controller => $functions) { + $is_admin_controller = true; + foreach ($functions as $function) { + $is_admin_controller &= $function->is_admin_controller; + $flags = array(); + if ($function->uses_input() && !$function->checks_csrf()) { + $flags[] = "DIRTY_CSRF"; + } + if (!$function->is_admin_controller && !$function->checks_authorization()) { + $flags[] = "DIRTY_AUTH"; + } + + if (!$flags) { + // Don't print CLEAN instances + continue; + } + + fprintf($fd, "%-60s %-20s %s\n", + $controller, $function->name, implode("|", $flags)); + } + + if (strpos(basename($controller), "admin_") === 0 && !$is_admin_controller) { + fprintf($fd, "%-60s %-20s %s\n", + $controller, basename($controller), "NO_ADMIN_CONTROLLER"); + } + } + fclose($fd); + + // Compare with the expected report from our golden file. + $canonical = MODPATH . "gallery/tests/controller_auth_data.txt"; + exec("diff $canonical $new", $output, $return_value); + $this->assert_false( + $return_value, "Controller auth golden file mismatch. Output:\n" . implode("\n", $output) ); + } + + private static function _token_matches($expected_token, &$tokens, $token_number) { + if (!isset($tokens[$token_number])) { + return false; + } + + $token = $tokens[$token_number]; + + if (is_array($expected_token)) { + for ($i = 0; $i < count($expected_token); $i++) { + if ($expected_token[$i] != $token[$i]) { + return false; + } + } + return true; + } else { + return $expected_token == $token; + } + } + + static function _function($name, $line, $is_admin_controller) { + return new Controller_Auth_Test_Function($name, $line, $is_admin_controller); + } +} + +class Controller_Auth_Test_Function { + public $name; + public $line; + public $is_admin_controller = false; + private $_uses_input = false; + private $_checks_authorization = false; + private $_checks_csrf = false; + + function __construct($name, $line, $is_admin_controller) { + $this->name = $name; + $this->line = $line; + $this->is_admin_controller = $is_admin_controller; + } + + function uses_input($val=null) { + if ($val !== null) { + $this->_uses_input = (bool) $val; + } + return $this->_uses_input; + } + + function checks_authorization($val=null) { + if ($val !== null) { + $this->_checks_authorization = (bool) $val; + } + return $this->_checks_authorization; + } + + function checks_csrf($val=null) { + if ($val !== null) { + $this->_checks_csrf = $val; + } + return $this->_checks_csrf; + } +}
\ No newline at end of file diff --git a/modules/gallery/tests/Item_Model_Test.php b/modules/gallery/tests/Item_Model_Test.php index 585e247c..84210e4c 100644 --- a/modules/gallery/tests/Item_Model_Test.php +++ b/modules/gallery/tests/Item_Model_Test.php @@ -150,4 +150,14 @@ class Item_Model_Test extends Unit_Test_Case { $this->assert_same("ORIGINAL_VALUE", $item->original()->title); $this->assert_same("NEW_VALUE", $item->title); } + + public function urls_are_rawurlencoded_test() { + $item = self::_create_random_item(); + $item->slug = "foo bar"; + $item->name = "foo bar.jpg"; + $item->save(); + + $this->assert_equal("foo%20bar", $item->relative_url()); + $this->assert_equal("foo%20bar.jpg", $item->relative_path()); + } } diff --git a/modules/gallery/tests/Locales_Helper_Test.php b/modules/gallery/tests/Locales_Helper_Test.php index 85b8e206..4c03d8d4 100644 --- a/modules/gallery/tests/Locales_Helper_Test.php +++ b/modules/gallery/tests/Locales_Helper_Test.php @@ -67,7 +67,7 @@ class Locales_Helper_Test extends Unit_Test_Case { locales::update_installed(array("no_NO", "pt_PT", "ja_JP")); $_SERVER["HTTP_ACCEPT_LANGUAGE"] = "en,en-us,ja_JP;q=0.7,no-fr;q=0.9"; $locale = locales::locale_from_http_request(); - $this->assert_equal("ja_JP", $locale); + $this->assert_equal("no_NO", $locale); } public function locale_from_http_request_best_match_vs_installed_2_test() { @@ -83,4 +83,12 @@ class Locales_Helper_Test extends Unit_Test_Case { $locale = locales::locale_from_http_request(); $this->assert_equal(null, $locale); } + + public function locale_from_http_request_prefer_inexact_same_language_match_over_exact_other_language_match_test() { + locales::update_installed(array("de_DE", "ar_AR", "fa_IR", "he_IL", "en_US")); + // Accept-Language header from Firefox 3.5/Ubuntu + $_SERVER["HTTP_ACCEPT_LANGUAGE"] = "he,en-us;q=0.9,de-ch;q=0.5,en;q=0.3"; + $locale = locales::locale_from_http_request(); + $this->assert_equal("he_IL", $locale); + } }
\ No newline at end of file diff --git a/modules/gallery/tests/Xss_Security_Test.php b/modules/gallery/tests/Xss_Security_Test.php index 85624517..16541017 100644 --- a/modules/gallery/tests/Xss_Security_Test.php +++ b/modules/gallery/tests/Xss_Security_Test.php @@ -144,7 +144,8 @@ class Xss_Security_Test extends Unit_Test_Case { "dynamic_bottom", "dynamic_top", "footer", "head", "header_bottom", "header_top", "page_bottom", "page_top", "photo_blocks", "photo_bottom", "photo_top", "resize_bottom", "resize_top", "sidebar_blocks", "sidebar_bottom", - "sidebar_top", "thumb_bottom", "thumb_info", "thumb_top")) && + "sidebar_top", "thumb_bottom", "thumb_info", "thumb_top", + "movie_menu")) && self::_token_matches("(", $tokens, $token_number + 3)) { $method = $tokens[$token_number + 2][1]; diff --git a/modules/gallery/tests/controller_auth_data.txt b/modules/gallery/tests/controller_auth_data.txt new file mode 100644 index 00000000..fdf00c5e --- /dev/null +++ b/modules/gallery/tests/controller_auth_data.txt @@ -0,0 +1,40 @@ +modules/comment/controllers/admin_comments.php queue DIRTY_CSRF +modules/comment/controllers/comments.php _index DIRTY_CSRF +modules/comment/helpers/comment_rss.php feed DIRTY_AUTH +modules/digibug/controllers/digibug.php print_proxy DIRTY_CSRF|DIRTY_AUTH +modules/digibug/controllers/digibug.php close_window DIRTY_AUTH +modules/gallery/controllers/admin.php __call DIRTY_AUTH +modules/gallery/controllers/albums.php _show DIRTY_CSRF +modules/gallery/controllers/albums.php _form_add DIRTY_CSRF +modules/gallery/controllers/combined.php javascript DIRTY_AUTH +modules/gallery/controllers/combined.php css DIRTY_AUTH +modules/gallery/controllers/file_proxy.php __call DIRTY_CSRF|DIRTY_AUTH +modules/gallery/controllers/maintenance.php index DIRTY_AUTH +modules/gallery/controllers/rest.php __construct DIRTY_AUTH +modules/gallery/controllers/rest.php __call DIRTY_AUTH +modules/gallery/controllers/rest.php form_edit DIRTY_AUTH +modules/gallery/controllers/rest.php form_add DIRTY_AUTH +modules/gallery/controllers/rest.php _index DIRTY_AUTH +modules/gallery/controllers/rest.php _create DIRTY_AUTH +modules/gallery/controllers/rest.php _show DIRTY_AUTH +modules/gallery/controllers/rest.php _update DIRTY_AUTH +modules/gallery/controllers/rest.php _delete DIRTY_AUTH +modules/gallery/controllers/rest.php _form_add DIRTY_AUTH +modules/gallery/controllers/rest.php _form_edit DIRTY_AUTH +modules/gallery/controllers/simple_uploader.php start DIRTY_AUTH +modules/gallery/controllers/simple_uploader.php finish DIRTY_AUTH +modules/gallery/controllers/upgrader.php index DIRTY_AUTH +modules/gallery/controllers/welcome_message.php index DIRTY_AUTH +modules/rss/controllers/rss.php feed DIRTY_CSRF|DIRTY_AUTH +modules/search/controllers/search.php index DIRTY_CSRF|DIRTY_AUTH +modules/server_add/controllers/admin_server_add.php autocomplete DIRTY_CSRF +modules/server_add/controllers/server_add.php children DIRTY_CSRF +modules/tag/controllers/admin_tags.php index DIRTY_CSRF +modules/tag/controllers/tags.php _show DIRTY_CSRF|DIRTY_AUTH +modules/user/controllers/login.php ajax DIRTY_AUTH +modules/user/controllers/login.php auth_ajax DIRTY_AUTH +modules/user/controllers/login.php html DIRTY_AUTH +modules/user/controllers/login.php auth_html DIRTY_AUTH +modules/user/controllers/logout.php index DIRTY_CSRF|DIRTY_AUTH +modules/user/controllers/password.php reset DIRTY_AUTH +modules/user/controllers/password.php do_reset DIRTY_CSRF|DIRTY_AUTH diff --git a/modules/gallery/tests/xss_data.txt b/modules/gallery/tests/xss_data.txt index 8c71740e..57da8730 100644 --- a/modules/gallery/tests/xss_data.txt +++ b/modules/gallery/tests/xss_data.txt @@ -48,6 +48,7 @@ modules/gallery/views/admin_block_log_entries.html.php 8 DIRTY $entry modules/gallery/views/admin_block_news.html.php 5 DIRTY_JS $entry["link"] modules/gallery/views/admin_block_news.html.php 5 DIRTY $entry["title"] modules/gallery/views/admin_block_news.html.php 7 DIRTY text::limit_words(strip_tags($entry["description"]),25); +modules/gallery/views/admin_block_photo_stream.html.php 5 DIRTY_JS $photo->url() modules/gallery/views/admin_block_photo_stream.html.php 6 DIRTY photo::img_dimensions($photo->width,$photo->height,72) modules/gallery/views/admin_block_photo_stream.html.php 7 DIRTY_ATTR $photo->thumb_url() modules/gallery/views/admin_dashboard.html.php 5 DIRTY_JS $csrf @@ -180,14 +181,14 @@ modules/image_block/views/image_block_block.html.php 3 DIRTY_JS $item- modules/image_block/views/image_block_block.html.php 4 DIRTY $item->thumb_img(array("class"=>"gThumbnail")) modules/info/views/info_block.html.php 22 DIRTY date("M j, Y H:i:s",$item->captured) modules/info/views/info_block.html.php 29 DIRTY_JS $item->owner->url -modules/notification/views/comment_published.html.php 28 DIRTY_JS $comment->item()->url(array(),true) -modules/notification/views/comment_published.html.php 29 DIRTY $comment->item()->url(array(),true) -modules/notification/views/item_added.html.php 16 DIRTY_JS $item->url(array(),true) -modules/notification/views/item_added.html.php 17 DIRTY $item->url(array(),true) -modules/notification/views/item_deleted.html.php 18 DIRTY_JS $item->parent()->url(array(),true) -modules/notification/views/item_deleted.html.php 19 DIRTY $item->parent()->url(array(),true) -modules/notification/views/item_updated.html.php 20 DIRTY_JS $item->url(array(),true) -modules/notification/views/item_updated.html.php 20 DIRTY $item->url(array(),true) +modules/notification/views/comment_published.html.php 28 DIRTY_JS $comment->item()->abs_url() +modules/notification/views/comment_published.html.php 29 DIRTY $comment->item()->abs_url() +modules/notification/views/item_added.html.php 16 DIRTY_JS $item->abs_url() +modules/notification/views/item_added.html.php 17 DIRTY $item->abs_url() +modules/notification/views/item_deleted.html.php 18 DIRTY_JS $item->parent()->abs_url() +modules/notification/views/item_deleted.html.php 19 DIRTY $item->parent()->abs_url() +modules/notification/views/item_updated.html.php 20 DIRTY_JS $item->abs_url() +modules/notification/views/item_updated.html.php 20 DIRTY $item->abs_url() modules/organize/views/organize_dialog.html.php 3 DIRTY_JS url::site("organize/move_to/__ALBUM_ID__?csrf=$csrf") modules/organize/views/organize_dialog.html.php 4 DIRTY_JS url::site("organize/rearrange/__TARGET_ID__/__BEFORE__?csrf=$csrf") modules/organize/views/organize_dialog.html.php 5 DIRTY_JS url::site("organize/sort_order/__ALBUM_ID__/__COL__/__DIR__?csrf=$csrf") @@ -246,6 +247,7 @@ modules/rss/views/feed.mrss.php 73 DIRTY_ATTR $chi modules/rss/views/feed.mrss.php 74 DIRTY_ATTR $child->mime_type modules/rss/views/rss_block.html.php 6 DIRTY_JS rss::url($url) modules/search/views/search.html.php 30 DIRTY_ATTR $item_class +modules/search/views/search.html.php 31 DIRTY_JS $item->url() modules/search/views/search.html.php 32 DIRTY $item->thumb_img() modules/server_add/views/admin_server_add.html.php 15 DIRTY_ATTR $id modules/server_add/views/admin_server_add.html.php 24 DIRTY $form @@ -285,6 +287,7 @@ themes/admin_default/views/admin.html.php 15 DIRTY_JS $theme themes/admin_default/views/admin.html.php 32 DIRTY $theme->admin_head() themes/admin_default/views/admin.html.php 36 DIRTY $theme->admin_page_top() themes/admin_default/views/admin.html.php 44 DIRTY $theme->admin_header_top() +themes/admin_default/views/admin.html.php 49 DIRTY_JS item::root()->url() themes/admin_default/views/admin.html.php 53 DIRTY $theme->admin_menu() themes/admin_default/views/admin.html.php 55 DIRTY $theme->admin_header_bottom() themes/admin_default/views/admin.html.php 62 DIRTY $content @@ -292,10 +295,11 @@ themes/admin_default/views/admin.html.php 68 DIRTY $sideb themes/admin_default/views/admin.html.php 73 DIRTY $theme->admin_footer() themes/admin_default/views/admin.html.php 75 DIRTY $theme->admin_credits() themes/admin_default/views/admin.html.php 79 DIRTY $theme->admin_page_bottom() -themes/admin_default/views/block.html.php 2 DIRTY $id -themes/admin_default/views/block.html.php 2 DIRTY_ATTR $css_id -themes/admin_default/views/block.html.php 10 DIRTY $title -themes/admin_default/views/block.html.php 13 DIRTY $content +themes/admin_default/views/block.html.php 3 DIRTY_ATTR $anchor +themes/admin_default/views/block.html.php 5 DIRTY $id +themes/admin_default/views/block.html.php 5 DIRTY_ATTR $css_id +themes/admin_default/views/block.html.php 13 DIRTY $title +themes/admin_default/views/block.html.php 16 DIRTY $content themes/admin_default/views/pager.html.php 13 DIRTY_JS str_replace('{page}',1,$url) themes/admin_default/views/pager.html.php 20 DIRTY_JS str_replace('{page}',$previous_page,$url) themes/admin_default/views/pager.html.php 27 DIRTY $from_to_msg @@ -306,10 +310,10 @@ themes/default/views/album.html.php 16 DIRTY_ATTR $ite themes/default/views/album.html.php 18 DIRTY_JS $child->url() themes/default/views/album.html.php 19 DIRTY $child->thumb_img(array("class"=>"gThumbnail")) themes/default/views/album.html.php 23 DIRTY_JS $child->url() -themes/default/views/block.html.php 2 DIRTY_ATTR $anchor -themes/default/views/block.html.php 3 DIRTY_ATTR $css_id -themes/default/views/block.html.php 4 DIRTY $title -themes/default/views/block.html.php 6 DIRTY $content +themes/default/views/block.html.php 3 DIRTY_ATTR $anchor +themes/default/views/block.html.php 5 DIRTY_ATTR $css_id +themes/default/views/block.html.php 6 DIRTY $title +themes/default/views/block.html.php 8 DIRTY $content themes/default/views/dynamic.html.php 11 DIRTY_ATTR $child->is_album()?"gAlbum":"" themes/default/views/dynamic.html.php 13 DIRTY_JS $child->url() themes/default/views/dynamic.html.php 14 DIRTY_ATTR $child->id @@ -325,9 +329,11 @@ themes/default/views/page.html.php 41 DIRTY $new_w themes/default/views/page.html.php 42 DIRTY $new_height themes/default/views/page.html.php 43 DIRTY $thumb_proportion themes/default/views/page.html.php 82 DIRTY $header_text -themes/default/views/page.html.php 112 DIRTY $content -themes/default/views/page.html.php 118 DIRTY newView("sidebar.html") -themes/default/views/page.html.php 125 DIRTY $footer_text +themes/default/views/page.html.php 84 DIRTY_JS item::root()->url() +themes/default/views/page.html.php 102 DIRTY_JS $parent->url($parent==$theme->item()->parent()?"show={$theme->item()->id}":null) +themes/default/views/page.html.php 117 DIRTY $content +themes/default/views/page.html.php 123 DIRTY newView("sidebar.html") +themes/default/views/page.html.php 130 DIRTY $footer_text themes/default/views/pager.html.php 13 DIRTY_JS str_replace('{page}',1,$url) themes/default/views/pager.html.php 20 DIRTY_JS str_replace('{page}',$previous_page,$url) themes/default/views/pager.html.php 27 DIRTY $from_to_msg diff --git a/modules/gallery/views/admin_languages.html.php b/modules/gallery/views/admin_languages.html.php index fa97d299..fb30c7ba 100644 --- a/modules/gallery/views/admin_languages.html.php +++ b/modules/gallery/views/admin_languages.html.php @@ -98,6 +98,6 @@ </a> </div> -<h3>Sharing your translations</h3> +<h3><?= t("Sharing your translations") ?></h3> <?= $share_translations_form ?> </div> diff --git a/modules/gallery/views/upgrader.html.php b/modules/gallery/views/upgrader.html.php index 5f93c2d5..8a01cd29 100644 --- a/modules/gallery/views/upgrader.html.php +++ b/modules/gallery/views/upgrader.html.php @@ -81,7 +81,7 @@ <? else: // can_upgrade ?> <h1> <?= t("Who are you?") ?> </h1> <p> - <?= t("You're not logged in as an administrator, so we have to verify you to make sure it's ok for you to do an upgrade. To prove you can run an upgrade, create a file called %name in your <b>gallery3/var/tmp</b> directory.", array("name" => "<br/><b>$upgrade_token</b>")) ?> + <?= t("You're not logged in as an administrator, so we have to verify you to make sure it's ok for you to do an upgrade. To prove you can run an upgrade, create a file called <br/><b>%name</b> in your <b>gallery3/var/tmp</b> directory.", array("name" => "$upgrade_token")) ?> </p> <a href="<?= url::site("upgrader?") ?>"><?= t("Ok, I've done that") ?></a> <? endif // can_upgrade ?> diff --git a/modules/info/helpers/info_theme.php b/modules/info/helpers/info_theme.php index 51378e54..4bf894ad 100644 --- a/modules/info/helpers/info_theme.php +++ b/modules/info/helpers/info_theme.php @@ -38,9 +38,11 @@ class info_theme_Core { if ($item->owner) { $results .= "<li>"; if ($item->owner->url) { - $results .= t("By: %owner_name", array("owner_name" => "<a href=\"{$item->owner->url}\">{$item->owner->full_name}</a>")); + $results .= t("By: <a href=\"%owner_url\">%owner_name</a>", + array("owner_name" => $item->owner->display_name(), + "owner_url" => $item->owner->url)); } else { - $results .= t("By: %owner_name", array("owner_name" => "{$item->owner->full_name}")); + $results .= t("By: %owner_name", array("owner_name" => $item->owner->display_name())); } $results .= "</li>"; } diff --git a/modules/organize/controllers/organize.php b/modules/organize/controllers/organize.php index 4639777c..08c80de3 100644 --- a/modules/organize/controllers/organize.php +++ b/modules/organize/controllers/organize.php @@ -45,16 +45,20 @@ class Organize_Controller extends Controller { access::verify_csrf(); $target_album = ORM::factory("item", $target_album_id); + access::required("view", $target_album); + access::required("add", $target_album); + foreach ($this->input->post("source_ids") as $source_id) { $source = ORM::factory("item", $source_id); if (!$source->contains($target_album)) { + access::required("edit", $source); item::move($source, $target_album); } } print json_encode( - array("tree" => self::_expanded_tree(ORM::factory("item", 1), $album)->__toString(), - "grid" => self::_get_micro_thumb_grid($album, 0)->__toString())); + array("tree" => self::_expanded_tree(ORM::factory("item", 1), $target_album)->__toString(), + "grid" => self::_get_micro_thumb_grid($target_album, 0)->__toString())); } function rearrange($target_id, $before_or_after) { diff --git a/modules/organize/css/organize.css b/modules/organize/css/organize.css index 15b5538d..b1cef33c 100644 --- a/modules/organize/css/organize.css +++ b/modules/organize/css/organize.css @@ -102,12 +102,13 @@ width: 9em; } -.gOrganizeMicroThumbGridCell.ui-state-selected { +.gOrganizeMicroThumbGridCell.ui-selecting, +.gOrganizeMicroThumbGridCell.ui-selected { margin: 2px; border: 2px solid #13A; } -.ui-selectable-lasso { +.ui-selectable-helper { z-index: 2000 !important; border: 1px dashed #00F; opacity: 0.25; diff --git a/modules/organize/js/organize.js b/modules/organize/js/organize.js index c30f89e0..cfaff01c 100644 --- a/modules/organize/js/organize.js +++ b/modules/organize/js/organize.js @@ -1,12 +1,12 @@ (function($) { $.organize = { micro_thumb_draggable: { - handle: ".ui-state-selected", + handle: ".ui-selected", distance: 10, cursorAt: { left: -10, top: -10}, appendTo: "#gOrganizeMicroThumbPanel", helper: function(event, ui) { - var selected = $(".ui-draggable.ui-state-selected img"); + var selected = $(".ui-draggable.ui-selected img"); if (selected.length) { var set = $('<div class="gDragHelper"></div>') .css({ @@ -37,7 +37,7 @@ }, start: function(event, ui) { - $("#gOrganizeMicroThumbPanel .ui-state-selected").hide(); + $("#gOrganizeMicroThumbPanel .ui-selected").hide(); }, drag: function(event, ui) { @@ -80,7 +80,7 @@ greedy: true, drop: function(event, ui) { if ($(event.target).hasClass("gViewOnly")) { - $(".ui-state-selected").show(); + $(".ui-selected").show(); $(".gOrganizeMicroThumbGridCell").css("borderStyle", "none"); } else { $.organize.do_drop({ @@ -142,7 +142,7 @@ $("#gDialog").bind("dialogopen", function(event, ui) { $("#gOrganize").height($("#gDialog").innerHeight() - 20); $("#gOrganizeMicroThumbPanel").height($("#gDialog").innerHeight() - 90); - $("#gOrganizeAlbumTree").height($("#gDialog").innerHeight() - 59); + $("#gOrganizeTreeContainer").height($("#gDialog").innerHeight() - 59); }); $("#gDialog").bind("dialogclose", function(event, ui) { diff --git a/modules/rss/controllers/rss.php b/modules/rss/controllers/rss.php index b89bed40..015d6032 100644 --- a/modules/rss/controllers/rss.php +++ b/modules/rss/controllers/rss.php @@ -21,13 +21,13 @@ class Rss_Controller extends Controller { public static $page_size = 20; public function feed($module_id, $feed_id, $id=null) { - $page = $this->input->get("page", 1); + $page = (int) $this->input->get("page", 1); if ($page < 1) { url::redirect(url::merge(array("page" => 1))); } // Configurable page size between 1 and 100, default 20 - $page_size = max(1, min(100, $this->input->get("page_size", self::$page_size))); + $page_size = max(1, min(100, (int) $this->input->get("page_size", self::$page_size))); // Run the appropriate feed callback if (module::is_active($module_id)) { diff --git a/modules/search/helpers/search_installer.php b/modules/search/helpers/search_installer.php index 10d8211f..f3225b4e 100644 --- a/modules/search/helpers/search_installer.php +++ b/modules/search/helpers/search_installer.php @@ -28,6 +28,7 @@ class search_installer { PRIMARY KEY (`id`), KEY(`item_id`), FULLTEXT INDEX (`data`)) + ENGINE=MyISAM DEFAULT CHARSET=utf8;"); module::set_version("search", 1); } diff --git a/modules/server_add/controllers/admin_server_add.php b/modules/server_add/controllers/admin_server_add.php index fac2aa44..38190fee 100644 --- a/modules/server_add/controllers/admin_server_add.php +++ b/modules/server_add/controllers/admin_server_add.php @@ -34,15 +34,17 @@ class Admin_Server_Add_Controller extends Admin_Controller { $form = $this->_get_admin_form(); $paths = unserialize(module::get_var("server_add", "authorized_paths", "a:0:{}")); if ($form->validate()) { - if (is_readable($form->add_path->path->value)) { + if (is_link($form->add_path->path->value)) { + $form->add_path->path->add_error("is_symlink", 1); + } else if (!is_readable($form->add_path->path->value)) { + $form->add_path->path->add_error("not_readable", 1); + } else { $path = $form->add_path->path->value; $paths[$path] = 1; module::set_var("server_add", "authorized_paths", serialize($paths)); message::success(t("Added path %path", array("path" => $path))); server_add::check_config($paths); url::redirect("admin/server_add"); - } else { - $form->add_path->path->add_error("not_readable", 1); } } @@ -71,7 +73,7 @@ class Admin_Server_Add_Controller extends Admin_Controller { $directories = array(); $path_prefix = $this->input->get("q"); foreach (glob("{$path_prefix}*") as $file) { - if (is_dir($file)) { + if (is_dir($file) && !is_link($file)) { $directories[] = $file; } } @@ -84,9 +86,10 @@ class Admin_Server_Add_Controller extends Admin_Controller { array("id" => "gServerAddAdminForm")); $add_path = $form->group("add_path"); $add_path->input("path")->label(t("Path"))->rules("required") - ->error_messages("not_readable", t("This directory is not readable by the webserver")); + ->error_messages("not_readable", t("This directory is not readable by the webserver")) + ->error_messages("is_symlink", t("Symbolic links are not allowed")); $add_path->submit("add")->value(t("Add Path")); return $form; } -}
\ No newline at end of file +} diff --git a/modules/server_add/controllers/server_add.php b/modules/server_add/controllers/server_add.php index 26b3bd08..9769cd6f 100644 --- a/modules/server_add/controllers/server_add.php +++ b/modules/server_add/controllers/server_add.php @@ -137,17 +137,25 @@ class Server_Add_Controller extends Admin_Controller { // form [path, parent_id] where the parent_id refers to another Server_Add_File_Model. We // have this extra level of abstraction because we don't know its Item_Model id yet. $queue = $task->get("queue"); + $paths = unserialize(module::get_var("server_add", "authorized_paths")); + while ($queue && microtime(true) - $start < 0.5) { list($file, $parent_entry_id) = array_shift($queue); - $entry = ORM::factory("server_add_file"); - $entry->task_id = $task->id; - $entry->file = $file; - $entry->parent_id = $parent_entry_id; - $entry->save(); + // Ignore the staging directories as directories to be imported. + if (empty($paths[$file])) { + $entry = ORM::factory("server_add_file"); + $entry->task_id = $task->id; + $entry->file = $file; + $entry->parent_id = $parent_entry_id; + $entry->save(); + $entry_id = $entry->id; + } else { + $entry_id = null; + } foreach (glob("$file/*") as $child) { if (is_dir($child)) { - $queue[] = array($child, $entry->id); + $queue[] = array($child, $entry_id); } else { $ext = strtolower(pathinfo($child, PATHINFO_EXTENSION)); if (in_array($ext, array("gif", "jpeg", "jpg", "png", "flv", "mp4")) && @@ -155,7 +163,7 @@ class Server_Add_Controller extends Admin_Controller { $child_entry = ORM::factory("server_add_file"); $child_entry->task_id = $task->id; $child_entry->file = $child; - $child_entry->parent_id = $entry->id; + $child_entry->parent_id = $entry_id; $child_entry->save(); } } diff --git a/modules/server_add/helpers/server_add_event.php b/modules/server_add/helpers/server_add_event.php index b9dd8c28..4db83f74 100644 --- a/modules/server_add/helpers/server_add_event.php +++ b/modules/server_add/helpers/server_add_event.php @@ -30,7 +30,8 @@ class server_add_event_Core { $item = $theme->item(); $paths = unserialize(module::get_var("server_add", "authorized_paths")); - if ($item && user::active()->admin && $item->is_album() && !empty($paths)) { + if ($item && user::active()->admin && $item->is_album() && !empty($paths) && + is_writable($item->is_album() ? $item->file_path() : $item->parent()->file_path())) { $menu->get("add_menu") ->append(Menu::factory("dialog") ->id("server_add") diff --git a/modules/server_add/js/server_add.js b/modules/server_add/js/server_add.js index 51ef41a7..50a8c36b 100644 --- a/modules/server_add/js/server_add.js +++ b/modules/server_add/js/server_add.js @@ -1,64 +1,125 @@ -/** - * Manage file selection state. - */ -function select_file(li) { - $(li).toggleClass("selected"); - if ($("#gServerAdd span.selected").length) { - $("#gServerAddAddButton").enable(true).removeClass("ui-state-disabled"); - } else { - $("#gServerAddAddButton").enable(false).addClass("ui-state-disabled"); - } -} +(function($) { + $.widget("ui.gallery_server_add", { + _init: function() { + var self = this; + $("#gServerAddAddButton", this.element).click(function(event) { + event.preventDefault(); + $(".gProgressBar", this.element). + progressbar(). + progressbar("value", 0); + $("#gServerAddProgress", this.element).slideDown("fast", function() { self.start_add(); }); + }); + $("#gServerAddPauseButton", this.element).click(function(event) { + self.pause = true; + $("#gServerAddPauseButton", this.element).hide(); + $("#gServerAddContinueButton", this.element).show(); + }); + $("#gServerAddContinueButton", this.element).click(function(event) { + self.pause = false; + $("#gServerAddPauseButton", this.element).show(); + $("#gServerAddContinueButton", this.element).hide(); + self.run_add(); + }); + $("#gServerAddCloseButton", this.element).click(function(event) { + $("#gDialog").dialog("close"); + window.location.reload(); + }); + $("#gServerAddTree span.gDirectory", this.element).dblclick(function(event) { + self.open_dir(event); + }); + $("#gServerAddTree span.gFile, #gServerAddTree span.gDirectory", this.element).click(function(event) { + self.select_file(event); + }); + $("#gServerAddTree span.gDirectory", this.element).dblclick(function(event) { + self.open_dir(event); + }); + $("#gDialog").bind("dialogclose", function(event, ui) { + window.location.reload(); + }); + }, -/** - * Load a new directory - */ -function open_dir(path) { - $.ajax({ - url: GET_CHILDREN_URL.replace("__PATH__", path), - success: function(data, textStatus) { - $("#gServerAddTree").html(data); - } - }); -} + taskURL: null, + pause: false, -function start_add() { - var paths = []; - $.each($("#gServerAdd span.selected"), - function () { - paths.push($(this).attr("file")); - } - ); + start_add: function() { + var self = this; + var paths = []; + $.each($("span.selected", self.element), function () { + paths.push($(this).attr("ref")); + }); - $.ajax({ - url: START_URL, - type: "POST", - async: false, - data: { "paths[]": paths }, - dataType: "json", - success: function(data, textStatus) { - $("#gStatus").html(data.status); - $("#gServerAdd .gProgressBar").progressbar("value", data.percent_complete); - setTimeout(function() { run_add(data.url); }, 0); - } - }); - return false; -} + $("#gServerAddAddButton", this.element).hide(); + $("#gServerAddPauseButton", this.element).show(); -function run_add(url) { - $.ajax({ - url: url, - async: false, - dataType: "json", - success: function(data, textStatus) { - $("#gStatus").html(data.status); - $("#gServerAdd .gProgressBar").progressbar("value", data.percent_complete); - if (data.done) { - $("#gServerAddProgress").slideUp(); - } else { - setTimeout(function() { run_add(url); }, 0); - } - } - }); -} + $.ajax({ + url: START_URL, + type: "POST", + async: false, + data: { "paths[]": paths }, + dataType: "json", + success: function(data, textStatus) { + $("#gStatus").html(data.status); + $(".gProgressBar", self.element).progressbar("value", data.percent_complete); + self.taskURL = data.url; + setTimeout(function() { self.run_add(); }, 25); + } + }); + return false; + }, + run_add: function () { + var self = this; + $.ajax({ + url: self.taskURL, + async: false, + dataType: "json", + success: function(data, textStatus) { + $("#gStatus").html(data.status); + $(".gProgressBar", self.element).progressbar("value", data.percent_complete); + if (data.done) { + $("#gServerAddProgress", this.element).slideUp(); + $("#gServerAddAddButton", this.element).show(); + $("#gServerAddPauseButton", this.element).hide(); + $("#gServerAddContinueButton", this.element).hide(); + } else { + if (!self.pause) { + setTimeout(function() { self.run_add(); }, 25); + } + } + } + }); + }, + + /** + * Load a new directory + */ + open_dir: function(event) { + var self = this; + var path = $(event.target).attr("ref"); + $.ajax({ + url: GET_CHILDREN_URL.replace("__PATH__", path), + success: function(data, textStatus) { + $("#gServerAddTree", self.element).html(data); + $("#gServerAddTree span.gDirectory", self.element).dblclick(function(event) { + self.open_dir(event); + }); + $("#gServerAddTree span.gFile, #gServerAddTree span.gDirectory", this.element).click(function(event) { + self.select_file(event); + }); + } + }); + }, + + /** + * Manage file selection state. + */ + select_file: function (event) { + $(event.target).toggleClass("selected"); + if ($("#gServerAdd span.selected").length) { + $("#gServerAddAddButton").enable(true).removeClass("ui-state-disabled"); + } else { + $("#gServerAddAddButton").enable(false).addClass("ui-state-disabled"); + } + } + }); +})(jQuery); diff --git a/modules/server_add/views/server_add_tree.html.php b/modules/server_add/views/server_add_tree.html.php index 918fbdc7..4e5bf601 100644 --- a/modules/server_add/views/server_add_tree.html.php +++ b/modules/server_add/views/server_add_tree.html.php @@ -1,7 +1,7 @@ <?php defined("SYSPATH") or die("No direct script access.") ?> <li class="ui-icon-left"> <span class="ui-icon ui-icon-folder-open"></span> - <span ondblclick="open_dir('')"> + <span class="gDirectory" ref=""> <?= t("All") ?> </span> <ul> @@ -9,7 +9,7 @@ <? foreach ($parents as $dir): ?> <li class="ui-icon-left"> <span class="ui-icon ui-icon-folder-open"></span> - <span ondblclick='open_dir(<?= html::js_string($dir) ?>)'> + <span class="gDirectory" ref="<?= html::clean_attribute($dir) ?>"> <?= html::clean(basename($dir)) ?> </span> <ul> @@ -18,12 +18,8 @@ <? foreach ($files as $file): ?> <li class="ui-icon-left"> <span class="ui-icon <?= is_dir($file) ? "ui-icon-folder-collapsed" : "ui-icon-document" ?>"></span> - <span onclick="select_file(this)" - <? if (is_dir($file)): ?> - ondblclick="open_dir($(this).attr('file'))" - <? endif ?> - file="<?= html::clean_attribute($file) ?>" - > + <span class="<?= is_dir($file) ? "gDirectory" : "gFile" ?>" + ref="<?= html::clean_attribute($file) ?>" > <?= html::clean(basename($file)) ?> </span> </li> diff --git a/modules/server_add/views/server_add_tree_dialog.html.php b/modules/server_add/views/server_add_tree_dialog.html.php index 8eb6e4df..fdb66d1c 100644 --- a/modules/server_add/views/server_add_tree_dialog.html.php +++ b/modules/server_add/views/server_add_tree_dialog.html.php @@ -33,24 +33,21 @@ disabled="disabled"> <?= t("Add") ?> </button> + <button id="gServerAddPauseButton" class="ui-state-default ui-corner-all" style="display:none"> + <?= t("Pause") ?> + </button> + <button id="gServerAddContinueButton" class="ui-state-default ui-corner-all" style="display:none"> + <?= t("Continue") ?> + </button> - <button id="gServerCloseButton" class="ui-state-default ui-corner-all"> + <button id="gServerAddCloseButton" class="ui-state-default ui-corner-all"> <?= t("Close") ?> </button> </span> <script type="text/javascript"> - $("#gServerAddAddButton").ready(function() { - $("#gServerAddAddButton").click(function(event) { - event.preventDefault(); - $("#gServerAdd .gProgressBar"). - progressbar(). - progressbar("value", 0); - $("#gServerAddProgress").slideDown("fast", function() { start_add() }); - }); - $("#gServerCloseButton").click(function(event) { - $("#gDialog").dialog("close"); - }); + $("#gServerAdd").ready(function() { + $("#gServerAdd").gallery_server_add(); }); </script> diff --git a/modules/tag/controllers/admin_tags.php b/modules/tag/controllers/admin_tags.php index 8b8dde21..63f7957c 100644 --- a/modules/tag/controllers/admin_tags.php +++ b/modules/tag/controllers/admin_tags.php @@ -81,15 +81,20 @@ class Admin_Tags_Controller extends Admin_Controller { kohana::show_404(); } - $form = tag::get_rename_form($tag); - $valid = $form->validate(); + // Don't use a form as the form is dynamically created in the js + $post = new Validation($_POST); + $post->add_rules("name", "required", "length[1,64]"); + $valid = $post->validate(); if ($valid) { - $new_name = $form->rename_tag->inputs["name"]->value; + $new_name = $this->input->post("name"); $new_tag = ORM::factory("tag")->where("name", $new_name)->find(); if ($new_tag->loaded) { - $form->rename_tag->inputs["name"]->add_error("in_use", 1); + $error_msg = t("There is already a tag with that name"); $valid = false; } + } else { + $error_msg = $post->errors(); + $error_msg = $error_msg[0]; } if ($valid) { @@ -110,7 +115,7 @@ class Admin_Tags_Controller extends Admin_Controller { } else { print json_encode( array("result" => "error", - "form" => $form->__toString())); + "message" => (string) $error_msg)); } } } diff --git a/modules/tag/controllers/tags.php b/modules/tag/controllers/tags.php index c993e374..1bd6b3cc 100644 --- a/modules/tag/controllers/tags.php +++ b/modules/tag/controllers/tags.php @@ -22,7 +22,7 @@ class Tags_Controller extends REST_Controller { public function _show($tag) { $page_size = module::get_var("gallery", "page_size", 9); - $page = $this->input->get("page", "1"); + $page = (int) $this->input->get("page", "1"); $children_count = $tag->items_count(); $offset = ($page-1) * $page_size; @@ -43,6 +43,9 @@ class Tags_Controller extends REST_Controller { } public function _index() { + // Far from perfection, but at least require view permission for the root album + $album = ORM::factory("item", 1); + access::required("view", $album); print tag::cloud(30); } diff --git a/modules/tag/js/tag.js b/modules/tag/js/tag.js index aaae9e72..52c695c6 100644 --- a/modules/tag/js/tag.js +++ b/modules/tag/js/tag.js @@ -19,6 +19,7 @@ function ajaxify_tag_form() { function closeEditInPlaceForms() { // closes currently open inplace edit forms if ($("#gRenameTagForm").length) { + $("#gEditErrorMessage").remove(); var li = $("#gRenameTagForm").parent(); $("#gRenameTagForm").parent().html($("#gRenameTagForm").parent().data("revert")); li.height(""); @@ -66,6 +67,11 @@ function editInPlace(element) { $("#gTag-" + data.tag_id).text(data.new_tagname); // update tagname console.log(data); window.location.reload(); + } else if (data.result == "error") { + $("#gRenameTagForm #name") + .addClass("gError") + .focus(); + $("#gTagAdmin").before("<p id=\"gEditErrorMessage\" class=\"gError\">" + data.message + "</p>"); } } }); diff --git a/modules/user/controllers/admin_users.php b/modules/user/controllers/admin_users.php index 521f82fa..0b748955 100644 --- a/modules/user/controllers/admin_users.php +++ b/modules/user/controllers/admin_users.php @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Admin_Users_Controller extends Controller { +class Admin_Users_Controller extends Admin_Controller { public function index() { $view = new Admin_View("admin.html"); $view->content = new View("admin_users.html"); |