static function fix_internet_addresses($task) { $start = microtime(true); $total = $task->get("total"); if (empty($total)) { $task->set("total", $total = db::build()->count_records("items")); $task->set("last_id", 0); $task->set("completed", 0); } $last_id = $task->get("last_id"); $completed = $task->get("completed"); foreach (ORM::factory("item")->where("id", ">", $last_id)->find_all(100) as $item) { $item->slug = item::convert_filename_to_slug($item->slug); $item->save(); $last_id = $item->id; $completed++; if ($completed == $total || microtime(true) - $start > 1.5) { break; } } $task->set("completed", $completed); $task->set("last_id", $last_id); if ($total == $completed) { $task->done = true; $task->state = "success"; $task->percent_complete = 100; db::build()->update("items")->set("relative_path_cache", null)->set("relative_url_cache", null)->execute(); } else { $task->percent_complete = round(100 * $completed / $total); } $task->status = t2("One row updated", "%count / %total rows updated", $completed, array("total" => $total)); }
public function convert_filename_to_slug_test() { $this->assert_equal("foo", item::convert_filename_to_slug("{[foo]}")); $this->assert_equal("foo-bar", item::convert_filename_to_slug("{[foo!@#!\$@#^\$@(\$!(@bar]}")); $this->assert_equal("english-text", item::convert_filename_to_slug("english text")); $this->assert_equal("new-line", item::convert_filename_to_slug("new \n line")); $this->assert_equal("foo-and-bar", item::convert_filename_to_slug("foo&bar")); $this->assert_equal("special", item::convert_filename_to_slug("šṗëçîąļ")); }
/** * Create a new album. * @param integer $parent_id id of parent album * @param string $name the name of this new album (it will become the directory name on disk) * @param integer $title the title of the new album * @param string $description (optional) the longer description of this album * @param string $slug (optional) the url component for this photo * @return Item_Model */ static function create($parent, $name, $title, $description = null, $owner_id = null, $slug = null) { if (!$parent->loaded || !$parent->is_album()) { throw new Exception("@todo INVALID_PARENT"); } if (strpos($name, "/")) { throw new Exception("@todo NAME_CANNOT_CONTAIN_SLASH"); } // We don't allow trailing periods as a security measure // ref: http://dev.kohanaphp.com/issues/684 if (rtrim($name, ".") != $name) { throw new Exception("@todo NAME_CANNOT_END_IN_PERIOD"); } if (empty($slug)) { $slug = item::convert_filename_to_slug($name); } $album = ORM::factory("item"); $album->type = "album"; $album->title = $title; $album->description = $description; $album->name = $name; $album->owner_id = $owner_id; $album->thumb_dirty = 1; $album->resize_dirty = 1; $album->slug = $slug; $album->rand_key = (double) mt_rand() / (double) mt_getrandmax(); $album->sort_column = "created"; $album->sort_order = "ASC"; // Randomize the name or slug if there's a conflict // @todo Improve this. Random numbers are not user friendly while (ORM::factory("item")->where("parent_id", $parent->id)->open_paren()->where("name", $album->name)->orwhere("slug", $album->slug)->close_paren()->find()->id) { $rand = rand(); $album->name = "{$name}-{$rand}"; $album->slug = "{$slug}-{$rand}"; } $album = $album->add_to_parent($parent); mkdir($album->file_path()); mkdir(dirname($album->thumb_path())); mkdir(dirname($album->resize_path())); // @todo: publish this from inside Item_Model::save() when we refactor to the point where // there's only one save() happening here. module::event("item_created", $album); return $album; }
static function upgrade($version) { $db = Database::instance(); if ($version == 1) { module::set_var("gallery", "date_format", "Y-M-d"); module::set_var("gallery", "date_time_format", "Y-M-d H:i:s"); module::set_var("gallery", "time_format", "H:i:s"); module::set_version("gallery", $version = 2); } if ($version == 2) { module::set_var("gallery", "show_credits", 1); module::set_version("gallery", $version = 3); } if ($version == 3) { $db->query("CREATE TABLE {caches} (\n `id` varchar(255) NOT NULL,\n `tags` varchar(255),\n `expiration` int(9) NOT NULL,\n `cache` text,\n PRIMARY KEY (`id`),\n KEY (`tags`))\n DEFAULT CHARSET=utf8;"); module::set_version("gallery", $version = 4); } if ($version == 4) { Cache::instance()->delete_all(); $db->query("ALTER TABLE {caches} MODIFY COLUMN `cache` LONGBLOB"); module::set_version("gallery", $version = 5); } if ($version == 5) { Cache::instance()->delete_all(); $db->query("ALTER TABLE {caches} DROP COLUMN `id`"); $db->query("ALTER TABLE {caches} ADD COLUMN `key` varchar(255) NOT NULL"); $db->query("ALTER TABLE {caches} ADD COLUMN `id` int(9) NOT NULL auto_increment PRIMARY KEY"); module::set_version("gallery", $version = 6); } if ($version == 6) { module::clear_var("gallery", "version"); module::set_version("gallery", $version = 7); } if ($version == 7) { $groups = identity::groups(); $permissions = ORM::factory("permission")->find_all(); foreach ($groups as $group) { foreach ($permissions as $permission) { // Update access intents $db->query("ALTER TABLE {access_intents} MODIFY COLUMN {$permission->name}_{$group->id} BINARY(1) DEFAULT NULL"); // Update access cache if ($permission->name === "view") { $db->query("ALTER TABLE {items} MODIFY COLUMN {$permission->name}_{$group->id} BINARY(1) DEFAULT FALSE"); } else { $db->query("ALTER TABLE {access_caches} MODIFY COLUMN {$permission->name}_{$group->id} BINARY(1) NOT NULL DEFAULT FALSE"); } } } module::set_version("gallery", $version = 8); } if ($version == 8) { $db->query("ALTER TABLE {items} CHANGE COLUMN `left` `left_ptr` INT(9) NOT NULL;"); $db->query("ALTER TABLE {items} CHANGE COLUMN `right` `right_ptr` INT(9) NOT NULL;"); module::set_version("gallery", $version = 9); } if ($version == 9) { $db->query("ALTER TABLE {items} ADD KEY `weight` (`weight` DESC);"); module::set_version("gallery", $version = 10); } if ($version == 10) { module::set_var("gallery", "image_sharpen", 15); module::set_version("gallery", $version = 11); } if ($version == 11) { $db->query("ALTER TABLE {items} ADD COLUMN `relative_url_cache` varchar(255) DEFAULT NULL"); $db->query("ALTER TABLE {items} ADD COLUMN `slug` varchar(255) DEFAULT NULL"); // This is imperfect since some of the slugs may contain invalid characters, but it'll do // for now because we don't want a lengthy operation here. $db->query("UPDATE {items} SET `slug` = `name`"); // Flush all path caches because we're going to start urlencoding them. $db->query("UPDATE {items} SET `relative_url_cache` = NULL, `relative_path_cache` = NULL"); module::set_version("gallery", $version = 12); } if ($version == 12) { if (module::get_var("gallery", "active_site_theme") == "default") { module::set_var("gallery", "active_site_theme", "wind"); } if (module::get_var("gallery", "active_admin_theme") == "admin_default") { module::set_var("gallery", "active_admin_theme", "admin_wind"); } module::set_version("gallery", $version = 13); } if ($version == 13) { // Add rules for generating our thumbnails and resizes Database::instance()->query("UPDATE {graphics_rules} SET `operation` = CONCAT('gallery_graphics::', `operation`);"); module::set_version("gallery", $version = 14); } if ($version == 14) { $sidebar_blocks = block_manager::get_active("site_sidebar"); if (empty($sidebar_blocks)) { $available_blocks = block_manager::get_available_site_blocks(); foreach (array_keys(block_manager::get_available_site_blocks()) as $id) { $sidebar_blocks[] = explode(":", $id); } block_manager::set_active("site_sidebar", $sidebar_blocks); } module::set_version("gallery", $version = 15); } if ($version == 15) { module::set_var("gallery", "identity_provider", "user"); module::set_version("gallery", $version = 16); } // Convert block keys to an md5 hash of the module and block name if ($version == 16) { foreach (array("dashboard_sidebar", "dashboard_center", "site_sidebar") as $location) { $blocks = block_manager::get_active($location); $new_blocks = array(); foreach ($blocks as $block) { $new_blocks[md5("{$block[0]}:{$block[1]}")] = $block; } block_manager::set_active($location, $new_blocks); } module::set_version("gallery", $version = 17); } // We didn't like md5 hashes so convert block keys back to random keys to allow duplicates. if ($version == 17) { foreach (array("dashboard_sidebar", "dashboard_center", "site_sidebar") as $location) { $blocks = block_manager::get_active($location); $new_blocks = array(); foreach ($blocks as $block) { $new_blocks[random::int()] = $block; } block_manager::set_active($location, $new_blocks); } module::set_version("gallery", $version = 18); } // Rename blocks_site.sidebar to blocks_site_sidebar if ($version == 18) { $blocks = block_manager::get_active("site.sidebar"); block_manager::set_active("site_sidebar", $blocks); module::clear_var("gallery", "blocks_site.sidebar"); module::set_version("gallery", $version = 19); } // Set a default for the number of simultaneous uploads // Version 20 was reverted in 57adefc5baa7a2b0dfcd3e736e80c2fa86d3bfa2, so skip it. if ($version == 19 || $version == 20) { module::set_var("gallery", "simultaneous_upload_limit", 5); module::set_version("gallery", $version = 21); } // Update the graphics rules table so that the maximum height for resizes is 640 not 480. // Fixes ticket #671 if ($version == 21) { $resize_rule = ORM::factory("graphics_rule")->where("id", "=", "2")->find(); // make sure it hasn't been changed already $args = unserialize($resize_rule->args); if ($args["height"] == 480 && $args["width"] == 640) { $args["height"] = 640; $resize_rule->args = serialize($args); $resize_rule->save(); } module::set_version("gallery", $version = 22); } // Update slug values to be legal. We should have done this in the 11->12 upgrader, but I was // lazy. Mea culpa! if ($version == 22) { foreach (db::build()->from("items")->select("id", "slug")->where(db::expr("`slug` REGEXP '[^_A-Za-z0-9-]'"), "=", 1)->execute() as $row) { $new_slug = item::convert_filename_to_slug($row->slug); if (empty($new_slug)) { $new_slug = random::int(); } db::build()->update("items")->set("slug", $new_slug)->set("relative_url_cache", null)->where("id", "=", $row->id)->execute(); } module::set_version("gallery", $version = 23); } if ($version == 23) { $db->query("CREATE TABLE {failed_logins} (\n `id` int(9) NOT NULL auto_increment,\n `count` int(9) NOT NULL,\n `name` varchar(255) NOT NULL,\n `time` int(9) NOT NULL,\n PRIMARY KEY (`id`))\n DEFAULT CHARSET=utf8;"); module::set_version("gallery", $version = 24); } if ($version == 24) { foreach (array("logs", "tmp", "uploads") as $dir) { self::_protect_directory(VARPATH . $dir); } module::set_version("gallery", $version = 25); } if ($version == 25) { db::build()->update("items")->set("title", db::expr("`name`"))->and_open()->where("title", "IS", null)->or_where("title", "=", "")->close()->execute(); module::set_version("gallery", $version = 26); } if ($version == 26) { if (in_array("failed_logins", Database::instance()->list_tables())) { $db->query("RENAME TABLE {failed_logins} TO {failed_auths}"); } module::set_version("gallery", $version = 27); } if ($version == 27) { // Set the admin area timeout to 90 minutes module::set_var("gallery", "admin_area_timeout", 90 * 60); module::set_version("gallery", $version = 28); } if ($version == 28) { module::set_var("gallery", "credits", "Powered by <a href=\"%url\">%gallery_version</a>"); module::set_version("gallery", $version = 29); } if ($version == 29) { $db->query("ALTER TABLE {caches} ADD KEY (`key`);"); module::set_version("gallery", $version = 30); } if ($version == 30) { module::set_var("gallery", "maintenance_mode", 0); module::set_version("gallery", $version = 31); } if ($version == 31) { $db->query("ALTER TABLE {modules} ADD COLUMN `weight` int(9) DEFAULT NULL"); $db->query("ALTER TABLE {modules} ADD KEY (`weight`)"); db::update("modules")->set("weight", db::expr("`id`"))->execute(); module::set_version("gallery", $version = 32); } if ($version == 32) { $db->query("ALTER TABLE {items} ADD KEY (`left_ptr`)"); module::set_version("gallery", $version = 33); } if ($version == 33) { $db->query("ALTER TABLE {access_caches} ADD KEY (`item_id`)"); module::set_version("gallery", $version = 34); } if ($version == 34) { module::set_var("gallery", "visible_title_length", 15); module::set_version("gallery", $version = 35); } if ($version == 35) { module::set_var("gallery", "favicon_url", "lib/images/favicon.ico"); module::set_version("gallery", $version = 36); } if ($version == 36) { module::set_var("gallery", "email_from", "*****@*****.**"); module::set_var("gallery", "email_reply_to", "*****@*****.**"); module::set_var("gallery", "email_line_length", 70); module::set_var("gallery", "email_header_separator", serialize("\n")); module::set_version("gallery", $version = 37); } // Changed our minds and decided that the initial value should be empty // But don't just reset it blindly, only do it if the value is version 37 default if ($version == 37) { $email = module::get_var("gallery", "email_from", ""); if ($email == "*****@*****.**") { module::set_var("gallery", "email_from", ""); } $email = module::get_var("gallery", "email_reply_to", ""); if ($email == "*****@*****.**") { module::set_var("gallery", "email_reply_to", ""); } module::set_version("gallery", $version = 38); } if ($version == 38) { module::set_var("gallery", "show_user_profiles_to", "registered_users"); module::set_version("gallery", $version = 39); } if ($version == 39) { module::set_var("gallery", "extra_binary_paths", "/usr/local/bin:/opt/local/bin:/opt/bin"); module::set_version("gallery", $version = 40); } if ($version == 40) { module::clear_var("gallery", "_cache"); module::set_version("gallery", $version = 41); } if ($version == 41) { $db->query("TRUNCATE TABLE {caches}"); $db->query("ALTER TABLE {caches} DROP INDEX `key`, ADD UNIQUE `key` (`key`)"); module::set_version("gallery", $version = 42); } if ($version == 42) { $db->query("ALTER TABLE {items} CHANGE `description` `description` text DEFAULT NULL"); module::set_version("gallery", $version = 43); } if ($version == 43) { $db->query("ALTER TABLE {items} CHANGE `rand_key` `rand_key` DECIMAL(11, 10)"); module::set_version("gallery", $version = 44); } if ($version == 44) { $db->query("ALTER TABLE {messages} CHANGE `value` `value` text default NULL"); module::set_version("gallery", $version = 45); } if ($version == 45) { // Splice the upgrade_checker block into the admin dashboard at the top // of the page, but under the welcome block if it's in the first position. $blocks = block_manager::get_active("dashboard_center"); $index = count($blocks) && current($blocks) == array("gallery", "welcome") ? 1 : 0; array_splice($blocks, $index, 0, array(random::int() => array("gallery", "upgrade_checker"))); block_manager::set_active("dashboard_center", $blocks); module::set_var("gallery", "upgrade_checker_auto_enabled", true); module::set_version("gallery", $version = 46); } if ($version == 46) { module::set_var("gallery", "apple_touch_icon_url", "lib/images/apple-touch-icon.png"); module::set_version("gallery", $version = 47); } if ($version == 47 || $version == 48) { // Add configuration variable to set timezone. Defaults to the currently // used timezone (from PHP configuration). Note that in v48 we were // setting this value incorrectly, so we're going to stomp this value for v49. module::set_var("gallery", "timezone", null); module::set_version("gallery", $version = 49); } if ($version == 49) { // In v49 we changed the Item_Model validation code to disallow files with two dots in them, // but we didn't rename any files which fail to validate, so as soon as you do anything to // change those files (eg. as a side effect of getting the url or file path) it fails to // validate. Fix those here. This might be slow, but if it times out it can just pick up // where it left off. foreach (db::build()->from("items")->select("id")->where("type", "<>", "album")->where(db::expr("`name` REGEXP '\\\\..*\\\\.'"), "=", 1)->order_by("id", "asc")->execute() as $row) { set_time_limit(30); $item = ORM::factory("item", $row->id); $item->name = legal_file::smash_extensions($item->name); $item->save(); } module::set_version("gallery", $version = 50); } if ($version == 50) { // In v51, we added a lock_timeout variable so that administrators could edit the time out // from 1 second to a higher variable if their system runs concurrent parallel uploads for // instance. module::set_var("gallery", "lock_timeout", 1); module::set_version("gallery", $version = 51); } if ($version == 51) { // In v52, we added functions to the legal_file helper that map photo and movie file // extensions to their mime types (and allow extension of the list by other modules). During // this process, we correctly mapped m4v files to video/x-m4v, correcting a previous error // where they were mapped to video/mp4. This corrects the existing items. db::build()->update("items")->set("mime_type", "video/x-m4v")->where("name", "REGEXP", "\\.m4v\$")->execute(); module::set_version("gallery", $version = 52); } if ($version == 52) { // In v53, we added the ability to change the default time used when extracting frames from // movies. Previously we hard-coded this at 3 seconds, so we use that as the default. module::set_var("gallery", "movie_extract_frame_time", 3); module::set_version("gallery", $version = 53); } if ($version == 53) { // In v54, we changed how we check for name and slug conflicts in Item_Model. Previously, // we checked the whole filename. As a result, "foo.jpg" and "foo.png" were not considered // conflicting if their slugs were different (a rare case in practice since server_add and // uploader would give them both the same slug "foo"). Now, we check the filename without its // extension. This upgrade stanza fixes any conflicts where they were previously allowed. // This might be slow, but if it times out it can just pick up where it left off. // Find and loop through each conflict (e.g. "foo.jpg", "foo.png", and "foo.flv" are one // conflict; "bar.jpg", "bar.png", and "bar.flv" are another) foreach (db::build()->select_distinct(array("parent_base_name" => db::expr("CONCAT(`parent_id`, ':', LOWER(SUBSTR(`name`, 1, LOCATE('.', `name`) - 1)))")))->select(array("C" => "COUNT(\"*\")"))->from("items")->where("type", "<>", "album")->having("C", ">", 1)->group_by("parent_base_name")->execute() as $conflict) { list($parent_id, $base_name) = explode(":", $conflict->parent_base_name, 2); $base_name_escaped = Database::escape_for_like($base_name); // Loop through the items for each conflict foreach (db::build()->from("items")->select("id")->where("type", "<>", "album")->where("parent_id", "=", $parent_id)->where("name", "LIKE", "{$base_name_escaped}.%")->limit(1000000)->offset(1)->execute() as $row) { set_time_limit(30); $item = ORM::factory("item", $row->id); $item->name = $item->name; // this will force Item_Model to check for conflicts on save $item->save(); } } module::set_version("gallery", $version = 54); } if ($version == 54) { $db->query("ALTER TABLE {items} ADD KEY `relative_path_cache` (`relative_path_cache`)"); module::set_version("gallery", $version = 55); } if ($version == 55) { // In v56, we added the ability to change the default behavior regarding movie uploads. It // can be set to "always", "never", or "autodetect" to match the previous behavior where they // are allowed only if FFmpeg is found. module::set_var("gallery", "movie_allow_uploads", "autodetect"); module::set_version("gallery", $version = 56); } if ($version == 56) { // Cleanup possible instances where resize_dirty of albums or movies was set to 0. This is // unlikely to have occurred, and doesn't currently matter much since albums and movies don't // have resize images anyway. However, it may be useful to be consistent here going forward. db::build()->update("items")->set("resize_dirty", 1)->where("type", "<>", "photo")->execute(); module::set_version("gallery", $version = 57); } if ($version == 57) { // In v58 we changed the Item_Model validation code to disallow files or directories with // backslashes in them, and we need to fix any existing items that have them. This is // pretty unlikely, as having backslashes would have probably already caused other issues for // users, but we should check anyway. This might be slow, but if it times out it can just // pick up where it left off. foreach (db::build()->from("items")->select("id")->where(db::expr("`name` REGEXP '\\\\\\\\'"), "=", 1)->order_by("id", "asc")->execute() as $row) { set_time_limit(30); $item = ORM::factory("item", $row->id); $item->name = str_replace("\\", "_", $item->name); $item->save(); } module::set_version("gallery", $version = 58); } }
static function upgrade($version) { $db = Database::instance(); if ($version == 1) { module::set_var("gallery", "date_format", "Y-M-d"); module::set_var("gallery", "date_time_format", "Y-M-d H:i:s"); module::set_var("gallery", "time_format", "H:i:s"); module::set_version("gallery", $version = 2); } if ($version == 2) { module::set_var("gallery", "show_credits", 1); module::set_version("gallery", $version = 3); } if ($version == 3) { $db->query("CREATE TABLE {caches} (\n `id` varchar(255) NOT NULL,\n `tags` varchar(255),\n `expiration` int(9) NOT NULL,\n `cache` text,\n PRIMARY KEY (`id`),\n KEY (`tags`))\n DEFAULT CHARSET=utf8;"); module::set_version("gallery", $version = 4); } if ($version == 4) { Cache::instance()->delete_all(); $db->query("ALTER TABLE {caches} MODIFY COLUMN `cache` LONGBLOB"); module::set_version("gallery", $version = 5); } if ($version == 5) { Cache::instance()->delete_all(); $db->query("ALTER TABLE {caches} DROP COLUMN `id`"); $db->query("ALTER TABLE {caches} ADD COLUMN `key` varchar(255) NOT NULL"); $db->query("ALTER TABLE {caches} ADD COLUMN `id` int(9) NOT NULL auto_increment PRIMARY KEY"); module::set_version("gallery", $version = 6); } if ($version == 6) { module::clear_var("gallery", "version"); module::set_version("gallery", $version = 7); } if ($version == 7) { $groups = identity::groups(); $permissions = ORM::factory("permission")->find_all(); foreach ($groups as $group) { foreach ($permissions as $permission) { // Update access intents $db->query("ALTER TABLE {access_intents} MODIFY COLUMN {$permission->name}_{$group->id} BINARY(1) DEFAULT NULL"); // Update access cache if ($permission->name === "view") { $db->query("ALTER TABLE {items} MODIFY COLUMN {$permission->name}_{$group->id} BINARY(1) DEFAULT FALSE"); } else { $db->query("ALTER TABLE {access_caches} MODIFY COLUMN {$permission->name}_{$group->id} BINARY(1) NOT NULL DEFAULT FALSE"); } } } module::set_version("gallery", $version = 8); } if ($version == 8) { $db->query("ALTER TABLE {items} CHANGE COLUMN `left` `left_ptr` INT(9) NOT NULL;"); $db->query("ALTER TABLE {items} CHANGE COLUMN `right` `right_ptr` INT(9) NOT NULL;"); module::set_version("gallery", $version = 9); } if ($version == 9) { $db->query("ALTER TABLE {items} ADD KEY `weight` (`weight` DESC);"); module::set_version("gallery", $version = 10); } if ($version == 10) { module::set_var("gallery", "image_sharpen", 15); module::set_version("gallery", $version = 11); } if ($version == 11) { $db->query("ALTER TABLE {items} ADD COLUMN `relative_url_cache` varchar(255) DEFAULT NULL"); $db->query("ALTER TABLE {items} ADD COLUMN `slug` varchar(255) DEFAULT NULL"); // This is imperfect since some of the slugs may contain invalid characters, but it'll do // for now because we don't want a lengthy operation here. $db->query("UPDATE {items} SET `slug` = `name`"); // Flush all path caches becuase we're going to start urlencoding them. $db->query("UPDATE {items} SET `relative_url_cache` = NULL, `relative_path_cache` = NULL"); module::set_version("gallery", $version = 12); } if ($version == 12) { if (module::get_var("gallery", "active_site_theme") == "default") { module::set_var("gallery", "active_site_theme", "wind"); } if (module::get_var("gallery", "active_admin_theme") == "admin_default") { module::set_var("gallery", "active_admin_theme", "admin_wind"); } module::set_version("gallery", $version = 13); } if ($version == 13) { // Add rules for generating our thumbnails and resizes Database::instance()->query("UPDATE {graphics_rules} SET `operation` = CONCAT('gallery_graphics::', `operation`);"); module::set_version("gallery", $version = 14); } if ($version == 14) { $sidebar_blocks = block_manager::get_active("site_sidebar"); if (empty($sidebar_blocks)) { $available_blocks = block_manager::get_available_site_blocks(); foreach (array_keys(block_manager::get_available_site_blocks()) as $id) { $sidebar_blocks[] = explode(":", $id); } block_manager::set_active("site_sidebar", $sidebar_blocks); } module::set_version("gallery", $version = 15); } if ($version == 15) { module::set_var("gallery", "identity_provider", "user"); module::set_version("gallery", $version = 16); } // Convert block keys to an md5 hash of the module and block name if ($version == 16) { foreach (array("dashboard_sidebar", "dashboard_center", "site_sidebar") as $location) { $blocks = block_manager::get_active($location); $new_blocks = array(); foreach ($blocks as $block) { $new_blocks[md5("{$block[0]}:{$block[1]}")] = $block; } block_manager::set_active($location, $new_blocks); } module::set_version("gallery", $version = 17); } // We didn't like md5 hashes so convert block keys back to random keys to allow duplicates. if ($version == 17) { foreach (array("dashboard_sidebar", "dashboard_center", "site_sidebar") as $location) { $blocks = block_manager::get_active($location); $new_blocks = array(); foreach ($blocks as $block) { $new_blocks[random::int()] = $block; } block_manager::set_active($location, $new_blocks); } module::set_version("gallery", $version = 18); } // Rename blocks_site.sidebar to blocks_site_sidebar if ($version == 18) { $blocks = block_manager::get_active("site.sidebar"); block_manager::set_active("site_sidebar", $blocks); module::clear_var("gallery", "blocks_site.sidebar"); module::set_version("gallery", $version = 19); } // Set a default for the number of simultaneous uploads // Version 20 was reverted in 57adefc5baa7a2b0dfcd3e736e80c2fa86d3bfa2, so skip it. if ($version == 19 || $version == 20) { module::set_var("gallery", "simultaneous_upload_limit", 5); module::set_version("gallery", $version = 21); } // Update the graphics rules table so that the maximum height for resizes is 640 not 480. // Fixes ticket #671 if ($version == 21) { $resize_rule = ORM::factory("graphics_rule")->where("id", "=", "2")->find(); // make sure it hasn't been changed already $args = unserialize($resize_rule->args); if ($args["height"] == 480 && $args["width"] == 640) { $args["height"] = 640; $resize_rule->args = serialize($args); $resize_rule->save(); } module::set_version("gallery", $version = 22); } // Update slug values to be legal. We should have done this in the 11->12 upgrader, but I was // lazy. Mea culpa! if ($version == 22) { foreach (db::build()->from("items")->select("id", "slug")->where(db::expr("`slug` REGEXP '[^_A-Za-z0-9-]'"), "=", 1)->execute() as $row) { $new_slug = item::convert_filename_to_slug($row->slug); if (empty($new_slug)) { $new_slug = random::int(); } db::build()->update("items")->set("slug", $new_slug)->set("relative_url_cache", null)->where("id", "=", $row->id)->execute(); } module::set_version("gallery", $version = 23); } if ($version == 23) { $db->query("CREATE TABLE {failed_logins} (\n `id` int(9) NOT NULL auto_increment,\n `count` int(9) NOT NULL,\n `name` varchar(255) NOT NULL,\n `time` int(9) NOT NULL,\n PRIMARY KEY (`id`))\n DEFAULT CHARSET=utf8;"); module::set_version("gallery", $version = 24); } if ($version == 24) { foreach (array("logs", "tmp", "uploads") as $dir) { self::_protect_directory(VARPATH . $dir); } module::set_version("gallery", $version = 25); } if ($version == 25) { db::build()->update("items")->set("title", db::expr("`name`"))->and_open()->where("title", "IS", null)->or_where("title", "=", "")->close()->execute(); module::set_version("gallery", $version = 26); } if ($version == 26) { if (in_array("failed_logins", Database::instance()->list_tables())) { $db->query("RENAME TABLE {failed_logins} TO {failed_auths}"); } module::set_version("gallery", $version = 27); } if ($version == 27) { // Set the admin area timeout to 90 minutes module::set_var("gallery", "admin_area_timeout", 90 * 60); module::set_version("gallery", $version = 28); } if ($version == 28) { module::set_var("gallery", "credits", "Powered by <a href=\"%url\">%gallery_version</a>"); module::set_version("gallery", $version = 29); } if ($version == 29) { $db->query("ALTER TABLE {caches} ADD KEY (`key`);"); module::set_version("gallery", $version = 30); } if ($version == 30) { module::set_var("gallery", "maintenance_mode", 0); module::set_version("gallery", $version = 31); } if ($version == 31) { $db->query("ALTER TABLE {modules} ADD COLUMN `weight` int(9) DEFAULT NULL"); $db->query("ALTER TABLE {modules} ADD KEY (`weight`)"); db::update("modules")->set("weight", db::expr("`id`"))->execute(); module::set_version("gallery", $version = 32); } if ($version == 32) { $db->query("ALTER TABLE {items} ADD KEY (`left_ptr`)"); module::set_version("gallery", $version = 33); } if ($version == 33) { $db->query("ALTER TABLE {access_caches} ADD KEY (`item_id`)"); module::set_version("gallery", $version = 34); } if ($version == 34) { module::set_var("gallery", "visible_title_length", 15); module::set_version("gallery", $version = 35); } if ($version == 35) { module::set_var("gallery", "favicon_url", "lib/images/favicon.ico"); module::set_version("gallery", $version = 36); } if ($version == 36) { module::set_var("gallery", "email_from", "*****@*****.**"); module::set_var("gallery", "email_reply_to", "*****@*****.**"); module::set_var("gallery", "email_line_length", 70); module::set_var("gallery", "email_header_separator", serialize("\n")); module::set_version("gallery", $version = 37); } // Changed our minds and decided that the initial value should be empty // But don't just reset it blindly, only do it if the value is version 37 default if ($version == 37) { $email = module::get_var("gallery", "email_from", ""); if ($email == "*****@*****.**") { module::set_var("gallery", "email_from", ""); } $email = module::get_var("gallery", "email_reply_to", ""); if ($email == "*****@*****.**") { module::set_var("gallery", "email_reply_to", ""); } module::set_version("gallery", $version = 38); } if ($version == 38) { module::set_var("gallery", "show_user_profiles_to", "registered_users"); module::set_version("gallery", $version = 39); } if ($version == 39) { module::set_var("gallery", "extra_binary_paths", "/usr/local/bin:/opt/local/bin:/opt/bin"); module::set_version("gallery", $version = 40); } if ($version == 40) { module::clear_var("gallery", "_cache"); module::set_version("gallery", $version = 41); } if ($version == 41) { $db->query("TRUNCATE TABLE {caches}"); $db->query("ALTER TABLE {caches} DROP INDEX `key`, ADD UNIQUE `key` (`key`)"); module::set_version("gallery", $version = 42); } if ($version == 42) { $db->query("ALTER TABLE {items} CHANGE `description` `description` text DEFAULT NULL"); module::set_version("gallery", $version = 43); } if ($version == 43) { $db->query("ALTER TABLE {items} CHANGE `rand_key` `rand_key` DECIMAL(11, 10)"); module::set_version("gallery", $version = 44); } if ($version == 44) { $db->query("ALTER TABLE {messages} CHANGE `value` `value` text default NULL"); module::set_version("gallery", $version = 45); } if ($version == 45) { // Splice the upgrade_checker block into the admin dashboard at the top // of the page, but under the welcome block if it's in the first position. $blocks = block_manager::get_active("dashboard_center"); $index = count($blocks) && current($blocks) == array("gallery", "welcome") ? 1 : 0; array_splice($blocks, $index, 0, array(random::int() => array("gallery", "upgrade_checker"))); block_manager::set_active("dashboard_center", $blocks); module::set_var("gallery", "upgrade_checker_auto_enabled", true); module::set_version("gallery", $version = 46); } }
/** * Handle any business logic necessary to create or modify an item. * @see ORM::save() * * @return ORM Item_Model */ public function save() { $significant_changes = $this->changed; unset($significant_changes["view_count"]); unset($significant_changes["relative_url_cache"]); unset($significant_changes["relative_path_cache"]); if (!empty($this->changed) && $significant_changes || isset($this->data_file)) { $this->updated = time(); if (!$this->loaded()) { // Create a new item. module::event("item_before_create", $this); // Set a weight if it's missing. We don't do this in the constructor because it's not a // simple assignment. if (empty($this->weight)) { $this->weight = item::get_max_weight(); } // Make an url friendly slug from the name, if necessary if (empty($this->slug)) { $this->slug = item::convert_filename_to_slug(pathinfo($this->name, PATHINFO_FILENAME)); // If the filename is all invalid characters, then the slug may be empty here. Pick a // random value. if (empty($this->slug)) { $this->slug = (string) rand(1000, 9999); } } // Get the width, height and mime type from our data file for photos and movies. if ($this->is_photo() || $this->is_movie()) { if ($this->is_photo()) { list($this->width, $this->height, $this->mime_type, $extension) = photo::get_file_metadata($this->data_file); } else { if ($this->is_movie()) { list($this->width, $this->height, $this->mime_type, $extension) = movie::get_file_metadata($this->data_file); } } // Force an extension onto the name if necessary $pi = pathinfo($this->data_file); if (empty($pi["extension"])) { $this->name = "{$this->name}.{$extension}"; } } $this->_randomize_name_or_slug_on_conflict(); parent::save(); // Build our url caches, then save again. We have to do this after it's already been // saved once because we use only information from the database to build the paths. If we // could depend on a save happening later we could defer this 2nd save. $this->_build_relative_caches(); parent::save(); // Take any actions that we can only do once all our paths are set correctly after saving. switch ($this->type) { case "album": mkdir($this->file_path()); mkdir(dirname($this->thumb_path())); mkdir(dirname($this->resize_path())); break; case "photo": case "movie": // The thumb or resize may already exist in the case where a movie and a photo generate // a thumbnail of the same name (eg, foo.flv movie and foo.jpg photo will generate // foo.jpg thumbnail). If that happens, randomize and save again. if (file_exists($this->resize_path()) || file_exists($this->thumb_path())) { $pi = pathinfo($this->name); $this->name = $pi["filename"] . "-" . random::int() . "." . $pi["extension"]; parent::save(); } copy($this->data_file, $this->file_path()); break; } // This will almost definitely trigger another save, so put it at the end so that we're // tail recursive. Null out the data file variable first, otherwise the next save will // trigger an item_updated_data_file event. $this->data_file = null; module::event("item_created", $this); } else { // Update an existing item module::event("item_before_update", $item); // If any significant fields have changed, load up a copy of the original item and // keep it around. $original = ORM::factory("item", $this->id); // Preserve the extension of the data file. Many helpers, (e.g. ImageMagick), assume // the MIME type from the extension. So when we adopt the new data file, it's important // to adopt the new extension. That ensures that the item's extension is always // appropriate for its data. We don't try to preserve the name of the data file, though, // because the name is typically a temporary randomly-generated name. if (isset($this->data_file)) { $extension = pathinfo($this->data_file, PATHINFO_EXTENSION); $new_name = pathinfo($this->name, PATHINFO_FILENAME) . ".{$extension}"; if (!empty($extension) && strcmp($this->name, $new_name)) { $this->name = $new_name; } if ($this->is_photo()) { list($this->width, $this->height, $this->mime_type, $extension) = photo::get_file_metadata($this->data_file); } else { if ($this->is_movie()) { list($this->width, $this->height, $this->mime_type, $extension) = movie::get_file_metadata($this->data_file); } } } if (array_intersect($this->changed, array("parent_id", "name", "slug"))) { $original->_build_relative_caches(); $this->relative_path_cache = null; $this->relative_url_cache = null; } $this->_randomize_name_or_slug_on_conflict(); parent::save(); // Now update the filesystem and any database caches if there were significant value // changes. If anything past this point fails, then we'll have an inconsistent database // so this code should be as robust as we can make it. // Update the MPTT pointers, if necessary. We have to do this before we generate any // cached paths! if ($original->parent_id != $this->parent_id) { parent::move_to($this->parent()); } if ($original->parent_id != $this->parent_id || $original->name != $this->name) { $this->_build_relative_caches(); // If there is a data file, then we want to preserve both the old data and the new data. // (Third-party event handlers would like access to both). The old data file will be // accessible via the $original item, and the new one via $this item. But in that case, // we don't want to rename the original as below, because the old data would end up being // clobbered by the new data file. Also, the rename isn't necessary, because the new item // data is coming from the data file anyway. So we only perform the rename if there isn't // a data file. Another way to solve this would be to copy the original file rather than // conditionally rename it, but a copy would cost far more than the rename. if (!isset($this->data_file)) { @rename($original->file_path(), $this->file_path()); } // Move all of the items associated data files if ($this->is_album()) { @rename(dirname($original->resize_path()), dirname($this->resize_path())); @rename(dirname($original->thumb_path()), dirname($this->thumb_path())); } else { @rename($original->resize_path(), $this->resize_path()); @rename($original->thumb_path(), $this->thumb_path()); } if ($original->parent_id != $this->parent_id) { // This will result in 2 events since we'll still fire the item_updated event below module::event("item_moved", $this, $original->parent()); } } // Changing the name, slug or parent ripples downwards if ($this->is_album() && ($original->name != $this->name || $original->slug != $this->slug || $original->parent_id != $this->parent_id)) { db::build()->update("items")->set("relative_url_cache", null)->set("relative_path_cache", null)->where("left_ptr", ">", $this->left_ptr)->where("right_ptr", "<", $this->right_ptr)->execute(); } // Replace the data file, if requested. if ($this->data_file && ($this->is_photo() || $this->is_movie())) { copy($this->data_file, $this->file_path()); // Get the width, height and mime type from our data file for photos and movies. if ($this->is_photo()) { list($this->width, $this->height) = photo::get_file_metadata($this->file_path()); } else { if ($this->is_movie()) { list($this->width, $this->height) = movie::get_file_metadata($this->file_path()); } } $this->thumb_dirty = 1; $this->resize_dirty = 1; } module::event("item_updated", $original, $this); if ($this->data_file) { // Null out the data file variable here, otherwise this event will trigger another // save() which will think that we're doing another file move. $this->data_file = null; if ($original->file_path() != $this->file_path()) { @unlink($original->file_path()); } module::event("item_updated_data_file", $this); } } } else { if (!empty($this->changed)) { // Insignificant changes only. Don't fire events or do any special checking to try to keep // this lightweight. parent::save(); } } return $this; }
/** * Create a new photo. * @param integer $parent_id id of parent album * @param string $filename path to the photo file on disk * @param string $name the filename to use for this photo in the album * @param integer $title the title of the new photo * @param string $description (optional) the longer description of this photo * @param string $slug (optional) the url component for this photo * @return Item_Model */ static function create($parent, $filename, $name, $title, $description = null, $owner_id = null, $slug = null) { if (!$parent->loaded || !$parent->is_album()) { throw new Exception("@todo INVALID_PARENT"); } if (!is_file($filename)) { throw new Exception("@todo MISSING_IMAGE_FILE"); } if (strpos($name, "/")) { throw new Exception("@todo NAME_CANNOT_CONTAIN_SLASH"); } // We don't allow trailing periods as a security measure // ref: http://dev.kohanaphp.com/issues/684 if (rtrim($name, ".") != $name) { throw new Exception("@todo NAME_CANNOT_END_IN_PERIOD"); } if (filesize($filename) == 0) { throw new Exception("@todo EMPTY_INPUT_FILE"); } $image_info = getimagesize($filename); // Force an extension onto the name $pi = pathinfo($filename); if (empty($pi["extension"])) { $pi["extension"] = image_type_to_extension($image_info[2], false); $name .= "." . $pi["extension"]; } if (empty($slug)) { $slug = item::convert_filename_to_slug($name); } $photo = ORM::factory("item"); $photo->type = "photo"; $photo->title = $title; $photo->description = $description; $photo->name = $name; $photo->owner_id = $owner_id ? $owner_id : user::active(); $photo->width = $image_info[0]; $photo->height = $image_info[1]; $photo->mime_type = empty($image_info['mime']) ? "application/unknown" : $image_info['mime']; $photo->thumb_dirty = 1; $photo->resize_dirty = 1; $photo->sort_column = "weight"; $photo->slug = $slug; $photo->rand_key = (double) mt_rand() / (double) mt_getrandmax(); // Randomize the name or slug if there's a conflict // @todo Improve this. Random numbers are not user friendly while (ORM::factory("item")->where("parent_id", $parent->id)->open_paren()->where("name", $photo->name)->orwhere("slug", $photo->slug)->close_paren()->find()->id) { $rand = rand(); $photo->name = "{$name}.{$rand}.{$pi['extension']}"; $photo->slug = "{$slug}-{$rand}"; } // This saves the photo $photo->add_to_parent($parent); /* * If the thumb or resize already exists then rename it. We need to do this after the save * because the resize_path and thumb_path both call relative_path which caches the * path. Before add_to_parent the relative path will be incorrect. */ if (file_exists($photo->resize_path()) || file_exists($photo->thumb_path())) { $photo->name = $pi["filename"] . "-" . rand() . "." . $pi["extension"]; $photo->save(); } copy($filename, $photo->file_path()); // @todo: publish this from inside Item_Model::save() when we refactor to the point where // there's only one save() happening here. module::event("item_created", $photo); // Build our thumbnail/resizes. If we fail to build thumbnail/resize we assume that the image // is bad in some way and discard it. try { graphics::generate($photo); } catch (Exception $e) { $photo->delete(); throw $e; } // If the parent has no cover item, make this it. if (access::can("edit", $parent) && $parent->album_cover_item_id == null) { item::make_album_cover($photo); } return $photo; }
/** * Create a new movie. * @param integer $parent_id id of parent album * @param string $filename path to the photo file on disk * @param string $name the filename to use for this photo in the album * @param integer $title the title of the new photo * @param string $description (optional) the longer description of this photo * @param string $slug (optional) the url component for this photo * @return Item_Model */ static function create($parent, $filename, $name, $title, $description = null, $owner_id = null, $slug = null) { if (!$parent->loaded || !$parent->is_album()) { throw new Exception("@todo INVALID_PARENT"); } if (!is_file($filename)) { throw new Exception("@todo MISSING_MOVIE_FILE"); } if (strpos($name, "/")) { throw new Exception("@todo NAME_CANNOT_CONTAIN_SLASH"); } // We don't allow trailing periods as a security measure // ref: http://dev.kohanaphp.com/issues/684 if (rtrim($name, ".") != $name) { throw new Exception("@todo NAME_CANNOT_END_IN_PERIOD"); } try { $movie_info = movie::getmoviesize($filename); } catch (Exception $e) { // Assuming this is MISSING_FFMPEG for now $movie_info = getimagesize(MODPATH . "gallery/images/missing_movie.png"); } // Force an extension onto the name $pi = pathinfo($filename); if (empty($pi["extension"])) { $pi["extension"] = image_type_to_extension($movie_info[2], false); $name .= "." . $pi["extension"]; } if (empty($slug)) { $slug = item::convert_filename_to_slug($name); } $movie = ORM::factory("item"); $movie->type = "movie"; $movie->title = $title; $movie->description = $description; $movie->name = $name; $movie->owner_id = $owner_id ? $owner_id : user::active(); $movie->width = $movie_info[0]; $movie->height = $movie_info[1]; $movie->mime_type = strtolower($pi["extension"]) == "mp4" ? "video/mp4" : "video/x-flv"; $movie->thumb_dirty = 1; $movie->resize_dirty = 1; $movie->sort_column = "weight"; $movie->slug = $slug; $movie->rand_key = (double) mt_rand() / (double) mt_getrandmax(); // Randomize the name if there's a conflict // @todo Improve this. Random numbers are not user friendly while (ORM::factory("item")->where("parent_id", $parent->id)->open_paren()->where("name", $movie->name)->orwhere("slug", $movie->slug)->close_paren()->find()->id) { $rand = rand(); $movie->name = "{$name}.{$rand}.{$pi['extension']}"; $movie->slug = "{$slug}-{$rand}"; } // This saves the photo $movie->add_to_parent($parent); // If the thumb or resize already exists then rename it if (file_exists($movie->resize_path()) || file_exists($movie->thumb_path())) { $movie->name = $pi["filename"] . "-" . rand() . "." . $pi["extension"]; $movie->save(); } copy($filename, $movie->file_path()); // @todo: publish this from inside Item_Model::save() when we refactor to the point where // there's only one save() happening here. module::event("item_created", $movie); // Build our thumbnail graphics::generate($movie); // If the parent has no cover item, make this it. if (access::can("edit", $parent) && $parent->album_cover_item_id == null) { item::make_album_cover($movie); } return $movie; }
/** * Handle any business logic necessary to create or modify an item. * @see ORM::save() * * @return ORM Item_Model */ public function save() { $significant_changes = $this->changed; foreach (array("view_count", "relative_url_cache", "relative_path_cache", "resize_width", "resize_height", "resize_dirty", "thumb_width", "thumb_height", "thumb_dirty") as $key) { unset($significant_changes[$key]); } if (!empty($this->changed) && $significant_changes || isset($this->data_file)) { $this->updated = time(); if (!$this->loaded()) { // Create a new item. module::event("item_before_create", $this); // Set a weight if it's missing. We don't do this in the constructor because it's not a // simple assignment. if (empty($this->weight)) { $this->weight = item::get_max_weight(); } if ($this->is_album()) { // Sanitize the album name. $this->name = legal_file::sanitize_dirname($this->name); } else { // Process the data file info. This also sanitizes the item name. if (isset($this->data_file)) { $this->_process_data_file_info(); } else { // New photos and movies must have a data file. $this->data_file_error = true; } } // Make an url friendly slug from the name, if necessary if (empty($this->slug)) { $this->slug = item::convert_filename_to_slug(pathinfo($this->name, PATHINFO_FILENAME)); // If the filename is all invalid characters, then the slug may be empty here. We set a // generic name ("photo", "movie", or "album") based on its type, then rely on // check_and_fix_conflicts to ensure it doesn't conflict with another name. if (empty($this->slug)) { $this->slug = $this->type; } } $this->_check_and_fix_conflicts(); parent::save(); // Build our url caches, then save again. We have to do this after it's already been // saved once because we use only information from the database to build the paths. If we // could depend on a save happening later we could defer this 2nd save. $this->_build_relative_caches(); parent::save(); // Take any actions that we can only do once all our paths are set correctly after saving. switch ($this->type) { case "album": mkdir($this->file_path()); mkdir(dirname($this->thumb_path())); mkdir(dirname($this->resize_path())); break; case "photo": case "movie": copy($this->data_file, $this->file_path()); break; } // This will almost definitely trigger another save, so put it at the end so that we're // tail recursive. Null out the data file variable first, otherwise the next save will // trigger an item_updated_data_file event. $this->data_file = null; module::event("item_created", $this); } else { // Update an existing item module::event("item_before_update", $this); // If any significant fields have changed, load up a copy of the original item and // keep it around. $original = ORM::factory("item", $this->id); // If we have a new data file, process its info. This will get its metadata and // preserve the extension of the data file. Many helpers, (e.g. ImageMagick), assume // the MIME type from the extension. So when we adopt the new data file, it's important // to adopt the new extension. That ensures that the item's extension is always // appropriate for its data. We don't try to preserve the name of the data file, though, // because the name is typically a temporary randomly-generated name. if (isset($this->data_file)) { $this->_process_data_file_info(); } else { if (!$this->is_album() && array_key_exists("name", $this->changed)) { // There's no new data file, but the name changed. If it's a photo or movie, // make sure the new name still agrees with the file type. $this->name = legal_file::sanitize_filename($this->name, pathinfo($original->name, PATHINFO_EXTENSION), $this->type); } } // If an album's name changed, sanitize it. if ($this->is_album() && array_key_exists("name", $this->changed)) { $this->name = legal_file::sanitize_dirname($this->name); } // If an album's cover has changed (or been removed), delete any existing album cover, // reset the thumb metadata, and mark the thumb as dirty. if (array_key_exists("album_cover_item_id", $this->changed) && $this->is_album()) { @unlink($original->thumb_path()); $this->thumb_dirty = 1; $this->thumb_height = 0; $this->thumb_width = 0; } if (array_intersect($this->changed, array("parent_id", "name", "slug"))) { $original->_build_relative_caches(); $this->relative_path_cache = null; $this->relative_url_cache = null; } $this->_check_and_fix_conflicts(); parent::save(); // Now update the filesystem and any database caches if there were significant value // changes. If anything past this point fails, then we'll have an inconsistent database // so this code should be as robust as we can make it. // Update the MPTT pointers, if necessary. We have to do this before we generate any // cached paths! if ($original->parent_id != $this->parent_id) { parent::move_to($this->parent()); } if ($original->parent_id != $this->parent_id || $original->name != $this->name) { $this->_build_relative_caches(); // If there is a data file, then we want to preserve both the old data and the new data. // (Third-party event handlers would like access to both). The old data file will be // accessible via the $original item, and the new one via $this item. But in that case, // we don't want to rename the original as below, because the old data would end up being // clobbered by the new data file. Also, the rename isn't necessary, because the new item // data is coming from the data file anyway. So we only perform the rename if there isn't // a data file. Another way to solve this would be to copy the original file rather than // conditionally rename it, but a copy would cost far more than the rename. if (!isset($this->data_file)) { @rename($original->file_path(), $this->file_path()); } // Move all of the items associated data files if ($this->is_album()) { @rename(dirname($original->resize_path()), dirname($this->resize_path())); @rename(dirname($original->thumb_path()), dirname($this->thumb_path())); } else { @rename($original->resize_path(), $this->resize_path()); @rename($original->thumb_path(), $this->thumb_path()); } } // Changing the name, slug or parent ripples downwards if ($this->is_album() && ($original->name != $this->name || $original->slug != $this->slug || $original->parent_id != $this->parent_id)) { db::build()->update("items")->set("relative_url_cache", null)->set("relative_path_cache", null)->where("left_ptr", ">", $this->left_ptr)->where("right_ptr", "<", $this->right_ptr)->execute(); } // Replace the data file, if requested. if ($this->data_file && ($this->is_photo() || $this->is_movie())) { copy($this->data_file, $this->file_path()); $this->thumb_dirty = 1; $this->resize_dirty = 1; } if ($original->parent_id != $this->parent_id) { // This will result in 2 events since we'll still fire the item_updated event below module::event("item_moved", $this, $original->parent()); } module::event("item_updated", $original, $this); if ($this->data_file) { // Null out the data file variable here, otherwise this event will trigger another // save() which will think that we're doing another file move. $this->data_file = null; if ($original->file_path() != $this->file_path()) { @unlink($original->file_path()); } module::event("item_updated_data_file", $this); } } } else { if (!empty($this->changed)) { // Insignificant changes only. Don't fire events or do any special checking to try to keep // this lightweight. parent::save(); } } return $this; }
static function upgrade($version) { $db = Database::instance(); if ($version == 1) { module::set_var("gallery", "date_format", "Y-M-d"); module::set_var("gallery", "date_time_format", "Y-M-d H:i:s"); module::set_var("gallery", "time_format", "H:i:s"); module::set_version("gallery", $version = 2); } if ($version == 2) { module::set_var("gallery", "show_credits", 1); module::set_version("gallery", $version = 3); } if ($version == 3) { $db->query("CREATE TABLE {caches} (\n `id` varchar(255) NOT NULL,\n `tags` varchar(255),\n `expiration` int(9) NOT NULL,\n `cache` text,\n PRIMARY KEY (`id`),\n KEY (`tags`))\n DEFAULT CHARSET=utf8;"); module::set_version("gallery", $version = 4); } if ($version == 4) { Cache::instance()->delete_all(); $db->query("ALTER TABLE {caches} MODIFY COLUMN `cache` LONGBLOB"); module::set_version("gallery", $version = 5); } if ($version == 5) { Cache::instance()->delete_all(); $db->query("ALTER TABLE {caches} DROP COLUMN `id`"); $db->query("ALTER TABLE {caches} ADD COLUMN `key` varchar(255) NOT NULL"); $db->query("ALTER TABLE {caches} ADD COLUMN `id` int(9) NOT NULL auto_increment PRIMARY KEY"); module::set_version("gallery", $version = 6); } if ($version == 6) { module::clear_var("gallery", "version"); module::set_version("gallery", $version = 7); } if ($version == 7) { $groups = identity::groups(); $permissions = ORM::factory("permission")->find_all(); foreach ($groups as $group) { foreach ($permissions as $permission) { // Update access intents $db->query("ALTER TABLE {access_intents} MODIFY COLUMN {$permission->name}_{$group->id} BINARY(1) DEFAULT NULL"); // Update access cache if ($permission->name === "view") { $db->query("ALTER TABLE {items} MODIFY COLUMN {$permission->name}_{$group->id} BINARY(1) DEFAULT FALSE"); } else { $db->query("ALTER TABLE {access_caches} MODIFY COLUMN {$permission->name}_{$group->id} BINARY(1) NOT NULL DEFAULT FALSE"); } } } module::set_version("gallery", $version = 8); } if ($version == 8) { $db->query("ALTER TABLE {items} CHANGE COLUMN `left` `left_ptr` INT(9) NOT NULL;"); $db->query("ALTER TABLE {items} CHANGE COLUMN `right` `right_ptr` INT(9) NOT NULL;"); module::set_version("gallery", $version = 9); } if ($version == 9) { $db->query("ALTER TABLE {items} ADD KEY `weight` (`weight` DESC);"); module::set_version("gallery", $version = 10); } if ($version == 10) { module::set_var("gallery", "image_sharpen", 15); module::set_version("gallery", $version = 11); } if ($version == 11) { $db->query("ALTER TABLE {items} ADD COLUMN `relative_url_cache` varchar(255) DEFAULT NULL"); $db->query("ALTER TABLE {items} ADD COLUMN `slug` varchar(255) DEFAULT NULL"); // This is imperfect since some of the slugs may contain invalid characters, but it'll do // for now because we don't want a lengthy operation here. $db->query("UPDATE {items} SET `slug` = `name`"); // Flush all path caches becuase we're going to start urlencoding them. $db->query("UPDATE {items} SET `relative_url_cache` = NULL, `relative_path_cache` = NULL"); module::set_version("gallery", $version = 12); } if ($version == 12) { if (module::get_var("gallery", "active_site_theme") == "default") { module::set_var("gallery", "active_site_theme", "wind"); } if (module::get_var("gallery", "active_admin_theme") == "admin_default") { module::set_var("gallery", "active_admin_theme", "admin_wind"); } module::set_version("gallery", $version = 13); } if ($version == 13) { // Add rules for generating our thumbnails and resizes Database::instance()->query("UPDATE {graphics_rules} SET `operation` = CONCAT('gallery_graphics::', `operation`);"); module::set_version("gallery", $version = 14); } if ($version == 14) { $sidebar_blocks = block_manager::get_active("site_sidebar"); if (empty($sidebar_blocks)) { $available_blocks = block_manager::get_available_site_blocks(); foreach (array_keys(block_manager::get_available_site_blocks()) as $id) { $sidebar_blocks[] = explode(":", $id); } block_manager::set_active("site_sidebar", $sidebar_blocks); } module::set_version("gallery", $version = 15); } if ($version == 15) { module::set_var("gallery", "identity_provider", "user"); module::set_version("gallery", $version = 16); } // Convert block keys to an md5 hash of the module and block name if ($version == 16) { foreach (array("dashboard_sidebar", "dashboard_center", "site_sidebar") as $location) { $blocks = block_manager::get_active($location); $new_blocks = array(); foreach ($blocks as $block) { $new_blocks[md5("{$block[0]}:{$block[1]}")] = $block; } block_manager::set_active($location, $new_blocks); } module::set_version("gallery", $version = 17); } // We didn't like md5 hashes so convert block keys back to random keys to allow duplicates. if ($version == 17) { foreach (array("dashboard_sidebar", "dashboard_center", "site_sidebar") as $location) { $blocks = block_manager::get_active($location); $new_blocks = array(); foreach ($blocks as $block) { $new_blocks[rand()] = $block; } block_manager::set_active($location, $new_blocks); } module::set_version("gallery", $version = 18); } // Rename blocks_site.sidebar to blocks_site_sidebar if ($version == 18) { $blocks = block_manager::get_active("site.sidebar"); block_manager::set_active("site_sidebar", $blocks); module::clear_var("gallery", "blocks_site.sidebar"); module::set_version("gallery", $version = 19); } // Set a default for the number of simultaneous uploads // Version 20 was reverted in 57adefc5baa7a2b0dfcd3e736e80c2fa86d3bfa2, so skip it. if ($version == 19 || $version == 20) { module::set_var("gallery", "simultaneous_upload_limit", 5); module::set_version("gallery", $version = 21); } // Update the graphics rules table so that the maximum height for resizes is 640 not 480. // Fixes ticket #671 if ($version == 21) { $resize_rule = ORM::factory("graphics_rule")->where("id", "=", "2")->find(); // make sure it hasn't been changed already $args = unserialize($resize_rule->args); if ($args["height"] == 480 && $args["width"] == 640) { $args["height"] = 640; $resize_rule->args = serialize($args); $resize_rule->save(); } module::set_version("gallery", $version = 22); } // Update slug values to be legal. We should have done this in the 11->12 upgrader, but I was // lazy. Mea culpa! if ($version == 22) { foreach (db::build()->from("items")->select("id", "slug")->where(new Database_Expression("`slug` REGEXP '[^_A-Za-z0-9-]'"), "=", 1)->execute() as $row) { $new_slug = item::convert_filename_to_slug($row->slug); if (empty($new_slug)) { $new_slug = rand(); } db::build()->update("items")->set("slug", $new_slug)->set("relative_url_cache", null)->where("id", "=", $row->id)->execute(); } module::set_version("gallery", $version = 23); } if ($version == 23) { $db->query("CREATE TABLE {failed_logins} (\n `id` int(9) NOT NULL auto_increment,\n `count` int(9) NOT NULL,\n `name` varchar(255) NOT NULL,\n `time` int(9) NOT NULL,\n PRIMARY KEY (`id`))\n DEFAULT CHARSET=utf8;"); module::set_version("gallery", $version = 24); } if ($version == 24) { foreach (array("logs", "tmp", "uploads") as $dir) { self::_protect_directory(VARPATH . $dir); } module::set_version("gallery", $version = 25); } if ($version == 25) { db::build()->update("items")->set("title", new Database_Expression("`name`"))->and_open()->where("title", "IS", null)->or_where("title", "=", "")->close()->execute(); module::set_version("gallery", $version = 26); } if ($version == 26) { $db->query("RENAME TABLE {failed_logins} TO {failed_auths}"); module::set_version("gallery", $version = 27); } if ($version == 27) { // Set the admin area timeout to 90 minutes module::set_var("gallery", "admin_area_timeout", 90 * 60); module::set_version("gallery", $version = 28); } if ($version == 28) { module::set_var("gallery", "credits", "Powered by <a href=\"%url\">%gallery_version</a>"); module::set_version("gallery", $version = 29); } if ($version == 29) { $db->query("ALTER TABLE {caches} ADD KEY (`key`);"); module::set_version("gallery", $version = 30); } }
public function convert_filename_to_slug_test() { $this->assert_equal("foo", item::convert_filename_to_slug("{[foo]}")); $this->assert_equal("foo-bar", item::convert_filename_to_slug("{[foo!@#!\$@#^\$@(\$!(@bar]}")); }
private function _add_item(&$input, &$reply) { $album = trim($input->post('set_albumName')); $userfilename = $this->decode($input->post('userfile_name')); $title = $this->decode($input->post('caption')); $forcefilename = $this->decode($input->post('force_filename')); $autorotate = trim($input->post('auto_rotate')); if ($album == '0') { $parent = item::root(); } else { $parent = ORM::factory("item")->where("id", "=", $album)->find(); } if (isset($parent) && $parent->loaded() && $parent->id != '') { if (function_exists('mime_content_type')) { $type = mime_content_type($_FILES['userfile']['tmp_name']); } else { $type = self::get_mime_type($_FILES['userfile']['name']); } /* <any ugly idea is welcome here> */ if ($type == '') { if (function_exists('getimagesize')) { $size = getimagesize($_FILES['userfile']['tmp_name']); $type = $size['mime']; } else { if (function_exists('exif_imagetype') && function_exists('image_type_to_mime_type')) { $type = image_type_to_mime_type(exif_imagetype($_FILES['userfile']['tmp_name'])); } } } /* </any ugly idea is welcome here> */ if ($type != '' && !in_array($type, array('image/jpeg', 'image/gif', 'image/png'))) { $reply->set('status_text', t("'%path' is an unsupported image type '%type'", array('path' => $_FILES['userfile']['tmp_name'], 'type' => $type))); $reply->send(gallery_remote::UPLOAD_PHOTO_FAIL); return; } if ($forcefilename != '') { $filename = $forcefilename; } else { if ($userfilename != '') { $filename = $userfilename; } else { $filename = $_FILES['userfile']['name']; } } $slug = $filename; $pos = strpos($slug, '.'); if ($pos !== false) { $slug = substr($slug, 0, $pos); } //*/ fix for a gallery remote bug... $filename = str_replace('.JPG.jpeg', '.jpeg', $filename); //*/ //*/ suddenly gallery fails because the uploaded(!) file (of cause!) doesn't contain a file extension if (strpos($_FILES['userfile']['tmp_name'], '.') === false) { $moveto = $_FILES['userfile']['tmp_name'] . '.' . substr($type, strpos($type, '/') + 1); rename($_FILES['userfile']['tmp_name'], $moveto); $_FILES['userfile']['tmp_name'] = $moveto; } //*/ try { $item = ORM::factory('item'); $item->type = 'photo'; $item->parent_id = $parent->id; $item->set_data_file($_FILES['userfile']['tmp_name']); $item->name = $filename; $item->slug = item::convert_filename_to_slug($slug); $item->mime_type = $type; $item->title = $title; $item->title or $item->title = ' '; //don't use $item->name as this clutters up the UI //$item->description = $item->view_count = 0; try { $item->validate(); try { $item->save(); $reply->set('item_name', $item->id); $reply->set('status_text', 'New item created successfuly.'); $reply->send(); } catch (ORM_Validation_Exception $e) { $validation = $e->validation; //print_r($validation->errors()); exit; $reply->set('status_text', t('Failed to validate item %item: %errors', array('item' => $filename, 'errors' => str_replace("\n", ' ', print_r($validation->errors(), true))))); $reply->send(gallery_remote::UPLOAD_PHOTO_FAIL); //FIXME gallery remote ignores this return value and continues to wait } catch (Exception $e) { $reply->set('status_text', t('Failed to add item %item.', array('item' => $filename))); $reply->send(gallery_remote::UPLOAD_PHOTO_FAIL); //FIXME gallery remote ignores this return value and continues to wait } } catch (ORM_Validation_Exception $e) { $validation = $e->validation; //print_r($validation->errors()); exit; $reply->set('status_text', t('Failed to validate item %item: %errors', array('item' => $filename, 'errors' => str_replace("\n", ' ', print_r($validation->errors(), true))))); $reply->send(gallery_remote::UPLOAD_PHOTO_FAIL); //FIXME gallery remote ignores this return value and continues to wait } } catch (Exception $e) { $reply->set('status_text', t("Corrupt image '%path'", array('path' => $_FILES['userfile']['tmp_name']))); $reply->send(gallery_remote::UPLOAD_PHOTO_FAIL); //FIXME gallery remote ignores this return value and continues to wait } } else { $reply->set('status_text', t('Failed to load album with name %name.', array('name' => $album))); $reply->send(gallery_remote::UPLOAD_PHOTO_FAIL); //FIXME gallery remote ignores this return value and continues to wait } }
/** * Import a single photo or movie. */ static function import_item(&$queue) { $messages = array(); if (count($queue) == 0) { $messages[] = t('Empty item queue'); return $messages; } $item_id = array_shift($queue); g1_import::debug(t('Now importing item %item', array('item' => $item_id))); // Item names come in as FolderX/ItemX $pos = strrpos($item_id, '/'); if ($pos === false) { return $messages; } // Get ItemX into g1_item $g1_item = substr($item_id, $pos + 1); // Get FolderX into g1_item $g1_album = substr($item_id, 0, $pos); if (self::map($g1_album, $g1_item, 'item')) { return $messages; } $album_id = self::map($g1_album, '', 'album'); if (!$album_id) { $messages[] = t('Album %name not found', array('name' => $g1_album)); return $messages; } $album_item = null; $albumDir = self::$album_dir; if (substr($albumDir, -1) != DIRECTORY_SEPARATOR) { $albumDir .= DIRECTORY_SEPARATOR; } require_once 'Gallery1DataParser.php'; list($result, $items) = Gallery1DataParser::getPhotos($albumDir . $g1_album . DIRECTORY_SEPARATOR); if ($result == null) { foreach ($items as $object) { if (isset($object->image) && is_a($object->image, 'G1Img') && isset($object->image->name) && $object->image->name == $g1_item) { $album_item = $object; } } } if ($album_item == null) { $messages[] = t('Failed to import Gallery 1 item: %item', array('item' => $item_id)); return $messages; } $corrupt = 0; self::$current_g1_item = array($item_id => $album_item); $g1_path = $albumDir . $g1_album . DIRECTORY_SEPARATOR . $album_item->image->name . '.' . $album_item->image->type; $parent = ORM::factory('item', $album_id); switch ($album_item->image->type) { case 'jpg': case 'jpeg': case 'gif': case 'png': $g1_type = 'GalleryPhotoItem'; break; case 'wmv': case '3gp': case 'avi': case 'mp4': case 'mov': case 'flv': $g1_type = 'GalleryMovieItem'; break; default: $g1_type = 'GalleryPhotoItem'; break; } if (!file_exists($g1_path)) { // If the Gallery 1 source image isn't available, this operation is going to fail. That can // happen in cases where there's corruption in the source Gallery 1. In that case, fall // back on using a broken image. It's important that we import *something* otherwise // anything that refers to this item in Gallery 1 will have a dangling pointer in Gallery 3 // // Note that this will change movies to be photos, if there's a broken movie. Hopefully // this case is rare enough that we don't need to take any heroic action here. g1_import::log(t('%path missing in import; replacing it with a placeholder', array('path' => $g1_path))); $g1_path = MODPATH . 'g1_import/data/broken-image.gif'; $g1_type = 'GalleryPhotoItem'; $corrupt = 1; } $item = null; switch ($g1_type) { case 'GalleryPhotoItem': if (function_exists('mime_content_type')) { $type = mime_content_type($g1_path); } else { $type = self::get_mime_type($g1_path); } if ($type != '' && !in_array($type, array('image/jpeg', 'image/gif', 'image/png'))) { Kohana_Log::add('alert', "{$g1_path} is an unsupported image type {$type}; using a placeholder gif"); $messages[] = t("'%path' is an unsupported image type '%type', using a placeholder", array('path' => $g1_path, 'type' => $type)); $g1_path = MODPATH . 'g1_import/data/broken-image.gif'; $corrupt = 1; } try { $item = ORM::factory('item'); $item->type = 'photo'; $item->parent_id = $album_id; $item->set_data_file($g1_path); $item->name = $g1_item . '.' . $album_item->image->type; $item->slug = item::convert_filename_to_slug($g1_item); $item->mime_type = $type; $item->title = utf8_encode(self::_decode_html_special_chars(trim($album_item->caption))); $item->title or $item->title = ' '; //don't use $item->name as this clutters up the UI if (isset($album_item->description) && $album_item->description != '') { $item->description = utf8_encode(self::_decode_html_special_chars(trim($album_item->description))); } //$item->owner_id = self::map($g1_item->getOwnerId()); try { $item->view_count = (int) $album_item->clicks; } catch (Exception $e) { $item->view_count = 1; } if (strlen($item->title) > 255) { if (strlen($item->description) == 0) { $item->description = $item->title; } $item->title = substr($item->title, 0, 252) . '...'; } } catch (Exception $e) { $exception_info = (string) new G1_Import_Exception(t("Corrupt image '%path'", array('path' => $g1_path)), $e, $messages); Kohana_Log::add('alert', "Corrupt image {$g1_path}\n" . $exception_info); $messages[] = $exception_info; $corrupt = 1; $item = null; return $messages; } try { $item->validate(); } catch (ORM_Validation_Exception $e) { $exception_info = (string) new G1_Import_Exception(t('Failed to validate Gallery 1 item %item.', array('item' => $item_id)), $e, $messages); Kohana_Log::add('alert', "Failed to validate Gallery 1 item {$item_id}.\n" . $exception_info); $messages[] = $exception_info; $corrupt = 1; $item = null; return $messages; } try { $item->save(); } catch (Exception $e) { $exception_info = (string) new G1_Import_Exception(t('Failed to import Gallery 1 item %item.', array('item' => $item_id)), $e, $messages); Kohana_Log::add('alert', "Failed to import Gallery 1 item {$item_id}.\n" . $exception_info); $messages[] = $exception_info; $corrupt = 1; $item = null; } break; case 'GalleryMovieItem': // @todo we should transcode other types into FLV if (function_exists('mime_content_type')) { $type = mime_content_type($g1_path); } else { $type = self::get_mime_type($g1_path); } if ($type == '' || in_array($type, array('video/mp4', 'video/x-flv'))) { try { $item = ORM::factory('item'); $item->type = 'movie'; $item->parent_id = $album_id; $item->set_data_file($g1_path); $item->name = $g1_item . '.' . $album_item->image->type; $item->slug = item::convert_filename_to_slug($g1_item); $item->mime_type = $type; $item->title = utf8_encode(self::_decode_html_special_chars(trim($album_item->caption))); $item->title or $item->title = ' '; //$item->name; if (isset($album_item->description) && $album_item->description != '') { $item->description = utf8_encode(self::_decode_html_special_chars(trim($album_item->description))); } //$item->owner_id = self::map($g1_item->getOwnerId()); try { $item->view_count = (int) $album_item->clicks; } catch (Exception $e) { $item->view_count = 1; } } catch (Exception $e) { $exception_info = (string) new G1_Import_Exception(t("Corrupt movie '%path'", array("path" => $g1_path)), $e, $messages); Kohana_Log::add('alert', "Corrupt movie {$g1_path}\n" . $exception_info); $messages[] = $exception_info; $corrupt = 1; $item = null; return $messages; } try { $item->validate(); } catch (ORM_Validation_Exception $e) { $exception_info = (string) new G1_Import_Exception(t('Failed to validate Gallery 1 item %item.', array('item' => $item_id)), $e, $messages); Kohana_Log::add('alert', "Failed to validate Gallery 1 item {$item_id}.\n" . $exception_info); $messages[] = $exception_info; $corrupt = 1; $item = null; return $messages; } try { $item->save(); } catch (Exception $e) { $exception_info = (string) new G1_Import_Exception(t('Failed to import Gallery 1 item %item.', array('item' => $item_id)), $e, $messages); Kohana_Log::add('alert', "Failed to import Gallery 1 item {$item_id}.\n" . $exception_info); $messages[] = $exception_info; $corrupt = 1; $item = null; } } else { Kohana_Log::add('alert', "{$g1_path} is an unsupported movie type {$type}"); $messages[] = t("'%path' is an unsupported movie type '%type'", array('path' => $g1_path, 'type' => $type)); $corrupt = 1; } break; default: // Ignore break; } if (isset($item)) { self::set_map($item->id, $g1_album, $g1_item, 'item'); if (isset($album_item->keywords) && $album_item->keywords != '') { $keywords = utf8_encode(self::_decode_html_special_chars(trim($album_item->keywords))); if ($keywords != '') { self::import_keywords_as_tags($keywords, $item); } } } if ($corrupt) { $title = utf8_encode(self::_decode_html_special_chars(trim($album_item->caption))); $title or $title = $g1_item; if (!empty($item)) { $messages[] = t('<a href="%g1_url">%title</a> from Gallery 1 could not be processed; (imported as <a href="%g3_url">%title</a>)', array('g1_url' => $gallery_url . '/' . $item_id, 'g3_url' => $item->url(), 'title' => $title)); } else { $messages[] = t('<a href="%g1_url">%title</a> from Gallery 1 could not be processed', array('g1_url' => $gallery_url . '/' . $item_id, 'title' => $title)); } } self::$current_g1_item = null; return $messages; }