"required|length[0,255]", "title" => "required|length[0,255]", "description" => "length[0,65535]" ); /** * Add a set of restrictions to any following queries to restrict access only to items * viewable by the active user. * @chainable */ public function viewable() { if (is_null($this->view_restrictions)) { if (user::active()->admin) { $this->view_restrictions = array(); } else { foreach (user::group_ids() as $id) { // Separate the first restriction from the rest to make it easier for us to formulate // our where clause below if (empty($this->view_restrictions)) { $this->view_restrictions[0] = "view_$id"; } else { $this->view_restrictions[1]["view_$id"] = access::ALLOW; } } } } switch (count($this->view_restrictions)) { case 0: break; case 1: $this->where($this->view_restrictions[0], access::ALLOW); break; default: $this->open_paren(); $this->where($this->view_restrictions[0], access::ALLOW); $this->orwhere($this->view_restrictions[1]); $this->close_paren(); break; } return $this; } /** * Is this item an album? * @return true if it's an album */ public function is_album() { return $this->type == 'album'; } /** * Is this item a photo? * @return true if it's a photo */ public function is_photo() { return $this->type == 'photo'; } /** * Is this item a movie? * @return true if it's a movie */ public function is_movie() { return $this->type == 'movie'; } public function delete() { module::event("item_before_delete", $this); $parent = $this->parent(); if ($parent->album_cover_item_id == $this->id) { item::remove_album_cover($parent); } $path = $this->file_path(); $resize_path = $this->resize_path(); $thumb_path = $this->thumb_path(); parent::delete(); if (is_dir($path)) { @dir::unlink($path); @dir::unlink(dirname($resize_path)); @dir::unlink(dirname($thumb_path)); } else { @unlink($path); @unlink($resize_path); @unlink($thumb_path); } } /** * Move this item to the specified target. * @chainable * @param Item_Model $target Target item (must be an album * @return ORM_MTPP */ function move_to($target) { if (!$target->is_album()) { throw new Exception("@todo INVALID_MOVE_TYPE $target->type"); } if ($this->id == 1) { throw new Exception("@todo INVALID_SOURCE root album"); } $original_path = $this->file_path(); $original_resize_path = $this->resize_path(); $original_thumb_path = $this->thumb_path(); parent::move_to($target, true); $this->relative_path_cache = null; rename($original_path, $this->file_path()); if ($this->is_album()) { @rename(dirname($original_resize_path), dirname($this->resize_path())); @rename(dirname($original_thumb_path), dirname($this->thumb_path())); Database::instance() ->update("items", array("relative_path_cache" => null), array("left >" => $this->left, "right <" => $this->right)); } else { @rename($original_resize_path, $this->resize_path()); @rename($original_thumb_path, $this->thumb_path()); } return $this; } /** * Rename the underlying file for this item to a new name. Move all the files. This requires a * save. * * @chainable */ public function rename($new_name) { if ($new_name == $this->name) { return; } if (strpos($new_name, "/")) { throw new Exception("@todo NAME_CANNOT_CONTAIN_SLASH"); } $old_relative_path = $this->relative_path(); $new_relative_path = dirname($old_relative_path) . "/" . $new_name; @rename(VARPATH . "albums/$old_relative_path", VARPATH . "albums/$new_relative_path"); @rename(VARPATH . "resizes/$old_relative_path", VARPATH . "resizes/$new_relative_path"); @rename(VARPATH . "thumbs/$old_relative_path", VARPATH . "thumbs/$new_relative_path"); $this->name = $new_name; if ($this->is_album()) { Database::instance() ->update("items", array("relative_path_cache" => null), array("left >" => $this->left, "right <" => $this->right)); } return $this; } /** * album: url::site("albums/2") * photo: url::site("photos/3") * * @param string $query the query string (eg "show=3") */ public function url($query=array(), $full_uri=false) { $url = ($full_uri ? url::abs_site("{$this->type}s/$this->id") : url::site("{$this->type}s/$this->id")); if ($query) { $url .= "?$query"; } return $url; } /** * album: /var/albums/album1/album2 * photo: /var/albums/album1/album2/photo.jpg */ public function file_path() { return VARPATH . "albums/" . $this->relative_path(); } /** * album: http://example.com/gallery3/var/resizes/album1/ * photo: http://example.com/gallery3/var/albums/album1/photo.jpg */ public function file_url($full_uri=false) { return $full_uri ? url::abs_file("var/albums/" . $this->relative_path()) : url::file("var/albums/" . $this->relative_path()); } /** * album: /var/resizes/album1/.thumb.jpg * photo: /var/albums/album1/photo.thumb.jpg */ public function thumb_path() { $base = VARPATH . "thumbs/" . $this->relative_path(); if ($this->is_photo()) { return $base; } else if ($this->is_album()) { return $base . "/.album.jpg"; } else if ($this->is_movie()) { // Replace the extension with jpg return preg_replace("/...$/", "jpg", $base); } } /** * Return true if there is a thumbnail for this item. */ public function has_thumb() { return $this->thumb_width && $this->thumb_height; } /** * album: http://example.com/gallery3/var/resizes/album1/.thumb.jpg * photo: http://example.com/gallery3/var/albums/album1/photo.thumb.jpg */ public function thumb_url($full_uri=false) { $base = ($full_uri ? url::abs_file("var/thumbs/" . $this->relative_path()) : url::file("var/thumbs/" . $this->relative_path())); if ($this->is_photo()) { return $base; } else if ($this->is_album()) { return $base . "/.album.jpg"; } else if ($this->is_movie()) { // Replace the extension with jpg return preg_replace("/...$/", "jpg", $base); } } /** * album: /var/resizes/album1/.resize.jpg * photo: /var/albums/album1/photo.resize.jpg */ public function resize_path() { return VARPATH . "resizes/" . $this->relative_path() . ($this->is_album() ? "/.album.jpg" : ""); } /** * album: http://example.com/gallery3/var/resizes/album1/.resize.jpg * photo: http://example.com/gallery3/var/albums/album1/photo.resize.jpg */ public function resize_url($full_uri=false) { return ($full_uri ? url::abs_file("var/resizes/" . $this->relative_path()) : url::file("var/resizes/" . $this->relative_path())) . ($this->is_album() ? "/.album.jpg" : ""); } /** * Return the relative path to this item's file. * @return string */ public function relative_path() { if (!isset($this->relative_path_cache)) { $paths = array(); foreach (Database::instance() ->select("name") ->from("items") ->where("left <=", $this->left) ->where("right >=", $this->right) ->where("id <>", 1) ->orderby("left", "ASC") ->get() as $row) { $paths[] = $row->name; } $this->relative_path_cache = implode($paths, "/"); $this->save(); } return $this->relative_path_cache; } /** * @see ORM::__get() */ public function __get($column) { if ($column == "owner") { // This relationship depends on an outside module, which may not be present so handle // failures gracefully. try { return model_cache::get("user", $this->owner_id); } catch (Exception $e) { return null; } } else { return parent::__get($column); } } /** * @see ORM::__set() */ public function __set($column, $value) { if ($column == "name") { // Clear the relative path as it is no longer valid. $this->relative_path_cache = null; } parent::__set($column, $value); } /** * @see ORM::save() */ public function save() { if (!empty($this->changed) && $this->changed != array("view_count" => "view_count")) { $this->updated = time(); if (!$this->loaded) { $this->created = $this->updated; $r = ORM::factory("item")->select("MAX(weight) as max_weight")->find(); $this->weight = $r->max_weight + 1; } } return parent::save(); } /** * Return the Item_Model representing the cover for this album. * @return Item_Model or null if there's no cover */ public function album_cover() { if (!$this->is_album()) { return null; } if (empty($this->album_cover_item_id)) { return null; } try { return model_cache::get("item", $this->album_cover_item_id); } catch (Exception $e) { // It's possible (unlikely) that the item was deleted, if so keep going. return null; } } /** * Find the position of the given child id in this album. The resulting value is 1-indexed, so * the first child in the album is at position 1. */ public function get_position($child_id) { $result = Database::instance()->query(" SELECT COUNT(*) AS position FROM {items} WHERE parent_id = {$this->parent_id} AND {$this->sort_column} <= (SELECT {$this->sort_column} FROM {items} WHERE id = $child_id) ORDER BY {$this->sort_column} {$this->sort_order}"); return $result->current()->position; } /** * Return an tag for the thumbnail. * @param array $extra_attrs Extra attributes to add to the img tag * @param int (optional) $max Maximum size of the thumbnail (default: null) * @param boolean (optional) $center_vertically Center vertically (default: false) * @return string */ public function thumb_tag($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 $margin_top = ($max - $height) / 20; $extra_attrs["style"] = "margin-top: {$margin_top}em"; $extra_attrs["title"] = $this->title; } $attrs = array_merge($extra_attrs, array( "src" => $this->thumb_url(), "alt" => $this->title, "width" => $width, "height" => $height) ); // html::image forces an absolute url which we don't want return ""; } /** * Calculate the largest width/height that fits inside the given maximum, while preserving the * aspect ratio. * @param int $max Maximum size of the largest dimension * @return array */ public function scale_dimensions($max) { $width = $this->thumb_width; $height = $this->thumb_height; if ($height) { if (isset($max)) { if ($width > $height) { $height = (int)($max * ($height / $width)); $width = $max; } else { $width = (int)($max * ($width / $height)); $height = $max; } } } else { // Missing thumbnail, can happen on albums with no photos yet. // @todo we should enforce a placeholder for those albums. $width = 0; $height = 0; } return array($height, $width); } /** * Return an tag for the resize. * @param array $extra_attrs Extra attributes to add to the img tag * @return string */ public function resize_tag($extra_attrs) { $attrs = array_merge($extra_attrs, array("src" => $this->resize_url(), "alt" => $this->title, "width" => $this->resize_width, "height" => $this->resize_height) ); // html::image forces an absolute url which we don't want return ""; } /** * Return a flowplayer "; } /** * Return all of the children of this node, ordered by the defined sort order. * * @chainable * @param integer SQL limit * @param integer SQL offset * @return array ORM */ function children($limit=null, $offset=0) { return parent::children($limit, $offset, array($this->sort_column => $this->sort_order)); } /** * Return all of the children of the specified type, ordered by the defined sort order. * @param integer SQL limit * @param integer SQL offset * @param string type to return * @return object ORM_Iterator */ function descendants($limit=null, $offset=0, $type=null) { return parent::descendants($limit, $offset, $type, array($this->sort_column => $this->sort_order)); } }