diff options
80 files changed, 1057 insertions, 491 deletions
@@ -9,19 +9,19 @@ interface. SECURITY (& INTENDED AUDIENCE): -This is the third and final beta release of Gallery 3.0 and while it's -not a finished product, it's heading into the home stretch. You can -install it on public websites, but remember that until the final -release is out, we make no guarantees that it won't do bad things. +This is the second (and hopefully final) release candidate of Gallery +3.0. We're putting it out there so that we can find out if we +overlooked any small things. We expect the final version to be +virtually identical. You can install it on public websites and use it +freely -- we recommend it! Note: - We've contracted a professional security audit, received their results and resolved all the issues they found. - You can upgrade from beta 1, but not from alpha releases. -The intended audience of this release is folks who are willing to live -a little bit on the edge. We'll do our best to take care of your data -and your security, but we might screw it up here or there. We welcome +This version is intended for broad distribution. We stand ready to +support the product and help you to make the most of it. We welcome theme and module developers to play with this release and start turning out slick new designs for our happy users. diff --git a/lib/gallery.common.js b/lib/gallery.common.js index a9aa6b2c..a8b237bf 100644 --- a/lib/gallery.common.js +++ b/lib/gallery.common.js @@ -151,24 +151,24 @@ var width = size.width() - 6, height = size.height() - 6; - var ratio = width / imageWidth; - imageWidth *= ratio; - imageHeight *= ratio; - - /* after scaling the width, check that the height fits */ - if (imageHeight > height) { - ratio = height / imageHeight; + var ratio = width / imageWidth; imageWidth *= ratio; imageHeight *= ratio; - } - // handle the case where the calculation is almost zero (2.14e-14) - return { - top: Number((height - imageHeight) / 2), - left: Number((width - imageWidth) / 2), - width: Number(imageWidth), - height: Number(imageHeight) - }; + /* after scaling the width, check that the height fits */ + if (imageHeight > height) { + ratio = height / imageHeight; + imageWidth *= ratio; + imageHeight *= ratio; + } + + // handle the case where the calculation is almost zero (2.14e-14) + return { + top: Number((height - imageHeight) / 2), + left: Number((width - imageWidth) / 2), + width: Number(imageWidth), + height: Number(imageHeight) + }; }; // Initialize a short form. Short forms may contain only one text input. diff --git a/lib/gallery.dialog.js b/lib/gallery.dialog.js index f280a525..f1d146ab 100644 --- a/lib/gallery.dialog.js +++ b/lib/gallery.dialog.js @@ -1,3 +1,4 @@ + (function($) { $.widget("ui.gallery_dialog", { _init: function() { @@ -26,26 +27,42 @@ $("#g-dialog").gallery_show_loading(); - $.get(sHref, function(data) { - $("#g-dialog").html(data).gallery_show_loading(); + $.ajax({ + url: sHref, + type: "GET", + beforeSend: function(xhr) { + // Until we convert to jquery 1.4, we need to save the XMLHttpRequest object so that we + // can detect the mime type of the reply + this.xhrData = xhr; + }, + success: function(data, textStatus, xhr) { + // Pre jquery 1.4, get the saved XMLHttpRequest object + if (xhr == undefined) { + xhr = this.xhrData; + } + var mimeType = /^(\w+\/\w+)\;?/.exec(xhr.getResponseHeader("Content-Type")); - if ($("#g-dialog form").length) { - self.form_loaded(null, $("#g-dialog form")); - } - self._layout(); - - $("#g-dialog").dialog("open"); - // Remove titlebar for progress dialogs or set title - if ($("#g-dialog #g-progress").length) { - $(".ui-dialog-titlebar").remove(); - } else if ($("#g-dialog h1").length) { - $("#g-dialog").dialog('option', 'title', $("#g-dialog h1:eq(0)").html()); - } else if ($("#g-dialog fieldset legend").length) { - $("#g-dialog").dialog('option', 'title', $("#g-dialog fieldset legend:eq(0)").html()); - } + var content = ""; + if (mimeType[1] == "application/json") { + data = JSON.parse(data); + content = unescape(data.form); + } else { + content = data; + } + + $("#g-dialog").html(content).gallery_show_loading(); + + if ($("#g-dialog form").length) { + self.form_loaded(null, $("#g-dialog form")); + } + self._layout(); - if ($("#g-dialog form").length) { - self._ajaxify_dialog(); + $("#g-dialog").dialog("open"); + self._set_title(); + + if ($("#g-dialog form").length) { + self._ajaxify_dialog(); + } } }); $("#g-dialog").dialog("option", "self", self); @@ -105,19 +122,46 @@ _ajaxify_dialog: function() { var self = this; $("#g-dialog form").ajaxForm({ - dataType: "json", beforeSubmit: function(formData, form, options) { form.find(":submit") .addClass("ui-state-disabled") .attr("disabled", "disabled"); return true; }, + beforeSend: function(xhr) { + // Until we convert to jquery 1.4, we need to save the XMLHttpRequest object so that we + // can detect the mime type of the reply + this.xhrData = xhr; + }, success: function(data) { - if (data.form) { - var formData = unescape(data.form); - $("#g-dialog form").replaceWith(formData); + // Pre jquery 1.4, get the saved XMLHttpRequest object + xhr = this.xhrData; + if (xhr) { + var mimeType = /^(\w+\/\w+)\;?/.exec(xhr.getResponseHeader("Content-Type")); + + var content = ""; + if (mimeType[1] == "application/json") { + data = JSON.parse(data); + } else { + data = {"html": escape(data)}; + } + } else { + // Uploading files (eg: watermark) uses a fake xhr in jquery.form.js so + // all we have is in the data field, which should be some very simple JSON. + // Weirdly enough in Chrome the result gets wrapped in a <pre> element and + // looks like this: + // <pre style="word-wrap: break-word; white-space: pre-wrap;">{"result":"success", + // "location":"\/~bharat\/gallery3\/index.php\/admin\/watermarks"}</pre> + // bizarre. Strip that off before parsing. + data = JSON.parse(data.match("({.*})")[0]); + } + + if (data.html) { + $("#g-dialog").html(unescape(data.html)); + $("#g-dialog").dialog("option", "position", "center"); $("#g-dialog form :submit").removeClass("ui-state-disabled") .attr("disabled", null); + self._set_title(); self._ajaxify_dialog(); self.form_loaded(null, $("#g-dialog form")); if (typeof data.reset == 'function') { @@ -135,6 +179,18 @@ }); }, + _set_title: function() { + // Remove titlebar for progress dialogs or set title + if ($("#g-dialog #g-progress").length) { + $(".ui-dialog-titlebar").remove(); + } else if ($("#g-dialog h1").length) { + $("#g-dialog").dialog('option', 'title', $("#g-dialog h1:eq(0)").html()); + $("#g-dialog h1:eq(0)").hide(); + } else if ($("#g-dialog fieldset legend").length) { + $("#g-dialog").dialog('option', 'title', $("#g-dialog fieldset legend:eq(0)").html()); + } + }, + form_closing: function(event, ui) {}, dialog_closing: function(event, ui) {} }); diff --git a/lib/gallery.panel.js b/lib/gallery.panel.js index 8d627369..e0605ca3 100644 --- a/lib/gallery.panel.js +++ b/lib/gallery.panel.js @@ -31,15 +31,37 @@ if (should_open) { $(parent).after(ePanel); $("#g-panel td").html(sHref); - $.get(sHref, function(data) { - $("#g-panel td").html(data); - self._ajaxify_panel(); - if ($(element).attr("open_text")) { - $(element).attr("orig_text", $(element).children(".g-button-text").text()); - $(element).children(".g-button-text").text($(element).attr("open_text")); + $.ajax({ + url: sHref, + type: "GET", + beforeSend: function(xhr) { + // Until we convert to jquery 1.4, we need to save the + // XMLHttpRequest object + this.xhrData = xhr; + }, + success: function(data, textStatus, xhr) { + // Pre jquery 1.4, get the saved XMLHttpRequest object + if (xhr == undefined) { + xhr = this.xhrData; + } + var mimeType = /^(\w+\/\w+)\;?/.exec(xhr.getResponseHeader("Content-Type")); + var content = ""; + if (mimeType[1] == "application/json") { + data = JSON.parse(data); + content = unescape(data.html); + } else { + content = data; + } + + $("#g-panel td").html(content); + self._ajaxify_panel(); + if ($(element).attr("open_text")) { + $(element).attr("orig_text", $(element).children(".g-button-text").text()); + $(element).children(".g-button-text").text($(element).attr("open_text")); + } + $("#g-panel").addClass(parentClass).show().slideDown("slow"); } - $("#g-panel").addClass(parentClass).show().slideDown("slow"); - }); + }); } return false; @@ -57,8 +79,8 @@ return true; }, success: function(data) { - if (data.form) { - $("#g-panel td form").replaceWith(data.form); + if (data.html) { + $("#g-panel td").html(data.html); self._ajaxify_panel(); } if (data.result == "success") { diff --git a/lib/gallery.show_full_size.js b/lib/gallery.show_full_size.js index 49dc620a..f617836b 100644 --- a/lib/gallery.show_full_size.js +++ b/lib/gallery.show_full_size.js @@ -1,57 +1,56 @@ (function($) { - /** - * @todo Move inline CSS out to external style sheet (theme style sheet) - */ - $.gallery_show_full_size = function(image_url, image_width, image_height) { - var width = $(document).width(); - var height = $(document).height(); - var size = $.gallery_get_viewport_size(); + /** + * @todo Move inline CSS out to external style sheet (theme style sheet) + */ + $.gallery_show_full_size = function(image_url, image_width, image_height) { + var width = $(document).width(); + var height = $(document).height(); + var size = $.gallery_get_viewport_size(); - $("body").append('<div id="g-fullsize-overlay" class="ui-dialog-overlay" ' + - 'style="border: none; margin: 0; padding: 0; background-color: #000; ' + - 'position: absolute; top: 0px; left: 0px; ' + - 'width: ' + width + 'px; height: ' + height + 'px;' + - ' opacity: 0.7; filter: alpha(opacity=70);' + - '-moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; ' + - '-moz-background-inline-policy: -moz-initial; z-index: 1001;"> </div>'); + $("body").append('<div id="g-fullsize-overlay" class="ui-dialog-overlay" ' + + 'style="border: none; margin: 0; padding: 0; background-color: #000; ' + + 'position: absolute; top: 0px; left: 0px; ' + + 'width: ' + width + 'px; height: ' + height + 'px;' + + ' opacity: 0.7; filter: alpha(opacity=70);' + + '-moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; ' + + '-moz-background-inline-policy: -moz-initial; z-index: 1001;"> </div>'); - var image_size; - if (image_width >= size.width() - 6 || image_height >= size.height() - 6) { - image_size = $.gallery_auto_fit_window(image_width, image_height); - } else { - image_size = { + var image_size; + if (image_width >= size.width() - 6 || image_height >= size.height() - 6) { + image_size = $.gallery_auto_fit_window(image_width, image_height); + } else { + image_size = { top: Number((height - image_height) / 2), left: Number((width - image_width) / 2), width: Number(image_width), height: Number(image_height) - }; - } + }; + } + $("body").append('<div id="g-fullsize" class="ui-dialog ui-widget" ' + + 'style="overflow: hidden; display: block; ' + + 'position: absolute; z-index: 1002; outline-color: -moz-use-text-color; ' + + 'outline-style: none; outline-width: 0px; ' + + 'height: ' + image_size.height + 'px; ' + + 'width: ' + image_size.width + 'px; ' + + 'top: ' + image_size.top + 'px; left: ' + image_size.left + 'px;">' + + '<img id="g-fullsize-image" src="' + image_url + '"' + + 'height="' + image_size.height + '" width="' + image_size.width + '"/></div>'); - $("body").append('<div id="g-fullsize" class="ui-dialog ui-widget" ' + - 'style="overflow: hidden; display: block; ' + - 'position: absolute; z-index: 1002; outline-color: -moz-use-text-color; ' + - 'outline-style: none; outline-width: 0px; ' + - 'height: ' + image_size.height + 'px; ' + - 'width: ' + image_size.width + 'px; ' + - 'top: ' + image_size.top + 'px; left: ' + image_size.left + 'px;">' + - '<img id="g-fullsize-image" src="' + image_url + '"' + - 'height="' + image_size.height + '" width="' + image_size.width + '"/></div>'); - - $().click(function() { - $("#g-fullsize-overlay*").remove(); - $("#g-fullsize").remove(); - }); - $().bind("keypress", function() { - $("#g-fullsize-overlay*").remove(); - $("#g-fullsize").remove(); - }); - $(window).resize(function() { - $("#g-fullsize-overlay").width($(document).width()).height($(document).height()); - image_size = $.gallery_auto_fit_window(image_width, image_height); - $("#g-fullsize").height(image_size.height) - .width(image_size.width) - .css("top", image_size.top) - .css("left", image_size.left); - $("#g-fullsize-image").height(image_size.height).width(image_size.width); - }); - }; + $().click(function() { + $("#g-fullsize-overlay*").remove(); + $("#g-fullsize").remove(); + }); + $().bind("keypress", function() { + $("#g-fullsize-overlay*").remove(); + $("#g-fullsize").remove(); + }); + $(window).resize(function() { + $("#g-fullsize-overlay").width($(document).width()).height($(document).height()); + image_size = $.gallery_auto_fit_window(image_width, image_height); + $("#g-fullsize").height(image_size.height) + .width(image_size.width) + .css("top", image_size.top) + .css("left", image_size.left); + $("#g-fullsize-image").height(image_size.height).width(image_size.width); + }); + }; })(jQuery); diff --git a/modules/comment/controllers/admin_manage_comments.php b/modules/comment/controllers/admin_manage_comments.php index bc1c9e64..e451791f 100644 --- a/modules/comment/controllers/admin_manage_comments.php +++ b/modules/comment/controllers/admin_manage_comments.php @@ -34,10 +34,10 @@ class Admin_Manage_Comments_Controller extends Admin_Controller { public function menu_labels() { $menu = $this->_menu($this->_counts()); - print json_encode(array((string) $menu->get("unpublished")->label, - (string) $menu->get("published")->label, - (string) $menu->get("spam")->label, - (string) $menu->get("deleted")->label)); + json::reply(array((string) $menu->get("unpublished")->label, + (string) $menu->get("published")->label, + (string) $menu->get("spam")->label, + (string) $menu->get("deleted")->label)); } public function queue($state) { diff --git a/modules/comment/controllers/comments.php b/modules/comment/controllers/comments.php index c42ad24e..6ec4132b 100644 --- a/modules/comment/controllers/comments.php +++ b/modules/comment/controllers/comments.php @@ -56,13 +56,12 @@ class Comments_Controller extends Controller { $view = new Theme_View("comment.html", "other", "comment-fragment"); $view->comment = $comment; - print json_encode( - array("result" => "success", - "view" => (string) $view, - "form" => (string) comment::get_add_form($item))); + json::reply(array("result" => "success", + "view" => (string)$view, + "form" => (string)comment::get_add_form($item))); } else { $form = comment::prefill_add_form($form); - print json_encode(array("result" => "error", "form" => (string) $form)); + json::reply(array("result" => "error", "form" => (string)$form)); } } diff --git a/modules/exif/helpers/exif.php b/modules/exif/helpers/exif.php index 943feba7..aa77b42e 100644 --- a/modules/exif/helpers/exif.php +++ b/modules/exif/helpers/exif.php @@ -36,7 +36,8 @@ class exif_Core { foreach(self::_keys() as $field => $exifvar) { if (isset($exif_raw[$exifvar[0]][$exifvar[1]])) { $value = $exif_raw[$exifvar[0]][$exifvar[1]]; - if (function_exists("mb_detect_encoding") && mb_detect_encoding($value) != "UTF-8") { + if (function_exists("mb_detect_encoding") && + mb_detect_encoding($value, "ISO-8859-1, UTF-8") != "UTF-8") { $value = utf8_encode($value); } $keys[$field] = Input::clean($value); @@ -59,7 +60,8 @@ class exif_Core { foreach (array("Keywords" => "2#025", "Caption" => "2#120") as $keyword => $iptc_key) { if (!empty($iptc[$iptc_key])) { $value = implode(" ", $iptc[$iptc_key]); - if (function_exists("mb_detect_encoding") && mb_detect_encoding($value) != "UTF-8") { + if (function_exists("mb_detect_encoding") && + mb_detect_encoding($value, "ISO-8859-1, UTF-8") != "UTF-8") { $value = utf8_encode($value); } $keys[$keyword] = Input::clean($value); diff --git a/modules/forge/libraries/Form_Upload.php b/modules/forge/libraries/Form_Upload.php index da48764c..eda9c8a6 100644 --- a/modules/forge/libraries/Form_Upload.php +++ b/modules/forge/libraries/Form_Upload.php @@ -135,6 +135,10 @@ class Form_Upload_Core extends Form_Input { $mime = $this->upload['type']; } + // Get rid of the ";charset=binary" that can occasionally occur and is + // legal via RFC2045 + $mime = preg_replace('/; charset=binary/', '', $mime); + // Allow nothing by default $allow = FALSE; diff --git a/modules/g2_import/controllers/admin_g2_import.php b/modules/g2_import/controllers/admin_g2_import.php index cc60f757..b9427f79 100644 --- a/modules/g2_import/controllers/admin_g2_import.php +++ b/modules/g2_import/controllers/admin_g2_import.php @@ -60,7 +60,7 @@ class Admin_g2_import_Controller extends Admin_Controller { } if (g2_import::is_valid_embed_path($embed_path)) { - message::success("Gallery 2 path saved."); + message::success(t("Gallery 2 path saved")); module::set_var("g2_import", "embed_path", $embed_path); url::redirect("admin/g2_import"); } else { diff --git a/modules/g2_import/helpers/g2_import.php b/modules/g2_import/helpers/g2_import.php index c0ea09d6..4aa9e642 100644 --- a/modules/g2_import/helpers/g2_import.php +++ b/modules/g2_import/helpers/g2_import.php @@ -139,6 +139,15 @@ class g2_import_Core { "function G2_Gallery"), array_merge(array("<?php defined(\"SYSPATH\") or die(\"No direct script access.\") ?>\n"), file("$base_dir/modules/core/classes/Gallery.class")))); + } else { + // Ok, this is a good one. If you're running a bytecode accelerator and you move your + // Gallery install, these files sometimes get cached with the wrong path and then fail to + // load properly. + // Documented in https://sourceforge.net/apps/trac/gallery/ticket/1253 + touch("$mod_path/embed.php"); + touch("$mod_path/main.php"); + touch("$mod_path/bootstrap.inc"); + touch("$mod_path/Gallery.class.inc"); } require("$mod_path/embed.php"); diff --git a/modules/gallery/controllers/admin.php b/modules/gallery/controllers/admin.php index c460f58c..8fc5432d 100644 --- a/modules/gallery/controllers/admin.php +++ b/modules/gallery/controllers/admin.php @@ -74,17 +74,20 @@ class Admin_Controller extends Controller { $result = new stdClass(); $result->result = "success"; if ($time_remaining < 30) { + message::success(t("Automatically logged out of the admin area for your security")); $result->location = url::abs_site(""); } - print json_encode($result); + json::reply($result); } private static function _prompt_for_reauth($controller_name, $args) { - if (request::method() == "get" && !request::is_ajax()) { + if (request::method() == "get") { // Avoid anti-phishing protection by passing the url as session variable. Session::instance()->set("continue_url", url::abs_current(true)); } + // Save the is_ajax value as we lose it, if set, when we redirect + Session::instance()->set("is_ajax_request", request::is_ajax()); url::redirect("reauthenticate"); } } diff --git a/modules/gallery/controllers/admin_advanced_settings.php b/modules/gallery/controllers/admin_advanced_settings.php index 6f4e9403..cf197743 100644 --- a/modules/gallery/controllers/admin_advanced_settings.php +++ b/modules/gallery/controllers/admin_advanced_settings.php @@ -50,6 +50,6 @@ class Admin_Advanced_Settings_Controller extends Admin_Controller { t("Saved value for %var (%module_name)", array("var" => $var_name, "module_name" => $module_name))); - print json_encode(array("result" => "success")); + json::reply(array("result" => "success")); } } diff --git a/modules/gallery/controllers/admin_languages.php b/modules/gallery/controllers/admin_languages.php index 0f134fcd..573ededf 100644 --- a/modules/gallery/controllers/admin_languages.php +++ b/modules/gallery/controllers/admin_languages.php @@ -51,7 +51,7 @@ class Admin_Languages_Controller extends Admin_Controller { } module::set_var("gallery", "default_locale", $new_default_locale); - print json_encode(array("result" => "success")); + json::reply(array("result" => "success")); } public function share() { @@ -109,7 +109,7 @@ class Admin_Languages_Controller extends Admin_Controller { private function _share_translations_form() { $form = new Forge("admin/languages/share", "", "post", array("id" => "g-share-translations-form")); $group = $form->group("sharing") - ->label(t("Sharing your own translations with the Gallery community is easy. Please do!")); + ->label("Translations API Key"); $api_key = l10n_client::api_key(); $server_link = l10n_client::server_api_key_url(); $group->input("api_key") diff --git a/modules/gallery/controllers/admin_maintenance.php b/modules/gallery/controllers/admin_maintenance.php index d6a2d191..3567b4f0 100644 --- a/modules/gallery/controllers/admin_maintenance.php +++ b/modules/gallery/controllers/admin_maintenance.php @@ -211,19 +211,19 @@ class Admin_Maintenance_Controller extends Admin_Controller { break; } // Using sprintf("%F") to avoid comma as decimal separator. - print json_encode(array("result" => "success", - "task" => array( - "percent_complete" => sprintf("%F", $task->percent_complete), - "status" => (string) $task->status, - "done" => (bool) $task->done), - "location" => url::site("admin/maintenance"))); + json::reply(array("result" => "success", + "task" => array( + "percent_complete" => sprintf("%F", $task->percent_complete), + "status" => (string) $task->status, + "done" => (bool) $task->done), + "location" => url::site("admin/maintenance"))); } else { - print json_encode(array("result" => "in_progress", - "task" => array( - "percent_complete" => sprintf("%F", $task->percent_complete), - "status" => (string) $task->status, - "done" => (bool) $task->done))); + json::reply(array("result" => "in_progress", + "task" => array( + "percent_complete" => sprintf("%F", $task->percent_complete), + "status" => (string) $task->status, + "done" => (bool) $task->done))); } } } diff --git a/modules/gallery/controllers/admin_modules.php b/modules/gallery/controllers/admin_modules.php index bf638a37..f5af9a5a 100644 --- a/modules/gallery/controllers/admin_modules.php +++ b/modules/gallery/controllers/admin_modules.php @@ -57,7 +57,7 @@ class Admin_Modules_Controller extends Admin_Controller { $result["dialog"] = (string)$v; $result["allow_continue"] = empty($messages["error"]); } - print json_encode($result); + json::reply($result); } public function save() { diff --git a/modules/gallery/controllers/admin_sidebar.php b/modules/gallery/controllers/admin_sidebar.php index fb857e4e..2e49097a 100644 --- a/modules/gallery/controllers/admin_sidebar.php +++ b/modules/gallery/controllers/admin_sidebar.php @@ -50,7 +50,7 @@ class Admin_Sidebar_Controller extends Admin_Controller { $result["active"] = $v->render(); $message = t("Updated sidebar blocks"); $result["message"] = (string) $message; - print json_encode($result); + json::reply($result); } private function _get_blocks() { diff --git a/modules/gallery/controllers/albums.php b/modules/gallery/controllers/albums.php index eaa09be5..f3f5dee3 100644 --- a/modules/gallery/controllers/albums.php +++ b/modules/gallery/controllers/albums.php @@ -113,9 +113,9 @@ class Albums_Controller extends Items_Controller { message::success(t("Created album %album_title", array("album_title" => html::purify($album->title)))); - print json_encode(array("result" => "success", "location" => $album->url())); + json::reply(array("result" => "success", "location" => $album->url())); } else { - print json_encode(array("result" => "error", "form" => (string) $form)); + print $form; } } @@ -153,13 +153,13 @@ class Albums_Controller extends Items_Controller { if ($form->from_id->value == $album->id) { // Use the new url; it might have changed. - print json_encode(array("result" => "success", "location" => $album->url())); + json::reply(array("result" => "success", "location" => $album->url())); } else { // Stay on the same page - print json_encode(array("result" => "success")); + json::reply(array("result" => "success")); } } else { - print json_encode(array("result" => "error", "form" => (string) $form)); + json::reply(array("result" => "error", "html" => (string)$form)); } } diff --git a/modules/gallery/controllers/file_proxy.php b/modules/gallery/controllers/file_proxy.php index 32690fc0..15b4279f 100644 --- a/modules/gallery/controllers/file_proxy.php +++ b/modules/gallery/controllers/file_proxy.php @@ -72,10 +72,10 @@ class File_Proxy_Controller extends Controller { // necessary, it's easily resurrected. // If we're looking for a .jpg then it's it's possible that we're requesting the thumbnail - // for a movie. In that case, the .flv or .mp4 file would have been converted to a .jpg. - // So try some alternate types: + // for a movie. In that case, the .flv, .mp4 or .m4v file would have been converted to a + // .jpg. So try some alternate types: if (preg_match('/.jpg$/', $path)) { - foreach (array("flv", "mp4") as $ext) { + foreach (array("flv", "mp4", "m4v") as $ext) { $movie_path = preg_replace('/.jpg$/', ".$ext", $encoded_path); $item = ORM::factory("item")->where("relative_path_cache", "=", $movie_path)->find(); if ($item->loaded()) { @@ -131,10 +131,7 @@ class File_Proxy_Controller extends Controller { } else { header("Content-Type: $item->mime_type"); } - Kohana::close_buffers(false); - $fd = fopen($file, "rb"); - fpassthru($fd); - fclose($fd); + readfile($file); } } diff --git a/modules/gallery/controllers/l10n_client.php b/modules/gallery/controllers/l10n_client.php index d5b322ef..6833a9ae 100644 --- a/modules/gallery/controllers/l10n_client.php +++ b/modules/gallery/controllers/l10n_client.php @@ -91,7 +91,7 @@ class L10n_Client_Controller extends Controller { Gallery_I18n::clear_cache($locale); - print json_encode(new stdClass()); + json::reply(new stdClass()); } public function toggle_l10n_mode() { diff --git a/modules/gallery/controllers/login.php b/modules/gallery/controllers/login.php index 2b60316b..62d33345 100644 --- a/modules/gallery/controllers/login.php +++ b/modules/gallery/controllers/login.php @@ -30,10 +30,11 @@ class Login_Controller extends Controller { list ($valid, $form) = $this->_auth("login/auth_ajax"); if ($valid) { - print json_encode( - array("result" => "success")); + json::reply(array("result" => "success")); } else { - print json_encode(array("result" => "error", "form" => (string) $form)); + $view = new View("login_ajax.html"); + $view->form = $form; + json::reply(array("result" => "error", "html" => (string)$view)); } } diff --git a/modules/gallery/controllers/move.php b/modules/gallery/controllers/move.php index f8b85b6f..7b2d6165 100644 --- a/modules/gallery/controllers/move.php +++ b/modules/gallery/controllers/move.php @@ -41,9 +41,7 @@ class Move_Controller extends Controller { item::move($source, $target); - print json_encode( - array("result" => "success", - "location" => $target->url())); + json::reply(array("result" => "success", "location" => $target->url())); } public function show_sub_tree($source_id, $target_id) { diff --git a/modules/gallery/controllers/movies.php b/modules/gallery/controllers/movies.php index 16d22d90..02d2a497 100644 --- a/modules/gallery/controllers/movies.php +++ b/modules/gallery/controllers/movies.php @@ -87,13 +87,13 @@ class Movies_Controller extends Items_Controller { if ($form->from_id->value == $movie->id) { // Use the new url; it might have changed. - print json_encode(array("result" => "success", "location" => $movie->url())); + json::reply(array("result" => "success", "location" => $movie->url())); } else { // Stay on the same page - print json_encode(array("result" => "success")); + json::reply(array("result" => "success")); } } else { - print json_encode(array("result" => "error", "form" => (string) $form)); + json::reply(array("result" => "error", "html" => (string) $form)); } } diff --git a/modules/gallery/controllers/photos.php b/modules/gallery/controllers/photos.php index f336d07c..8377e6c7 100644 --- a/modules/gallery/controllers/photos.php +++ b/modules/gallery/controllers/photos.php @@ -24,9 +24,9 @@ class Photos_Controller extends Items_Controller { // sure that we're actually receiving an object throw new Kohana_404_Exception(); } - + access::required("view", $photo); - + $where = array(array("type", "!=", "album")); $position = $photo->parent()->get_position($photo, $where); if ($position > 1) { @@ -87,13 +87,13 @@ class Photos_Controller extends Items_Controller { if ($form->from_id->value == $photo->id) { // Use the new url; it might have changed. - print json_encode(array("result" => "success", "location" => $photo->url())); + json::reply(array("result" => "success", "location" => $photo->url())); } else { // Stay on the same page - print json_encode(array("result" => "success")); + json::reply(array("result" => "success")); } } else { - print json_encode(array("result" => "error", "form" => (string) $form)); + json::reply(array("result" => "error", "html" => (string)$form)); } } diff --git a/modules/gallery/controllers/quick.php b/modules/gallery/controllers/quick.php index 6cfbbc62..fee601d9 100644 --- a/modules/gallery/controllers/quick.php +++ b/modules/gallery/controllers/quick.php @@ -46,23 +46,24 @@ class Quick_Controller extends Controller { graphics::generate($item); - $parent = $item->parent(); - // @todo: this is an inadequate way to regenerate the parent's thumbnail after rotation. - if ($parent->album_cover_item_id == $item->id) { - copy($item->thumb_path(), $parent->thumb_path()); - $parent->thumb_width = $item->thumb_width; - $parent->thumb_height = $item->thumb_height; - $parent->save(); + // @todo: this is an inadequate way to regenerate album cover thumbnails after rotation. + foreach (ORM::factory("item") + ->where("album_cover_item_id", "=", $item->id) + ->find_all() as $target) { + copy($item->thumb_path(), $target->thumb_path()); + $target->thumb_width = $item->thumb_width; + $target->thumb_height = $item->thumb_height; + $target->save(); } } if (Input::instance()->get("page_type") == "collection") { - print json_encode( + json::reply( array("src" => $item->thumb_url(), "width" => $item->thumb_width, "height" => $item->thumb_height)); } else { - print json_encode( + json::reply( array("src" => $item->resize_url(), "width" => $item->resize_width, "height" => $item->resize_height)); @@ -82,7 +83,7 @@ class Quick_Controller extends Controller { item::make_album_cover($item); message::success($msg); - print json_encode(array("result" => "success", "reload" => 1)); + json::reply(array("result" => "success", "reload" => 1)); } public function form_delete($id) { @@ -90,17 +91,10 @@ class Quick_Controller extends Controller { access::required("view", $item); access::required("edit", $item); - if ($item->is_album()) { - print t( - "Delete the album <b>%title</b>? All photos and movies in the album will also be deleted.", - array("title" => html::purify($item->title))); - } else { - print t("Are you sure you want to delete <b>%title</b>?", - array("title" => html::purify($item->title))); - } - - $form = item::get_delete_form($item); - print $form; + $v = new View("quick_delete_confirm.html"); + $v->item = $item; + $v->form = item::get_delete_form($item); + print $v; } public function delete($id) { @@ -116,14 +110,24 @@ class Quick_Controller extends Controller { } $parent = $item->parent(); - $item->delete(); + + if ($item->is_album()) { + // Album delete will trigger deletes for all children. Do this in a batch so that we can be + // smart about notifications, album cover updates, etc. + batch::start(); + $item->delete(); + batch::stop(); + } else { + $item->delete(); + } message::success($msg); - if (Input::instance()->get("page_type") == "collection") { - print json_encode(array("result" => "success", "reload" => 1)); + $from_id = Input::instance()->get("from_id"); + if (Input::instance()->get("page_type") == "collection" && + $from_id != $id /* deleted the item we were viewing */) { + json::reply(array("result" => "success", "reload" => 1)); } else { - print json_encode(array("result" => "success", - "location" => $parent->url())); + json::reply(array("result" => "success", "location" => $parent->url())); } } diff --git a/modules/gallery/controllers/reauthenticate.php b/modules/gallery/controllers/reauthenticate.php index acb27f6a..0486c0fe 100644 --- a/modules/gallery/controllers/reauthenticate.php +++ b/modules/gallery/controllers/reauthenticate.php @@ -18,11 +18,21 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ class Reauthenticate_Controller extends Controller { - public function index($share_translations_form=null) { + public function index() { if (!identity::active_user()->admin) { access::forbidden(); } - return self::_show_form(self::_form()); + // On redirects from the admin controller, the ajax request indicator is lost, + // so we store it in the session. + $is_ajax = Session::instance()->get_once("is_ajax_request", request::is_ajax()); + if ($is_ajax) { + $v = new View("reauthenticate.html"); + $v->form = self::_form(); + $v->user_name = identity::active_user()->name; + print $v; + } else { + self::_show_form(self::_form()); + } } public function auth() { @@ -35,14 +45,23 @@ class Reauthenticate_Controller extends Controller { $valid = $form->validate(); $user = identity::active_user(); if ($valid) { - message::success(t("Successfully re-authenticated!")); module::event("user_auth", $user); - url::redirect($form->continue_url->value); + if (!request::is_ajax()) { + message::success(t("Successfully re-authenticated!")); + } + url::redirect(Session::instance()->get_once("continue_url")); } else { $name = $user->name; log::warning("user", t("Failed re-authentication for %name", array("name" => $name))); module::event("user_auth_failed", $name); - return self::_show_form($form); + if (request::is_ajax()) { + $v = new View("reauthenticate.html"); + $v->form = $form; + $v->user_name = identity::active_user()->name; + json::reply(array("html" => (string)$v)); + } else { + self::_show_form($form); + } } } @@ -52,17 +71,17 @@ class Reauthenticate_Controller extends Controller { $view->content = new View("reauthenticate.html"); $view->content->form = $form; $view->content->user_name = identity::active_user()->name; + print $view; } private static function _form() { $form = new Forge("reauthenticate/auth", "", "post", array("id" => "g-reauthenticate-form")); - $form->set_attr('class', "g-narrow"); - $form->hidden("continue_url")->value(Session::instance()->get("continue_url", "admin")); + $form->set_attr("class", "g-narrow"); $group = $form->group("reauthenticate")->label(t("Re-authenticate")); $group->password("password")->label(t("Password"))->id("g-password")->class(null) ->callback("auth::validate_too_many_failed_auth_attempts") - ->callback("user::valid_password") + ->callback("Reauthenticate_Controller::valid_password") ->error_messages("invalid_password", t("Incorrect password")) ->error_messages( "too_many_failed_auth_attempts", @@ -70,4 +89,10 @@ class Reauthenticate_Controller extends Controller { $group->submit("")->value(t("Submit")); return $form; } + + static function valid_password($password_input) { + if (!identity::is_correct_password(identity::active_user(), $password_input->value)) { + $password_input->add_error("invalid_password", 1); + } + } } diff --git a/modules/gallery/controllers/flash_uploader.php b/modules/gallery/controllers/uploader.php index f7da5124..87520032 100644 --- a/modules/gallery/controllers/flash_uploader.php +++ b/modules/gallery/controllers/uploader.php @@ -17,8 +17,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Flash_Uploader_Controller extends Controller { - public function app($id) { +class Uploader_Controller extends Controller { + public function index($id) { $item = ORM::factory("item", $id); access::required("view", $item); access::required("add", $item); @@ -50,7 +50,7 @@ class Flash_Uploader_Controller extends Controller { // Uploadify adds its own field to the form, so validate that separately. $file_validation = new Validation($_FILES); $file_validation->add_rules( - "Filedata", "upload::valid", "upload::required", "upload::type[gif,jpg,jpeg,png,flv,mp4]"); + "Filedata", "upload::valid", "upload::required", "upload::type[gif,jpg,jpeg,png,flv,mp4,m4v]"); if ($form->validate() && $file_validation->validate()) { $temp_filename = upload::save("Filedata"); @@ -63,7 +63,7 @@ class Flash_Uploader_Controller extends Controller { $path_info = @pathinfo($temp_filename); if (array_key_exists("extension", $path_info) && - in_array(strtolower($path_info["extension"]), array("flv", "mp4"))) { + in_array(strtolower($path_info["extension"]), array("flv", "mp4", "m4v"))) { $item->type = "movie"; $item->save(); log::success("content", t("Added a movie"), @@ -105,11 +105,11 @@ class Flash_Uploader_Controller extends Controller { access::verify_csrf(); batch::stop(); - print json_encode(array("result" => "success")); + json::reply(array("result" => "success")); } private function _get_add_form($album) { - $form = new Forge("flash_uploader/finish", "", "post", array("id" => "g-add-photos-form")); + $form = new Forge("uploader/finish", "", "post", array("id" => "g-add-photos-form")); $group = $form->group("add_photos") ->label(t("Add photos to %album_title", array("album_title" => html::purify($album->title)))); $group->uploadify("uploadify")->album($album); diff --git a/modules/gallery/controllers/user_profile.php b/modules/gallery/controllers/user_profile.php index c064e791..726d3e51 100644 --- a/modules/gallery/controllers/user_profile.php +++ b/modules/gallery/controllers/user_profile.php @@ -61,9 +61,9 @@ class User_Profile_Controller extends Controller { ->message(html::purify($form->message->message->value)) ->send(); message::success(t("Sent message to %user_name", array("user_name" => $user->display_name()))); - print json_encode(array("result" => "success")); + json::reply(array("result" => "success")); } else { - print json_encode(array("result" => "error", "form" => (string)$form)); + json::reply(array("result" => "error", "html" => (string)$form)); } } } diff --git a/modules/gallery/css/upgrader.css b/modules/gallery/css/upgrader.css index 2b3b5afd..d1b74c31 100644 --- a/modules/gallery/css/upgrader.css +++ b/modules/gallery/css/upgrader.css @@ -9,16 +9,15 @@ h1 { } div#outer { - width: 650px; - background: white; + background: #fff; border: 1px solid #999; margin: 0 auto; - padding: -10px; + width: 650px; } div#inner { - padding: 0 1em 0 1em; - margin: 0px; + margin: 0; + padding: 0 1em; } div#footer { diff --git a/modules/gallery/helpers/access.php b/modules/gallery/helpers/access.php index 87b6b313..f1ea00c0 100644 --- a/modules/gallery/helpers/access.php +++ b/modules/gallery/helpers/access.php @@ -222,7 +222,7 @@ class access_Core { self::_update_access_non_view_cache($group, $perm_name, $album); } - self::_update_htaccess_files($album, $group, $perm_name, $value); + self::update_htaccess_files($album, $group, $perm_name, $value); model_cache::clear(); } @@ -623,11 +623,18 @@ class access_Core { } /** - * Maintain .htacccess files to prevent direct access to albums, resizes and thumbnails when we - * apply the view and view_full permissions to guest users. + * Rebuild the .htaccess files that prevent direct access to albums, resizes and thumbnails. We + * call this internally any time we change the view or view_full permissions for guest users. + * This function is only public because we use it in maintenance tasks. + * + * @param Item_Model the album + * @param Group_Model the group whose permission is changing + * @param string the permission name + * @param string the new permission value (eg access::DENY) */ - private static function _update_htaccess_files($album, $group, $perm_name, $value) { - if ($group->id != 1 || !($perm_name == "view" || $perm_name == "view_full")) { + static function update_htaccess_files($album, $group, $perm_name, $value) { + if ($group->id != identity::everybody()->id || + !($perm_name == "view" || $perm_name == "view_full")) { return; } diff --git a/modules/gallery/helpers/album.php b/modules/gallery/helpers/album.php index 0baae631..0ac5e8b0 100644 --- a/modules/gallery/helpers/album.php +++ b/modules/gallery/helpers/album.php @@ -58,7 +58,7 @@ class album_Core { static function get_edit_form($parent) { $form = new Forge( "albums/update/{$parent->id}", "", "post", array("id" => "g-edit-album-form")); - $form->hidden("from_id"); + $form->hidden("from_id")->value($parent->id); $group = $form->group("edit_item")->label(t("Edit Album")); $group->input("title")->label(t("Title"))->value($parent->title) diff --git a/modules/gallery/helpers/gallery.php b/modules/gallery/helpers/gallery.php index 7f7db10b..d4078209 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 RC1 (Santa Fe)"; + const VERSION = "3.0 RC2 (Santa Fe)"; /** * If Gallery is in maintenance mode, then force all non-admins to get routed to a "This site is diff --git a/modules/gallery/helpers/gallery_event.php b/modules/gallery/helpers/gallery_event.php index 82f42d98..e3fa5e08 100644 --- a/modules/gallery/helpers/gallery_event.php +++ b/modules/gallery/helpers/gallery_event.php @@ -98,20 +98,55 @@ class gallery_event_Core { static function item_deleted($item) { access::delete_item($item); + // Find any other albums that had the deleted item as the album cover and null it out. + // In some cases this may leave us with a missing album cover up in this item's parent + // hierarchy, but in most cases it'll work out fine. + foreach (ORM::factory("item") + ->where("album_cover_item_id", "=", $item->id) + ->find_all() as $parent) { + item::remove_album_cover($parent); + } + $parent = $item->parent(); if (!$parent->album_cover_item_id) { - // Assume we deleted the album cover and pick a new one. Choosing the first photo in the - // album is logical, but it's not the most efficient in the case where we're deleting all - // the photos in the album one at a time since we'll probably delete them in order which - // means that we'll be resetting the album cover each time. - if ($child = $parent->children(1)->current()) { - item::make_album_cover($child); + // Assume that we deleted the album cover + if (batch::in_progress()) { + // Remember that this parent is missing an album cover, for later. + $batch_missing_album_cover = Session::instance()->get("batch_missing_album_cover", array()); + $batch_missing_album_cover[$parent->id] = 1; + Session::instance()->set("batch_missing_album_cover", $batch_missing_album_cover); + } else { + // Choose the first child as the new cover. + if ($child = $parent->children(1)->current()) { + item::make_album_cover($child); + } } } } + static function batch_complete() { + // Set the album covers for any items that where we probably deleted the album cover during + // this batch. The item may have been deleted, so don't count on it being around. Choose the + // first child as the new album cover. + // NOTE: if the first child doesn't have an album cover, then this won't work. + foreach (array_keys(Session::instance()->get("batch_missing_album_cover", array())) as $id) { + $item = ORM::factory("item", $id); + if ($item->loaded() && !$item->album_cover_item_id) { + if ($child = $item->children(1)->current()) { + item::make_album_cover($child); + } + } + } + Session::instance()->delete("batch_missing_album_cover"); + } + static function item_moved($item, $old_parent) { access::recalculate_permissions($item->parent()); + + // If the new parent doesn't have an album cover, make this it. + if (!$item->parent()->album_cover_item_id) { + item::make_album_cover($item); + } } static function user_login($user) { @@ -158,7 +193,9 @@ class gallery_event_Core { ->url(user_profile::url($user->id)) ->label($user->display_name())); - if (isset($theme->item)) { + if (Router::$controller == "admin") { + $continue_url = url::site(""); + } else if (isset($theme->item)) { if (access::user_can(identity::guest(), "view", $theme->item)) { $continue_url = $theme->item->abs_url(); } else { @@ -171,8 +208,7 @@ class gallery_event_Core { $menu->append(Menu::factory("link") ->id("user_menu_logout") ->css_id("g-logout-link") - ->url(url::site("logout?csrf=$csrf&continue_url=" . - urlencode($continue_url))) + ->url(url::site("logout?csrf=$csrf&continue_url=" . urlencode($continue_url))) ->label(t("Logout"))); } } @@ -202,7 +238,7 @@ class gallery_event_Core { $add_menu->append(Menu::factory("dialog") ->id("add_photos_item") ->label(t("Add photos")) - ->url(url::site("flash_uploader/app/$item->id"))); + ->url(url::site("uploader/index/$item->id"))); if ($item->is_album()) { $add_menu->append(Menu::factory("dialog") ->id("add_album_item") @@ -219,14 +255,17 @@ class gallery_event_Core { case "album": $option_text = t("Album options"); $edit_text = t("Edit album"); + $delete_text = t("Delete album"); break; case "movie": $option_text = t("Movie options"); $edit_text = t("Edit movie"); + $delete_text = t("Delete movie"); break; default: $option_text = t("Photo options"); $edit_text = t("Edit photo"); + $delete_text = t("Delete photo"); } $menu->append($options_menu = Menu::factory("submenu") @@ -237,7 +276,7 @@ class gallery_event_Core { $options_menu->append(Menu::factory("dialog") ->id("edit_item") ->label($edit_text) - ->url(url::site("form/edit/{$item->type}s/$item->id"))); + ->url(url::site("form/edit/{$item->type}s/$item->id?from_id={$item->id}"))); } if ($item->is_album()) { @@ -251,7 +290,6 @@ class gallery_event_Core { } $csrf = access::csrf_token(); - $theme_item = $theme->item(); $page_type = $theme->page_type(); if ($can_edit && $item->is_photo() && graphics::can("rotate")) { $options_menu @@ -262,7 +300,7 @@ class gallery_event_Core { ->css_class("ui-icon-rotate-ccw") ->ajax_handler("function(data) { " . "\$.gallery_replace_image(data, \$('$item_css_selector')) }") - ->url(url::site("quick/rotate/$item->id/ccw?csrf=$csrf&from_id=$theme_item->id&page_type=$page_type"))) + ->url(url::site("quick/rotate/$item->id/ccw?csrf=$csrf&from_id={$item->id}&page_type=$page_type"))) ->append( Menu::factory("ajax_link") ->id("rotate_cw") @@ -270,7 +308,7 @@ class gallery_event_Core { ->css_class("ui-icon-rotate-cw") ->ajax_handler("function(data) { " . "\$.gallery_replace_image(data, \$('$item_css_selector')) }") - ->url(url::site("quick/rotate/$item->id/cw?csrf=$csrf&from_id=$theme_item->id&page_type=$page_type"))); + ->url(url::site("quick/rotate/$item->id/cw?csrf=$csrf&from_id={$item->id}&page_type=$page_type"))); } if ($item->id != item::root()->id) { @@ -300,10 +338,10 @@ class gallery_event_Core { ->append( Menu::factory("dialog") ->id("delete") - ->label(t("Delete this photo")) + ->label($delete_text) ->css_class("ui-icon-trash") ->css_class("g-quick-delete") - ->url(url::site("quick/form_delete/$item->id?csrf=$csrf&from_id=$theme_item->id&page_type=$page_type"))); + ->url(url::site("quick/form_delete/$item->id?csrf=$csrf&from_id={$item->id}&page_type=$page_type"))); } } } @@ -404,7 +442,7 @@ class gallery_event_Core { ->id("edit") ->label($edit_title) ->css_class("ui-icon-pencil") - ->url(url::site("quick/form_edit/$item->id?from_id=$theme_item->id"))); + ->url(url::site("quick/form_edit/$item->id?from_id={$theme_item->id}"))); if ($item->is_photo() && graphics::can("rotate")) { $options_menu @@ -415,7 +453,7 @@ class gallery_event_Core { ->css_class("ui-icon-rotate-ccw") ->ajax_handler("function(data) { " . "\$.gallery_replace_image(data, \$('$thumb_css_selector')) }") - ->url(url::site("quick/rotate/$item->id/ccw?csrf=$csrf&from_id=$theme_item->id&page_type=$page_type"))) + ->url(url::site("quick/rotate/$item->id/ccw?csrf=$csrf&from_id={$theme_item->id}&page_type=$page_type"))) ->append( Menu::factory("ajax_link") ->id("rotate_cw") @@ -423,7 +461,7 @@ class gallery_event_Core { ->css_class("ui-icon-rotate-cw") ->ajax_handler("function(data) { " . "\$.gallery_replace_image(data, \$('$thumb_css_selector')) }") - ->url(url::site("quick/rotate/$item->id/cw?csrf=$csrf&from_id=$theme_item->id&page_type=$page_type"))); + ->url(url::site("quick/rotate/$item->id/cw?csrf=$csrf&from_id={$theme_item->id}&page_type=$page_type"))); } // @todo Don't move photos from the photo page; we don't yet have a good way of redirecting @@ -462,7 +500,7 @@ class gallery_event_Core { ->id("delete") ->label($delete_title) ->css_class("ui-icon-trash") - ->url(url::site("quick/form_delete/$item->id?csrf=$csrf&from_id=$theme_item->id&page_type=$page_type"))); + ->url(url::site("quick/form_delete/$item->id?csrf=$csrf&from_id={$theme_item->id}&page_type=$page_type"))); } if ($item->is_album()) { @@ -471,7 +509,7 @@ class gallery_event_Core { ->id("add_item") ->label(t("Add a photo")) ->css_class("ui-icon-plus") - ->url(url::site("flash_uploader/app/$item->id"))) + ->url(url::site("uploader/index/$item->id"))) ->append(Menu::factory("dialog") ->id("add_album") ->label(t("Add an album")) diff --git a/modules/gallery/helpers/gallery_task.php b/modules/gallery/helpers/gallery_task.php index bc128b3e..da9fba49 100644 --- a/modules/gallery/helpers/gallery_task.php +++ b/modules/gallery/helpers/gallery_task.php @@ -18,8 +18,15 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ class gallery_task_Core { - const MPTT_LEFT = 0; - const MPTT_RIGHT = 1; + const FIX_STATE_START_MPTT = 0; + const FIX_STATE_RUN_MPTT = 1; + const FIX_STATE_START_PERMISSIONS = 2; + const FIX_STATE_RUN_PERMISSIONS = 3; + const FIX_STATE_START_DUPE_SLUGS = 4; + const FIX_STATE_RUN_DUPE_SLUGS = 5; + const FIX_STATE_START_DUPE_NAMES = 6; + const FIX_STATE_RUN_DUPE_NAMES = 7; + const FIX_STATE_DONE = 8; static function available_tasks() { $dirty_count = graphics::find_dirty_images_query()->count_records(); @@ -47,10 +54,10 @@ class gallery_task_Core { ->severity(log::SUCCESS); $tasks[] = Task_Definition::factory() - ->callback("gallery_task::fix_mptt") - ->name(t("Fix Album/Photo hierarchy")) - ->description(t("Fix problems where your album/photo breadcrumbs are out of " . - "sync with your actual hierarchy.")) + ->callback("gallery_task::fix") + ->name(t("Fix your Gallery")) + ->description(t("Fix up a variety of little problems that might be causing " . + "your Gallery to act a little weird")) ->severity(log::SUCCESS); return $tasks; @@ -310,78 +317,261 @@ class gallery_task_Core { } } - static function fix_mptt($task) { + static function fix($task) { $start = microtime(true); $total = $task->get("total"); if (empty($total)) { - $task->set("total", $total = db::build()->count_records("items")); - $task->set("stack", "1:" . self::MPTT_LEFT); + // mptt: 2 operations for every item + // permissions: 1 operation for every album + // dupe slugs: 1 operation for each unique conflicted slug + $total = 2 * db::build()->count_records("items"); + $total += db::build()->where("type", "=", "album")->count_records("items"); + foreach (self::find_dupe_slugs() as $row) { + $total++; + } + foreach (self::find_dupe_names() as $row) { + $total++; + } + + $task->set("total", $total); + $task->set("state", $state = self::FIX_STATE_START_MPTT); $task->set("ptr", 1); $task->set("completed", 0); } - $ptr = $task->get("ptr"); - $stack = explode(" ", $task->get("stack")); $completed = $task->get("completed"); - - // Implement a depth-first tree walk using a stack. Not the most efficient, but it's simple. - while ($stack && microtime(true) - $start < 1.5) { - list($id, $state) = explode(":", array_pop($stack)); + $state = $task->get("state"); + + // This is a state machine that checks each item in the database. It verifies the following + // attributes for an item. + // 1. Left and right MPTT pointers are correct + // 2. The .htaccess permission files for restricted items exist and are well formed. + // 3. The relative_path_cache and relative_url_cache values are set to null. + // + // We'll do a depth-first tree walk over our hierarchy using only the adjacency data because + // we don't trust MPTT here (that might be what we're here to fix!). Avoid avoid using ORM + // calls as much as possible since they're expensive. + while ($state != self::FIX_STATE_DONE && microtime(true) - $start < 1.5) { switch ($state) { - case self::MPTT_LEFT: - self::fix_mptt_set_left($id, $ptr++); - $item = ORM::factory("item", $id); - array_push($stack, $id . ":" . self::MPTT_RIGHT); - foreach (self::fix_mptt_children($id) as $child) { - array_push($stack, $child->id . ":" . self::MPTT_LEFT); + case self::FIX_STATE_START_MPTT: + $task->set("ptr", $ptr = 1); + $task->set("stack", item::root()->id . ":L"); + $state = self::FIX_STATE_RUN_MPTT; + break; + + case self::FIX_STATE_RUN_MPTT: + $ptr = $task->get("ptr"); + $stack = explode(" ", $task->get("stack")); + list ($id, $ptr_mode) = explode(":", array_pop($stack)); + if ($ptr_mode == "L") { + $stack[] = "$id:R"; + db::build() + ->update("items") + ->set("left_ptr", $ptr++) + ->where("id", "=", $id) + ->execute(); + + foreach (db::build() + ->select(array("id")) + ->from("items") + ->where("parent_id", "=", $id) + ->order_by("left_ptr", "ASC") + ->execute() as $child) { + array_push($stack, "{$child->id}:L"); + } + } else if ($ptr_mode == "R") { + db::build() + ->update("items") + ->set("right_ptr", $ptr++) + ->set("relative_path_cache", null) + ->set("relative_url_cache", null) + ->where("id", "=", $id) + ->execute(); + } + $task->set("ptr", $ptr); + $task->set("stack", implode(" ", $stack)); + $completed++; + + if (empty($stack)) { + $state = self::FIX_STATE_START_DUPE_SLUGS; + } + break; + + + case self::FIX_STATE_START_DUPE_SLUGS: + $stack = array(); + foreach (self::find_dupe_slugs() as $row) { + list ($parent_id, $slug) = explode(":", $row->parent_slug, 2); + $stack[] = join(":", array($parent_id, $slug)); + } + if ($stack) { + $task->set("stack", implode(" ", $stack)); + $state = self::FIX_STATE_RUN_DUPE_SLUGS; + } else { + $state = self::FIX_STATE_START_DUPE_NAMES; } break; - case self::MPTT_RIGHT: - self::fix_mptt_set_right($id, $ptr++); + case self::FIX_STATE_RUN_DUPE_SLUGS: + $stack = explode(" ", $task->get("stack")); + list ($parent_id, $slug) = explode(":", array_pop($stack)); + + // We want to leave the first one alone and update all conflicts to be random values. + $fixed = 0; + $conflicts = ORM::factory("item") + ->where("parent_id", "=", $parent_id) + ->where("slug", "=", $slug) + ->find_all(1, 1); + if ($conflicts->count() && $conflict = $conflicts->current()) { + $task->log("Fixing conflicting slug for item id {$conflict->id}"); + db::build() + ->update("items") + ->set("slug", $slug . "-" . (string)rand(1000, 9999)) + ->where("id", "=", $conflict->id) + ->execute(); + + // We fixed one conflict, but there might be more so put this parent back on the stack + // and try again. We won't consider it completed when we don't fix a conflict. This + // guarantees that we won't spend too long fixing one set of conflicts, and that we + // won't stop before all are fixed. + $stack[] = "$parent_id:$slug"; + break; + } + $task->set("stack", implode(" ", $stack)); $completed++; + + if (empty($stack)) { + $state = self::FIX_STATE_START_DUPE_NAMES; + } + break; + + case self::FIX_STATE_START_DUPE_NAMES: + $stack = array(); + foreach (self::find_dupe_names() as $row) { + list ($parent_id, $name) = explode(":", $row->parent_name, 2); + $stack[] = join(":", array($parent_id, $name)); + } + if ($stack) { + $task->set("stack", implode(" ", $stack)); + $state = self::FIX_STATE_RUN_DUPE_NAMES; + } else { + $state = self::FIX_STATE_START_PERMISSIONS; + } + break; + + case self::FIX_STATE_RUN_DUPE_NAMES: + $stack = explode(" ", $task->get("stack")); + list ($parent_id, $name) = explode(":", array_pop($stack)); + + $fixed = 0; + // We want to leave the first one alone and update all conflicts to be random values. + $conflicts = ORM::factory("item") + ->where("parent_id", "=", $parent_id) + ->where("name", "=", $name) + ->find_all(1, 1); + if ($conflicts->count() && $conflict = $conflicts->current()) { + $task->log("Fixing conflicting name for item id {$conflict->id}"); + db::build() + ->update("items") + ->set("name", $name . "-" . (string)rand(1000, 9999)) + ->where("id", "=", $conflict->id) + ->execute(); + + // We fixed one conflict, but there might be more so put this parent back on the stack + // and try again. We won't consider it completed when we don't fix a conflict. This + // guarantees that we won't spend too long fixing one set of conflicts, and that we + // won't stop before all are fixed. + $stack[] = "$parent_id:$name"; + break; + } + $task->set("stack", implode(" ", $stack)); + $completed++; + + if (empty($stack)) { + $state = self::FIX_STATE_START_PERMISSIONS; + } + break; + + case self::FIX_STATE_START_PERMISSIONS: + $stack = array(); + foreach (db::build() + ->select("id") + ->from("items") + ->where("type", "=", "album") + ->execute() as $row) { + $stack[] = $row->id; + } + $task->set("stack", implode(" ", $stack)); + $state = self::FIX_STATE_RUN_PERMISSIONS; + break; + + case self::FIX_STATE_RUN_PERMISSIONS: + $stack = explode(" ", $task->get("stack")); + $id = array_pop($stack); + + $everybody = identity::everybody(); + $view_col = "view_{$everybody->id}"; + $view_full_col = "view_full_{$everybody->id}"; + $intent = ORM::factory("access_intent")->where("item_id", "=", $id)->find(); + + // Only load the item if we're going to use it below + if ($intent->$view_col === access::DENY || + $intent->$view_full_col === access::DENY) { + $item = ORM::factory("item", $id); + } + if ($intent->$view_col === access::DENY) { + access::update_htaccess_files($item, $everybody, "view", access::DENY); + } + if ($intent->$view_full_col === access::DENY) { + access::update_htaccess_files($item, $everybody, "view_full", access::DENY); + } + $task->set("stack", implode(" ", $stack)); + $completed++; + + if (empty($stack)) { + $state = self::FIX_STATE_DONE; + } break; } } - $task->set("stack", implode(" ", $stack)); - $task->set("ptr", $ptr); + $task->set("state", $state); $task->set("completed", $completed); - if ($total == $completed) { + if ($state == self::FIX_STATE_DONE) { $task->done = true; $task->state = "success"; $task->percent_complete = 100; } else { $task->percent_complete = round(100 * $completed / $total); } - $task->status = t2("One row updated", "%count / %total rows updated", $completed, + $task->status = t2("One operation complete", "%count / %total operations complete", $completed, array("total" => $total)); } - static function fix_mptt_children($parent_id) { + static function find_dupe_slugs() { return db::build() + ->select_distinct( + array("parent_slug" => new Database_Expression("CONCAT(`parent_id`, ':', `slug`)"))) ->select("id") + ->select(array("C" => "COUNT(\"*\")")) ->from("items") - ->where("parent_id", "=", $parent_id) - ->order_by("left_ptr", "ASC") + ->having("C", ">", 1) + ->group_by("parent_slug") ->execute(); } - static function fix_mptt_set_left($id, $value) { - db::build() - ->update("items") - ->set("left_ptr", $value) - ->where("id", "=", $id) - ->execute(); - } - - static function fix_mptt_set_right($id, $value) { - db::build() - ->update("items") - ->set("right_ptr", $value) - ->where("id", "=", $id) + static function find_dupe_names() { + return db::build() + ->select_distinct( + array("parent_name" => new Database_Expression("CONCAT(`parent_id`, ':', `name`)"))) + ->select("id") + ->select(array("C" => "COUNT(\"*\")")) + ->from("items") + ->where("type", "<>", "album") + ->having("C", ">", 1) + ->group_by("parent_name") ->execute(); } }
\ No newline at end of file diff --git a/modules/gallery/helpers/gallery_theme.php b/modules/gallery/helpers/gallery_theme.php index 62deb831..0d7cc44a 100644 --- a/modules/gallery/helpers/gallery_theme.php +++ b/modules/gallery/helpers/gallery_theme.php @@ -79,8 +79,7 @@ class gallery_theme_Core { return L10n_Client_Controller::l10n_form(); } - if ($session->get("after_install")) { - $session->delete("after_install"); + if ($session->get_once("after_install")) { return new View("welcome_message_loader.html"); } } diff --git a/modules/gallery/helpers/graphics.php b/modules/gallery/helpers/graphics.php index 7ff09d13..cc4d2e76 100644 --- a/modules/gallery/helpers/graphics.php +++ b/modules/gallery/helpers/graphics.php @@ -117,7 +117,18 @@ class graphics_Core { static function generate($item) { if ($item->is_album()) { if (!$cover = $item->album_cover()) { - // This album has no cover; there's nothing to generate. + // This album has no cover; there's nothing to generate. Because of an old bug, it's + // possible that there's an album cover item id that points to an invalid item. In that + // case, just null out the album cover item id. It's not optimal to do that at this low + // level, but it's not trivial to find these cases quickly in an upgrade script and if we + // don't do this, the album may be permanently marked as "needs rebuilding" + // + // ref: http://sourceforge.net/apps/trac/gallery/ticket/1172 + // http://gallery.menalto.com/node/96926 + if ($item->album_cover_item_id) { + $item->album_cover_item_id = null; + $item->save(); + } return; } $input_file = $cover->file_path(); diff --git a/modules/gallery/helpers/item.php b/modules/gallery/helpers/item.php index aef68c6e..092904a5 100644 --- a/modules/gallery/helpers/item.php +++ b/modules/gallery/helpers/item.php @@ -105,9 +105,15 @@ class item_Core { model_cache::clear(); $parent->album_cover_item_id = $item->is_album() ? $item->album_cover_item_id : $item->id; - $parent->thumb_dirty = 1; + if ($item->thumb_dirty) { + $parent->thumb_dirty = 1; + graphics::generate($parent); + } else { + copy($item->thumb_path(), $parent->thumb_path()); + $parent->thumb_width = $item->thumb_width; + $parent->thumb_height = $item->thumb_height; + } $parent->save(); - graphics::generate($parent); $grand_parent = $parent->parent(); if ($grand_parent && access::can("edit", $grand_parent) && $grand_parent->album_cover_item_id == null) { @@ -158,8 +164,10 @@ class item_Core { */ static function get_delete_form($item) { $page_type = Input::instance()->get("page_type"); + $from_id = Input::instance()->get("from_id"); $form = new Forge( - "quick/delete/$item->id?page_type=$page_type", "", "post", array("id" => "g-confirm-delete")); + "quick/delete/$item->id?page_type=$page_type&from_id=$from_id", "", + "post", array("id" => "g-confirm-delete")); $group = $form->group("confirm_delete")->label(t("Confirm Deletion")); $group->submit("")->value(t("Delete")); $form->script("") diff --git a/modules/gallery/helpers/json.php b/modules/gallery/helpers/json.php new file mode 100644 index 00000000..a39db27a --- /dev/null +++ b/modules/gallery/helpers/json.php @@ -0,0 +1,33 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2010 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ +class json_Core { + /** + * JSON Encode a reply to the browser and set the content type to specify that it's a JSON + * payload. + * + * @param mixed $message string or object to json encode and print + */ + static function reply($message) { + if (!headers_sent()) { + header("Content-Type: application/json; charset=" . Kohana::CHARSET); + } + print json_encode($message); + } +}
\ No newline at end of file diff --git a/modules/gallery/helpers/l10n_client.php b/modules/gallery/helpers/l10n_client.php index f45f502d..43cc2036 100644 --- a/modules/gallery/helpers/l10n_client.php +++ b/modules/gallery/helpers/l10n_client.php @@ -20,13 +20,12 @@ class l10n_client_Core { - private static function _server_url() { - return "http://gallery.menalto.com/index.php"; + private static function _server_url($path) { + return "http://gallery.menalto.com/translations/$path"; } static function server_api_key_url() { - return self::_server_url() . "?q=translations/userkey/" . - self::client_token(); + return self::_server_url("userkey/" . self::client_token()); } static function client_token() { @@ -53,7 +52,7 @@ class l10n_client_Core { static function validate_api_key($api_key) { $version = "1.0"; - $url = self::_server_url() . "?q=translations/status"; + $url = self::_server_url("status"); $signature = self::_sign($version, $api_key); list ($response_data, $response_status) = remote::post( @@ -123,7 +122,7 @@ class l10n_client_Core { } $request_data = json_encode($request); - $url = self::_server_url() . "?q=translations/fetch"; + $url = self::_server_url("fetch"); list ($response_data, $response_status) = remote::post($url, array("data" => $request_data)); if (!remote::success($response_status)) { throw new Exception("@todo TRANSLATIONS_FETCH_REQUEST_FAILED " . $response_status); @@ -195,12 +194,15 @@ class l10n_client_Core { // @todo Batch requests (max request size) // @todo include base_revision in submission / how to handle resubmissions / edit fights? + $request = new stdClass(); foreach (db::build() ->select("key", "message", "locale", "base_revision", "translation") ->from("outgoing_translations") ->execute() as $row) { $key = $row->key; if (!isset($request->{$key})) { + $request->{$key} = new stdClass(); + $request->{$key}->translations = new stdClass(); $request->{$key}->message = json_encode(unserialize($row->message)); } $request->{$key}->translations->{$row->locale} = json_encode(unserialize($row->translation)); @@ -208,7 +210,7 @@ class l10n_client_Core { // @todo reduce memory consumption, e.g. free $request $request_data = json_encode($request); - $url = self::_server_url() . "?q=translations/submit"; + $url = self::_server_url("submit"); $signature = self::_sign($request_data); list ($response_data, $response_status) = remote::post( diff --git a/modules/gallery/helpers/movie.php b/modules/gallery/helpers/movie.php index 6333eaf2..bbb5b66c 100644 --- a/modules/gallery/helpers/movie.php +++ b/modules/gallery/helpers/movie.php @@ -26,7 +26,7 @@ class movie_Core { static function get_edit_form($movie) { $form = new Forge("movies/update/$movie->id", "", "post", array("id" => "g-edit-movie-form")); - $form->hidden("from_id"); + $form->hidden("from_id")->value($movie->id); $group = $form->group("edit_item")->label(t("Edit Movie")); $group->input("title")->label(t("Title"))->value($movie->title) ->error_messages("required", t("You must provide a title")) diff --git a/modules/gallery/helpers/theme.php b/modules/gallery/helpers/theme.php index 980ee11a..3589a5b7 100644 --- a/modules/gallery/helpers/theme.php +++ b/modules/gallery/helpers/theme.php @@ -53,13 +53,22 @@ class theme_Core { if (file_exists(THEMEPATH . self::$site_theme_name . "/admin")) { array_unshift($modules, THEMEPATH . self::$site_theme_name . "/admin"); } + // Admins can override the site theme, temporarily. This lets us preview themes. + if (identity::active_user()->admin && $override = $input->get("theme")) { + if (file_exists(THEMEPATH . $override)) { + self::$admin_theme_name = $override; + array_unshift($modules, THEMEPATH . self::$admin_theme_name); + } else { + Kohana_Log::add("error", "Missing override admin theme: '$override'"); + } + } } else { // Admins can override the site theme, temporarily. This lets us preview themes. if (identity::active_user()->admin && $override = $input->get("theme")) { if (file_exists(THEMEPATH . $override)) { self::$site_theme_name = $override; } else { - Kohana_Log::add("error", "Missing override theme: '$override'"); + Kohana_Log::add("error", "Missing override site theme: '$override'"); } } array_unshift($modules, THEMEPATH . self::$site_theme_name); @@ -70,7 +79,7 @@ class theme_Core { static function get_edit_form_admin() { $form = new Forge("admin/theme_options/save/", "", null, array("id" =>"g-theme-options-form")); - $group = $form->group("edit_theme"); + $group = $form->group("edit_theme")->label(t("Theme layout")); $group->input("page_size")->label(t("Items per page"))->id("g-page-size") ->rules("required|valid_digit") ->error_messages("required", t("You must enter a number")) @@ -95,7 +104,8 @@ class theme_Core { module::event("theme_edit_form", $form); - $group = $form->group("buttons"); + $group = $form->group("buttons") + ->set_attr("style","border: none"); $group->submit("")->value(t("Save")); return $form; } diff --git a/modules/gallery/libraries/IdentityProvider.php b/modules/gallery/libraries/IdentityProvider.php index 067a9929..5f341c90 100644 --- a/modules/gallery/libraries/IdentityProvider.php +++ b/modules/gallery/libraries/IdentityProvider.php @@ -99,18 +99,24 @@ class IdentityProvider_Core { $restore_already_running = true; // Make sure new provider is not in the database - module::uninstall($new_provider); - - // Lets reset to the current provider so that the gallery installation is still - // working. - module::set_var("gallery", "identity_provider", null); - IdentityProvider::change_provider($current_provider); - module::activate($current_provider); + try { + module::uninstall($new_provider); + + // Lets reset to the current provider so that the gallery installation is still + // working. + module::set_var("gallery", "identity_provider", null); + IdentityProvider::change_provider($current_provider); + module::activate($current_provider); + } catch (Exception $e2) { + Kohana_Log::add("error", "Error restoring original identity provider\n" . + $e2->getMessage() . "\n" . $e2->getTraceAsString()); + } + message::error( t("Error attempting to enable \"%new_provider\" identity provider, " . "reverted to \"%old_provider\" identity provider", array("new_provider" => $new_provider, "old_provider" => $current_provider))); - + $restore_already_running = false; } throw $e; diff --git a/modules/gallery/libraries/MY_View.php b/modules/gallery/libraries/MY_View.php index d76e25ff..ded77792 100644 --- a/modules/gallery/libraries/MY_View.php +++ b/modules/gallery/libraries/MY_View.php @@ -67,6 +67,10 @@ class View extends View_Core { try { $this->kohana_local_data = array_merge(View::$global_data, $this->kohana_local_data); return parent::render($print, $renderer, $modifier); + } catch (ORM_Validation_Exception $e) { + Kohana_Log::add("error", $e->getMessage() . "\n" . $e->getTraceAsString()); + Kohana_Log::add("error", "Validation errors: " . print_r($e->validation->errors(), 1)); + return ""; } catch (Exception $e) { Kohana_Log::add("error", $e->getMessage() . "\n" . $e->getTraceAsString()); return ""; diff --git a/modules/gallery/libraries/ORM_MPTT.php b/modules/gallery/libraries/ORM_MPTT.php index e5b8ecd4..d8d88e4e 100644 --- a/modules/gallery/libraries/ORM_MPTT.php +++ b/modules/gallery/libraries/ORM_MPTT.php @@ -92,6 +92,7 @@ class ORM_MPTT_Core extends ORM { // Deleting children affects the MPTT tree, so we have to reload each child before we // delete it so that we have current left_ptr/right_ptr pointers. This is inefficient. // @todo load each child once, not twice. + set_time_limit(30); $item->reload()->delete(); } @@ -175,7 +176,7 @@ class ORM_MPTT_Core extends ORM { } /** - * Return all of the children of this node, ordered by id. + * Return the number of children of this node. * * @chainable * @param array additional where clauses diff --git a/modules/gallery/models/item.php b/modules/gallery/models/item.php index 4d05e4da..c00b7972 100644 --- a/modules/gallery/models/item.php +++ b/modules/gallery/models/item.php @@ -364,7 +364,7 @@ class Item_Model extends ORM_MPTT { $this->name .= "." . $pi["extension"]; } - $this->mime_type = strtolower($pi["extension"]) == "mp4" ? "video/mp4" : "video/x-flv"; + $this->mime_type = in_array(strtolower($pi["extension"]), array("mp4", "m4v")) ? "video/mp4" : "video/x-flv"; } } @@ -760,9 +760,9 @@ class Item_Model extends ORM_MPTT { // Conditional rules if ($this->id == 1) { - // Root album can't have a name or slug so replace the rules - $this->rules["name"] = array("rules" => array("length[0]")); - $this->rules["slug"] = array("rules" => array("length[0]")); + // We don't care about the name and slug for the root album. + $this->rules["name"] = array(); + $this->rules["slug"] = array(); } // Movies and photos must have data files diff --git a/modules/gallery/tests/Item_Helper_Test.php b/modules/gallery/tests/Item_Helper_Test.php index 00229973..eb2458cb 100644 --- a/modules/gallery/tests/Item_Helper_Test.php +++ b/modules/gallery/tests/Item_Helper_Test.php @@ -111,15 +111,18 @@ class Item_Helper_Test extends Gallery_Unit_Test_Case { $this->assert_not_same($rand, $photo2->slug); } - public function delete_cover_photo_picks_new_album_cover() { - $album = test::random_album(); + public function delete_cover_photo_picks_new_album_cover_test() { + $parent = test::random_album(); + $album = test::random_album($parent); $photo1 = test::random_photo($album); // At this point, $photo1 is the album cover. We verify this in // Item_Model_Test::first_photo_becomes_album_cover $photo2 = test::random_photo($album); $photo1->delete(); $album->reload(); + $parent->reload(); $this->assert_same($photo2->id, $album->album_cover_item_id); + $this->assert_same($photo2->id, $parent->album_cover_item_id); } } diff --git a/modules/gallery/tests/controller_auth_data.txt b/modules/gallery/tests/controller_auth_data.txt index 8263f79d..3c9b3afc 100644 --- a/modules/gallery/tests/controller_auth_data.txt +++ b/modules/gallery/tests/controller_auth_data.txt @@ -16,9 +16,9 @@ modules/gallery/controllers/login.php auth_html modules/gallery/controllers/logout.php index DIRTY_AUTH modules/gallery/controllers/maintenance.php index DIRTY_AUTH modules/gallery/controllers/quick.php form_edit DIRTY_CSRF -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/uploader.php start DIRTY_AUTH +modules/gallery/controllers/uploader.php finish DIRTY_AUTH modules/gallery/controllers/user_profile.php show DIRTY_AUTH modules/gallery/controllers/user_profile.php contact DIRTY_AUTH modules/gallery/controllers/user_profile.php send DIRTY_AUTH diff --git a/modules/gallery/tests/xss_data.txt b/modules/gallery/tests/xss_data.txt index 4ead8a3f..02483865 100644 --- a/modules/gallery/tests/xss_data.txt +++ b/modules/gallery/tests/xss_data.txt @@ -28,11 +28,11 @@ modules/comment/views/comment.mrss.php 16 DIRTY_JS $feed- modules/comment/views/comment.mrss.php 19 DIRTY_JS $feed->next_page_uri modules/comment/views/comment.mrss.php 21 DIRTY $pub_date modules/comment/views/comment.mrss.php 22 DIRTY $pub_date -modules/comment/views/comment.mrss.php 28 DIRTY $child->item_uri -modules/comment/views/comment.mrss.php 29 DIRTY $child->pub_date -modules/comment/views/comment.mrss.php 34 DIRTY_ATTR $child->thumb_url -modules/comment/views/comment.mrss.php 35 DIRTY_ATTR $child->thumb_height -modules/comment/views/comment.mrss.php 35 DIRTY_ATTR $child->thumb_width +modules/comment/views/comment.mrss.php 28 DIRTY $comment->item_uri +modules/comment/views/comment.mrss.php 29 DIRTY $comment->pub_date +modules/comment/views/comment.mrss.php 34 DIRTY_ATTR $comment->thumb_url +modules/comment/views/comment.mrss.php 35 DIRTY_ATTR $comment->thumb_height +modules/comment/views/comment.mrss.php 35 DIRTY_ATTR $comment->thumb_width modules/comment/views/comments.html.php 28 DIRTY_ATTR $comment->id modules/comment/views/comments.html.php 31 DIRTY_ATTR $comment->author()->avatar_url(40,$theme->url(,true)) modules/comment/views/user_profile_comments.html.php 5 DIRTY_ATTR $comment->id @@ -76,7 +76,7 @@ modules/gallery/views/admin_languages.html.php 61 DIRTY_ATTR ($de modules/gallery/views/admin_languages.html.php 62 DIRTY form::checkbox("installed_locales[]",$code,isset($installed_locales[$code])) modules/gallery/views/admin_languages.html.php 63 DIRTY $display_name modules/gallery/views/admin_languages.html.php 65 DIRTY form::radio("default_locale",$code,($default_locale==$code),((isset($installed_locales[$code]))?'':'disabled="disabled"')) -modules/gallery/views/admin_languages.html.php 110 DIRTY $share_translations_form +modules/gallery/views/admin_languages.html.php 113 DIRTY $share_translations_form modules/gallery/views/admin_maintenance.html.php 24 DIRTY_ATTR text::alternate("g-odd","g-even") modules/gallery/views/admin_maintenance.html.php 24 DIRTY_ATTR log::severity_class($task->severity) modules/gallery/views/admin_maintenance.html.php 25 DIRTY_ATTR log::severity_class($task->severity) @@ -109,7 +109,7 @@ modules/gallery/views/admin_sidebar.html.php 50 DIRTY $avail modules/gallery/views/admin_sidebar.html.php 58 DIRTY $active modules/gallery/views/admin_sidebar_blocks.html.php 4 DIRTY_ATTR $ref modules/gallery/views/admin_sidebar_blocks.html.php 4 DIRTY $text -modules/gallery/views/admin_theme_options.html.php 6 DIRTY $form +modules/gallery/views/admin_theme_options.html.php 36 DIRTY $form modules/gallery/views/admin_themes.html.php 3 DIRTY_JS url::site("admin/themes/choose") modules/gallery/views/admin_themes.html.php 5 DIRTY_JS $csrf modules/gallery/views/admin_themes.html.php 22 DIRTY $themes[$site]->name @@ -120,7 +120,7 @@ modules/gallery/views/admin_themes.html.php 60 DIRTY $theme modules/gallery/views/admin_themes.html.php 62 DIRTY $themes[$admin]->description modules/gallery/views/admin_themes.html.php 76 DIRTY $info->name modules/gallery/views/admin_themes.html.php 78 DIRTY $info->description -modules/gallery/views/admin_themes_preview.html.php 7 DIRTY_ATTR $url +modules/gallery/views/admin_themes_preview.html.php 8 DIRTY_ATTR $url modules/gallery/views/error_404.html.php 14 DIRTY $login_form modules/gallery/views/error_admin.html.php 150 DIRTY $type modules/gallery/views/error_admin.html.php 150 DIRTY $code @@ -167,7 +167,7 @@ modules/gallery/views/error_admin.html.php 251 DIRTY_ATTR $env modules/gallery/views/error_admin.html.php 257 DIRTY $key modules/gallery/views/error_admin.html.php 261 DIRTY Kohana_Exception::safe_dump($value,$key) modules/gallery/views/form_uploadify.html.php 9 DIRTY_JS url::file("lib/uploadify/uploadify.swf") -modules/gallery/views/form_uploadify.html.php 10 DIRTY_JS url::site("simple_uploader/add_photo/{$album->id}") +modules/gallery/views/form_uploadify.html.php 10 DIRTY_JS url::site("uploader/add_photo/{$album->id}") modules/gallery/views/form_uploadify.html.php 14 DIRTY_JS url::file("lib/uploadify/cancel.png") modules/gallery/views/form_uploadify.html.php 15 DIRTY_JS $simultaneous_upload_limit modules/gallery/views/in_place_edit.html.php 2 DIRTY form::open($action,array("method"=>"post","id"=>"g-in-place-edit-form","class"=>"g-short-form")) @@ -190,7 +190,7 @@ modules/gallery/views/l10n_client.html.php 58 DIRTY form:: modules/gallery/views/l10n_client.html.php 62 DIRTY form::textarea("l10n-edit-plural-translation-many","",' rows="2"') modules/gallery/views/l10n_client.html.php 67 DIRTY form::textarea("l10n-edit-plural-translation-other","",' rows="2"') modules/gallery/views/login_ajax.html.php 6 DIRTY_JS url::site("password/reset") -modules/gallery/views/login_ajax.html.php 37 DIRTY $form +modules/gallery/views/login_ajax.html.php 44 DIRTY $form modules/gallery/views/maintenance.html.php 46 DIRTY auth::get_login_form("login/auth_html") modules/gallery/views/menu.html.php 4 DIRTY $menu->css_id?"id='$menu->css_id'":"" modules/gallery/views/menu.html.php 4 DIRTY_ATTR $menu->css_class @@ -206,9 +206,9 @@ modules/gallery/views/menu_dialog.html.php 5 DIRTY_JS $menu- modules/gallery/views/menu_link.html.php 3 DIRTY $menu->css_id?"id='{$menu->css_id}'":"" modules/gallery/views/menu_link.html.php 4 DIRTY_ATTR $menu->css_class modules/gallery/views/menu_link.html.php 5 DIRTY_JS $menu->url -modules/gallery/views/move_browse.html.php 4 DIRTY_JS url::site("move/show_sub_tree/{$source->id}/__TARGETID__") -modules/gallery/views/move_browse.html.php 39 DIRTY $tree -modules/gallery/views/move_browse.html.php 43 DIRTY access::csrf_form_field() +modules/gallery/views/move_browse.html.php 5 DIRTY_JS url::site("move/show_sub_tree/{$source->id}/__TARGETID__") +modules/gallery/views/move_browse.html.php 40 DIRTY $tree +modules/gallery/views/move_browse.html.php 44 DIRTY access::csrf_form_field() modules/gallery/views/move_tree.html.php 2 DIRTY $parent->thumb_img(array(),25); modules/gallery/views/move_tree.html.php 4 DIRTY_JS $parent->id modules/gallery/views/move_tree.html.php 6 DIRTY_JS $parent->id @@ -253,15 +253,16 @@ modules/gallery/views/permissions_form.html.php 75 DIRTY_JS $item- modules/gallery/views/permissions_form.html.php 80 DIRTY_JS $group->id modules/gallery/views/permissions_form.html.php 80 DIRTY_JS $permission->id modules/gallery/views/permissions_form.html.php 80 DIRTY_JS $item->id +modules/gallery/views/quick_delete_confirm.html.php 11 DIRTY $form modules/gallery/views/reauthenticate.html.php 9 DIRTY $form -modules/gallery/views/upgrader.html.php 57 DIRTY_ATTR $done?"muted":"" -modules/gallery/views/upgrader.html.php 61 DIRTY_ATTR $done?"muted":"" -modules/gallery/views/upgrader.html.php 69 DIRTY_ATTR $module->version==$module->code_version?"current":"upgradeable" -modules/gallery/views/upgrader.html.php 70 DIRTY_ATTR $id -modules/gallery/views/upgrader.html.php 74 DIRTY $module->version -modules/gallery/views/upgrader.html.php 77 DIRTY $module->code_version -modules/gallery/views/upgrader.html.php 99 DIRTY_ATTR $done?"muted":"" -modules/gallery/views/upgrader.html.php 102 DIRTY_ATTR $done?"muted":"" +modules/gallery/views/upgrader.html.php 59 DIRTY_ATTR $done?"muted":"" +modules/gallery/views/upgrader.html.php 63 DIRTY_ATTR $done?"muted":"" +modules/gallery/views/upgrader.html.php 71 DIRTY_ATTR $module->version==$module->code_version?"current":"upgradeable" +modules/gallery/views/upgrader.html.php 72 DIRTY_ATTR $id +modules/gallery/views/upgrader.html.php 76 DIRTY $module->version +modules/gallery/views/upgrader.html.php 79 DIRTY $module->code_version +modules/gallery/views/upgrader.html.php 101 DIRTY_ATTR $done?"muted":"" +modules/gallery/views/upgrader.html.php 104 DIRTY_ATTR $done?"muted":"" modules/gallery/views/user_languages_block.html.php 2 DIRTY form::dropdown("g-select-session-locale",$installed_locales,$selected) modules/gallery/views/user_profile.html.php 34 DIRTY_ATTR $user->avatar_url(40,$theme->url(,true)) modules/gallery/views/user_profile.html.php 43 DIRTY $info->view @@ -288,7 +289,9 @@ modules/organize/views/organize_dialog.html.php 95 DIRTY_JS $sort_ modules/organize/views/organize_dialog.html.php 96 DIRTY_JS $album->id modules/organize/views/organize_dialog.html.php 97 DIRTY_JS $rest_uri modules/organize/views/organize_dialog.html.php 98 DIRTY_JS $controller_uri +modules/organize/views/organize_dialog.html.php 104 DIRTY_JS $flash_minimum_version="10.0.0" modules/organize/views/organize_dialog.html.php 122 DIRTY_JS $swf_uri +modules/organize/views/organize_dialog.html.php 136 DIRTY_ATTR request::protocol() modules/recaptcha/views/admin_recaptcha.html.php 11 DIRTY $form modules/recaptcha/views/admin_recaptcha.html.php 23 DIRTY_JS $public_key modules/recaptcha/views/form_recaptcha.html.php 7 DIRTY_JS $public_key @@ -298,26 +301,26 @@ modules/rss/views/feed.mrss.php 16 DIRTY_JS $feed- modules/rss/views/feed.mrss.php 19 DIRTY_JS $feed->next_page_uri modules/rss/views/feed.mrss.php 21 DIRTY $pub_date modules/rss/views/feed.mrss.php 22 DIRTY $pub_date -modules/rss/views/feed.mrss.php 28 DIRTY date("D, d M Y H:i:s T",$child->created); -modules/rss/views/feed.mrss.php 35 DIRTY_ATTR $child->resize_url(true) -modules/rss/views/feed.mrss.php 37 DIRTY_ATTR $child->resize_height -modules/rss/views/feed.mrss.php 37 DIRTY_ATTR $child->resize_width -modules/rss/views/feed.mrss.php 40 DIRTY_ATTR $child->thumb_url(true) -modules/rss/views/feed.mrss.php 42 DIRTY_ATTR $child->thumb_height -modules/rss/views/feed.mrss.php 42 DIRTY_ATTR $child->thumb_width -modules/rss/views/feed.mrss.php 48 DIRTY_ATTR $child->thumb_url(true) -modules/rss/views/feed.mrss.php 49 DIRTY_ATTR $child->thumb_height -modules/rss/views/feed.mrss.php 50 DIRTY_ATTR $child->thumb_width -modules/rss/views/feed.mrss.php 57 DIRTY_ATTR $child->resize_url(true) -modules/rss/views/feed.mrss.php 58 DIRTY_ATTR @filesize($child->resize_path()) -modules/rss/views/feed.mrss.php 59 DIRTY_ATTR $child->mime_type -modules/rss/views/feed.mrss.php 60 DIRTY_ATTR $child->resize_height -modules/rss/views/feed.mrss.php 61 DIRTY_ATTR $child->resize_width -modules/rss/views/feed.mrss.php 65 DIRTY_ATTR $child->file_url(true) -modules/rss/views/feed.mrss.php 66 DIRTY_ATTR @filesize($child->file_path()) -modules/rss/views/feed.mrss.php 67 DIRTY_ATTR $child->mime_type -modules/rss/views/feed.mrss.php 68 DIRTY_ATTR $child->height -modules/rss/views/feed.mrss.php 69 DIRTY_ATTR $child->width +modules/rss/views/feed.mrss.php 28 DIRTY date("D, d M Y H:i:s T",$item->created); +modules/rss/views/feed.mrss.php 35 DIRTY_ATTR $item->resize_url(true) +modules/rss/views/feed.mrss.php 37 DIRTY_ATTR $item->resize_height +modules/rss/views/feed.mrss.php 37 DIRTY_ATTR $item->resize_width +modules/rss/views/feed.mrss.php 40 DIRTY_ATTR $item->thumb_url(true) +modules/rss/views/feed.mrss.php 42 DIRTY_ATTR $item->thumb_height +modules/rss/views/feed.mrss.php 42 DIRTY_ATTR $item->thumb_width +modules/rss/views/feed.mrss.php 48 DIRTY_ATTR $item->thumb_url(true) +modules/rss/views/feed.mrss.php 49 DIRTY_ATTR $item->thumb_height +modules/rss/views/feed.mrss.php 50 DIRTY_ATTR $item->thumb_width +modules/rss/views/feed.mrss.php 57 DIRTY_ATTR $item->resize_url(true) +modules/rss/views/feed.mrss.php 58 DIRTY_ATTR @filesize($item->resize_path()) +modules/rss/views/feed.mrss.php 59 DIRTY_ATTR $item->mime_type +modules/rss/views/feed.mrss.php 60 DIRTY_ATTR $item->resize_height +modules/rss/views/feed.mrss.php 61 DIRTY_ATTR $item->resize_width +modules/rss/views/feed.mrss.php 65 DIRTY_ATTR $item->file_url(true) +modules/rss/views/feed.mrss.php 66 DIRTY_ATTR @filesize($item->file_path()) +modules/rss/views/feed.mrss.php 67 DIRTY_ATTR $item->mime_type +modules/rss/views/feed.mrss.php 68 DIRTY_ATTR $item->height +modules/rss/views/feed.mrss.php 69 DIRTY_ATTR $item->width modules/rss/views/rss_block.html.php 6 DIRTY_JS rss::url($url) modules/search/views/search.html.php 27 DIRTY_ATTR $item_class modules/search/views/search.html.php 28 DIRTY_JS $item->url() @@ -406,9 +409,9 @@ themes/wind/views/page.html.php 81 DIRTY $heade themes/wind/views/page.html.php 83 DIRTY_JS item::root()->url() themes/wind/views/page.html.php 87 DIRTY $theme->user_menu() themes/wind/views/page.html.php 108 DIRTY_JS $parent->url($parent==$theme->item()->parent()?"show={$theme->item()->id}":null) -themes/wind/views/page.html.php 124 DIRTY $content -themes/wind/views/page.html.php 130 DIRTY newView("sidebar.html") -themes/wind/views/page.html.php 137 DIRTY $footer_text +themes/wind/views/page.html.php 126 DIRTY $content +themes/wind/views/page.html.php 132 DIRTY newView("sidebar.html") +themes/wind/views/page.html.php 139 DIRTY $footer_text themes/wind/views/paginator.html.php 33 DIRTY_JS $first_page_url themes/wind/views/paginator.html.php 42 DIRTY_JS $previous_page_url themes/wind/views/paginator.html.php 70 DIRTY_JS $next_page_url diff --git a/modules/gallery/views/admin_languages.html.php b/modules/gallery/views/admin_languages.html.php index d4b7b0c1..01d1ce3f 100644 --- a/modules/gallery/views/admin_languages.html.php +++ b/modules/gallery/views/admin_languages.html.php @@ -107,6 +107,9 @@ </a> <h3><?= t("Sharing your translations") ?></h3> + <p> + <?= t("Sharing your own translations with the Gallery community is easy. Please do!") ?> + </p> <?= $share_translations_form ?> </div> </div> diff --git a/modules/gallery/views/admin_maintenance.html.php b/modules/gallery/views/admin_maintenance.html.php index ac597715..ad0e2f55 100644 --- a/modules/gallery/views/admin_maintenance.html.php +++ b/modules/gallery/views/admin_maintenance.html.php @@ -41,6 +41,9 @@ <? if ($running_tasks->count()): ?> <div id="g-running-tasks"> + <a href="<?= url::site("admin/maintenance/cancel_running_tasks?csrf=$csrf") ?>" + class="g-button g-right ui-icon-left ui-state-default ui-corner-all"> + <?= t("cancel all running") ?></a> <h2> <?= t("Running tasks") ?> </h2> <table> <tr> @@ -60,9 +63,6 @@ <?= t("Owner") ?> </th> <th> - <a href="<?= url::site("admin/maintenance/cancel_running_tasks?csrf=$csrf") ?>" - class="g-button g-right ui-icon-left ui-state-default ui-corner-all"> - <?= t("cancel all") ?></a> <?= t("Action") ?> </th> </tr> diff --git a/modules/gallery/views/admin_maintenance_show_log.html.php b/modules/gallery/views/admin_maintenance_show_log.html.php index d2472fdc..ecf882f0 100644 --- a/modules/gallery/views/admin_maintenance_show_log.html.php +++ b/modules/gallery/views/admin_maintenance_show_log.html.php @@ -15,5 +15,5 @@ appendTo('body').submit().remove(); <pre><?= html::purify($task->get_log()) ?></pre> </div> <button id="g-close" class="ui-state-default ui-corner-all" onclick="dismiss()"><?= t("Close") ?></button> - <button id="g-save" class="ui-state-default ui-corner-all" onclick="download()"><?= t("Save") ?></button> + <button id="g-save" class="ui-state-default ui-corner-all" onclick="download()"><?= t("Download") ?></button> </div> diff --git a/modules/gallery/views/admin_theme_options.html.php b/modules/gallery/views/admin_theme_options.html.php index a4bf1c4e..b4a90682 100644 --- a/modules/gallery/views/admin_theme_options.html.php +++ b/modules/gallery/views/admin_theme_options.html.php @@ -1,7 +1,37 @@ <?php defined("SYSPATH") or die("No direct script access.") ?> -<div class="g-block"> - <h1> <?= t("Theme Options") ?> </h1> +<script type="text/javascript"> + $("#g-theme-options-form").ready(function() { + var contents = $("#g-theme-options-form fieldset:not(:last-child)"); + if (contents.length > 1) { + $("<div id='g-theme-options-form-tabs'>" + + " <ul class='tabnav'></ul>" + + "</div>").insertBefore("#g-theme-options-form fieldset:last-child"); + $(contents).each(function(index) { + var text = $("legend", this).text(); + var tabId = "tab_" + index; + var tabContentId = "tab_content_" + index; + if (text == "") { + text = <?= t("Tab_")->for_js() ?> + index; + } + $(".tabnav").append( + "<li><a id='" + tabId + "' href='#" + tabContentId + "'>" + text + "</a></li>"); + $("#g-theme-options-form-tabs").append( + "<div id='" + tabContentId + "' class='tabdiv'></div>"); + if ($("li.g-error", this).length > 0) { + $("#" + tabId).addClass("g-error"); + } + $("#" + tabContentId).append($("ul", this)); + $(this).remove(); + }); + $("#g-theme-options-form-tabs").tabs({}); + } else { + $("#g-theme-options-form fieldset:first legend").hide(); + } + }); +</script> +<div class="g-block"> + <h1> <?= t("Theme options") ?> </h1> <div class="g-block-content"> <?= $form ?> </div> diff --git a/modules/gallery/views/admin_themes_preview.html.php b/modules/gallery/views/admin_themes_preview.html.php index a7aea172..80a6158b 100644 --- a/modules/gallery/views/admin_themes_preview.html.php +++ b/modules/gallery/views/admin_themes_preview.html.php @@ -1,4 +1,5 @@ <?php defined("SYSPATH") or die("No direct script access.") ?> +<h1><?= t("Preview of the %theme_name theme", array("theme_name" => $info->name)) ?></h1> <p> <a href="<?= url::site("admin/themes/choose/$type/$theme_name?csrf=$csrf") ?>"> <?= t("Activate <strong>%theme_name</strong>", array("theme_name" => $info->name)) ?> diff --git a/modules/gallery/views/form_uploadify.html.php b/modules/gallery/views/form_uploadify.html.php index 588fa16d..4f564b07 100644 --- a/modules/gallery/views/form_uploadify.html.php +++ b/modules/gallery/views/form_uploadify.html.php @@ -7,9 +7,9 @@ width: 150, height: 33, uploader: "<?= url::file("lib/uploadify/uploadify.swf") ?>", - script: "<?= url::site("flash_uploader/add_photo/{$album->id}") ?>", + script: "<?= url::site("uploader/add_photo/{$album->id}") ?>", scriptData: <?= json_encode($script_data) ?>, - fileExt: "*.gif;*.jpg;*.jpeg;*.png;*.flv;*.mp4;*.GIF;*.JPG;*.JPEG;*.PNG;*.FLV;*.MP4", + fileExt: "*.gif;*.jpg;*.jpeg;*.png;*.flv;*.mp4;*.m4v;*.GIF;*.JPG;*.JPEG;*.PNG;*.FLV;*.MP4;*.M4V", fileDesc: <?= t("Photos and movies")->for_js() ?>, cancelImg: "<?= url::file("lib/uploadify/cancel.png") ?>", simUploadLimit: <?= $simultaneous_upload_limit ?>, diff --git a/modules/gallery/views/kohana/error.php b/modules/gallery/views/kohana/error.php index cc9d2e84..0e84f093 100644 --- a/modules/gallery/views/kohana/error.php +++ b/modules/gallery/views/kohana/error.php @@ -25,16 +25,20 @@ try { // Try to show a themed error page for 404 errors if ($e instanceof Kohana_404_Exception) { - $view = new Theme_View("page.html", "other", "error"); - $view->page_title = t("Dang... Page not found!"); - $view->content = new View("error_404.html"); - $user = identity::active_user(); - $view->content->is_guest = $user && $user->guest; - if ($view->content->is_guest) { - $view->content->login_form = new View("login_ajax.html"); - $view->content->login_form->form = auth::get_login_form("login/auth_html"); + if (Router::$controller == "file_proxy") { + print "File not found"; + } else { + $view = new Theme_View("page.html", "other", "error"); + $view->page_title = t("Dang... Page not found!"); + $view->content = new View("error_404.html"); + $user = identity::active_user(); + $view->content->is_guest = $user && $user->guest; + if ($view->content->is_guest) { + $view->content->login_form = new View("login_ajax.html"); + $view->content->login_form->form = auth::get_login_form("login/auth_html"); + } + print $view; } - print $view; return; } diff --git a/modules/gallery/views/move_browse.html.php b/modules/gallery/views/move_browse.html.php index ce3fc2fd..f77c724c 100644 --- a/modules/gallery/views/move_browse.html.php +++ b/modules/gallery/views/move_browse.html.php @@ -1,4 +1,5 @@ <?php defined("SYSPATH") or die("No direct script access.") ?> +<div> <script type="text/javascript"> var load_tree = function(target_id, locked) { var load_url = "<?= url::site("move/show_sub_tree/{$source->id}/__TARGETID__") ?>"; @@ -24,13 +25,13 @@ } } </script> -<h1 style="display: none"> +<h1 style="display:none" > <? if ($source->type == "photo"): ?> - <? t("Move this photo to a new album") ?> + <?= t("Move this photo to a new album") ?> <? elseif ($source->type == "movie"): ?> - <? t("Move this movie to a new album") ?> + <?= t("Move this movie to a new album") ?> <? elseif ($source->type == "album"): ?> - <? t("Move this album to a new album") ?> + <?= t("Move this album to a new album") ?> <? endif ?> </h1> <div id="g-move"> @@ -42,6 +43,8 @@ <form method="post" action="<?= url::site("move/save/$source->id") ?>"> <?= access::csrf_form_field() ?> <input type="hidden" name="target_id" value="" /> - <input type="submit" id="g-move-button" value="<?= t("Move")->for_html_attr() ?>" disabled="disabled"/> + <input type="submit" id="g-move-button" value="<?= t("Move")->for_html_attr() ?>" + disabled="disabled" class="submit" /> </form> </div> +</div> diff --git a/modules/gallery/views/quick_delete_confirm.html.php b/modules/gallery/views/quick_delete_confirm.html.php new file mode 100644 index 00000000..176ffb96 --- /dev/null +++ b/modules/gallery/views/quick_delete_confirm.html.php @@ -0,0 +1,12 @@ +<?php defined("SYSPATH") or die("No direct script access.") ?> +<div class="ui-helper-clearfix"> + <p> + <? if ($item->is_album()): ?> + <?= t("Delete the album <b>%title</b>? All photos and movies in the album will also be deleted.", + array("title" => html::purify($item->title))) ?> + <? else: ?> + <?= t("Are you sure you want to delete <b>%title</b>?", array("title" => html::purify($item->title))) ?> + <? endif ?> + </p> + <?= $form ?> +</div> diff --git a/modules/gallery/views/upgrader.html.php b/modules/gallery/views/upgrader.html.php index c7a96cb5..0ce24ef8 100644 --- a/modules/gallery/views/upgrader.html.php +++ b/modules/gallery/views/upgrader.html.php @@ -1,4 +1,6 @@ <?php defined("SYSPATH") or die("No direct script access.") ?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title><?= t("Gallery 3 upgrader") ?></title> @@ -107,7 +109,7 @@ </li> <? endif ?> <? endforeach ?> - </p> + </ul> <? endif ?> <? else: // can_upgrade ?> <h1> <?= t("Who are you?") ?> </h1> @@ -121,11 +123,11 @@ </div> <div id="footer"> <p> - <i> + <em> <?= t("Did something go wrong? Try the <a href=\"%faq_url\">FAQ</a> or ask in the <a href=\"%forums_url\">Gallery forums</a>.", array("faq_url" => "http://codex.gallery2.org/Gallery3:FAQ", "forums_url" => "http://gallery.menalto.com/forum")) ?> - </i> + </em> </p> </div> </div> diff --git a/modules/gallery/views/welcome_message.html.php b/modules/gallery/views/welcome_message.html.php index 4d6ed726..1fcca971 100644 --- a/modules/gallery/views/welcome_message.html.php +++ b/modules/gallery/views/welcome_message.html.php @@ -18,7 +18,7 @@ <a href="<?= url::site("admin/users/edit_user_form/{$user->id}") ?>" title="<?= t("Edit your profile")->for_html_attr() ?>" id="g-after-install-change-password-link" - class="g-button ui-state-default ui-corners-all"> + class="g-button ui-state-default ui-corner-all"> <?= t("Change password and email now") ?> </a> <script type="text/javascript"> diff --git a/modules/organize/controllers/organize.php b/modules/organize/controllers/organize.php index 135a6fc9..3005eb67 100644 --- a/modules/organize/controllers/organize.php +++ b/modules/organize/controllers/organize.php @@ -34,7 +34,7 @@ class Organize_Controller extends Controller { $file_filter = json_encode( array("photo" => array("label" => "Images", "types" => array("*.jpg", "*.jpeg", "*.png", "*.gif")), - "movie" => array("label" => "Movies", "types" => array("*.flv", "*.mp4")))); + "movie" => array("label" => "Movies", "types" => array("*.flv", "*.mp4", "*.m4v")))); $v = new View("organize_dialog.html"); $v->album = $album; @@ -51,10 +51,10 @@ class Organize_Controller extends Controller { } function add_album_fields() { - print json_encode(array("title" => (string)t("Title"), - "description" => (string)t("Description"), - "name" => (string)t("Directory name"), - "slug" => (string)t("Internet Address"))); + json::reply(array("title" => (string)t("Title"), + "description" => (string)t("Description"), + "name" => (string)t("Directory name"), + "slug" => (string)t("Internet Address"))); } } diff --git a/modules/organize/views/organize_dialog.html.php b/modules/organize/views/organize_dialog.html.php index c41e5960..4cc6385e 100644 --- a/modules/organize/views/organize_dialog.html.php +++ b/modules/organize/views/organize_dialog.html.php @@ -2,7 +2,7 @@ <script type="text/javascript" src="<?= url::file("lib/swfobject.js") ?>"></script> <style type="text/css" media="screen"> #flashContent { - display:none; + //display:none; } .g-organize { @@ -98,12 +98,12 @@ controllerUri: "<?= $controller_uri ?>" }; }; - /* - For version detection, set to min. required Flash Player version, or 0 (or 0.0.0), - for no version detection. - */ - var swfVersionStr = "0.0.0"; - /* To use express install, set to playerProductInstall.swf, otherwise the empty string.*/ + + // For version detection, set to minimum required Flash Player version, or 0 (or 0.0.0), + // for no version detection. + var swfVersionStr = "<?= $flash_minimum_version = "10.0.0" ?>"; + + // To use express install, set to playerProductInstall.swf, otherwise the empty string. var xiSwfUrlStr = ""; var flashvars = {}; @@ -127,5 +127,14 @@ <!-- The following spans are placeholders so we can load the hover and active styles for the flex component --> <span id="g-organize-hover" /><span id="g-organize-active" /> <h1 style="display:none"><?= t("Organize :: %name", array("name" => html::purify($album->title))) ?></h1> - <div id="flashContent"> </div> + <div id="flashContent"> + <p> + <?= t("To use the Organize feature, please ensure that Adobe Flash Player version %flash_minimum_version " . + "or greater is installed.", array("flash_minimum_version" => $flash_minimum_version)) ?> + </p> + <a href="http://www.adobe.com/go/getflashplayer"> + <img src="<?= request::protocol() ?>://www.adobe.com/images/shared/download_buttons/get_flash_player.gif" + alt=<?= t("Get Adobe Flash Player")->for_js() ?> /> + </a> + </div> </div> diff --git a/modules/rest/controllers/rest.php b/modules/rest/controllers/rest.php index f8a46515..bf2f0a54 100644 --- a/modules/rest/controllers/rest.php +++ b/modules/rest/controllers/rest.php @@ -81,12 +81,19 @@ class Rest_Controller extends Controller { } $response = call_user_func(array($handler_class, $handler_method), $request); + if ($handler_method == "post") { + // post methods must return a response containing a URI. + header("HTTP/1.1 201 Created"); + header("Location: {$response['url']}"); + } rest::reply($response); } catch (ORM_Validation_Exception $e) { // Note: this is totally insufficient because it doesn't take into account localization. We // either need to map the result values to localized strings in the application code, or every // client needs its own l10n string set. throw new Rest_Exception("Bad Request", 400, $e->validation->errors()); + } catch (Kohana_404_Exception $e) { + throw new Rest_Exception("Not Found", 404); } } }
\ No newline at end of file diff --git a/modules/rest/helpers/rest.php b/modules/rest/helpers/rest.php index bcb12d58..644779da 100644 --- a/modules/rest/helpers/rest.php +++ b/modules/rest/helpers/rest.php @@ -35,8 +35,7 @@ class rest_Core { } print "<pre>$html</pre>"; } else { - header("Content-type: application/json"); - print json_encode($data); + json::reply($data); } } diff --git a/modules/rest/tests/Rest_Controller_Test.php b/modules/rest/tests/Rest_Controller_Test.php index 0c8a4a98..43139d29 100644 --- a/modules/rest/tests/Rest_Controller_Test.php +++ b/modules/rest/tests/Rest_Controller_Test.php @@ -142,8 +142,8 @@ class Rest_Controller_Test extends Gallery_Unit_Test_Case { } class mock_rest { - static function get($request) { return $request; } - static function post($request) { return $request; } - static function put($request) { return $request; } - static function delete($request) { return $request; } + static function get($request) { return (array)$request; } + static function post($request) { return (array)$request; } + static function put($request) { return (array)$request; } + static function delete($request) { return (array)$request; } }
\ No newline at end of file diff --git a/modules/rest/views/user_profile_rest.html.php b/modules/rest/views/user_profile_rest.html.php index 397afa89..e81f3d0b 100644 --- a/modules/rest/views/user_profile_rest.html.php +++ b/modules/rest/views/user_profile_rest.html.php @@ -1,8 +1,10 @@ <?php defined("SYSPATH") or die("No direct script access.") ?> <div id="g-rest-detail"> -<ul> - <li id="g-rest-key"> - <p><b><?= t("Key") ?></b>:<?= html::clean($rest_key) ?></p> - </li> -</ul> + <ul> + <li id="g-rest-key"> + <p> + <?= t("<b>Key</b>: %key", array("key" => $rest_key)) ?> + </p> + </li> + </ul> </div> diff --git a/modules/server_add/controllers/server_add.php b/modules/server_add/controllers/server_add.php index 715274ab..e4c3e69c 100644 --- a/modules/server_add/controllers/server_add.php +++ b/modules/server_add/controllers/server_add.php @@ -55,7 +55,7 @@ class Server_Add_Controller extends Admin_Controller { } if (!is_dir($file)) { $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION)); - if (!in_array($ext, array("gif", "jpeg", "jpg", "png", "flv", "mp4"))) { + if (!in_array($ext, array("gif", "jpeg", "jpg", "png", "flv", "mp4", "m4v"))) { continue; } } @@ -91,9 +91,9 @@ class Server_Add_Controller extends Admin_Controller { ->name(t("Add from server")); $task = task::create($task_def, array("item_id" => $item->id, "queue" => $paths)); - print json_encode( + json::reply( array("result" => "started", - "status" => $task->status, + "status" => (string)$task->status, "url" => url::site("server_add/run/$task->id?csrf=" . access::csrf_token()))); } @@ -111,9 +111,9 @@ class Server_Add_Controller extends Admin_Controller { $task = task::run($task_id); // Prevent the JavaScript code from breaking by forcing a period as // decimal separator for all locales with sprintf("%F", $value). - print json_encode(array("done" => (bool)$task->done, - "status" => $task->status, - "percent_complete" => sprintf("%F", $task->percent_complete))); + json::reply(array("done" => (bool)$task->done, + "status" => (string)$task->status, + "percent_complete" => sprintf("%F", $task->percent_complete))); } /** @@ -162,7 +162,7 @@ class Server_Add_Controller extends Admin_Controller { $queue[] = array($child, $entry_id); } else { $ext = strtolower(pathinfo($child, PATHINFO_EXTENSION)); - if (in_array($ext, array("gif", "jpeg", "jpg", "png", "flv", "mp4")) && + if (in_array($ext, array("gif", "jpeg", "jpg", "png", "flv", "mp4", "m4v")) && filesize($child) > 0) { $child_entry = ORM::factory("server_add_file"); $child_entry->task_id = $task->id; @@ -249,7 +249,7 @@ class Server_Add_Controller extends Admin_Controller { $photo->owner_id = $owner_id; $photo->save(); $entry->item_id = $photo->id; - } else if (in_array($extension, array("flv", "mp4"))) { + } else if (in_array($extension, array("flv", "mp4", "m4v"))) { $movie = ORM::factory("item"); $movie->type = "movie"; $movie->parent_id = $parent->id; diff --git a/modules/tag/controllers/admin_tags.php b/modules/tag/controllers/admin_tags.php index 9e875d14..0c82579b 100644 --- a/modules/tag/controllers/admin_tags.php +++ b/modules/tag/controllers/admin_tags.php @@ -57,11 +57,9 @@ class Admin_Tags_Controller extends Admin_Controller { message::success(t("Deleted tag %tag_name", array("tag_name" => $name))); log::success("tags", t("Deleted tag %tag_name", array("tag_name" => $name))); - print json_encode( - array("result" => "success", - "location" => url::site("admin/tags"))); + json::reply(array("result" => "success", "location" => url::site("admin/tags"))); } else { - print json_encode(array("result" => "error", "form" => (string) $form)); + print $form; } } @@ -93,15 +91,14 @@ class Admin_Tags_Controller extends Admin_Controller { $tag->name = $in_place_edit->value(); $tag->save(); - $message = t("Renamed tag %old_name to %new_name", + $message = t("Renamed tag <b>%old_name</b> to <b>%new_name</b>", array("old_name" => $old_name, "new_name" => $tag->name)); message::success($message); log::success("tags", $message); - print json_encode(array("result" => "success", - "location" => url::site("admin/tags"))); + json::reply(array("result" => "success", "location" => url::site("admin/tags"))); } else { - print json_encode(array("result" => "error", "form" => $in_place_edit->render())); + json::reply(array("result" => "error", "form" => (string)$in_place_edit->render())); } } diff --git a/modules/tag/controllers/tags.php b/modules/tag/controllers/tags.php index f3d456d3..bc657644 100644 --- a/modules/tag/controllers/tags.php +++ b/modules/tag/controllers/tags.php @@ -67,11 +67,9 @@ class Tags_Controller extends Controller { } } - print json_encode( - array("result" => "success", - "cloud" => (string)tag::cloud(30))); + json::reply(array("result" => "success", "cloud" => (string)tag::cloud(30))); } else { - print json_encode(array("result" => "error", "form" => (string) $form)); + json::reply(array("result" => "error", "html" => (string)$form)); } } diff --git a/modules/tag/helpers/tag_event.php b/modules/tag/helpers/tag_event.php index a790b930..0cc2170c 100644 --- a/modules/tag/helpers/tag_event.php +++ b/modules/tag/helpers/tag_event.php @@ -36,7 +36,8 @@ class tag_event_Core { $tag = str_replace("\0", "", $tag); foreach (explode(",", $tag) as $word) { $word = trim($word); - if (function_exists("mb_detect_encoding") && mb_detect_encoding($word) != "UTF-8") { + if (function_exists("mb_detect_encoding") && + mb_detect_encoding($word, "ISO-8859-1, UTF-8") != "UTF-8") { $word = utf8_encode($word); } $tags[$word] = 1; @@ -67,8 +68,8 @@ class tag_event_Core { static function item_edit_form($item, $form) { $url = url::site("tags/autocomplete"); $form->script("") - ->text("$('form input[id=tags]').ready(function() { - $('form input[id=tags]').autocomplete( + ->text("$('form input[name=tags]').ready(function() { + $('form input[name=tags]').autocomplete( '$url', {max: 30, multiple: true, multipleSeparator: ',', cacheLength: 1}); });"); @@ -105,6 +106,10 @@ class tag_event_Core { } static function add_photos_form($album, $form) { + if (!isset($group->uploadify)) { + return; + } + $group = $form->add_photos; $group->input("tags") ->label(t("Add tags to all uploaded files")) @@ -124,6 +129,10 @@ class tag_event_Core { } static function add_photos_form_completed($album, $form) { + if (!isset($group->uploadify)) { + return; + } + foreach (explode(",", $form->add_photos->tags->value) as $tag_name) { $tag_name = trim($tag_name); if ($tag_name) { diff --git a/modules/user/controllers/admin_users.php b/modules/user/controllers/admin_users.php index e14be393..24478aa5 100644 --- a/modules/user/controllers/admin_users.php +++ b/modules/user/controllers/admin_users.php @@ -54,9 +54,9 @@ class Admin_Users_Controller extends Admin_Controller { $user->save(); module::event("user_add_form_admin_completed", $user, $form); message::success(t("Created user %user_name", array("user_name" => $user->name))); - print json_encode(array("result" => "success")); + json::reply(array("result" => "success")); } else { - print json_encode(array("result" => "error", "form" => (string) $form)); + print json::reply(array("result" => "error", "html" => (string)$form)); } } @@ -81,13 +81,13 @@ class Admin_Users_Controller extends Admin_Controller { $name = $user->name; $user->delete(); } else { - print json_encode(array("result" => "error", "form" => (string) $form)); + json::reply(array("result" => "error", "html" => (string)$form)); } $message = t("Deleted user %user_name", array("user_name" => $name)); log::success("user", $message); message::success($message); - print json_encode(array("result" => "success")); + json::reply(array("result" => "success")); } public function delete_user_form($id) { @@ -134,9 +134,9 @@ class Admin_Users_Controller extends Admin_Controller { $user->save(); module::event("user_edit_form_admin_completed", $user, $form); message::success(t("Changed user %user_name", array("user_name" => $user->name))); - print json_encode(array("result" => "success")); + json::reply(array("result" => "success")); } else { - print json_encode(array("result" => "error", "form" => (string) $form)); + json::reply(array("result" => "error", "html" => (string) $form)); } } @@ -192,9 +192,9 @@ class Admin_Users_Controller extends Admin_Controller { $group->save(); message::success( t("Created group %group_name", array("group_name" => $group->name))); - print json_encode(array("result" => "success")); + json::reply(array("result" => "success")); } else { - print json_encode(array("result" => "error", "form" => (string) $form)); + json::reply(array("result" => "error", "html" => (string)$form)); } } @@ -215,13 +215,13 @@ class Admin_Users_Controller extends Admin_Controller { $name = $group->name; $group->delete(); } else { - print json_encode(array("result" => "error", "form" => (string) $form)); + json::reply(array("result" => "error", "html" => (string) $form)); } $message = t("Deleted group %group_name", array("group_name" => $name)); log::success("group", $message); message::success($message); - print json_encode(array("result" => "success")); + json::reply(array("result" => "success")); } public function delete_group_form($id) { @@ -258,11 +258,12 @@ class Admin_Users_Controller extends Admin_Controller { $group->save(); message::success( t("Changed group %group_name", array("group_name" => $group->name))); - print json_encode(array("result" => "success")); + json::reply(array("result" => "success")); } else { + $group->reload(); message::error( t("Failed to change group %group_name", array("group_name" => $group->name))); - print json_encode(array("result" => "error", "form" => (string) $form)); + json::reply(array("result" => "error", "html" => (string) $form)); } } @@ -281,7 +282,9 @@ class Admin_Users_Controller extends Admin_Controller { "admin/users/edit_user/$user->id", "", "post", array("id" => "g-edit-user-form")); $group = $form->group("edit_user")->label(t("Edit user")); $group->input("name")->label(t("Username"))->id("g-username")->value($user->name) - ->error_messages("conflict", t("There is already a user with that username")); + ->error_messages("required", t("A name is required")) + ->error_messages("conflict", t("There is already a user with that username")) + ->error_messages("length", t("This name is too long")); $group->input("full_name")->label(t("Full name"))->id("g-fullname")->value($user->full_name) ->error_messages("length", t("This name is too long")); $group->password("password")->label(t("Password"))->id("g-password") @@ -307,7 +310,7 @@ class Admin_Users_Controller extends Admin_Controller { } module::event("user_edit_form_admin", $user, $form); - $group->submit("")->value(t("Modify User")); + $group->submit("")->value(t("Modify user")); return $form; } @@ -352,7 +355,7 @@ class Admin_Users_Controller extends Admin_Controller { $locales = array_merge(array("" => t("« none »")), $locales); $selected_locale = ($user && $user->locale) ? $user->locale : ""; $form->dropdown("locale") - ->label(t("Language Preference")) + ->label(t("Language preference")) ->options($locales) ->selected($selected_locale); } @@ -370,9 +373,13 @@ class Admin_Users_Controller extends Admin_Controller { private function _get_group_edit_form_admin($group) { $form = new Forge("admin/users/edit_group/$group->id", "", "post", array("id" => "g-edit-group-form")); $form_group = $form->group("edit_group")->label(t("Edit group")); - $form_group->input("name")->label(t("Name"))->id("g-name")->value($group->name); - $form_group->inputs["name"]->error_messages( - "conflict", t("There is already a group with that name")); + $form_group->input("name")->label(t("Name"))->id("g-name")->value($group->name) + ->error_messages("required", t("A name is required")); + $form_group->inputs["name"]->error_messages("conflict", t("There is already a group with that name")) + ->error_messages("required", t("You must enter a group name")) + ->error_messages("length", + t("The group name must be between %min_length and %max_length characters", + array("min_length" => 4, "max_length" => 255))); $form_group->submit("")->value(t("Save")); return $form; } @@ -381,8 +388,10 @@ class Admin_Users_Controller extends Admin_Controller { $form = new Forge("admin/users/add_group", "", "post", array("id" => "g-add-group-form")); $form_group = $form->group("add_group")->label(t("Add group")); $form_group->input("name")->label(t("Name"))->id("g-name"); - $form_group->inputs["name"]->error_messages( - "conflict", t("There is already a group with that name")); + $form_group->inputs["name"]->error_messages("conflict", t("There is already a group with that name")) + ->error_messages("required", t("You must enter a group name")) + ->error_messages("length", t("The group name must be at least %min_length characters", + array("min_length" => 4))); $form_group->submit("")->value(t("Add group")); return $form; } diff --git a/modules/user/controllers/password.php b/modules/user/controllers/password.php index 522b6b35..575720a8 100644 --- a/modules/user/controllers/password.php +++ b/modules/user/controllers/password.php @@ -27,8 +27,7 @@ class Password_Controller extends Controller { if ($form->validate()) { $this->_send_reset($form); } else { - print json_encode(array("result" => "error", - "form" => (string) $form)); + json::reply(array("result" => "error", "html" => (string)$form)); } } else { print $form; @@ -83,8 +82,7 @@ class Password_Controller extends Controller { // Always pretend that an email has been sent to avoid leaking // information on what user names are actually real. message::success(t("Password reset email sent")); - print json_encode( - array("result" => "success")); + json::reply(array("result" => "success")); } private static function _reset_form() { diff --git a/modules/user/controllers/users.php b/modules/user/controllers/users.php index 7f3f6b1f..d13cccb2 100644 --- a/modules/user/controllers/users.php +++ b/modules/user/controllers/users.php @@ -54,11 +54,10 @@ class Users_Controller extends Controller { $user->save(); module::event("user_edit_form_completed", $user, $form); message::success(t("User information updated")); - print json_encode( - array("result" => "success", - "resource" => url::site("users/{$user->id}"))); + json::reply(array("result" => "success", + "resource" => url::site("users/{$user->id}"))); } else { - print json_encode(array("result" => "error", "form" => (string) $form)); + json::reply(array("result" => "error", "html" => (string)$form)); } } @@ -87,14 +86,13 @@ class Users_Controller extends Controller { message::success(t("Password changed")); module::event("user_auth", $user); module::event("user_password_change", $user); - print json_encode( - array("result" => "success", - "resource" => url::site("users/{$user->id}"))); + json::reply(array("result" => "success", + "resource" => url::site("users/{$user->id}"))); } else { log::warning("user", t("Failed password change for %name", array("name" => $user->name))); $name = $user->name; module::event("user_auth_failed", $name); - print json_encode(array("result" => "error", "form" => (string) $form)); + json::reply(array("result" => "error", "html" => (string)$form)); } } @@ -122,14 +120,13 @@ class Users_Controller extends Controller { module::event("user_change_email_form_completed", $user, $form); message::success(t("Email address changed")); module::event("user_auth", $user); - print json_encode( - array("result" => "success", - "resource" => url::site("users/{$user->id}"))); + json::reply(array("result" => "success", + "resource" => url::site("users/{$user->id}"))); } else { log::warning("user", t("Failed email change for %name", array("name" => $user->name))); $name = $user->name; module::event("user_auth_failed", $name); - print json_encode(array("result" => "error", "form" => (string) $form)); + json::reply(array("result" => "error", "html" => (string)$form)); } } @@ -234,7 +231,7 @@ class Users_Controller extends Controller { $locales = array_merge(array("" => t("« none »")), $locales); $selected_locale = ($user && $user->locale) ? $user->locale : ""; $form->dropdown("locale") - ->label(t("Language Preference")) + ->label(t("Language preference")) ->options($locales) ->selected($selected_locale); } diff --git a/modules/watermark/controllers/admin_watermarks.php b/modules/watermark/controllers/admin_watermarks.php index 18b463ca..a2cafee0 100644 --- a/modules/watermark/controllers/admin_watermarks.php +++ b/modules/watermark/controllers/admin_watermarks.php @@ -49,11 +49,11 @@ class Admin_Watermarks_Controller extends Admin_Controller { log::success("watermark", t("Watermark changed")); message::success(t("Watermark changed")); - print json_encode( + json::reply( array("result" => "success", "location" => url::site("admin/watermarks"))); } else { - print json_encode(array("result" => "error", "form" => (string) $form)); + json::reply(array("result" => "error", "html" => (string)$form)); } } @@ -79,11 +79,9 @@ class Admin_Watermarks_Controller extends Admin_Controller { log::success("watermark", t("Watermark deleted")); message::success(t("Watermark deleted")); } - print json_encode( - array("result" => "success", - "location" => url::site("admin/watermarks"))); + json::reply(array("result" => "success", "location" => url::site("admin/watermarks"))); } else { - print json_encode(array("result" => "error", "form" => (string) $form)); + json::reply(array("result" => "error", "html" => (string)$form)); } } @@ -120,12 +118,20 @@ class Admin_Watermarks_Controller extends Admin_Controller { message::success(t("Watermark saved")); log::success("watermark", t("Watermark saved")); - print json_encode( - array("result" => "success", - "location" => url::site("admin/watermarks"))); + json::reply(array("result" => "success", "location" => url::site("admin/watermarks"))); } else { - print json_encode(array("result" => "error", "form" => rawurlencode((string) $form))); + // rawurlencode the results because the JS code that uploads the file buffers it in an + // iframe which entitizes the HTML and makes it difficult for the JS to process. If we url + // encode it now, it passes through cleanly. See ticket #797. + json::reply(array("result" => "error", "html" => rawurlencode((string)$form))); } + + // Override the application/json mime type. The dialog based HTML uploader uses an iframe to + // buffer the reply, and on some browsers (Firefox 3.6) it does not know what to do with the + // JSON that it gets back so it puts up a dialog asking the user what to do with it. So force + // the encoding type back to HTML for the iframe. + // See: http://jquery.malsup.com/form/#file-upload + header("Content-Type: text/html; charset=" . Kohana::CHARSET); } private function _update_graphics_rules() { diff --git a/modules/watermark/helpers/watermark.php b/modules/watermark/helpers/watermark.php index 557d31be..515f8abb 100644 --- a/modules/watermark/helpers/watermark.php +++ b/modules/watermark/helpers/watermark.php @@ -24,9 +24,12 @@ class watermark_Core { } $form = new Forge("admin/watermarks/add", "", "post", array("id" => "g-add-watermark-form")); - $group = $form->group("add_watermark")->label(t("Upload Watermark")); - $group->upload("file")->label(t("Watermark"))->rules("allow[jpg,png,gif]|size[1MB]|required"); - $group->dropdown("position")->label(t("Watermark Position")) + $group = $form->group("add_watermark")->label(t("Upload watermark")); + $group->upload("file")->label(t("Watermark"))->rules("allow[jpg,png,gif]|size[1MB]|required") + ->error_messages("required", "You must select a watermark") + ->error_messages("invalid_type", "The watermark must be a JPG, GIF or PNG") + ->error_messages("max_size", "The watermark is too big (1 MB max)"); + $group->dropdown("position")->label(t("Watermark position")) ->options(self::positions()) ->selected("southeast"); $group->dropdown("transparency")->label(t("Transparency (100% = completely transparent)")) diff --git a/system/helpers/text.php b/system/helpers/text.php index bf82f12a..f7f040cd 100644 --- a/system/helpers/text.php +++ b/system/helpers/text.php @@ -20,7 +20,7 @@ class text_Core { public static function limit_words($str, $limit = 100, $end_char = NULL) { $limit = (int) $limit; - $end_char = ($end_char === NULL) ? '…' : $end_char; + $end_char = ($end_char === NULL) ? '…' : $end_char; if (trim($str) === '') return $str; @@ -46,7 +46,7 @@ class text_Core { */ public static function limit_chars($str, $limit = 100, $end_char = NULL, $preserve_words = FALSE) { - $end_char = ($end_char === NULL) ? '…' : $end_char; + $end_char = ($end_char === NULL) ? '…' : $end_char; $limit = (int) $limit; diff --git a/system/libraries/Database_Builder.php b/system/libraries/Database_Builder.php index 62b2a163..e86ce379 100644 --- a/system/libraries/Database_Builder.php +++ b/system/libraries/Database_Builder.php @@ -1021,14 +1021,14 @@ class Database_Builder_Core { $sql .= "\n".'WHERE '.$this->compile_conditions($this->where); } - if ( ! empty($this->having)) + if ( ! empty($this->group_by)) { - $sql .= "\n".'HAVING '.$this->compile_conditions($this->having); + $sql .= "\n".'GROUP BY '.$this->compile_group_by(); } - if ( ! empty($this->group_by)) + if ( ! empty($this->having)) { - $sql .= "\n".'GROUP BY '.$this->compile_group_by(); + $sql .= "\n".'HAVING '.$this->compile_conditions($this->having); } if ( ! empty($this->order_by)) diff --git a/themes/admin_wind/css/screen.css b/themes/admin_wind/css/screen.css index dbfb59e8..2ea60402 100644 --- a/themes/admin_wind/css/screen.css +++ b/themes/admin_wind/css/screen.css @@ -399,6 +399,37 @@ th { background-color: #FFF; } +/* Theme options ~~~~~~~~~~~~~~~~~~~~~~~~ */ +#g-theme-options-form { + border: 1px solid #a6c9e2; +} +#g-theme-options-form-tabs { + border: none !important; +} +#g-theme-options-form fieldset { + border: none; +} + +.ui-tabs .ui-tabs-nav li a { + padding: 0 1em; +} + +.ui-tabs .ui-tabs-nav li a.g-error { + background: none no-repeat scroll 0 0 transparent; + color: #FF0000 !important; +} + +/* Language options ~~~~~~~~~~~~~~~~~~~~~~~~ */ +#g-share-translations-form fieldset { + border: 0px; + margin: 0px; + padding: 0px; +} + +#g-share-translations-form fieldset legend { + display: none; +} + /** ******************************************************************* * 5) Navigation and menus *********************************************************************/ @@ -487,4 +518,4 @@ th { .rtl .g-selected img, .rtl .g-available .g-block img { margin: 0 0 1em 1em; -}
\ No newline at end of file +} diff --git a/themes/wind/css/fix-ie.css b/themes/wind/css/fix-ie.css index f7f08486..ac100da4 100644 --- a/themes/wind/css/fix-ie.css +++ b/themes/wind/css/fix-ie.css @@ -7,6 +7,10 @@ zoom: 1; } +#g-sidebar { + overflow: hidden; +} + #g-photo, #g-movie { zoom: 1; @@ -22,8 +26,17 @@ input.submit { display: inline !important; } +.g-short-form input[type='submit'] { + line-height: 1em; + padding: .38em .3em; +} + #g-add-tag-form input.textbox { - width: 110px; + width: 110px !important; +} + +#g-add-tag-form input[type='submit'] { + padding: .3em 0 !important; } #g-dialog .g-cancel { diff --git a/themes/wind/views/album.html.php b/themes/wind/views/album.html.php index b9072e2b..de196be0 100644 --- a/themes/wind/views/album.html.php +++ b/themes/wind/views/album.html.php @@ -29,7 +29,7 @@ <? endforeach ?> <? else: ?> <? if ($user->admin || access::can("add", $item)): ?> - <? $addurl = url::site("flash_uploader/app/$item->id") ?> + <? $addurl = url::site("uploader/index/$item->id") ?> <li><?= t("There aren't any photos here yet! <a %attrs>Add some</a>.", array("attrs" => html::mark_clean("href=\"$addurl\" class=\"g-dialog-link\""))) ?></li> <? else: ?> diff --git a/themes/wind/views/page.html.php b/themes/wind/views/page.html.php index 16e43c63..9f94b04f 100644 --- a/themes/wind/views/page.html.php +++ b/themes/wind/views/page.html.php @@ -29,7 +29,7 @@ <?= $theme->css("themeroller/ui.base.css") ?> <?= $theme->css("gallery.common.css") ?> <?= $theme->css("screen.css") ?> - <!--[if lt IE 8]> + <!--[if lte IE 8]> <link rel="stylesheet" type="text/css" href="<?= $theme->url("css/fix-ie.css") ?>" media="screen,print,projection" /> <![endif]--> @@ -107,13 +107,13 @@ level you're on the right page. --> <a href="<?= $parent->url($parent == $theme->item()->parent() ? "show={$theme->item()->id}" : null) ?>"> - <?= text::limit_chars(html::purify($parent->title), 15) ?> + <?= html::purify(text::limit_chars($parent->title, 15)) ?> </a> </li> <? $i++ ?> <? endforeach ?> <li class="g-active<? if ($i == 0) print " g-first" ?>"> - <?= text::limit_chars(html::purify($theme->item()->title), 15) ?> + <?= html::purify(text::limit_chars($theme->item()->title, 15)) ?> </li> </ul> <? endif ?> |