diff options
author | Chad Kieffer <ckieffer@gmail.com> | 2009-06-02 22:55:23 -0600 |
---|---|---|
committer | Chad Kieffer <ckieffer@gmail.com> | 2009-06-02 22:55:23 -0600 |
commit | 1df6db45dc569cc6f18a9d1622fce5ebe62f8d30 (patch) | |
tree | e4e8cac619bc2443c3e26d0f87a25c51f8a3b273 | |
parent | 2bd8051c28621f6c25a3f85b73da2f94d62440f2 (diff) | |
parent | dde5fb96ee9db5a67b286ea4ac4f35190453a6ef (diff) |
Merge branch 'master' of git@github.com:gallery/gallery3
73 files changed, 909 insertions, 519 deletions
@@ -7,27 +7,38 @@ interface. SECURITY (& INTENDED AUDIENCE): -This is the fourth technology preview of Gallery 3.0 and as such -it is not intended to be installed on public websites yet. +This is the first beta release of Gallery 3.0 and while it's not a +finished product, it's heading into the home stretch. You can install +it on public websites, but remember that until the final release is +out, we make no guarantees that it won't do bad things. + Note: - - We've contracted a professional security audit, but not all issues - have been resolved in this release. There may be serious security - vulnerabilities. - - A series of key features are yet to be added (e.g. a way to - - There is no guaranteed upgrade path yet. You *will* be forced to - reinstall Gallery 3 to use the next public release. + - We've contracted a professional security audit, received their results + and resolved all the really dire issues they found. There are still some + issues, but they are more along the lines of "you should ask for the old + password as a confirmation when you let users change their password". + Ie, they're issues but they're not really bad. We'll take care of them + before the final release. + - Most of the key features are in, but some of them (notably the "add from + server" and "organize album" features) are probably going to be completely + rewritten. + - Starting with beta 1 we're going to offer an upgrade path so you won't + have to reinstall and start all over (yay!) + +The intended audience of this release is folks who are willing to live +a little bit on the edge. We'll do our best to take care of your data +and your security, but we might screw it up here or there. We welcome +theme and module developers to play with this release and start +turning out slick new designs for our happy users. -The intended audience of this release are enthusiasts, designers and -module developers. This is the right time to do a test drive with the -improved user interface and to start developing feature extensions and -designing new themes. +Did you find a security flaw? Please email security@gallery.menalto.com +with the details and we'll fix it ASAP! -Please send your feedback to gallery-devel@lists.sourceforge.net. SUPPORTED CONFIGURATION: - Platform: Linux / Unix. - Web server: Apache 2.2 and newer. - - PHP 5.2 and newer (PHP's safe_mode must be disabled and simplexml, + - PHP 5.2.3 and newer (PHP's safe_mode must be disabled and simplexml, filter, and json must be installed). - Database: MySQL 5 and newer. @@ -48,8 +59,17 @@ INSTALLATION FROM THE COMMAND LINE: -x Table prefix (default: ) -Questions, problems: - - Check out the gallery3 FAQ http://codex.gallery2.org/Gallery3:FAQ - - Email gallery-devel@lists.sourceforge.net +BUGS? +Go to http://apps.sourceforge.net/trac/gallery/ click the "login" link +and log in with your SourceForge username and password, then click the +"new ticket" button. Mark any issues you find with the "Beta 2" +milestone and we'll try to get 'em done for the next release. + + +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 + - Email gallery-devel@lists.sourceforge.net + @@ -48,11 +48,16 @@ define('SYSPATH', strtr(realpath('system') . '/', DIRECTORY_SEPARATOR, '/')); // Force a test run if we're in command line mode. if (PHP_SAPI == 'cli') { - array_splice($_SERVER['argv'], 1, 0, 'gallery_unit_test'); - define('TEST_MODE', 1); - @mkdir('test/var/logs', 0777, true); - define('VARPATH', strtr(realpath('test/var') . '/', DIRECTORY_SEPARATOR, '/')); - @copy("var/database.php", VARPATH . "database.php"); + if ($_SERVER['argv'][1] != "package") { + array_splice($_SERVER['argv'], 1, 0, 'gallery_unit_test'); + define('TEST_MODE', 1); + @mkdir('test/var/logs', 0777, true); + define('VARPATH', strtr(realpath('test/var') . '/', DIRECTORY_SEPARATOR, '/')); + @copy("var/database.php", VARPATH . "database.php"); + } else { + define('TEST_MODE', 0); + define('VARPATH', strtr(realpath('var') . '/', DIRECTORY_SEPARATOR, '/')); + } } else { define('TEST_MODE', 0); define('VARPATH', strtr(realpath('var') . '/', DIRECTORY_SEPARATOR, '/')); diff --git a/installer/index.php b/installer/index.php index 6b1d434b..317f27b8 100644 --- a/installer/index.php +++ b/installer/index.php @@ -27,7 +27,7 @@ if (version_compare(PHP_VERSION, "5.2.3", "<")) { } require(DOCROOT . "installer/installer.php"); -if (php_sapi_name() == "cli") { +if (PHP_SAPI == "cli") { include("cli.php"); } else { if (@$_GET["page"] == "check") { diff --git a/installer/install.sql b/installer/install.sql index 7109236f..0aaf8e79 100755 --- a/installer/install.sql +++ b/installer/install.sql @@ -333,6 +333,6 @@ CREATE TABLE {vars} ( `value` text, PRIMARY KEY (`id`), UNIQUE KEY `module_name` (`module_name`,`name`) -) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; -INSERT INTO {vars} VALUES (1,'gallery','active_site_theme','default'),(2,'gallery','active_admin_theme','admin_default'),(3,'gallery','page_size','9'),(4,'gallery','thumb_size','200'),(5,'gallery','resize_size','640'),(6,'gallery','default_locale','en_US'),(7,'gallery','image_quality','75'),(9,'gallery','blocks_dashboard_sidebar','a:4:{i:1804289383;a:2:{i:0;s:7:\"gallery\";i:1;s:11:\"block_adder\";}i:846930886;a:2:{i:0;s:7:\"gallery\";i:1;s:5:\"stats\";}i:1681692777;a:2:{i:0;s:7:\"gallery\";i:1;s:13:\"platform_info\";}i:1714636915;a:2:{i:0;s:7:\"gallery\";i:1;s:12:\"project_news\";}}'),(14,'gallery','blocks_dashboard_center','a:4:{i:1957747793;a:2:{i:0;s:7:\"gallery\";i:1;s:7:\"welcome\";}i:424238335;a:2:{i:0;s:7:\"gallery\";i:1;s:12:\"photo_stream\";}i:719885386;a:2:{i:0;s:7:\"gallery\";i:1;s:11:\"log_entries\";}i:1649760492;a:2:{i:0;s:7:\"comment\";i:1;s:15:\"recent_comments\";}}'),(17,'gallery','version','3.0 pre-beta git'),(18,'gallery','choose_default_tookit','1'),(19,'gallery','credits','Powered by <a href=\"%url\">Gallery %version</a>'),(21,'comment','spam_caught','0'); +INSERT INTO {vars} VALUES (1,'gallery','active_site_theme','default'),(2,'gallery','active_admin_theme','admin_default'),(3,'gallery','page_size','9'),(4,'gallery','thumb_size','200'),(5,'gallery','resize_size','640'),(6,'gallery','default_locale','en_US'),(7,'gallery','image_quality','75'),(9,'gallery','blocks_dashboard_sidebar','a:4:{i:2;a:2:{i:0;s:7:\"gallery\";i:1;s:11:\"block_adder\";}i:3;a:2:{i:0;s:7:\"gallery\";i:1;s:5:\"stats\";}i:4;a:2:{i:0;s:7:\"gallery\";i:1;s:13:\"platform_info\";}i:5;a:2:{i:0;s:7:\"gallery\";i:1;s:12:\"project_news\";}}'),(14,'gallery','blocks_dashboard_center','a:4:{i:6;a:2:{i:0;s:7:\"gallery\";i:1;s:7:\"welcome\";}i:7;a:2:{i:0;s:7:\"gallery\";i:1;s:12:\"photo_stream\";}i:8;a:2:{i:0;s:7:\"gallery\";i:1;s:11:\"log_entries\";}i:9;a:2:{i:0;s:7:\"comment\";i:1;s:15:\"recent_comments\";}}'),(17,'gallery','version','3.0 pre-beta git'),(18,'gallery','choose_default_tookit','1'),(19,'gallery','credits','Powered by <a href=\"%url\">Gallery %version</a>'),(21,'comment','spam_caught','0'); diff --git a/installer/package.php b/installer/package.php deleted file mode 100755 index 591f12d0..00000000 --- a/installer/package.php +++ /dev/null @@ -1,239 +0,0 @@ -#!/usr/bin/php -f -<?php -define('EXT', '.php'); -define("DOCROOT", realpath("../") . "/"); -define("VARPATH", DOCROOT . "var/"); - -// Define application and system paths -define("SYSPATH", DOCROOT . "system/"); -define('APPPATH', DOCROOT . "application/"); -define('MODPATH', DOCROOT . "modules/"); -define('THEMEPATH', DOCROOT . "themes/"); - -$modules_list = null; -$active_modules = null; - -function setup($config) { - system("rm -rf tmp"); - mkdir("tmp"); - - // Lets backup the database - $conn = $config["connection"]; - do_system("mysqldump -u{$conn['user']} -p{$conn['pass']} -h{$conn['host']} --add-drop-table " . - "--compact {$conn['database']} > tmp/dump.sql"); - - $db = Database::instance(); - // Drop all tables - foreach ($db->list_tables() as $table) { - $db->query("DROP TABLE IF EXISTS `$table`"); - } - - // Now the var directory - rename (VARPATH, realpath("tmp/var")); - mkdir(VARPATH); - copy(realpath("tmp/var/database.php"), VARPATH . "database.php"); - - $db->clear_cache(); - $modules_list = module::$modules; - $active_modules = module::$active; - - module::$modules = array(); - module::$active = array(); - - // Use a known random seed so that subsequent packaging runs will reuse the same random - // numbers, keeping our install.sql file more stable. - srand(0); -} - -function reset_install($config) { - // Reset the var path - system("rm -rf " . VARPATH); - rename (realpath("tmp/var"), VARPATH); - - $db = Database::instance(); - // Drop all tables - foreach ($db->list_tables() as $table) { - $db->query("DROP TABLE IF EXISTS `$table`"); - } - - // Lets restorep the database - $conn = $config["connection"]; - do_system("mysql -u{$conn['user']} -p{$conn['pass']} {$conn['database']} " . - " < tmp/dump.sql"); - - // Clear any database caching - $db->clear_cache(); - - module::$modules = $modules_list; - module::$active = $active_modules; -} - -function do_system($command) { - exec($command, $output, $status); - if ($status) { - throw new Exception("$command\nFailed to dump database\n" . implode("\n", $output)); - } -} - -function kohana_bootstrap() { - define('KOHANA_VERSION', '2.3.3'); - define('KOHANA_CODENAME', 'aegolius'); - - // Test of Kohana is running in Windows - define('KOHANA_IS_WIN', DIRECTORY_SEPARATOR === '\\'); - - // Kohana benchmarks are prefixed to prevent collisions - define('SYSTEM_BENCHMARK', 'system_benchmark'); - - // Load benchmarking support - require SYSPATH.'core/Benchmark'.EXT; - - // Start total_execution - Benchmark::start(SYSTEM_BENCHMARK.'_total_execution'); - - // Start kohana_loading - Benchmark::start(SYSTEM_BENCHMARK.'_kohana_loading'); - - // Load core files - require SYSPATH.'core/utf8'.EXT; - require SYSPATH.'core/Event'.EXT; - require SYSPATH.'core/Kohana'.EXT; - - // Prepare the environment - Kohana::setup(); - // End kohana_loading - Benchmark::stop(SYSTEM_BENCHMARK.'_kohana_loading'); - - // Prepare the system - Event::run('system.ready'); - - // Clean up and exit (this basically shuts down output buffering - Event::run('system.shutdown'); -} - -function install() { - gallery_installer::install(true); - module::load_modules(); - - foreach (array("user", "comment", "organize", "info", "rss", - "search", "slideshow", "tag") as $module_name) { - module::install($module_name); - module::activate($module_name); - } -} - -function dump_database() { - // We now have a clean install with just the packages that we want. Make sure that the - // database is clean too. - $db = Database::instance(); - $db->query("TRUNCATE {sessions}"); - $db->query("TRUNCATE {logs}"); - $db->query("DELETE FROM {vars} WHERE `module_name` = 'gallery' AND `name` = '_cache'"); - $db->update("users", array("password" => ""), array("id" => 1)); - $db->update("users", array("password" => ""), array("id" => 2)); - - $dbconfig = Kohana::config('database.default'); - $conn = $dbconfig["connection"]; - $pass = $conn["pass"] ? "-p{$conn['pass']}" : ""; - $sql_file = DOCROOT . "installer/install.sql"; - if (!is_writable($sql_file)) { - throw new Exception("$sql_file is not writeable"); - return; - } - do_system("mysqldump --compact --add-drop-table -h{$conn['host']} " . - "-u{$conn['user']} $pass {$conn['database']} > $sql_file"); - - // Post-process the sql file - $buf = ""; - $root = ORM::factory("item", 1); - $root_created_timestamp = $root->created; - $root_updated_timestamp = $root->updated; - foreach (file($sql_file) as $line) { - // Prefix tables - $line = preg_replace( - "/(CREATE TABLE|IF EXISTS|INSERT INTO) `{$dbconfig['table_prefix']}(\w+)`/", "\\1 {\\2}", - $line); - - // Normalize dates - $line = preg_replace("/,$root_created_timestamp,/", ",UNIX_TIMESTAMP(),", $line); - $line = preg_replace("/,$root_updated_timestamp,/", ",UNIX_TIMESTAMP(),", $line); - $buf .= $line; - } - $fd = fopen($sql_file, "wb"); - fwrite($fd, $buf); - fclose($fd); -} - -function dump_var() { - $objects = new RecursiveIteratorIterator( - new RecursiveDirectoryIterator(VARPATH), - RecursiveIteratorIterator::SELF_FIRST); - - $var_file = DOCROOT . "installer/init_var.php"; - if (!is_writable($var_file)) { - throw new Exception("$var_file is not writeable"); - return; - } - - $paths = array(); - foreach($objects as $name => $file) { - if ($file->getBasename() == "database.php") { - continue; - } else if (basename($file->getPath()) == "logs") { - continue; - } - - if ($file->isDir()) { - $paths[] = "VARPATH . \"" . substr($name, strlen(VARPATH)) . "\""; - } else { - // @todo: serialize non-directories - throw new Exception("Unknown file: $name"); - } - } - // Sort the paths so that the var file is stable - sort($paths); - - $fd = fopen($var_file, "w"); - fwrite($fd, "<?php defined(\"SYSPATH\") or die(\"No direct script access.\") ?>\n"); - fwrite($fd, "<?php\n"); - foreach ($paths as $path) { - fwrite($fd, "!file_exists($path) && mkdir($path);\n"); - } - fclose($fd); -} - -// Bootstrap kohana so we can use it as we need it -kohana_bootstrap(); - -$config = Kohana::config("database.default"); - -try { - // Empty the tmp directory, backup the database, and copy the var directory - setup($config); -} catch (Exception $e) { - print $e->getTrace(); - return; -} - -try { - // Install the standard modules - install(); - - // Dump the empty gallery3 database and format it for the installer - dump_database(); - - // Dump the var directory - dump_var(); -} catch (Exception $e) { - print $e->getTrace(); -} - -try { - // Reset the Gallery3 installation - reset_install($config); -} catch (Exception $e) { - print $e->getTrace(); -} - -system("rm -rf tmp"); -?>
\ No newline at end of file diff --git a/modules/akismet/controllers/admin_akismet.php b/modules/akismet/controllers/admin_akismet.php index 7485f283..9ba89bd4 100644 --- a/modules/akismet/controllers/admin_akismet.php +++ b/modules/akismet/controllers/admin_akismet.php @@ -22,6 +22,9 @@ class Admin_Akismet_Controller extends Admin_Controller { $form = akismet::get_configure_form(); if (request::method() == "post") { + // @todo move the "post" handler part of this code into a separate function + access::verify_csrf(); + $valid = $form->validate(); if ($valid) { diff --git a/modules/akismet/module.info b/modules/akismet/module.info index dd2df5dd..8655927d 100644 --- a/modules/akismet/module.info +++ b/modules/akismet/module.info @@ -1,3 +1,3 @@ name = Akismet -description = "Filter comments through the <a href="http://akismet.com">Akismet web service</a> to detect and eliminate spam. You'll need a <a href="http://wordpress.com/api-keys/">WordPress.com API</a> key to use it." +description = "Filter comments through the <a href='http://akismet.com'>Akismet web service</a> to detect and eliminate spam. You'll need a <a href='http://wordpress.com/api-keys/'>WordPress.com API</a> key to use it." version = 1 diff --git a/modules/comment/controllers/admin_comments.php b/modules/comment/controllers/admin_comments.php index 50e35d23..3e8d3c46 100644 --- a/modules/comment/controllers/admin_comments.php +++ b/modules/comment/controllers/admin_comments.php @@ -107,6 +107,7 @@ class Admin_Comments_Controller extends Admin_Controller { public function set_state($id, $state) { access::verify_csrf(); + $comment = ORM::factory("comment", $id); $orig = clone $comment; if ($comment->loaded) { @@ -121,6 +122,7 @@ class Admin_Comments_Controller extends Admin_Controller { public function delete_all_spam() { access::verify_csrf(); + ORM::factory("comment") ->where("state", "spam") ->delete_all(); diff --git a/modules/comment/controllers/comments.php b/modules/comment/controllers/comments.php index 930579ac..c48bd380 100644 --- a/modules/comment/controllers/comments.php +++ b/modules/comment/controllers/comments.php @@ -134,6 +134,7 @@ class Comments_Controller extends REST_Controller { */ public function _update($comment) { $item = ORM::factory("item", $comment->item_id); + access::required("view", $item); access::required("edit", $item); $form = comment::get_edit_form($comment); @@ -161,6 +162,7 @@ class Comments_Controller extends REST_Controller { */ public function _delete($comment) { $item = ORM::factory("item", $comment->item_id); + access::required("view", $item); access::required("edit", $item); $comment->delete(); @@ -183,6 +185,9 @@ class Comments_Controller extends REST_Controller { * @see REST_Controller::form_edit($resource) */ public function _form_edit($comment) { + if (!user::active()->admin) { + access::forbidden(); + } print comment::get_edit_form($comment); } } diff --git a/modules/exif/helpers/exif_theme.php b/modules/exif/helpers/exif_theme.php index 14186d15..bb6926d3 100644 --- a/modules/exif/helpers/exif_theme.php +++ b/modules/exif/helpers/exif_theme.php @@ -21,13 +21,13 @@ class exif_theme_Core { static function sidebar_bottom($theme) { $item = $theme->item(); if ($item && $item->is_photo()) { - if (Database::instance() - ->select("key_count") - ->from("exif_records") - ->where("item_id", $item->id) - ->get() - ->current() - ->key_count) { + $record = Database::instance() + ->select("key_count") + ->from("exif_records") + ->where("item_id", $item->id) + ->get() + ->current(); + if ($record && $record->key_count) { $view = new View("exif_sidebar.html"); $view->item = $item; return $view; diff --git a/modules/g2_import/controllers/admin_g2_import.php b/modules/g2_import/controllers/admin_g2_import.php index 25894291..f2969f49 100644 --- a/modules/g2_import/controllers/admin_g2_import.php +++ b/modules/g2_import/controllers/admin_g2_import.php @@ -39,6 +39,8 @@ class Admin_g2_import_Controller extends Admin_Controller { } public function save() { + access::verify_csrf(); + $form = $this->_get_import_form(); if ($form->validate()) { $embed_path = $form->configure_g2_import->embed_path->value; diff --git a/modules/gallery/controllers/admin.php b/modules/gallery/controllers/admin.php index af0f387a..b92a32cd 100644 --- a/modules/gallery/controllers/admin.php +++ b/modules/gallery/controllers/admin.php @@ -22,8 +22,9 @@ class Admin_Controller extends Controller { public function __construct($theme=null) { if (!(user::active()->admin)) { - throw new Exception("@todo UNAUTHORIZED", 401); + access::forbidden(); } + parent::__construct(); } diff --git a/modules/gallery/controllers/admin_dashboard.php b/modules/gallery/controllers/admin_dashboard.php index a1090a6d..3cb97b14 100644 --- a/modules/gallery/controllers/admin_dashboard.php +++ b/modules/gallery/controllers/admin_dashboard.php @@ -29,6 +29,8 @@ class Admin_Dashboard_Controller extends Admin_Controller { } public function add_block() { + access::verify_csrf(); + $form = gallery_block::get_add_block_form(); if ($form->validate()) { list ($module_name, $id) = explode(":", $form->add_block->id->value); @@ -51,6 +53,7 @@ class Admin_Dashboard_Controller extends Admin_Controller { public function remove_block($id) { access::verify_csrf(); + $blocks_center = block_manager::get_active("dashboard_center"); $blocks_sidebar = block_manager::get_active("dashboard_sidebar"); @@ -73,6 +76,7 @@ class Admin_Dashboard_Controller extends Admin_Controller { public function reorder() { access::verify_csrf(); + $active_set = array(); foreach (array("dashboard_sidebar", "dashboard_center") as $location) { foreach (block_manager::get_active($location) as $id => $info) { diff --git a/modules/gallery/controllers/admin_graphics.php b/modules/gallery/controllers/admin_graphics.php index 7e8ef47c..72f8d8e1 100644 --- a/modules/gallery/controllers/admin_graphics.php +++ b/modules/gallery/controllers/admin_graphics.php @@ -43,6 +43,7 @@ class Admin_Graphics_Controller extends Admin_Controller { public function choose($toolkit) { access::verify_csrf(); + if ($toolkit != module::get_var("gallery", "graphics_toolkit")) { module::set_var("gallery", "graphics_toolkit", $toolkit); diff --git a/modules/gallery/controllers/admin_languages.php b/modules/gallery/controllers/admin_languages.php index 1dea733c..4639de89 100644 --- a/modules/gallery/controllers/admin_languages.php +++ b/modules/gallery/controllers/admin_languages.php @@ -31,6 +31,8 @@ class Admin_Languages_Controller extends Admin_Controller { } public function save() { + access::verify_csrf(); + $form = $this->_languages_form(); if ($form->validate()) { module::set_var("gallery", "default_locale", $form->choose_language->locale->value); @@ -41,6 +43,8 @@ class Admin_Languages_Controller extends Admin_Controller { } public function share() { + access::verify_csrf(); + $form = $this->_share_translations_form(); if (!$form->validate()) { // Show the page with form errors diff --git a/modules/gallery/controllers/admin_theme_details.php b/modules/gallery/controllers/admin_theme_details.php index fec1311b..97696df5 100644 --- a/modules/gallery/controllers/admin_theme_details.php +++ b/modules/gallery/controllers/admin_theme_details.php @@ -26,6 +26,8 @@ class Admin_Theme_Details_Controller extends Admin_Controller { } public function save() { + access::verify_csrf(); + $form = theme::get_edit_form_admin(); if ($form->validate()) { module::set_var("gallery", "page_size", $form->edit_theme->page_size->value); diff --git a/modules/gallery/controllers/albums.php b/modules/gallery/controllers/albums.php index 5ccadb37..efde4f09 100644 --- a/modules/gallery/controllers/albums.php +++ b/modules/gallery/controllers/albums.php @@ -24,11 +24,11 @@ class Albums_Controller extends Items_Controller { */ public function _show($album) { if (!access::can("view", $album)) { - if ($album->id != 1) { - access::forbidden(); - } else { + if ($album->id == 1) { print new Theme_View("login_page.html", "album"); return; + } else { + access::forbidden(); } } @@ -77,6 +77,8 @@ class Albums_Controller extends Items_Controller { * @see REST_Controller::_create($resource) */ public function _create($album) { + access::verify_csrf(); + access::required("view", $album); access::required("add", $album); switch ($this->input->post("type")) { @@ -92,6 +94,7 @@ class Albums_Controller extends Items_Controller { } private function _create_album($album) { + access::required("view", $album); access::required("add", $album); $form = album::get_add_form($album); @@ -120,6 +123,7 @@ class Albums_Controller extends Items_Controller { } private function _create_photo($album) { + access::required("view", $album); access::required("add", $album); // If we set the content type as JSON, it triggers saving the result as @@ -153,6 +157,8 @@ class Albums_Controller extends Items_Controller { * @see REST_Controller::_update($resource) */ public function _update($album) { + access::verify_csrf(); + access::required("view", $album); access::required("edit", $album); $form = album::get_edit_form($album); @@ -202,6 +208,7 @@ class Albums_Controller extends Items_Controller { */ public function _form_add($album_id) { $album = ORM::factory("item", $album_id); + access::required("view", $album); access::required("add", $album); switch ($this->input->get("type")) { @@ -223,6 +230,7 @@ class Albums_Controller extends Items_Controller { * @see REST_Controller::_form_add($parameters) */ public function _form_edit($album) { + access::required("view", $album); access::required("edit", $album); print album::get_edit_form($album); diff --git a/modules/gallery/controllers/file_proxy.php b/modules/gallery/controllers/file_proxy.php index 1901bd9f..1f885e53 100644 --- a/modules/gallery/controllers/file_proxy.php +++ b/modules/gallery/controllers/file_proxy.php @@ -32,13 +32,12 @@ class File_Proxy_Controller extends Controller { $request_uri = $this->input->server("REQUEST_URI"); $request_uri = preg_replace("/\?.*/", "", $request_uri); - // Firefox converts ~ to %7E breaking our url comparison, below. Convert that back here. - $request_uri = str_replace("%7E", "~", $request_uri); + // Unescape %7E ("~") and %20 (" ") + $request_uri = str_replace(array("%7E", "%20"), array("~", " "), $request_uri); // var_uri: http://example.com/gallery3/var/ $var_uri = url::file("var/"); - // Make sure that the request is for a file inside var $offset = strpos($request_uri, $var_uri); if ($offset === false) { diff --git a/modules/gallery/controllers/l10n_client.php b/modules/gallery/controllers/l10n_client.php index 17520051..aa93a758 100644 --- a/modules/gallery/controllers/l10n_client.php +++ b/modules/gallery/controllers/l10n_client.php @@ -20,13 +20,40 @@ class L10n_Client_Controller extends Controller { public function save() { access::verify_csrf(); - user::active()->admin or access::forbidden(); + if (!user::active()->admin) { + access::forbidden(); + } - $input = Input::instance(); - $message = $input->post("l10n-message-source"); - $translation = $input->post("l10n-edit-target"); - $key = I18n::get_message_key($message); $locale = I18n::instance()->locale(); + $input = Input::instance(); + $key = $input->post("l10n-message-key"); + + $root_message = ORM::factory("incoming_translation") + ->where(array("key" => $key, + "locale" => "root")) + ->find(); + + if (!$root_message->loaded) { + throw new Exception("@todo bad request data / illegal state"); + } + $is_plural = I18n::is_plural_message(unserialize($root_message->message)); + + if ($is_plural) { + $plural_forms = l10n_client::plural_forms($locale); + $translation = array(); + foreach($plural_forms as $plural_form) { + $value = $input->post("l10n-edit-plural-translation-$plural_form"); + if (null === $value || !is_string($value)) { + throw new Exception("@todo bad request data"); + } + $translation[$plural_form] = $value; + } + } else { + $translation = $input->post("l10n-edit-translation"); + if (null === $translation || !is_string($translation)) { + throw new Exception("@todo bad request data"); + } + } $entry = ORM::factory("outgoing_translation") ->where(array("key" => $key, @@ -36,7 +63,7 @@ class L10n_Client_Controller extends Controller { if (!$entry->loaded) { $entry->key = $key; $entry->locale = $locale; - $entry->message = serialize($message); + $entry->message = $root_message->message; $entry->base_revision = null; } @@ -58,6 +85,9 @@ class L10n_Client_Controller extends Controller { public function toggle_l10n_mode() { access::verify_csrf(); + if (!user::active()->admin) { + access::forbidden(); + } $session = Session::instance(); $session->set("l10n_mode", @@ -66,19 +96,6 @@ class L10n_Client_Controller extends Controller { url::redirect("albums/1"); } - private static function _l10n_client_form() { - $form = new Forge("l10n_client/save", "", "post", array("id" => "gL10nClientSaveForm")); - $group = $form->group("l10n_message"); - $group->hidden("l10n-message-source")->value(""); - $group->textarea("l10n-edit-target"); - $group->submit("l10n-edit-save")->value(t("Save translation")); - // TODO(andy_st): Avoiding multiple submit buttons for now (hassle with jQuery form plugin). - // $group->submit("l10n-edit-copy")->value(t("Copy source")); - // $group->submit("l10n-edit-clear")->value(t("Clear")); - - return $form; - } - private static function _l10n_client_search_form() { $form = new Forge("l10n_client/search", "", "post", array("id" => "gL10nSearchForm")); $group = $form->group("l10n_search"); @@ -90,36 +107,46 @@ class L10n_Client_Controller extends Controller { public static function l10n_form() { $calls = I18n::instance()->call_log(); + $locale = I18n::instance()->locale(); if ($calls) { + $translations = array(); + foreach (Database::instance() + ->select("key", "translation") + ->from("incoming_translations") + ->where(array("locale" => $locale)) + ->get() + ->as_array() as $row) { + $translations[$row->key] = unserialize($row->translation); + } + // Override incoming with outgoing... + foreach (Database::instance() + ->select("key", "translation") + ->from("outgoing_translations") + ->where(array("locale" => $locale)) + ->get() + ->as_array() as $row) { + $translations[$row->key] = unserialize($row->translation); + } + $string_list = array(); - foreach ($calls as $call) { + $cache = array(); + foreach ($calls as $key => $call) { list ($message, $options) = $call; - // Note: Don't interpolate placeholders for the actual translation input field. - // TODO: Use $options to generate a preview. - if (is_array($message)) { - // TODO: Handle plural forms. - // Translate each message. If it has a plural form, get - // the current locale's plural rules and all plural translations. - continue; - } - $source = $message; - $translation = ''; - $options_for_raw_translation = array(); - if (isset($options['count'])) { - $options_for_raw_translation['count'] = $options['count']; - } - if (I18n::instance()->has_translation($message, $options_for_raw_translation)) { - $translation = I18n::instance()->translate($message, $options_for_raw_translation); - } - $string_list[] = array('source' => $source, + // Ensure that the message is in the DB + l10n_scanner::process_message($message, $cache); + // Note: Not interpolating placeholders for the actual translation input field. + // TODO: Might show a preview w/ interpolations (using $options) + $translation = isset($translations[$key]) ? $translations[$key] : ''; + $string_list[] = array('source' => $message, + 'key' => $key, 'translation' => $translation); } $v = new View('l10n_client.html'); $v->string_list = $string_list; - $v->l10n_form = self::_l10n_client_form(); $v->l10n_search_form = self::_l10n_client_search_form(); + $v->plural_forms = l10n_client::plural_forms($locale); return $v; } diff --git a/modules/gallery/controllers/move.php b/modules/gallery/controllers/move.php index 130c247f..93ef05a6 100644 --- a/modules/gallery/controllers/move.php +++ b/modules/gallery/controllers/move.php @@ -20,6 +20,7 @@ class Move_Controller extends Controller { public function browse($source_id) { $source = ORM::factory("item", $source_id); + access::required("view", $source); access::required("edit", $source); $view = new View("move_browse.html"); @@ -33,6 +34,11 @@ class Move_Controller extends Controller { $source = ORM::factory("item", $source_id); $target = ORM::factory("item", $this->input->post("target_id")); + access::required("view", $source); + access::required("edit", $source); + access::required("view", $target); + access::required("edit", $target); + item::move($source, $target); print json_encode( @@ -43,8 +49,11 @@ class Move_Controller extends Controller { public function show_sub_tree($source_id, $target_id) { $source = ORM::factory("item", $source_id); $target = ORM::factory("item", $target_id); + access::required("view", $source); access::required("edit", $source); access::required("view", $target); + // show targets even if they're not editable because they may contain children which *are* + // editable print $this->_get_tree_html($source, $target); } diff --git a/modules/gallery/controllers/movies.php b/modules/gallery/controllers/movies.php index 55bbb0e5..86b0f177 100644 --- a/modules/gallery/controllers/movies.php +++ b/modules/gallery/controllers/movies.php @@ -66,6 +66,8 @@ class Movies_Controller extends Items_Controller { * @see REST_Controller::_update($resource) */ public function _update($photo) { + access::verify_csrf(); + access::required("view", $photo); access::required("edit", $photo); $form = photo::get_edit_form($photo); @@ -108,6 +110,7 @@ class Movies_Controller extends Items_Controller { * @see REST_Controller::_form_edit($resource) */ public function _form_edit($photo) { + access::required("view", $photo); access::required("edit", $photo); print photo::get_edit_form($photo); } diff --git a/modules/gallery/controllers/package.php b/modules/gallery/controllers/package.php new file mode 100644 index 00000000..f5146fc8 --- /dev/null +++ b/modules/gallery/controllers/package.php @@ -0,0 +1,169 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2009 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ +class Package_Controller extends Controller { + function index() { + if (PHP_SAPI != 'cli') { + Kohana::show_404(); + } + + try { + $this->_reset(); // empty and reinstall the standard modules + $this->_dump_database(); // Dump the database + $this->_dump_var(); // Dump the var directory + } catch (Exception $e) { + print $e->getTraceAsString(); + return; + } + + print "Successfully wrote install.sql and init_var.php\n"; + } + + private function _reset() { + $db = Database::instance(); + + // Drop all tables + foreach ($db->list_tables() as $table) { + $db->query("DROP TABLE IF EXISTS `$table`"); + } + + // Clean out data + dir::unlink(VARPATH . "uploads"); + dir::unlink(VARPATH . "albums"); + dir::unlink(VARPATH . "resizes"); + dir::unlink(VARPATH . "thumbs"); + dir::unlink(VARPATH . "modules"); + dir::unlink(VARPATH . "tmp"); + + $db->clear_cache(); + module::$modules = array(); + module::$active = array(); + + // Use a known random seed so that subsequent packaging runs will reuse the same random + // numbers, keeping our install.sql file more stable. + srand(0); + + gallery_installer::install(true); + module::load_modules(); + + foreach (array("user", "comment", "organize", "info", "rss", + "search", "slideshow", "tag") as $module_name) { + module::install($module_name); + module::activate($module_name); + } + } + + private function _dump_database() { + // We now have a clean install with just the packages that we want. Make sure that the + // database is clean too. + $i = 1; + foreach (array("blocks_dashboard_sidebar", "blocks_dashboard_center") as $key) { + $blocks = array(); + foreach (unserialize(module::get_var("gallery", $key)) as $rnd => $value) { + $blocks[++$i] = $value; + } + module::set_var("gallery", $key, serialize($blocks)); + } + + $db = Database::instance(); + $db->query("TRUNCATE {sessions}"); + $db->query("TRUNCATE {logs}"); + $db->query("DELETE FROM {vars} WHERE `module_name` = 'core' AND `name` = '_cache'"); + $db->update("users", array("password" => ""), array("id" => 1)); + $db->update("users", array("password" => ""), array("id" => 2)); + + $dbconfig = Kohana::config('database.default'); + $conn = $dbconfig["connection"]; + $pass = $conn["pass"] ? "-p{$conn['pass']}" : ""; + $sql_file = DOCROOT . "installer/install.sql"; + if (!is_writable($sql_file)) { + print "$sql_file is not writeable"; + return; + } + $command = "mysqldump --compact --add-drop-table -h{$conn['host']} " . + "-u{$conn['user']} $pass {$conn['database']} > $sql_file"; + exec($command, $output, $status); + if ($status) { + print "<pre>"; + print "$command\n"; + print "Failed to dump database\n"; + print implode("\n", $output); + return; + } + + // Post-process the sql file + $buf = ""; + $root = ORM::factory("item", 1); + $root_created_timestamp = $root->created; + $root_updated_timestamp = $root->updated; + foreach (file($sql_file) as $line) { + // Prefix tables + $line = preg_replace( + "/(CREATE TABLE|IF EXISTS|INSERT INTO) `{$dbconfig['table_prefix']}(\w+)`/", "\\1 {\\2}", + $line); + + // Normalize dates + $line = preg_replace("/,$root_created_timestamp,/", ",UNIX_TIMESTAMP(),", $line); + $line = preg_replace("/,$root_updated_timestamp,/", ",UNIX_TIMESTAMP(),", $line); + $buf .= $line; + } + $fd = fopen($sql_file, "wb"); + fwrite($fd, $buf); + fclose($fd); + } + + private function _dump_var() { + $objects = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator(VARPATH), + RecursiveIteratorIterator::SELF_FIRST); + + $var_file = DOCROOT . "installer/init_var.php"; + if (!is_writable($var_file)) { + print "$var_file is not writeable"; + return; + } + + $paths = array(); + foreach($objects as $name => $file){ + if ($file->getBasename() == "database.php") { + continue; + } else if (basename($file->getPath()) == "logs") { + continue; + } + + if ($file->isDir()) { + $paths[] = "VARPATH . \"" . substr($name, strlen(VARPATH)) . "\""; + } else { + // @todo: serialize non-directories + print "Unknown file: $name"; + return; + } + } + // Sort the paths so that the var file is stable + sort($paths); + + $fd = fopen($var_file, "w"); + fwrite($fd, "<?php defined(\"SYSPATH\") or die(\"No direct script access.\") ?>\n"); + fwrite($fd, "<?php\n"); + foreach ($paths as $path) { + fwrite($fd, "!file_exists($path) && mkdir($path);\n"); + } + fclose($fd); + } +}
\ No newline at end of file diff --git a/modules/gallery/controllers/permissions.php b/modules/gallery/controllers/permissions.php index b0cee303..c776a0fd 100644 --- a/modules/gallery/controllers/permissions.php +++ b/modules/gallery/controllers/permissions.php @@ -20,6 +20,7 @@ class Permissions_Controller extends Controller { function browse($id) { $item = ORM::factory("item", $id); + access::required("view", $item); access::required("edit", $item); if (!$item->is_album()) { @@ -37,6 +38,7 @@ class Permissions_Controller extends Controller { function form($id) { $item = ORM::factory("item", $id); + access::required("view", $item); access::required("edit", $item); if (!$item->is_album()) { @@ -48,9 +50,11 @@ class Permissions_Controller extends Controller { function change($command, $group_id, $perm_id, $item_id) { access::verify_csrf(); + $group = ORM::factory("group", $group_id); $perm = ORM::factory("permission", $perm_id); $item = ORM::factory("item", $item_id); + access::required("view", $item); access::required("edit", $item); if ($group->loaded && $perm->loaded && $item->loaded) { diff --git a/modules/gallery/controllers/photos.php b/modules/gallery/controllers/photos.php index 5d4040cf..2de51bc7 100644 --- a/modules/gallery/controllers/photos.php +++ b/modules/gallery/controllers/photos.php @@ -62,10 +62,13 @@ class Photos_Controller extends Items_Controller { print $template; } + /** * @see REST_Controller::_update($resource) */ public function _update($photo) { + access::verify_csrf(); + access::required("view", $photo); access::required("edit", $photo); $form = photo::get_edit_form($photo); @@ -110,7 +113,9 @@ class Photos_Controller extends Items_Controller { * @see REST_Controller::_form_edit($resource) */ public function _form_edit($photo) { + access::required("view", $photo); access::required("edit", $photo); + print photo::get_edit_form($photo); } } diff --git a/modules/gallery/controllers/quick.php b/modules/gallery/controllers/quick.php index 643dce30..6efcb9de 100644 --- a/modules/gallery/controllers/quick.php +++ b/modules/gallery/controllers/quick.php @@ -19,8 +19,8 @@ */ class Quick_Controller extends Controller { public function pane($id) { - $item = ORM::factory("item", $id); - if (!$item->loaded) { + $item = model_cache::get("item", $id); + if (!access::can("view", $item) || !access::can("edit", $item)) { return ""; } @@ -32,10 +32,9 @@ class Quick_Controller extends Controller { public function rotate($id, $dir) { access::verify_csrf(); - $item = ORM::factory("item", $id); - if (!$item->loaded) { - return ""; - } + $item = model_cache::get("item", $id); + access::required("view", $item); + access::required("edit", $item); $degrees = 0; switch($dir) { @@ -82,14 +81,21 @@ class Quick_Controller extends Controller { public function make_album_cover($id) { access::verify_csrf(); - item::make_album_cover(ORM::factory("item", $id)); + + $item = model_cache::get("item", $id); + access::required("view", $item); + access::required("view", $item->parent()); + access::required("edit", $item->parent()); + + item::make_album_cover($item); print json_encode(array("result" => "success")); } public function delete($id) { access::verify_csrf(); - $item = ORM::factory("item", $id); + $item = model_cache::get("item", $id); + access::required("view", $item); access::required("edit", $item); if ($item->is_album()) { @@ -110,8 +116,10 @@ class Quick_Controller extends Controller { } public function form_edit($id) { - $item = ORM::factory("item", $id); + $item = model_cache::get("item", $id); + access::required("view", $item); access::required("edit", $item); + if ($item->is_album()) { $form = album::get_edit_form($item); } else { diff --git a/modules/gallery/controllers/rest.php b/modules/gallery/controllers/rest.php index 11a6bbac..2edf079f 100644 --- a/modules/gallery/controllers/rest.php +++ b/modules/gallery/controllers/rest.php @@ -86,21 +86,20 @@ class REST_Controller extends Controller { return Kohana::show_404(); } - if ($request_method != "get") { - access::verify_csrf(); - } - switch ($request_method) { case "get": return $this->_show($resource); case "put": + access::verify_csrf(); return $this->_update($resource); case "delete": + access::verify_csrf(); return $this->_delete($resource); case "post": + access::verify_csrf(); return $this->_create($resource); } } @@ -111,17 +110,18 @@ class REST_Controller extends Controller { throw new Exception("@todo ERROR_MISSING_RESOURCE_TYPE"); } - // @todo this needs security checks $resource = ORM::factory($this->resource_type, $resource_id); if (!$resource->loaded) { return Kohana::show_404(); } + // Security checks must be performed in _form_edit return $this->_form_edit($resource); } /* We're adding a new item, pass along any additional parameters. */ public function form_add($parameters) { + // Security checks must be performed in _form_add return $this->_form_add($parameters); } diff --git a/modules/gallery/controllers/simple_uploader.php b/modules/gallery/controllers/simple_uploader.php index ec2a5ab9..dfbd4f17 100644 --- a/modules/gallery/controllers/simple_uploader.php +++ b/modules/gallery/controllers/simple_uploader.php @@ -20,6 +20,7 @@ class Simple_Uploader_Controller extends Controller { public function app($id) { $item = ORM::factory("item", $id); + access::required("view", $item); access::required("add", $item); $v = new View("simple_uploader.html"); @@ -33,13 +34,13 @@ class Simple_Uploader_Controller extends Controller { public function add_photo($id) { $album = ORM::factory("item", $id); + access::required("view", $album); access::required("add", $album); access::verify_csrf(); $file_validation = new Validation($_FILES); $file_validation->add_rules("Filedata", "upload::valid", "upload::type[gif,jpg,png,flv,mp4]"); if ($file_validation->validate()) { - // SimpleUploader.swf does not yet call /start directly, so simulate it here for now. if (!batch::in_progress()) { batch::start(); @@ -48,7 +49,7 @@ class Simple_Uploader_Controller extends Controller { $temp_filename = upload::save("Filedata"); try { $name = substr(basename($temp_filename), 10); // Skip unique identifier Kohana adds - $title = $this->convert_filename_to_title($name); + $title = item::convert_filename_to_title($name); $path_info = pathinfo($temp_filename); if (array_key_exists("extension", $path_info) && in_array(strtolower($path_info["extension"]), array("flv", "mp4"))) { @@ -69,18 +70,11 @@ class Simple_Uploader_Controller extends Controller { print "File Received"; } - /** - * We should move this into a helper somewhere.. but where is appropriate? - */ - private function convert_filename_to_title($filename) { - $title = strtr($filename, "_", " "); - $title = preg_replace("/\..*?$/", "", $title); - $title = preg_replace("/ +/", " ", $title); - return $title; - } - public function finish() { + access::verify_csrf(); + batch::stop(); print json_encode(array("result" => "success")); } + } diff --git a/modules/gallery/css/l10n_client.css b/modules/gallery/css/l10n_client.css index 8973715f..6616f511 100644 --- a/modules/gallery/css/l10n_client.css +++ b/modules/gallery/css/l10n_client.css @@ -145,7 +145,6 @@ how it wants to round. */ margin:0em; } - #l10n-client-string-editor { display:none; float:left; @@ -168,18 +167,13 @@ how it wants to round. */ #gL10nClientSaveForm { padding:0em;} - #gL10nClientSaveForm .form-textarea { - height:13em; - font-size:1em; line-height:1.25em; - width:95%;} - - #gL10nClientSaveForm .form-submit { - margin-top: 0em;} - - #l10n-client form ul, #l10n-client form li, #l10n-client form input[type=submit], #l10n-client form input[type=text] { display: inline ! important ; } + +#l10n-client form .hidden { + display: none; +} diff --git a/modules/gallery/helpers/gallery.php b/modules/gallery/helpers/gallery.php index 34671f1f..1686571c 100644 --- a/modules/gallery/helpers/gallery.php +++ b/modules/gallery/helpers/gallery.php @@ -23,7 +23,7 @@ class gallery_Core { * down for maintenance" page. */ static function maintenance_mode() { - $maintenance_mode = Kohana::config("gallery.maintenance_mode", false, false); + $maintenance_mode = Kohana::config("core.maintenance_mode", false, false); if (Router::$controller != "login" && !empty($maintenance_mode) && !user::active()->admin) { Router::$controller = "maintenance"; diff --git a/modules/gallery/helpers/gallery_menu.php b/modules/gallery/helpers/gallery_menu.php index 09c2d91a..d28e71c9 100644 --- a/modules/gallery/helpers/gallery_menu.php +++ b/modules/gallery/helpers/gallery_menu.php @@ -30,7 +30,14 @@ class gallery_menu_Core { $can_edit = $item && access::can("edit", $item) || $is_admin; $can_add = $item && (access::can("add", $item) || $is_admin); - + + if ($can_add) { + $menu->append(Menu::factory("dialog") + ->id("add_photos_item") + ->label(t("Add photos")) + ->url(url::site("simple_uploader/app/$item->id"))); + } + if ($item && $can_edit || $can_add) { $menu->append($options_menu = Menu::factory("submenu") ->id("options_menu") @@ -49,10 +56,6 @@ class gallery_menu_Core { if ($can_add) { $options_menu ->append(Menu::factory("dialog") - ->id("add_item") - ->label(t("Add a photo")) - ->url(url::site("simple_uploader/app/$item->id"))) - ->append(Menu::factory("dialog") ->id("add_album") ->label(t("Add an album")) ->url(url::site("form/add/albums/$item->id?type=album"))); diff --git a/modules/gallery/helpers/item.php b/modules/gallery/helpers/item.php index 7daaf1e1..09870b45 100644 --- a/modules/gallery/helpers/item.php +++ b/modules/gallery/helpers/item.php @@ -19,6 +19,8 @@ */ class item_Core { static function move($source, $target) { + access::required("view", $source); + access::required("view", $target); access::required("edit", $source); access::required("edit", $target); @@ -47,6 +49,8 @@ class item_Core { static function make_album_cover($item) { $parent = $item->parent(); + access::required("view", $item); + access::required("view", $parent); access::required("edit", $parent); model_cache::clear("item", $parent->album_cover_item_id); @@ -61,6 +65,7 @@ class item_Core { } static function remove_album_cover($album) { + access::required("view", $album); access::required("edit", $album); @unlink($album->thumb_path()); @@ -102,4 +107,16 @@ class item_Core { $input->add_error("conflict", 1); } } + + /** + * Sanitize a filename into something presentable as an item title + * @param string $filename + * @return string title + */ + static function convert_filename_to_title($filename) { + $title = strtr($filename, "_", " "); + $title = preg_replace("/\..*?$/", "", $title); + $title = preg_replace("/ +/", " ", $title); + return $title; + } }
\ No newline at end of file diff --git a/modules/gallery/helpers/l10n_client.php b/modules/gallery/helpers/l10n_client.php index d26739f5..4e905c6c 100644 --- a/modules/gallery/helpers/l10n_client.php +++ b/modules/gallery/helpers/l10n_client.php @@ -200,4 +200,80 @@ class l10n_client_Core { // @todo Move messages out of outgoing into incoming, using new rev? // @todo show which messages have been rejected / are pending? } + + /** + * Plural forms. + */ + static function plural_forms($locale) { + $parts = explode('_', $locale); + $language = $parts[0]; + + // Data from CLDR 1.6 (http://unicode.org/cldr/data/common/supplemental/plurals.xml). + // Docs: http://www.unicode.org/cldr/data/charts/supplemental/language_plural_rules.html + switch ($language) { + case 'az': + case 'fa': + case 'hu': + case 'ja': + case 'ko': + case 'my': + case 'to': + case 'tr': + case 'vi': + case 'yo': + case 'zh': + case 'bo': + case 'dz': + case 'id': + case 'jv': + case 'ka': + case 'km': + case 'kn': + case 'ms': + case 'th': + return array('other'); + + case 'ar': + return array('zero', 'one', 'two', 'few', 'many', 'other'); + + case 'lv': + return array('zero', 'one', 'other'); + + case 'ga': + case 'se': + case 'sma': + case 'smi': + case 'smj': + case 'smn': + case 'sms': + return array('one', 'two', 'other'); + + case 'ro': + case 'mo': + case 'lt': + case 'cs': + case 'sk': + case 'pl': + return array('one', 'few', 'other'); + + case 'hr': + case 'ru': + case 'sr': + case 'uk': + case 'be': + case 'bs': + case 'sh': + case 'mt': + return array('one', 'few', 'many', 'other'); + + case 'sl': + return array('one', 'two', 'few', 'other'); + + case 'cy': + return array('one', 'two', 'many', 'other'); + + default: // en, de, etc. + return array('one', 'other'); + } + } }
\ No newline at end of file diff --git a/modules/gallery/helpers/p.php b/modules/gallery/helpers/p.php index c3074c23..0a6210dc 100644 --- a/modules/gallery/helpers/p.php +++ b/modules/gallery/helpers/p.php @@ -18,7 +18,7 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ class p_Core { - function clean($dirty_html) { + static function clean($dirty_html) { return html::specialchars($dirty_html); } } diff --git a/modules/gallery/js/l10n_client.js b/modules/gallery/js/l10n_client.js index f43671f1..efd956e2 100644 --- a/modules/gallery/js/l10n_client.js +++ b/modules/gallery/js/l10n_client.js @@ -90,6 +90,40 @@ jQuery.extend(Gallery, { this.setString = function(index, data) { l10n_client_data[index]['translation'] = data; } + // Display the source message + this.showSourceMessage = function(source, is_plural) { + if (is_plural) { + var pretty_source = '[one] - ' + source['one'] + "\n"; + pretty_source += '[other] - ' + source['other']; + } else { + var pretty_source = source; + } + $('#l10n-client-string-editor .source-text').text(pretty_source); + } + this.isPluralMessage = function(message) { + return typeof(message) == 'object'; + } + this.updateTranslationForm = function(translation, is_plural) { + $('.translationField').addClass('hidden'); + if (is_plural) { + if (typeof(translation) != 'object') { + translation = {}; + } + var num_plural_forms = plural_forms.length; + for (var i = 0; i < num_plural_forms; i++) { + var form = plural_forms[i]; + if (translation[form] == undefined) { + translation[form] = ''; + } + $('#l10n-edit-plural-translation-' + form) + .attr('value', translation[form]); + $('#plural-' + form).removeClass('hidden'); + } + } else { + $('#l10n-edit-translation').attr('value', translation); + $('#l10n-edit-translation').removeClass('hidden'); + } + } // Filter the the string list by a search string this.filter = function(search) { if(search == false || search == '') { @@ -126,11 +160,12 @@ Gallery.behaviors.l10nClient = function(context) { $('#l10n-client-string-select li').removeClass('active'); $(this).addClass('active'); var index = $('#l10n-client-string-select li').index(this); - - $('#l10n-client-string-editor .source-text').text(Gallery.l10nClient.getString(index, 'source')); - $("#gL10nClientSaveForm input[name='l10n-message-source']").val(Gallery.l10nClient.getString(index, 'source')); - $('#gL10nClientSaveForm #l10n-edit-target').val(Gallery.l10nClient.getString(index, 'translation')); - + var source = Gallery.l10nClient.getString(index, 'source'); + var key = Gallery.l10nClient.getString(index, 'key'); + var is_plural = Gallery.l10nClient.isPluralMessage(source); + Gallery.l10nClient.showSourceMessage(source, is_plural); + Gallery.l10nClient.updateTranslationForm(Gallery.l10nClient.getString(index, 'translation'), is_plural); + $("#gL10nClientSaveForm input[name='l10n-message-key']").val(key); Gallery.l10nClient.selected = index; }); @@ -165,23 +200,46 @@ Gallery.behaviors.l10nClient = function(context) { $('#gL10nClientSaveForm').ajaxForm({ dataType: "json", success: function(data) { - // Store string in local js - Gallery.l10nClient.setString(Gallery.l10nClient.selected, $('#gL10nClientSaveForm #l10n-edit-target').val()); - - // Mark string as translated. - $('#l10n-client-string-select li').eq(Gallery.l10nClient.selected).removeClass('untranslated').removeClass('active').addClass('translated').text($('#gL10nClientSaveForm #l10n-edit-target').val()); - - // Empty input fields. - $('#l10n-client-string-editor .source-text').html(''); - $('#gL10nClientSaveForm #l10n-edit-target').val(''); - $("#gL10nClientSaveForm input[name='l10n-message-source']").val(''); - }, - error: function(xmlhttp) { - // TODO: Localize this message - alert('An HTTP error @status occured (or empty response).'.replace('@status', xmlhttp.status)); - } - }); + var source = Gallery.l10nClient.getString(Gallery.l10nClient.selected, 'source'); + var is_plural = Gallery.l10nClient.isPluralMessage(source); + var num_plural_forms = plural_forms.length; + + // Store translation in local js + if (is_plural) { + var translation = {}; + for (var i = 0; i < num_plural_forms; i++) { + var form = plural_forms[i]; + translation[form] = $('#gL10nClientSaveForm #l10n-edit-plural-translation-' + form).attr('value'); + } + } else { + translation = $('#l10n-edit-translation').attr('value'); + } + Gallery.l10nClient.setString(Gallery.l10nClient.selected, translation); + + // Mark message as translated. + $('#l10n-client-string-select li').eq(Gallery.l10nClient.selected).removeClass('untranslated').removeClass('active').addClass('translated'); + + // Clear the translation form fields + Gallery.l10nClient.showSourceMessage('', false); + $('#gL10nClientSaveForm #l10n-edit-translation').val(''); + + for (var i = 0; i < num_plural_forms; i++) { + var form = plural_forms[i]; + $('#gL10nClientSaveForm #l10n-edit-plural-translation-' + form).val(''); + } + $("#gL10nClientSaveForm input[name='l10n-message-key']").val(''); + }, + error: function(xmlhttp) { + // TODO: Localize this message + alert('An HTTP error @status occured (or empty response).'.replace('@status', xmlhttp.status)); + } + }); + // TODO: Add copy/clear buttons (without ajax behavior) + /* <input type="submit" name="l10n-edit-copy" value="<?= t("Copy source") ?>"/> + <input type="submit" name="l10n-edit-clear" value="<?= t("Clear") ?>"/> + */ + // TODO: Handle plurals in copy button // Copy source text to translation field on button click. $('#gL10nClientSaveForm #l10n-edit-copy').click(function() { diff --git a/modules/gallery/libraries/I18n.php b/modules/gallery/libraries/I18n.php index f2801169..03a6d8f6 100644 --- a/modules/gallery/libraries/I18n.php +++ b/modules/gallery/libraries/I18n.php @@ -148,30 +148,37 @@ class I18n_Core { public function has_translation($message, $options=null) { $locale = empty($options['locale']) ? $this->_config['default_locale'] : $options['locale']; - $count = empty($options['count']) ? null : $options['count']; - $values = $options; - unset($values['locale']); - $this->log($message, $options); $entry = $this->lookup($locale, $message); if (null === $entry) { return false; - } else if (!is_array($entry)) { + } else if (!is_array($message)) { return $entry !== ''; } else { - $plural_key = self::get_plural_key($locale, $count); - return isset($entry[$plural_key]) - && $entry[$plural_key] !== null - && $entry[$plural_key] !== ''; + if (!is_array($entry) || empty($entry)) { + return false; + } + // It would be better to verify that all the locale's plural forms have a non-empty + // translation, but this is fine for now. + foreach ($entry as $value) { + if ($value === '') { + return false; + } + } + return true; } } - public static function get_message_key($message) { + static function get_message_key($message) { $as_string = is_array($message) ? implode('|', $message) : $message; return md5($as_string); } + static function is_plural_message($message) { + return is_array($message); + } + private function interpolate($locale, $string, $values) { // TODO: Handle locale specific number formatting. diff --git a/modules/gallery/libraries/MY_Forge.php b/modules/gallery/libraries/MY_Forge.php index 17d0465b..b40d067d 100644 --- a/modules/gallery/libraries/MY_Forge.php +++ b/modules/gallery/libraries/MY_Forge.php @@ -26,6 +26,7 @@ class Forge extends Forge_Core { parent::__construct($action, $title, $method, $attr); $this->hidden("csrf")->value(""); } + /** * Use our own template */ diff --git a/modules/gallery/libraries/Menu.php b/modules/gallery/libraries/Menu.php index d19d8b1e..83bd1616 100644 --- a/modules/gallery/libraries/Menu.php +++ b/modules/gallery/libraries/Menu.php @@ -171,10 +171,21 @@ class Menu_Core extends Menu_Element { } /** + * Remove an element from the menu + */ + public function remove($target_id) { + unset($this->elements[$target_id]); + } + + /** * Retrieve a Menu_Element by id */ public function get($id) { - return $this->elements[$id]; + if (array_key_exists($id, $this->elements)) { + return $this->elements[$id]; + } + + return null; } public function __toString() { diff --git a/modules/gallery/models/item.php b/modules/gallery/models/item.php index 4b8cac8e..10bad0b2 100644 --- a/modules/gallery/models/item.php +++ b/modules/gallery/models/item.php @@ -287,6 +287,10 @@ class Item_Model extends ORM_MPTT { * @return string */ public function relative_path() { + if (!$this->loaded) { + return; + } + if (!isset($this->relative_path_cache)) { $paths = array(); foreach (Database::instance() @@ -391,7 +395,7 @@ class Item_Model extends ORM_MPTT { * @param boolean (optional) $center_vertically Center vertically (default: false) * @return string */ - public function thumb_tag($extra_attrs=array(), $max=null, $center_vertically=false) { + public function thumb_img($extra_attrs=array(), $max=null, $center_vertically=false) { list ($height, $width) = $this->scale_dimensions($max); if ($center_vertically && $max) { // The constant is divide by 2 to calculate the file and 10 to convert to em @@ -444,7 +448,7 @@ class Item_Model extends ORM_MPTT { * @param array $extra_attrs Extra attributes to add to the img tag * @return string */ - public function resize_tag($extra_attrs) { + public function resize_img($extra_attrs) { $attrs = array_merge($extra_attrs, array("src" => $this->resize_url(), "alt" => $this->title, @@ -460,7 +464,7 @@ class Item_Model extends ORM_MPTT { * @param array $extra_attrs * @return string */ - public function movie_tag($extra_attrs) { + public function movie_img($extra_attrs) { $attrs = array_merge($extra_attrs, array("id" => "player", "style" => "display:block;width:400px;height:300px") diff --git a/modules/gallery/tests/Access_Helper_Test.php b/modules/gallery/tests/Access_Helper_Test.php index 7012a487..d71bf971 100644 --- a/modules/gallery/tests/Access_Helper_Test.php +++ b/modules/gallery/tests/Access_Helper_Test.php @@ -38,6 +38,10 @@ class Access_Helper_Test extends Unit_Test_Case { $user->delete(); } } catch (Exception $e) { } + + // Reset some permissions that we mangle below + $root = ORM::factory("item", 1); + access::allow(group::everybody(), "view", $root); } public function setup() { diff --git a/modules/gallery/tests/Albums_Controller_Test.php b/modules/gallery/tests/Albums_Controller_Test.php index ef1fac77..7674e85f 100644 --- a/modules/gallery/tests/Albums_Controller_Test.php +++ b/modules/gallery/tests/Albums_Controller_Test.php @@ -20,17 +20,21 @@ class Albums_Controller_Test extends Unit_Test_Case { public function setup() { $this->_post = $_POST; + $this->_album = null; } public function teardown() { $_POST = $this->_post; + if ($this->_album) { + $this->_album->delete(); + } } public function change_album_test() { $controller = new Albums_Controller(); $root = ORM::factory("item", 1); - $album = album::create($root, "test", "test", "test"); - $orig_name = $album->name; + $this->_album = album::create($root, "test", "test", "test"); + $orig_name = $this->_album->name; $_POST["dirname"] = "test"; $_POST["name"] = "new name"; @@ -43,31 +47,31 @@ class Albums_Controller_Test extends Unit_Test_Case { access::allow(group::everybody(), "edit", $root); ob_start(); - $controller->_update($album); + $controller->_update($this->_album); $results = ob_get_contents(); ob_end_clean(); $this->assert_equal( json_encode(array("result" => "success", "location" => "http://./index.php/test")), $results); - $this->assert_equal("new title", $album->title); - $this->assert_equal("new description", $album->description); + $this->assert_equal("new title", $this->_album->title); + $this->assert_equal("new description", $this->_album->description); // We don't change the name, yet. - $this->assert_equal($orig_name, $album->name); + $this->assert_equal($orig_name, $this->_album->name); } public function change_album_no_csrf_fails_test() { $controller = new Albums_Controller(); $root = ORM::factory("item", 1); - $album = album::create($root, "test", "test", "test"); + $this->_album = album::create($root, "test", "test", "test"); $_POST["name"] = "new name"; $_POST["title"] = "new title"; $_POST["description"] = "new description"; access::allow(group::everybody(), "edit", $root); try { - $controller->_update($album); + $controller->_update($this->_album); $this->assert_true(false, "This should fail"); } catch (Exception $e) { // pass diff --git a/modules/gallery/tests/Photos_Controller_Test.php b/modules/gallery/tests/Photos_Controller_Test.php index 771cc90d..f7d3f72f 100644 --- a/modules/gallery/tests/Photos_Controller_Test.php +++ b/modules/gallery/tests/Photos_Controller_Test.php @@ -20,17 +20,22 @@ class Photos_Controller_Test extends Unit_Test_Case { public function setup() { $this->_post = $_POST; + $this->_photo = null; } public function teardown() { $_POST = $this->_post; + if ($this->_photo) { + $this->_photo->delete(); + } } public function change_photo_test() { $controller = new Photos_Controller(); $root = ORM::factory("item", 1); - $photo = photo::create($root, MODPATH . "gallery/tests/test.jpg", "test.jpeg", "test", "test"); - $orig_name = $photo->name; + $this->_photo = photo::create($root, MODPATH . "gallery/tests/test.jpg", "test.jpeg", "test", + "test"); + $orig_name = $this->_photo->name; $_POST["filename"] = "test.jpeg"; $_POST["name"] = "new name"; @@ -40,7 +45,7 @@ class Photos_Controller_Test extends Unit_Test_Case { access::allow(group::everybody(), "edit", $root); ob_start(); - $controller->_update($photo); + $controller->_update($this->_photo); $results = ob_get_contents(); ob_end_clean(); @@ -48,24 +53,24 @@ class Photos_Controller_Test extends Unit_Test_Case { json_encode(array("result" => "success", "location" => "http://./index.php/test.jpeg")), $results); - $this->assert_equal("new title", $photo->title); - $this->assert_equal("new description", $photo->description); + $this->assert_equal("new title", $this->_photo->title); + $this->assert_equal("new description", $this->_photo->description); // We don't change the name, yet. - $this->assert_equal($orig_name, $photo->name); + $this->assert_equal($orig_name, $this->_photo->name); } public function change_photo_no_csrf_fails_test() { $controller = new Photos_Controller(); $root = ORM::factory("item", 1); - $photo = photo::create($root, MODPATH . "gallery/tests/test.jpg", "test", "test", "test"); + $this->_photo = photo::create($root, MODPATH . "gallery/tests/test.jpg", "test", "test", "test"); $_POST["name"] = "new name"; $_POST["title"] = "new title"; $_POST["description"] = "new description"; access::allow(group::everybody(), "edit", $root); try { - $controller->_update($photo); + $controller->_update($this->_photo); $this->assert_true(false, "This should fail"); } catch (Exception $e) { // pass diff --git a/modules/gallery/tests/xss_data.txt b/modules/gallery/tests/xss_data.txt index 4aaa520d..67f293dd 100644 --- a/modules/gallery/tests/xss_data.txt +++ b/modules/gallery/tests/xss_data.txt @@ -195,14 +195,14 @@ modules/gallery/views/l10n_client.html.php 29 DIRTY $string_l modules/gallery/views/move_browse.html.php 4 DIRTY $source->id modules/gallery/views/move_browse.html.php 39 DIRTY $tree modules/gallery/views/move_browse.html.php 42 DIRTY $source->id -modules/gallery/views/move_tree.html.php 2 DIRTY $parent->thumb_tag(array(), 25) +modules/gallery/views/move_tree.html.php 2 DIRTY $parent->thumb_img(array(), 25) modules/gallery/views/move_tree.html.php 4 DIRTY $parent->id modules/gallery/views/move_tree.html.php 4 CLEAN $parent->title modules/gallery/views/move_tree.html.php 6 DIRTY $parent->id modules/gallery/views/move_tree.html.php 6 CLEAN $parent->title modules/gallery/views/move_tree.html.php 8 DIRTY $parent->id modules/gallery/views/move_tree.html.php 10 DIRTY $child->id -modules/gallery/views/move_tree.html.php 11 DIRTY $child->thumb_tag(array(), 25) +modules/gallery/views/move_tree.html.php 11 DIRTY $child->thumb_img(array(), 25) modules/gallery/views/move_tree.html.php 13 DIRTY $child->id modules/gallery/views/move_tree.html.php 13 CLEAN $child->title modules/gallery/views/move_tree.html.php 15 DIRTY $child->id @@ -335,7 +335,7 @@ modules/organize/views/organize_thumb_grid.html.php 7 DIRTY $child->i modules/organize/views/organize_thumb_grid.html.php 7 DIRTY $child->id modules/organize/views/organize_thumb_grid.html.php 8 DIRTY $child->id modules/organize/views/organize_thumb_grid.html.php 8 DIRTY $item_class -modules/organize/views/organize_thumb_grid.html.php 9 DIRTY $child->thumb_tag(array("class" => "gThumbnail"), $thumbsize, true) +modules/organize/views/organize_thumb_grid.html.php 9 DIRTY $child->thumb_img(array("class" => "gThumbnail"), $thumbsize, true) modules/recaptcha/views/admin_recaptcha.html.php 5 DIRTY $form->get_key_url modules/recaptcha/views/admin_recaptcha.html.php 8 DIRTY $form modules/recaptcha/views/admin_recaptcha.html.php 21 DIRTY $public_key @@ -406,7 +406,7 @@ modules/rss/views/rss_block.html.php 8 DIRTY $title modules/search/views/search.html.php 11 CLEAN $q modules/search/views/search.html.php 30 DIRTY $item_class modules/search/views/search.html.php 31 DIRTY $item->id -modules/search/views/search.html.php 32 DIRTY $item->thumb_tag() +modules/search/views/search.html.php 32 DIRTY $item->thumb_img() modules/search/views/search.html.php 34 CLEAN $item->title modules/search/views/search.html.php 37 CLEAN $item->description modules/search/views/search.html.php 43 DIRTY $theme->pager() @@ -518,7 +518,7 @@ themes/default/views/album.html.php 15 DIRTY $child->i themes/default/views/album.html.php 15 DIRTY $item_class themes/default/views/album.html.php 16 DIRTY $theme->thumb_top($child) themes/default/views/album.html.php 17 DIRTY $child->url() -themes/default/views/album.html.php 18 DIRTY $child->thumb_tag(array("class" => "gThumbnail")) +themes/default/views/album.html.php 18 DIRTY $child->thumb_img(array("class" => "gThumbnail")) themes/default/views/album.html.php 20 DIRTY $theme->thumb_bottom($child) themes/default/views/album.html.php 21 DIRTY $child->url() themes/default/views/album.html.php 21 CLEAN $child->title @@ -607,7 +607,7 @@ themes/default/views/photo.html.php 15 DIRTY $position themes/default/views/photo.html.php 15 DIRTY $sibling_count themes/default/views/photo.html.php 18 DIRTY $next_item->url() themes/default/views/photo.html.php 28 DIRTY $theme->resize_top($item) -themes/default/views/photo.html.php 32 DIRTY $item->resize_tag(array("id" => "gPhotoId-{$item->id}", "class" => "gResize")) +themes/default/views/photo.html.php 32 DIRTY $item->resize_img(array("id" => "gPhotoId-{$item->id}", "class" => "gResize")) themes/default/views/photo.html.php 36 DIRTY $theme->resize_bottom($item) themes/default/views/photo.html.php 40 CLEAN $item->title themes/default/views/photo.html.php 41 CLEAN $item->description diff --git a/modules/gallery/views/l10n_client.html.php b/modules/gallery/views/l10n_client.html.php index 8f4092c7..faa6e939 100644 --- a/modules/gallery/views/l10n_client.html.php +++ b/modules/gallery/views/l10n_client.html.php @@ -11,21 +11,58 @@ <ul class="string-list"> <? foreach ($string_list as $string): ?> <li class="<?= $string["translation"] === "" ? "untranslated" : "translated" ?>"> + <? if (is_array($string["source"])): ?> + [one] - <?= $string["source"]["one"] ?><br/> + [other] - <?= $string["source"]["other"] ?> + <? else: ?> <?= $string["source"] ?> + <? endif; ?> </li> <? endforeach; ?> </ul> + <?= $l10n_search_form ?> </div> <div id="l10n-client-string-editor"> <div class="source"> - <div class="source-text"></div> + <pre class="source-text"></pre> </div> <div class="translation"> - <?= $l10n_form ?> + <form method="post" action="<?= url::site("l10n_client/save") ?>" id="gL10nClientSaveForm"> + <?= access::csrf_form_field() ?> + <?= form::hidden("l10n-message-key") ?> + <?= form::textarea("l10n-edit-translation", "", ' rows="5" class="translationField"') ?> + <div id="plural-zero" class="translationField hidden"> + <label for="l10n-edit-plural-translation-zero">[zero]</label> + <?= form::textarea("l10n-edit-plural-translation-zero", "", ' rows="2"') ?> + </div> + <div id="plural-one" class="translationField hidden"> + <label for="l10n-edit-plural-translation-one">[one]</label> + <?= form::textarea("l10n-edit-plural-translation-one", "", ' rows="2"') ?> + </div> + <div id="plural-two" class="translationField hidden"> + <label for="l10n-edit-plural-translation-two">[two]</label> + <?= form::textarea("l10n-edit-plural-translation-two", "", ' rows="2"') ?> + </div> + <div id="plural-few" class="translationField hidden"> + <label for="l10n-edit-plural-translation-few">[few]</label> + <?= form::textarea("l10n-edit-plural-translation-few", "", ' rows="2"') ?> + </div> + <div id="plural-many" class="translationField hidden"> + <label for="l10n-edit-plural-translation-many">[many]</label> + <?= form::textarea("l10n-edit-plural-translation-many", "", ' rows="2"') ?> + </div> + <div id="plural-other" class="translationField hidden"> + <label for="l10n-edit-plural-translation-other">[other]</label> + (<a href="http://www.unicode.org/cldr/data/charts/supplemental/language_plural_rules.html"><?= t("learn more about plural forms") ?></a>) + <?= form::textarea("l10n-edit-plural-translation-other", "", ' rows="2"') ?> + </div> + <input type="submit" name="l10n-edit-save" value="<?= t("Save translation") ?>"/> + </form> </div> </div> <script type="text/javascript"> var l10n_client_data = <?= json_encode($string_list) ?>; + var plural_forms = <?= json_encode($plural_forms) ?>; </script> </div> diff --git a/modules/gallery/views/move_tree.html.php b/modules/gallery/views/move_tree.html.php index 91a2f9da..5f70cf67 100644 --- a/modules/gallery/views/move_tree.html.php +++ b/modules/gallery/views/move_tree.html.php @@ -1,5 +1,5 @@ <?php defined("SYSPATH") or die("No direct script access.") ?> -<?= $parent->thumb_tag(array(), 25); ?> +<?= $parent->thumb_img(array(), 25); ?> <? if (!access::can("edit", $parent) || $source->is_descendant($parent)): ?> <a href="javascript:load_tree('<?= $parent->id ?>',1)"> <?= p::clean($parent->title) ?> <?= t("(locked)") ?> </a> <? else: ?> @@ -8,7 +8,7 @@ <ul id="tree_<?= $parent->id ?>"> <? foreach ($children as $child): ?> <li id="node_<?= $child->id ?>" class="node"> - <?= $child->thumb_tag(array(), 25); ?> + <?= $child->thumb_img(array(), 25); ?> <? if (!access::can("edit", $child) || $source->is_descendant($child)): ?> <a href="javascript:load_tree('<?= $child->id ?>',1)"> <?= p::clean($child->title) ?> <?= t("(locked)") ?></a> <? else: ?> diff --git a/modules/gallery/views/permissions_browse.html.php b/modules/gallery/views/permissions_browse.html.php index 5cd9cf82..8bb2e830 100644 --- a/modules/gallery/views/permissions_browse.html.php +++ b/modules/gallery/views/permissions_browse.html.php @@ -27,7 +27,7 @@ <? if (!$htaccess_works): ?> <ul id="gMessage"> <li class="gError"> - <?= t("Oh no! Your server needs a configuration change in order for you to hide photos! Ask your server administrator to set <a href=\"%url\"><i>AllowOverride FileInfo Options</i></a> to fix this.", array("url" => "http://httpd.apache.org/docs/2.0/mod/gallery.html#allowoverride")) ?> + <?= t("Oh no! Your server needs a configuration change in order for you to hide photos! Ask your server administrator to set <a %attrs><i>AllowOverride FileInfo Options</i></a> to fix this.", array("attrs" => "href=\"http://httpd.apache.org/docs/2.0/mod/core.html#allowoverride\" target=\"_blank\"")) ?> </li> </ul> <? endif ?> diff --git a/modules/gallery/views/simple_uploader.html.php b/modules/gallery/views/simple_uploader.html.php index abda6d26..7b90c5be 100644 --- a/modules/gallery/views/simple_uploader.html.php +++ b/modules/gallery/views/simple_uploader.html.php @@ -3,7 +3,7 @@ <script type="text/javascript" src="<?= url::file("lib/swfupload/swfupload.queue.js") ?>"></script> <!-- hack to set the title for the dialog --> -<form id="gAddPhotosForm" action="<?= url::site("simple_uploader/finish") ?>"> +<form id="gAddPhotosForm" action="<?= url::site("simple_uploader/finish?csrf=$csrf") ?>"> <fieldset> <legend> <?= t("Add photos to %album_title", array("album_title" => p::clean($item->title))) ?> </legend> </fieldset> diff --git a/modules/image_block/views/image_block_block.html.php b/modules/image_block/views/image_block_block.html.php index 0ba9915e..48a3c912 100644 --- a/modules/image_block/views/image_block_block.html.php +++ b/modules/image_block/views/image_block_block.html.php @@ -1,6 +1,6 @@ <?php defined("SYSPATH") or die("No direct script access.") ?> <div class="gImageBlock"> <a href="<?= $item->url() ?>"> - <?= $item->thumb_tag(array("class" => "gThumbnail")) ?> + <?= $item->thumb_image(array("class" => "gThumbnail")) ?> </a> </div> diff --git a/modules/notification/helpers/notification.php b/modules/notification/helpers/notification.php index 32301fe0..8ee0c6ba 100644 --- a/modules/notification/helpers/notification.php +++ b/modules/notification/helpers/notification.php @@ -149,7 +149,7 @@ class notification { $result = ORM::factory("pending_notification") ->where("email", $email) ->find_all(); - if ($result->count == 1) { + if ($result->count() == 1) { $pending = $result->get(); Sendmail::factory() ->to($email) diff --git a/modules/organize/controllers/organize.php b/modules/organize/controllers/organize.php index 1c4792b2..43d41357 100644 --- a/modules/organize/controllers/organize.php +++ b/modules/organize/controllers/organize.php @@ -24,19 +24,22 @@ class Organize_Controller extends Controller { function index($item_id=1) { $item = ORM::factory("item", $item_id); $root = ($item->id == 1) ? $item : ORM::factory("item", 1); + access::required("view", $item); + access::required("edit", $item); $v = new View("organize.html"); $v->root = $root; $v->item = $item; $v->album_tree = $this->tree($item, $root); - $v->button_pane = new View("organize_button_pane.html"); - print $v; } function content($item_id) { $item = ORM::factory("item", $item_id); + access::required("view", $item); + access::required("edit", $item); + $width = $this->input->get("width"); $height = $this->input->get("height"); $offset = $this->input->get("offset", 0); @@ -55,12 +58,17 @@ class Organize_Controller extends Controller { function header($item_id) { $item = ORM::factory("item", $item_id); + access::required("view", $item); + access::required("edit", $item); print json_encode(array("title" => $item->title, "description" => empty($item->description) ? "" : $item->description)); } function tree($item, $parent) { + access::required("view", $item); + access::required("edit", $item); + $albums = ORM::factory("item") ->where(array("parent_id" => $parent->id, "type" => "album")) ->orderby(array("title" => "ASC")) @@ -88,6 +96,8 @@ class Organize_Controller extends Controller { $items = $this->input->post("item"); $item = ORM::factory("item", $id); + access::required("view", $item); + access::required("edit", $item); $definition = $this->_getOperationDefinition($item, $operation); @@ -101,22 +111,26 @@ class Organize_Controller extends Controller { // @todo If there is only one item then call task_run($task->id); Maybe even change js so // we can call finish as well. batch::start(); - print json_encode(array("result" => "started", - "runningMsg" => $definition["runningMsg"], - "pauseMsg" => "<div class=\"gWarning\">{$definition['pauseMsg']}</div>", - "resumeMsg" => "<div class=\"gWarning\">{$definition['resumeMsg']}</div>", - "task" => array("id" => $task->id, - "percent_complete" => $task->percent_complete, - "type" => $task->get("type"), - "status" => $task->status, - "state" => $task->state, - "done" => $task->done))); + print json_encode( + array("result" => "started", + "runningMsg" => $definition["runningMsg"], + "pauseMsg" => "<div class=\"gWarning\">{$definition['pauseMsg']}</div>", + "resumeMsg" => "<div class=\"gWarning\">{$definition['resumeMsg']}</div>", + "task" => array("id" => $task->id, + "percent_complete" => $task->percent_complete, + "type" => $task->get("type"), + "status" => $task->status, + "state" => $task->state, + "done" => $task->done))); } function runTask($task_id) { access::verify_csrf(); $task = task::run($task_id); + if (!$task->loaded || $task->owner_id != user::active()->id) { + access::forbidden(); + } print json_encode(array("result" => $task->done ? $task->state : "in_progress", "task" => array("id" => $task->id, @@ -132,6 +146,9 @@ class Organize_Controller extends Controller { access::verify_csrf(); $task = ORM::factory("task", $task_id); + if (!$task->loaded || $task->owner_id != user::active()->id) { + access::forbidden(); + } if ($task->done) { $item = ORM::factory("item", (int)$task->get("target")); @@ -178,6 +195,9 @@ class Organize_Controller extends Controller { access::verify_csrf(); $task = ORM::factory("task", $task_id); + if (!$task->loaded || $task->owner_id != user::active()->id) { + access::forbidden(); + } if (!$task->done) { $task->done = 1; @@ -210,7 +230,7 @@ class Organize_Controller extends Controller { function editForm() { $event_parms = new stdClass(); $event_parms->panes = array(); - $event_parms->itemids = $this->input->get("item");; + $event_parms->itemids = $this->input->get("item"); // The following code should be done more dynamically i.e. use the event mechanism if (count($event_parms->itemids) == 1) { @@ -218,8 +238,12 @@ class Organize_Controller extends Controller { ->in("id", $event_parms->itemids[0]) ->find(); - $event_parms->panes[] = array("label" => $item->is_album() ? t("Edit Album") : t("Edit Photo"), - "content" => organize::get_general_edit_form($item)); + access::required("view", $item); + access::required("edit", $item); + + $event_parms->panes[] = array( + "label" => $item->is_album() ? t("Edit Album") : t("Edit Photo"), + "content" => organize::get_general_edit_form($item)); if ($item->is_album()) { $event_parms->panes[] = array("label" => t("Sort Order"), @@ -243,6 +267,7 @@ class Organize_Controller extends Controller { $item = ORM::factory("item") ->in("id", $itemids[0]) ->find(); + access::required("view", $item); access::required("edit", $item); $form = organize::get_general_edit_form($item); @@ -273,6 +298,7 @@ class Organize_Controller extends Controller { $item = ORM::factory("item") ->in("id", $itemids[0]) ->find(); + access::required("view", $item); access::required("edit", $item); print organize::get_general_edit_form($item); @@ -285,6 +311,7 @@ class Organize_Controller extends Controller { $item = ORM::factory("item") ->in("id", $itemids[0]) ->find(); + access::required("view", $item); access::required("edit", $item); $form = organize::get_sort_edit_form($item); @@ -309,6 +336,7 @@ class Organize_Controller extends Controller { $item = ORM::factory("item") ->in("id", $itemids[0]) ->find(); + access::required("view", $item); access::required("edit", $item); print organize::get_sort_edit_form($item); @@ -373,6 +401,13 @@ class Organize_Controller extends Controller { } private function _add_tag($new_tag, $itemids) { + // Super lame security stopgap. This code is going to get rewritten anyway. + foreach ($itemids as $item_id) { + $item = ORM::factory("item", $item_id); + access::required("view", $item); + access::required("edit", $item); + } + $tag = ORM::factory("tag") ->where("name", $new_tag) ->find(); @@ -391,6 +426,13 @@ class Organize_Controller extends Controller { } private function _delete_tag($new_tag, $itemids) { + // Super lame security stopgap. This code is going to get rewritten anyway. + foreach ($itemids as $item_id) { + $item = ORM::factory("item", $item_id); + access::required("view", $item); + access::required("edit", $item); + } + $tag = ORM::factory("tag") ->where("name", $new_tag) ->find(); @@ -407,6 +449,13 @@ class Organize_Controller extends Controller { } private function _update_tag($new_tag, $itemids) { + // Super lame security stopgap. This code is going to get rewritten anyway. + foreach ($itemids as $item_id) { + $item = ORM::factory("item", $item_id); + access::required("view", $item); + access::required("edit", $item); + } + $tag = ORM::factory("tag") ->where("name", $new_tag) ->find(); @@ -441,6 +490,7 @@ class Organize_Controller extends Controller { "pauseMsg" => t("The move operation was paused"), "resumeMsg" => t("The move operation was resumed")); break; + case "rearrange": return array("description" => t("Rearrange the order of albums and photos"), "name" => t("Rearrange: %name", array("name" => $item->title)), @@ -449,6 +499,7 @@ class Organize_Controller extends Controller { "pauseMsg" => t("The rearrange operation was paused"), "resumeMsg" => t("The rearrange operation was resumed")); break; + case "rotateCcw": return array("description" => t("Rotate the selected photos counter clockwise"), "name" => t("Rotate images in %name", array("name" => $item->title)), @@ -457,6 +508,7 @@ class Organize_Controller extends Controller { "pauseMsg" => t("The rotate operation was paused"), "resumeMsg" => t("The rotate operation was resumed")); break; + case "rotateCw": return array("description" => t("Rotate the selected photos clockwise"), "name" => t("Rotate images in %name", array("name" => $item->title)), @@ -465,6 +517,7 @@ class Organize_Controller extends Controller { "pauseMsg" => t("The rotate operation was paused"), "resumeMsg" => t("The rotate operation was resumed")); break; + case "delete": return array("description" => t("Delete selected photos and albums"), "name" => t("Delete images in %name", array("name" => $item->title)), @@ -473,6 +526,7 @@ class Organize_Controller extends Controller { "pauseMsg" => t("The delete operation was paused"), "resumeMsg" => t("The delete operation was resumed")); break; + case "albumCover": return array("description" => t("Reset Album Cover"), "name" => t("Reset Album cover for %name", array("name" => $item->title)), @@ -481,6 +535,7 @@ class Organize_Controller extends Controller { "pauseMsg" => t("Reset album cover was paused"), "resumeMsg" => t("Reset album cover was resumed")); break; + default: throw new Exception("Operation '$operation' is not implmented"); } diff --git a/modules/organize/helpers/organize.php b/modules/organize/helpers/organize.php index 3a207c95..9bf4e986 100644 --- a/modules/organize/helpers/organize.php +++ b/modules/organize/helpers/organize.php @@ -66,6 +66,14 @@ class organize_Core { $tagPane->hidden("item")->value(implode("|", $itemids)); $item_count = count($itemids); $ids = implode(", ", $itemids); + + // Lame stopgap security check. This code is going to get rewritten anyway. + foreach ($itemids as $id) { + $item = ORM::factory("item", $id); + access::required("view", $item); + access::required("edit", $item); + } + $tags = Database::instance()->query( "SELECT t.name, COUNT(it.item_id) as count FROM {items_tags} it, {tags} t diff --git a/modules/organize/helpers/organize_task.php b/modules/organize/helpers/organize_task.php index 0f0e4792..dc474818 100644 --- a/modules/organize/helpers/organize_task.php +++ b/modules/organize/helpers/organize_task.php @@ -38,30 +38,55 @@ class organize_task_Core { switch ($taskType) { case "move": $source = ORM::factory("item", $id); + access::required("view", $source); + access::required("view", $target); + access::required("edit", $source); + access::required("edit", $target); + item::move($source, $target); break; + case "rearrange": + $item = ORM::factory("item", $id); + access::required("view", $item); + access::required("edit", $item); + Database::instance() ->query("Update {items} set weight = {$context["position"]} where id=$id;"); break; + case "rotateCcw": case "rotateCw": $item = ORM::factory("item", $id); + access::required("view", $item); + access::required("edit", $item); + if ($item->is_photo()) { $context["post_process"]["reload"][] = self::_do_rotation($item, $taskType == "rotateCcw" ? -90 : 90); } break; + case "albumCover": - item::make_album_cover(ORM::factory("item", $id)); + $item = ORM::factory("item", $id); + access::required("view", $item); + access::required("view", $item->parent()); + access::required("edit", $item->parent()); + + item::make_album_cover($item); break; + case "delete": $item = ORM::factory("item", $id); + access::required("view", $item); + access::required("edit", $item); + $item->delete(); $context["post_process"]["remove"][] = array("id" => $id); break; + default: - throw new Exception("Task '$taskType' is not implmented"); + throw new Exception("Task '$taskType' is not implemented"); } } $context["position"] += $stop; diff --git a/modules/organize/views/organize_thumb_grid.html.php b/modules/organize/views/organize_thumb_grid.html.php index 64d8aaf3..c80696ad 100644 --- a/modules/organize/views/organize_thumb_grid.html.php +++ b/modules/organize/views/organize_thumb_grid.html.php @@ -6,7 +6,7 @@ <? endif ?> <li id="thumb_<?= $child->id ?>" class="gMicroThumbContainer" ref="<?= $child->id ?>"> <div id="gMicroThumb-<?= $child->id ?>" class="gMicroThumb <?= $item_class ?>"> - <?= $child->thumb_tag(array("class" => "gThumbnail"), $thumbsize, true) ?> + <?= $child->thumb_img(array("class" => "gThumbnail"), $thumbsize, true) ?> </div> </li> <? endforeach ?> diff --git a/modules/recaptcha/controllers/admin_recaptcha.php b/modules/recaptcha/controllers/admin_recaptcha.php index a8f85ed9..5813df8b 100644 --- a/modules/recaptcha/controllers/admin_recaptcha.php +++ b/modules/recaptcha/controllers/admin_recaptcha.php @@ -21,6 +21,8 @@ class Admin_Recaptcha_Controller extends Admin_Controller { public function index() { $form = recaptcha::get_configure_form(); if (request::method() == "post") { + // @todo move the "save" part of this into a separate controller function + access::verify_csrf(); $old_public_key = module::get_var("recaptcha", "public_key"); $old_private_key = module::get_var("recaptcha", "private_key"); if ($form->validate()) { @@ -55,10 +57,4 @@ class Admin_Recaptcha_Controller extends Admin_Controller { $view->content->form = $form; print $view; } - - public function test() { - $view = new View("admin_recaptcha_test.html"); - $view->public_key = module::get_var("recaptcha", "public_key"); - print $view; - } } diff --git a/modules/recaptcha/helpers/recaptcha_installer.php b/modules/recaptcha/helpers/recaptcha_installer.php index 6269c632..f74bf558 100644 --- a/modules/recaptcha/helpers/recaptcha_installer.php +++ b/modules/recaptcha/helpers/recaptcha_installer.php @@ -29,7 +29,7 @@ class recaptcha_installer { recaptcha::check_config(); } - static function uninstall() { + static function deactivate() { site_status::clear("recaptcha_config"); } } diff --git a/modules/recaptcha/libraries/Form_Recaptcha.php b/modules/recaptcha/libraries/Form_Recaptcha.php index d804fd78..4c0c4997 100644 --- a/modules/recaptcha/libraries/Form_Recaptcha.php +++ b/modules/recaptcha/libraries/Form_Recaptcha.php @@ -39,20 +39,9 @@ class Form_Recaptcha_Core extends Form_Input { "http://recaptcha.net/api/getkey</a>"); } - $server = "http://api.recaptcha.net"; - - $options[] = "callback: Recaptcha.focus_response_field"; - $options[] = "lang: \"" . Kohana::config("locale.root_locale") . "\""; - $options[] = "theme: \"white\""; - $options = implode(", ", $options); - - $html = "<div id=\"gRecaptcha\" />"; - $html .= "<script type=\"text/javascript\" "; - $html .= "src=\"http://api.recaptcha.net/js/recaptcha_ajax.js\"></script>"; - $html .= "<script type=\"text/javascript\">"; - $html .= "Recaptcha.create(\"$public_key\", \"gRecaptcha\", {" . $options . "});"; - $html .= "</script>"; - return $html; + $view = new View("form_recaptcha.html"); + $view->public_key = $public_key; + return $view; } /** diff --git a/modules/recaptcha/module.info b/modules/recaptcha/module.info index 134ae31c..85397580 100644 --- a/modules/recaptcha/module.info +++ b/modules/recaptcha/module.info @@ -1,3 +1,3 @@ name = Recaptcha -description = "<a href="http://recaptcha.net">Recaptcha</a> displays a graphical verification that protects the input form from abuse from "bots," or automated programs usually written to generate spam." +description = "<a href='http://recaptcha.net'>Recaptcha</a> displays a graphical verification that protects the input form from abuse from 'bots,' or automated programs usually written to generate spam." version = 1 diff --git a/modules/recaptcha/views/form_recaptcha.html.php b/modules/recaptcha/views/form_recaptcha.html.php new file mode 100644 index 00000000..abdf8428 --- /dev/null +++ b/modules/recaptcha/views/form_recaptcha.html.php @@ -0,0 +1,12 @@ +<?php defined("SYSPATH") or die("No direct script access.") ?> +<div id="gRecaptcha"></div> +<script type="text/javascript" src="http://api.recaptcha.net/js/recaptcha_ajax.js"></script> +<script type="text/javascript"> + setTimeout(function() { + Recaptcha.create( + "<?= $public_key ?>", + "gRecaptcha", + { theme: "white", callback: Recaptcha.focus_response_field }); + }, 0); +</script> + diff --git a/modules/search/views/search.html.php b/modules/search/views/search.html.php index de4343ae..5db07bad 100644 --- a/modules/search/views/search.html.php +++ b/modules/search/views/search.html.php @@ -29,7 +29,7 @@ <? endif ?> <li class="gItem <?= $item_class ?>"> <a href="<?= url::site("items/$item->id") ?>"> - <?= $item->thumb_tag() ?> + <?= $item->thumb_img() ?> <p> <?= p::clean($item->title) ?> </p> diff --git a/modules/server_add/controllers/server_add.php b/modules/server_add/controllers/server_add.php index d5278b3b..c37eab58 100644 --- a/modules/server_add/controllers/server_add.php +++ b/modules/server_add/controllers/server_add.php @@ -21,10 +21,11 @@ class Server_Add_Controller extends Controller { public function index($id) { $paths = unserialize(module::get_var("server_add", "authorized_paths")); - $item = ORM::factory("item", $id); - access::required("server_add", $item); - access::required("add", $item); + if (!user::active()->admin) { + access::forbidden(); + } + $item = ORM::factory("item", $id); $view = new View("server_add_tree_dialog.html"); $view->action = url::abs_site("__ARGS__/{$id}__TASK_ID__?csrf=" . access::csrf_token()); $view->parents = $item->parents(); @@ -41,8 +42,11 @@ class Server_Add_Controller extends Controller { } public function children() { - $paths = unserialize(module::get_var("server_add", "authorized_paths")); + if (!user::active()->admin) { + access::forbidden(); + } + $paths = unserialize(module::get_var("server_add", "authorized_paths")); $path_valid = false; $path = $this->input->post("path"); @@ -66,7 +70,12 @@ class Server_Add_Controller extends Controller { } function start($id) { + if (!user::active()->admin) { + access::forbidden(); + } access::verify_csrf(); + + $item = ORM::factory("item", $id); $paths = unserialize(module::get_var("server_add", "authorized_paths")); $input_files = $this->input->post("path"); $files = array(); @@ -114,9 +123,15 @@ class Server_Add_Controller extends Controller { } function add_photo($task_id) { + if (!user::active()->admin) { + access::forbidden(); + } access::verify_csrf(); $task = task::run($task_id); + if (!$task->loaded || $task->owner_id != user::active()->id) { + access::forbidden(); + } if ($task->done) { switch ($task->state) { @@ -146,10 +161,16 @@ class Server_Add_Controller extends Controller { } public function finish($id, $task_id) { + if (!user::active()->admin) { + access::forbidden(); + } access::verify_csrf(); - $task = ORM::factory("task", $task_id); + if (!$task->loaded || $task->owner_id != user::active()->id) { + access::forbidden(); + } + if (!$task->done) { message::warning(t("Add from server was cancelled prior to completion")); } @@ -159,9 +180,14 @@ class Server_Add_Controller extends Controller { } public function pause($id, $task_id) { + if (!user::active()->admin) { + access::forbidden(); + } access::verify_csrf(); - $task = ORM::factory("task", $task_id); + if (!$task->loaded || $task->owner_id != user::active()->id) { + access::forbidden(); + } message::warning(t("Add from server was cancelled prior to completion")); batch::stop(); diff --git a/modules/server_add/helpers/server_add_installer.php b/modules/server_add/helpers/server_add_installer.php index b592b448..f8773a2e 100644 --- a/modules/server_add/helpers/server_add_installer.php +++ b/modules/server_add/helpers/server_add_installer.php @@ -22,7 +22,6 @@ class server_add_installer { $db = Database::instance(); $version = module::get_version("server_add"); if ($version == 0) { - access::register_permission("server_add", t("Add files from server")); module::set_version("server_add", 1); } server_add::check_config(); @@ -31,8 +30,4 @@ class server_add_installer { static function deactivate() { site_status::clear("server_add_configuration"); } - - static function uninstall() { - access::delete_permission("server_add"); - } } diff --git a/modules/server_add/helpers/server_add_menu.php b/modules/server_add/helpers/server_add_menu.php index 04c94493..f02223f7 100644 --- a/modules/server_add/helpers/server_add_menu.php +++ b/modules/server_add/helpers/server_add_menu.php @@ -28,16 +28,41 @@ class server_add_menu_Core { static function site($menu, $theme) { $item = $theme->item(); - $paths = unserialize(module::get_var("server_add", "authorized_paths")); - if ($item && access::can("edit", $item) && access::can("server_add", $item) && - $item->is_album() && !empty($paths)) { - $options_menu = $menu->get("options_menu") - ->append(Menu::factory("dialog") - ->id("server_add") - ->label(t("Add from server")) - ->url(url::site("server_add/index/$item->id"))); + if (user::active()->admin && $item->is_album() && !empty($paths)) { + // This is a little tricky. Normally there's an "Add Photo" menu option, but we want to + // turn that into a dropdown if there are two different ways to add things. Do that in a + // portable way for now. If we find ourselves duplicating this pattern, we should make an + // API method for this. + $server_add = Menu::factory("dialog") + ->id("server_add") + ->label(t("Add from server")) + ->url(url::site("server_add/index/$item->id")); + $options_menu = $menu->get("options_menu"); + $add_photos_item = $menu->get("add_photos_item"); + $add_photos_menu = $menu->get("add_photos_menu"); + + if ($add_photos_item && !$add_photos_menu) { + // Assuming that $add_menu is unset, create add_menu and add our item + $menu->add_after( + "home", + Menu::factory("submenu") + ->id("add_photos_menu") + ->label(t("Add Photos")) + ->append(Menu::factory("dialog") + ->id("add_photos_submenu_item") + ->label(t("via Simple Uploader")) + ->url(url::site("simple_uploader/app/$item->id"))) + ->append($server_add)); + $menu->remove("add_photos_item"); + } else if ($add_photos_menu) { + // Append to the existing sub-menu + $add_photos_menu->append($server_add); + } else { + // Else just add it in at the end of Options + $options_menu->append($server_add); + } } } } diff --git a/modules/server_add/helpers/server_add_task.php b/modules/server_add/helpers/server_add_task.php index c5a7f067..98575915 100644 --- a/modules/server_add/helpers/server_add_task.php +++ b/modules/server_add/helpers/server_add_task.php @@ -31,7 +31,6 @@ class server_add_task_Core { if (!empty($context["files"][$path])) { $file = $context["files"][$path][$context["position"]]; $parent = ORM::factory("item", $file["parent_id"]); - access::required("server_add", $parent); access::required("add", $parent); if (!$parent->is_album()) { throw new Exception("@todo BAD_ALBUM"); diff --git a/modules/tag/controllers/admin_tags.php b/modules/tag/controllers/admin_tags.php index 1176b0ca..01884bb8 100644 --- a/modules/tag/controllers/admin_tags.php +++ b/modules/tag/controllers/admin_tags.php @@ -42,6 +42,7 @@ class Admin_Tags_Controller extends Admin_Controller { public function delete($id) { access::verify_csrf(); + $tag = ORM::factory("tag", $id); if (!$tag->loaded) { kohana::show_404(); diff --git a/modules/tag/controllers/tags.php b/modules/tag/controllers/tags.php index aecd1db7..295a9d3b 100644 --- a/modules/tag/controllers/tags.php +++ b/modules/tag/controllers/tags.php @@ -27,16 +27,16 @@ class Tags_Controller extends REST_Controller { $offset = ($page-1) * $page_size; // Make sure that the page references a valid offset - if ($page < 1 || $page > ceil($children_count / $page_size)) { + if ($page < 1 || ($children_count && $page > ceil($children_count / $page_size))) { Kohana::show_404(); } $template = new Theme_View("page.html", "tag"); - $template->set_global('page_size', $page_size); - $template->set_global('page_title', t("Browse Tag::%name", array("name" => $tag->name))); - $template->set_global('tag', $tag); - $template->set_global('children', $tag->items($page_size, $offset)); - $template->set_global('children_count', $children_count); + $template->set_global("page_size", $page_size); + $template->set_global("page_title", t("Browse Tag::%name", array("name" => $tag->name))); + $template->set_global("tag", $tag); + $template->set_global("children", $tag->items($page_size, $offset)); + $template->set_global("children_count", $children_count); $template->content = new View("dynamic.html"); print $template; @@ -48,6 +48,7 @@ class Tags_Controller extends REST_Controller { public function _create($tag) { $item = ORM::factory("item", $this->input->post("item_id")); + access::required("view", $item); access::required("edit", $item); $form = tag::get_add_form($item); @@ -73,6 +74,7 @@ class Tags_Controller extends REST_Controller { public function _form_add($item_id) { $item = ORM::factory("item", $item_id); access::required("view", $item); + access::required("edit", $item); return tag::get_add_form($item); } diff --git a/modules/user/controllers/admin_users.php b/modules/user/controllers/admin_users.php index ac17c577..fe8061aa 100644 --- a/modules/user/controllers/admin_users.php +++ b/modules/user/controllers/admin_users.php @@ -28,6 +28,7 @@ class Admin_Users_Controller extends Controller { public function add_user() { access::verify_csrf(); + $form = user::get_add_form_admin(); $valid = $form->validate(); $name = $form->add_user->inputs["name"]->value; @@ -63,6 +64,7 @@ class Admin_Users_Controller extends Controller { public function delete_user($id) { access::verify_csrf(); + if ($id == user::active()->id || $id == user::guest()->id) { access::forbidden(); } @@ -97,6 +99,7 @@ class Admin_Users_Controller extends Controller { public function edit_user($id) { access::verify_csrf(); + $user = ORM::factory("user", $id); if (!$user->loaded) { kohana::show_404(); @@ -182,6 +185,7 @@ class Admin_Users_Controller extends Controller { public function add_group() { access::verify_csrf(); + $form = group::get_add_form_admin(); $valid = $form->validate(); if ($valid) { @@ -210,6 +214,7 @@ class Admin_Users_Controller extends Controller { public function delete_group($id) { access::verify_csrf(); + $group = ORM::factory("group", $id); if (!$group->loaded) { kohana::show_404(); @@ -240,6 +245,7 @@ class Admin_Users_Controller extends Controller { public function edit_group($id) { access::verify_csrf(); + $group = ORM::factory("group", $id); if (!$group->loaded) { kohana::show_404(); diff --git a/modules/user/controllers/login.php b/modules/user/controllers/login.php index 6ee2e69d..54a7905e 100644 --- a/modules/user/controllers/login.php +++ b/modules/user/controllers/login.php @@ -26,6 +26,8 @@ class Login_Controller extends Controller { } public function auth_ajax() { + access::verify_csrf(); + list ($valid, $form) = $this->_auth("login/auth_ajax"); if ($valid) { print json_encode( @@ -42,6 +44,8 @@ class Login_Controller extends Controller { } public function auth_html() { + access::verify_csrf(); + list ($valid, $form) = $this->_auth("login/auth_html"); if ($valid) { url::redirect("albums/1"); diff --git a/modules/user/controllers/logout.php b/modules/user/controllers/logout.php index b43680d5..6ceb7192 100644 --- a/modules/user/controllers/logout.php +++ b/modules/user/controllers/logout.php @@ -19,6 +19,8 @@ */ class Logout_Controller extends Controller { public function index() { + access::verify_csrf(); + $user = user::active(); user::logout(); log::info("user", t("User %name logged out", array("name" => $user->name)), diff --git a/modules/user/controllers/password.php b/modules/user/controllers/password.php index c3e66634..3b0eac66 100644 --- a/modules/user/controllers/password.php +++ b/modules/user/controllers/password.php @@ -19,6 +19,8 @@ */ class Password_Controller extends Controller { public function reset() { + access::verify_csrf(); + if (request::method() == "post") { $this->_send_reset(); } else { @@ -27,6 +29,8 @@ class Password_Controller extends Controller { } public function do_reset() { + access::verify_csrf(); + if (request::method() == "post") { $this->_change_password(); } else { diff --git a/modules/user/views/login.html.php b/modules/user/views/login.html.php index cce2fb54..3889f06e 100644 --- a/modules/user/views/login.html.php +++ b/modules/user/views/login.html.php @@ -12,7 +12,7 @@ '" title="' . t("Edit Your Profile") . '" id="gUserProfileLink" class="gDialogLink">' . p::clean(empty($user->full_name) ? $user->name : $user->full_name) . '</a>')) ?></li> - <li><a href="<?= url::site("logout?continue=" . url::current(true)) ?>" + <li><a href="<?= url::site("logout?csrf=$csrf&continue=" . url::current(true)) ?>" id="gLogoutLink"><?= t("Logout") ?></a></li> <? endif; ?> </ul> diff --git a/modules/watermark/controllers/admin_watermarks.php b/modules/watermark/controllers/admin_watermarks.php index d487edb8..423196ac 100644 --- a/modules/watermark/controllers/admin_watermarks.php +++ b/modules/watermark/controllers/admin_watermarks.php @@ -38,6 +38,8 @@ class Admin_Watermarks_Controller extends Admin_Controller { } public function edit() { + access::verify_csrf(); + $form = watermark::get_edit_form(); if ($form->validate()) { module::set_var("watermark", "position", $form->edit_watermark->position->value); @@ -61,6 +63,8 @@ class Admin_Watermarks_Controller extends Admin_Controller { } public function delete() { + access::verify_csrf(); + $form = watermark::get_delete_form(); if ($form->validate()) { if ($name = module::get_var("watermark", "name")) { @@ -91,6 +95,8 @@ class Admin_Watermarks_Controller extends Admin_Controller { } public function add() { + access::verify_csrf(); + $form = watermark::get_add_form(); if ($form->validate()) { $file = $_POST["file"]; diff --git a/themes/default/views/album.html.php b/themes/default/views/album.html.php index 6e17696d..7d4a6f54 100644 --- a/themes/default/views/album.html.php +++ b/themes/default/views/album.html.php @@ -15,7 +15,7 @@ <li id="gItemId-<?= $child->id ?>" class="gItem <?= $item_class ?>"> <?= $theme->thumb_top($child) ?> <a href="<?= $child->url() ?>"> - <?= $child->thumb_tag(array("class" => "gThumbnail")) ?> + <?= $child->thumb_img(array("class" => "gThumbnail")) ?> </a> <?= $theme->thumb_bottom($child) ?> <h2><span></span><a href="<?= $child->url() ?>"><?= p::clean($child->title) ?></a></h2> diff --git a/themes/default/views/login_page.html.php b/themes/default/views/login_page.html.php index 1bd76c18..27f8571e 100644 --- a/themes/default/views/login_page.html.php +++ b/themes/default/views/login_page.html.php @@ -7,33 +7,11 @@ <title> <?= t("Please Login to Gallery") ?> </title> - <link rel="shortcut icon" href="<?= $theme->url("images/favicon.ico") ?>" type="image/x-icon" /> - <link rel="stylesheet" type="text/css" href="<?= url::file("lib/yui/reset-fonts-grids.css") ?>" - media="screen,print,projection" /> - <link rel="stylesheet" type="text/css" href="<?= url::file("lib/superfish/css/superfish.css") ?>" - media="screen" /> - <link rel="stylesheet" type="text/css" href="<?= url::file("lib/themeroller/ui.base.css") ?>" - media="screen,print,projection" /> <link rel="stylesheet" type="text/css" href="<?= $theme->url("css/screen.css") ?>" media="screen,print,projection" /> - <!--[if IE]> - <link rel="stylesheet" type="text/css" href="<?= $theme->url("css/fix-ie.css") ?>" - media="screen,print,projection" /> - <![endif]--> - <script src="<?= url::file("lib/jquery.js") ?>" type="text/javascript"></script> - <script src="<?= url::file("lib/jquery.form.js") ?>" type="text/javascript"></script> - <script src="<?= url::file("lib/jquery-ui.js") ?>" type="text/javascript"></script> - <script src="<?= url::file("lib/gallery.dialog.js") ?>" type="text/javascript"></script> - <script src="<?= url::file("lib/superfish/js/superfish.js") ?>" type="text/javascript"></script> - <script src="<?= $theme->url("js/ui.init.js") ?>" type="text/javascript"></script> - <script> - $("#gLoginLink").ready(function() { - $("#gLoginLink").click(); - }); - </script> </head> <body> - <a id="gLoginLink" href="<?= url::site("login/ajax") ?>">Log in</a> + <?= user::get_login_form("login/auth_html") ?> </body> </html> diff --git a/themes/default/views/photo.html.php b/themes/default/views/photo.html.php index cc069158..85143da8 100644 --- a/themes/default/views/photo.html.php +++ b/themes/default/views/photo.html.php @@ -29,7 +29,7 @@ <? if (access::can("view_full", $item)): ?> <a href="#" class="gFullSizeLink" title="<?= t("View full size") ?>"> <? endif ?> - <?= $item->resize_tag(array("id" => "gPhotoId-{$item->id}", "class" => "gResize")) ?> + <?= $item->resize_img(array("id" => "gPhotoId-{$item->id}", "class" => "gResize")) ?> <? if (access::can("view_full", $item)): ?> </a> <? endif ?> |