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");
}
}