parent(); if ($parent->album_cover_item_id == $source->id) { if ($parent->children_count() > 1) { foreach ($parent->children(2) as $child) { if ($child->id != $source->id) { $new_cover_item = $child; break; } } item::make_album_cover($new_cover_item); } else { item::remove_album_cover($parent); } } $source->parent_id = $target->id; // Moving may result in name or slug conflicts. If that happens, try up to 5 times to pick a // random name (or slug) to avoid the conflict. $orig_name = $source->name; $orig_name_filename = pathinfo($source->name, PATHINFO_FILENAME); $orig_name_extension = pathinfo($source->name, PATHINFO_EXTENSION); $orig_slug = $source->slug; for ($i = 0; $i < 5; $i++) { try { $source->save(); if ($orig_name != $source->name) { switch ($source->type) { case "album": message::info( t("Album %old_name renamed to %new_name to avoid a conflict", array("old_name" => $orig_name, "new_name" => $source->name))); break; case "photo": message::info( t("Photo %old_name renamed to %new_name to avoid a conflict", array("old_name" => $orig_name, "new_name" => $source->name))); break; case "movie": message::info( t("Movie %old_name renamed to %new_name to avoid a conflict", array("old_name" => $orig_name, "new_name" => $source->name))); break; } } break; } catch (ORM_Validation_Exception $e) { $rand = rand(10, 99); $errors = $e->validation->errors(); if (isset($errors["name"])) { $source->name = $orig_name_filename . "-{$rand}." . $orig_name_extension; unset($errors["name"]); } if (isset($errors["slug"])) { $source->slug = $orig_slug . "-{$rand}"; unset($errors["slug"]); } if ($errors) { // There were other validation issues-- we don't know how to handle those throw $e; } } } // If the target has no cover item, make this it. if ($target->album_cover_item_id == null) { item::make_album_cover($source); } } static function make_album_cover($item) { $parent = $item->parent(); access::required("view", $item); access::required("view", $parent); access::required("edit", $parent); model_cache::clear(); $parent->album_cover_item_id = $item->is_album() ? $item->album_cover_item_id : $item->id; if ($item->thumb_dirty) { $parent->thumb_dirty = 1; graphics::generate($parent); } else { copy($item->thumb_path(), $parent->thumb_path()); $parent->thumb_width = $item->thumb_width; $parent->thumb_height = $item->thumb_height; } $parent->save(); $grand_parent = $parent->parent(); if ($grand_parent && access::can("edit", $grand_parent) && $grand_parent->album_cover_item_id == null) { item::make_album_cover($parent); } } static function remove_album_cover($album) { access::required("view", $album); access::required("edit", $album); @unlink($album->thumb_path()); model_cache::clear(); $album->album_cover_item_id = null; $album->thumb_width = 0; $album->thumb_height = 0; $album->thumb_dirty = 1; $album->save(); graphics::generate($album); } /** * 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("/\..{3,4}$/", "", $title); $title = preg_replace("/ +/", " ", $title); return $title; } /** * Convert a filename into something we can use as a url component. * @param string $filename */ static function convert_filename_to_slug($filename) { $result = pathinfo($filename, PATHINFO_FILENAME); $result = preg_replace("/[^A-Za-z0-9-_]+/", "-", $result); return trim($result, "-"); } /** * Display delete confirmation message and form * @param object $item * @return string form */ static function get_delete_form($item) { $page_type = Input::instance()->get("page_type"); $from_id = Input::instance()->get("from_id"); $form = new Forge( "quick/delete/$item->id?page_type=$page_type&from_id=$from_id", "", "post", array("id" => "g-confirm-delete")); $group = $form->group("confirm_delete")->label(t("Confirm Deletion")); $group->submit("")->value(t("Delete")); $form->script("") ->url(url::abs_file("modules/gallery/js/item_form_delete.js")); return $form; } /** * Get the next weight value */ static function get_max_weight() { // Guard against an empty result when we create the first item. It's unfortunate that we // have to check this every time. // @todo: figure out a better way to bootstrap the weight. $result = db::build() ->select("weight")->from("items") ->order_by("weight", "desc")->limit(1) ->execute()->current(); return ($result ? $result->weight : 0) + 1; } /** * Add a set of restrictions to any following queries to restrict access only to items * viewable by the active user. * @chainable */ static function viewable($model) { $view_restrictions = array(); if (!identity::active_user()->admin) { foreach (identity::group_ids_for_active_user() as $id) { $view_restrictions[] = array("items.view_$id", "=", access::ALLOW); } } if (count($view_restrictions)) { $model->and_open()->merge_or_where($view_restrictions)->close(); } return $model; } /** * Find an item by its path. If there's no match, return an empty Item_Model. * NOTE: the caller is responsible for performing security checks on the resulting item. * @param string $path * @return object Item_Model */ static function find_by_path($path) { $path = trim($path, "/"); // The root path name is NULL not "", hence this workaround. if ($path == "") { return item::root(); } // Check to see if there's an item in the database with a matching relative_path_cache value. // Since that field is urlencoded, we must urlencoded the components of the path. foreach (explode("/", $path) as $part) { $encoded_array[] = rawurlencode($part); } $encoded_path = join("/", $encoded_array); $item = ORM::factory("item") ->where("relative_path_cache", "=", $encoded_path) ->find(); if ($item->loaded()) { return $item; } // Since the relative_path_cache field is a cache, it can be unavailable. If we don't find // anything, fall back to checking the path the hard way. $paths = explode("/", $path); foreach (ORM::factory("item") ->where("name", "=", end($paths)) ->where("level", "=", count($paths) + 1) ->find_all() as $item) { if (urldecode($item->relative_path()) == $path) { return $item; } } return new Item_Model(); } /** * Return the root Item_Model * @return Item_Model */ static function root() { return model_cache::get("item", 1); } /** * Return a query to get a random Item_Model, with optional filters. * Usage: item::random_query()->execute(); * * Note: You can add your own ->where() clauses but if your Gallery is * small or your where clauses are over-constrained you may wind up with * no item. You should try running this a few times in a loop if you * don't get an item back. */ static function random_query() { // Pick a random number and find the item that's got nearest smaller number. // This approach works best when the random numbers in the system are roughly evenly // distributed so this is going to be more efficient with larger data sets. return ORM::factory("item") ->viewable() ->where("rand_key", "<", random::percent()) ->order_by("rand_key", "DESC"); } }