From d04080c7be7c8a06bd81a9747943600812339f40 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Thu, 17 Jan 2013 15:05:46 -0500 Subject: Follow-on to 94b26e506c339f50b8d094057bffc1877a79afa9 - make the new legal_file functions more robust when passed an unknown extension. Fixes Item_Model_Test. --- modules/gallery/helpers/legal_file.php | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/helpers/legal_file.php b/modules/gallery/helpers/legal_file.php index b3622764..e6f4cb54 100644 --- a/modules/gallery/helpers/legal_file.php +++ b/modules/gallery/helpers/legal_file.php @@ -19,7 +19,7 @@ */ class legal_file_Core { /** - * Create a default list of allowed photo MIME types paired with their extensions and then let + * Create a default list of allowed photo MIME types paired with their extensions and then let * modules modify it. This is an ordered map, mapping extensions to their MIME types. * Extensions cannot be duplicated, but MIMEs can (e.g. jpeg and jpg both map to image/jpeg). * @@ -32,7 +32,11 @@ class legal_file_Core { module::event("photo_types_by_extension", $types_by_extension_wrapper); if ($extension) { // return matching MIME type - return $types_by_extension_wrapper->types_by_extension[$extension]; + if (isset($types_by_extension_wrapper->types_by_extension[$extension])) { + return $types_by_extension_wrapper->types_by_extension[$extension]; + } else { + return null; + } } else { // return complete array return $types_by_extension_wrapper->types_by_extension; @@ -53,7 +57,11 @@ class legal_file_Core { module::event("movie_types_by_extension", $types_by_extension_wrapper); if ($extension) { // return matching MIME type - return $types_by_extension_wrapper->types_by_extension[$extension]; + if (isset($types_by_extension_wrapper->types_by_extension[$extension])) { + return $types_by_extension_wrapper->types_by_extension[$extension]; + } else { + return null; + } } else { // return complete array return $types_by_extension_wrapper->types_by_extension; -- cgit v1.2.3 From 3a9009492e6d05fe657e3aacf34cf40ca3495db5 Mon Sep 17 00:00:00 2001 From: shadlaws Date: Fri, 18 Jan 2013 20:06:05 +0100 Subject: #1943 - Make legal_file::change_extension more robust. Previously would fail with dots in the directory but no extension. Added unit tests to verify the new change works. --- modules/gallery/helpers/legal_file.php | 9 +++------ modules/gallery/tests/Legal_File_Helper_Test.php | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 6 deletions(-) (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/helpers/legal_file.php b/modules/gallery/helpers/legal_file.php index e6f4cb54..e24aca36 100644 --- a/modules/gallery/helpers/legal_file.php +++ b/modules/gallery/helpers/legal_file.php @@ -137,15 +137,12 @@ class legal_file_Core { } /** - * Convert the extension of a filename. If the original filename has no + * Change the extension of a filename. If the original filename has no * extension, add the new one to the end. */ static function change_extension($filename, $new_ext) { - if (strpos($filename, ".") === false) { - return "{$filename}.{$new_ext}"; - } else { - return preg_replace("/\.[^\.]*?$/", ".{$new_ext}", $filename); - } + $filename_no_ext = preg_replace("/\.[^\.\/]*?$/", "", $filename); + return "{$filename_no_ext}.{$new_ext}"; } /** diff --git a/modules/gallery/tests/Legal_File_Helper_Test.php b/modules/gallery/tests/Legal_File_Helper_Test.php index d80bcafe..d2813633 100644 --- a/modules/gallery/tests/Legal_File_Helper_Test.php +++ b/modules/gallery/tests/Legal_File_Helper_Test.php @@ -36,6 +36,24 @@ class Legal_File_Helper_Test extends Gallery_Unit_Test_Case { legal_file::change_extension("/website/foo.com/VID_20120513_105421.mp4", "jpg")); } + public function change_extension_path_containing_dots_and_no_extension_test() { + $this->assert_equal( + "/website/foo.com/VID_20120513_105421.jpg", + legal_file::change_extension("/website/foo.com/VID_20120513_105421", "jpg")); + } + + public function change_extension_path_containing_dots_and_dot_extension_test() { + $this->assert_equal( + "/website/foo.com/VID_20120513_105421.jpg", + legal_file::change_extension("/website/foo.com/VID_20120513_105421.", "jpg")); + } + + public function change_extension_path_containing_dots_and_non_standard_chars_test() { + $this->assert_equal( + "/j'écris@un#nom/bizarre(mais quand.même/ça_passe.jpg", + legal_file::change_extension("/j'écris@un#nom/bizarre(mais quand.même/ça_passe.\$ÇÀ@€#_", "jpg")); + } + public function smash_extensions_test() { $this->assert_equal("foo_bar.jpg", legal_file::smash_extensions("foo.bar.jpg")); $this->assert_equal("foo_bar_baz.jpg", legal_file::smash_extensions("foo.bar.baz.jpg")); -- cgit v1.2.3 From e2a2a5ce812c249b46d56ed2f738162e3f4cd09b Mon Sep 17 00:00:00 2001 From: shadlaws Date: Sat, 19 Jan 2013 00:52:13 +0100 Subject: #1944 - Fix possible warnings in legal_file::get_photo_types_by_extension and legal_file::get_movie_types_by_extension. Added unit tests for these two functions, too. --- modules/gallery/helpers/legal_file.php | 6 ++-- modules/gallery/tests/Legal_File_Helper_Test.php | 37 ++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/helpers/legal_file.php b/modules/gallery/helpers/legal_file.php index e24aca36..70c07065 100644 --- a/modules/gallery/helpers/legal_file.php +++ b/modules/gallery/helpers/legal_file.php @@ -25,13 +25,14 @@ class legal_file_Core { * * @param string $extension (opt.) - return MIME of extension; if not given, return complete array */ - static function get_photo_types_by_extension($extension=NULL) { + static function get_photo_types_by_extension($extension=null) { $types_by_extension_wrapper = new stdClass(); $types_by_extension_wrapper->types_by_extension = array( "jpg" => "image/jpeg", "jpeg" => "image/jpeg", "gif" => "image/gif", "png" => "image/png"); module::event("photo_types_by_extension", $types_by_extension_wrapper); if ($extension) { // return matching MIME type + $extension = strtolower($extension); if (isset($types_by_extension_wrapper->types_by_extension[$extension])) { return $types_by_extension_wrapper->types_by_extension[$extension]; } else { @@ -50,13 +51,14 @@ class legal_file_Core { * * @param string $extension (opt.) - return MIME of extension; if not given, return complete array */ - static function get_movie_types_by_extension($extension=NULL) { + static function get_movie_types_by_extension($extension=null) { $types_by_extension_wrapper = new stdClass(); $types_by_extension_wrapper->types_by_extension = array( "flv" => "video/x-flv", "mp4" => "video/mp4", "m4v" => "video/x-m4v"); module::event("movie_types_by_extension", $types_by_extension_wrapper); if ($extension) { // return matching MIME type + $extension = strtolower($extension); if (isset($types_by_extension_wrapper->types_by_extension[$extension])) { return $types_by_extension_wrapper->types_by_extension[$extension]; } else { diff --git a/modules/gallery/tests/Legal_File_Helper_Test.php b/modules/gallery/tests/Legal_File_Helper_Test.php index d2813633..66a5cc68 100644 --- a/modules/gallery/tests/Legal_File_Helper_Test.php +++ b/modules/gallery/tests/Legal_File_Helper_Test.php @@ -18,6 +18,43 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ class Legal_File_Helper_Test extends Gallery_Unit_Test_Case { + public function get_photo_types_by_extension_test() { + // Valid extensions return their corresponding mimes, invalid extensions return null + $tests = array("jpg" => "image/jpeg", + "JPG" => "image/jpeg", + "jpeg" => "image/jpeg", + "png" => "image/png", + "Png" => "image/png", + "gif" => "image/gif", + "tif" => null, + "mp4" => null, + "php" => null, + "php.jpg" => null); + foreach ($tests as $extension => $mime_type) { + $this->assert_equal($mime_type, legal_file::get_photo_types_by_extension($extension)); + } + // No extension returns full array + $this->assert_equal(4, count(legal_file::get_photo_types_by_extension())); + } + + public function get_movie_types_by_extension_test() { + // Valid extensions return their corresponding mimes, invalid extensions return null + $tests = array("flv" => "video/x-flv", + "FLV" => "video/x-flv", + "mp4" => "video/mp4", + "Mp4" => "video/mp4", + "m4v" => "video/x-m4v", + "avi" => null, + "jpg" => null, + "php" => null, + "php.flv" => null); + foreach ($tests as $extension => $mime_type) { + $this->assert_equal($mime_type, legal_file::get_movie_types_by_extension($extension)); + } + // No extension returns full array + $this->assert_equal(3, count(legal_file::get_movie_types_by_extension())); + } + public function change_extension_test() { $this->assert_equal("foo.jpg", legal_file::change_extension("foo.png", "jpg")); } -- cgit v1.2.3 From ea8219e1d462362985b526260cd71230a5db2afb Mon Sep 17 00:00:00 2001 From: shadlaws Date: Sat, 19 Jan 2013 00:59:55 +0100 Subject: #1941, 1948 - Fix possible warnings in movie and graphics helpers, add functions to convert between seconds and hh:mm:ss.dd. Also add unit tests for new movie helper functions. --- modules/gallery/helpers/graphics.php | 2 +- modules/gallery/helpers/movie.php | 35 ++++++++++++++++----- modules/gallery/tests/Movie_Helper_Test.php | 49 +++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 8 deletions(-) create mode 100644 modules/gallery/tests/Movie_Helper_Test.php (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/helpers/graphics.php b/modules/gallery/helpers/graphics.php index e7c5da68..c840b920 100644 --- a/modules/gallery/helpers/graphics.php +++ b/modules/gallery/helpers/graphics.php @@ -157,7 +157,7 @@ class graphics_Core { if ($input_item->is_movie()) { // Convert the movie filename to a JPG first, delete anything that might already be there $output_file = legal_file::change_extension($output_file, "jpg"); - unlink($output_file); + @unlink($output_file); // Run movie_extract_frame events, which can either: // - generate an output file, bypassing the ffmpeg-based movie::extract_frame // - add to the options sent to movie::extract_frame (e.g. change frame extract time, diff --git a/modules/gallery/helpers/movie.php b/modules/gallery/helpers/movie.php index 6d70ab2d..3524a8f9 100644 --- a/modules/gallery/helpers/movie.php +++ b/modules/gallery/helpers/movie.php @@ -66,7 +66,7 @@ class movie_Core { * @param string $output_file * @param array $movie_options (optional) */ - static function extract_frame($input_file, $output_file, $movie_options=NULL) { + static function extract_frame($input_file, $output_file, $movie_options=null) { $ffmpeg = movie::find_ffmpeg(); if (empty($ffmpeg)) { throw new Exception("@todo MISSING_FFMPEG"); @@ -74,17 +74,17 @@ class movie_Core { list($width, $height, $mime_type, $extension, $duration) = movie::get_file_metadata($input_file); - if (is_numeric($movie_options["start_time"])) { + if (isset($movie_options["start_time"]) && is_numeric($movie_options["start_time"])) { $start_time = max(0, $movie_options["start_time"]); // ensure it's non-negative } else { $start_time = module::get_var("gallery", "movie_extract_frame_time", 3); // use default } // extract frame at start_time, unless movie is too short $start_time_arg = ($duration >= $start_time + 0.1) ? - "-ss " . date("H:i:s", mktime(0,0,$start_time,0,0,0,0)) : ""; + "-ss " . movie::seconds_to_hhmmssdd($start_time) : ""; - $input_args = $movie_options["input_args"] ? $movie_options["input_args"] : ""; - $output_args = $movie_options["output_args"] ? $movie_options["output_args"] : ""; + $input_args = isset($movie_options["input_args"]) ? $movie_options["input_args"] : ""; + $output_args = isset($movie_options["output_args"]) ? $movie_options["output_args"] : ""; $cmd = escapeshellcmd($ffmpeg) . " $input_args -i " . escapeshellarg($input_file) . " -an $start_time_arg -an -r 1 -vframes 1" . @@ -149,8 +149,8 @@ class movie_Core { $mime_type = legal_file::get_movie_types_by_extension($extension); $mime_type = $mime_type ? $mime_type : "video/x-flv"; // No MIME found? Default to video/x-flv. - if (preg_match("/Duration: (\d+):(\d+):(\d+\.\d+)/", $result, $matches)) { - $duration = 3600 * $matches[1] + 60 * $matches[2] + $matches[3]; + if (preg_match("/Duration: (\d+:\d+:\d+\.\d+)/", $result, $matches)) { + $duration = movie::hhmmssdd_to_seconds($matches[1]); } else if (preg_match("/duration.*?:.*?(\d+)/", $result, $matches)) { $duration = $matches[1]; } else { @@ -160,4 +160,25 @@ class movie_Core { return array($width, $height, $mime_type, $extension, $duration); } + /** + * Return the time/duration formatted in hh:mm:ss.dd from a number of seconds. + * Useful for inputs to ffmpeg. + * + * Note that this is similar to date("H:i:s", mktime(0,0,$seconds,0,0,0,0)), but unlike this + * approach avoids potential issues with time zone and DST mismatch and/or using deprecated + * features (the last argument of mkdate above, which disables DST, is deprecated as of PHP 5.3). + */ + static function seconds_to_hhmmssdd($seconds) { + return sprintf("%02d:%02d:%05.2f", floor($seconds / 3600), floor(($seconds % 3600) / 60), + floor(100 * $seconds % 6000) / 100); + } + + /** + * Return the number of seconds from a time/duration formatted in hh:mm:ss.dd. + * Useful for outputs from ffmpeg. + */ + static function hhmmssdd_to_seconds($hhmmssdd) { + preg_match("/(\d+):(\d+):(\d+\.\d+)/", $hhmmssdd, $matches); + return 3600 * $matches[1] + 60 * $matches[2] + $matches[3]; + } } diff --git a/modules/gallery/tests/Movie_Helper_Test.php b/modules/gallery/tests/Movie_Helper_Test.php new file mode 100644 index 00000000..78ac21a1 --- /dev/null +++ b/modules/gallery/tests/Movie_Helper_Test.php @@ -0,0 +1,49 @@ + 0.5, + "00:00:06.00" => 6, + "00:00:59.99" => 59.999, + "00:01:00.00" => 60.001, + "00:07:00.00" => 7 * 60, + "00:45:19.00" => 45 * 60 + 19, + "03:45:19.00" => 3 * 3600 + 45 * 60 + 19, + "126:45:19.00" => 126 * 3600 + 45 * 60 + 19); + foreach ($times as $hhmmssdd => $seconds) { + $this->assert_equal($hhmmssdd, movie::seconds_to_hhmmssdd($seconds)); + } + } + + public function hhmmssdd_to_seconds_test() { + $times = array("0:00:00.01" => 0.01, + "00:00:00.50" => 0.5, + "00:00:06.00" => 6, + "00:00:59.99" => 59.99, + "00:01:00.00" => 60.00, + "00:07:00.00" => 7 * 60, + "00:45:19.00" => 45 * 60 + 19, + "03:45:19.00" => 3 * 3600 + 45 * 60 + 19, + "126:45:19.00" => 126 * 3600 + 45 * 60 + 19); + foreach ($times as $hhmmssdd => $seconds) { + $this->assert_equal($seconds, movie::hhmmssdd_to_seconds($hhmmssdd)); + } + } +} -- cgit v1.2.3 From 592eff0e5a8af6f74eff4806dc6e7de56ca02761 Mon Sep 17 00:00:00 2001 From: shadlaws Date: Sat, 19 Jan 2013 08:40:19 +0100 Subject: #1942 - Make data_rest and file_proxy more consistent - several minor documentation/formatting changes. No actual functionality changed here. --- modules/gallery/controllers/file_proxy.php | 3 +++ modules/gallery/helpers/data_rest.php | 20 +++++++------------- 2 files changed, 10 insertions(+), 13 deletions(-) (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/controllers/file_proxy.php b/modules/gallery/controllers/file_proxy.php index b9ff7df1..9af58c0c 100644 --- a/modules/gallery/controllers/file_proxy.php +++ b/modules/gallery/controllers/file_proxy.php @@ -100,6 +100,9 @@ class File_Proxy_Controller extends Controller { throw new Kohana_404_Exception(); } + // Note: this code is roughly duplicated in data_rest, so if you modify this, please look to + // see if you should make the same change there as well. + if ($type == "albums") { $file = $item->file_path(); } else if ($type == "resizes") { diff --git a/modules/gallery/helpers/data_rest.php b/modules/gallery/helpers/data_rest.php index 343975f6..abd4638e 100644 --- a/modules/gallery/helpers/data_rest.php +++ b/modules/gallery/helpers/data_rest.php @@ -32,27 +32,21 @@ class data_rest_Core { throw new Rest_Exception("Bad Request", 400, array("errors" => array("size" => "invalid"))); } - switch ($p->size) { - case "thumb": - $file = $item->thumb_path(); - break; - - case "resize": - $file = $item->resize_path(); - break; + // Note: this code is roughly duplicated in file_proxy, so if you modify this, please look to + // see if you should make the same change there as well. - case "full": + if ($p->size == "full") { $file = $item->file_path(); - break; + } else if ($p->size == "resize") { + $file = $item->resize_path(); + } else { + $file = $item->thumb_path(); } if (!file_exists($file)) { throw new Kohana_404_Exception(); } - // Note: this code is roughly duplicated in data_rest, so if you modify this, please look to - // see if you should make the same change there as well. - // // We don't have a cache buster in the url, so don't set cache headers here. // We don't need to save the session for this request Session::instance()->abort_save(); -- cgit v1.2.3 From 1927dd00e42062a98855d121ec8427b25d74d015 Mon Sep 17 00:00:00 2001 From: shadlaws Date: Sun, 20 Jan 2013 08:34:12 +0100 Subject: #1949 - Fix album thumb mime types given by data_rest and file_proxy Correct result: always "image/jpeg" Old data_rest result: mime of cover item Old file_proxy result: mime of album item (null) --- modules/gallery/controllers/file_proxy.php | 4 ++-- modules/gallery/helpers/data_rest.php | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/controllers/file_proxy.php b/modules/gallery/controllers/file_proxy.php index 9af58c0c..b2120455 100644 --- a/modules/gallery/controllers/file_proxy.php +++ b/modules/gallery/controllers/file_proxy.php @@ -126,8 +126,8 @@ class File_Proxy_Controller extends Controller { expires::set(2592000, $item->updated); // 30 days - // Dump out the image. If the item is a movie, then its thumbnail will be a JPG. - if ($item->is_movie() && $type != "albums") { + // Dump out the image. If the item is a movie or album, then its thumbnail will be a JPG. + if (($item->is_movie() || $item->is_album()) && $type == "thumbs") { header("Content-Type: image/jpeg"); } else { header("Content-Type: $item->mime_type"); diff --git a/modules/gallery/helpers/data_rest.php b/modules/gallery/helpers/data_rest.php index abd4638e..ef4f17e7 100644 --- a/modules/gallery/helpers/data_rest.php +++ b/modules/gallery/helpers/data_rest.php @@ -57,13 +57,11 @@ class data_rest_Core { return; } - // Dump out the image. If the item is a movie, then its thumbnail will be a JPG. - if ($item->is_movie() && $p->size == "thumb") { + // Dump out the image. If the item is a movie or album, then its thumbnail will be a JPG. + if (($item->is_movie() || $item->is_album()) && $p->size == "thumb") { header("Content-Type: image/jpeg"); - } else if ($item->is_album()) { - header("Content-Type: " . $item->album_cover()->mime_type); } else { - header("Content-Type: {$item->mime_type}"); + header("Content-Type: $item->mime_type"); } Kohana::close_buffers(false); -- cgit v1.2.3 From 8dc34dade882768feb8100d7041d94c7d446b818 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Mon, 21 Jan 2013 00:51:31 -0500 Subject: Add unit tests for data_rest. While I'm in there, get rid of the clause that returns nothing when the album has no album cover - we'll fail before that if the album's thumbnail is missing, and if it's not missing then we'll have something to serve even if it's out of date. --- modules/gallery/helpers/data_rest.php | 12 ++- modules/gallery/tests/Data_Rest_Helper_Test.php | 102 ++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 7 deletions(-) create mode 100644 modules/gallery/tests/Data_Rest_Helper_Test.php (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/helpers/data_rest.php b/modules/gallery/helpers/data_rest.php index ef4f17e7..ad369037 100644 --- a/modules/gallery/helpers/data_rest.php +++ b/modules/gallery/helpers/data_rest.php @@ -51,12 +51,6 @@ class data_rest_Core { // We don't need to save the session for this request Session::instance()->abort_save(); - if ($item->is_album() && !$item->album_cover_item_id) { - // No thumbnail. Return nothing. - // @todo: what should we do here? - return; - } - // Dump out the image. If the item is a movie or album, then its thumbnail will be a JPG. if (($item->is_movie() || $item->is_album()) && $p->size == "thumb") { header("Content-Type: image/jpeg"); @@ -68,7 +62,11 @@ class data_rest_Core { if (isset($p->encoding) && $p->encoding == "base64") { print base64_encode(file_get_contents($file)); } else { - readfile($file); + if (TEST_MODE) { + return $file; + } else { + readfile($file); + } } // We must exit here to keep the regular REST framework reply code from adding more bytes on diff --git a/modules/gallery/tests/Data_Rest_Helper_Test.php b/modules/gallery/tests/Data_Rest_Helper_Test.php new file mode 100644 index 00000000..feec6d32 --- /dev/null +++ b/modules/gallery/tests/Data_Rest_Helper_Test.php @@ -0,0 +1,102 @@ +assert_equal($photo->id, $resolved->id); + } + + public function resolve_needs_permission_test() { + $album = test::random_album(); + $photo = test::random_photo($album); + $album->reload(); // new photo changed the album in the db + + access::deny(identity::everybody(), "view", $album); + identity::set_active_user(identity::guest()); + + try { + data_rest::resolve($photo->id); + $this->assert_true(false); + } catch (Kohana_404_Exception $e) { + // pass + } + } + + public function basic_get_test() { + $photo = test::random_photo(); + + $request = new stdClass(); + $request->url = rest::url("data", $photo, "thumb"); + $request->params = new stdClass(); + + $request->params->size = "thumb"; + $this->assert_same($photo->thumb_path(), data_rest::get($request)); + + $request->params->size = "resize"; + $this->assert_same($photo->resize_path(), data_rest::get($request)); + + $request->params->size = "full"; + $this->assert_same($photo->file_path(), data_rest::get($request)); + } + + public function illegal_access_test() { + $album = test::random_album(); + $photo = test::random_photo($album); + $album->reload(); + + access::deny(identity::everybody(), "view", $album); + identity::set_active_user(identity::guest()); + + $request = new stdClass(); + $request->url = rest::url("data", $photo, "thumb"); + $request->params = new stdClass(); + $request->params->size = "thumb"; + + try { + data_rest::get($request); + $this->assert_true(false); + } catch (Kohana_404_Exception $e) { + // pass + } + } + + public function missing_file_test() { + $photo = test::random_photo(); + + $request = new stdClass(); + $request->url = rest::url("data", $photo, "thumb"); + $request->params = new stdClass(); + $request->params->size = "thumb"; + + unlink($photo->thumb_path()); // oops! + + try { + data_rest::get($request); + $this->assert_true(false); + } catch (Kohana_404_Exception $e) { + // pass + } + } +} -- cgit v1.2.3 From d45a73777935c86fc5131955831833d7465b5e9d Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Mon, 21 Jan 2013 01:22:01 -0500 Subject: Update copyright to 2013. Fixes #1953. --- application/Bootstrap.php | 2 +- application/config/config.php | 2 +- index.php | 2 +- installer/cli.php | 2 +- installer/index.php | 2 +- installer/installer.php | 2 +- installer/web.php | 2 +- modules/akismet/controllers/admin_akismet.php | 2 +- modules/akismet/helpers/akismet.php | 2 +- modules/akismet/helpers/akismet_event.php | 2 +- modules/akismet/helpers/akismet_installer.php | 2 +- modules/akismet/tests/Akismet_Helper_Test.php | 2 +- modules/comment/controllers/admin_comments.php | 2 +- modules/comment/controllers/admin_manage_comments.php | 2 +- modules/comment/controllers/comments.php | 2 +- modules/comment/helpers/comment.php | 2 +- modules/comment/helpers/comment_block.php | 2 +- modules/comment/helpers/comment_event.php | 2 +- modules/comment/helpers/comment_installer.php | 2 +- modules/comment/helpers/comment_rest.php | 2 +- modules/comment/helpers/comment_rss.php | 2 +- modules/comment/helpers/comment_theme.php | 2 +- modules/comment/helpers/comments_rest.php | 2 +- modules/comment/helpers/item_comments_rest.php | 2 +- modules/comment/models/comment.php | 2 +- modules/comment/tests/Comment_Event_Test.php | 2 +- modules/comment/tests/Comment_Helper_Test.php | 2 +- modules/comment/tests/Comment_Model_Test.php | 2 +- modules/digibug/config/digibug.php | 2 +- modules/digibug/controllers/admin_digibug.php | 2 +- modules/digibug/controllers/digibug.php | 2 +- modules/digibug/helpers/digibug_event.php | 2 +- modules/digibug/helpers/digibug_installer.php | 2 +- modules/digibug/helpers/digibug_theme.php | 2 +- modules/digibug/models/digibug_proxy.php | 2 +- modules/digibug/tests/Digibug_Controller_Test.php | 2 +- modules/exif/controllers/exif.php | 2 +- modules/exif/helpers/exif.php | 2 +- modules/exif/helpers/exif_event.php | 2 +- modules/exif/helpers/exif_installer.php | 2 +- modules/exif/helpers/exif_task.php | 2 +- modules/exif/helpers/exif_theme.php | 2 +- modules/exif/models/exif_key.php | 2 +- modules/exif/models/exif_record.php | 2 +- modules/exif/tests/Exif_Test.php | 2 +- modules/g2_import/controllers/admin_g2_import.php | 2 +- modules/g2_import/controllers/g2.php | 2 +- modules/g2_import/helpers/g2_import.php | 2 +- modules/g2_import/helpers/g2_import_event.php | 2 +- modules/g2_import/helpers/g2_import_installer.php | 2 +- modules/g2_import/helpers/g2_import_task.php | 2 +- modules/g2_import/libraries/G2_Import_Exception.php | 2 +- modules/g2_import/models/g2_map.php | 2 +- modules/gallery/config/cache.php | 2 +- modules/gallery/config/cookie.php | 2 +- modules/gallery/config/database.php | 2 +- modules/gallery/config/locale.php | 2 +- modules/gallery/config/log_file.php | 2 +- modules/gallery/config/routes.php | 2 +- modules/gallery/config/session.php | 2 +- modules/gallery/config/upload.php | 2 +- modules/gallery/config/user_agents.php | 2 +- modules/gallery/controllers/admin.php | 2 +- modules/gallery/controllers/admin_advanced_settings.php | 2 +- modules/gallery/controllers/admin_dashboard.php | 2 +- modules/gallery/controllers/admin_graphics.php | 2 +- modules/gallery/controllers/admin_languages.php | 2 +- modules/gallery/controllers/admin_maintenance.php | 2 +- modules/gallery/controllers/admin_modules.php | 2 +- modules/gallery/controllers/admin_sidebar.php | 2 +- modules/gallery/controllers/admin_theme_options.php | 2 +- modules/gallery/controllers/admin_themes.php | 2 +- modules/gallery/controllers/admin_upgrade_checker.php | 2 +- modules/gallery/controllers/albums.php | 2 +- modules/gallery/controllers/combined.php | 2 +- modules/gallery/controllers/file_proxy.php | 2 +- modules/gallery/controllers/items.php | 2 +- modules/gallery/controllers/l10n_client.php | 2 +- modules/gallery/controllers/login.php | 2 +- modules/gallery/controllers/logout.php | 2 +- modules/gallery/controllers/movies.php | 2 +- modules/gallery/controllers/packager.php | 2 +- modules/gallery/controllers/permissions.php | 2 +- modules/gallery/controllers/photos.php | 2 +- modules/gallery/controllers/quick.php | 2 +- modules/gallery/controllers/reauthenticate.php | 2 +- modules/gallery/controllers/upgrader.php | 2 +- modules/gallery/controllers/uploader.php | 2 +- modules/gallery/controllers/user_profile.php | 2 +- modules/gallery/controllers/welcome_message.php | 2 +- modules/gallery/helpers/MY_html.php | 2 +- modules/gallery/helpers/MY_num.php | 2 +- modules/gallery/helpers/MY_remote.php | 2 +- modules/gallery/helpers/MY_url.php | 2 +- modules/gallery/helpers/MY_valid.php | 2 +- modules/gallery/helpers/access.php | 2 +- modules/gallery/helpers/ajax.php | 2 +- modules/gallery/helpers/album.php | 2 +- modules/gallery/helpers/auth.php | 2 +- modules/gallery/helpers/batch.php | 2 +- modules/gallery/helpers/block_manager.php | 2 +- modules/gallery/helpers/data_rest.php | 2 +- modules/gallery/helpers/dir.php | 2 +- modules/gallery/helpers/encoding.php | 2 +- modules/gallery/helpers/gallery.php | 2 +- modules/gallery/helpers/gallery_block.php | 2 +- modules/gallery/helpers/gallery_error.php | 2 +- modules/gallery/helpers/gallery_event.php | 2 +- modules/gallery/helpers/gallery_graphics.php | 2 +- modules/gallery/helpers/gallery_installer.php | 2 +- modules/gallery/helpers/gallery_rss.php | 2 +- modules/gallery/helpers/gallery_task.php | 2 +- modules/gallery/helpers/gallery_theme.php | 2 +- modules/gallery/helpers/graphics.php | 2 +- modules/gallery/helpers/identity.php | 2 +- modules/gallery/helpers/item.php | 2 +- modules/gallery/helpers/item_rest.php | 2 +- modules/gallery/helpers/items_rest.php | 2 +- modules/gallery/helpers/json.php | 2 +- modules/gallery/helpers/l10n_client.php | 2 +- modules/gallery/helpers/l10n_scanner.php | 2 +- modules/gallery/helpers/legal_file.php | 2 +- modules/gallery/helpers/locales.php | 2 +- modules/gallery/helpers/log.php | 2 +- modules/gallery/helpers/message.php | 2 +- modules/gallery/helpers/model_cache.php | 2 +- modules/gallery/helpers/module.php | 2 +- modules/gallery/helpers/movie.php | 2 +- modules/gallery/helpers/photo.php | 2 +- modules/gallery/helpers/random.php | 2 +- modules/gallery/helpers/site_status.php | 2 +- modules/gallery/helpers/system.php | 2 +- modules/gallery/helpers/task.php | 2 +- modules/gallery/helpers/theme.php | 2 +- modules/gallery/helpers/tree_rest.php | 2 +- modules/gallery/helpers/upgrade_checker.php | 2 +- modules/gallery/helpers/user_profile.php | 2 +- modules/gallery/helpers/xml.php | 2 +- modules/gallery/hooks/init_gallery.php | 2 +- modules/gallery/libraries/Admin_View.php | 2 +- modules/gallery/libraries/Block.php | 2 +- modules/gallery/libraries/Breadcrumb.php | 2 +- modules/gallery/libraries/Form_Script.php | 2 +- modules/gallery/libraries/Form_Uploadify.php | 2 +- modules/gallery/libraries/Form_Uploadify_buttons.php | 2 +- modules/gallery/libraries/Gallery_I18n.php | 2 +- modules/gallery/libraries/Gallery_View.php | 2 +- modules/gallery/libraries/IdentityProvider.php | 2 +- modules/gallery/libraries/InPlaceEdit.php | 2 +- modules/gallery/libraries/MY_Database.php | 2 +- modules/gallery/libraries/MY_Forge.php | 2 +- modules/gallery/libraries/MY_Input.php | 2 +- modules/gallery/libraries/MY_Kohana_Exception.php | 2 +- modules/gallery/libraries/MY_ORM.php | 2 +- modules/gallery/libraries/MY_View.php | 2 +- modules/gallery/libraries/Menu.php | 2 +- modules/gallery/libraries/ORM_MPTT.php | 2 +- modules/gallery/libraries/SafeString.php | 2 +- modules/gallery/libraries/Sendmail.php | 2 +- modules/gallery/libraries/Task_Definition.php | 2 +- modules/gallery/libraries/Theme_View.php | 2 +- modules/gallery/libraries/drivers/Cache/Database.php | 2 +- modules/gallery/libraries/drivers/IdentityProvider.php | 2 +- modules/gallery/models/access_cache.php | 2 +- modules/gallery/models/access_intent.php | 2 +- modules/gallery/models/cache.php | 2 +- modules/gallery/models/failed_auth.php | 2 +- modules/gallery/models/graphics_rule.php | 2 +- modules/gallery/models/incoming_translation.php | 2 +- modules/gallery/models/item.php | 2 +- modules/gallery/models/log.php | 2 +- modules/gallery/models/message.php | 2 +- modules/gallery/models/module.php | 2 +- modules/gallery/models/outgoing_translation.php | 2 +- modules/gallery/models/permission.php | 2 +- modules/gallery/models/task.php | 2 +- modules/gallery/models/theme.php | 2 +- modules/gallery/models/var.php | 2 +- modules/gallery/tests/Access_Helper_Test.php | 2 +- modules/gallery/tests/Albums_Controller_Test.php | 2 +- modules/gallery/tests/Breadcrumb_Test.php | 2 +- modules/gallery/tests/Cache_Test.php | 2 +- modules/gallery/tests/Controller_Auth_Test.php | 2 +- modules/gallery/tests/Data_Rest_Helper_Test.php | 2 +- modules/gallery/tests/Database_Test.php | 2 +- modules/gallery/tests/Dir_Helper_Test.php | 2 +- modules/gallery/tests/DrawForm_Test.php | 2 +- modules/gallery/tests/File_Proxy_Controller_Test.php | 2 +- modules/gallery/tests/File_Structure_Test.php | 4 ++-- modules/gallery/tests/Gallery_Filters.php | 2 +- modules/gallery/tests/Gallery_I18n_Test.php | 2 +- modules/gallery/tests/Gallery_Installer_Test.php | 2 +- modules/gallery/tests/Html_Helper_Test.php | 2 +- modules/gallery/tests/Input_Library_Test.php | 2 +- modules/gallery/tests/Item_Helper_Test.php | 2 +- modules/gallery/tests/Item_Model_Test.php | 2 +- modules/gallery/tests/Item_Rest_Helper_Test.php | 2 +- modules/gallery/tests/Items_Rest_Helper_Test.php | 2 +- modules/gallery/tests/Kohana_Exception_Test.php | 2 +- modules/gallery/tests/Legal_File_Helper_Test.php | 2 +- modules/gallery/tests/Locales_Helper_Test.php | 2 +- modules/gallery/tests/Menu_Test.php | 2 +- modules/gallery/tests/Movie_Helper_Test.php | 2 +- modules/gallery/tests/Num_Helper_Test.php | 2 +- modules/gallery/tests/ORM_MPTT_Test.php | 2 +- modules/gallery/tests/Photos_Controller_Test.php | 2 +- modules/gallery/tests/SafeString_Test.php | 2 +- modules/gallery/tests/Sendmail_Test.php | 2 +- modules/gallery/tests/System_Helper_Test.php | 2 +- modules/gallery/tests/Url_Security_Test.php | 2 +- modules/gallery/tests/Valid_Test.php | 2 +- modules/gallery/tests/Var_Test.php | 2 +- modules/gallery/tests/Xss_Security_Test.php | 2 +- modules/gallery_unit_test/controllers/gallery_unit_test.php | 2 +- modules/gallery_unit_test/helpers/MY_request.php | 2 +- modules/gallery_unit_test/helpers/test.php | 2 +- modules/gallery_unit_test/libraries/Gallery_Unit_Test_Case.php | 2 +- modules/image_block/controllers/image_block.php | 2 +- modules/image_block/helpers/image_block_block.php | 2 +- modules/image_block/helpers/image_block_installer.php | 2 +- modules/info/helpers/info_block.php | 2 +- modules/info/helpers/info_installer.php | 2 +- modules/info/helpers/info_theme.php | 2 +- modules/kohana23_compat/config/pagination.php | 2 +- modules/kohana23_compat/libraries/MY_Database_Builder.php | 2 +- modules/kohana23_compat/libraries/Pagination.php | 2 +- modules/notification/controllers/notification.php | 2 +- modules/notification/helpers/notification.php | 2 +- modules/notification/helpers/notification_event.php | 2 +- modules/notification/helpers/notification_installer.php | 2 +- modules/notification/models/pending_notification.php | 2 +- modules/notification/models/subscription.php | 2 +- modules/organize/controllers/organize.php | 2 +- modules/organize/helpers/organize_event.php | 2 +- modules/organize/helpers/organize_installer.php | 2 +- modules/recaptcha/controllers/admin_recaptcha.php | 2 +- modules/recaptcha/helpers/recaptcha.php | 2 +- modules/recaptcha/helpers/recaptcha_event.php | 2 +- modules/recaptcha/helpers/recaptcha_installer.php | 2 +- modules/recaptcha/helpers/recaptcha_theme.php | 2 +- modules/recaptcha/libraries/Form_Recaptcha.php | 2 +- modules/rest/controllers/rest.php | 2 +- modules/rest/helpers/registry_rest.php | 2 +- modules/rest/helpers/rest.php | 2 +- modules/rest/helpers/rest_event.php | 2 +- modules/rest/helpers/rest_installer.php | 2 +- modules/rest/libraries/Rest_Exception.php | 2 +- modules/rest/models/user_access_key.php | 2 +- modules/rest/tests/Rest_Controller_Test.php | 2 +- modules/rss/controllers/rss.php | 2 +- modules/rss/helpers/rss.php | 2 +- modules/rss/helpers/rss_block.php | 2 +- modules/search/controllers/search.php | 2 +- modules/search/helpers/search.php | 2 +- modules/search/helpers/search_event.php | 2 +- modules/search/helpers/search_installer.php | 2 +- modules/search/helpers/search_task.php | 2 +- modules/search/helpers/search_theme.php | 2 +- modules/search/models/search_record.php | 2 +- modules/server_add/controllers/admin_server_add.php | 2 +- modules/server_add/controllers/server_add.php | 2 +- modules/server_add/helpers/server_add.php | 2 +- modules/server_add/helpers/server_add_event.php | 2 +- modules/server_add/helpers/server_add_installer.php | 2 +- modules/server_add/helpers/server_add_theme.php | 2 +- modules/server_add/models/server_add_entry.php | 2 +- modules/slideshow/helpers/slideshow_event.php | 2 +- modules/slideshow/helpers/slideshow_installer.php | 2 +- modules/slideshow/helpers/slideshow_theme.php | 2 +- modules/tag/controllers/admin_tags.php | 2 +- modules/tag/controllers/tag.php | 2 +- modules/tag/controllers/tag_name.php | 2 +- modules/tag/controllers/tags.php | 2 +- modules/tag/helpers/item_tags_rest.php | 2 +- modules/tag/helpers/tag.php | 2 +- modules/tag/helpers/tag_block.php | 2 +- modules/tag/helpers/tag_event.php | 2 +- modules/tag/helpers/tag_installer.php | 2 +- modules/tag/helpers/tag_item_rest.php | 2 +- modules/tag/helpers/tag_items_rest.php | 2 +- modules/tag/helpers/tag_rest.php | 2 +- modules/tag/helpers/tag_rss.php | 2 +- modules/tag/helpers/tag_task.php | 2 +- modules/tag/helpers/tag_theme.php | 2 +- modules/tag/helpers/tags_rest.php | 2 +- modules/tag/models/tag.php | 2 +- modules/tag/tests/Tag_Item_Rest_Helper_Test.php | 2 +- modules/tag/tests/Tag_Rest_Helper_Test.php | 2 +- modules/tag/tests/Tag_Test.php | 2 +- modules/tag/tests/Tags_Rest_Helper_Test.php | 2 +- modules/user/config/identity.php | 2 +- modules/user/controllers/admin_users.php | 2 +- modules/user/controllers/password.php | 2 +- modules/user/controllers/users.php | 2 +- modules/user/helpers/group.php | 2 +- modules/user/helpers/user.php | 2 +- modules/user/helpers/user_event.php | 2 +- modules/user/helpers/user_installer.php | 2 +- modules/user/helpers/user_theme.php | 2 +- modules/user/libraries/drivers/IdentityProvider/Gallery.php | 2 +- modules/user/models/group.php | 2 +- modules/user/models/user.php | 2 +- modules/user/tests/No_Direct_ORM_Access_Test.php | 2 +- modules/user/tests/User_Groups_Test.php | 2 +- modules/user/tests/User_Installer_Test.php | 2 +- modules/watermark/controllers/admin_watermarks.php | 2 +- modules/watermark/helpers/watermark.php | 2 +- modules/watermark/helpers/watermark_event.php | 2 +- modules/watermark/helpers/watermark_installer.php | 2 +- 309 files changed, 310 insertions(+), 310 deletions(-) (limited to 'modules/gallery/helpers') diff --git a/application/Bootstrap.php b/application/Bootstrap.php index 183705d9..93353b47 100644 --- a/application/Bootstrap.php +++ b/application/Bootstrap.php @@ -1,7 +1,7 @@ Date: Mon, 21 Jan 2013 10:45:34 +0100 Subject: #1954 - Skip buffer calls for unit tests of file_proxy and data_rest. Moved the "if (TEST_MODE)" statement before the buffer calls in file_proxy and data_rest. This has no impact on normal use, but will make the unit tests more compatible with different server/PHP configurations. Note: We do not have to skip setting the headers, which means we can build unit tests around them if we wish. --- modules/gallery/controllers/file_proxy.php | 25 ++++++++++++------------- modules/gallery/helpers/data_rest.php | 11 ++++++----- 2 files changed, 18 insertions(+), 18 deletions(-) (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/controllers/file_proxy.php b/modules/gallery/controllers/file_proxy.php index f29cdf98..a9e98ce1 100644 --- a/modules/gallery/controllers/file_proxy.php +++ b/modules/gallery/controllers/file_proxy.php @@ -147,22 +147,21 @@ class File_Proxy_Controller extends Controller { header("Content-Type: $item->mime_type"); } - // Don't use Kohana::close_buffers(false) here because that only closes all the buffers - // that Kohana started. We want to close *all* buffers at this point because otherwise we're - // going to buffer up whatever file we're proxying (and it may be very large). This may - // affect embedding or systems with PHP's output_buffering enabled. - while (ob_get_level()) { - Kohana_Log::add("error","".print_r(ob_get_level(),1)); - if (!@ob_end_clean()) { - // ob_end_clean() can return false if the buffer can't be removed for some reason - // (zlib output compression buffers sometimes cause problems). - break; - } - } - if (TEST_MODE) { return $file; } else { + // Don't use Kohana::close_buffers(false) here because that only closes all the buffers + // that Kohana started. We want to close *all* buffers at this point because otherwise we're + // going to buffer up whatever file we're proxying (and it may be very large). This may + // affect embedding or systems with PHP's output_buffering enabled. + while (ob_get_level()) { + Kohana_Log::add("error","".print_r(ob_get_level(),1)); + if (!@ob_end_clean()) { + // ob_end_clean() can return false if the buffer can't be removed for some reason + // (zlib output compression buffers sometimes cause problems). + break; + } + } readfile($file); } } diff --git a/modules/gallery/helpers/data_rest.php b/modules/gallery/helpers/data_rest.php index 8a3a159d..dc213510 100644 --- a/modules/gallery/helpers/data_rest.php +++ b/modules/gallery/helpers/data_rest.php @@ -57,13 +57,14 @@ class data_rest_Core { } else { header("Content-Type: $item->mime_type"); } - Kohana::close_buffers(false); - if (isset($p->encoding) && $p->encoding == "base64") { - print base64_encode(file_get_contents($file)); + if (TEST_MODE) { + return $file; } else { - if (TEST_MODE) { - return $file; + Kohana::close_buffers(false); + + if (isset($p->encoding) && $p->encoding == "base64") { + print base64_encode(file_get_contents($file)); } else { readfile($file); } -- cgit v1.2.3 From 49f6ce2d94194220213edb108e4ac372acd2eabb Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Tue, 22 Jan 2013 18:39:24 -0500 Subject: gallery.menalto.com -> galleryproject.org codex.gallery2.org -> codex.galleryproject.org Fixes #1957. --- README | 12 ++++++------ index.php | 2 +- installer/cli.php | 4 ++-- installer/views/install.html.php | 4 ++-- modules/akismet/module.info | 6 +++--- modules/comment/module.info | 6 +++--- modules/digibug/module.info | 6 +++--- modules/exif/module.info | 6 +++--- modules/g2_import/module.info | 6 +++--- modules/gallery/helpers/gallery_block.php | 2 +- modules/gallery/helpers/gallery_theme.php | 2 +- modules/gallery/helpers/graphics.php | 2 +- modules/gallery/helpers/l10n_client.php | 2 +- modules/gallery/helpers/upgrade_checker.php | 2 +- modules/gallery/module.info | 6 +++--- modules/gallery/views/admin_graphics.html.php | 2 +- modules/gallery/views/admin_languages.html.php | 2 +- modules/gallery/views/admin_modules.html.php | 2 +- modules/gallery/views/admin_themes.html.php | 6 +++--- modules/gallery/views/error_admin.html.php | 2 +- modules/gallery/views/form_uploadify.html.php | 4 ++-- modules/gallery/views/upgrader.html.php | 4 ++-- modules/gallery/views/welcome_message.html.php | 4 ++-- modules/image_block/module.info | 6 +++--- modules/info/module.info | 6 +++--- modules/notification/module.info | 6 +++--- modules/organize/module.info | 6 +++--- modules/recaptcha/module.info | 6 +++--- modules/rest/module.info | 6 +++--- modules/rss/module.info | 6 +++--- modules/search/module.info | 6 +++--- modules/server_add/module.info | 6 +++--- modules/slideshow/module.info | 6 +++--- modules/tag/module.info | 6 +++--- modules/user/module.info | 6 +++--- modules/watermark/module.info | 6 +++--- themes/admin_wind/theme.info | 6 +++--- themes/wind/theme.info | 6 +++--- 38 files changed, 92 insertions(+), 92 deletions(-) (limited to 'modules/gallery/helpers') diff --git a/README b/README index 11256921..b50186d2 100644 --- a/README +++ b/README @@ -15,14 +15,14 @@ welcome theme and module developers to play with this release and start turning out slick new designs for our happy users. If you have questions or problems, you can get help in the Gallery forums: - http://gallery.menalto.com/forum/96 + http://galleryproject.org/forum/96 SECURITY: We've contracted a professional security audit, received their results and resolved all the issues they found. -Did you find a security flaw? Please email security@gallery.menalto.com +Did you find a security flaw? Please email security@galleryproject.org with the details and we'll fix it ASAP! @@ -34,12 +34,12 @@ SUPPORTED CONFIGURATION: - Database: MySQL 5 and newer. For complete system requirements, please refer to: - http://codex.gallery2.org/Gallery3:Requirements + http://codex.galleryproject.org/Gallery3:Requirements INSTALLING AND UPGRADING INSTRUCTIONS: For comprehensive instructions, The online User Guide is your best resource: - http://codex.gallery2.org/Gallery3:User_guide + http://codex.galleryproject.org/Gallery3:User_guide There are also simple instructions below. NOTE: You can upgrade from beta 1 and beyond, but not from alpha releases. @@ -68,7 +68,7 @@ and log in with your SourceForge username and password, then click the QUESTIONS, PROBLEMS: - - Check out the gallery3 FAQ http://codex.gallery2.org/Gallery3:FAQ - - Post to the Gallery 3 forums: http://gallery.menalto.com/forum/96 + - Check out the gallery3 FAQ http://codex.galleryproject.org/Gallery3:FAQ + - Post to the Gallery 3 forums: http://galleryproject.org/forum/96 - Email gallery-devel@lists.sourceforge.net diff --git a/index.php b/index.php index 763cfbbd..9ff8e3b4 100644 --- a/index.php +++ b/index.php @@ -39,7 +39,7 @@ if (!ini_get("date.timezone")) { !ini_get("short_open_tag") and exit("Gallery requires short_open_tag to be on."); // Suppress errors. For information on how to debug Gallery 3, see: -// http://codex.gallery2.org/Gallery3:FAQ#How_do_I_see_debug_information.3F +// http://codex.galleryproject.org/Gallery3:FAQ#How_do_I_see_debug_information.3F error_reporting(0); // Disabling display_errors will effectively disable Kohana error display diff --git a/installer/cli.php b/installer/cli.php index 93699ba9..f5a9e260 100644 --- a/installer/cli.php +++ b/installer/cli.php @@ -78,8 +78,8 @@ function oops($message) { print "==> " . $message; print "\n"; print "For help you can try:\n"; - print " * The Gallery 3 FAQ - http://codex.gallery2.org/Gallery3:FAQ\n"; - print " * The Gallery Forums - http://gallery.menalto.com/forum\n"; + print " * The Gallery 3 FAQ - http://codex.galleryproject.org/Gallery3:FAQ\n"; + print " * The Gallery Forums - http://galleryproject.org/forum\n"; print "\n\n** INSTALLATION FAILED **\n"; exit(1); } diff --git a/installer/views/install.html.php b/installer/views/install.html.php index a0eddaf3..7a30561a 100644 --- a/installer/views/install.html.php +++ b/installer/views/install.html.php @@ -13,9 +13,9 @@ diff --git a/modules/akismet/module.info b/modules/akismet/module.info index 63473468..263b7b82 100644 --- a/modules/akismet/module.info +++ b/modules/akismet/module.info @@ -2,6 +2,6 @@ name = "Akismet" description = "Filter comments through the Akismet web service to detect and eliminate spam (http://akismet.com). You'll need a WordPress.com API key to use it." version = 1 author_name = "Gallery Team" -author_url = "http://codex.gallery2.org/Gallery:Team" -info_url = "http://codex.gallery2.org/Gallery3:Modules:akismet" -discuss_url = "http://gallery.menalto.com/forum_module_akismet" +author_url = "http://codex.galleryproject.org/Gallery:Team" +info_url = "http://codex.galleryproject.org/Gallery3:Modules:akismet" +discuss_url = "http://galleryproject.org/forum_module_akismet" diff --git a/modules/comment/module.info b/modules/comment/module.info index 97e8a73b..b69379fa 100644 --- a/modules/comment/module.info +++ b/modules/comment/module.info @@ -2,6 +2,6 @@ name = "Comments" description = "Allows users and guests to leave comments on photos and albums." version = 7 author_name = "Gallery Team" -author_url = "http://codex.gallery2.org/Gallery:Team" -info_url = "http://codex.gallery2.org/Gallery3:Modules:comment" -discuss_url = "http://gallery.menalto.com/forum_module_comment" +author_url = "http://codex.galleryproject.org/Gallery:Team" +info_url = "http://codex.galleryproject.org/Gallery3:Modules:comment" +discuss_url = "http://galleryproject.org/forum_module_comment" diff --git a/modules/digibug/module.info b/modules/digibug/module.info index 781d5f01..5e5ca10f 100644 --- a/modules/digibug/module.info +++ b/modules/digibug/module.info @@ -2,6 +2,6 @@ name = "Digibug" description = "Digibug Photo Printing Module" version = 2 author_name = "Gallery Team" -author_url = "http://codex.gallery2.org/Gallery:Team" -info_url = "http://codex.gallery2.org/Gallery3:Modules:digibug" -discuss_url = "http://gallery.menalto.com/forum_module_digibug" +author_url = "http://codex.galleryproject.org/Gallery:Team" +info_url = "http://codex.galleryproject.org/Gallery3:Modules:digibug" +discuss_url = "http://galleryproject.org/forum_module_digibug" diff --git a/modules/exif/module.info b/modules/exif/module.info index e266e20e..9bbda957 100644 --- a/modules/exif/module.info +++ b/modules/exif/module.info @@ -2,6 +2,6 @@ name = "Exif Data" description = "Extract Exif data and display it on photo pages." version = 1 author_name = "Gallery Team" -author_url = "http://codex.gallery2.org/Gallery:Team" -info_url = "http://codex.gallery2.org/Gallery3:Modules:exif" -discuss_url = "http://gallery.menalto.com/forum_module_exif" +author_url = "http://codex.galleryproject.org/Gallery:Team" +info_url = "http://codex.galleryproject.org/Gallery3:Modules:exif" +discuss_url = "http://galleryproject.org/forum_module_exif" diff --git a/modules/g2_import/module.info b/modules/g2_import/module.info index 6b03d097..32af27d0 100644 --- a/modules/g2_import/module.info +++ b/modules/g2_import/module.info @@ -2,6 +2,6 @@ name = "Gallery 2 Import" description = "Import your Gallery 2 content into Gallery 3" version = 2 author_name = "Gallery Team" -author_url = "http://codex.gallery2.org/Gallery:Team" -info_url = "http://codex.gallery2.org/Gallery3:Modules:g2_import" -discuss_url = "http://gallery.menalto.com/forum_module_g2_import" +author_url = "http://codex.galleryproject.org/Gallery:Team" +info_url = "http://codex.galleryproject.org/Gallery3:Modules:g2_import" +discuss_url = "http://galleryproject.org/forum_module_g2_import" diff --git a/modules/gallery/helpers/gallery_block.php b/modules/gallery/helpers/gallery_block.php index 5ab811de..5ac4d74d 100644 --- a/modules/gallery/helpers/gallery_block.php +++ b/modules/gallery/helpers/gallery_block.php @@ -78,7 +78,7 @@ class gallery_block_Core { $block->css_id = "g-project-news"; $block->title = t("Gallery project news"); $block->content = new View("admin_block_news.html"); - $block->content->feed = feed::parse("http://gallery.menalto.com/node/feed", 3); + $block->content->feed = feed::parse("http://galleryproject.org/node/feed", 3); break; case "block_adder": diff --git a/modules/gallery/helpers/gallery_theme.php b/modules/gallery/helpers/gallery_theme.php index e592db53..f94b9ecd 100644 --- a/modules/gallery/helpers/gallery_theme.php +++ b/modules/gallery/helpers/gallery_theme.php @@ -134,7 +134,7 @@ class gallery_theme_Core { 'Gallery ' . gallery::version_string() . ''); return "
  • " . t(module::get_var("gallery", "credits"), - array("url" => "http://gallery.menalto.com", + array("url" => "http://galleryproject.org", "gallery_version" => $version_string)) . "
  • "; } diff --git a/modules/gallery/helpers/graphics.php b/modules/gallery/helpers/graphics.php index b8735120..51437d4b 100644 --- a/modules/gallery/helpers/graphics.php +++ b/modules/gallery/helpers/graphics.php @@ -124,7 +124,7 @@ class graphics_Core { // 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 + // http://galleryproject.org/node/96926 if ($item->album_cover_item_id) { $item->album_cover_item_id = null; $item->save(); diff --git a/modules/gallery/helpers/l10n_client.php b/modules/gallery/helpers/l10n_client.php index 5954865d..2a1be2f9 100644 --- a/modules/gallery/helpers/l10n_client.php +++ b/modules/gallery/helpers/l10n_client.php @@ -21,7 +21,7 @@ class l10n_client_Core { private static function _server_url($path) { - return "http://gallery.menalto.com/translations/$path"; + return "http://galleryproject.org/translations/$path"; } static function server_api_key_url() { diff --git a/modules/gallery/helpers/upgrade_checker.php b/modules/gallery/helpers/upgrade_checker.php index 5059e619..492f72e9 100644 --- a/modules/gallery/helpers/upgrade_checker.php +++ b/modules/gallery/helpers/upgrade_checker.php @@ -18,7 +18,7 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ class upgrade_checker_Core { - const CHECK_URL = "http://gallery.menalto.com/versioncheck/gallery3"; + const CHECK_URL = "http://galleryproject.org/versioncheck/gallery3"; const AUTO_CHECK_INTERVAL = 604800; // 7 days in seconds /** diff --git a/modules/gallery/module.info b/modules/gallery/module.info index 64cad0a7..566ca2eb 100644 --- a/modules/gallery/module.info +++ b/modules/gallery/module.info @@ -2,6 +2,6 @@ name = "Gallery 3" description = "Gallery core application" version = 53 author_name = "Gallery Team" -author_url = "http://codex.gallery2.org/Gallery:Team" -info_url = "http://codex.gallery2.org/Gallery3:Modules:gallery" -discuss_url = "http://gallery.menalto.com/forum_module_gallery" +author_url = "http://codex.galleryproject.org/Gallery:Team" +info_url = "http://codex.galleryproject.org/Gallery3:Modules:gallery" +discuss_url = "http://galleryproject.org/forum_module_gallery" diff --git a/modules/gallery/views/admin_graphics.html.php b/modules/gallery/views/admin_graphics.html.php index ae76f1e1..1f45bb14 100644 --- a/modules/gallery/views/admin_graphics.html.php +++ b/modules/gallery/views/admin_graphics.html.php @@ -16,7 +16,7 @@

    - We can help!", array("url" => "http://codex.gallery2.org/Gallery3:Choosing_A_Graphics_Toolkit")) ?> + We can help!", array("url" => "http://codex.galleryproject.org/Gallery3:Choosing_A_Graphics_Toolkit")) ?>

    diff --git a/modules/gallery/views/admin_languages.html.php b/modules/gallery/views/admin_languages.html.php index eef087e1..d6a9c225 100644 --- a/modules/gallery/views/admin_languages.html.php +++ b/modules/gallery/views/admin_languages.html.php @@ -80,7 +80,7 @@

    - for_html_attr() ?>"> diff --git a/modules/gallery/views/admin_modules.html.php b/modules/gallery/views/admin_modules.html.php index 03993bb2..5a7f7b6c 100644 --- a/modules/gallery/views/admin_modules.html.php +++ b/modules/gallery/views/admin_modules.html.php @@ -43,7 +43,7 @@

    - adding more modules! Each module provides new cool features.", array("url" => "http://codex.gallery2.org/Category:Gallery_3:Modules")) ?> + adding more modules! Each module provides new cool features.", array("url" => "http://codex.galleryproject.org/Category:Gallery_3:Modules")) ?>

    diff --git a/modules/gallery/views/admin_themes.html.php b/modules/gallery/views/admin_themes.html.php index 9d53779f..547f27d2 100644 --- a/modules/gallery/views/admin_themes.html.php +++ b/modules/gallery/views/admin_themes.html.php @@ -10,7 +10,7 @@

    - with a new theme! There are separate themes for the regular site and for the administration interface. Click a theme below to preview and activate it.", array("url" => "http://codex.gallery2.org/Category:Gallery_3:Themes")) ?> + with a new theme! There are separate themes for the regular site and for the administration interface. Click a theme below to preview and activate it.", array("url" => "http://codex.galleryproject.org/Category:Gallery_3:Themes")) ?>

    @@ -48,7 +48,7 @@

    - Download one now!", array("url" => "http://codex.gallery2.org/Category:Gallery_3:Themes")) ?> + Download one now!", array("url" => "http://codex.galleryproject.org/Category:Gallery_3:Themes")) ?>

    @@ -88,7 +88,7 @@

    - Download one now!", array("url" => "http://codex.gallery2.org/Category:Gallery_3:Themes")) ?> + Download one now!", array("url" => "http://codex.galleryproject.org/Category:Gallery_3:Themes")) ?>

    diff --git a/modules/gallery/views/error_admin.html.php b/modules/gallery/views/error_admin.html.php index 3e7c286e..cd1bd569 100644 --- a/modules/gallery/views/error_admin.html.php +++ b/modules/gallery/views/error_admin.html.php @@ -170,7 +170,7 @@

    There's an error message below and you can find more details in gallery3/var/logs (look for the file with the most recent - date on it). Stuck? Stop by the Gallery 3 + date on it). Stuck? Stop by the Gallery 3 Forums and ask for help. You can also look at our list of open tickets to see if the problem you're seeing has been diff --git a/modules/gallery/views/form_uploadify.html.php b/modules/gallery/views/form_uploadify.html.php index c1f985c3..4426514a 100644 --- a/modules/gallery/views/form_uploadify.html.php +++ b/modules/gallery/views/form_uploadify.html.php @@ -89,7 +89,7 @@ .replace("__INFO__", errorObj.info) .replace("__TYPE__", errorObj.type); } - msg = " - " + + msg = " - " + error_msg + ""; $("#g-add-photos-status ul").append( @@ -131,7 +131,7 @@ admin && !$movies_allowed): ?>

    - ffmpeg on your system. Movie uploading disabled. Help!", array("help_url" => "http://codex.gallery2.org/Gallery3:FAQ#Why_does_it_say_I.27m_missing_ffmpeg.3F")) ?> + ffmpeg on your system. Movie uploading disabled. Help!", array("help_url" => "http://codex.galleryproject.org/Gallery3:FAQ#Why_does_it_say_I.27m_missing_ffmpeg.3F")) ?>

    diff --git a/modules/gallery/views/upgrader.html.php b/modules/gallery/views/upgrader.html.php index 70d37dd1..edfaf720 100644 --- a/modules/gallery/views/upgrader.html.php +++ b/modules/gallery/views/upgrader.html.php @@ -144,8 +144,8 @@

    FAQ or ask in the Gallery forums.", - array("faq_url" => "http://codex.gallery2.org/Gallery3:FAQ", - "forums_url" => "http://gallery.menalto.com/forum")) ?> + array("faq_url" => "http://codex.galleryproject.org/Gallery3:FAQ", + "forums_url" => "http://galleryproject.org/forum")) ?>

    diff --git a/modules/gallery/views/welcome_message.html.php b/modules/gallery/views/welcome_message.html.php index 1fcca971..bb6b4a83 100644 --- a/modules/gallery/views/welcome_message.html.php +++ b/modules/gallery/views/welcome_message.html.php @@ -27,10 +27,10 @@

    - Gallery website has news and information about the Gallery project and community.", array("url" => "http://gallery.menalto.com")) ?> + Gallery website has news and information about the Gallery project and community.", array("url" => "http://galleryproject.org")) ?>

    - documentation site or you can ask for help in the forums!", array("codex_url" => "http://codex.gallery2.org/Main_Page", "forum_url" => "http://gallery.menalto.com/forum")) ?> + documentation site or you can ask for help in the forums!", array("codex_url" => "http://codex.galleryproject.org/Main_Page", "forum_url" => "http://galleryproject.org/forum")) ?>

    diff --git a/modules/image_block/module.info b/modules/image_block/module.info index 6722cc8f..25b89e6a 100644 --- a/modules/image_block/module.info +++ b/modules/image_block/module.info @@ -2,6 +2,6 @@ name = "Image Block" description = "Display a random image in the sidebar" version = 3 author_name = "Gallery Team" -author_url = "http://codex.gallery2.org/Gallery:Team" -info_url = "http://codex.gallery2.org/Gallery3:Modules:image_block" -discuss_url = "http://gallery.menalto.com/forum_module_image_block" +author_url = "http://codex.galleryproject.org/Gallery:Team" +info_url = "http://codex.galleryproject.org/Gallery3:Modules:image_block" +discuss_url = "http://galleryproject.org/forum_module_image_block" diff --git a/modules/info/module.info b/modules/info/module.info index f8964a78..0f35c922 100644 --- a/modules/info/module.info +++ b/modules/info/module.info @@ -2,6 +2,6 @@ name = "Info" description = "Display extra information about photos and albums" version = 2 author_name = "Gallery Team" -author_url = "http://codex.gallery2.org/Gallery:Team" -info_url = "http://codex.gallery2.org/Gallery3:Modules:info" -discuss_url = "http://gallery.menalto.com/forum_module_info" +author_url = "http://codex.galleryproject.org/Gallery:Team" +info_url = "http://codex.galleryproject.org/Gallery3:Modules:info" +discuss_url = "http://galleryproject.org/forum_module_info" diff --git a/modules/notification/module.info b/modules/notification/module.info index 84be8f99..0e2cdb65 100644 --- a/modules/notification/module.info +++ b/modules/notification/module.info @@ -2,6 +2,6 @@ name = "Notification" description = "Send notifications to users when changes are made to watched albums." version = 2 author_name = "Gallery Team" -author_url = "http://codex.gallery2.org/Gallery:Team" -info_url = "http://codex.gallery2.org/Gallery3:Modules:notification" -discuss_url = "http://gallery.menalto.com/forum_module_notification" +author_url = "http://codex.galleryproject.org/Gallery:Team" +info_url = "http://codex.galleryproject.org/Gallery3:Modules:notification" +discuss_url = "http://galleryproject.org/forum_module_notification" diff --git a/modules/organize/module.info b/modules/organize/module.info index 07b9dc38..4d4560b9 100644 --- a/modules/organize/module.info +++ b/modules/organize/module.info @@ -2,6 +2,6 @@ name = "Organize" description = "Visually rearrange and move photos in your gallery" version = 4 author_name = "Gallery Team" -author_url = "http://codex.gallery2.org/Gallery:Team" -info_url = "http://codex.gallery2.org/Gallery3:Modules:organize" -discuss_url = "http://gallery.menalto.com/forum_module_organize" +author_url = "http://codex.galleryproject.org/Gallery:Team" +info_url = "http://codex.galleryproject.org/Gallery3:Modules:organize" +discuss_url = "http://galleryproject.org/forum_module_organize" diff --git a/modules/recaptcha/module.info b/modules/recaptcha/module.info index ebaff7de..6806bb91 100644 --- a/modules/recaptcha/module.info +++ b/modules/recaptcha/module.info @@ -2,6 +2,6 @@ name = "reCAPTCHA" description = "reCAPTCHA displays a graphical verification that protects the input form from abuse from 'bots,' or automated programs usually written to generate spam (http://recaptcha.net)." version = 1 author_name = "Gallery Team" -author_url = "http://codex.gallery2.org/Gallery:Team" -info_url = "http://codex.gallery2.org/Gallery3:Modules:recaptcha" -discuss_url = "http://gallery.menalto.com/forum_module_recaptcha" +author_url = "http://codex.galleryproject.org/Gallery:Team" +info_url = "http://codex.galleryproject.org/Gallery3:Modules:recaptcha" +discuss_url = "http://galleryproject.org/forum_module_recaptcha" diff --git a/modules/rest/module.info b/modules/rest/module.info index 33c9f1cf..93a7873b 100644 --- a/modules/rest/module.info +++ b/modules/rest/module.info @@ -3,6 +3,6 @@ description = "A REST-based API that allows desktop clients and other apps to in version = 3 author_name = "Gallery Team" -author_url = "http://codex.gallery2.org/Gallery:Team" -info_url = "http://codex.gallery2.org/Gallery3:Modules:rest" -discuss_url = "http://gallery.menalto.com/forum_module_rest" +author_url = "http://codex.galleryproject.org/Gallery:Team" +info_url = "http://codex.galleryproject.org/Gallery3:Modules:rest" +discuss_url = "http://galleryproject.org/forum_module_rest" diff --git a/modules/rss/module.info b/modules/rss/module.info index cd13c1b0..5f32387e 100644 --- a/modules/rss/module.info +++ b/modules/rss/module.info @@ -2,6 +2,6 @@ name = "RSS" description = "Provides RSS feeds" version = 1 author_name = "Gallery Team" -author_url = "http://codex.gallery2.org/Gallery:Team" -info_url = "http://codex.gallery2.org/Gallery3:Modules:rss" -discuss_url = "http://gallery.menalto.com/forum_module_rss" +author_url = "http://codex.galleryproject.org/Gallery:Team" +info_url = "http://codex.galleryproject.org/Gallery3:Modules:rss" +discuss_url = "http://galleryproject.org/forum_module_rss" diff --git a/modules/search/module.info b/modules/search/module.info index 1389798d..f1bb1fab 100644 --- a/modules/search/module.info +++ b/modules/search/module.info @@ -2,6 +2,6 @@ name = "Search" description = "Allows users to search their Gallery" version = 1 author_name = "Gallery Team" -author_url = "http://codex.gallery2.org/Gallery:Team" -info_url = "http://codex.gallery2.org/Gallery3:Modules:search" -discuss_url = "http://gallery.menalto.com/forum_module_search" +author_url = "http://codex.galleryproject.org/Gallery:Team" +info_url = "http://codex.galleryproject.org/Gallery3:Modules:search" +discuss_url = "http://galleryproject.org/forum_module_search" diff --git a/modules/server_add/module.info b/modules/server_add/module.info index 4ce0a97d..dc455c71 100644 --- a/modules/server_add/module.info +++ b/modules/server_add/module.info @@ -2,6 +2,6 @@ name = "Server Add" description = "Allows authorized users to load images directly from your web server" version = 4 author_name = "Gallery Team" -author_url = "http://codex.gallery2.org/Gallery:Team" -info_url = "http://codex.gallery2.org/Gallery3:Modules:server_add" -discuss_url = "http://gallery.menalto.com/forum_module_server_add" +author_url = "http://codex.galleryproject.org/Gallery:Team" +info_url = "http://codex.galleryproject.org/Gallery3:Modules:server_add" +discuss_url = "http://galleryproject.org/forum_module_server_add" diff --git a/modules/slideshow/module.info b/modules/slideshow/module.info index 8c9a3176..2d71f710 100644 --- a/modules/slideshow/module.info +++ b/modules/slideshow/module.info @@ -2,6 +2,6 @@ name = "Slideshow" description = "Allows users to view a slideshow of photos" version = 2 author_name = "Gallery Team" -author_url = "http://codex.gallery2.org/Gallery:Team" -info_url = "http://codex.gallery2.org/Gallery3:Modules:slideshow" -discuss_url = "http://gallery.menalto.com/forum_module_slideshow" +author_url = "http://codex.galleryproject.org/Gallery:Team" +info_url = "http://codex.galleryproject.org/Gallery3:Modules:slideshow" +discuss_url = "http://galleryproject.org/forum_module_slideshow" diff --git a/modules/tag/module.info b/modules/tag/module.info index 75d16bf0..19fbdb45 100644 --- a/modules/tag/module.info +++ b/modules/tag/module.info @@ -2,6 +2,6 @@ name = "Tags" description = "Allows users to tag photos and albums" version = 3 author_name = "Gallery Team" -author_url = "http://codex.gallery2.org/Gallery:Team" -info_url = "http://codex.gallery2.org/Gallery3:Modules:tag" -discuss_url = "http://gallery.menalto.com/forum_module_tag" +author_url = "http://codex.galleryproject.org/Gallery:Team" +info_url = "http://codex.galleryproject.org/Gallery3:Modules:tag" +discuss_url = "http://galleryproject.org/forum_module_tag" diff --git a/modules/user/module.info b/modules/user/module.info index 503bcd0d..d5128db4 100644 --- a/modules/user/module.info +++ b/modules/user/module.info @@ -3,6 +3,6 @@ description = "Gallery 3 user and group management" version = 4 author_name = "Gallery Team" -author_url = "http://codex.gallery2.org/Gallery:Team" -info_url = "http://codex.gallery2.org/Gallery3:Modules:user" -discuss_url = "http://gallery.menalto.com/forum_module_user" +author_url = "http://codex.galleryproject.org/Gallery:Team" +info_url = "http://codex.galleryproject.org/Gallery3:Modules:user" +discuss_url = "http://galleryproject.org/forum_module_user" diff --git a/modules/watermark/module.info b/modules/watermark/module.info index 58efa43f..e5003cda 100644 --- a/modules/watermark/module.info +++ b/modules/watermark/module.info @@ -2,6 +2,6 @@ name = "Watermarks" description = "Allows users to watermark their photos" version = 2 author_name = "Gallery Team" -author_url = "http://codex.gallery2.org/Gallery:Team" -info_url = "http://codex.gallery2.org/Gallery3:Modules:watermark" -discuss_url = "http://gallery.menalto.com/forum_module_watermark" +author_url = "http://codex.galleryproject.org/Gallery:Team" +info_url = "http://codex.galleryproject.org/Gallery3:Modules:watermark" +discuss_url = "http://galleryproject.org/forum_module_watermark" diff --git a/themes/admin_wind/theme.info b/themes/admin_wind/theme.info index 466d8e43..e2be9284 100644 --- a/themes/admin_wind/theme.info +++ b/themes/admin_wind/theme.info @@ -5,6 +5,6 @@ author = "Gallery Team" admin = 1 site = 0 author_name = "Gallery Team" -author_url = "http://codex.gallery2.org/Gallery:Team" -info_url = "http://codex.gallery2.org/Gallery3:Themes:admin_wind" -discuss_url = "http://gallery.menalto.com/forum_theme_admin_wind" +author_url = "http://codex.galleryproject.org/Gallery:Team" +info_url = "http://codex.galleryproject.org/Gallery3:Themes:admin_wind" +discuss_url = "http://galleryproject.org/forum_theme_admin_wind" diff --git a/themes/wind/theme.info b/themes/wind/theme.info index e0be78b9..b00e56d7 100644 --- a/themes/wind/theme.info +++ b/themes/wind/theme.info @@ -5,6 +5,6 @@ author = "Gallery Team" site = 1 admin = 0 author_name = "Gallery Team" -author_url = "http://codex.gallery2.org/Gallery:Team" -info_url = "http://codex.gallery2.org/Gallery3:Themes:wind" -discuss_url = "http://gallery.menalto.com/forum_theme_wind" +author_url = "http://codex.galleryproject.org/Gallery:Team" +info_url = "http://codex.galleryproject.org/Gallery3:Themes:wind" +discuss_url = "http://galleryproject.org/forum_theme_wind" -- cgit v1.2.3 From a1a66004571c2f6161335f9413b39d255190f153 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Wed, 23 Jan 2013 18:03:09 -0500 Subject: Enable the profiler and debug output if var/PROFILE exists. This provides a quick/easy way for server admins to provide profile output. Fixes #1959. --- modules/gallery/helpers/gallery.php | 8 ++++++++ modules/gallery/helpers/gallery_theme.php | 4 ++-- modules/gallery/libraries/MY_Database.php | 3 +++ modules/rest/helpers/rest.php | 2 +- 4 files changed, 14 insertions(+), 3 deletions(-) (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/helpers/gallery.php b/modules/gallery/helpers/gallery.php index 38002b58..725a710d 100644 --- a/modules/gallery/helpers/gallery.php +++ b/modules/gallery/helpers/gallery.php @@ -214,4 +214,12 @@ class gallery_Core { } return null; } + + /** + * Return true if we should show the profiler at the bottom of the page. Note that this + * function is called at database setup time so it cannot rely on the database. + */ + static function show_profiler() { + return file_exists(VARPATH . "PROFILE"); + } } \ No newline at end of file diff --git a/modules/gallery/helpers/gallery_theme.php b/modules/gallery/helpers/gallery_theme.php index f94b9ecd..3c6d71e9 100644 --- a/modules/gallery/helpers/gallery_theme.php +++ b/modules/gallery/helpers/gallery_theme.php @@ -71,7 +71,7 @@ class gallery_theme_Core { static function page_bottom($theme) { $session = Session::instance(); - if ($session->get("profiler", false)) { + if (gallery::show_profiler()) { Profiler::enable(); $profiler = new Profiler(); $profiler->render(); @@ -96,7 +96,7 @@ class gallery_theme_Core { static function admin_page_bottom($theme) { $session = Session::instance(); - if ($session->get("profiler", false)) { + if (gallery::show_profiler()) { Profiler::enable(); $profiler = new Profiler(); $profiler->render(); diff --git a/modules/gallery/libraries/MY_Database.php b/modules/gallery/libraries/MY_Database.php index 604b6484..aae0bb79 100644 --- a/modules/gallery/libraries/MY_Database.php +++ b/modules/gallery/libraries/MY_Database.php @@ -32,6 +32,9 @@ abstract class Database extends Database_Core { $config["connection"]["params"] = null; } parent::__construct($config); + if (gallery::show_profiler()) { + $this->config['benchmark'] = true; + } } /** diff --git a/modules/rest/helpers/rest.php b/modules/rest/helpers/rest.php index eeedff2a..9b367feb 100644 --- a/modules/rest/helpers/rest.php +++ b/modules/rest/helpers/rest.php @@ -54,7 +54,7 @@ class rest_Core { $html = t("Empty response"); } print "
    $html
    "; - if (Session::instance()->get("profiler", false)) { + if (gallery::show_profiler()) { Profiler::enable(); $profiler = new Profiler(); $profiler->render(); -- cgit v1.2.3 From 34198e71d320fe013993abff870986eafb6aa8bc Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Wed, 23 Jan 2013 20:50:54 -0500 Subject: Add a cache buster to all data_rest urls, add caching headers to all data_rest responses, and check cache validity. Fixes #1909. --- modules/gallery/helpers/data_rest.php | 25 +++++++++++++++++++++++-- modules/gallery/tests/Data_Rest_Helper_Test.php | 9 +++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/helpers/data_rest.php b/modules/gallery/helpers/data_rest.php index dc213510..d4f456d7 100644 --- a/modules/gallery/helpers/data_rest.php +++ b/modules/gallery/helpers/data_rest.php @@ -47,7 +47,16 @@ class data_rest_Core { throw new Kohana_404_Exception(); } - // We don't have a cache buster in the url, so don't set cache headers here. + header("Content-Length: " . filesize($file)); + + if (isset($p->m)) { + header("Pragma:"); + // Check that the content hasn't expired or it wasn't changed since cached + expires::check(2592000, $item->updated); + + expires::set(2592000, $item->updated); // 30 days + } + // We don't need to save the session for this request Session::instance()->abort_save(); @@ -84,6 +93,18 @@ class data_rest_Core { } static function url($item, $size) { - return url::abs_site("rest/data/{$item->id}?size=$size"); + if ($size == "full") { + $file = $item->file_path(); + } else if ($size == "resize") { + $file = $item->resize_path(); + } else { + $file = $item->thumb_path(); + } + if (!file_exists($file)) { + throw new Kohana_404_Exception(); + } + + return url::abs_site("rest/data/{$item->id}?size=$size&m=" . filemtime($file)); } } + diff --git a/modules/gallery/tests/Data_Rest_Helper_Test.php b/modules/gallery/tests/Data_Rest_Helper_Test.php index 69d17997..e6a94864 100644 --- a/modules/gallery/tests/Data_Rest_Helper_Test.php +++ b/modules/gallery/tests/Data_Rest_Helper_Test.php @@ -99,4 +99,13 @@ class Data_Rest_Helper_Test extends Gallery_Unit_Test_Case { // pass } } + + public function cache_buster_test() { + $photo = test::random_photo(); + + $this->assert_same( + url::abs_site("rest/data/{$photo->id}?size=thumb&m=" . filemtime($photo->thumb_path())), + data_rest::url($photo, "thumb")); + } } + -- cgit v1.2.3 From aa8fffcd8fc7d16f4bd84a9f8f7ead206d9ce45a Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Wed, 23 Jan 2013 21:33:19 -0500 Subject: Extract reweighting logic out of Organize_Controller into item::reweight_all_children as an API and write a test for it. Work in progress on #1914. --- modules/gallery/helpers/item.php | 12 ++++++++++++ modules/gallery/tests/Item_Helper_Test.php | 15 +++++++++++++++ modules/organize/controllers/organize.php | 8 +------- 3 files changed, 28 insertions(+), 7 deletions(-) (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/helpers/item.php b/modules/gallery/helpers/item.php index 20a865d1..b2c4cadf 100644 --- a/modules/gallery/helpers/item.php +++ b/modules/gallery/helpers/item.php @@ -437,4 +437,16 @@ class item_Core { } return call_user_func_array($callback, $args); } + + /** + * Reset all child weights of a given album to a monotonically increasing sequence based on the + * current sort order of the album. + */ + static function resequence_child_weights($album) { + $weight = 0; + foreach ($album->children() as $child) { + $child->weight = ++$weight; + $child->save(); + } + } } \ No newline at end of file diff --git a/modules/gallery/tests/Item_Helper_Test.php b/modules/gallery/tests/Item_Helper_Test.php index 0c08d1af..63081884 100644 --- a/modules/gallery/tests/Item_Helper_Test.php +++ b/modules/gallery/tests/Item_Helper_Test.php @@ -235,4 +235,19 @@ class Item_Helper_Test extends Gallery_Unit_Test_Case { $level3b->id, item::find_by_relative_url("{$level1->slug}/{$level2b->slug}/{$level3b->slug}")->id); } + + public function resequence_child_weights_test() { + $album = test::random_album(); + $photo1 = test::random_photo($album); + $photo2 = test::random_photo($album); + $this->assert_true($photo2->weight > $photo1->weight); + + $album->reload(); + $album->sort_order = "DESC"; + $album->save(); + item::resequence_child_weights($album); + + $this->assert_equal(2, $photo1->reload()->weight); + $this->assert_equal(1, $photo2->reload()->weight); + } } diff --git a/modules/organize/controllers/organize.php b/modules/organize/controllers/organize.php index 97280489..37920020 100644 --- a/modules/organize/controllers/organize.php +++ b/modules/organize/controllers/organize.php @@ -128,13 +128,7 @@ class Organize_Controller extends Controller { access::required("edit", $album); if ($album->sort_column != "weight") { - // Force all the weights into the current order before changing the order to manual - $weight = 0; - foreach ($album->children() as $child) { - $child->weight = ++$weight; - $child->save(); - } - + item::resequence_child_weights($album); $album->sort_column = "weight"; $album->sort_order = "ASC"; $album->save(); -- cgit v1.2.3 From 031dd3bd6f279b5ddec6f08ff11e0acee521a5fe Mon Sep 17 00:00:00 2001 From: shadlaws Date: Thu, 24 Jan 2013 12:03:05 +0100 Subject: #1960 - Add unit test to look for extra spaces at end of line - Added no_extra_spaces_at_end_of_line_test to File_Structure_Test. - Updated Gallery_Filters to exclude testing code that isn't ours. - Removed existing extra spaces. New test now passes. --- modules/gallery/helpers/gallery_installer.php | 6 +++--- modules/gallery/helpers/movie.php | 6 +++--- modules/gallery/tests/File_Structure_Test.php | 18 +++++++++++++++++- modules/gallery/tests/Gallery_Filters.php | 4 ++++ modules/gallery/views/movieplayer.html.php | 2 +- modules/info/helpers/info_block.php | 2 +- modules/organize/views/organize_frame.html.php | 6 +++--- themes/admin_wind/css/fix-ie.css | 2 +- 8 files changed, 33 insertions(+), 13 deletions(-) (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/helpers/gallery_installer.php b/modules/gallery/helpers/gallery_installer.php index 91c785f6..fe651a4e 100644 --- a/modules/gallery/helpers/gallery_installer.php +++ b/modules/gallery/helpers/gallery_installer.php @@ -718,14 +718,14 @@ class gallery_installer { if ($version == 50) { // In v51, we added a lock_timeout variable so that administrators could edit the time out - // from 1 second to a higher variable if their system runs concurrent parallel uploads for + // from 1 second to a higher variable if their system runs concurrent parallel uploads for // instance. module::set_var("gallery", "lock_timeout", 1); module::set_version("gallery", $version = 51); } if ($version == 51) { - // In v52, we added functions to the legal_file helper that map photo and movie file + // In v52, we added functions to the legal_file helper that map photo and movie file // extensions to their mime types (and allow extension of the list by other modules). During // this process, we correctly mapped m4v files to video/x-m4v, correcting a previous error // where they were mapped to video/mp4. This corrects the existing items. @@ -736,7 +736,7 @@ class gallery_installer { ->execute(); module::set_version("gallery", $version = 52); } - + if ($version == 52) { // In v53, we added the ability to change the default time used when extracting frames from // movies. Previously we hard-coded this at 3 seconds, so we use that as the default. diff --git a/modules/gallery/helpers/movie.php b/modules/gallery/helpers/movie.php index fb3fab80..7e6a2e55 100644 --- a/modules/gallery/helpers/movie.php +++ b/modules/gallery/helpers/movie.php @@ -82,7 +82,7 @@ class movie_Core { // extract frame at start_time, unless movie is too short $start_time_arg = ($duration >= $start_time + 0.1) ? "-ss " . movie::seconds_to_hhmmssdd($start_time) : ""; - + $input_args = isset($movie_options["input_args"]) ? $movie_options["input_args"] : ""; $output_args = isset($movie_options["output_args"]) ? $movie_options["output_args"] : ""; @@ -164,7 +164,7 @@ class movie_Core { * Return the time/duration formatted in hh:mm:ss.dd from a number of seconds. * Useful for inputs to ffmpeg. * - * Note that this is similar to date("H:i:s", mktime(0,0,$seconds,0,0,0,0)), but unlike this + * Note that this is similar to date("H:i:s", mktime(0,0,$seconds,0,0,0,0)), but unlike this * approach avoids potential issues with time zone and DST mismatch and/or using deprecated * features (the last argument of mkdate above, which disables DST, is deprecated as of PHP 5.3). */ @@ -172,7 +172,7 @@ class movie_Core { return sprintf("%02d:%02d:%05.2f", floor($seconds / 3600), floor(($seconds % 3600) / 60), floor(100 * $seconds % 6000) / 100); } - + /** * Return the number of seconds from a time/duration formatted in hh:mm:ss.dd. * Useful for outputs from ffmpeg. diff --git a/modules/gallery/tests/File_Structure_Test.php b/modules/gallery/tests/File_Structure_Test.php index 7ad865e0..ce75ea13 100644 --- a/modules/gallery/tests/File_Structure_Test.php +++ b/modules/gallery/tests/File_Structure_Test.php @@ -285,7 +285,7 @@ class File_Structure_Test extends Gallery_Unit_Test_Case { } public function all_public_functions_in_test_files_end_in_test() { - // Who tests the tests? :-) + // Who tests the tests? :-) (ref: http://www.xkcd.com/1163) $dir = new PhpCodeFilterIterator( new GalleryCodeFilterIterator( new RecursiveIteratorIterator( @@ -315,4 +315,20 @@ class File_Structure_Test extends Gallery_Unit_Test_Case { } } } + + public function no_extra_spaces_at_end_of_line_test() { + $dir = new GalleryCodeFilterIterator( + new RecursiveIteratorIterator(new RecursiveDirectoryIterator(DOCROOT))); + $errors = ""; + foreach ($dir as $file) { + if (preg_match("/\.(php|css|html|js)$/", $file)) { + foreach (file($file) as $line_num => $line) { + if ((substr($line, -2) == " \n") || (substr($line, -1) == " ")) { + $errors .= "$file at line " . ($line_num + 1) . "\n"; + } + } + } + } + $this->assert_true(empty($errors), "Extra spaces at end of line found at:\n$errors"); + } } diff --git a/modules/gallery/tests/Gallery_Filters.php b/modules/gallery/tests/Gallery_Filters.php index 83dbd53f..6c2a6aa3 100644 --- a/modules/gallery/tests/Gallery_Filters.php +++ b/modules/gallery/tests/Gallery_Filters.php @@ -47,6 +47,10 @@ class GalleryCodeFilterIterator extends FilterIterator { strpos($path_name, SYSPATH) !== false || strpos($path_name, MODPATH . "gallery/libraries/HTMLPurifier") !== false || strpos($path_name, MODPATH . "gallery/vendor/joomla") !== false || + strpos($path_name, MODPATH . "organize/vendor/ext") !== false || + strpos($path_name, DOCROOT . "lib") !== false || + strpos($path_name, DOCROOT . "themes/admin_wind/css/themeroller") !== false || + strpos($path_name, DOCROOT . "themes/wind/css/themeroller") !== false || substr($path_name, -1, 1) == "~"); } } diff --git a/modules/gallery/views/movieplayer.html.php b/modules/gallery/views/movieplayer.html.php index 25cb9f58..edb5184c 100644 --- a/modules/gallery/views/movieplayer.html.php +++ b/modules/gallery/views/movieplayer.html.php @@ -18,7 +18,7 @@ $("#" + id).css({width: width, height: height}); }; // setup flowplayer - flowplayer(id, + flowplayer(id, $.extend(true, { "src": "", "wmode": "transparent", diff --git a/modules/info/helpers/info_block.php b/modules/info/helpers/info_block.php index 4079fe55..62aa0746 100644 --- a/modules/info/helpers/info_block.php +++ b/modules/info/helpers/info_block.php @@ -29,7 +29,7 @@ class info_block_Core { if ($theme->item()) { $block = new Block(); $block->css_id = "g-metadata"; - $block->title = $theme->item()->is_album() ? t("Album info") : + $block->title = $theme->item()->is_album() ? t("Album info") : ($theme->item()->is_movie() ? t("Movie info") : t("Photo info")); $block->content = new View("info_block.html"); if ($theme->item->title && module::get_var("info", "show_title")) { diff --git a/modules/organize/views/organize_frame.html.php b/modules/organize/views/organize_frame.html.php index c33b7607..b1d92675 100644 --- a/modules/organize/views/organize_frame.html.php +++ b/modules/organize/views/organize_frame.html.php @@ -365,7 +365,7 @@ } } }); - + var tag_button = new Ext.Button({ flex: 2, text: for_js() ?>, @@ -425,11 +425,11 @@ sort_column_combobox, sort_order_combobox ] - }, + }, { xtype: "spacer", - flex: 3 + flex: 3 }, tag_textfield, tag_button, diff --git a/themes/admin_wind/css/fix-ie.css b/themes/admin_wind/css/fix-ie.css index 5475cb79..4546f9fa 100644 --- a/themes/admin_wind/css/fix-ie.css +++ b/themes/admin_wind/css/fix-ie.css @@ -15,4 +15,4 @@ tr.g-info td, tr.g-success td, tr.g-warning td { background: none !important; -} +} -- cgit v1.2.3 From 48bd19808c38a8de20cfece1adc1ffe226da3783 Mon Sep 17 00:00:00 2001 From: shadlaws Date: Fri, 25 Jan 2013 08:47:29 +0100 Subject: #1956 - Escape LIKE queries (for _ and %). In MySQL queries, _ and % characters are treated as wildcards (similar to ? and *, respectively). - Added escape_for_like function to MY_Database.php - Added unit test to Database_Test - Corrected the five unescaped instances in the code using this function. --- modules/g2_import/controllers/g2.php | 2 +- modules/gallery/helpers/item_rest.php | 2 +- modules/gallery/libraries/MY_Database.php | 10 ++++++++++ modules/gallery/libraries/drivers/Cache/Database.php | 4 ++-- modules/gallery/tests/Database_Test.php | 6 ++++++ modules/tag/controllers/tags.php | 2 +- 6 files changed, 21 insertions(+), 5 deletions(-) (limited to 'modules/gallery/helpers') diff --git a/modules/g2_import/controllers/g2.php b/modules/g2_import/controllers/g2.php index 5a76940e..0645266b 100644 --- a/modules/g2_import/controllers/g2.php +++ b/modules/g2_import/controllers/g2.php @@ -49,7 +49,7 @@ class G2_Controller extends Controller { if ($view == "core.DownloadItem") { $where[] = array("resource_type", "IN", array("file", "resize", "thumbnail", "full")); } else if ($view) { - $where[] = array("g2_url", "like", "%g2_view=$view%"); + $where[] = array("g2_url", "LIKE", "%" . Database::escape_for_like("g2_view=$view") . "%"); } // else: Assuming that the first search hit is sufficiently good. } else if ($path) { $where = array(array("g2_url", "IN", array($path, str_replace(" ", "+", $path)))); diff --git a/modules/gallery/helpers/item_rest.php b/modules/gallery/helpers/item_rest.php index 10799567..efeba2ef 100644 --- a/modules/gallery/helpers/item_rest.php +++ b/modules/gallery/helpers/item_rest.php @@ -64,7 +64,7 @@ class item_rest_Core { } if (isset($p->name)) { - $orm->where("name", "LIKE", "%{$p->name}%"); + $orm->where("name", "LIKE", "%" . Database::escape_for_like($p->name) . "%"); } if (isset($p->type)) { diff --git a/modules/gallery/libraries/MY_Database.php b/modules/gallery/libraries/MY_Database.php index aae0bb79..33759b67 100644 --- a/modules/gallery/libraries/MY_Database.php +++ b/modules/gallery/libraries/MY_Database.php @@ -88,4 +88,14 @@ abstract class Database extends Database_Core { static function set_default_instance($db) { self::$instances["default"] = $db; } + + /** + * Escape LIKE queries, add wildcards. In MySQL queries using LIKE, _ and % characters are + * treated as wildcards similar to ? and *, respectively. Therefore, we need to escape _, %, + * and \ (the escape character itself). + */ + static function escape_for_like($value) { + // backslash must go first to avoid double-escaping + return addcslashes($value, '\_%'); + } } \ No newline at end of file diff --git a/modules/gallery/libraries/drivers/Cache/Database.php b/modules/gallery/libraries/drivers/Cache/Database.php index a7aae92c..8790d0e1 100644 --- a/modules/gallery/libraries/drivers/Cache/Database.php +++ b/modules/gallery/libraries/drivers/Cache/Database.php @@ -69,7 +69,7 @@ class Cache_Database_Driver extends Cache_Driver { ->select() ->from("caches"); foreach ($tags as $tag) { - $db->where("tags", "LIKE", "%<$tag>%"); + $db->where("tags", "LIKE", "%" . Database::escape_for_like("<$tag>") . "%"); } $db_result = $db->execute(); @@ -139,7 +139,7 @@ class Cache_Database_Driver extends Cache_Driver { // Delete all caches } else if ($is_tag === true) { foreach ($keys as $tag) { - $db->where("tags", "LIKE", "%<$tag>%"); + $db->where("tags", "LIKE", "%" . Database::escape_for_like("<$tag>") . "%"); } } else { $db->where("key", "IN", $keys); diff --git a/modules/gallery/tests/Database_Test.php b/modules/gallery/tests/Database_Test.php index ab3290a9..106062f5 100644 --- a/modules/gallery/tests/Database_Test.php +++ b/modules/gallery/tests/Database_Test.php @@ -147,6 +147,12 @@ class Database_Test extends Gallery_Unit_Test_Case { $sql = str_replace("\n", " ", $sql); $this->assert_same("UPDATE [test_tables] SET [name] = [Test Name] WHERE [1] = [1]", $sql); } + + function escape_for_like_test() { + // Note: literal double backslash is written as \\\ + $this->assert_same('basic\_test', Database::escape_for_like("basic_test")); + $this->assert_same('\\\100\%\_test/', Database::escape_for_like('\100%_test/')); + } } class Database_Mock extends Database { diff --git a/modules/tag/controllers/tags.php b/modules/tag/controllers/tags.php index 77ad7f50..77d45a95 100644 --- a/modules/tag/controllers/tags.php +++ b/modules/tag/controllers/tags.php @@ -52,7 +52,7 @@ class Tags_Controller extends Controller { $limit = Input::instance()->get("limit"); $tag_part = ltrim(end($tag_parts)); $tag_list = ORM::factory("tag") - ->where("name", "LIKE", "{$tag_part}%") + ->where("name", "LIKE", Database::escape_for_like($tag_part) . "%") ->order_by("name", "ASC") ->limit($limit) ->find_all(); -- cgit v1.2.3 From c2d1c2407fdef326cd1f59d6852620f915a5c0e1 Mon Sep 17 00:00:00 2001 From: shadlaws Date: Fri, 25 Jan 2013 23:47:08 +0100 Subject: #1965 - Improve sanity checks and copy/convert/process logic for rotate and resize. - resize: ensured that resize is skipped *only* if the metadata is valid or the options are well-defined and would upscale. Then, if resize is skipped, check to see if it still needs to be converted. Previous conditions would allow a small PNG to get copied to a JPG, and would allow a corrupted JPG to be copied to the output. - rotate: add checks for empty file or empty options. - use get_file_metadata instead of direct getimagesize call. - add unit tests for rotate and resize, including some for corrupted input files and missing options. --- modules/gallery/helpers/gallery_graphics.php | 36 +++++- .../gallery/tests/Gallery_Graphics_Helper_Test.php | 137 +++++++++++++++++++++ 2 files changed, 167 insertions(+), 6 deletions(-) create mode 100644 modules/gallery/tests/Gallery_Graphics_Helper_Test.php (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/helpers/gallery_graphics.php b/modules/gallery/helpers/gallery_graphics.php index 1f0728c0..e81d5468 100644 --- a/modules/gallery/helpers/gallery_graphics.php +++ b/modules/gallery/helpers/gallery_graphics.php @@ -31,6 +31,15 @@ class gallery_graphics_Core { module::event("graphics_rotate", $input_file, $output_file, $options, $item); + if (@filesize($input_file) == 0) { + throw new Exception("@todo EMPTY_INPUT_FILE"); + } + + if (!isset($options["degrees"])) { + $options["degrees"] = 0; + } + + // Rotate the image. This also implicitly converts its format if needed. Image::factory($input_file) ->quality(module::get_var("gallery", "image_quality")) ->rotate($options["degrees"]) @@ -57,11 +66,26 @@ class gallery_graphics_Core { throw new Exception("@todo EMPTY_INPUT_FILE"); } - $dims = getimagesize($input_file); - if (max($dims[0], $dims[1]) <= min($options["width"], $options["height"])) { - // Image would get upscaled; do nothing - copy($input_file, $output_file); + list ($input_width, $input_height, $input_mime, $input_extension) = + photo::get_file_metadata($input_file); + if ($input_width && $input_height && + (empty($options["width"]) || empty($options["height"]) || empty($options["master"]) || + (max($input_width, $input_height) <= min($options["width"], $options["height"])))) { + // Photo dimensions well-defined, but options not well-defined or would upscale the image. + // Do not resize. Check mimes to see if we can copy the file or if we need to convert it. + // (checking mimes avoids needlessly converting jpg to jpeg, etc.) + $output_mime = legal_file::get_photo_types_by_extension(pathinfo($output_file, PATHINFO_EXTENSION)); + if ($input_mime && $output_mime && ($input_mime == $output_mime)) { + // Mimes well-defined and identical - copy input to output + copy($input_file, $output_file); + } else { + // Mimes not well-defined or not the same - convert input to output + $image = Image::factory($input_file) + ->quality(module::get_var("gallery", "image_quality")) + ->save($output_file); + } } else { + // Resize the image. This also implicitly converts its format if needed. $image = Image::factory($input_file) ->resize($options["width"], $options["height"], $options["master"]) ->quality(module::get_var("gallery", "image_quality")); @@ -96,8 +120,8 @@ class gallery_graphics_Core { module::event("graphics_composite", $input_file, $output_file, $options, $item); - list ($width, $height) = getimagesize($input_file); - list ($w_width, $w_height) = getimagesize($options["file"]); + list ($width, $height) = photo::get_file_metadata($input_file); + list ($w_width, $w_height) = photo::get_file_metadata($options["file"]); $pad = isset($options["padding"]) ? $options["padding"] : 10; $top = $pad; diff --git a/modules/gallery/tests/Gallery_Graphics_Helper_Test.php b/modules/gallery/tests/Gallery_Graphics_Helper_Test.php new file mode 100644 index 00000000..20096b23 --- /dev/null +++ b/modules/gallery/tests/Gallery_Graphics_Helper_Test.php @@ -0,0 +1,137 @@ + 90); + gallery_graphics::rotate($input_file, $output_file, $options, null); + + // Output is rotated to 768x1024 jpg + $this->assert_equal(array(768, 1024, "image/jpeg", "jpg"), photo::get_file_metadata($output_file)); + } + + public function rotate_jpg_without_options_test() { + // Input is a 1024x768 jpg, output options undefined + $input_file = MODPATH . "gallery/tests/test.jpg"; + $output_file = TMPPATH . test::random_name() . ".jpg"; + gallery_graphics::rotate($input_file, $output_file, null, null); + + // Output is not rotated, still a 1024x768 jpg + $this->assert_equal(array(1024, 768, "image/jpeg", "jpg"), photo::get_file_metadata($output_file)); + } + + public function rotate_bad_jpg_test() { + // Input is a garbled jpg, output is jpg autofit to 300x300 + $input_file = TMPPATH . test::random_name() . ".jpg"; + $output_file = TMPPATH . test::random_name() . ".jpg"; + $options = array("degrees" => 90); + file_put_contents($input_file, test::lorem_ipsum(200)); + + // Should get passed to Image library and throw an exception + try { + gallery_graphics::rotate($input_file, $output_file, $options, null); + $this->assert_true(false, "Shouldn't get here"); + } catch (Exception $e) { + // pass + } + } + + public function resize_jpg_test() { + // Input is a 1024x768 jpg, output is jpg autofit to 300x300 + $input_file = MODPATH . "gallery/tests/test.jpg"; + $output_file = TMPPATH . test::random_name() . ".jpg"; + $options = array("width" => 300, "height" => 300, "master" => Image::AUTO); + gallery_graphics::resize($input_file, $output_file, $options, null); + + // Output is resized to 300x225 jpg + $this->assert_equal(array(300, 225, "image/jpeg", "jpg"), photo::get_file_metadata($output_file)); + } + + public function resize_jpg_to_png_test() { + // Input is a 1024x768 jpg, output is png autofit to 300x300 + $input_file = MODPATH . "gallery/tests/test.jpg"; + $output_file = TMPPATH . test::random_name() . ".png"; + $options = array("width" => 300, "height" => 300, "master" => Image::AUTO); + gallery_graphics::resize($input_file, $output_file, $options, null); + + // Output is resized to 300x225 png + $this->assert_equal(array(300, 225, "image/png", "png"), photo::get_file_metadata($output_file)); + } + + public function resize_jpg_with_no_upscale_test() { + // Input is a 1024x768 jpg, output is jpg autofit to 1200x1200 - should not upscale + $input_file = MODPATH . "gallery/tests/test.jpg"; + $output_file = TMPPATH . test::random_name() . ".jpg"; + $options = array("width" => 1200, "height" => 1200, "master" => Image::AUTO); + gallery_graphics::resize($input_file, $output_file, $options, null); + + // Output is copied directly from input + $this->assert_equal(file_get_contents($input_file), file_get_contents($output_file)); + } + + public function resize_jpg_to_png_with_no_upscale_test() { + // Input is a 1024x768 jpg, output is png autofit to 1200x1200 - should not upscale + $input_file = MODPATH . "gallery/tests/test.jpg"; + $output_file = TMPPATH . test::random_name() . ".png"; + $options = array("width" => 1200, "height" => 1200, "master" => Image::AUTO); + gallery_graphics::resize($input_file, $output_file, $options, null); + + // Output is converted from input without resize + $this->assert_equal(array(1024, 768, "image/png", "png"), photo::get_file_metadata($output_file)); + } + + public function resize_jpg_without_options_test() { + // Input is a 1024x768 jpg, output is jpg without options - should not attempt resize + $input_file = MODPATH . "gallery/tests/test.jpg"; + $output_file = TMPPATH . test::random_name() . ".jpg"; + gallery_graphics::resize($input_file, $output_file, null, null); + + // Output is copied directly from input + $this->assert_equal(file_get_contents($input_file), file_get_contents($output_file)); + } + + public function resize_jpg_to_png_without_options_test() { + // Input is a 1024x768 jpg, output is png without options - should not attempt resize + $input_file = MODPATH . "gallery/tests/test.jpg"; + $output_file = TMPPATH . test::random_name() . ".png"; + gallery_graphics::resize($input_file, $output_file, null, null); + + // Output is converted from input without resize + $this->assert_equal(array(1024, 768, "image/png", "png"), photo::get_file_metadata($output_file)); + } + + public function resize_bad_jpg_test() { + // Input is a garbled jpg, output is jpg autofit to 300x300 + $input_file = TMPPATH . test::random_name() . ".jpg"; + $output_file = TMPPATH . test::random_name() . ".jpg"; + $options = array("width" => 300, "height" => 300, "master" => Image::AUTO); + file_put_contents($input_file, test::lorem_ipsum(200)); + + // Should get passed to Image library and throw an exception + try { + gallery_graphics::resize($input_file, $output_file, $options, null); + $this->assert_true(false, "Shouldn't get here"); + } catch (Exception $e) { + // pass + } + } +} \ No newline at end of file -- cgit v1.2.3 From 212944e1eae2ffb9a3e27ea7d1c6813160cce1ed Mon Sep 17 00:00:00 2001 From: shadlaws Date: Sat, 26 Jan 2013 00:04:55 +0100 Subject: #1951 - Make metadata generation more flexible (photo and movie helpers). - added photo_get_file_metadata and movie_get_file_metadata events - modified photo::get_file_metadata and movie::get_file_metadata to use them - ensure that non-readable files throw exceptions - redirected other photo metadata calls in core to photo::get_file_metadata (the helper function already exists, but in many places getimagesize is still called directly) - added some unit tests (neither of the functions above had one) --- modules/gallery/helpers/graphics.php | 10 +-- modules/gallery/helpers/movie.php | 86 ++++++++++++++++------ modules/gallery/helpers/photo.php | 63 +++++++++++++--- modules/gallery/tests/Movie_Helper_Test.php | 32 ++++++++ modules/gallery/tests/Photo_Helper_Test.php | 56 ++++++++++++++ modules/watermark/controllers/admin_watermarks.php | 32 +++----- 6 files changed, 218 insertions(+), 61 deletions(-) create mode 100644 modules/gallery/tests/Photo_Helper_Test.php (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/helpers/graphics.php b/modules/gallery/helpers/graphics.php index 51437d4b..0c5f8366 100644 --- a/modules/gallery/helpers/graphics.php +++ b/modules/gallery/helpers/graphics.php @@ -195,9 +195,8 @@ class graphics_Core { } else { copy(MODPATH . "gallery/images/missing_photo.png", $item->thumb_path()); } - $dims = getimagesize($item->thumb_path()); - $item->thumb_width = $dims[0]; - $item->thumb_height = $dims[1]; + list ($item->thumb_width, $item->thumb_height) = + photo::get_file_metadata($item->thumb_path()); } if (!empty($ops["resize"])) { @@ -206,9 +205,8 @@ class graphics_Core { } else { copy(MODPATH . "gallery/images/missing_photo.png", $item->resize_path()); } - $dims = getimagesize($item->resize_path()); - $item->resize_width = $dims[0]; - $item->resize_height = $dims[1]; + list ($item->resize_width, $item->resize_height) = + photo::get_file_metadata($item->resize_path()); } $item->save(); } catch (Exception $e) { diff --git a/modules/gallery/helpers/movie.php b/modules/gallery/helpers/movie.php index 7e6a2e55..6844771b 100644 --- a/modules/gallery/helpers/movie.php +++ b/modules/gallery/helpers/movie.php @@ -123,41 +123,79 @@ class movie_Core { /** * Return the width, height, mime_type, extension and duration of the given movie file. + * Metadata is first generated using ffmpeg (or set to defaults if it fails), + * then can be modified by other modules using movie_get_file_metadata events. + * + * This function and its use cases are symmetric to those of photo::get_file_metadata. + * + * @param string $file_path + * @return array array($width, $height, $mime_type, $extension, $duration) + * + * Use cases in detail: + * Input is standard movie type (flv/mp4/m4v) + * -> return metadata from ffmpeg + * Input is *not* standard movie type that is supported by ffmpeg (e.g. avi, mts...) + * -> return metadata from ffmpeg + * Input is *not* standard movie type that is *not* supported by ffmpeg but is legal + * -> return zero width, height, and duration; mime type and extension according to legal_file + * Input is *not* standard movie type that is *not* supported by ffmpeg and is *not* legal + * -> return zero width, height, and duration; null mime type and extension + * Input is not readable or does not exist + * -> throw exception + * Note: movie_get_file_metadata events can change any of the above cases (except the last one). */ static function get_file_metadata($file_path) { - $ffmpeg = movie::find_ffmpeg(); - if (empty($ffmpeg)) { - throw new Exception("@todo MISSING_FFMPEG"); + if (!is_readable($file_path)) { + throw new Exception("@todo UNREADABLE_FILE"); } - $cmd = escapeshellcmd($ffmpeg) . " -i " . escapeshellarg($file_path) . " 2>&1"; - $result = `$cmd`; - if (preg_match("/Stream.*?Video:.*?, (\d+)x(\d+)/", $result, $matches_res)) { - if (preg_match("/Stream.*?Video:.*? \[.*?DAR (\d+):(\d+).*?\]/", $result, $matches_dar) && - $matches_dar[1] >= 1 && $matches_dar[2] >= 1) { - // DAR is defined - determine width based on height and DAR - // (should always be int, but adding round to be sure) - $matches_res[1] = round($matches_res[2] * $matches_dar[1] / $matches_dar[2]); + $metadata = new stdClass(); + $ffmpeg = movie::find_ffmpeg(); + if (!empty($ffmpeg)) { + // ffmpeg found - use it to get width, height, and duration. + $cmd = escapeshellcmd($ffmpeg) . " -i " . escapeshellarg($file_path) . " 2>&1"; + $result = `$cmd`; + if (preg_match("/Stream.*?Video:.*?, (\d+)x(\d+)/", $result, $matches_res)) { + if (preg_match("/Stream.*?Video:.*? \[.*?DAR (\d+):(\d+).*?\]/", $result, $matches_dar) && + $matches_dar[1] >= 1 && $matches_dar[2] >= 1) { + // DAR is defined - determine width based on height and DAR + // (should always be int, but adding round to be sure) + $matches_res[1] = round($matches_res[2] * $matches_dar[1] / $matches_dar[2]); + } + list ($metadata->width, $metadata->height) = array($matches_res[1], $matches_res[2]); + } else { + list ($metadata->width, $metadata->height) = array(0, 0); + } + + if (preg_match("/Duration: (\d+:\d+:\d+\.\d+)/", $result, $matches)) { + $metadata->duration = movie::hhmmssdd_to_seconds($matches[1]); + } else if (preg_match("/duration.*?:.*?(\d+)/", $result, $matches)) { + $metadata->duration = $matches[1]; + } else { + $metadata->duration = 0; } - list ($width, $height) = array($matches_res[1], $matches_res[2]); } else { - list ($width, $height) = array(0, 0); + // ffmpeg not found - set width, height, and duration to zero. + $metadata->width = 0; + $metadata->height = 0; + $metadata->duration = 0; } - $extension = strtolower(pathinfo($file_path, PATHINFO_EXTENSION)); - $extension = $extension ? $extension : "flv"; // No extension? Assume FLV. - $mime_type = legal_file::get_movie_types_by_extension($extension); - $mime_type = $mime_type ? $mime_type : "video/x-flv"; // No MIME found? Default to video/x-flv. - - if (preg_match("/Duration: (\d+:\d+:\d+\.\d+)/", $result, $matches)) { - $duration = movie::hhmmssdd_to_seconds($matches[1]); - } else if (preg_match("/duration.*?:.*?(\d+)/", $result, $matches)) { - $duration = $matches[1]; + $extension = pathinfo($file_path, PATHINFO_EXTENSION); + if (!$extension || + (!$metadata->mime_type = legal_file::get_movie_types_by_extension($extension))) { + // Extension is empty or illegal. + $metadata->extension = null; + $metadata->mime_type = null; } else { - $duration = 0; + // Extension is legal (and mime is already set above). + $metadata->extension = strtolower($extension); } - return array($width, $height, $mime_type, $extension, $duration); + // Run movie_get_file_metadata events which can modify the class, then return results. + module::event("movie_get_file_metadata", $file_path, $metadata); + return array($metadata->width, $metadata->height, $metadata->mime_type, + $metadata->extension, $metadata->duration); } /** diff --git a/modules/gallery/helpers/photo.php b/modules/gallery/helpers/photo.php index 855cd0ae..51e51507 100644 --- a/modules/gallery/helpers/photo.php +++ b/modules/gallery/helpers/photo.php @@ -80,20 +80,61 @@ class photo_Core { /** * Return the width, height, mime_type and extension of the given image file. + * Metadata is first generated using getimagesize (or the legal_file mapping if it fails), + * then can be modified by other modules using photo_get_file_metadata events. + * + * This function and its use cases are symmetric to those of photo::get_file_metadata. + * + * @param string $file_path + * @return array array($width, $height, $mime_type, $extension) + * + * Use cases in detail: + * Input is standard photo type (jpg/png/gif) + * -> return metadata from getimagesize() + * Input is *not* standard photo type that is supported by getimagesize (e.g. tif, bmp...) + * -> return metadata from getimagesize() + * Input is *not* standard photo type that is *not* supported by getimagesize but is legal + * -> return zero width and height, mime type and extension according to legal_file + * Input is *not* standard photo type that is *not* supported by getimagesize and is *not* legal + * -> return zero width and height, null mime type and extension + * Input is not readable or does not exist + * -> throw exception + * Note: photo_get_file_metadata events can change any of the above cases (except the last one). */ static function get_file_metadata($file_path) { - $image_info = getimagesize($file_path); - if ($image_info) { - $width = $image_info[0]; - $height = $image_info[1]; - $mime_type = $image_info["mime"]; - $extension = image_type_to_extension($image_info[2], false); - return array($width, $height, $mime_type, $extension); + if (!is_readable($file_path)) { + throw new Exception("@todo UNREADABLE_FILE"); + } + + $metadata = new stdClass(); + if ($image_info = getimagesize($file_path)) { + // getimagesize worked - use its results. + $metadata->width = $image_info[0]; + $metadata->height = $image_info[1]; + $metadata->mime_type = $image_info["mime"]; + $metadata->extension = image_type_to_extension($image_info[2], false); + // We prefer jpg instead of jpeg (which is returned by image_type_to_extension). + if ($metadata->extension == "jpeg") { + $metadata->extension = "jpg"; + } } else { - // getimagesize failed - use legal_file mapping instead. - $extension = strtolower(pathinfo($file_path, PATHINFO_EXTENSION)); - $mime_type = legal_file::get_photo_types_by_extension($extension); - return array(0, 0, $mime_type, $extension); + // getimagesize failed - try to use legal_file mapping instead. + $extension = pathinfo($file_path, PATHINFO_EXTENSION); + if (!$extension || + (!$metadata->mime_type = legal_file::get_photo_types_by_extension($extension))) { + // Extension is empty or illegal. + $metadata->extension = null; + $metadata->mime_type = null; + } else { + // Extension is legal (and mime is already set above). + $metadata->extension = strtolower($extension); + } + $metadata->width = 0; + $metadata->height = 0; } + + // Run photo_get_file_metadata events which can modify the class, then return results. + module::event("photo_get_file_metadata", $file_path, $metadata); + return array($metadata->width, $metadata->height, $metadata->mime_type, $metadata->extension); } } diff --git a/modules/gallery/tests/Movie_Helper_Test.php b/modules/gallery/tests/Movie_Helper_Test.php index ff7f798c..0c262620 100644 --- a/modules/gallery/tests/Movie_Helper_Test.php +++ b/modules/gallery/tests/Movie_Helper_Test.php @@ -46,4 +46,36 @@ class Movie_Helper_Test extends Gallery_Unit_Test_Case { $this->assert_equal($seconds, movie::hhmmssdd_to_seconds($hhmmssdd)); } } + + public function get_file_metadata_test() { + $movie = test::random_movie(); + $this->assert_equal(array(360, 288, "video/x-flv", "flv", 6.00), + movie::get_file_metadata($movie->file_path())); + } + + public function get_file_metadata_with_non_existent_file_test() { + try { + $metadata = movie::get_file_metadata(MODPATH . "gallery/tests/this_does_not_exist"); + $this->assert_true(false, "Shouldn't get here"); + } catch (Exception $e) { + // pass + } + } + + public function get_file_metadata_with_no_extension_test() { + copy(MODPATH . "gallery/tests/test.flv", TMPPATH . "test_flv_with_no_extension"); + $this->assert_equal(array(360, 288, null, null, 6.00), + movie::get_file_metadata(TMPPATH . "test_flv_with_no_extension")); + } + + public function get_file_metadata_with_illegal_extension_test() { + $this->assert_equal(array(0, 0, null, null, 0), + movie::get_file_metadata(MODPATH . "gallery/tests/Movie_Helper_Test.php")); + } + + public function get_file_metadata_with_illegal_extension_but_valid_file_contents_test() { + copy(MODPATH . "gallery/tests/test.flv", TMPPATH . "test_flv_with_php_extension.php"); + $this->assert_equal(array(360, 288, null, null, 6.00), + movie::get_file_metadata(TMPPATH . "test_flv_with_php_extension.php")); + } } diff --git a/modules/gallery/tests/Photo_Helper_Test.php b/modules/gallery/tests/Photo_Helper_Test.php new file mode 100644 index 00000000..5207a6db --- /dev/null +++ b/modules/gallery/tests/Photo_Helper_Test.php @@ -0,0 +1,56 @@ +assert_equal(array(1024, 768, "image/jpeg", "jpg"), + photo::get_file_metadata($photo->file_path())); + } + + public function get_file_metadata_with_non_existent_file_test() { + try { + $metadata = photo::get_file_metadata(MODPATH . "gallery/tests/this_does_not_exist"); + $this->assert_true(false, "Shouldn't get here"); + } catch (Exception $e) { + // pass + } + } + + public function get_file_metadata_with_no_extension_test() { + copy(MODPATH . "gallery/tests/test.jpg", TMPPATH . "test_jpg_with_no_extension"); + $this->assert_equal(array(1024, 768, "image/jpeg", "jpg"), + photo::get_file_metadata(TMPPATH . "test_jpg_with_no_extension")); + } + + public function get_file_metadata_with_illegal_extension_test() { + $this->assert_equal(array(0, 0, null, null), + photo::get_file_metadata(MODPATH . "gallery/tests/Photo_Helper_Test.php")); + } + + public function get_file_metadata_with_illegal_extension_but_valid_file_contents_test() { + // This ensures that we correctly "re-type" files with invalid extensions if the contents + // themselves are valid. This is needed to ensure that issues similar to those corrected by + // ticket #1855, where an image that looked valid (header said jpg) with a php extension was + // previously accepted without changing its extension, do not arise and cause security issues. + copy(MODPATH . "gallery/tests/test.jpg", TMPPATH . "test_jpg_with_php_extension.php"); + $this->assert_equal(array(1024, 768, "image/jpeg", "jpg"), + photo::get_file_metadata(TMPPATH . "test_jpg_with_php_extension.php")); + } +} diff --git a/modules/watermark/controllers/admin_watermarks.php b/modules/watermark/controllers/admin_watermarks.php index 0e6e214b..14c2b394 100644 --- a/modules/watermark/controllers/admin_watermarks.php +++ b/modules/watermark/controllers/admin_watermarks.php @@ -100,32 +100,24 @@ class Admin_Watermarks_Controller extends Admin_Controller { $name = preg_replace("/uploadfile-[^-]+-(.*)/", '$1', $pathinfo["basename"]); $name = legal_file::smash_extensions($name); - if (!($image_info = getimagesize($file)) || - !in_array($image_info[2], array(IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG))) { - message::error(t("Unable to identify this image file")); + list ($width, $height, $mime_type, $extension) = photo::get_file_metadata($file); + if (!legal_file::get_photo_extensions($extension)) { + message::error(t("Invalid or unidentifiable image file")); @unlink($file); return; - } - - if (!in_array($pathinfo["extension"], legal_file::get_photo_extensions())) { - switch ($image_info[2]) { - case IMAGETYPE_GIF: - $name = legal_file::change_extension($name, "gif"); - break; - case IMAGETYPE_JPEG: - $name = legal_file::change_extension($name, "jpg"); - break; - case IMAGETYPE_PNG: - $name = legal_file::change_extension($name, "png"); - break; - } + } else { + // Force correct, legal extension type on file, which will be of our canonical type + // (i.e. all lowercase, jpg instead of jpeg, etc.). This renaming prevents the issues + // addressed in ticket #1855, where an image that looked valid (header said jpg) with a + // php extension was previously accepted without changing its extension. + $name = legal_file::change_extension($name, $extension); } rename($file, VARPATH . "modules/watermark/$name"); module::set_var("watermark", "name", $name); - module::set_var("watermark", "width", $image_info[0]); - module::set_var("watermark", "height", $image_info[1]); - module::set_var("watermark", "mime_type", $image_info["mime"]); + module::set_var("watermark", "width", $width); + module::set_var("watermark", "height", $height); + module::set_var("watermark", "mime_type", $mime_type); module::set_var("watermark", "position", $form->add_watermark->position->value); module::set_var("watermark", "transparency", $form->add_watermark->transparency->value); $this->_update_graphics_rules(); -- cgit v1.2.3 From cc2aed656c7289881ba23e8ec700a4daf7889688 Mon Sep 17 00:00:00 2001 From: shadlaws Date: Sat, 26 Jan 2013 08:38:31 +0100 Subject: #1946, 1947 - Make altered names/slugs more user-friendly, make conflict-finding code check filenames with no extensions - Reduced from four places that alter names/slugs to two (one to populate empty slugs, one to handle conflicting names/slugs). - For empty slugs, fill with generic human-readable name (e.g. "photo") instead of random value. - For conflicting names/slugs, add suffix that's sequential (e.g. "-01"), only using random after 99 conflicts. - Made conflict-finding code check filenames with no extensions. - Renamed _randomize_name_or_slug_on_conflict to _check_and_fix_conflicts. - Added unit tests. Also cleaned up existing unit tests to reflect new logic and removed a redundant test. - Added installer logic to correct existing items now considered in conflict. - Revised gallery_task to look for duplicate names based on new criteria. --- installer/install.sql | 2 +- modules/gallery/helpers/gallery_installer.php | 43 +++++- modules/gallery/helpers/gallery_task.php | 113 +++++++++++++-- modules/gallery/helpers/item.php | 63 +++------ modules/gallery/models/item.php | 127 +++++++++++------ modules/gallery/module.info | 2 +- modules/gallery/tests/Item_Model_Test.php | 194 ++++++++++++++++++++++---- 7 files changed, 418 insertions(+), 126 deletions(-) (limited to 'modules/gallery/helpers') diff --git a/installer/install.sql b/installer/install.sql index b01c5a7c..70cf0c86 100644 --- a/installer/install.sql +++ b/installer/install.sql @@ -244,7 +244,7 @@ CREATE TABLE {modules} ( KEY `weight` (`weight`) ) AUTO_INCREMENT=10 DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; -INSERT INTO {modules} VALUES (1,1,'gallery',53,1); +INSERT INTO {modules} VALUES (1,1,'gallery',54,1); INSERT INTO {modules} VALUES (2,1,'user',4,2); INSERT INTO {modules} VALUES (3,1,'comment',7,3); INSERT INTO {modules} VALUES (4,1,'organize',4,4); diff --git a/modules/gallery/helpers/gallery_installer.php b/modules/gallery/helpers/gallery_installer.php index fe651a4e..86ff7ce5 100644 --- a/modules/gallery/helpers/gallery_installer.php +++ b/modules/gallery/helpers/gallery_installer.php @@ -315,7 +315,7 @@ class gallery_installer { module::set_var("gallery", "lock_timeout", 1); module::set_var("gallery", "movie_extract_frame_time", 3); - module::set_version("gallery", 53); + module::set_version("gallery", 54); } static function upgrade($version) { @@ -743,6 +743,47 @@ class gallery_installer { module::set_var("gallery", "movie_extract_frame_time", 3); module::set_version("gallery", $version = 53); } + + if ($version == 53) { + // In v54, we changed how we check for name and slug conflicts in Item_Model. Previously, + // we checked the whole filename. As a result, "foo.jpg" and "foo.png" were not considered + // conflicting if their slugs were different (a rare case in practice since server_add and + // uploader would give them both the same slug "foo"). Now, we check the filename without its + // extension. This upgrade stanza fixes any conflicts where they were previously allowed. + + // This might be slow, but if it times out it can just pick up where it left off. + + // Find and loop through each conflict (e.g. "foo.jpg", "foo.png", and "foo.flv" are one + // conflict; "bar.jpg", "bar.png", and "bar.flv" are another) + foreach (db::build() + ->select_distinct(array("parent_base_name" => + db::expr("CONCAT(`parent_id`, ':', LOWER(SUBSTR(`name`, 1, LOCATE('.', `name`) - 1)))"))) + ->select(array("C" => "COUNT(\"*\")")) + ->from("items") + ->where("type", "<>", "album") + ->having("C", ">", 1) + ->group_by("parent_base_name") + ->execute() as $conflict) { + list ($parent_id, $base_name) = explode(":", $conflict->parent_base_name, 2); + $base_name_escaped = Database::escape_for_like($base_name); + // Loop through the items for each conflict + foreach (db::build() + ->from("items") + ->select("id") + ->where("type", "<>", "album") + ->where("parent_id", "=", $parent_id) + ->where("name", "LIKE", "{$base_name_escaped}.%") + ->limit(1000000) // required to satisfy SQL syntax (no offset without limit) + ->offset(1) // skips the 0th item + ->execute() as $row) { + set_time_limit(30); + $item = ORM::factory("item", $row->id); + $item->name = $item->name; // this will force Item_Model to check for conflicts on save + $item->save(); + } + } + module::set_version("gallery", $version = 54); + } } static function uninstall() { diff --git a/modules/gallery/helpers/gallery_task.php b/modules/gallery/helpers/gallery_task.php index 82ca9ffc..e986b822 100644 --- a/modules/gallery/helpers/gallery_task.php +++ b/modules/gallery/helpers/gallery_task.php @@ -26,11 +26,13 @@ class gallery_task_Core { const FIX_STATE_RUN_DUPE_SLUGS = 5; const FIX_STATE_START_DUPE_NAMES = 6; const FIX_STATE_RUN_DUPE_NAMES = 7; - const FIX_STATE_START_REBUILD_ITEM_CACHES = 8; - const FIX_STATE_RUN_REBUILD_ITEM_CACHES = 9; - const FIX_STATE_START_MISSING_ACCESS_CACHES = 10; - const FIX_STATE_RUN_MISSING_ACCESS_CACHES = 11; - const FIX_STATE_DONE = 12; + const FIX_STATE_START_DUPE_BASE_NAMES = 8; + const FIX_STATE_RUN_DUPE_BASE_NAMES = 9; + const FIX_STATE_START_REBUILD_ITEM_CACHES = 10; + const FIX_STATE_RUN_REBUILD_ITEM_CACHES = 11; + const FIX_STATE_START_MISSING_ACCESS_CACHES = 12; + const FIX_STATE_RUN_MISSING_ACCESS_CACHES = 13; + const FIX_STATE_DONE = 14; static function available_tasks() { $dirty_count = graphics::find_dirty_images_query()->count_records(); @@ -348,8 +350,9 @@ class gallery_task_Core { // album audit (permissions and bogus album covers): 1 operation for every album $total += db::build()->where("type", "=", "album")->count_records("items"); - // one operation for each missing slug, name and access cache - foreach (array("find_dupe_slugs", "find_dupe_names", "find_missing_access_caches") as $func) { + // one operation for each dupe slug, dupe name, dupe base name, and missing access cache + foreach (array("find_dupe_slugs", "find_dupe_names", "find_dupe_base_names", + "find_missing_access_caches") as $func) { foreach (self::$func() as $row) { $total++; } @@ -489,11 +492,12 @@ class gallery_task_Core { $task->set("stack", implode(" ", $stack)); $state = self::FIX_STATE_RUN_DUPE_NAMES; } else { - $state = self::FIX_STATE_START_ALBUMS; + $state = self::FIX_STATE_START_DUPE_BASE_NAMES; } break; case self::FIX_STATE_RUN_DUPE_NAMES: + // NOTE: This does *not* attempt to fix the file system! $stack = explode(" ", $task->get("stack")); list ($parent_id, $name) = explode(":", array_pop($stack)); @@ -505,9 +509,16 @@ class gallery_task_Core { ->find_all(1, 1); if ($conflicts->count() && $conflict = $conflicts->current()) { $task->log("Fixing conflicting name for item id {$conflict->id}"); + if (!$conflict->is_album() && preg_match("/^(.*)(\.[^\.\/]*?)$/", $conflict->name, $matches)) { + $item_base_name = $matches[1]; + $item_extension = $matches[2]; // includes a leading dot + } else { + $item_base_name = $conflict->name; + $item_extension = ""; + } db::build() ->update("items") - ->set("name", $name . "-" . (string)rand(1000, 9999)) + ->set("name", $item_base_name . "-" . (string)rand(1000, 9999) . $item_extension) ->where("id", "=", $conflict->id) ->execute(); @@ -521,6 +532,74 @@ class gallery_task_Core { $task->set("stack", implode(" ", $stack)); $completed++; + if (empty($stack)) { + $state = self::FIX_STATE_START_DUPE_BASE_NAMES; + } + break; + + case self::FIX_STATE_START_DUPE_BASE_NAMES: + $stack = array(); + foreach (self::find_dupe_base_names() as $row) { + list ($parent_id, $base_name) = explode(":", $row->parent_base_name, 2); + $stack[] = join(":", array($parent_id, $base_name)); + } + if ($stack) { + $task->set("stack", implode(" ", $stack)); + $state = self::FIX_STATE_RUN_DUPE_BASE_NAMES; + } else { + $state = self::FIX_STATE_START_ALBUMS; + } + break; + + case self::FIX_STATE_RUN_DUPE_BASE_NAMES: + // NOTE: This *does* attempt to fix the file system! So, it must go *after* run_dupe_names. + $stack = explode(" ", $task->get("stack")); + list ($parent_id, $base_name) = explode(":", array_pop($stack)); + $base_name_escaped = Database::escape_for_like($base_name); + + $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", "LIKE", "{$base_name_escaped}.%") + ->where("type", "<>", "album") + ->find_all(1, 1); + if ($conflicts->count() && $conflict = $conflicts->current()) { + $task->log("Fixing conflicting name for item id {$conflict->id}"); + if (preg_match("/^(.*)(\.[^\.\/]*?)$/", $conflict->name, $matches)) { + $item_base_name = $matches[1]; // unlike $base_name, this always maintains capitalization + $item_extension = $matches[2]; // includes a leading dot + } else { + $item_base_name = $conflict->name; + $item_extension = ""; + } + // Unlike conflicts found in run_dupe_names, these items are likely to have an intact + // file system. Let's use the item save logic to rebuild the paths and rename the files + // if possible. + try { + $conflict->name = $item_base_name . "-" . (string)rand(1000, 9999) . $item_extension; + $conflict->validate(); + // If we get here, we're safe to proceed with save + $conflict->save(); + } catch (Exception $e) { + // Didn't work. Edit database directly without fixing file system. + db::build() + ->update("items") + ->set("name", $item_base_name . "-" . (string)rand(1000, 9999) . $item_extension) + ->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:$base_name"; + break; + } + $task->set("stack", implode(" ", $stack)); + $completed++; + if (empty($stack)) { $state = self::FIX_STATE_START_ALBUMS; } @@ -669,18 +748,32 @@ class gallery_task_Core { } static function find_dupe_names() { + // looking for photos, movies, and albums return db::build() ->select_distinct( array("parent_name" => db::expr("CONCAT(`parent_id`, ':', LOWER(`name`))"))) ->select("id") ->select(array("C" => "COUNT(\"*\")")) ->from("items") - ->where("type", "<>", "album") ->having("C", ">", 1) ->group_by("parent_name") ->execute(); } + static function find_dupe_base_names() { + // looking for photos or movies, not albums + return db::build() + ->select_distinct( + array("parent_base_name" => db::expr("CONCAT(`parent_id`, ':', LOWER(SUBSTR(`name`, 1, LOCATE('.', `name`) - 1)))"))) + ->select("id") + ->select(array("C" => "COUNT(\"*\")")) + ->from("items") + ->where("type", "<>", "album") + ->having("C", ">", 1) + ->group_by("parent_base_name") + ->execute(); + } + static function find_empty_item_caches($limit) { return db::build() ->select("items.id") diff --git a/modules/gallery/helpers/item.php b/modules/gallery/helpers/item.php index b2c4cadf..386eeb08 100644 --- a/modules/gallery/helpers/item.php +++ b/modules/gallery/helpers/item.php @@ -39,55 +39,28 @@ class item_Core { } } - $source->parent_id = $target->id; - - // Moving may result in name or slug conflicts. If that happens, try up to 5 times to pick a - // random name (or slug) to avoid the conflict. $orig_name = $source->name; - $orig_name_filename = pathinfo($source->name, PATHINFO_FILENAME); - $orig_name_extension = pathinfo($source->name, PATHINFO_EXTENSION); - $orig_slug = $source->slug; - for ($i = 0; $i < 5; $i++) { - try { - $source->save(); - if ($orig_name != $source->name) { - switch ($source->type) { - case "album": - message::info( - t("Album %old_name renamed to %new_name to avoid a conflict", - array("old_name" => $orig_name, "new_name" => $source->name))); - break; - - case "photo": - message::info( - t("Photo %old_name renamed to %new_name to avoid a conflict", - array("old_name" => $orig_name, "new_name" => $source->name))); - break; + $source->parent_id = $target->id; + $source->save(); + if ($orig_name != $source->name) { + switch ($source->type) { + case "album": + message::info( + t("Album %old_name renamed to %new_name to avoid a conflict", + array("old_name" => $orig_name, "new_name" => $source->name))); + break; - case "movie": - message::info( - t("Movie %old_name renamed to %new_name to avoid a conflict", - array("old_name" => $orig_name, "new_name" => $source->name))); - break; - } - } + case "photo": + message::info( + t("Photo %old_name renamed to %new_name to avoid a conflict", + array("old_name" => $orig_name, "new_name" => $source->name))); break; - } catch (ORM_Validation_Exception $e) { - $rand = rand(10, 99); - $errors = $e->validation->errors(); - if (isset($errors["name"])) { - $source->name = $orig_name_filename . "-{$rand}." . $orig_name_extension; - unset($errors["name"]); - } - if (isset($errors["slug"])) { - $source->slug = $orig_slug . "-{$rand}"; - unset($errors["slug"]); - } - if ($errors) { - // There were other validation issues-- we don't know how to handle those - throw $e; - } + case "movie": + message::info( + t("Movie %old_name renamed to %new_name to avoid a conflict", + array("old_name" => $orig_name, "new_name" => $source->name))); + break; } } diff --git a/modules/gallery/models/item.php b/modules/gallery/models/item.php index bbfeee47..e4b997aa 100644 --- a/modules/gallery/models/item.php +++ b/modules/gallery/models/item.php @@ -338,10 +338,11 @@ class Item_Model_Core extends ORM_MPTT { if (empty($this->slug)) { $this->slug = item::convert_filename_to_slug(pathinfo($this->name, PATHINFO_FILENAME)); - // If the filename is all invalid characters, then the slug may be empty here. Pick a - // random value. + // If the filename is all invalid characters, then the slug may be empty here. We set a + // generic name ("photo", "movie", or "album") based on its type, then rely on + // check_and_fix_conflicts to ensure it doesn't conflict with another name. if (empty($this->slug)) { - $this->slug = (string)rand(1000, 9999); + $this->slug = $this->type; } } @@ -362,7 +363,7 @@ class Item_Model_Core extends ORM_MPTT { } } - $this->_randomize_name_or_slug_on_conflict(); + $this->_check_and_fix_conflicts(); parent::save(); @@ -382,16 +383,6 @@ class Item_Model_Core extends ORM_MPTT { case "photo": case "movie": - // The thumb or resize may already exist in the case where a movie and a photo generate - // a thumbnail of the same name (eg, foo.flv movie and foo.jpg photo will generate - // foo.jpg thumbnail). If that happens, randomize and save again. - if (file_exists($this->resize_path()) || - file_exists($this->thumb_path())) { - $pi = pathinfo($this->name); - $this->name = $pi["filename"] . "-" . random::int() . "." . $pi["extension"]; - parent::save(); - } - copy($this->data_file, $this->file_path()); break; } @@ -435,7 +426,7 @@ class Item_Model_Core extends ORM_MPTT { $this->relative_url_cache = null; } - $this->_randomize_name_or_slug_on_conflict(); + $this->_check_and_fix_conflicts(); parent::save(); @@ -528,30 +519,60 @@ class Item_Model_Core extends ORM_MPTT { /** * Check to see if there's another item that occupies the same name or slug that this item - * intends to use, and if so choose a new name/slug while preserving the extension. - * @todo Improve this. Random numbers are not user friendly + * intends to use, and if so choose a new name/slug while preserving the extension. Since this + * checks the name without its extension, it covers possible collisions with thumbs and resizes + * as well (e.g. between the thumbs of movie "foo.flv" and photo "foo.jpg"). */ - private function _randomize_name_or_slug_on_conflict() { - $base_name = pathinfo($this->name, PATHINFO_FILENAME); - $base_ext = pathinfo($this->name, PATHINFO_EXTENSION); - $base_slug = $this->slug; - while (ORM::factory("item") - ->where("parent_id", "=", $this->parent_id) - ->where("id", $this->id ? "<>" : "IS NOT", $this->id) - ->and_open() - ->where("name", "=", $this->name) - ->or_where("slug", "=", $this->slug) - ->close() - ->find()->id) { - $rand = random::int(); - if ($base_ext) { - $this->name = "$base_name-$rand.$base_ext"; + private function _check_and_fix_conflicts() { + $suffix_num = 1; + $suffix = ""; + if ($this->is_album()) { + while (db::build() + ->from("items") + ->where("parent_id", "=", $this->parent_id) + ->where("id", $this->id ? "<>" : "IS NOT", $this->id) + ->and_open() + ->where("name", "=", "{$this->name}{$suffix}") + ->or_where("slug", "=", "{$this->slug}{$suffix}") + ->close() + ->count_records()) { + $suffix = "-" . (($suffix_num <= 99) ? sprintf("%02d", $suffix_num++) : random::int()); + } + if ($suffix) { + $this->name = "{$this->name}{$suffix}"; + $this->slug = "{$this->slug}{$suffix}"; + $this->relative_path_cache = null; + $this->relative_url_cache = null; + } + } else { + // Split the filename into its base and extension. This uses a regexp similar to + // legal_file::change_extension (which isn't always the same as pathinfo). + if (preg_match("/^(.*)(\.[^\.\/]*?)$/", $this->name, $matches)) { + $base_name = $matches[1]; + $extension = $matches[2]; // includes a leading dot } else { - $this->name = "$base_name-$rand"; + $base_name = $this->name; + $extension = ""; + } + $base_name_escaped = Database::escape_for_like($base_name); + // Note: below query uses LIKE with wildcard % at end, which is still sargable (i.e. quick) + while (db::build() + ->from("items") + ->where("parent_id", "=", $this->parent_id) + ->where("id", $this->id ? "<>" : "IS NOT", $this->id) + ->and_open() + ->where("name", "LIKE", "{$base_name_escaped}{$suffix}.%") + ->or_where("slug", "=", "{$this->slug}{$suffix}") + ->close() + ->count_records()) { + $suffix = "-" . (($suffix_num <= 99) ? sprintf("%02d", $suffix_num++) : random::int()); + } + if ($suffix) { + $this->name = "{$base_name}{$suffix}{$extension}"; + $this->slug = "{$this->slug}{$suffix}"; + $this->relative_path_cache = null; + $this->relative_url_cache = null; } - $this->slug = "$base_slug-$rand"; - $this->relative_path_cache = null; - $this->relative_url_cache = null; } } @@ -871,14 +892,32 @@ class Item_Model_Core extends ORM_MPTT { } } - if (db::build() - ->from("items") - ->where("parent_id", "=", $this->parent_id) - ->where("name", "=", $this->name) - ->merge_where($this->id ? array(array("id", "<>", $this->id)) : null) - ->count_records()) { - $v->add_error("name", "conflict"); - return; + if ($this->is_album()) { + if (db::build() + ->from("items") + ->where("parent_id", "=", $this->parent_id) + ->where("name", "=", $this->name) + ->merge_where($this->id ? array(array("id", "<>", $this->id)) : null) + ->count_records()) { + $v->add_error("name", "conflict"); + return; + } + } else { + if (preg_match("/^(.*)(\.[^\.\/]*?)$/", $this->name, $matches)) { + $base_name = $matches[1]; + } else { + $base_name = $this->name; + } + $base_name_escaped = Database::escape_for_like($base_name); + if (db::build() + ->from("items") + ->where("parent_id", "=", $this->parent_id) + ->where("name", "LIKE", "{$base_name_escaped}.%") + ->merge_where($this->id ? array(array("id", "<>", $this->id)) : null) + ->count_records()) { + $v->add_error("name", "conflict"); + return; + } } if ($this->parent_id == 1 && Kohana::auto_load("{$this->slug}_Controller")) { diff --git a/modules/gallery/module.info b/modules/gallery/module.info index 566ca2eb..b6cefe39 100644 --- a/modules/gallery/module.info +++ b/modules/gallery/module.info @@ -1,6 +1,6 @@ name = "Gallery 3" description = "Gallery core application" -version = 53 +version = 54 author_name = "Gallery Team" author_url = "http://codex.galleryproject.org/Gallery:Team" info_url = "http://codex.galleryproject.org/Gallery3:Modules:gallery" diff --git a/modules/gallery/tests/Item_Model_Test.php b/modules/gallery/tests/Item_Model_Test.php index c45bbc3c..41361b32 100644 --- a/modules/gallery/tests/Item_Model_Test.php +++ b/modules/gallery/tests/Item_Model_Test.php @@ -136,19 +136,6 @@ class Item_Model_Test extends Gallery_Unit_Test_Case { $this->assert_true(false, "Shouldn't get here"); } - public function item_rename_over_existing_name_gets_uniqified_test() { - // Create a test photo - $item = test::random_photo(); - $item2 = test::random_photo(); - - $item->name = $item2->name; - $item->save(); - - // foo.jpg should become foo-####.jpg - $this->assert_true( - preg_match("/" . str_replace(".jpg", "", $item2->name) . "-\d+\.jpg/", $item->name)); - } - public function move_album_test() { $album2 = test::random_album(); $album1 = test::random_album($album2); @@ -205,7 +192,7 @@ class Item_Model_Test extends Gallery_Unit_Test_Case { $this->assert_equal($fullsize_file, file_get_contents($photo->file_path())); } - public function move_album_with_conflicting_target_gets_uniqified_test() { + public function move_album_with_conflicting_target_gets_uniquified_test() { $album = test::random_album(); $source = test::random_album_unsaved($album); $source->name = $album->name; @@ -217,9 +204,9 @@ class Item_Model_Test extends Gallery_Unit_Test_Case { $source->parent_id = item::root()->id; $source->save(); - // foo should become foo-#### - $this->assert_true(preg_match("/{$album->name}-\d+/", $source->name)); - $this->assert_true(preg_match("/{$album->slug}-\d+/", $source->slug)); + // foo should become foo-01 + $this->assert_same("{$album->name}-01", $source->name); + $this->assert_same("{$album->slug}-01", $source->slug); } public function move_album_fails_wrong_target_type_test() { @@ -239,7 +226,7 @@ class Item_Model_Test extends Gallery_Unit_Test_Case { $this->assert_true(false, "Shouldn't get here"); } - public function move_photo_with_conflicting_target_gets_uniqified_test() { + public function move_photo_with_conflicting_target_gets_uniquified_test() { $photo1 = test::random_photo(); $album = test::random_album(); $photo2 = test::random_photo_unsaved($album); @@ -247,17 +234,16 @@ class Item_Model_Test extends Gallery_Unit_Test_Case { $photo2->save(); // $photo1 and $photo2 have the same name, so if we move $photo1 into the root they should - // conflict and get uniqified. + // conflict and get uniquified. $photo2->parent_id = item::root()->id; $photo2->save(); - // foo.jpg should become foo-####.jpg - $this->assert_true( - preg_match("/" . str_replace(".jpg", "", $photo1->name) . "-\d+\.jpg/", $photo2->name)); + // foo.jpg should become foo-01.jpg + $this->assert_same(pathinfo($photo1->name, PATHINFO_FILENAME) . "-01.jpg", $photo2->name); - // foo should become foo - $this->assert_true(preg_match("/{$photo1->slug}/", $photo2->name)); + // foo should become foo-01 + $this->assert_same("{$photo1->slug}-01", $photo2->slug); } public function move_album_inside_descendent_fails_test() { @@ -535,4 +521,164 @@ class Item_Model_Test extends Gallery_Unit_Test_Case { $album->name = $album->name . ".foo.bar"; $album->save(); } + + public function no_conflict_when_parents_different_test() { + $parent1 = test::random_album(); + $parent2 = test::random_album(); + $photo1 = test::random_photo($parent1); + $photo2 = test::random_photo($parent2); + + $photo2->name = $photo1->name; + $photo2->slug = $photo1->slug; + $photo2->save(); + + // photo2 has same name and slug as photo1 but different parent - no conflict. + $this->assert_same($photo1->name, $photo2->name); + $this->assert_same($photo1->slug, $photo2->slug); + } + + public function fix_conflict_when_names_identical_test() { + $parent = test::random_album(); + $photo1 = test::random_photo($parent); + $photo2 = test::random_photo($parent); + + $photo1_orig_base = pathinfo($photo1->name, PATHINFO_FILENAME); + $photo2_orig_slug = $photo2->slug; + + $photo2->name = $photo1->name; + $photo2->save(); + + // photo2 has same name as photo1 - conflict resolved by renaming with -01. + $this->assert_same("{$photo1_orig_base}-01.jpg", $photo2->name); + $this->assert_same("{$photo2_orig_slug}-01", $photo2->slug); + } + + public function fix_conflict_when_slugs_identical_test() { + $parent = test::random_album(); + $photo1 = test::random_photo($parent); + $photo2 = test::random_photo($parent); + + $photo2_orig_base = pathinfo($photo2->name, PATHINFO_FILENAME); + + $photo2->slug = $photo1->slug; + $photo2->save(); + + // photo2 has same slug as photo1 - conflict resolved by renaming with -01. + $this->assert_same("{$photo2_orig_base}-01.jpg", $photo2->name); + $this->assert_same("{$photo1->slug}-01", $photo2->slug); + } + + public function no_conflict_when_parents_different_for_albums_test() { + $parent1 = test::random_album(); + $parent2 = test::random_album(); + $album1 = test::random_album($parent1); + $album2 = test::random_album($parent2); + + $album2->name = $album1->name; + $album2->slug = $album1->slug; + $album2->save(); + + // album2 has same name and slug as album1 but different parent - no conflict. + $this->assert_same($album1->name, $album2->name); + $this->assert_same($album1->slug, $album2->slug); + } + + public function fix_conflict_when_names_identical_for_albums_test() { + $parent = test::random_album(); + $album1 = test::random_album($parent); + $album2 = test::random_album($parent); + + $album2_orig_slug = $album2->slug; + + $album2->name = $album1->name; + $album2->save(); + + // album2 has same name as album1 - conflict resolved by renaming with -01. + $this->assert_same("{$album1->name}-01", $album2->name); + $this->assert_same("{$album2_orig_slug}-01", $album2->slug); + } + + public function fix_conflict_when_slugs_identical_for_albums_test() { + $parent = test::random_album(); + $album1 = test::random_album($parent); + $album2 = test::random_album($parent); + + $album2_orig_name = $album2->name; + + $album2->slug = $album1->slug; + $album2->save(); + + // album2 has same slug as album1 - conflict resolved by renaming with -01. + $this->assert_same("{$album2_orig_name}-01", $album2->name); + $this->assert_same("{$album1->slug}-01", $album2->slug); + } + + public function no_conflict_when_base_names_identical_between_album_and_photo_test() { + $parent = test::random_album(); + $album = test::random_album($parent); + $photo = test::random_photo($parent); + + $photo_orig_slug = $photo->slug; + + $photo->name = "{$album->name}.jpg"; + $photo->save(); + + // photo has same base name as album - no conflict. + $this->assert_same("{$album->name}.jpg", $photo->name); + $this->assert_same($photo_orig_slug, $photo->slug); + } + + public function fix_conflict_when_full_names_identical_between_album_and_photo_test() { + $parent = test::random_album(); + $photo = test::random_photo($parent); + $album = test::random_album($parent); + + $album_orig_slug = $album->slug; + + $album->name = $photo->name; + $album->save(); + + // album has same full name as album - conflict resolved by renaming with -01. + $this->assert_same("{$photo->name}-01", $album->name); + $this->assert_same("{$album_orig_slug}-01", $album->slug); + } + + public function fix_conflict_when_slugs_identical_between_album_and_photo_test() { + $parent = test::random_album(); + $album = test::random_album($parent); + $photo = test::random_photo($parent); + + $photo_orig_base = pathinfo($photo->name, PATHINFO_FILENAME); + + $photo->slug = $album->slug; + $photo->save(); + + // photo has same slug as album - conflict resolved by renaming with -01. + $this->assert_same("{$photo_orig_base}-01.jpg", $photo->name); + $this->assert_same("{$album->slug}-01", $photo->slug); + } + + public function fix_conflict_when_base_names_identical_between_jpg_png_flv_test() { + $parent = test::random_album(); + $item1 = test::random_photo($parent); + $item2 = test::random_photo($parent); + $item3 = test::random_movie($parent); + + $item1_orig_base = pathinfo($item1->name, PATHINFO_FILENAME); + $item2_orig_slug = $item2->slug; + $item3_orig_slug = $item3->slug; + + $item2->set_data_file(MODPATH . "gallery/images/graphicsmagick.png"); + $item2->name = "{$item1_orig_base}.png"; + $item2->save(); + + $item3->name = "{$item1_orig_base}.flv"; + $item3->save(); + + // item2 and item3 have same base name as item1 - conflict resolved by renaming with -01 and -02. + $this->assert_same("{$item1_orig_base}-01.png", $item2->name); + $this->assert_same("{$item2_orig_slug}-01", $item2->slug); + $this->assert_same("{$item1_orig_base}-02.flv", $item3->name); + $this->assert_same("{$item3_orig_slug}-02", $item3->slug); + } } -- cgit v1.2.3 From 603db40459fdaebb5917c8c5aef922476700dce2 Mon Sep 17 00:00:00 2001 From: shadlaws Date: Sat, 26 Jan 2013 09:08:03 +0100 Subject: #1966 - "Fix your Gallery" shows 60/59 items completed. - Fixed counter in gallery_task::fix that was often one too many. For FIX_STATE_RUN_MISSING_ACCESS_CACHES, changed this: $stack = explode(" ", $task->get("stack")); To this: $stack = array_filter(explode(" ", $task->get("stack"))); --- modules/gallery/helpers/gallery_task.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/helpers/gallery_task.php b/modules/gallery/helpers/gallery_task.php index 82ca9ffc..8eb981a2 100644 --- a/modules/gallery/helpers/gallery_task.php +++ b/modules/gallery/helpers/gallery_task.php @@ -612,7 +612,7 @@ class gallery_task_Core { break; case self::FIX_STATE_RUN_MISSING_ACCESS_CACHES: - $stack = explode(" ", $task->get("stack")); + $stack = array_filter(explode(" ", $task->get("stack"))); // filter removes empty/zero ids if (!empty($stack)) { $id = array_pop($stack); $access_cache = ORM::factory("access_cache"); -- cgit v1.2.3 From e957c97c947427093d03de3302f4d98909d1ab1a Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Sun, 27 Jan 2013 15:35:42 -0500 Subject: Add a key on relative_path_cache in the items table to improve performance on installs that use File_Proxy heavily. Fixes #1920. --- installer/install.sql | 5 +++-- modules/gallery/helpers/gallery_installer.php | 11 +++++++++-- modules/gallery/module.info | 2 +- 3 files changed, 13 insertions(+), 5 deletions(-) (limited to 'modules/gallery/helpers') diff --git a/installer/install.sql b/installer/install.sql index 70cf0c86..4097d51e 100644 --- a/installer/install.sql +++ b/installer/install.sql @@ -186,7 +186,8 @@ CREATE TABLE {items} ( KEY `type` (`type`), KEY `random` (`rand_key`), KEY `weight` (`weight`), - KEY `left_ptr` (`left_ptr`) + KEY `left_ptr` (`left_ptr`), + KEY `relative_path_cache` (`relative_path_cache`) ) AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; INSERT INTO {items} VALUES (1,NULL,NULL,UNIX_TIMESTAMP(),'',NULL,1,1,NULL,NULL,2,0,NULL,'','',1,NULL,NULL,2,NULL,'weight','ASC',1,NULL,NULL,'Gallery','album',UNIX_TIMESTAMP(),0,1,NULL,'1','1'); @@ -244,7 +245,7 @@ CREATE TABLE {modules} ( KEY `weight` (`weight`) ) AUTO_INCREMENT=10 DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; -INSERT INTO {modules} VALUES (1,1,'gallery',54,1); +INSERT INTO {modules} VALUES (1,1,'gallery',55,1); INSERT INTO {modules} VALUES (2,1,'user',4,2); INSERT INTO {modules} VALUES (3,1,'comment',7,3); INSERT INTO {modules} VALUES (4,1,'organize',4,4); diff --git a/modules/gallery/helpers/gallery_installer.php b/modules/gallery/helpers/gallery_installer.php index 86ff7ce5..d4c4de14 100644 --- a/modules/gallery/helpers/gallery_installer.php +++ b/modules/gallery/helpers/gallery_installer.php @@ -116,7 +116,8 @@ class gallery_installer { KEY `type` (`type`), KEY `random` (`rand_key`), KEY `weight` (`weight` DESC), - KEY `left_ptr` (`left_ptr`)) + KEY `left_ptr` (`left_ptr`), + KEY `relative_path_cache` (`relative_path_cache`)) DEFAULT CHARSET=utf8;"); $db->query("CREATE TABLE {logs} ( @@ -315,7 +316,7 @@ class gallery_installer { module::set_var("gallery", "lock_timeout", 1); module::set_var("gallery", "movie_extract_frame_time", 3); - module::set_version("gallery", 54); + module::set_version("gallery", 55); } static function upgrade($version) { @@ -784,6 +785,12 @@ class gallery_installer { } module::set_version("gallery", $version = 54); } + + if ($version == 54) { + $db->query("ALTER TABLE {items} ADD KEY `relative_path_cache` (`relative_path_cache`)"); + module::set_version("gallery", $version = 55); + } + } static function uninstall() { diff --git a/modules/gallery/module.info b/modules/gallery/module.info index b6cefe39..d79a5077 100644 --- a/modules/gallery/module.info +++ b/modules/gallery/module.info @@ -1,6 +1,6 @@ name = "Gallery 3" description = "Gallery core application" -version = 54 +version = 55 author_name = "Gallery Team" author_url = "http://codex.galleryproject.org/Gallery:Team" info_url = "http://codex.galleryproject.org/Gallery3:Modules:gallery" -- cgit v1.2.3 From 9e20a30d22c48f5aacc09035405dd0added499de Mon Sep 17 00:00:00 2001 From: shadlaws Date: Sun, 27 Jan 2013 21:55:24 +0100 Subject: #1969 - Give graphics events the ability to override the standard process. While graphics_rotate, graphics_resize, and graphics_composite events already exist, they don't have the ability to *override* the standard process. This makes it a bit tricky when you want to replace the standard procedure with another (e.g. use jpegtran to perform lossless jpg rotation). Solution: - make a temp filename. - tell the events to use it as the output file. - if an event makes something, use it and skip the standard process. --- modules/gallery/helpers/gallery_graphics.php | 158 +++++++++++++++------------ 1 file changed, 90 insertions(+), 68 deletions(-) (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/helpers/gallery_graphics.php b/modules/gallery/helpers/gallery_graphics.php index e81d5468..b78bd9a7 100644 --- a/modules/gallery/helpers/gallery_graphics.php +++ b/modules/gallery/helpers/gallery_graphics.php @@ -29,21 +29,28 @@ class gallery_graphics_Core { static function rotate($input_file, $output_file, $options, $item=null) { graphics::init_toolkit(); - module::event("graphics_rotate", $input_file, $output_file, $options, $item); + $temp_file = system::temp_filename("rotate_", pathinfo($output_file, PATHINFO_EXTENSION)); + module::event("graphics_rotate", $input_file, $temp_file, $options, $item); - if (@filesize($input_file) == 0) { - throw new Exception("@todo EMPTY_INPUT_FILE"); - } + if (@filesize($temp_file) > 0) { + // A graphics_rotate event made an image - move it to output_file and use it. + @rename($temp_file, $output_file); + } else { + // No events made an image - proceed with standard process. + if (@filesize($input_file) == 0) { + throw new Exception("@todo EMPTY_INPUT_FILE"); + } - if (!isset($options["degrees"])) { - $options["degrees"] = 0; - } + if (!isset($options["degrees"])) { + $options["degrees"] = 0; + } - // Rotate the image. This also implicitly converts its format if needed. - Image::factory($input_file) - ->quality(module::get_var("gallery", "image_quality")) - ->rotate($options["degrees"]) - ->save($output_file); + // Rotate the image. This also implicitly converts its format if needed. + Image::factory($input_file) + ->quality(module::get_var("gallery", "image_quality")) + ->rotate($options["degrees"]) + ->save($output_file); + } module::event("graphics_rotate_completed", $input_file, $output_file, $options, $item); } @@ -60,39 +67,46 @@ class gallery_graphics_Core { static function resize($input_file, $output_file, $options, $item=null) { graphics::init_toolkit(); - module::event("graphics_resize", $input_file, $output_file, $options, $item); + $temp_file = system::temp_filename("resize_", pathinfo($output_file, PATHINFO_EXTENSION)); + module::event("graphics_resize", $input_file, $temp_file, $options, $item); - if (@filesize($input_file) == 0) { - throw new Exception("@todo EMPTY_INPUT_FILE"); - } + if (@filesize($temp_file) > 0) { + // A graphics_resize event made an image - move it to output_file and use it. + @rename($temp_file, $output_file); + } else { + // No events made an image - proceed with standard process. + if (@filesize($input_file) == 0) { + throw new Exception("@todo EMPTY_INPUT_FILE"); + } - list ($input_width, $input_height, $input_mime, $input_extension) = - photo::get_file_metadata($input_file); - if ($input_width && $input_height && - (empty($options["width"]) || empty($options["height"]) || empty($options["master"]) || - (max($input_width, $input_height) <= min($options["width"], $options["height"])))) { - // Photo dimensions well-defined, but options not well-defined or would upscale the image. - // Do not resize. Check mimes to see if we can copy the file or if we need to convert it. - // (checking mimes avoids needlessly converting jpg to jpeg, etc.) - $output_mime = legal_file::get_photo_types_by_extension(pathinfo($output_file, PATHINFO_EXTENSION)); - if ($input_mime && $output_mime && ($input_mime == $output_mime)) { - // Mimes well-defined and identical - copy input to output - copy($input_file, $output_file); + list ($input_width, $input_height, $input_mime, $input_extension) = + photo::get_file_metadata($input_file); + if ($input_width && $input_height && + (empty($options["width"]) || empty($options["height"]) || empty($options["master"]) || + (max($input_width, $input_height) <= min($options["width"], $options["height"])))) { + // Photo dimensions well-defined, but options not well-defined or would upscale the image. + // Do not resize. Check mimes to see if we can copy the file or if we need to convert it. + // (checking mimes avoids needlessly converting jpg to jpeg, etc.) + $output_mime = legal_file::get_photo_types_by_extension(pathinfo($output_file, PATHINFO_EXTENSION)); + if ($input_mime && $output_mime && ($input_mime == $output_mime)) { + // Mimes well-defined and identical - copy input to output + copy($input_file, $output_file); + } else { + // Mimes not well-defined or not the same - convert input to output + $image = Image::factory($input_file) + ->quality(module::get_var("gallery", "image_quality")) + ->save($output_file); + } } else { - // Mimes not well-defined or not the same - convert input to output + // Resize the image. This also implicitly converts its format if needed. $image = Image::factory($input_file) - ->quality(module::get_var("gallery", "image_quality")) - ->save($output_file); - } - } else { - // Resize the image. This also implicitly converts its format if needed. - $image = Image::factory($input_file) - ->resize($options["width"], $options["height"], $options["master"]) - ->quality(module::get_var("gallery", "image_quality")); - if (graphics::can("sharpen")) { - $image->sharpen(module::get_var("gallery", "image_sharpen")); + ->resize($options["width"], $options["height"], $options["master"]) + ->quality(module::get_var("gallery", "image_quality")); + if (graphics::can("sharpen")) { + $image->sharpen(module::get_var("gallery", "image_sharpen")); + } + $image->save($output_file); } - $image->save($output_file); } module::event("graphics_resize_completed", $input_file, $output_file, $options, $item); @@ -118,35 +132,43 @@ class gallery_graphics_Core { try { graphics::init_toolkit(); - module::event("graphics_composite", $input_file, $output_file, $options, $item); - - list ($width, $height) = photo::get_file_metadata($input_file); - list ($w_width, $w_height) = photo::get_file_metadata($options["file"]); - - $pad = isset($options["padding"]) ? $options["padding"] : 10; - $top = $pad; - $left = $pad; - $y_center = max($height / 2 - $w_height / 2, $pad); - $x_center = max($width / 2 - $w_width / 2, $pad); - $bottom = max($height - $w_height - $pad, $pad); - $right = max($width - $w_width - $pad, $pad); - - switch ($options["position"]) { - case "northwest": $x = $left; $y = $top; break; - case "north": $x = $x_center; $y = $top; break; - case "northeast": $x = $right; $y = $top; break; - case "west": $x = $left; $y = $y_center; break; - case "center": $x = $x_center; $y = $y_center; break; - case "east": $x = $right; $y = $y_center; break; - case "southwest": $x = $left; $y = $bottom; break; - case "south": $x = $x_center; $y = $bottom; break; - case "southeast": $x = $right; $y = $bottom; break; - } + $temp_file = system::temp_filename("composite_", pathinfo($output_file, PATHINFO_EXTENSION)); + module::event("graphics_composite", $input_file, $temp_file, $options, $item); - Image::factory($input_file) - ->composite($options["file"], $x, $y, $options["transparency"]) - ->quality(module::get_var("gallery", "image_quality")) - ->save($output_file); + if (@filesize($temp_file) > 0) { + // A graphics_composite event made an image - move it to output_file and use it. + @rename($temp_file, $output_file); + } else { + // No events made an image - proceed with standard process. + + list ($width, $height) = photo::get_file_metadata($input_file); + list ($w_width, $w_height) = photo::get_file_metadata($options["file"]); + + $pad = isset($options["padding"]) ? $options["padding"] : 10; + $top = $pad; + $left = $pad; + $y_center = max($height / 2 - $w_height / 2, $pad); + $x_center = max($width / 2 - $w_width / 2, $pad); + $bottom = max($height - $w_height - $pad, $pad); + $right = max($width - $w_width - $pad, $pad); + + switch ($options["position"]) { + case "northwest": $x = $left; $y = $top; break; + case "north": $x = $x_center; $y = $top; break; + case "northeast": $x = $right; $y = $top; break; + case "west": $x = $left; $y = $y_center; break; + case "center": $x = $x_center; $y = $y_center; break; + case "east": $x = $right; $y = $y_center; break; + case "southwest": $x = $left; $y = $bottom; break; + case "south": $x = $x_center; $y = $bottom; break; + case "southeast": $x = $right; $y = $bottom; break; + } + + Image::factory($input_file) + ->composite($options["file"], $x, $y, $options["transparency"]) + ->quality(module::get_var("gallery", "image_quality")) + ->save($output_file); + } module::event("graphics_composite_completed", $input_file, $output_file, $options, $item); } catch (ErrorException $e) { -- cgit v1.2.3 From 5fca371a616dba16f955087c4477ee229ee222d0 Mon Sep 17 00:00:00 2001 From: shadlaws Date: Mon, 28 Jan 2013 23:31:18 +0100 Subject: #1945 - Extend legal_file helper functions. - Added get_types_by_extension function, which is a merged version of get...types_by_extension functions (similar to get_extensions). - Added optional extension argument to get...extensions functions similar to get...types_by_extension functions. - Added unit tests. Now, every legal_file function has one. - Restructured helper file to include caches. - Added array_unique to get...types (derived from get...types_by_extension, which can be many-to-one). - Edited server_add, uploader, and item model to use new functionality. --- modules/gallery/controllers/uploader.php | 2 +- modules/gallery/helpers/legal_file.php | 146 +++++++++++++++++------ modules/gallery/models/item.php | 13 +- modules/gallery/tests/Legal_File_Helper_Test.php | 57 +++++++++ modules/server_add/controllers/server_add.php | 8 +- 5 files changed, 176 insertions(+), 50 deletions(-) (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/controllers/uploader.php b/modules/gallery/controllers/uploader.php index b2ec5b55..55c65c95 100644 --- a/modules/gallery/controllers/uploader.php +++ b/modules/gallery/controllers/uploader.php @@ -69,7 +69,7 @@ class Uploader_Controller extends Controller { $path_info = @pathinfo($temp_filename); if (array_key_exists("extension", $path_info) && - in_array(strtolower($path_info["extension"]), legal_file::get_movie_extensions())) { + legal_file::get_movie_extensions($path_info["extension"])) { $item->type = "movie"; $item->save(); log::success("content", t("Added a movie"), diff --git a/modules/gallery/helpers/legal_file.php b/modules/gallery/helpers/legal_file.php index 5768cf14..ab9047c8 100644 --- a/modules/gallery/helpers/legal_file.php +++ b/modules/gallery/helpers/legal_file.php @@ -18,6 +18,13 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ class legal_file_Core { + private static $photo_types_by_extension; + private static $movie_types_by_extension; + private static $photo_extensions; + private static $movie_extensions; + private static $photo_types; + private static $movie_types; + /** * Create a default list of allowed photo MIME types paired with their extensions and then let * modules modify it. This is an ordered map, mapping extensions to their MIME types. @@ -26,21 +33,24 @@ class legal_file_Core { * @param string $extension (opt.) - return MIME of extension; if not given, return complete array */ static function get_photo_types_by_extension($extension=null) { - $types_by_extension_wrapper = new stdClass(); - $types_by_extension_wrapper->types_by_extension = array( - "jpg" => "image/jpeg", "jpeg" => "image/jpeg", "gif" => "image/gif", "png" => "image/png"); - module::event("photo_types_by_extension", $types_by_extension_wrapper); + if (empty(self::$photo_types_by_extension)) { + $types_by_extension_wrapper = new stdClass(); + $types_by_extension_wrapper->types_by_extension = array( + "jpg" => "image/jpeg", "jpeg" => "image/jpeg", "gif" => "image/gif", "png" => "image/png"); + module::event("photo_types_by_extension", $types_by_extension_wrapper); + self::$photo_types_by_extension = $types_by_extension_wrapper->types_by_extension; + } if ($extension) { // return matching MIME type $extension = strtolower($extension); - if (isset($types_by_extension_wrapper->types_by_extension[$extension])) { - return $types_by_extension_wrapper->types_by_extension[$extension]; + if (isset(self::$photo_types_by_extension[$extension])) { + return self::$photo_types_by_extension[$extension]; } else { return null; } } else { // return complete array - return $types_by_extension_wrapper->types_by_extension; + return self::$photo_types_by_extension; } } @@ -52,53 +62,111 @@ class legal_file_Core { * @param string $extension (opt.) - return MIME of extension; if not given, return complete array */ static function get_movie_types_by_extension($extension=null) { - $types_by_extension_wrapper = new stdClass(); - $types_by_extension_wrapper->types_by_extension = array( - "flv" => "video/x-flv", "mp4" => "video/mp4", "m4v" => "video/x-m4v"); - module::event("movie_types_by_extension", $types_by_extension_wrapper); + if (empty(self::$movie_types_by_extension)) { + $types_by_extension_wrapper = new stdClass(); + $types_by_extension_wrapper->types_by_extension = array( + "flv" => "video/x-flv", "mp4" => "video/mp4", "m4v" => "video/x-m4v"); + module::event("movie_types_by_extension", $types_by_extension_wrapper); + self::$movie_types_by_extension = $types_by_extension_wrapper->types_by_extension; + } + if ($extension) { + // return matching MIME type + $extension = strtolower($extension); + if (isset(self::$movie_types_by_extension[$extension])) { + return self::$movie_types_by_extension[$extension]; + } else { + return null; + } + } else { + // return complete array + return self::$movie_types_by_extension; + } + } + + /** + * Create a merged list of all allowed photo and movie MIME types paired with their extensions. + * + * @param string $extension (opt.) - return MIME of extension; if not given, return complete array + */ + static function get_types_by_extension($extension=null) { + $types_by_extension = legal_file::get_photo_types_by_extension(); + if (movie::find_ffmpeg()) { + $types_by_extension = array_merge($types_by_extension, + legal_file::get_movie_types_by_extension()); + } if ($extension) { // return matching MIME type $extension = strtolower($extension); - if (isset($types_by_extension_wrapper->types_by_extension[$extension])) { - return $types_by_extension_wrapper->types_by_extension[$extension]; + if (isset($types_by_extension[$extension])) { + return $types_by_extension[$extension]; } else { return null; } } else { // return complete array - return $types_by_extension_wrapper->types_by_extension; + return $types_by_extension; } } /** * Create a default list of allowed photo extensions and then let modules modify it. + * + * @param string $extension (opt.) - return true if allowed; if not given, return complete array */ - static function get_photo_extensions() { - $extensions_wrapper = new stdClass(); - $extensions_wrapper->extensions = array_keys(legal_file::get_photo_types_by_extension()); - module::event("legal_photo_extensions", $extensions_wrapper); - return $extensions_wrapper->extensions; + static function get_photo_extensions($extension=null) { + if (empty(self::$photo_extensions)) { + $extensions_wrapper = new stdClass(); + $extensions_wrapper->extensions = array_keys(legal_file::get_photo_types_by_extension()); + module::event("legal_photo_extensions", $extensions_wrapper); + self::$photo_extensions = $extensions_wrapper->extensions; + } + if ($extension) { + // return true if in array, false if not + return in_array(strtolower($extension), self::$photo_extensions); + } else { + // return complete array + return self::$photo_extensions; + } } /** * Create a default list of allowed movie extensions and then let modules modify it. + * + * @param string $extension (opt.) - return true if allowed; if not given, return complete array */ - static function get_movie_extensions() { - $extensions_wrapper = new stdClass(); - $extensions_wrapper->extensions = array_keys(legal_file::get_movie_types_by_extension()); - module::event("legal_movie_extensions", $extensions_wrapper); - return $extensions_wrapper->extensions; + static function get_movie_extensions($extension=null) { + if (empty(self::$movie_extensions)) { + $extensions_wrapper = new stdClass(); + $extensions_wrapper->extensions = array_keys(legal_file::get_movie_types_by_extension()); + module::event("legal_movie_extensions", $extensions_wrapper); + self::$movie_extensions = $extensions_wrapper->extensions; + } + if ($extension) { + // return true if in array, false if not + return in_array(strtolower($extension), self::$movie_extensions); + } else { + // return complete array + return self::$movie_extensions; + } } /** * Create a merged list of all allowed photo and movie extensions. + * + * @param string $extension (opt.) - return true if allowed; if not given, return complete array */ - static function get_extensions() { + static function get_extensions($extension=null) { $extensions = legal_file::get_photo_extensions(); if (movie::find_ffmpeg()) { $extensions = array_merge($extensions, legal_file::get_movie_extensions()); } - return $extensions; + if ($extension) { + // return true if in array, false if not + return in_array(strtolower($extension), $extensions); + } else { + // return complete array + return $extensions; + } } /** @@ -119,10 +187,14 @@ class legal_file_Core { * (e.g. flv maps to video/x-flv by default, but video/flv is still legal). */ static function get_photo_types() { - $types_wrapper = new stdClass(); - $types_wrapper->types = array_values(legal_file::get_photo_types_by_extension()); - module::event("legal_photo_types", $types_wrapper); - return $types_wrapper->types; + if (empty(self::$photo_types)) { + $types_wrapper = new stdClass(); + // Need array_unique since types_by_extension can be many-to-one (e.g. jpeg and jpg). + $types_wrapper->types = array_unique(array_values(legal_file::get_photo_types_by_extension())); + module::event("legal_photo_types", $types_wrapper); + self::$photo_types = $types_wrapper->types; + } + return self::$photo_types; } /** @@ -131,11 +203,15 @@ class legal_file_Core { * (e.g. flv maps to video/x-flv by default, but video/flv is still legal). */ static function get_movie_types() { - $types_wrapper = new stdClass(); - $types_wrapper->types = array_values(legal_file::get_movie_types_by_extension()); - $types_wrapper->types[] = "video/flv"; - module::event("legal_movie_types", $types_wrapper); - return $types_wrapper->types; + if (empty(self::$movie_types)) { + $types_wrapper = new stdClass(); + // Need array_unique since types_by_extension can be many-to-one (e.g. jpeg and jpg). + $types_wrapper->types = array_unique(array_values(legal_file::get_movie_types_by_extension())); + $types_wrapper->types[] = "video/flv"; + module::event("legal_movie_types", $types_wrapper); + self::$movie_types = $types_wrapper->types; + } + return self::$movie_types; } /** diff --git a/modules/gallery/models/item.php b/modules/gallery/models/item.php index e4b997aa..60318c26 100644 --- a/modules/gallery/models/item.php +++ b/modules/gallery/models/item.php @@ -879,16 +879,9 @@ class Item_Model_Core extends ORM_MPTT { return; } - if ($this->is_photo()) { - if (!in_array(strtolower($ext), legal_file::get_photo_extensions())) { - $v->add_error("name", "illegal_data_file_extension"); - } - } - - if ($this->is_movie()) { - if (!in_array(strtolower($ext), legal_file::get_movie_extensions())) { - $v->add_error("name", "illegal_data_file_extension"); - } + if ($this->is_photo() && !legal_file::get_photo_extensions($ext) || + $this->is_movie() && !legal_file::get_movie_extensions($ext)) { + $v->add_error("name", "illegal_data_file_extension"); } } diff --git a/modules/gallery/tests/Legal_File_Helper_Test.php b/modules/gallery/tests/Legal_File_Helper_Test.php index 5db99935..84a29a52 100644 --- a/modules/gallery/tests/Legal_File_Helper_Test.php +++ b/modules/gallery/tests/Legal_File_Helper_Test.php @@ -40,6 +40,63 @@ class Legal_File_Helper_Test extends Gallery_Unit_Test_Case { $this->assert_equal(3, count(legal_file::get_movie_types_by_extension())); } + public function get_types_by_extension_test() { + $this->assert_equal("image/jpeg", legal_file::get_types_by_extension("jpg")); // photo + $this->assert_equal("video/x-flv", legal_file::get_types_by_extension("FLV")); // movie + $this->assert_equal(null, legal_file::get_types_by_extension("php")); // invalid + $this->assert_equal(null, legal_file::get_types_by_extension("php.flv")); // invalid w/ . + + // No extension returns full array + $this->assert_equal(7, count(legal_file::get_types_by_extension())); + } + + public function get_photo_extensions_test() { + $this->assert_equal(true, legal_file::get_photo_extensions("jpg")); // regular + $this->assert_equal(true, legal_file::get_photo_extensions("JPG")); // all caps + $this->assert_equal(true, legal_file::get_photo_extensions("Png")); // some caps + $this->assert_equal(false, legal_file::get_photo_extensions("php")); // invalid + $this->assert_equal(false, legal_file::get_photo_extensions("php.jpg")); // invalid w/ . + + // No extension returns full array + $this->assert_equal(4, count(legal_file::get_photo_extensions())); + } + + public function get_movie_extensions_test() { + $this->assert_equal(true, legal_file::get_movie_extensions("flv")); // regular + $this->assert_equal(true, legal_file::get_movie_extensions("FLV")); // all caps + $this->assert_equal(true, legal_file::get_movie_extensions("Mp4")); // some caps + $this->assert_equal(false, legal_file::get_movie_extensions("php")); // invalid + $this->assert_equal(false, legal_file::get_movie_extensions("php.jpg")); // invalid w/ . + + // No extension returns full array + $this->assert_equal(3, count(legal_file::get_movie_extensions())); + } + + public function get_extensions_test() { + $this->assert_equal(true, legal_file::get_extensions("jpg")); // photo + $this->assert_equal(true, legal_file::get_extensions("FLV")); // movie + $this->assert_equal(false, legal_file::get_extensions("php")); // invalid + $this->assert_equal(false, legal_file::get_extensions("php.jpg")); // invalid w/ . + + // No extension returns full array + $this->assert_equal(7, count(legal_file::get_extensions())); + } + + public function get_filters_test() { + // All 7 extensions both uppercase and lowercase + $this->assert_equal(14, count(legal_file::get_filters())); + } + + public function get_photo_types_test() { + // Note that this is one *less* than photo extensions since jpeg and jpg have the same mime. + $this->assert_equal(3, count(legal_file::get_photo_types())); + } + + public function get_movie_types_test() { + // Note that this is one *more* than movie extensions since video/flv is added. + $this->assert_equal(4, count(legal_file::get_movie_types())); + } + public function change_extension_test() { $this->assert_equal("foo.jpg", legal_file::change_extension("foo.png", "jpg")); } diff --git a/modules/server_add/controllers/server_add.php b/modules/server_add/controllers/server_add.php index df55d8db..f6e0a944 100644 --- a/modules/server_add/controllers/server_add.php +++ b/modules/server_add/controllers/server_add.php @@ -61,7 +61,7 @@ class Server_Add_Controller extends Admin_Controller { } if (!is_dir($file)) { $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION)); - if (!in_array($ext, legal_file::get_extensions())) { + if (!legal_file::get_extensions($ext)) { continue; } } @@ -169,7 +169,7 @@ class Server_Add_Controller extends Admin_Controller { foreach ($child_paths as $child_path) { if (!is_dir($child_path)) { $ext = strtolower(pathinfo($child_path, PATHINFO_EXTENSION)); - if (!in_array($ext, legal_file::get_extensions()) || !filesize($child_path)) { + if (!legal_file::get_extensions($ext) || !filesize($child_path)) { // Not importable, skip it. continue; } @@ -255,7 +255,7 @@ class Server_Add_Controller extends Admin_Controller { } else { try { $extension = strtolower(pathinfo($name, PATHINFO_EXTENSION)); - if (in_array($extension, legal_file::get_photo_extensions())) { + if (legal_file::get_photo_extensions($extension)) { $photo = ORM::factory("item"); $photo->type = "photo"; $photo->parent_id = $parent->id; @@ -265,7 +265,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, legal_file::get_movie_extensions())) { + } else if (legal_file::get_movie_extensions($extension)) { $movie = ORM::factory("item"); $movie->type = "movie"; $movie->parent_id = $parent->id; -- cgit v1.2.3 From 536bdaa4db6e950cb4f382697964651b8eab63cd Mon Sep 17 00:00:00 2001 From: shadlaws Date: Tue, 29 Jan 2013 18:35:10 +0100 Subject: #1967 - Improve how graphics::generate handles missing/bad images. - Made missing_photo match the image format (jpg, png, etc.). - Swapped missing_photo.png for missing_photo.jpg since it's likely to require less conversion to match. - Improved error messages to user when things go wrong. - Ensured that missing image placeholders are always copied when there's an error. - Ensured we don't mistake no file output for a correct file output (delete target before attempt). - Restructured graphics::generate a bit to work better with above changes. - Added unit tests for graphics::generate. --- modules/gallery/helpers/graphics.php | 78 ++++++++++++++++++---- modules/gallery/images/missing_photo.jpg | Bin 0 -> 2034 bytes modules/gallery/images/missing_photo.png | Bin 1570 -> 0 bytes modules/gallery/tests/Graphics_Helper_Test.php | 89 +++++++++++++++++++++++++ 4 files changed, 153 insertions(+), 14 deletions(-) create mode 100644 modules/gallery/images/missing_photo.jpg delete mode 100644 modules/gallery/images/missing_photo.png create mode 100644 modules/gallery/tests/Graphics_Helper_Test.php (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/helpers/graphics.php b/modules/gallery/helpers/graphics.php index 0c5f8366..3f5e2d56 100644 --- a/modules/gallery/helpers/graphics.php +++ b/modules/gallery/helpers/graphics.php @@ -154,10 +154,9 @@ class graphics_Core { try { foreach ($ops as $target => $output_file) { + // Delete anything that might already be there + @unlink($output_file); if ($input_item->is_movie()) { - // Convert the movie filename to a JPG first, delete anything that might already be there - $output_file = legal_file::change_extension($output_file, "jpg"); - @unlink($output_file); // Run movie_extract_frame events, which can either: // - generate an output file, bypassing the ffmpeg-based movie::extract_frame // - add to the options sent to movie::extract_frame (e.g. change frame extract time, @@ -173,8 +172,8 @@ class graphics_Core { try { movie::extract_frame($input_file, $output_file, $movie_options_wrapper->movie_options); } catch (Exception $e) { - // Didn't work, likely because of MISSING_FFMPEG - copy missing_movie instead - copy(MODPATH . "gallery/images/missing_movie.jpg", $output_file); + // Didn't work, likely because of MISSING_FFMPEG - use placeholder + graphics::_replace_image_with_placeholder($item, $target); } } $working_file = $output_file; @@ -193,31 +192,82 @@ class graphics_Core { if (file_exists($item->thumb_path())) { $item->thumb_dirty = 0; } else { - copy(MODPATH . "gallery/images/missing_photo.png", $item->thumb_path()); + Kohana_Log::add("error", "Failed to rebuild thumb image: $item->title"); + graphics::_replace_image_with_placeholder($item, "thumb"); } - list ($item->thumb_width, $item->thumb_height) = - photo::get_file_metadata($item->thumb_path()); } if (!empty($ops["resize"])) { if (file_exists($item->resize_path())) { $item->resize_dirty = 0; } else { - copy(MODPATH . "gallery/images/missing_photo.png", $item->resize_path()); + Kohana_Log::add("error", "Failed to rebuild resize image: $item->title"); + graphics::_replace_image_with_placeholder($item, "resize"); } - list ($item->resize_width, $item->resize_height) = - photo::get_file_metadata($item->resize_path()); } + graphics::_update_item_dimensions($item); $item->save(); } catch (Exception $e) { - // Something went wrong rebuilding the image. Leave it dirty and move on. - // @todo we should handle this better. - Kohana_Log::add("error", "Caught exception rebuilding image: {$item->title}\n" . + // Something went wrong rebuilding the image. Replace with the placeholder images, + // leave it dirty and move on. + Kohana_Log::add("error", "Caught exception rebuilding images: {$item->title}\n" . $e->getMessage() . "\n" . $e->getTraceAsString()); + if ($item->is_photo()) { + graphics::_replace_image_with_placeholder($item, "resize"); + } + graphics::_replace_image_with_placeholder($item, "thumb"); + graphics::_update_item_dimensions($item); + $item->save(); throw $e; } } + private static function _update_item_dimensions($item) { + if ($item->is_photo()) { + list ($item->resize_width, $item->resize_height) = + photo::get_file_metadata($item->resize_path()); + } + list ($item->thumb_width, $item->thumb_height) = + photo::get_file_metadata($item->thumb_path()); + } + + private static function _replace_image_with_placeholder($item, $target) { + if ($item->is_movie() || ($item->is_album() && $item->album_cover()->is_movie())) { + $input_path = MODPATH . "gallery/images/missing_movie.jpg"; + } else { + $input_path = MODPATH . "gallery/images/missing_photo.jpg"; + } + + if ($target == "thumb") { + $output_path = $item->thumb_path(); + $size = module::get_var("gallery", "thumb_size", 200); + } else { + $output_path = $item->resize_path(); + $size = module::get_var("gallery", "resize_size", 640); + } + $options = array("width" => $size, "height" => $size, "master" => Image::AUTO); + + try { + // Copy/convert/resize placeholder as needed. + gallery_graphics::resize($input_path, $output_path, $options, null); + } catch (Exception $e) { + // Copy/convert/resize didn't work. Add to the log and copy the jpg version (which could have + // a non-jpg extension). This is less than ideal, but it's better than putting nothing + // there and causing theme views to act strangely because a file is missing. + // @todo we should handle this better. + Kohana_Log::add("error", "Caught exception converting placeholder for missing image: " . + $item->title . "\n" . $e->getMessage() . "\n" . $e->getTraceAsString()); + copy($input_path, $output_path); + } + + if (!file_exists($output_path)) { + // Copy/convert/resize didn't throw an exception, but still didn't work - do the same as above. + // @todo we should handle this better. + Kohana_Log::add("error", "Failed to convert placeholder for missing image: $item->title"); + copy($input_path, $output_path); + } + } + private static function _get_rules($target) { if (empty(self::$_rules_cache[$target])) { $rules = array(); diff --git a/modules/gallery/images/missing_photo.jpg b/modules/gallery/images/missing_photo.jpg new file mode 100644 index 00000000..a9d176d8 Binary files /dev/null and b/modules/gallery/images/missing_photo.jpg differ diff --git a/modules/gallery/images/missing_photo.png b/modules/gallery/images/missing_photo.png deleted file mode 100644 index 67786275..00000000 Binary files a/modules/gallery/images/missing_photo.png and /dev/null differ diff --git a/modules/gallery/tests/Graphics_Helper_Test.php b/modules/gallery/tests/Graphics_Helper_Test.php new file mode 100644 index 00000000..ddcb9dfd --- /dev/null +++ b/modules/gallery/tests/Graphics_Helper_Test.php @@ -0,0 +1,89 @@ +assert_equal(array(640, 480, "image/jpeg", "jpg"), + photo::get_file_metadata($photo->resize_path())); + $this->assert_equal(array(200, 150, "image/jpeg", "jpg"), + photo::get_file_metadata($photo->thumb_path())); + // Check that the items table got updated + $this->assert_equal(array(640, 480), array($photo->resize_width, $photo->resize_height)); + $this->assert_equal(array(200, 150), array($photo->thumb_width, $photo->thumb_height)); + // Check that the images are not marked dirty + $this->assert_equal(0, $photo->resize_dirty); + $this->assert_equal(0, $photo->thumb_dirty); + } + + public function generate_movie_test() { + $movie = test::random_movie(); + // Check that the image was correctly resized + $this->assert_equal(array(200, 160, "image/jpeg", "jpg"), + photo::get_file_metadata($movie->thumb_path())); + // Check that the items table got updated + $this->assert_equal(array(200, 160), array($movie->thumb_width, $movie->thumb_height)); + // Check that the image is not marked dirty + $this->assert_equal(0, $movie->thumb_dirty); + } + + public function generate_bad_photo_test() { + $photo = test::random_photo(); + // At this point, the photo is valid and has a valid resize and thumb. Make it garble. + file_put_contents($photo->file_path(), test::lorem_ipsum(200)); + // Regenerate + $photo->resize_dirty = 1; + $photo->thumb_dirty = 1; + try { + graphics::generate($photo); + $this->assert_true(false, "Shouldn't get here"); + } catch (Exception $e) { + // Exception expected + } + // Check that the images got replaced with missing image placeholders + $this->assert_same(file_get_contents(MODPATH . "gallery/images/missing_photo.jpg"), + file_get_contents($photo->resize_path())); + $this->assert_same(file_get_contents(MODPATH . "gallery/images/missing_photo.jpg"), + file_get_contents($photo->thumb_path())); + // Check that the items table got updated with new metadata + $this->assert_equal(array(200, 200), array($photo->resize_width, $photo->resize_height)); + $this->assert_equal(array(200, 200), array($photo->thumb_width, $photo->thumb_height)); + // Check that the images are marked as dirty + $this->assert_equal(1, $photo->resize_dirty); + $this->assert_equal(1, $photo->thumb_dirty); + } + + public function generate_bad_movie_test() { + // Unlike photos, its ok to have missing movies - no thrown exceptions, thumb_dirty can be reset. + $movie = test::random_movie(); + // At this point, the movie is valid and has a valid thumb. Make it garble. + file_put_contents($movie->file_path(), test::lorem_ipsum(200)); + // Regenerate + $movie->thumb_dirty = 1; + graphics::generate($movie); + // Check that the image got replaced with a missing image placeholder + $this->assert_same(file_get_contents(MODPATH . "gallery/images/missing_movie.jpg"), + file_get_contents($movie->thumb_path())); + // Check that the items table got updated with new metadata + $this->assert_equal(array(200, 200), array($movie->thumb_width, $movie->thumb_height)); + // Check that the image is *not* marked as dirty + $this->assert_equal(0, $movie->thumb_dirty); + } +} \ No newline at end of file -- cgit v1.2.3 From cf077425953a6a492dea97eaf1517e7d08c9648f Mon Sep 17 00:00:00 2001 From: shadlaws Date: Wed, 30 Jan 2013 01:07:36 +0100 Subject: #1968 - Improve album cover generation/removal/etc. - Added stanza to Item_Model::save that handles when cover id is null. - Added logic to graphics::generate to copy/convert album cover thumbs from their item thumbs to ensure they're always jpg, and eliminate the possibility that we copy/convert a dirty thumb. - Redirected other places in code where we want to do one of the above two things to use these two functions instead (gallery_event::item_updated_data_file, item::make_album_cover, item::remove_album_cover). - Improved validation in Item_Model so only albums can have covers and all covers must be non-albums. - Added unit tests to Graphics_Helper_Test. --- modules/gallery/helpers/gallery_event.php | 5 +- modules/gallery/helpers/graphics.php | 78 ++++++++++++++------------ modules/gallery/helpers/item.php | 15 +---- modules/gallery/models/item.php | 15 ++++- modules/gallery/tests/Graphics_Helper_Test.php | 58 +++++++++++++++++++ 5 files changed, 117 insertions(+), 54 deletions(-) (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/helpers/gallery_event.php b/modules/gallery/helpers/gallery_event.php index bd9acc1c..ee3c51cc 100644 --- a/modules/gallery/helpers/gallery_event.php +++ b/modules/gallery/helpers/gallery_event.php @@ -141,10 +141,9 @@ class gallery_event_Core { 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->thumb_dirty = 1; $target->save(); + graphics::generate($target); } } diff --git a/modules/gallery/helpers/graphics.php b/modules/gallery/helpers/graphics.php index 3f5e2d56..19ae1036 100644 --- a/modules/gallery/helpers/graphics.php +++ b/modules/gallery/helpers/graphics.php @@ -115,36 +115,12 @@ class graphics_Core { * @param Item_Model $item */ static function generate($item) { - if ($item->is_album()) { - if (!$cover = $item->album_cover()) { - // 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://galleryproject.org/node/96926 - if ($item->album_cover_item_id) { - $item->album_cover_item_id = null; - $item->save(); - } - return; - } - $input_file = $cover->file_path(); - $input_item = $cover; - } else { - $input_file = $item->file_path(); - $input_item = $item; - } - if ($item->thumb_dirty) { $ops["thumb"] = $item->thumb_path(); } - if ($item->resize_dirty && !$item->is_album() && !$item->is_movie()) { + if ($item->resize_dirty && $item->is_photo()) { $ops["resize"] = $item->resize_path(); } - if (empty($ops)) { $item->thumb_dirty = 0; $item->resize_dirty = 0; @@ -154,9 +130,11 @@ class graphics_Core { try { foreach ($ops as $target => $output_file) { + $working_file = $item->file_path(); // Delete anything that might already be there @unlink($output_file); - if ($input_item->is_movie()) { + switch ($item->type) { + case "movie": // Run movie_extract_frame events, which can either: // - generate an output file, bypassing the ffmpeg-based movie::extract_frame // - add to the options sent to movie::extract_frame (e.g. change frame extract time, @@ -164,27 +142,55 @@ class graphics_Core { // Note that the args are similar to those of the events in gallery_graphics $movie_options_wrapper = new stdClass(); $movie_options_wrapper->movie_options = array(); - module::event("movie_extract_frame", $input_file, $output_file, - $movie_options_wrapper, $input_item); + module::event("movie_extract_frame", $working_file, $output_file, + $movie_options_wrapper, $item); // If no output_file generated by events, run movie::extract_frame with movie_options clearstatcache(); if (@filesize($output_file) == 0) { try { - movie::extract_frame($input_file, $output_file, $movie_options_wrapper->movie_options); + movie::extract_frame($working_file, $output_file, $movie_options_wrapper->movie_options); } catch (Exception $e) { // Didn't work, likely because of MISSING_FFMPEG - use placeholder graphics::_replace_image_with_placeholder($item, $target); } } $working_file = $output_file; - } else { - $working_file = $input_file; - } - foreach (self::_get_rules($target) as $rule) { - $args = array($working_file, $output_file, unserialize($rule->args), $item); - call_user_func_array($rule->operation, $args); - $working_file = $output_file; + case "photo": + // Run the graphics rules (for both movies and photos) + foreach (self::_get_rules($target) as $rule) { + $args = array($working_file, $output_file, unserialize($rule->args), $item); + call_user_func_array($rule->operation, $args); + $working_file = $output_file; + } + break; + + case "album": + if (!$cover = $item->album_cover()) { + // 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://galleryproject.org/node/96926 + if ($item->album_cover_item_id) { + $item->album_cover_item_id = null; + $item->save(); + } + return; + } + if ($cover->thumb_dirty) { + graphics::generate($cover); + } + if (!$cover->thumb_dirty) { + // Make the album cover from the cover item's thumb. Run gallery_graphics::resize with + // null options and it will figure out if this is a direct copy or conversion to jpg. + $working_file = $cover->thumb_path(); + gallery_graphics::resize($working_file, $output_file, null, $item); + } + break; } } diff --git a/modules/gallery/helpers/item.php b/modules/gallery/helpers/item.php index 386eeb08..093feb2d 100644 --- a/modules/gallery/helpers/item.php +++ b/modules/gallery/helpers/item.php @@ -78,15 +78,9 @@ class item_Core { model_cache::clear(); $parent->album_cover_item_id = $item->is_album() ? $item->album_cover_item_id : $item->id; - 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) { @@ -97,15 +91,10 @@ class item_Core { static function remove_album_cover($album) { access::required("view", $album); access::required("edit", $album); - @unlink($album->thumb_path()); model_cache::clear(); $album->album_cover_item_id = null; - $album->thumb_width = 0; - $album->thumb_height = 0; - $album->thumb_dirty = 1; $album->save(); - graphics::generate($album); } /** diff --git a/modules/gallery/models/item.php b/modules/gallery/models/item.php index 60318c26..f9edd3c6 100644 --- a/modules/gallery/models/item.php +++ b/modules/gallery/models/item.php @@ -420,6 +420,15 @@ class Item_Model_Core extends ORM_MPTT { } } + // If an album's cover has changed (or been removed), delete any existing album cover, + // reset the thumb metadata, and mark the thumb as dirty. + if (array_key_exists("album_cover_item_id", $this->changed) && $this->is_album()) { + @unlink($original->thumb_path()); + $this->thumb_dirty = 1; + $this->thumb_height = 0; + $this->thumb_width = 0; + } + if (array_intersect($this->changed, array("parent_id", "name", "slug"))) { $original->_build_relative_caches(); $this->relative_path_cache = null; @@ -966,10 +975,12 @@ class Item_Model_Core extends ORM_MPTT { return; } - if ($this->album_cover_item_id && db::build() + if ($this->album_cover_item_id && ($this->is_photo() || $this->is_movie() || + db::build() ->from("items") ->where("id", "=", $this->album_cover_item_id) - ->count_records() != 1) { + ->where("type", "<>", "album") + ->count_records() != 1)) { $v->add_error("album_cover_item_id", "invalid_item"); } } diff --git a/modules/gallery/tests/Graphics_Helper_Test.php b/modules/gallery/tests/Graphics_Helper_Test.php index ddcb9dfd..a68822b0 100644 --- a/modules/gallery/tests/Graphics_Helper_Test.php +++ b/modules/gallery/tests/Graphics_Helper_Test.php @@ -44,6 +44,39 @@ class Graphics_Helper_Test extends Gallery_Unit_Test_Case { $this->assert_equal(0, $movie->thumb_dirty); } + public function generate_album_cover_test() { + $album = test::random_album(); + $photo = test::random_unique_photo($album); + $album->reload(); + // Check that the image was copied directly from item thumb + $this->assert_equal(file_get_contents($photo->thumb_path()), + file_get_contents($album->thumb_path())); + // Check that the items table got updated + $this->assert_equal(array(200, 150), array($album->thumb_width, $album->thumb_height)); + // Check that the image is not marked dirty + $this->assert_equal(0, $album->thumb_dirty); + } + + public function generate_album_cover_from_png_test() { + $input_file = MODPATH . "gallery/tests/test.jpg"; + $output_file = TMPPATH . test::random_name() . ".png"; + gallery_graphics::resize($input_file, $output_file, null, null); + + $album = test::random_album(); + $photo = test::random_photo_unsaved($album); + $photo->set_data_file($output_file); + $photo->name = "album_cover_from_png.png"; + $photo->save(); + $album->reload(); + // Check that the image was correctly resized and converted to jpg + $this->assert_equal(array(200, 150, "image/jpeg", "jpg"), + photo::get_file_metadata($album->thumb_path())); + // Check that the items table got updated + $this->assert_equal(array(200, 150), array($album->thumb_width, $album->thumb_height)); + // Check that the image is not marked dirty + $this->assert_equal(0, $album->thumb_dirty); + } + public function generate_bad_photo_test() { $photo = test::random_photo(); // At this point, the photo is valid and has a valid resize and thumb. Make it garble. @@ -86,4 +119,29 @@ class Graphics_Helper_Test extends Gallery_Unit_Test_Case { // Check that the image is *not* marked as dirty $this->assert_equal(0, $movie->thumb_dirty); } + + public function generate_album_cover_from_bad_photo_test() { + $album = test::random_album(); + $photo = test::random_photo($album); + $album->reload(); + // At this point, the photo is valid and has a valid resize and thumb. Make it garble. + file_put_contents($photo->file_path(), test::lorem_ipsum(200)); + // Regenerate album from garbled photo. + $photo->thumb_dirty = 1; + $photo->save(); + $album->thumb_dirty = 1; + try { + graphics::generate($album); + $this->assert_true(false, "Shouldn't get here"); + } catch (Exception $e) { + // Exception expected + } + // Check that the image got replaced with a missing image placeholder + $this->assert_same(file_get_contents(MODPATH . "gallery/images/missing_photo.jpg"), + file_get_contents($album->thumb_path())); + // Check that the items table got updated with new metadata + $this->assert_equal(array(200, 200), array($album->thumb_width, $album->thumb_height)); + // Check that the images are marked as dirty + $this->assert_equal(1, $album->thumb_dirty); + } } \ No newline at end of file -- cgit v1.2.3 From cff1e76e8da2055f9faf7449222b43a686014b1c Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Wed, 30 Jan 2013 21:08:36 -0500 Subject: When changing the album cover, find and retarget any other albums which were using the old item as their album cover. Fixes #1978. --- modules/gallery/helpers/item.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/helpers/item.php b/modules/gallery/helpers/item.php index 093feb2d..8d3b4354 100644 --- a/modules/gallery/helpers/item.php +++ b/modules/gallery/helpers/item.php @@ -76,16 +76,35 @@ class item_Core { access::required("view", $parent); access::required("edit", $parent); + $old_album_cover_id = $parent->album_cover_item_id; + model_cache::clear(); $parent->album_cover_item_id = $item->is_album() ? $item->album_cover_item_id : $item->id; $parent->save(); graphics::generate($parent); + // Walk up the parent hierarchy and set album covers if necessary $grand_parent = $parent->parent(); if ($grand_parent && access::can("edit", $grand_parent) && $grand_parent->album_cover_item_id == null) { item::make_album_cover($parent); } + + // When albums are album covers themselves, we hotlink directly to the target item. This + // means that when we change an album cover, the grandparent may have a deep link to the old + // album cover. So find any albums that had the old item as their album cover and switch them + // over to the new item. + if ($old_album_cover_id) { + foreach (ORM::factory("item") + ->where("album_cover_item_id", "=", $old_album_cover_id) + ->find_all() as $other_album) { + if (access::can("edit", $other_album)) { + $other_album->album_cover_item_id = $parent->album_cover_item_id; + $other_album->save(); + graphics::generate($other_album); + } + } + } } static function remove_album_cover($album) { -- cgit v1.2.3 From 075b95f0ed6f843c64756c772011a1ecb406168f Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Wed, 30 Jan 2013 21:18:26 -0500 Subject: Actually disable the "make album cover" option when the item is already the album cover. Fixes #1979. --- modules/gallery/helpers/gallery_event.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/helpers/gallery_event.php b/modules/gallery/helpers/gallery_event.php index ee3c51cc..4bbeccc2 100644 --- a/modules/gallery/helpers/gallery_event.php +++ b/modules/gallery/helpers/gallery_event.php @@ -346,9 +346,9 @@ class gallery_event_Core { if (($item->type == "album" && empty($item->album_cover_item_id)) || ($item->type == "album" && $parent->album_cover_item_id == $item->album_cover_item_id) || $parent->album_cover_item_id == $item->id) { - $disabledState = " ui-state-disabled"; + $disabledState = "ui-state-disabled"; } else { - $disabledState = " "; + $disabledState = ""; } if ($item->parent()->id != 1) { @@ -357,7 +357,7 @@ class gallery_event_Core { Menu::factory("ajax_link") ->id("make_album_cover") ->label(t("Choose as the album cover")) - ->css_class("ui-icon-star") + ->css_class("ui-icon-star $disabledState") ->ajax_handler("function(data) { window.location.reload() }") ->url(url::site("quick/make_album_cover/$item->id?csrf=$csrf"))); } @@ -500,16 +500,16 @@ class gallery_event_Core { if (($item->type == "album" && empty($item->album_cover_item_id)) || ($item->type == "album" && $parent->album_cover_item_id == $item->album_cover_item_id) || $parent->album_cover_item_id == $item->id) { - $disabledState = " ui-state-disabled"; + $disabledState = "ui-state-disabled"; } else { - $disabledState = " "; + $disabledState = ""; } if ($item->parent()->id != 1) { $options_menu ->append(Menu::factory("ajax_link") ->id("make_album_cover") ->label($cover_title) - ->css_class("ui-icon-star") + ->css_class("ui-icon-star $disabledState") ->ajax_handler("function(data) { window.location.reload() }") ->url(url::site("quick/make_album_cover/$item->id?csrf=$csrf"))); } @@ -518,7 +518,8 @@ 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()) { -- cgit v1.2.3 From 8d15e5cb2eed56bbd894ff5578d081deee8ca255 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Wed, 30 Jan 2013 21:42:47 -0500 Subject: Follow-in to cff1e76e8da2055f9faf7449222b43a686014b1c for #1978 Restrict which album cover ids we swap over to the hierarchy of the current album, otherwise we can wind up in sticky situations with hierarchical album cover chains. Eg, you have a hierarchy like this: root -> A1 -> A2 --> A3 -> P1 A4 -> P2 P1 is the album cover for its entire hierarchy. But then you swap A2's album cover for A3 making this: root -> A1 -> A2 + A3 -> P1 \-> A4 -> P2 Since A1, A2 and A3 all had P1 as their album cover item id. Now we're swapping it over to P2 but we want to leave P1 as A3's album cover item id. So only look at A4's hierarchy and ignore its peers. --- modules/gallery/helpers/item.php | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/helpers/item.php b/modules/gallery/helpers/item.php index 8d3b4354..975d46e5 100644 --- a/modules/gallery/helpers/item.php +++ b/modules/gallery/helpers/item.php @@ -92,16 +92,15 @@ class item_Core { // When albums are album covers themselves, we hotlink directly to the target item. This // means that when we change an album cover, the grandparent may have a deep link to the old - // album cover. So find any albums that had the old item as their album cover and switch them - // over to the new item. + // album cover. So find any parent albums that had the old item as their album cover and + // switch them over to the new item. if ($old_album_cover_id) { - foreach (ORM::factory("item") - ->where("album_cover_item_id", "=", $old_album_cover_id) - ->find_all() as $other_album) { - if (access::can("edit", $other_album)) { - $other_album->album_cover_item_id = $parent->album_cover_item_id; - $other_album->save(); - graphics::generate($other_album); + foreach ($item->parents(array(array("album_cover_item_id", "=", $old_album_cover_id))) + as $ancestor) { + if (access::can("edit", $ancestor)) { + $ancestor->album_cover_item_id = $parent->album_cover_item_id; + $ancestor->save(); + graphics::generate($ancestor); } } } -- cgit v1.2.3 From f83ed5f8716663a45c9d8e8118bbcf0e2849c3fb Mon Sep 17 00:00:00 2001 From: shadlaws Date: Thu, 31 Jan 2013 17:18:39 +0100 Subject: #1982 - Add placeholder for albums with no album cover. - Added missing_album_cover.jpg placeholder image. - Modified the graphics helper to use it. Calling graphics::generate will copy it. - Modified item::remove_album_cover and gallery_event::item_created to run graphics::generate. - Added unit test to Graphics_Helper_Test. --- modules/gallery/helpers/gallery_event.php | 20 ++++++++++---------- modules/gallery/helpers/graphics.php | 10 +++++++--- modules/gallery/helpers/item.php | 1 + modules/gallery/images/missing_album_cover.jpg | Bin 0 -> 4453 bytes modules/gallery/tests/Graphics_Helper_Test.php | 11 +++++++++++ 5 files changed, 29 insertions(+), 13 deletions(-) create mode 100644 modules/gallery/images/missing_album_cover.jpg (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/helpers/gallery_event.php b/modules/gallery/helpers/gallery_event.php index 4bbeccc2..aeb1c7eb 100644 --- a/modules/gallery/helpers/gallery_event.php +++ b/modules/gallery/helpers/gallery_event.php @@ -86,17 +86,17 @@ class gallery_event_Core { static function item_created($item) { access::add_item($item); - if ($item->is_photo() || $item->is_movie()) { - // Build our thumbnail/resizes. - try { - graphics::generate($item); - } catch (Exception $e) { - log::error("graphics", t("Couldn't create a thumbnail or resize for %item_title", - array("item_title" => $item->title)), - html::anchor($item->abs_url(), t("details"))); - Kohana_Log::add("error", $e->getMessage() . "\n" . $e->getTraceAsString()); - } + // Build our thumbnail/resizes. + try { + graphics::generate($item); + } catch (Exception $e) { + log::error("graphics", t("Couldn't create a thumbnail or resize for %item_title", + array("item_title" => $item->title)), + html::anchor($item->abs_url(), t("details"))); + Kohana_Log::add("error", $e->getMessage() . "\n" . $e->getTraceAsString()); + } + if ($item->is_photo() || $item->is_movie()) { // If the parent has no cover item, make this it. $parent = $item->parent(); if (access::can("edit", $parent) && $parent->album_cover_item_id == null) { diff --git a/modules/gallery/helpers/graphics.php b/modules/gallery/helpers/graphics.php index 19ae1036..7c8e89d5 100644 --- a/modules/gallery/helpers/graphics.php +++ b/modules/gallery/helpers/graphics.php @@ -152,6 +152,7 @@ class graphics_Core { } catch (Exception $e) { // Didn't work, likely because of MISSING_FFMPEG - use placeholder graphics::_replace_image_with_placeholder($item, $target); + break; } } $working_file = $output_file; @@ -167,7 +168,7 @@ class graphics_Core { case "album": if (!$cover = $item->album_cover()) { - // This album has no cover; there's nothing to generate. Because of an old bug, it's + // This album has no cover; copy its placeholder image. 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 @@ -179,7 +180,8 @@ class graphics_Core { $item->album_cover_item_id = null; $item->save(); } - return; + graphics::_replace_image_with_placeholder($item, $target); + break; } if ($cover->thumb_dirty) { graphics::generate($cover); @@ -238,7 +240,9 @@ class graphics_Core { } private static function _replace_image_with_placeholder($item, $target) { - if ($item->is_movie() || ($item->is_album() && $item->album_cover()->is_movie())) { + if ($item->is_album() && !$item->album_cover_item_id) { + $input_path = MODPATH . "gallery/images/missing_album_cover.jpg"; + } else if ($item->is_movie() || ($item->is_album() && $item->album_cover()->is_movie())) { $input_path = MODPATH . "gallery/images/missing_movie.jpg"; } else { $input_path = MODPATH . "gallery/images/missing_photo.jpg"; diff --git a/modules/gallery/helpers/item.php b/modules/gallery/helpers/item.php index 975d46e5..9882a9c5 100644 --- a/modules/gallery/helpers/item.php +++ b/modules/gallery/helpers/item.php @@ -113,6 +113,7 @@ class item_Core { model_cache::clear(); $album->album_cover_item_id = null; $album->save(); + graphics::generate($album); } /** diff --git a/modules/gallery/images/missing_album_cover.jpg b/modules/gallery/images/missing_album_cover.jpg new file mode 100644 index 00000000..bdddeec5 Binary files /dev/null and b/modules/gallery/images/missing_album_cover.jpg differ diff --git a/modules/gallery/tests/Graphics_Helper_Test.php b/modules/gallery/tests/Graphics_Helper_Test.php index a68822b0..2cf5caa7 100644 --- a/modules/gallery/tests/Graphics_Helper_Test.php +++ b/modules/gallery/tests/Graphics_Helper_Test.php @@ -77,6 +77,17 @@ class Graphics_Helper_Test extends Gallery_Unit_Test_Case { $this->assert_equal(0, $album->thumb_dirty); } + public function generate_album_cover_for_empty_album_test() { + $album = test::random_album(); + // Check that the album cover is the missing image placeholder + $this->assert_same(file_get_contents(MODPATH . "gallery/images/missing_album_cover.jpg"), + file_get_contents($album->thumb_path())); + // Check that the items table got updated with new metadata + $this->assert_equal(array(200, 200), array($album->thumb_width, $album->thumb_height)); + // Check that the image is *not* marked as dirty + $this->assert_equal(0, $album->thumb_dirty); + } + public function generate_bad_photo_test() { $photo = test::random_photo(); // At this point, the photo is valid and has a valid resize and thumb. Make it garble. -- cgit v1.2.3 From 93963422505ecc790af62ae0503f301145debac3 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Thu, 31 Jan 2013 19:55:53 -0500 Subject: Drop the requirement to have the install() function set the module version. It's redundant. Fixes #1985. --- modules/comment/helpers/comment_installer.php | 1 - modules/digibug/helpers/digibug_installer.php | 1 - modules/exif/helpers/exif_installer.php | 1 - modules/g2_import/helpers/g2_import_installer.php | 1 - modules/gallery/helpers/gallery_installer.php | 2 -- modules/gallery/helpers/module.php | 3 +-- modules/image_block/helpers/image_block_installer.php | 1 - modules/info/helpers/info_installer.php | 1 - modules/notification/helpers/notification_installer.php | 2 -- modules/rest/helpers/rest_installer.php | 1 - modules/search/helpers/search_installer.php | 1 - modules/server_add/helpers/server_add_installer.php | 1 - modules/slideshow/helpers/slideshow_installer.php | 1 - modules/tag/helpers/tag_installer.php | 1 - modules/user/helpers/user_installer.php | 1 - modules/watermark/helpers/watermark_installer.php | 1 - 16 files changed, 1 insertion(+), 19 deletions(-) (limited to 'modules/gallery/helpers') diff --git a/modules/comment/helpers/comment_installer.php b/modules/comment/helpers/comment_installer.php index 6dbd31cf..136f96ef 100644 --- a/modules/comment/helpers/comment_installer.php +++ b/modules/comment/helpers/comment_installer.php @@ -49,7 +49,6 @@ class comment_installer { module::set_var("comment", "spam_caught", 0); module::set_var("comment", "access_permissions", "everybody"); module::set_var("comment", "rss_visible", "all"); - module::set_version("comment", 7); } static function upgrade($version) { diff --git a/modules/digibug/helpers/digibug_installer.php b/modules/digibug/helpers/digibug_installer.php index b2e529d7..be88b5ec 100644 --- a/modules/digibug/helpers/digibug_installer.php +++ b/modules/digibug/helpers/digibug_installer.php @@ -30,7 +30,6 @@ class digibug_installer { module::set_var("digibug", "company_id", "3153"); module::set_var("digibug", "event_id", "8491"); - module::set_version("digibug", 2); } static function upgrade($version) { diff --git a/modules/exif/helpers/exif_installer.php b/modules/exif/helpers/exif_installer.php index f4c2aa3b..75d0f835 100644 --- a/modules/exif/helpers/exif_installer.php +++ b/modules/exif/helpers/exif_installer.php @@ -29,7 +29,6 @@ class exif_installer { PRIMARY KEY (`id`), KEY(`item_id`)) DEFAULT CHARSET=utf8;"); - module::set_version("exif", 1); } static function activate() { diff --git a/modules/g2_import/helpers/g2_import_installer.php b/modules/g2_import/helpers/g2_import_installer.php index b0c14425..c7569819 100644 --- a/modules/g2_import/helpers/g2_import_installer.php +++ b/modules/g2_import/helpers/g2_import_installer.php @@ -31,7 +31,6 @@ class g2_import_installer { KEY `g2_id` (`g2_id`)) DEFAULT CHARSET=utf8;"); - module::set_version("g2_import", 2); mkdir(VARPATH . "modules/g2_import"); } diff --git a/modules/gallery/helpers/gallery_installer.php b/modules/gallery/helpers/gallery_installer.php index d4c4de14..7f10cdee 100644 --- a/modules/gallery/helpers/gallery_installer.php +++ b/modules/gallery/helpers/gallery_installer.php @@ -315,8 +315,6 @@ class gallery_installer { module::set_var("gallery", "timezone", null); module::set_var("gallery", "lock_timeout", 1); module::set_var("gallery", "movie_extract_frame_time", 3); - - module::set_version("gallery", 55); } static function upgrade($version) { diff --git a/modules/gallery/helpers/module.php b/modules/gallery/helpers/module.php index f4ab5571..df258e87 100644 --- a/modules/gallery/helpers/module.php +++ b/modules/gallery/helpers/module.php @@ -175,9 +175,8 @@ class module_Core { $installer_class = "{$module_name}_installer"; if (method_exists($installer_class, "install")) { call_user_func_array(array($installer_class, "install"), array()); - } else { - module::set_version($module_name, module::available()->$module_name->code_version); } + module::set_version($module_name, module::available()->$module_name->code_version); // Set the weight of the new module, which controls the order in which the modules are // loaded. By default, new modules are installed at the end of the priority list. Since the diff --git a/modules/image_block/helpers/image_block_installer.php b/modules/image_block/helpers/image_block_installer.php index 8558fe51..b177b971 100644 --- a/modules/image_block/helpers/image_block_installer.php +++ b/modules/image_block/helpers/image_block_installer.php @@ -21,7 +21,6 @@ class image_block_installer { static function install() { module::set_var("image_block", "image_count", "1"); - module::set_version("image_block", $version = 3); } static function upgrade($version) { diff --git a/modules/info/helpers/info_installer.php b/modules/info/helpers/info_installer.php index 560af15c..43c216dc 100644 --- a/modules/info/helpers/info_installer.php +++ b/modules/info/helpers/info_installer.php @@ -25,7 +25,6 @@ class info_installer { module::set_var("info", "show_owner", 1); module::set_var("info", "show_name", 1); module::set_var("info", "show_captured", 1); - module::set_version("info", 2); } static function upgrade($version) { diff --git a/modules/notification/helpers/notification_installer.php b/modules/notification/helpers/notification_installer.php index 58435641..f6b05c18 100644 --- a/modules/notification/helpers/notification_installer.php +++ b/modules/notification/helpers/notification_installer.php @@ -36,8 +36,6 @@ class notification_installer { `text` text, PRIMARY KEY (`id`)) DEFAULT CHARSET=utf8;"); - - module::set_version("notification", 2); } static function upgrade($version) { diff --git a/modules/rest/helpers/rest_installer.php b/modules/rest/helpers/rest_installer.php index df7484fe..96f8acfa 100644 --- a/modules/rest/helpers/rest_installer.php +++ b/modules/rest/helpers/rest_installer.php @@ -29,7 +29,6 @@ class rest_installer { UNIQUE KEY(`user_id`)) DEFAULT CHARSET=utf8;"); module::set_var("rest", "allow_guest_access", false); - module::set_version("rest", 3); } static function upgrade($version) { diff --git a/modules/search/helpers/search_installer.php b/modules/search/helpers/search_installer.php index 78dbce38..c9e8f26c 100644 --- a/modules/search/helpers/search_installer.php +++ b/modules/search/helpers/search_installer.php @@ -30,7 +30,6 @@ class search_installer { FULLTEXT INDEX (`data`)) ENGINE=MyISAM DEFAULT CHARSET=utf8;"); - module::set_version("search", 1); } static function activate() { diff --git a/modules/server_add/helpers/server_add_installer.php b/modules/server_add/helpers/server_add_installer.php index e843fc79..b62bbcfa 100644 --- a/modules/server_add/helpers/server_add_installer.php +++ b/modules/server_add/helpers/server_add_installer.php @@ -30,7 +30,6 @@ class server_add_installer { `task_id` int(9) NOT NULL, PRIMARY KEY (`id`)) DEFAULT CHARSET=utf8;"); - module::set_version("server_add", 4); server_add::check_config(); } diff --git a/modules/slideshow/helpers/slideshow_installer.php b/modules/slideshow/helpers/slideshow_installer.php index d283487d..22bd9534 100644 --- a/modules/slideshow/helpers/slideshow_installer.php +++ b/modules/slideshow/helpers/slideshow_installer.php @@ -20,7 +20,6 @@ class slideshow_installer { static function install() { module::set_var("slideshow", "max_scale", 0); - module::set_version("slideshow", 2); } static function upgrade($version) { diff --git a/modules/tag/helpers/tag_installer.php b/modules/tag/helpers/tag_installer.php index f80a9de3..1fd18f3e 100644 --- a/modules/tag/helpers/tag_installer.php +++ b/modules/tag/helpers/tag_installer.php @@ -37,7 +37,6 @@ class tag_installer { KEY(`item_id`, `id`)) DEFAULT CHARSET=utf8;"); module::set_var("tag", "tag_cloud_size", 30); - module::set_version("tag", 3); } static function upgrade($version) { diff --git a/modules/user/helpers/user_installer.php b/modules/user/helpers/user_installer.php index e28af69a..67f6a3d5 100644 --- a/modules/user/helpers/user_installer.php +++ b/modules/user/helpers/user_installer.php @@ -138,6 +138,5 @@ class user_installer { access::allow($registered, "view_full", $root); module::set_var("user", "minimum_password_length", 5); - module::set_version("user", 4); } } \ No newline at end of file diff --git a/modules/watermark/helpers/watermark_installer.php b/modules/watermark/helpers/watermark_installer.php index 5df780a1..13338912 100644 --- a/modules/watermark/helpers/watermark_installer.php +++ b/modules/watermark/helpers/watermark_installer.php @@ -33,7 +33,6 @@ class watermark_installer { DEFAULT CHARSET=utf8;"); @mkdir(VARPATH . "modules/watermark"); - module::set_version("watermark", 2); } static function uninstall() { -- cgit v1.2.3 From dece6dc5a5880c6267431ba3299c5758b38662ee Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Sat, 2 Feb 2013 23:39:16 -0500 Subject: Create gallery::allow_css_and_js_combining() which lets you disable combining CSS/JS by touching var/DONT_COMBINE. Fixes #1989. --- modules/gallery/helpers/gallery.php | 8 ++++++++ modules/gallery/libraries/Gallery_View.php | 6 ++++-- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/helpers/gallery.php b/modules/gallery/helpers/gallery.php index 725a710d..f1f7190c 100644 --- a/modules/gallery/helpers/gallery.php +++ b/modules/gallery/helpers/gallery.php @@ -222,4 +222,12 @@ class gallery_Core { static function show_profiler() { return file_exists(VARPATH . "PROFILE"); } + + /** + * Return true if we should allow Javascript and CSS combining for performance reasons. + * Typically we want this, but it's convenient for developers to be able to disable it. + */ + static function allow_css_and_js_combining() { + return !file_exists(VARPATH . "DONT_COMBINE"); + } } \ No newline at end of file diff --git a/modules/gallery/libraries/Gallery_View.php b/modules/gallery/libraries/Gallery_View.php index 64fea0ad..8f02b53c 100644 --- a/modules/gallery/libraries/Gallery_View.php +++ b/modules/gallery/libraries/Gallery_View.php @@ -82,8 +82,10 @@ class Gallery_View_Core extends View { * @param $types a comma separated list of types to combine, eg "script,css" */ public function start_combining($types) { - foreach (explode(",", $types) as $type) { - $this->combine_queue[$type] = array(); + if (gallery::allow_css_and_js_combining()) { + foreach (explode(",", $types) as $type) { + $this->combine_queue[$type] = array(); + } } } -- cgit v1.2.3 From faa719551d68308be8a1c41d9cd6104604958593 Mon Sep 17 00:00:00 2001 From: shadlaws Date: Wed, 6 Feb 2013 11:04:24 +0100 Subject: #1991 - Add options to graphics::mark_dirty to specify type and/or mime type. - graphics::mark_dirty - added $type and $mime_type as options. - graphics::mark_dirty - used options to set additional where conditions. --- modules/gallery/helpers/graphics.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/helpers/graphics.php b/modules/gallery/helpers/graphics.php index 7c8e89d5..4df57fba 100644 --- a/modules/gallery/helpers/graphics.php +++ b/modules/gallery/helpers/graphics.php @@ -314,12 +314,19 @@ class graphics_Core { } /** - * Mark thumbnails and resizes as dirty. They will have to be rebuilt. + * Mark thumbnails and resizes as dirty. They will have to be rebuilt. Optionally, only those of + * a specified type and/or mime type can be marked (e.g. $type="movie" to rebuild movies only). */ - static function mark_dirty($thumbs, $resizes) { + static function mark_dirty($thumbs, $resizes, $type=null, $mime_type=null) { if ($thumbs || $resizes) { $db = db::build() ->update("items"); + if ($type) { + $db->where("type", "=", $type); + } + if ($mime_type) { + $db->where("mime_type", "=", $mime_type); + } if ($thumbs) { $db->set("thumb_dirty", 1); } -- cgit v1.2.3 From 0312d1b071bd4434ddb3f82888b0323da6bf3732 Mon Sep 17 00:00:00 2001 From: shadlaws Date: Fri, 8 Feb 2013 13:51:41 +0100 Subject: #1994 - Make get_file_metadata throw an exception if photo or movie is unidentifiable/illegal. - photo & movie helpers: modified to throw exceptions when file is known to be unidentifiable/illegal. - item model: revised to work with exceptions and be more explicit when the data file is invalid. - item model: removed duplicate get_file_metadata call for updated items. - admin_watermarks controller: revised to work with exceptions (really cleans up logic here). - graphics helper: revised to handle invalid placeholders (a nearly-impossible corner case, but still...). - photo & movie helper tests: revised to work with exceptions, added new tests for illegal files with valid extensions. - item model tests: revised to work with exceptions, added new tests for illegal files with valid extensions. --- modules/gallery/helpers/gallery_graphics.php | 5 ++ modules/gallery/helpers/graphics.php | 11 +++- modules/gallery/helpers/movie.php | 10 ++- modules/gallery/helpers/photo.php | 9 ++- modules/gallery/models/item.php | 71 +++++++++++++--------- modules/gallery/tests/Item_Model_Test.php | 16 ++++- modules/gallery/tests/Movie_Helper_Test.php | 36 +++++++++-- modules/gallery/tests/Photo_Helper_Test.php | 18 +++++- modules/watermark/controllers/admin_watermarks.php | 13 ++-- 9 files changed, 139 insertions(+), 50 deletions(-) (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/helpers/gallery_graphics.php b/modules/gallery/helpers/gallery_graphics.php index b78bd9a7..eb76353f 100644 --- a/modules/gallery/helpers/gallery_graphics.php +++ b/modules/gallery/helpers/gallery_graphics.php @@ -172,6 +172,11 @@ class gallery_graphics_Core { module::event("graphics_composite_completed", $input_file, $output_file, $options, $item); } catch (ErrorException $e) { + // Unlike rotate and resize, composite catches its exceptions here. This is because + // composite is typically called for watermarks. If during thumb/resize generation + // the watermark fails, we'd still like the image resized, just without its watermark. + // If the exception isn't caught here, graphics::generate will replace it with a + // placeholder. Kohana_Log::add("error", $e->getMessage()); } } diff --git a/modules/gallery/helpers/graphics.php b/modules/gallery/helpers/graphics.php index 4df57fba..e34af018 100644 --- a/modules/gallery/helpers/graphics.php +++ b/modules/gallery/helpers/graphics.php @@ -224,7 +224,16 @@ class graphics_Core { graphics::_replace_image_with_placeholder($item, "resize"); } graphics::_replace_image_with_placeholder($item, "thumb"); - graphics::_update_item_dimensions($item); + try { + graphics::_update_item_dimensions($item); + } catch (Exception $e) { + // Looks like get_file_metadata couldn't identify our placeholders. We should never get + // here, but in the odd case we do, we need to do something. Let's put in hardcoded values. + if ($item->is_photo()) { + list ($item->resize_width, $item->resize_height) = array(200, 200); + } + list ($item->thumb_width, $item->thumb_height) = array(200, 200); + } $item->save(); throw $e; } diff --git a/modules/gallery/helpers/movie.php b/modules/gallery/helpers/movie.php index 6844771b..d4b907a2 100644 --- a/modules/gallery/helpers/movie.php +++ b/modules/gallery/helpers/movie.php @@ -192,8 +192,16 @@ class movie_Core { $metadata->extension = strtolower($extension); } - // Run movie_get_file_metadata events which can modify the class, then return results. + // Run movie_get_file_metadata events which can modify the class. module::event("movie_get_file_metadata", $file_path, $metadata); + + // If the post-events results are invalid, throw an exception. Note that, unlike photos, having + // zero width and height isn't considered invalid (as is the case when FFmpeg isn't installed). + if (!$metadata->mime_type || !$metadata->extension || + ($metadata->mime_type != legal_file::get_movie_types_by_extension($metadata->extension))) { + throw new Exception("@todo ILLEGAL_OR_UNINDENTIFIABLE_FILE"); + } + return array($metadata->width, $metadata->height, $metadata->mime_type, $metadata->extension, $metadata->duration); } diff --git a/modules/gallery/helpers/photo.php b/modules/gallery/helpers/photo.php index 51e51507..2d32f0d3 100644 --- a/modules/gallery/helpers/photo.php +++ b/modules/gallery/helpers/photo.php @@ -133,8 +133,15 @@ class photo_Core { $metadata->height = 0; } - // Run photo_get_file_metadata events which can modify the class, then return results. + // Run photo_get_file_metadata events which can modify the class. module::event("photo_get_file_metadata", $file_path, $metadata); + + // If the post-events results are invalid, throw an exception. + if (!$metadata->width || !$metadata->height || !$metadata->mime_type || !$metadata->extension || + ($metadata->mime_type != legal_file::get_photo_types_by_extension($metadata->extension))) { + throw new Exception("@todo ILLEGAL_OR_UNINDENTIFIABLE_FILE"); + } + return array($metadata->width, $metadata->height, $metadata->mime_type, $metadata->extension); } } diff --git a/modules/gallery/models/item.php b/modules/gallery/models/item.php index 197d3057..33b8a89d 100644 --- a/modules/gallery/models/item.php +++ b/modules/gallery/models/item.php @@ -21,6 +21,7 @@ class Item_Model_Core extends ORM_MPTT { protected $children = "items"; protected $sorting = array(); public $data_file = null; + private $data_file_error = null; public function __construct($id=null) { parent::__construct($id); @@ -378,18 +379,26 @@ class Item_Model_Core extends ORM_MPTT { // Get the width, height and mime type from our data file for photos and movies. if ($this->is_photo() || $this->is_movie()) { - if ($this->is_photo()) { - list ($this->width, $this->height, $this->mime_type, $extension) = - photo::get_file_metadata($this->data_file); - } else if ($this->is_movie()) { - list ($this->width, $this->height, $this->mime_type, $extension) = - movie::get_file_metadata($this->data_file); - } - - // Force an extension onto the name if necessary - $pi = pathinfo($this->data_file); - if (empty($pi["extension"])) { - $this->name = "{$this->name}.$extension"; + try { + if ($this->is_photo()) { + list ($this->width, $this->height, $this->mime_type, $extension) = + photo::get_file_metadata($this->data_file); + } else if ($this->is_movie()) { + list ($this->width, $this->height, $this->mime_type, $extension) = + movie::get_file_metadata($this->data_file); + } + + // Force an extension onto the name if necessary + $pi = pathinfo($this->data_file); + if (empty($pi["extension"])) { + $this->name = "{$this->name}.$extension"; + } + + // Data file valid - make sure the flag is reset to false. + $this->data_file_error = false; + } catch (Exception $e) { + // Data file invalid - set the flag so it's reported during item validation. + $this->data_file_error = true; } } @@ -436,17 +445,24 @@ class Item_Model_Core extends ORM_MPTT { // appropriate for its data. We don't try to preserve the name of the data file, though, // because the name is typically a temporary randomly-generated name. if (isset($this->data_file)) { - $extension = pathinfo($this->data_file, PATHINFO_EXTENSION); - $new_name = pathinfo($this->name, PATHINFO_FILENAME) . ".$extension"; - if (!empty($extension) && strcmp($this->name, $new_name)) { - $this->name = $new_name; - } - if ($this->is_photo()) { - list ($this->width, $this->height, $this->mime_type, $extension) = - photo::get_file_metadata($this->data_file); - } else if ($this->is_movie()) { - list ($this->width, $this->height, $this->mime_type, $extension) = - movie::get_file_metadata($this->data_file); + try { + $extension = pathinfo($this->data_file, PATHINFO_EXTENSION); + $new_name = pathinfo($this->name, PATHINFO_FILENAME) . ".$extension"; + if (!empty($extension) && strcmp($this->name, $new_name)) { + $this->name = $new_name; + } + if ($this->is_photo()) { + list ($this->width, $this->height, $this->mime_type, $extension) = + photo::get_file_metadata($this->data_file); + } else if ($this->is_movie()) { + list ($this->width, $this->height, $this->mime_type, $extension) = + movie::get_file_metadata($this->data_file); + } + // Data file valid - make sure the flag is reset to false. + $this->data_file_error = false; + } catch (Exception $e) { + // Data file invalid - set the flag so it's reported during item validation. + $this->data_file_error = true; } } @@ -524,13 +540,6 @@ class Item_Model_Core extends ORM_MPTT { // Replace the data file, if requested. if ($this->data_file && ($this->is_photo() || $this->is_movie())) { copy($this->data_file, $this->file_path()); - - // Get the width, height and mime type from our data file for photos and movies. - if ($this->is_photo()) { - list ($this->width, $this->height) = photo::get_file_metadata($this->file_path()); - } else if ($this->is_movie()) { - list ($this->width, $this->height) = movie::get_file_metadata($this->file_path()); - } $this->thumb_dirty = 1; $this->resize_dirty = 1; } @@ -966,6 +975,8 @@ class Item_Model_Core extends ORM_MPTT { $v->add_error("name", "bad_data_file_path"); } else if (filesize($this->data_file) == 0) { $v->add_error("name", "empty_data_file"); + } else if ($this->data_file_error) { + $v->add_error("name", "invalid_data_file"); } } diff --git a/modules/gallery/tests/Item_Model_Test.php b/modules/gallery/tests/Item_Model_Test.php index a1c5bce6..a93498dd 100644 --- a/modules/gallery/tests/Item_Model_Test.php +++ b/modules/gallery/tests/Item_Model_Test.php @@ -445,13 +445,25 @@ class Item_Model_Test extends Gallery_Unit_Test_Case { $photo->set_data_file(MODPATH . "gallery/tests/Item_Model_Test.php"); $photo->save(); } catch (ORM_Validation_Exception $e) { - $this->assert_same(array("mime_type" => "invalid", "name" => "illegal_data_file_extension"), - $e->validation->errors()); + $this->assert_same(array("name" => "illegal_data_file_extension"), $e->validation->errors()); return; // pass } $this->assert_true(false, "Shouldn't get here"); } + public function unsafe_data_file_replacement_with_valid_extension_test() { + $temp_file = TMPPATH . "masquerading_php.jpg"; + copy(MODPATH . "gallery/tests/Item_Model_Test.php", $temp_file); + try { + $photo = test::random_photo(); + $photo->set_data_file($temp_file); + $photo->save(); + } catch (ORM_Validation_Exception $e) { + $this->assert_same(array("name" => "invalid_data_file"), $e->validation->errors()); + return; // pass + } + } + public function urls_test() { $photo = test::random_photo(); $this->assert_true( diff --git a/modules/gallery/tests/Movie_Helper_Test.php b/modules/gallery/tests/Movie_Helper_Test.php index 0c262620..03fa2da9 100644 --- a/modules/gallery/tests/Movie_Helper_Test.php +++ b/modules/gallery/tests/Movie_Helper_Test.php @@ -64,18 +64,42 @@ class Movie_Helper_Test extends Gallery_Unit_Test_Case { public function get_file_metadata_with_no_extension_test() { copy(MODPATH . "gallery/tests/test.flv", TMPPATH . "test_flv_with_no_extension"); - $this->assert_equal(array(360, 288, null, null, 6.00), - movie::get_file_metadata(TMPPATH . "test_flv_with_no_extension")); + // Since mime type and extension are based solely on the filename, this is considered invalid. + try { + $metadata = movie::get_file_metadata(TMPPATH . "test_flv_with_no_extension"); + $this->assert_true(false, "Shouldn't get here"); + } catch (Exception $e) { + // pass + } } public function get_file_metadata_with_illegal_extension_test() { - $this->assert_equal(array(0, 0, null, null, 0), - movie::get_file_metadata(MODPATH . "gallery/tests/Movie_Helper_Test.php")); + try { + $metadata = movie::get_file_metadata(MODPATH . "gallery/tests/Movie_Helper_Test.php"); + $this->assert_true(false, "Shouldn't get here"); + } catch (Exception $e) { + // pass + } } public function get_file_metadata_with_illegal_extension_but_valid_file_contents_test() { copy(MODPATH . "gallery/tests/test.flv", TMPPATH . "test_flv_with_php_extension.php"); - $this->assert_equal(array(360, 288, null, null, 6.00), - movie::get_file_metadata(TMPPATH . "test_flv_with_php_extension.php")); + // Since mime type and extension are based solely on the filename, this is considered invalid. + try { + $metadata = movie::get_file_metadata(TMPPATH . "test_flv_with_php_extension.php"); + $this->assert_true(false, "Shouldn't get here"); + } catch (Exception $e) { + // pass + } + } + + public function get_file_metadata_with_valid_extension_but_illegal_file_contents_test() { + copy(MODPATH . "gallery/tests/Photo_Helper_Test.php", TMPPATH . "test_php_with_flv_extension.flv"); + // Since mime type and extension are based solely on the filename, this is considered valid. + // Of course, FFmpeg cannot extract width, height, or duration from the file. Note that this + // isn't a really a security problem, since the filename doesn't have a php extension and + // therefore will never be executed. + $this->assert_equal(array(0, 0, "video/x-flv", "flv", 0), + movie::get_file_metadata(TMPPATH . "test_php_with_flv_extension.flv")); } } diff --git a/modules/gallery/tests/Photo_Helper_Test.php b/modules/gallery/tests/Photo_Helper_Test.php index 5207a6db..79b5ccfd 100644 --- a/modules/gallery/tests/Photo_Helper_Test.php +++ b/modules/gallery/tests/Photo_Helper_Test.php @@ -40,8 +40,12 @@ class Photo_Helper_Test extends Gallery_Unit_Test_Case { } public function get_file_metadata_with_illegal_extension_test() { - $this->assert_equal(array(0, 0, null, null), - photo::get_file_metadata(MODPATH . "gallery/tests/Photo_Helper_Test.php")); + try { + $metadata = photo::get_file_metadata(MODPATH . "gallery/tests/Photo_Helper_Test.php"); + $this->assert_true(false, "Shouldn't get here"); + } catch (Exception $e) { + // pass + } } public function get_file_metadata_with_illegal_extension_but_valid_file_contents_test() { @@ -53,4 +57,14 @@ class Photo_Helper_Test extends Gallery_Unit_Test_Case { $this->assert_equal(array(1024, 768, "image/jpeg", "jpg"), photo::get_file_metadata(TMPPATH . "test_jpg_with_php_extension.php")); } + + public function get_file_metadata_with_valid_extension_but_illegal_file_contents_test() { + copy(MODPATH . "gallery/tests/Photo_Helper_Test.php", TMPPATH . "test_php_with_jpg_extension.jpg"); + try { + $metadata = photo::get_file_metadata(TMPPATH . "test_php_with_jpg_extension.jpg"); + $this->assert_true(false, "Shouldn't get here"); + } catch (Exception $e) { + // pass + } + } } diff --git a/modules/watermark/controllers/admin_watermarks.php b/modules/watermark/controllers/admin_watermarks.php index 27c2efc9..59bb7fa9 100644 --- a/modules/watermark/controllers/admin_watermarks.php +++ b/modules/watermark/controllers/admin_watermarks.php @@ -102,18 +102,17 @@ class Admin_Watermarks_Controller extends Admin_Controller { $name = preg_replace("/uploadfile-[^-]+-(.*)/", '$1', $pathinfo["basename"]); $name = legal_file::smash_extensions($name); - list ($width, $height, $mime_type, $extension) = photo::get_file_metadata($file); - if (!$width || !$height || !$mime_type || !$extension || - !legal_file::get_photo_extensions($extension)) { - message::error(t("Invalid or unidentifiable image file")); - @unlink($file); - return; - } else { + try { + list ($width, $height, $mime_type, $extension) = photo::get_file_metadata($file); // Force correct, legal extension type on file, which will be of our canonical type // (i.e. all lowercase, jpg instead of jpeg, etc.). This renaming prevents the issues // addressed in ticket #1855, where an image that looked valid (header said jpg) with a // php extension was previously accepted without changing its extension. $name = legal_file::change_extension($name, $extension); + } catch (Exception $e) { + message::error(t("Invalid or unidentifiable image file")); + @unlink($file); + return; } rename($file, VARPATH . "modules/watermark/$name"); -- cgit v1.2.3 From 942233dd26a02f86936963e5e7adfea6ae746bba Mon Sep 17 00:00:00 2001 From: shadlaws Date: Fri, 8 Feb 2013 15:21:57 +0100 Subject: #1996 - Add blacklist to legal_file helper. Adding a blacklist to legal_file could prevent possible security holes arising from a contributed module that adds file types by user input (e.g. an admin screen). --- modules/gallery/helpers/legal_file.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/helpers/legal_file.php b/modules/gallery/helpers/legal_file.php index ab9047c8..9ed564a1 100644 --- a/modules/gallery/helpers/legal_file.php +++ b/modules/gallery/helpers/legal_file.php @@ -24,6 +24,8 @@ class legal_file_Core { private static $movie_extensions; private static $photo_types; private static $movie_types; + private static $blacklist = array("php", "php3", "php4", "php5", "phtml", "phtm", "shtml", "shtm", + "pl", "cgi", "asp", "sh", "py", "c", "js"); /** * Create a default list of allowed photo MIME types paired with their extensions and then let @@ -38,6 +40,9 @@ class legal_file_Core { $types_by_extension_wrapper->types_by_extension = array( "jpg" => "image/jpeg", "jpeg" => "image/jpeg", "gif" => "image/gif", "png" => "image/png"); module::event("photo_types_by_extension", $types_by_extension_wrapper); + foreach (self::$blacklist as $key) { + unset($types_by_extension_wrapper->types_by_extension[$key]); + } self::$photo_types_by_extension = $types_by_extension_wrapper->types_by_extension; } if ($extension) { @@ -67,6 +72,9 @@ class legal_file_Core { $types_by_extension_wrapper->types_by_extension = array( "flv" => "video/x-flv", "mp4" => "video/mp4", "m4v" => "video/x-m4v"); module::event("movie_types_by_extension", $types_by_extension_wrapper); + foreach (self::$blacklist as $key) { + unset($types_by_extension_wrapper->types_by_extension[$key]); + } self::$movie_types_by_extension = $types_by_extension_wrapper->types_by_extension; } if ($extension) { @@ -118,7 +126,7 @@ class legal_file_Core { $extensions_wrapper = new stdClass(); $extensions_wrapper->extensions = array_keys(legal_file::get_photo_types_by_extension()); module::event("legal_photo_extensions", $extensions_wrapper); - self::$photo_extensions = $extensions_wrapper->extensions; + self::$photo_extensions = array_diff($extensions_wrapper->extensions, self::$blacklist); } if ($extension) { // return true if in array, false if not @@ -139,7 +147,7 @@ class legal_file_Core { $extensions_wrapper = new stdClass(); $extensions_wrapper->extensions = array_keys(legal_file::get_movie_types_by_extension()); module::event("legal_movie_extensions", $extensions_wrapper); - self::$movie_extensions = $extensions_wrapper->extensions; + self::$movie_extensions = array_diff($extensions_wrapper->extensions, self::$blacklist); } if ($extension) { // return true if in array, false if not -- cgit v1.2.3 From 8dcdb3b8e14c58a65fb98a906dc8916d481c2af4 Mon Sep 17 00:00:00 2001 From: shadlaws Date: Sat, 9 Feb 2013 18:12:08 +0100 Subject: #1997 - Update movie dimensions and mime type if previously set without FFmpeg. - Added code to check/correct movie width, height, and mime in graphics::generate. As the comment says in the commit, this isn't ideal, but doing it in an upgrade script wouldn't be very ideal either. --- modules/gallery/helpers/graphics.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/helpers/graphics.php b/modules/gallery/helpers/graphics.php index 4df57fba..e8df56ed 100644 --- a/modules/gallery/helpers/graphics.php +++ b/modules/gallery/helpers/graphics.php @@ -149,6 +149,19 @@ class graphics_Core { if (@filesize($output_file) == 0) { try { movie::extract_frame($working_file, $output_file, $movie_options_wrapper->movie_options); + // If we're here, we know ffmpeg is installed and the movie is valid. Because the + // user may not always have had ffmpeg installed, the movie's width, height, and + // mime type may need updating. Let's use this opportunity to make sure they're + // correct. It's not optimal to do it at this low level, but it's not trivial to find + // these cases quickly in an upgrade script. + list ($width, $height, $mime_type) = movie::get_file_metadata($working_file); + // Only set them if they need updating to avoid marking them as "changed" + if (($item->width != $width) || ($item->height != $height) || + ($item->mime_type != $mime_type)) { + $item->width = $width; + $item->height = $height; + $item->mime_type = $mime_type; + } } catch (Exception $e) { // Didn't work, likely because of MISSING_FFMPEG - use placeholder graphics::_replace_image_with_placeholder($item, $target); -- cgit v1.2.3 From 1d7f5e3ab117a6cce8f2a1d3de5e311b74dbee81 Mon Sep 17 00:00:00 2001 From: shadlaws Date: Sat, 9 Feb 2013 20:48:02 +0100 Subject: #1935 - Make FFmpeg easier to install. - system::find_binary - add Gallery's bin subdirectory to search - system::find_binary - auto-fix permissions if found in Gallery's bin directory --- modules/gallery/helpers/system.php | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/helpers/system.php b/modules/gallery/helpers/system.php index fa834824..e1398103 100644 --- a/modules/gallery/helpers/system.php +++ b/modules/gallery/helpers/system.php @@ -20,22 +20,42 @@ class system_Core { /** * Return the path to an executable version of the named binary, or null. - * Traverse the PATH environment variable looking for the given file. If - * the $priority_path variable is set, check that path first. + * The paths are traversed in the following order: + * 1. $priority_path (if specified) + * 2. Gallery's own bin directory (DOCROOT . "bin") + * 3. PATH environment variable + * 4. extra_binary_paths Gallery variable (if specified) + * In addition, if the file is found inside Gallery's bin directory but + * it's not executable, we try to change its permissions to 0755. + * + * @param string $binary + * @param string $priority_path (optional) + * @return string path to binary if found; null if not found */ static function find_binary($binary, $priority_path=null) { - $paths = array_merge( - explode(":", getenv("PATH")), - explode(":", module::get_var("gallery", "extra_binary_paths"))); + $bin_path = DOCROOT . "bin"; + if ($priority_path) { - array_unshift($paths, $priority_path); + $paths = array($priority_path, $bin_path); + } else { + $paths = array($bin_path); } + $paths = array_merge($paths, + explode(":", getenv("PATH")), + explode(":", module::get_var("gallery", "extra_binary_paths"))); foreach ($paths as $path) { $candidate = "$path/$binary"; // @suppress errors below to avoid open_basedir issues - if (@file_exists($candidate) && @is_executable($candidate)) { - return $candidate; + if (@file_exists($candidate)) { + if (!@is_executable($candidate) && + (substr_compare($bin_path, $candidate, 0, strlen($bin_path)) == 0)) { + // Binary isn't executable but is in Gallery's bin directory - try fixing permissions. + @chmod($candidate, 0755); + } + if (@is_executable($candidate)) { + return $candidate; + } } } return null; -- cgit v1.2.3 From bfdf5a00fd8a256c4b564887f4eb649cf9d34774 Mon Sep 17 00:00:00 2001 From: shadlaws Date: Sun, 10 Feb 2013 02:28:47 +0100 Subject: #2000 - Make legal_file::smash_extensions more robust. - Fixed legal_file::smash_extensions. - Added new unit test. - Removed duplicated condition in existing unit test. --- modules/gallery/helpers/legal_file.php | 6 +++++- modules/gallery/tests/Legal_File_Helper_Test.php | 10 +++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/helpers/legal_file.php b/modules/gallery/helpers/legal_file.php index 9ed564a1..ef588ceb 100644 --- a/modules/gallery/helpers/legal_file.php +++ b/modules/gallery/helpers/legal_file.php @@ -235,6 +235,10 @@ class legal_file_Core { * Reduce the given file to having a single extension. */ static function smash_extensions($filename) { + if (!$filename) { + // It's harmless, so return it before it causes issues with pathinfo. + return $filename; + } $parts = pathinfo($filename); $result = ""; if ($parts["dirname"] != ".") { @@ -243,7 +247,7 @@ class legal_file_Core { $parts["filename"] = str_replace(".", "_", $parts["filename"]); $parts["filename"] = preg_replace("/[_]+/", "_", $parts["filename"]); $parts["filename"] = trim($parts["filename"], "_"); - $result .= "{$parts['filename']}.{$parts['extension']}"; + $result .= isset($parts["extension"]) ? "{$parts['filename']}.{$parts['extension']}" : $parts["filename"]; return $result; } } diff --git a/modules/gallery/tests/Legal_File_Helper_Test.php b/modules/gallery/tests/Legal_File_Helper_Test.php index 84a29a52..203d5616 100644 --- a/modules/gallery/tests/Legal_File_Helper_Test.php +++ b/modules/gallery/tests/Legal_File_Helper_Test.php @@ -136,10 +136,18 @@ class Legal_File_Helper_Test extends Gallery_Unit_Test_Case { public function smash_extensions_test() { $this->assert_equal("foo_bar.jpg", legal_file::smash_extensions("foo.bar.jpg")); $this->assert_equal("foo_bar_baz.jpg", legal_file::smash_extensions("foo.bar.baz.jpg")); - $this->assert_equal("foo_bar_baz.jpg", legal_file::smash_extensions("foo.bar.baz.jpg")); $this->assert_equal("foo_bar_baz.jpg", legal_file::smash_extensions("...foo...bar..baz...jpg")); $this->assert_equal("/path/to/foo_bar.jpg", legal_file::smash_extensions("/path/to/foo.bar.jpg")); $this->assert_equal("/path/to.to/foo_bar.jpg", legal_file::smash_extensions("/path/to.to/foo.bar.jpg")); $this->assert_equal("foo_bar-12345678.jpg", legal_file::smash_extensions("foo.bar-12345678.jpg")); } + + public function smash_extensions_pass_thru_names_without_extensions_test() { + $this->assert_equal("foo", legal_file::smash_extensions("foo")); + $this->assert_equal("foo.", legal_file::smash_extensions("foo.")); + $this->assert_equal(".foo", legal_file::smash_extensions(".foo")); + $this->assert_equal(".", legal_file::smash_extensions(".")); + $this->assert_equal("", legal_file::smash_extensions("")); + $this->assert_equal(null, legal_file::smash_extensions(null)); + } } \ No newline at end of file -- cgit v1.2.3 From 0a2670a19ab121fe6970f2fcdf1864cb452a76c1 Mon Sep 17 00:00:00 2001 From: shadlaws Date: Tue, 12 Feb 2013 00:30:30 +0100 Subject: #1988 - Add movie_allow_uploads option ("always", "never", or "autodetect"). - gallery_installer, module.info, install.sql - add movie_allow_uploads variable - movie::allow_uploads (new) - return true if movie_allow_uploads is "always" or "autodetect" and FFmpeg found, false otherwise - legal_file - use movie::allow_uploads instead of movie::find_ffmpeg - Form_Uploadify - use movie::allow_uploads instead of movie::find_ffmpeg --- installer/install.sql | 5 +++-- modules/gallery/helpers/gallery_installer.php | 8 ++++++++ modules/gallery/helpers/legal_file.php | 4 ++-- modules/gallery/helpers/movie.php | 25 +++++++++++++++++++++++++ modules/gallery/libraries/Form_Uploadify.php | 2 +- modules/gallery/module.info | 2 +- 6 files changed, 40 insertions(+), 6 deletions(-) (limited to 'modules/gallery/helpers') diff --git a/installer/install.sql b/installer/install.sql index 4097d51e..b89d6b9b 100644 --- a/installer/install.sql +++ b/installer/install.sql @@ -245,7 +245,7 @@ CREATE TABLE {modules} ( KEY `weight` (`weight`) ) AUTO_INCREMENT=10 DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; -INSERT INTO {modules} VALUES (1,1,'gallery',55,1); +INSERT INTO {modules} VALUES (1,1,'gallery',56,1); INSERT INTO {modules} VALUES (2,1,'user',4,2); INSERT INTO {modules} VALUES (3,1,'comment',7,3); INSERT INTO {modules} VALUES (4,1,'organize',4,4); @@ -383,7 +383,7 @@ CREATE TABLE {vars} ( `value` text, PRIMARY KEY (`id`), UNIQUE KEY `module_name` (`module_name`,`name`) -) AUTO_INCREMENT=46 DEFAULT CHARSET=utf8; +) AUTO_INCREMENT=47 DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; INSERT INTO {vars} VALUES (NULL,'gallery','active_site_theme','wind'); INSERT INTO {vars} VALUES (NULL,'gallery','active_admin_theme','admin_wind'); @@ -417,6 +417,7 @@ INSERT INTO {vars} VALUES (NULL,'gallery','extra_binary_paths','/usr/local/bin:/ INSERT INTO {vars} VALUES (NULL,'gallery','timezone',NULL); INSERT INTO {vars} VALUES (NULL,'gallery','lock_timeout','1'); INSERT INTO {vars} VALUES (NULL,'gallery','movie_extract_frame_time','3'); +INSERT INTO {vars} VALUES (NULL,'gallery','movie_allow_uploads','autodetect'); INSERT INTO {vars} VALUES (NULL,'gallery','blocks_site_sidebar','a:4:{i:10;a:2:{i:0;s:7:\"gallery\";i:1;s:8:\"language\";}i:11;a:2:{i:0;s:4:\"info\";i:1;s:8:\"metadata\";}i:12;a:2:{i:0;s:3:\"rss\";i:1;s:9:\"rss_feeds\";}i:13;a:2:{i:0;s:3:\"tag\";i:1;s:3:\"tag\";}}'); INSERT INTO {vars} VALUES (NULL,'gallery','identity_provider','user'); INSERT INTO {vars} VALUES (NULL,'user','minimum_password_length','5'); diff --git a/modules/gallery/helpers/gallery_installer.php b/modules/gallery/helpers/gallery_installer.php index 7f10cdee..051a66cf 100644 --- a/modules/gallery/helpers/gallery_installer.php +++ b/modules/gallery/helpers/gallery_installer.php @@ -315,6 +315,7 @@ class gallery_installer { module::set_var("gallery", "timezone", null); module::set_var("gallery", "lock_timeout", 1); module::set_var("gallery", "movie_extract_frame_time", 3); + module::set_var("gallery", "movie_allow_uploads", "autodetect"); } static function upgrade($version) { @@ -789,6 +790,13 @@ class gallery_installer { module::set_version("gallery", $version = 55); } + if ($version == 55) { + // In v56, we added the ability to change the default behavior regarding movie uploads. It + // can be set to "always", "never", or "autodetect" to match the previous behavior where they + // are allowed only if FFmpeg is found. + module::set_var("gallery", "movie_allow_uploads", "autodetect"); + module::set_version("gallery", $version = 56); + } } static function uninstall() { diff --git a/modules/gallery/helpers/legal_file.php b/modules/gallery/helpers/legal_file.php index ef588ceb..debd1e6d 100644 --- a/modules/gallery/helpers/legal_file.php +++ b/modules/gallery/helpers/legal_file.php @@ -98,7 +98,7 @@ class legal_file_Core { */ static function get_types_by_extension($extension=null) { $types_by_extension = legal_file::get_photo_types_by_extension(); - if (movie::find_ffmpeg()) { + if (movie::allow_uploads()) { $types_by_extension = array_merge($types_by_extension, legal_file::get_movie_types_by_extension()); } @@ -165,7 +165,7 @@ class legal_file_Core { */ static function get_extensions($extension=null) { $extensions = legal_file::get_photo_extensions(); - if (movie::find_ffmpeg()) { + if (movie::allow_uploads()) { $extensions = array_merge($extensions, legal_file::get_movie_extensions()); } if ($extension) { diff --git a/modules/gallery/helpers/movie.php b/modules/gallery/helpers/movie.php index d4b907a2..eda478c7 100644 --- a/modules/gallery/helpers/movie.php +++ b/modules/gallery/helpers/movie.php @@ -24,6 +24,8 @@ * Note: by design, this class does not do any permission checking. */ class movie_Core { + private static $allow_uploads; + static function get_edit_form($movie) { $form = new Forge("movies/update/$movie->id", "", "post", array("id" => "g-edit-movie-form")); $form->hidden("from_id")->value($movie->id); @@ -109,6 +111,29 @@ class movie_Core { } } + /** + * Return true if movie uploads are allowed, false if not. This is based on the + * "movie_allow_uploads" Gallery variable as well as whether or not ffmpeg is found. + */ + static function allow_uploads() { + if (empty(self::$allow_uploads)) { + // Refresh ffmpeg settings + $ffmpeg = movie::find_ffmpeg(); + switch (module::get_var("gallery", "movie_allow_uploads", "autodetect")) { + case "always": + self::$allow_uploads = true; + break; + case "never": + self::$allow_uploads = false; + break; + default: + self::$allow_uploads = !empty($ffmpeg); + break; + } + } + return self::$allow_uploads; + } + /** * Return the path to the ffmpeg binary if one exists and is executable, or null. */ diff --git a/modules/gallery/libraries/Form_Uploadify.php b/modules/gallery/libraries/Form_Uploadify.php index 56793c69..1e58018d 100644 --- a/modules/gallery/libraries/Form_Uploadify.php +++ b/modules/gallery/libraries/Form_Uploadify.php @@ -46,7 +46,7 @@ class Form_Uploadify_Core extends Form_Input { $v->album = $this->data["album"]; $v->script_data = $this->data["script_data"]; $v->simultaneous_upload_limit = module::get_var("gallery", "simultaneous_upload_limit"); - $v->movies_allowed = (bool) movie::find_ffmpeg(); + $v->movies_allowed = movie::allow_uploads(); $v->extensions = legal_file::get_filters(); $v->suhosin_session_encrypt = (bool) ini_get("suhosin.session.encrypt"); diff --git a/modules/gallery/module.info b/modules/gallery/module.info index d79a5077..2383ec3c 100644 --- a/modules/gallery/module.info +++ b/modules/gallery/module.info @@ -1,6 +1,6 @@ name = "Gallery 3" description = "Gallery core application" -version = 55 +version = 56 author_name = "Gallery Team" author_url = "http://codex.galleryproject.org/Gallery:Team" info_url = "http://codex.galleryproject.org/Gallery3:Modules:gallery" -- cgit v1.2.3 From d04a6fc87d96b70ab0f70414f2ff40d1f1e7f482 Mon Sep 17 00:00:00 2001 From: shadlaws Date: Tue, 12 Feb 2013 00:37:33 +0100 Subject: #2001 - Make filename sanitizing more consistent. - legal_file - added sanitize_filname() to sanitize photo/movie filenames. - admin_watermarks - revised add() to use new function. - item model - added _process_data_file_info() to validate the data file, get its metadata, and sanitize the item name. - item model - revised save() for new items to use _process_data_file_info *before* the slug is checked. - item model - revised save() for updated items to use _process_data_file_info. - item model - revised save() for updated items to sanitize name if changed. - uploader - removed call to smash_extensions (item model does this when it calls sanitize_filename). - Legal_File_Helper_Test - added unit tests for sanitize_filename. - Item_Model_Test - revised existing unit tests based on changes. - Item_Model_Test - added new unit tests for names with legal but incorrect extensions. - Averted take over by HAL with fix #2001... --- modules/gallery/controllers/uploader.php | 4 - modules/gallery/helpers/legal_file.php | 57 ++++++++++++ modules/gallery/models/item.php | 95 ++++++++++--------- modules/gallery/tests/Item_Model_Test.php | 101 ++++++++++----------- modules/gallery/tests/Legal_File_Helper_Test.php | 44 +++++++++ modules/watermark/controllers/admin_watermarks.php | 9 +- 6 files changed, 202 insertions(+), 108 deletions(-) (limited to 'modules/gallery/helpers') diff --git a/modules/gallery/controllers/uploader.php b/modules/gallery/controllers/uploader.php index 55c65c95..78437071 100644 --- a/modules/gallery/controllers/uploader.php +++ b/modules/gallery/controllers/uploader.php @@ -63,10 +63,6 @@ class Uploader_Controller extends Controller { $item->parent_id = $album->id; $item->set_data_file($temp_filename); - // Remove double extensions from the filename - they'll be disallowed in the model but if - // we don't do it here then it'll result in a failed upload. - $item->name = legal_file::smash_extensions($item->name); - $path_info = @pathinfo($temp_filename); if (array_key_exists("extension", $path_info) && legal_file::get_movie_extensions($path_info["extension"])) { diff --git a/modules/gallery/helpers/legal_file.php b/modules/gallery/helpers/legal_file.php index ef588ceb..5a852f2b 100644 --- a/modules/gallery/helpers/legal_file.php +++ b/modules/gallery/helpers/legal_file.php @@ -250,4 +250,61 @@ class legal_file_Core { $result .= isset($parts["extension"]) ? "{$parts['filename']}.{$parts['extension']}" : $parts["filename"]; return $result; } + + /** + * Sanitize a filename for a given type (given as "photo" or "movie") and a target file format + * (given as an extension). This returns a completely legal and valid filename, + * or throws an exception if the type or extension given is invalid or illegal. It tries to + * maintain the filename's original extension even if it's not identical to the given extension + * (e.g. don't change "JPG" or "jpeg" to "jpg"). + * + * Note: it is not okay if the extension given is legal but does not match the type (e.g. if + * extension is "mp4" and type is "photo", it will throw an exception) + * + * @param string $filename (with no directory) + * @param string $extension (can be uppercase or lowercase) + * @param string $type (as "photo" or "movie") + * @return string sanitized filename (or null if bad extension argument) + */ + static function sanitize_filename($filename, $extension, $type) { + // Check if the type is valid - if so, get the mime types of the + // original and target extensions; if not, throw an exception. + $original_extension = pathinfo($filename, PATHINFO_EXTENSION); + switch ($type) { + case "photo": + $mime_type = legal_file::get_photo_types_by_extension($extension); + $original_mime_type = legal_file::get_photo_types_by_extension($original_extension); + break; + case "movie": + $mime_type = legal_file::get_movie_types_by_extension($extension); + $original_mime_type = legal_file::get_movie_types_by_extension($original_extension); + break; + default: + throw new Exception("@todo INVALID_TYPE"); + } + + // Check if the target extension is blank or invalid - if so, throw an exception. + if (!$extension || !$mime_type) { + throw new Exception("@todo ILLEGAL_EXTENSION"); + } + + // Check if the mime types of the original and target extensions match - if not, fix it. + if (!$original_extension || ($mime_type != $original_mime_type)) { + $filename = legal_file::change_extension($filename, $extension); + } + + // It should be a filename without a directory - remove all slashes (and backslashes). + $filename = str_replace("/", "_", $filename); + $filename = str_replace("\\", "_", $filename); + + // Remove extra dots from the filename. This will also remove extraneous underscores. + $filename = legal_file::smash_extensions($filename); + + // It's possible that the filename has no base (e.g. ".jpg") - if so, give it a generic one. + if (empty($filename) || (substr($filename, 0, 1) == ".")) { + $filename = $type . $filename; // e.g. "photo.jpg" or "movie.mp4" + } + + return $filename; + } } diff --git a/modules/gallery/models/item.php b/modules/gallery/models/item.php index 33b8a89d..43b9a292 100644 --- a/modules/gallery/models/item.php +++ b/modules/gallery/models/item.php @@ -365,6 +365,14 @@ class Item_Model_Core extends ORM_MPTT { $this->weight = item::get_max_weight(); } + // Process the data file info. + if (isset($this->data_file)) { + $this->_process_data_file_info(); + } else if (!$this->is_album()) { + // Unless it's an album, new items must have a data file. + $this->data_file_error = true; + } + // Make an url friendly slug from the name, if necessary if (empty($this->slug)) { $this->slug = item::convert_filename_to_slug(pathinfo($this->name, PATHINFO_FILENAME)); @@ -377,31 +385,6 @@ class Item_Model_Core extends ORM_MPTT { } } - // Get the width, height and mime type from our data file for photos and movies. - if ($this->is_photo() || $this->is_movie()) { - try { - if ($this->is_photo()) { - list ($this->width, $this->height, $this->mime_type, $extension) = - photo::get_file_metadata($this->data_file); - } else if ($this->is_movie()) { - list ($this->width, $this->height, $this->mime_type, $extension) = - movie::get_file_metadata($this->data_file); - } - - // Force an extension onto the name if necessary - $pi = pathinfo($this->data_file); - if (empty($pi["extension"])) { - $this->name = "{$this->name}.$extension"; - } - - // Data file valid - make sure the flag is reset to false. - $this->data_file_error = false; - } catch (Exception $e) { - // Data file invalid - set the flag so it's reported during item validation. - $this->data_file_error = true; - } - } - $this->_check_and_fix_conflicts(); parent::save(); @@ -439,31 +422,19 @@ class Item_Model_Core extends ORM_MPTT { // keep it around. $original = ORM::factory("item", $this->id); - // Preserve the extension of the data file. Many helpers, (e.g. ImageMagick), assume + // If we have a new data file, process its info. This will get its metadata and + // preserve the extension of the data file. Many helpers, (e.g. ImageMagick), assume // the MIME type from the extension. So when we adopt the new data file, it's important // to adopt the new extension. That ensures that the item's extension is always // appropriate for its data. We don't try to preserve the name of the data file, though, // because the name is typically a temporary randomly-generated name. if (isset($this->data_file)) { - try { - $extension = pathinfo($this->data_file, PATHINFO_EXTENSION); - $new_name = pathinfo($this->name, PATHINFO_FILENAME) . ".$extension"; - if (!empty($extension) && strcmp($this->name, $new_name)) { - $this->name = $new_name; - } - if ($this->is_photo()) { - list ($this->width, $this->height, $this->mime_type, $extension) = - photo::get_file_metadata($this->data_file); - } else if ($this->is_movie()) { - list ($this->width, $this->height, $this->mime_type, $extension) = - movie::get_file_metadata($this->data_file); - } - // Data file valid - make sure the flag is reset to false. - $this->data_file_error = false; - } catch (Exception $e) { - // Data file invalid - set the flag so it's reported during item validation. - $this->data_file_error = true; - } + $this->_process_data_file_info(); + } else if (!$this->is_album() && array_key_exists("name", $this->changed)) { + // There's no new data file, but the name changed. If it's a photo or movie, + // make sure the new name still agrees with the file type. + $this->name = legal_file::sanitize_filename($this->name, + pathinfo($original->name, PATHINFO_EXTENSION), $this->type); } // If an album's cover has changed (or been removed), delete any existing album cover, @@ -624,6 +595,40 @@ class Item_Model_Core extends ORM_MPTT { } } + /** + * Process the data file info. Get its metadata and extension. + * If valid, use it to sanitize the item name and update the + * width, height, and mime type. + */ + private function _process_data_file_info() { + try { + if ($this->is_photo()) { + list ($this->width, $this->height, $this->mime_type, $extension) = + photo::get_file_metadata($this->data_file); + } else if ($this->is_movie()) { + list ($this->width, $this->height, $this->mime_type, $extension) = + movie::get_file_metadata($this->data_file); + } else { + // Albums don't have data files. + $this->data_file = null; + return; + } + + // Sanitize the name based on the idenified extension, but only set $this->name if different + // to ensure it isn't unnecessarily marked as "changed" + $name = legal_file::sanitize_filename($this->name, $extension, $this->type); + if ($this->name != $name) { + $this->name = $name; + } + + // Data file valid - make sure the flag is reset to false. + $this->data_file_error = false; + } catch (Exception $e) { + // Data file invalid - set the flag so it's reported during item validation. + $this->data_file_error = true; + } + } + /** * Return the Item_Model representing the cover for this album. * @return Item_Model or null if there's no cover diff --git a/modules/gallery/tests/Item_Model_Test.php b/modules/gallery/tests/Item_Model_Test.php index a93498dd..fcb5c2ad 100644 --- a/modules/gallery/tests/Item_Model_Test.php +++ b/modules/gallery/tests/Item_Model_Test.php @@ -126,14 +126,9 @@ class Item_Model_Test extends Gallery_Unit_Test_Case { public function item_rename_wont_accept_slash_test() { $item = test::random_photo(); - try { - $item->name = test::random_name() . "/"; - $item->save(); - } catch (ORM_Validation_Exception $e) { - $this->assert_equal(array("name" => "no_slashes"), $e->validation->errors()); - return; - } - $this->assert_true(false, "Shouldn't get here"); + $item->name = "/no_slashes/allowed/"; + $item->save(); + $this->assert_equal("no_slashes_allowed.jpg", $item->name); } public function move_album_test() { @@ -328,30 +323,17 @@ class Item_Model_Test extends Gallery_Unit_Test_Case { } public function photo_files_must_have_an_extension_test() { - try { - $photo = test::random_photo_unsaved(); - $photo->mime_type = "image/jpeg"; - $photo->name = "no_extension"; - $photo->save(); - } catch (ORM_Validation_Exception $e) { - $this->assert_same(array("name" => "illegal_data_file_extension"), $e->validation->errors()); - return; // pass - } - $this->assert_true(false, "Shouldn't get here"); + $photo = test::random_photo_unsaved(); + $photo->name = "no_extension_photo"; + $photo->save(); + $this->assert_equal("no_extension_photo.jpg", $photo->name); } public function movie_files_must_have_an_extension_test() { - try { - $movie = test::random_movie_unsaved(); - $movie->type = "movie"; - $movie->mime_type = "video/x-flv"; - $movie->name = "no_extension"; - $movie->save(); - } catch (ORM_Validation_Exception $e) { - $this->assert_same(array("name" => "illegal_data_file_extension"), $e->validation->errors()); - return; // pass - } - $this->assert_true(false, "Shouldn't get here"); + $movie = test::random_movie_unsaved(); + $movie->name = "no_extension_movie"; + $movie->save(); + $this->assert_equal("no_extension_movie.flv", $movie->name); } public function cant_delete_root_album_test() { @@ -445,7 +427,7 @@ class Item_Model_Test extends Gallery_Unit_Test_Case { $photo->set_data_file(MODPATH . "gallery/tests/Item_Model_Test.php"); $photo->save(); } catch (ORM_Validation_Exception $e) { - $this->assert_same(array("name" => "illegal_data_file_extension"), $e->validation->errors()); + $this->assert_same(array("name" => "invalid_data_file"), $e->validation->errors()); return; // pass } $this->assert_true(false, "Shouldn't get here"); @@ -462,6 +444,7 @@ class Item_Model_Test extends Gallery_Unit_Test_Case { $this->assert_same(array("name" => "invalid_data_file"), $e->validation->errors()); return; // pass } + $this->assert_true(false, "Shouldn't get here"); } public function urls_test() { @@ -493,43 +476,55 @@ class Item_Model_Test extends Gallery_Unit_Test_Case { $album->thumb_url() . " is malformed"); } - public function legal_extension_test() { - foreach (array("test.gif", "test.GIF", "test.Gif", "test.jpeg", "test.JPG") as $name) { + public function legal_extension_that_does_match_gets_used_test() { + foreach (array("jpg", "JPG", "Jpg", "jpeg") as $extension) { $photo = test::random_photo_unsaved(item::root()); - $photo->name = $name; + $photo->name = test::random_name() . ".{$extension}"; $photo->save(); + // Should get renamed with the correct jpg extension of the data file. + $this->assert_equal($extension, pathinfo($photo->name, PATHINFO_EXTENSION)); } } public function illegal_extension_test() { foreach (array("test.php", "test.PHP", "test.php5", "test.php4", "test.pl", "test.php.png") as $name) { - try { - $photo = test::random_photo_unsaved(item::root()); - $photo->name = $name; - $photo->save(); - } catch (ORM_Validation_Exception $e) { - $this->assert_equal(array("name" => "illegal_data_file_extension"), - $e->validation->errors()); - continue; - } - $this->assert_true(false, "Shouldn't get here"); + $photo = test::random_photo_unsaved(item::root()); + $photo->name = $name; + $photo->save(); + // Should get renamed with the correct jpg extension of the data file. + $this->assert_equal("jpg", pathinfo($photo->name, PATHINFO_EXTENSION)); } } public function cant_rename_to_illegal_extension_test() { foreach (array("test.php.test", "test.php", "test.PHP", "test.php5", "test.php4", "test.pl") as $name) { - try { - $photo = test::random_photo(item::root()); - $photo->name = $name; - $photo->save(); - } catch (ORM_Validation_Exception $e) { - $this->assert_equal(array("name" => "illegal_data_file_extension"), - $e->validation->errors()); - continue; - } - $this->assert_true(false, "Shouldn't get here"); + $photo = test::random_photo(item::root()); + $photo->name = $name; + $photo->save(); + // Should get renamed with the correct jpg extension of the data file. + $this->assert_equal("jpg", pathinfo($photo->name, PATHINFO_EXTENSION)); + } + } + + public function legal_extension_that_doesnt_match_gets_fixed_test() { + foreach (array("test.png", "test.mp4", "test.GIF") as $name) { + $photo = test::random_photo_unsaved(item::root()); + $photo->name = $name; + $photo->save(); + // Should get renamed with the correct jpg extension of the data file. + $this->assert_equal("jpg", pathinfo($photo->name, PATHINFO_EXTENSION)); + } + } + + public function rename_to_legal_extension_that_doesnt_match_gets_fixed_test() { + foreach (array("test.png", "test.mp4", "test.GIF") as $name) { + $photo = test::random_photo(item::root()); + $photo->name = $name; + $photo->save(); + // Should get renamed with the correct jpg extension of the data file. + $this->assert_equal("jpg", pathinfo($photo->name, PATHINFO_EXTENSION)); } } diff --git a/modules/gallery/tests/Legal_File_Helper_Test.php b/modules/gallery/tests/Legal_File_Helper_Test.php index 203d5616..7ed5214b 100644 --- a/modules/gallery/tests/Legal_File_Helper_Test.php +++ b/modules/gallery/tests/Legal_File_Helper_Test.php @@ -150,4 +150,48 @@ class Legal_File_Helper_Test extends Gallery_Unit_Test_Case { $this->assert_equal("", legal_file::smash_extensions("")); $this->assert_equal(null, legal_file::smash_extensions(null)); } + + public function sanitize_filename_with_no_rename_test() { + $this->assert_equal("foo.jpeg", legal_file::sanitize_filename("foo.jpeg", "jpg", "photo")); + $this->assert_equal("foo.jpg", legal_file::sanitize_filename("foo.jpg", "jpeg", "photo")); + $this->assert_equal("foo.MP4", legal_file::sanitize_filename("foo.MP4", "mp4", "movie")); + $this->assert_equal("foo.mp4", legal_file::sanitize_filename("foo.mp4", "MP4", "movie")); + } + + public function sanitize_filename_with_corrected_extension_test() { + $this->assert_equal("foo.jpg", legal_file::sanitize_filename("foo.png", "jpg", "photo")); + $this->assert_equal("foo.MP4", legal_file::sanitize_filename("foo.jpg", "MP4", "movie")); + $this->assert_equal("foo.jpg", legal_file::sanitize_filename("foo.php", "jpg", "photo")); + } + + public function sanitize_filename_with_non_standard_chars_and_dots_test() { + $this->assert_equal("foo.jpg", legal_file::sanitize_filename("foo", "jpg", "photo")); + $this->assert_equal("foo.mp4", legal_file::sanitize_filename("foo.", "mp4", "movie")); + $this->assert_equal("foo.jpeg", legal_file::sanitize_filename(".foo.jpeg", "jpg", "photo")); + $this->assert_equal("foo_2013_02_10.jpeg", + legal_file::sanitize_filename("foo.2013/02/10.jpeg", "jpg", "photo")); + $this->assert_equal("foo_bar_baz.jpg", + legal_file::sanitize_filename("...foo...bar..baz...png", "jpg", "photo")); + $this->assert_equal("j'écris@un#nom_bizarre(mais quand_même_ça_passe.jpg", + legal_file::sanitize_filename("/j'écris@un#nom/bizarre(mais quand.même/ça_passe.\$ÇÀ@€#_", "jpg", "photo")); + } + + public function sanitize_filename_with_no_base_name_test() { + $this->assert_equal("photo.jpg", legal_file::sanitize_filename(".png", "jpg", "photo")); + $this->assert_equal("movie.mp4", legal_file::sanitize_filename("__..__", "mp4", "movie")); + $this->assert_equal("photo.jpg", legal_file::sanitize_filename(".", "jpg", "photo")); + $this->assert_equal("movie.mp4", legal_file::sanitize_filename(null, "mp4", "movie")); + } + + public function sanitize_filename_with_invalid_arguments_test() { + foreach (array("flv" => "photo", "jpg" => "movie", "php" => "photo", + null => "movie", "jpg" => "album", "jpg" => null) as $extension => $type) { + try { + legal_file::sanitize_filename("foo.jpg", $extension, $type); + $this->assert_true(false, "Shouldn't get here"); + } catch (Exception $e) { + // pass + } + } + } } \ No newline at end of file diff --git a/modules/watermark/controllers/admin_watermarks.php b/modules/watermark/controllers/admin_watermarks.php index 59bb7fa9..b058d6a5 100644 --- a/modules/watermark/controllers/admin_watermarks.php +++ b/modules/watermark/controllers/admin_watermarks.php @@ -97,18 +97,15 @@ class Admin_Watermarks_Controller extends Admin_Controller { // validation logic will correctly reject it. So, we skip validation when we're running tests. if (TEST_MODE || $form->validate()) { $file = $_POST["file"]; - $pathinfo = pathinfo($file); // Forge prefixes files with "uploadfile-xxxxxxx" for uniqueness - $name = preg_replace("/uploadfile-[^-]+-(.*)/", '$1', $pathinfo["basename"]); - $name = legal_file::smash_extensions($name); + $name = preg_replace("/uploadfile-[^-]+-(.*)/", '$1', basename($file)); try { list ($width, $height, $mime_type, $extension) = photo::get_file_metadata($file); - // Force correct, legal extension type on file, which will be of our canonical type - // (i.e. all lowercase, jpg instead of jpeg, etc.). This renaming prevents the issues + // Sanitize filename, which ensures a valid extension. This renaming prevents the issues // addressed in ticket #1855, where an image that looked valid (header said jpg) with a // php extension was previously accepted without changing its extension. - $name = legal_file::change_extension($name, $extension); + $name = legal_file::sanitize_filename($name, $extension, "photo"); } catch (Exception $e) { message::error(t("Invalid or unidentifiable image file")); @unlink($file); -- cgit v1.2.3