summaryrefslogtreecommitdiff
path: root/modules/gallery/models
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gallery/models')
-rw-r--r--modules/gallery/models/item.php214
1 files changed, 156 insertions, 58 deletions
diff --git a/modules/gallery/models/item.php b/modules/gallery/models/item.php
index 6851e1a3..46b0304e 100644
--- a/modules/gallery/models/item.php
+++ b/modules/gallery/models/item.php
@@ -21,11 +21,13 @@ class Item_Model extends ORM_MPTT {
protected $children = 'items';
protected $sorting = array();
- var $form_rules = array(
- "name" => "required|length[0,255]",
- "title" => "required|length[0,255]",
- "description" => "length[0,65535]",
- "slug" => "required|length[0,255]"
+ var $rules = array(
+ "name" => array("rules" => array("length[0,255]", "required")),
+ "title" => array("rules" => array("length[0,255]", "required")),
+ "slug" => array("rules" => array("length[0,255]", "required")),
+ "description" => array("rules" => array("length[0,65535]")),
+ "parent_id" => array("rules" => array("Item_Model::valid_parent")),
+ "type" => array("rules" => array("Item_Model::valid_type")),
);
/**
@@ -146,21 +148,12 @@ class Item_Model extends ORM_MPTT {
}
/**
- * Rename the underlying file for this item to a new name. Move all the files. This requires a
- * save.
+ * Rename the underlying file for this item to a new name and move all related files.
*
* @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 = urldecode($this->relative_path());
+ private function rename($new_name) {
+ $old_relative_path = urldecode($this->original()->relative_path());
$new_relative_path = dirname($old_relative_path) . "/" . $new_name;
if (file_exists(VARPATH . "albums/$new_relative_path")) {
throw new Exception("@todo INVALID_RENAME_FILE_EXISTS: $new_relative_path");
@@ -178,18 +171,6 @@ class Item_Model extends ORM_MPTT {
@rename(VARPATH . "thumbs/$old_relative_path", VARPATH . "thumbs/$new_relative_path");
}
- $this->name = $new_name;
-
- if ($this->is_album()) {
- 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();
- }
-
return $this;
}
@@ -376,30 +357,10 @@ class Item_Model extends ORM_MPTT {
}
/**
- * @see ORM::__set()
- */
- public function __set($column, $value) {
- if ($column == "name") {
- $this->relative_path_cache = null;
- } else if ($column == "slug") {
- if ($this->slug != $value) {
- // Clear the relative url cache for this item and all children
- $this->relative_url_cache = null;
- if ($this->is_album()) {
- db::build()
- ->update("items")
- ->set("relative_url_cache", null)
- ->where("left_ptr", ">", $this->left_ptr)
- ->where("right_ptr", "<", $this->right_ptr)
- ->execute();
- }
- }
- }
- parent::__set($column, $value);
- }
-
- /**
+ * Handle any business logic necessary to create an item.
* @see ORM::save()
+ *
+ * @return ORM Item_Model
*/
public function save() {
$significant_changes = $this->changed;
@@ -410,18 +371,83 @@ class Item_Model extends ORM_MPTT {
if (!empty($this->changed) && $significant_changes) {
$this->updated = time();
if (!$this->loaded()) {
+ // Create a new item. Use whatever fields are set, and specify defaults for the rest.
$this->created = $this->updated;
$this->weight = item::get_max_weight();
+ $this->rand_key = ((float)mt_rand()) / (float)mt_getrandmax();
+ $this->thumb_dirty = 1;
+ $this->resize_dirty = 1;
+ if (empty($this->sort_column)) {
+ $this->sort_column = "created";
+ }
+ if (empty($this->sort_order)) {
+ $this->sort_order = "ASC";
+ }
+ if (empty($this->owner_id)) {
+ $this->owner_id = identity::active_user()->id;
+ }
+ if (empty($this->slug)) {
+ $tmp = pathinfo($this->name, PATHINFO_FILENAME);
+ $tmp = preg_replace("/[^A-Za-z0-9-_]+/", "-", $tmp);
+ $this->slug = trim($tmp, "-");
+ }
+
+ // Randomize the name or slug if there's a conflict
+ // @todo Improve this. Random numbers are not user friendly
+ $base_name = $this->name;
+ $base_slug = $this->slug;
+ while (ORM::factory("item")
+ ->where("parent_id", "=", $this->parent_id)
+ ->and_open()
+ ->where("name", "=", $this->name)
+ ->or_where("slug", "=", $this->slug)
+ ->close()
+ ->find()->id) {
+ $rand = rand();
+ $this->name = "$base_name-$rand";
+ $this->slug = "$base_slug-$rand";
+ }
+
+ parent::save();
+
+ // Call this after we finish saving so that the paths are correct.
+ if ($this->is_album()) {
+ mkdir($this->file_path());
+ mkdir(dirname($this->thumb_path()));
+ mkdir(dirname($this->resize_path()));
+ }
+
+ module::event("item_created", $this);
} else {
- $send_event = 1;
+ // Update an existing item
+ if ($this->original()->name != $this->name) {
+ $this->rename($this->name);
+ $this->relative_path_cache = null;
+ }
+
+ if ($this->original()->slug != $this->slug) {
+ // Clear the relative url cache for this item and all children
+ $this->relative_url_cache = null;
+ }
+
+ // Changing the name or the slug ripples downwards
+ if ($this->is_album() &&
+ ($this->original()->name != $this->name ||
+ $this->original()->slug != $this->slug)) {
+ 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();
+ }
+ $original = clone $this->original();
+ parent::save();
+ module::event("item_updated", $original, $this);
}
}
- $original = clone $this->original();
- parent::save();
- if (isset($send_event)) {
- module::event("item_updated", $original, $this);
- }
return $this;
}
@@ -657,4 +683,76 @@ class Item_Model extends ORM_MPTT {
}
return parent::descendants($limit, $offset, $where, $order_by);
}
+
+ /**
+ * Add some custom per-instance rules.
+ */
+ public function validate($array=null) {
+ if (!$array) {
+ // The root item has different rules for the name and slug.
+ if ($this->id == 1) {
+ $this->rules["name"]["rules"][] = "length[0]";
+ $this->rules["slug"]["rules"][] = "length[0]";
+ }
+
+ // Names and slugs can't conflict
+ $this->rules["name"]["callbacks"][] = array($this, "valid_name");
+ $this->rules["slug"]["callbacks"][] = array($this, "valid_slug");
+ }
+
+ parent::validate($array);
+ }
+
+ /**
+ * Validate that the desired slug does not conflict.
+ */
+ public function valid_slug(Validation $v, $value) {
+ if (preg_match("/[^A-Za-z0-9-_]/", $value)) {
+ $v->add_error("slug", "not_url_safe");
+ } else if (db::build()
+ ->from("items")
+ ->where("parent_id", "=", $this->parent_id)
+ ->where("id", "<>", $this->id)
+ ->where("slug", "=", $value)
+ ->count_records()) {
+ $v->add_error("slug", "conflict");
+ }
+ }
+
+ /**
+ * Validate the item name. It can't conflict with other names, can't contain slashes or
+ * trailing periods.
+ */
+ public function valid_name(Validation $v, $value) {
+ if (strpos($value, "/") !== false) {
+ $v->add_error("name", "no_slashes");
+ } else if (rtrim($value, ".") !== $value) {
+ $v->add_error("name", "no_trailing_period");
+ } else if (db::build()
+ ->from("items")
+ ->where("parent_id", "=", $this->parent_id)
+ ->where("id", "<>", $this->id)
+ ->where("name", "=", $value)
+ ->count_records()) {
+ $v->add_error("name", "conflict");
+ }
+ }
+
+ /**
+ * Make sure that the type is valid.
+ */
+ static function valid_type($value) {
+ return in_array($value, array("album", "photo", "movie"));
+ }
+
+ /**
+ * Make sure that the parent id refers to an album.
+ */
+ static function valid_parent($value) {
+ return db::build()
+ ->from("items")
+ ->where("id", "=", $value)
+ ->where("type", "=", "album")
+ ->count_records() == 1;
+ }
}