From 0e3327bca70623175791ee41085d55d0cb13fe5b Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Sun, 3 Jan 2010 20:30:35 -0800 Subject: Simplify the REST API code. Here's what I did: 1) Simplify gallery_rest to return flat models, no children and do no validation for now. 2) Flatten the REST replies and use HTTP codes to indicate success/failure instead of additional status messages. 3) Use the message and error code support in the base Exception class, instead of brewing our own in Rest_Exception. 4) Get rid of rest::success() and rest::fail() -- we only need rest::reply() since all failures are covered by throwing an exception. 5) Get rid of /rest/access_key and just use /rest for authentication. 6) Inline and simplify rest::normalize_request since we only use it once 7) Change rest::set_active_user to succeed or throw an exception 8) Extract Rest_Exception::sendHeaders into rest::send_headers() Here's what's currently broken: 1) Data validation. There currently is none 2) Logging. That's gone too 3) image block and tag code is broken 4) Tests are broken 5) No movie support --- modules/gallery/helpers/gallery_rest.php | 263 +++++++------------------------ 1 file changed, 57 insertions(+), 206 deletions(-) (limited to 'modules/gallery/helpers/gallery_rest.php') diff --git a/modules/gallery/helpers/gallery_rest.php b/modules/gallery/helpers/gallery_rest.php index a87ebb4e..f1c8d825 100644 --- a/modules/gallery/helpers/gallery_rest.php +++ b/modules/gallery/helpers/gallery_rest.php @@ -17,232 +17,83 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class gallery_rest_Core { - static function get($request) { - $path = implode("/", $request->arguments); - $item = gallery_rest::_get_item($path); +// @todo Add logging +// @todo VALIDATION + +// Validation questions +// +// We need to be able to properly validate anything we want to enter here. But all of our +// validation currently happens at the controller / form level, and we're not using the same +// controllers or forms. +// +// Possible solutions: +// 1) Move validation into the model and use it both here and in the regular controllers. But +// if we do that, how do we translate validation failures into a user-consumable output which +// we need so that we can return proper error responses to form submissions? +// +// 2) Create some kind of validation helper that can validate every field. Wait, isn't this +// just like #1 except in a helper instead of in the model? - $parent = $item->parent(); - $response_data = array("type" => $item->type, - "name" => $item->name, - "path" => $item->relative_url(), - "parent_path" => empty($parent) ? null : $parent->relative_url(), - "title" => $item->title, - "thumb_url" => $item->thumb_url(true), - "thumb_size" => array("height" => $item->thumb_height, - "width" => $item->thumb_width), - "resize_url" => $item->resize_url(true), - "resize_size" => array("height" => (int)$item->resize_height, - "width" => (int)$item->resize_width), - "url" => $item->file_url(true), - "size" => array("height" => $item->height, - "width" => $item->width), - "description" => $item->description, - "slug" => $item->slug); +class gallery_rest_Core { + static function get($request) { + $item = url::get_item_from_uri($request->path); + access::required("view", $item); - $children = self::_get_children($item, $request); - if (!empty($children) || $item->is_album()) { - $response_data["children"] = $children; - } - return rest::success(array("resource" => $response_data)); + return json_encode($item->as_array()); } static function put($request) { - if (empty($request->arguments)) { - throw new Rest_Exception(400, "Bad request"); - } - $path = implode("/", $request->arguments); - $item = gallery_rest::_get_item($path, "edit"); - - // Validate the request data - $new_values = gallery_rest::_validate($request, $item->parent_id, $item->id); - $errors = $new_values->errors(); - if (empty($errors)) { - $item->title = $new_values->title; - $item->description = $new_values->description; - if ($item->id != 1) { - $item->rename($new_values->name); + $item = url::get_item_from_uri($request->path); + access::required("edit", $item); + + $params = $request->params; + foreach (array("captured", "description", "slug", "sort_column", "sort_order", + "title", "view_count", "weight") as $key) { + if (isset($params->$key)) { + $item->$key = $params->$key; } - $item->slug = $new_values->slug; - $item->save(); - - log::success("content", "Updated $item->type", - "type}s/$item->id\">view"); - - return rest::success(); - } else { - return rest::validation_error($errors); } + $item->save(); + + return rest::reply(array("url" => url::abs_site("/rest/gallery/" . $item->relative_url()))); } static function post($request) { - if (empty($request->arguments)) { - throw new Rest_Exception(400, "Bad request"); - } + $parent = url::get_item_from_uri($request->path); + access::required("edit", $parent); - $components = $request->arguments; - $name = urldecode(array_pop($components)); - - $parent = gallery_rest::_get_item(implode("/", $components), "edit"); - - // Validate the request data - $request->name = $name; - $new_values = gallery_rest::_validate($request, $parent->id); - $errors = $new_values->errors(); - if (!empty($errors)) { - return rest::validation_error($errors); - } + $params = $request->params; + switch ($params->type) { + case "album": + $item = album::create( + $parent, + $params->name, + isset($params->title) ? $params->title : $name, + isset($params->description) ? $params->description : null); + break; - if (empty($new_values["image"])) { - $new_item = album::create( + case "photo": + $item = photo::create( $parent, - $name, - empty($new_values["title"]) ? $name : $new_values["title"], - empty($new_values["description"]) ? null : $new_values["description"], - identity::active_user()->id, - empty($new_values["slug"]) ? $name : $new_values["slug"]); - $log_message = t("Added an album"); - } else { - $temp_filename = upload::save("image"); - $path_info = @pathinfo($temp_filename); - if (array_key_exists("extension", $path_info) && - in_array(strtolower($path_info["extension"]), array("flv", "mp4"))) { - $new_item = - movie::create($parent, $temp_filename, $new_values["name"], $new_values["title"]); - $log_message = t("Added a movie"); - } else { - $new_item = - photo::create($parent, $temp_filename, $new_values["name"], $new_values["title"]); - $log_message = t("Added a photo"); - } - } + $request->file, + $params->name, + isset($params->title) ? $params->title : $name, + isset($params->description) ? $params->description : null); + break; - log::success("content", $log_message, "type}s/$new_item->id\">view"); + default: + throw new Rest_Exception("Invalid type: $args->type", 400); + } - return rest::success(array("path" => $new_item->relative_url())); + return rest::reply(array("url" => url::abs_site("/rest/gallery/" . $item->relative_url()))); } static function delete($request) { - if (empty($request->arguments)) { - throw new Rest_Exception(400, "Bad request"); - } - $path = implode("/", $request->arguments); - - $item = gallery_rest::_get_item($path, "edit"); - - if ($item->id == 1) { - throw new Rest_Exception(400, "Bad request"); - } + $item = url::get_item_from_uri($request->path); + access::required("edit", $item); - $parent = $item->parent(); $item->delete(); - - if ($item->is_album()) { - $msg = t("Deleted album %title", array("title" => html::purify($item->title))); - } else { - $msg = t("Deleted photo %title", array("title" => html::purify($item->title))); - } - log::success("content", $msg); - - return rest::success(array("resource" => array("parent_path" => $parent->relative_url()))); - } - - private static function _get_item($path, $permission="view") { - $item = url::get_item_from_uri($path); - - if (!$item->loaded()) { - throw new Kohana_404_Exception(); - } - - if (!access::can($permission, $item)) { - throw new Kohana_404_Exception(); - } - - return $item; - } - - private static function _get_children($item, $request) { - $children = array(); - $limit = empty($request->limit) ? null : $request->limit; - $offset = empty($request->offset) ? null : $request->offset; - $where = empty($request->filter) ? array() : array("type" => $request->filter); - foreach ($item->viewable()->children($limit, $offset, $where) as $child) { - $children[] = array("type" => $child->type, - "has_children" => $child->children_count() > 0, - "path" => $child->relative_url(), - "thumb_url" => $child->thumb_url(true), - "thumb_dimensions" => array("width" => $child->thumb_width, - "height" => $child->thumb_height), - "has_thumb" => $child->has_thumb(), - "title" => $child->title); - } - - return $children; - } - - private static function _validate($request, $parent_id, $item_id=0) { - $item = ORM::factory("item", $item_id); - - // Normalize the inputs so all fields have a value - $new_values = Validation::factory(array()); - foreach ($item->form_rules as $field => $rule_set) { - if (isset($request->$field)) { - $new_values[$field] = $request->$field; - } else if (isset($item->$field)) { - $new_values[$field] = $item->$field; - } - foreach (explode("|", $rule_set) as $rule) { - $new_values->add_rules($field, $rule); - } - } - $name = $new_values["name"]; - $new_values["title"] = empty($new_values["title"]) ? $name : $new_values["title"]; - $new_values["description"] = - empty($new_values["description"]) ? null : $new_values["description"]; - $new_values["slug"] = empty($new_values["slug"]) ? $name : $new_values["slug"]; - - if (!empty($request->image)) { - $new_values["image"] = $request->image; - $new_values->add_rules( - "image", "upload::valid", "upload::required", "upload::type[gif,jpg,jpeg,png,flv,mp4]"); - } - - if ($new_values->validate() && $item_id != 1) { - $errors = gallery_rest::_check_for_conflicts($parent_id, $item_id, - $new_values["name"], $new_values["slug"]); - if (!empty($errors)) { - !empty($errors["name_conflict"]) OR $new_values->add_error("name", "Duplicate name"); - !empty($errors["slug_conflict"]) OR - $new_values->add_error("slug", "Duplicate Internet address"); - } - } - - return $new_values; - } - - private static function _check_for_conflicts($parent_id, $item_id, $new_name, $new_slug) { - $errors = array(); - - if ($row = db::build() - ->select(array("name", "slug")) - ->from("items") - ->where("parent_id", "=", $parent_id) - ->where("id", "<>", $item_id) - ->and_open() - ->where("name", "=", $new_name) - ->or_where("slug", "=", $new_slug) - ->close() - ->execute() - ->current()) { - if ($row->name == $new_name) { - $errors["name_conflict"] = 1; - } - if ($row->slug == $new_slug) { - $errors["slug_conflict"] = 1; - } - } - - return $errors; + return rest::reply(); } } -- cgit v1.2.3 From 3fffa18e650189e7f846592c9d4c3e7bbfe71c62 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Mon, 4 Jan 2010 21:48:21 -0800 Subject: Further progress on refining the REST server side code. 1) Deal in fully qualified URL resources through the rest interface. All rest methods are now passed the complete url in request->url. 2) Create rest::resolve() which lets individual resource definition code convert a full url into the appropriate matching resource. Implement gallery_rest::resolve() and tag_rest::resolve() 3) Reimplement tag_rest's get() and post() methods. They're much simpler now. 4) Implement the tags_rest helper which supports working with the entire tags collection. --- modules/gallery/helpers/gallery_rest.php | 14 ++++--- modules/rest/controllers/rest.php | 2 +- modules/rest/helpers/rest.php | 20 +++++++++ modules/tag/helpers/tag.php | 2 +- modules/tag/helpers/tag_rest.php | 69 ++++++++------------------------ modules/tag/helpers/tags_rest.php | 48 ++++++++++++++++++++++ 6 files changed, 96 insertions(+), 59 deletions(-) create mode 100644 modules/tag/helpers/tags_rest.php (limited to 'modules/gallery/helpers/gallery_rest.php') diff --git a/modules/gallery/helpers/gallery_rest.php b/modules/gallery/helpers/gallery_rest.php index f1c8d825..858721d0 100644 --- a/modules/gallery/helpers/gallery_rest.php +++ b/modules/gallery/helpers/gallery_rest.php @@ -37,14 +37,14 @@ class gallery_rest_Core { static function get($request) { - $item = url::get_item_from_uri($request->path); + $item = rest::resolve($request->url); access::required("view", $item); - return json_encode($item->as_array()); + return rest::reply($item->as_array()); } static function put($request) { - $item = url::get_item_from_uri($request->path); + $item = rest::resolve($request->url); access::required("edit", $item); $params = $request->params; @@ -60,7 +60,7 @@ class gallery_rest_Core { } static function post($request) { - $parent = url::get_item_from_uri($request->path); + $parent = rest::resolve($request->url); access::required("edit", $parent); $params = $request->params; @@ -90,10 +90,14 @@ class gallery_rest_Core { } static function delete($request) { - $item = url::get_item_from_uri($request->path); + $item = rest::resolve($request->url); access::required("edit", $item); $item->delete(); return rest::reply(); } + + static function resolve($path) { + return url::get_item_from_uri($path); + } } diff --git a/modules/rest/controllers/rest.php b/modules/rest/controllers/rest.php index 0332e5fc..5ef9eb84 100644 --- a/modules/rest/controllers/rest.php +++ b/modules/rest/controllers/rest.php @@ -60,7 +60,7 @@ class Rest_Controller extends Controller { $request->method = strtolower($input->server("HTTP_X_GALLERY_REQUEST_METHOD", $method)); $request->access_token = $input->server("HTTP_X_GALLERY_REQUEST_KEY"); - $request->path = implode("/", $args); + $request->url = url::abs_current(true); try { rest::set_active_user($request->access_token); diff --git a/modules/rest/helpers/rest.php b/modules/rest/helpers/rest.php index f7f3f9fd..b1b83e1b 100644 --- a/modules/rest/helpers/rest.php +++ b/modules/rest/helpers/rest.php @@ -51,4 +51,24 @@ class rest_Core { static function send_headers($exception) { header("HTTP/1.1 " . $exception->getCode() . " " . $exception->getMessage()); } + + /** + * Convert a REST url into an object. + * Eg: "http://example.com/gallery3/index.php/rest/gallery/Family/Wedding" -> Item_Model + * + * @param string the fully qualified REST url + * @return mixed the corresponding object (usually a model of some kind) + */ + static function resolve($url) { + $components = explode("/", substr($url, strlen(url::abs_site("rest"))), 3); + + // The first component will be empty because of the slash between "rest" and the + // resource type. + $class = "$components[1]_rest"; + if (!method_exists($class, "resolve")) { + throw new Kohana_404_Exception($url); + } + + return call_user_func(array($class, "resolve"), !empty($components[2]) ? $components[2] : null); + } } diff --git a/modules/tag/helpers/tag.php b/modules/tag/helpers/tag.php index 8075afe4..d895e08f 100644 --- a/modules/tag/helpers/tag.php +++ b/modules/tag/helpers/tag.php @@ -41,7 +41,7 @@ class tag_Core { } if (!$tag->has($item)) { - if (!$tag->add($item, $tag)) { + if (!$tag->add($item)) { throw new Exception("@todo {$tag->name} WAS_NOT_ADDED_TO {$item->id}"); } $tag->count++; diff --git a/modules/tag/helpers/tag_rest.php b/modules/tag/helpers/tag_rest.php index 0c06587b..4b5103ef 100644 --- a/modules/tag/helpers/tag_rest.php +++ b/modules/tag/helpers/tag_rest.php @@ -18,71 +18,36 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ class tag_rest_Core { - // If no arguments just return all the tags. If 2 or more then it is a path then - // return the tags for that item. But if its only 1, then is it a path or a tag? - // Assume a tag first, if nothing is found then try finding the item. static function get($request) { - $resources = array(); - switch (count($request->arguments)) { - case 0: - $tags = ORM::factory("tag") - ->select("name", "count") - ->order_by("count", "DESC"); - if (!empty($request->limit)) { - $tags->limit($request->limit); - } - if (!empty($request->offset)) { - $tags->offset($request->offset); - } - $resources = array("tags" => array()); - foreach ($tags->find_all() as $row) { - $resources["tags"][] = array("name" => $row->name, "count" => $row->count); - } - break; - case 1: - $resources = tag_rest::_get_items($request); - if (!empty($resources)) { - $resources = array("resources" => $resources); - break; - } - default: - $item = ORM::factory("item") - ->where("relative_url_cache", "=", implode("/", $request->arguments)) - ->viewable() - ->find(); - if ($item->loaded()) { - $resources = array("tags" => tag::item_tags($item)); - } - } - - return rest::reply($resources); + return rest::reply(rest::resolve($request->url)->as_array()); } static function post($request) { - if (empty($request->arguments) || count($request->arguments) != 1 || empty($request->path)) { + $tag = rest::resolve($request->url); + + if (empty($request->params->url)) { throw new Rest_Exception("Bad request", 400); } - $path = $request->path; - $tags = explode(",", $request->arguments[0]); - $item = ORM::factory("item") - ->where("relative_url_cache", "=", $path) - ->viewable() - ->find(); - if (!$item->loaded()) { - throw new Kohana_404_Exception(); - } + $item = rest::resolve($request->params->url); - if (!access::can("edit", $item)) { + access::required("edit", $item); + tag::add($item, $tag->name); + + return rest::reply(); + } + + static function resolve($tag_name) { + $tag = ORM::factory("tag")->where("name", "=", $tag_name)->find(); + if (!$tag->loaded()) { throw new Kohana_404_Exception(); } - foreach ($tags as $tag) { - tag::add($item, $tag); - } - return rest::reply(); + return $tag; } + // ------------------------------------------------------------ + static function put($request) { if (empty($request->arguments[0]) || empty($request->new_name)) { throw new Rest_Exception("Bad request", 400); diff --git a/modules/tag/helpers/tags_rest.php b/modules/tag/helpers/tags_rest.php new file mode 100644 index 00000000..d2bd28b0 --- /dev/null +++ b/modules/tag/helpers/tags_rest.php @@ -0,0 +1,48 @@ +find_all() as $tag) { + $data[$tag->name] = url::abs_site("rest/tags/" . rawurlencode($tag->name)); + } + return rest::reply($data); + } + + static function post($request) { + // @todo: what permission should be required to create a tag here? + // for now, require edit at the top level. Perhaps later, just require any edit perms, + // anywhere in the gallery? + access::required("edit", item::root()); + + if (empty($request->params->name)) { + throw new Rest_Exception("Bad Request", 400); + } + + $tag = ORM::factory("tag")->where("name", "=", $request->params->name)->find(); + if (!$tag->loaded()) { + $tag->name = $request->params->name; + $tag->count = 0; + $tag->save(); + } + + return rest::reply(array("url" => url::abs_site("rest/tag/" . rawurlencode($tag->name)))); + } +} -- cgit v1.2.3 From 31454d37b3ea02104925f1976609576c5f09c0c6 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Tue, 5 Jan 2010 13:41:06 -0800 Subject: Improve REST tag support. - Add support for retrieving a list of members from a collection - Implement put(), post() and delete() for tags. - Use tag_rest::delete() as a way to remove members from the tag collection --- modules/gallery/helpers/gallery_rest.php | 7 +- modules/tag/helpers/tag_rest.php | 118 ++++++++++++------------------- modules/tag/helpers/tags_rest.php | 6 +- 3 files changed, 54 insertions(+), 77 deletions(-) (limited to 'modules/gallery/helpers/gallery_rest.php') diff --git a/modules/gallery/helpers/gallery_rest.php b/modules/gallery/helpers/gallery_rest.php index 858721d0..c7b32d8c 100644 --- a/modules/gallery/helpers/gallery_rest.php +++ b/modules/gallery/helpers/gallery_rest.php @@ -40,7 +40,12 @@ class gallery_rest_Core { $item = rest::resolve($request->url); access::required("view", $item); - return rest::reply($item->as_array()); + $children = array(); + foreach ($item->children() as $child) { + $children[] = url::abs_site("rest/gallery/" . $child->relative_url()); + } + + return rest::reply(array("resource" => $item->as_array(), "members" => $children)); } static function put($request) { diff --git a/modules/tag/helpers/tag_rest.php b/modules/tag/helpers/tag_rest.php index 4b5103ef..c1bbf4fb 100644 --- a/modules/tag/helpers/tag_rest.php +++ b/modules/tag/helpers/tag_rest.php @@ -19,107 +19,79 @@ */ class tag_rest_Core { static function get($request) { - return rest::reply(rest::resolve($request->url)->as_array()); + $tag = rest::resolve($request->url); + $items = array(); + foreach ($tag->items() as $item) { + $items[] = url::abs_site("rest/gallery/" . $item->relative_url()); + } + + return rest::reply(array("resource" => $tag->as_array(), "members" => $items)); } static function post($request) { - $tag = rest::resolve($request->url); - if (empty($request->params->url)) { throw new Rest_Exception("Bad request", 400); } + $tag = rest::resolve($request->url); $item = rest::resolve($request->params->url); - access::required("edit", $item); - tag::add($item, $tag->name); - return rest::reply(); + tag::add($item, $tag->name); + return rest::reply(array("url" => url::abs_site("rest/tag/" . rawurlencode($tag->name)))); } - static function resolve($tag_name) { - $tag = ORM::factory("tag")->where("name", "=", $tag_name)->find(); - if (!$tag->loaded()) { - throw new Kohana_404_Exception(); - } + static function put($request) { + $tag = rest::resolve($request->url); - return $tag; - } + // @todo: what permission should be required to edit a tag? + // for now, require edit at the top level. Perhaps later, just require any edit perms, + // anywhere in the gallery? - // ------------------------------------------------------------ + if (isset($request->params->remove)) { + if (!is_array($request->params->remove)) { + throw new Exception("Bad request", 400); + } - static function put($request) { - if (empty($request->arguments[0]) || empty($request->new_name)) { - throw new Rest_Exception("Bad request", 400); + foreach ($request->params->remove as $item_url) { + $item = rest::resolve($item_url); + access::required("edit", $item); + $tag->remove($item); + } } - $name = $request->arguments[0]; - - $tag = ORM::factory("tag") - ->where("name", "=", $name) - ->find(); - if (!$tag->loaded()) { - throw new Kohana_404_Exception(); + if (isset($request->params->name)) { + $tag->name = $request->params->name; } - $tag->name = $request->new_name; $tag->save(); - - return rest::reply(); + return rest::reply(array("url" => url::abs_site("rest/tag/" . rawurlencode($tag->name)))); } static function delete($request) { - if (empty($request->arguments[0])) { - throw new Rest_Exception("Bad request", 400); - } - $tags = explode(",", $request->arguments[0]); - if (!empty($request->path)) { - $tag_list = ORM::factory("tag") - ->join("items_tags", "tags.id", "items_tags.tag_id") - ->join("items", "items.id", "items_tags.item_id") - ->where("tags.name", "IN", $tags) - ->where("relative_url_cache", "=", $request->path) - ->viewable() - ->find_all(); - } else { - $tag_list = ORM::factory("tag") - ->where("name", "IN", $tags) - ->find_all(); - } + $tag = rest::resolve($request->url); - foreach ($tag_list as $row) { - $row->delete(); - }; + if (empty($request->params->url)) { + // Delete the tag + $tag->delete(); + return rest::reply(); + } else { + // Remove an item from the tag + $item = rest::resolve($request->params->url); + $tag->remove($item); + $tag->save(); - tag::compact(); - return rest::reply(); + tag::compact(); + return rest::reply(array("url" => url::abs_site("rest/tag/" . rawurlencode($tag->name)))); + } } - private static function _get_items($request) { - $tags = explode(",", $request->arguments[0]); - $items = ORM::factory("item") - ->select_distinct("*") - ->join("items_tags", "items.id", "items_tags.item_id") - ->join("tags", "tags.id", "items_tags.tag_id") - ->where("tags.name", "IN", $tags); - if (!empty($request->limit)) { - $items->limit($request->limit); - } - if (!empty($request->offset)) { - $items->offset($request->offset); - } - $resources = array(); - foreach ($items->find_all() as $item) { - $resources[] = array("type" => $item->type, - "has_children" => $item->children_count() > 0, - "path" => $item->relative_url(), - "thumb_url" => $item->thumb_url(true), - "thumb_dimensions" => array("width" => $item->thumb_width, - "height" => $item->thumb_height), - "has_thumb" => $item->has_thumb(), - "title" => $item->title); + static function resolve($tag_name) { + $tag = ORM::factory("tag")->where("name", "=", $tag_name)->find(); + if (!$tag->loaded()) { + throw new Kohana_404_Exception(); } - return $resources; + return $tag; } } diff --git a/modules/tag/helpers/tags_rest.php b/modules/tag/helpers/tags_rest.php index d2bd28b0..3ef897fd 100644 --- a/modules/tag/helpers/tags_rest.php +++ b/modules/tag/helpers/tags_rest.php @@ -19,11 +19,11 @@ */ class tags_rest_Core { static function get($request) { - $data = array(); + $tags = array(); foreach (ORM::factory("tag")->find_all() as $tag) { - $data[$tag->name] = url::abs_site("rest/tags/" . rawurlencode($tag->name)); + $tags[$tag->name] = url::abs_site("rest/tags/" . rawurlencode($tag->name)); } - return rest::reply($data); + return rest::reply(array("members" => $tags)); } static function post($request) { -- cgit v1.2.3 From 14f6e5f6d3933b958fa61b83c627412282610dee Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Fri, 8 Jan 2010 11:12:02 -0800 Subject: Allow the "name" param in get() so that you can restrict the query to children with a given name. --- modules/gallery/helpers/gallery_rest.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'modules/gallery/helpers/gallery_rest.php') diff --git a/modules/gallery/helpers/gallery_rest.php b/modules/gallery/helpers/gallery_rest.php index c7b32d8c..fd18d59a 100644 --- a/modules/gallery/helpers/gallery_rest.php +++ b/modules/gallery/helpers/gallery_rest.php @@ -40,8 +40,14 @@ class gallery_rest_Core { $item = rest::resolve($request->url); access::required("view", $item); + if (isset($request->params->name)) { + $where[] = array("name", "=", $request->params->name); + } else { + $where = array(); + } + $children = array(); - foreach ($item->children() as $child) { + foreach ($item->children($where) as $child) { $children[] = url::abs_site("rest/gallery/" . $child->relative_url()); } -- cgit v1.2.3 From 9864ab4b2708ec54c39092a21828403cbbd25e2e Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Fri, 8 Jan 2010 14:56:08 -0800 Subject: Move the random image functionality into the gallery REST helper since choosing a random image is essentially a function on an item collection. Also implemented a bunch of other query filters for item collections. Created item::random_query() as a way of generating a reasonable starting point for random queries. --- modules/gallery/helpers/gallery_rest.php | 55 +++++++++++++++++--- modules/gallery/helpers/item.php | 25 +++------ modules/image_block/helpers/image_block_block.php | 2 +- modules/image_block/helpers/image_block_rest.php | 62 ----------------------- 4 files changed, 55 insertions(+), 89 deletions(-) delete mode 100644 modules/image_block/helpers/image_block_rest.php (limited to 'modules/gallery/helpers/gallery_rest.php') diff --git a/modules/gallery/helpers/gallery_rest.php b/modules/gallery/helpers/gallery_rest.php index fd18d59a..0de5da2b 100644 --- a/modules/gallery/helpers/gallery_rest.php +++ b/modules/gallery/helpers/gallery_rest.php @@ -36,22 +36,63 @@ // just like #1 except in a helper instead of in the model? class gallery_rest_Core { + + /** + * For items that are collections, you can specify the following additional query parameters to + * query the collection. You can specify them in any combination. + * + * scope=direct + * only return items that are immediately under this one + * scope=all + * return items anywhere under this one + * + * name= + * only return items where the name contains this substring + * + * random=true + * return a single random item + * + * type= + * limit the type to types in this list. eg, "type=photo,movie" + */ static function get($request) { $item = rest::resolve($request->url); access::required("view", $item); - if (isset($request->params->name)) { - $where[] = array("name", "=", $request->params->name); + $p = $request->params; + if (isset($p->random)) { + $orm = item::random_query()->offset(0)->limit(1); } else { - $where = array(); + $orm = ORM::factory("item")->viewable(); + } + + if (!empty($p->scope) && !in_array($p->scope, array("direct", "all"))) { + throw new Exception("Bad Request", 400); + } + if (!empty($p->scope)) { + if ($p->scope == "direct") { + $orm->where("parent_id", "=", $item->id); + } else { + $orm->where("left_ptr", ">=", $item->left_ptr); + $orm->where("right_ptr", "<=", $item->left_ptr); + $orm->where("id", "<>", $item->id); + } + } + + if (isset($p->name)) { + $orm->where("name", "LIKE", "%{$p->name}%"); + } + + if (isset($p->type)) { + $orm->where("type", "IN", explode(",", $p->type)); } - $children = array(); - foreach ($item->children($where) as $child) { - $children[] = url::abs_site("rest/gallery/" . $child->relative_url()); + $members = array(); + foreach ($orm->find_all() as $child) { + $members[] = url::abs_site("rest/gallery/" . $child->relative_url()); } - return rest::reply(array("resource" => $item->as_array(), "members" => $children)); + return rest::reply(array("resource" => $item->as_array(), "members" => $members)); } static function put($request) { diff --git a/modules/gallery/helpers/item.php b/modules/gallery/helpers/item.php index eb528f8f..1fd9ef16 100644 --- a/modules/gallery/helpers/item.php +++ b/modules/gallery/helpers/item.php @@ -175,31 +175,18 @@ class item_Core { } /** - * Return a random Item_Model, with optional filters + * Return a query to get a random Item_Model, with optional filters * * @param array (optional) where tuple */ - static function random($where=null) { - $random = ((float)mt_rand()) / (float)mt_getrandmax(); - - // Pick a random number and find the item that's got nearest smaller number. In the rare case - // that we chose the smallest number in the system, choose the item with the smallest number. + static function random_query($where=null) { + // 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. - $items = ORM::factory("item") + return ORM::factory("item") ->viewable() - ->where("rand_key", "<", $random) + ->where("rand_key", "<", ((float)mt_rand()) / (float)mt_getrandmax()) ->merge_where($where) - ->order_by("rand_key", "DESC") - ->find_all(1); - - if ($items->count() == 0) { - $items = ORM::factory("item") - ->viewable() - ->merge_where($where) - ->order_by("rand_key", "ASC") - ->find_all(1); - } - return $items; + ->order_by("rand_key", "DESC"); } } \ No newline at end of file diff --git a/modules/image_block/helpers/image_block_block.php b/modules/image_block/helpers/image_block_block.php index 5f2bbcb7..f28e775f 100644 --- a/modules/image_block/helpers/image_block_block.php +++ b/modules/image_block/helpers/image_block_block.php @@ -30,7 +30,7 @@ class image_block_block_Core { $block->css_id = "g-image-block"; $block->title = t("Random image"); $block->content = new View("image_block_block.html"); - $block->content->items = item::random(array(array("type", "!=", "album"))); + $block->content->items = item::random_query(array(array("type", "!=", "album")))->find_all(1); if ($block->content->items->count() == 0) { $block = ""; diff --git a/modules/image_block/helpers/image_block_rest.php b/modules/image_block/helpers/image_block_rest.php deleted file mode 100644 index 65eefb21..00000000 --- a/modules/image_block/helpers/image_block_rest.php +++ /dev/null @@ -1,62 +0,0 @@ -type) ? "random" : $request->type; - switch ($type) { - case "random": - $random = ((float)mt_rand()) / (float)mt_getrandmax(); - - $items = ORM::factory("item") - ->viewable() - ->where("type", "!=", "album") - ->where("rand_key", "<", $random) - ->order_by(array("rand_key" => "DESC")) - ->find_all(1); - - if ($items->count() == 0) { - // Try once more. If this fails, just ditch the block altogether - $items = ORM::factory("item") - ->viewable() - ->where("type", "!=", "album") - ->where("rand_key", ">= ", $random) - ->order_by(array("rand_key" => "DESC")) - ->find_all(1); - } - break; - default: - return rest::fail("Unsupported image block type: '{$type}'"); - } - - if ($items->count() > 0) { - $item = $items->current(); - $response_data = array("name" => $item->name, - "path" => $item->relative_url(), - "title" => $item->title, - "thumb_url" => $item->thumb_url(true), - "thumb_size" => array("height" => $item->thumb_height, - "width" => $item->thumb_width)); - - return rest::reply(array("resource" => $response_data)); - } else { - return rest::reply(); - } - } -} -- cgit v1.2.3 From fafa7f277f0591c74bd3d162c4c39a01604b55ae Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Sun, 17 Jan 2010 16:55:48 -0800 Subject: Remove a @todo. --- modules/gallery/helpers/gallery_rest.php | 40 +++++++++++++++++++------------- 1 file changed, 24 insertions(+), 16 deletions(-) (limited to 'modules/gallery/helpers/gallery_rest.php') diff --git a/modules/gallery/helpers/gallery_rest.php b/modules/gallery/helpers/gallery_rest.php index 0de5da2b..24733f20 100644 --- a/modules/gallery/helpers/gallery_rest.php +++ b/modules/gallery/helpers/gallery_rest.php @@ -19,7 +19,6 @@ */ // @todo Add logging -// @todo VALIDATION // Validation questions // @@ -100,10 +99,15 @@ class gallery_rest_Core { access::required("edit", $item); $params = $request->params; - foreach (array("captured", "description", "slug", "sort_column", "sort_order", - "title", "view_count", "weight") as $key) { - if (isset($params->$key)) { - $item->$key = $params->$key; + + // Only change fields from a whitelist. + foreach (array("album_cover_item_id", "captured", "description", + "height", "mime_type", "name", "parent_id", "rand_key", "resize_dirty", + "resize_height", "resize_width", "slug", "sort_column", "sort_order", + "thumb_dirty", "thumb_height", "thumb_width", "title", "view_count", + "weight", "width") as $key) { + if (array_key_exists($key, $request->params)) { + $item->$key = $request->params->$key; } } $item->save(); @@ -116,22 +120,26 @@ class gallery_rest_Core { access::required("edit", $parent); $params = $request->params; + $item = ORM::factory("item"); switch ($params->type) { case "album": - $item = album::create( - $parent, - $params->name, - isset($params->title) ? $params->title : $name, - isset($params->description) ? $params->description : null); + $item->type = "album"; + $item->parent_id = $parent->id; + $item->name = $params->name; + $item->title = isset($params->title) ? $params->title : $name; + $item->description = isset($params->description) ? $params->description : null; + $item->save(); break; case "photo": - $item = photo::create( - $parent, - $request->file, - $params->name, - isset($params->title) ? $params->title : $name, - isset($params->description) ? $params->description : null); + case "movie": + $item->type = $params->type; + $item->parent_id = $parent->id; + $item->set_data_file($request->file); + $item->name = $params->name; + $item->title = isset($params->title) ? $params->title : $name; + $item->description = isset($params->description) ? $params->description : null; + $item->save(); break; default: -- cgit v1.2.3 From cb7c263b470bd1452c47a3e29d67373869150d2c Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Tue, 19 Jan 2010 00:36:40 -0800 Subject: Return arrays instead of calling rest::reply. --- modules/gallery/helpers/gallery_rest.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'modules/gallery/helpers/gallery_rest.php') diff --git a/modules/gallery/helpers/gallery_rest.php b/modules/gallery/helpers/gallery_rest.php index 24733f20..7f93bd38 100644 --- a/modules/gallery/helpers/gallery_rest.php +++ b/modules/gallery/helpers/gallery_rest.php @@ -91,7 +91,7 @@ class gallery_rest_Core { $members[] = url::abs_site("rest/gallery/" . $child->relative_url()); } - return rest::reply(array("resource" => $item->as_array(), "members" => $members)); + return array("resource" => $item->as_array(), "members" => $members); } static function put($request) { @@ -112,7 +112,7 @@ class gallery_rest_Core { } $item->save(); - return rest::reply(array("url" => url::abs_site("/rest/gallery/" . $item->relative_url()))); + return array("url" => url::abs_site("/rest/gallery/" . $item->relative_url())); } static function post($request) { @@ -146,7 +146,7 @@ class gallery_rest_Core { throw new Rest_Exception("Invalid type: $args->type", 400); } - return rest::reply(array("url" => url::abs_site("/rest/gallery/" . $item->relative_url()))); + return array("url" => url::abs_site("/rest/gallery/" . $item->relative_url())); } static function delete($request) { @@ -154,7 +154,6 @@ class gallery_rest_Core { access::required("edit", $item); $item->delete(); - return rest::reply(); } static function resolve($path) { -- cgit v1.2.3 From 069a23e81192578f5f02f6a52a07536ceb6c0bcd Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Tue, 19 Jan 2010 01:16:59 -0800 Subject: Make scope default to direct. Add slug to the post params. Fix minor output bug. --- modules/gallery/helpers/gallery_rest.php | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'modules/gallery/helpers/gallery_rest.php') diff --git a/modules/gallery/helpers/gallery_rest.php b/modules/gallery/helpers/gallery_rest.php index 7f93bd38..827da122 100644 --- a/modules/gallery/helpers/gallery_rest.php +++ b/modules/gallery/helpers/gallery_rest.php @@ -65,17 +65,19 @@ class gallery_rest_Core { $orm = ORM::factory("item")->viewable(); } - if (!empty($p->scope) && !in_array($p->scope, array("direct", "all"))) { + if (empty($p->scope)) { + $p->scope = "direct"; + } + + if (!in_array($p->scope, array("direct", "all"))) { throw new Exception("Bad Request", 400); } - if (!empty($p->scope)) { - if ($p->scope == "direct") { - $orm->where("parent_id", "=", $item->id); - } else { - $orm->where("left_ptr", ">=", $item->left_ptr); - $orm->where("right_ptr", "<=", $item->left_ptr); - $orm->where("id", "<>", $item->id); - } + + if ($p->scope == "direct") { + $orm->where("parent_id", "=", $item->id); + } else { + $orm->where("left_ptr", ">", $item->left_ptr); + $orm->where("right_ptr", "<", $item->right_ptr); } if (isset($p->name)) { @@ -128,6 +130,7 @@ class gallery_rest_Core { $item->name = $params->name; $item->title = isset($params->title) ? $params->title : $name; $item->description = isset($params->description) ? $params->description : null; + $item->slug = isset($params->slug) ? $params->slug : null; $item->save(); break; @@ -137,13 +140,14 @@ class gallery_rest_Core { $item->parent_id = $parent->id; $item->set_data_file($request->file); $item->name = $params->name; - $item->title = isset($params->title) ? $params->title : $name; + $item->title = isset($params->title) ? $params->title : $params->name; $item->description = isset($params->description) ? $params->description : null; + $item->slug = isset($params->slug) ? $params->slug : null; $item->save(); break; default: - throw new Rest_Exception("Invalid type: $args->type", 400); + throw new Rest_Exception("Invalid type: $params->type", 400); } return array("url" => url::abs_site("/rest/gallery/" . $item->relative_url())); -- cgit v1.2.3 From c590fed132b07647c38b1d5b4a93ffe30b6ac4bf Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Tue, 19 Jan 2010 01:33:57 -0800 Subject: Change rest::url() to take a module name and a resource. The module does the rest. This function is symmetrical to rest::resolve. --- modules/gallery/helpers/gallery_rest.php | 28 ++++--------- modules/gallery/tests/Gallery_Rest_Helper_Test.php | 46 +++++++++++----------- modules/rest/helpers/rest.php | 12 ++++-- modules/tag/helpers/tag_rest.php | 15 ++++--- modules/tag/helpers/tags_rest.php | 6 +-- 5 files changed, 50 insertions(+), 57 deletions(-) (limited to 'modules/gallery/helpers/gallery_rest.php') diff --git a/modules/gallery/helpers/gallery_rest.php b/modules/gallery/helpers/gallery_rest.php index 827da122..5fd73a2e 100644 --- a/modules/gallery/helpers/gallery_rest.php +++ b/modules/gallery/helpers/gallery_rest.php @@ -17,25 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ - -// @todo Add logging - -// Validation questions -// -// We need to be able to properly validate anything we want to enter here. But all of our -// validation currently happens at the controller / form level, and we're not using the same -// controllers or forms. -// -// Possible solutions: -// 1) Move validation into the model and use it both here and in the regular controllers. But -// if we do that, how do we translate validation failures into a user-consumable output which -// we need so that we can return proper error responses to form submissions? -// -// 2) Create some kind of validation helper that can validate every field. Wait, isn't this -// just like #1 except in a helper instead of in the model? - class gallery_rest_Core { - /** * For items that are collections, you can specify the following additional query parameters to * query the collection. You can specify them in any combination. @@ -90,7 +72,7 @@ class gallery_rest_Core { $members = array(); foreach ($orm->find_all() as $child) { - $members[] = url::abs_site("rest/gallery/" . $child->relative_url()); + $members[] = rest::url("gallery", $child); } return array("resource" => $item->as_array(), "members" => $members); @@ -114,7 +96,7 @@ class gallery_rest_Core { } $item->save(); - return array("url" => url::abs_site("/rest/gallery/" . $item->relative_url())); + return array("url" => rest::url("gallery", $item)); } static function post($request) { @@ -150,7 +132,7 @@ class gallery_rest_Core { throw new Rest_Exception("Invalid type: $params->type", 400); } - return array("url" => url::abs_site("/rest/gallery/" . $item->relative_url())); + return array("url" => rest::url("gallery", $item)); } static function delete($request) { @@ -163,4 +145,8 @@ class gallery_rest_Core { static function resolve($path) { return url::get_item_from_uri($path); } + + static function url($item) { + return url::abs_site("rest/gallery/" . $item->relative_url()); + } } diff --git a/modules/gallery/tests/Gallery_Rest_Helper_Test.php b/modules/gallery/tests/Gallery_Rest_Helper_Test.php index 35fd0daf..dcd9a9db 100644 --- a/modules/gallery/tests/Gallery_Rest_Helper_Test.php +++ b/modules/gallery/tests/Gallery_Rest_Helper_Test.php @@ -28,7 +28,7 @@ class Gallery_Rest_Helper_Test extends Gallery_Unit_Test_Case { public function resolve_test() { $album = test::random_album(); - $resolved = rest::resolve(rest::url("gallery", $album->relative_url())); + $resolved = rest::resolve(rest::url("gallery", $album)); $this->assert_equal($album->id, $resolved->id); } @@ -40,32 +40,32 @@ class Gallery_Rest_Helper_Test extends Gallery_Unit_Test_Case { $album1->reload(); // No scope is the same as "direct" - $request->url = rest::url("gallery", $album1->relative_url()); + $request->url = rest::url("gallery", $album1); $request->params = new stdClass(); $this->assert_equal_array( array("resource" => $album1->as_array(), "members" => array( - rest::url("gallery", $photo1->relative_url()), - rest::url("gallery", $album2->relative_url()))), + rest::url("gallery", $photo1), + rest::url("gallery", $album2))), gallery_rest::get($request)); - $request->url = rest::url("gallery", $album1->relative_url()); + $request->url = rest::url("gallery", $album1); $request->params->scope = "direct"; $this->assert_equal_array( array("resource" => $album1->as_array(), "members" => array( - rest::url("gallery", $photo1->relative_url()), - rest::url("gallery", $album2->relative_url()))), + rest::url("gallery", $photo1), + rest::url("gallery", $album2))), gallery_rest::get($request)); - $request->url = rest::url("gallery", $album1->relative_url()); + $request->url = rest::url("gallery", $album1); $request->params->scope = "all"; $this->assert_equal_array( array("resource" => $album1->as_array(), "members" => array( - rest::url("gallery", $photo1->relative_url()), - rest::url("gallery", $album2->relative_url()), - rest::url("gallery", $photo2->relative_url()))), + rest::url("gallery", $photo1), + rest::url("gallery", $album2), + rest::url("gallery", $photo2))), gallery_rest::get($request)); } @@ -77,12 +77,12 @@ class Gallery_Rest_Helper_Test extends Gallery_Unit_Test_Case { $photo2->save(); $album1->reload(); - $request->url = rest::url("gallery", $album1->relative_url()); + $request->url = rest::url("gallery", $album1); $request->params->name = "foo"; $this->assert_equal_array( array("resource" => $album1->as_array(), "members" => array( - rest::url("gallery", $photo2->relative_url()))), + rest::url("gallery", $photo2))), gallery_rest::get($request)); } @@ -92,12 +92,12 @@ class Gallery_Rest_Helper_Test extends Gallery_Unit_Test_Case { $album2 = test::random_album($album1); $album1->reload(); - $request->url = rest::url("gallery", $album1->relative_url()); + $request->url = rest::url("gallery", $album1); $request->params->type = "album"; $this->assert_equal_array( array("resource" => $album1->as_array(), "members" => array( - rest::url("gallery", $album2->relative_url()))), + rest::url("gallery", $album2))), gallery_rest::get($request)); } @@ -105,11 +105,11 @@ class Gallery_Rest_Helper_Test extends Gallery_Unit_Test_Case { $album1 = test::random_album(); access::allow(identity::everybody(), "edit", $album1); - $request->url = rest::url("gallery", $album1->relative_url()); + $request->url = rest::url("gallery", $album1); $request->params->title = "my new title"; $this->assert_equal_array( - array("url" => rest::url("gallery", $album1->relative_url())), + array("url" => rest::url("gallery", $album1)), gallery_rest::put($request)); $this->assert_equal("my new title", $album1->reload()->title); } @@ -118,7 +118,7 @@ class Gallery_Rest_Helper_Test extends Gallery_Unit_Test_Case { $album1 = test::random_album(); access::allow(identity::everybody(), "edit", $album1); - $request->url = rest::url("gallery", $album1->relative_url()); + $request->url = rest::url("gallery", $album1); $request->params->title = "my new title"; $request->params->slug = "not url safe"; @@ -135,7 +135,7 @@ class Gallery_Rest_Helper_Test extends Gallery_Unit_Test_Case { $album1 = test::random_album(); access::allow(identity::everybody(), "edit", $album1); - $request->url = rest::url("gallery", $album1->relative_url()); + $request->url = rest::url("gallery", $album1); $request->params->type = "album"; $request->params->name = "my album"; $request->params->title = "my album"; @@ -150,7 +150,7 @@ class Gallery_Rest_Helper_Test extends Gallery_Unit_Test_Case { $album1 = test::random_album(); access::allow(identity::everybody(), "edit", $album1); - $request->url = rest::url("gallery", $album1->relative_url()); + $request->url = rest::url("gallery", $album1); $request->params->type = "album"; $request->params->name = "my album"; $request->params->title = "my album"; @@ -170,7 +170,7 @@ class Gallery_Rest_Helper_Test extends Gallery_Unit_Test_Case { $album1 = test::random_album(); access::allow(identity::everybody(), "edit", $album1); - $request->url = rest::url("gallery", $album1->relative_url()); + $request->url = rest::url("gallery", $album1); $request->params->type = "photo"; $request->params->name = "my photo.jpg"; $request->file = MODPATH . "gallery/tests/test.jpg"; @@ -185,7 +185,7 @@ class Gallery_Rest_Helper_Test extends Gallery_Unit_Test_Case { $album1 = test::random_album(); access::allow(identity::everybody(), "edit", $album1); - $request->url = rest::url("gallery", $album1->relative_url()); + $request->url = rest::url("gallery", $album1); gallery_rest::delete($request); $album1->reload(); @@ -195,7 +195,7 @@ class Gallery_Rest_Helper_Test extends Gallery_Unit_Test_Case { public function delete_album_fails_without_permission_test() { $album1 = test::random_album(); - $request->url = rest::url("gallery", $album1->relative_url()); + $request->url = rest::url("gallery", $album1); try { gallery_rest::delete($request); } catch (Exception $e) { diff --git a/modules/rest/helpers/rest.php b/modules/rest/helpers/rest.php index 423765bb..93ad2bd3 100644 --- a/modules/rest/helpers/rest.php +++ b/modules/rest/helpers/rest.php @@ -75,10 +75,14 @@ class rest_Core { /** * Return an absolute url used for REST resource location. * @param string module name (eg, "gallery", "tags") - * @param string relative path (eg "Family/Weddings.jpg") - * @return string complete url + * @param object resource */ - static function url($module, $path) { - return url::abs_site("rest/$module/$path"); + static function url($module, $resource) { + $class = "{$module}_rest"; + if (!method_exists($class, "url")) { + throw new Exception("@todo MISSING REST CLASS: $class"); + } + + return call_user_func(array($class, "url"), $resource); } } diff --git a/modules/tag/helpers/tag_rest.php b/modules/tag/helpers/tag_rest.php index 0aac5291..a4eaee90 100644 --- a/modules/tag/helpers/tag_rest.php +++ b/modules/tag/helpers/tag_rest.php @@ -22,10 +22,10 @@ class tag_rest_Core { $tag = rest::resolve($request->url); $items = array(); foreach ($tag->items() as $item) { - $items[] = url::abs_site("rest/gallery/" . $item->relative_url()); + $items[] = rest::url("gallery", $item); } - return rest::reply(array("resource" => $tag->as_array(), "members" => $items)); + return array("resource" => $tag->as_array(), "members" => $items); } static function post($request) { @@ -38,7 +38,7 @@ class tag_rest_Core { access::required("edit", $item); tag::add($item, $tag->name); - return rest::reply(array("url" => url::abs_site("rest/tag/" . rawurlencode($tag->name)))); + return array("url" => rest::url("tag", $tag)); } static function put($request) { @@ -61,7 +61,7 @@ class tag_rest_Core { } $tag->save(); - return rest::reply(array("url" => url::abs_site("rest/tag/" . rawurlencode($tag->name)))); + return array("url" => rest::url("tag", $tag)); } static function delete($request) { @@ -70,7 +70,6 @@ class tag_rest_Core { if (empty($request->params->url)) { // Delete the tag $tag->delete(); - return rest::reply(); } else { // Remove an item from the tag $item = rest::resolve($request->params->url); @@ -78,7 +77,7 @@ class tag_rest_Core { $tag->save(); tag::compact(); - return rest::reply(array("url" => url::abs_site("rest/tag/" . rawurlencode($tag->name)))); + return array("url" => rest::url("tag", $tag)); } } @@ -90,4 +89,8 @@ class tag_rest_Core { return $tag; } + + static function url($item) { + return url::abs_site("rest/tag/" . rawurlencode($tag->name)); + } } diff --git a/modules/tag/helpers/tags_rest.php b/modules/tag/helpers/tags_rest.php index 7f0ed66a..dd23e97f 100644 --- a/modules/tag/helpers/tags_rest.php +++ b/modules/tag/helpers/tags_rest.php @@ -21,9 +21,9 @@ class tags_rest_Core { static function get($request) { $tags = array(); foreach (ORM::factory("tag")->find_all() as $tag) { - $tags[$tag->name] = url::abs_site("rest/tags/" . rawurlencode($tag->name)); + $tags[$tag->name] = rest::url("tags", $tag); } - return rest::reply(array("members" => $tags)); + return array("members" => $tags); } static function post($request) { @@ -43,6 +43,6 @@ class tags_rest_Core { $tag->save(); } - return rest::reply(array("url" => url::abs_site("rest/tag/" . rawurlencode($tag->name)))); + return array("url" => rest::url("tag", $tag)); } } -- cgit v1.2.3 From c3ed64fc6c0c1c9001d32191b0f1a5c21ec4b7c5 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Tue, 19 Jan 2010 01:46:45 -0800 Subject: Use property_exists() on our stdClass instead of array_key_exists() --- modules/gallery/helpers/gallery_rest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'modules/gallery/helpers/gallery_rest.php') diff --git a/modules/gallery/helpers/gallery_rest.php b/modules/gallery/helpers/gallery_rest.php index 5fd73a2e..49096100 100644 --- a/modules/gallery/helpers/gallery_rest.php +++ b/modules/gallery/helpers/gallery_rest.php @@ -90,7 +90,7 @@ class gallery_rest_Core { "resize_height", "resize_width", "slug", "sort_column", "sort_order", "thumb_dirty", "thumb_height", "thumb_width", "title", "view_count", "weight", "width") as $key) { - if (array_key_exists($key, $request->params)) { + if (property_exists($request->params, $key)) { $item->$key = $request->params->$key; } } -- cgit v1.2.3 From 210e02f0001489cdfa22da2fb57d6db08954aef3 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Wed, 20 Jan 2010 21:13:58 -0800 Subject: Throw Rest exceptions, not regular exceptions. --- modules/gallery/helpers/gallery_rest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'modules/gallery/helpers/gallery_rest.php') diff --git a/modules/gallery/helpers/gallery_rest.php b/modules/gallery/helpers/gallery_rest.php index 49096100..c5838ea5 100644 --- a/modules/gallery/helpers/gallery_rest.php +++ b/modules/gallery/helpers/gallery_rest.php @@ -52,7 +52,7 @@ class gallery_rest_Core { } if (!in_array($p->scope, array("direct", "all"))) { - throw new Exception("Bad Request", 400); + throw new Rest_Exception("Bad Request", 400); } if ($p->scope == "direct") { -- cgit v1.2.3 From bcf1caad1459a458a7923335a4a6bc521816de40 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Fri, 22 Jan 2010 00:27:00 -0800 Subject: Reshape the rest code to be more consistent with regards to relationships. Now when you view a resource, it has 4 top level elements: url: the url of this resource resource: array of key value pairs describing the resource members: array of urls to members of this collection relationships: array of array of members. Relationships are a special type of collection that links two different resources together. To remove a relationship, just DELETE its url. To create a relationship, POST to its collection. Individual modules can add their own relationships to any resource via a callback mechanism. Example: Array( [url] => http://g3.com/rest/item/1 [resource] => Array ( [id] => 1 [album_cover_item_id] => 4 [captured] => [created] => 1264056417 [description] => [height] => ... ) [members] => Array( [0] => http://g3.com/rest/item/2 [1] => http://g3.com/rest/item/3 [2] => http://g3.com/rest/item/4 [3] => http://g3.com/rest/item/5 ... ) [relationships] => Array( [tags] => Array ( [0] => http://g3.com/rest/tag_item/2,1 [1] => http://g3.com/rest/tag_item/23,1 ) ) ) --- modules/gallery/helpers/gallery_rest.php | 152 ----------------------------- modules/gallery/helpers/item_rest.php | 158 +++++++++++++++++++++++++++++++ modules/rest/controllers/rest.php | 2 +- modules/rest/helpers/rest.php | 50 ++++++++-- modules/tag/helpers/tag.php | 14 +-- modules/tag/helpers/tag_event.php | 12 ++- modules/tag/helpers/tag_item_rest.php | 50 ++++++++++ modules/tag/helpers/tag_rest.php | 43 +++++---- modules/tag/helpers/tags_rest.php | 4 +- 9 files changed, 289 insertions(+), 196 deletions(-) delete mode 100644 modules/gallery/helpers/gallery_rest.php create mode 100644 modules/gallery/helpers/item_rest.php create mode 100644 modules/tag/helpers/tag_item_rest.php (limited to 'modules/gallery/helpers/gallery_rest.php') diff --git a/modules/gallery/helpers/gallery_rest.php b/modules/gallery/helpers/gallery_rest.php deleted file mode 100644 index c5838ea5..00000000 --- a/modules/gallery/helpers/gallery_rest.php +++ /dev/null @@ -1,152 +0,0 @@ - - * only return items where the name contains this substring - * - * random=true - * return a single random item - * - * type= - * limit the type to types in this list. eg, "type=photo,movie" - */ - static function get($request) { - $item = rest::resolve($request->url); - access::required("view", $item); - - $p = $request->params; - if (isset($p->random)) { - $orm = item::random_query()->offset(0)->limit(1); - } else { - $orm = ORM::factory("item")->viewable(); - } - - if (empty($p->scope)) { - $p->scope = "direct"; - } - - if (!in_array($p->scope, array("direct", "all"))) { - throw new Rest_Exception("Bad Request", 400); - } - - if ($p->scope == "direct") { - $orm->where("parent_id", "=", $item->id); - } else { - $orm->where("left_ptr", ">", $item->left_ptr); - $orm->where("right_ptr", "<", $item->right_ptr); - } - - if (isset($p->name)) { - $orm->where("name", "LIKE", "%{$p->name}%"); - } - - if (isset($p->type)) { - $orm->where("type", "IN", explode(",", $p->type)); - } - - $members = array(); - foreach ($orm->find_all() as $child) { - $members[] = rest::url("gallery", $child); - } - - return array("resource" => $item->as_array(), "members" => $members); - } - - static function put($request) { - $item = rest::resolve($request->url); - access::required("edit", $item); - - $params = $request->params; - - // Only change fields from a whitelist. - foreach (array("album_cover_item_id", "captured", "description", - "height", "mime_type", "name", "parent_id", "rand_key", "resize_dirty", - "resize_height", "resize_width", "slug", "sort_column", "sort_order", - "thumb_dirty", "thumb_height", "thumb_width", "title", "view_count", - "weight", "width") as $key) { - if (property_exists($request->params, $key)) { - $item->$key = $request->params->$key; - } - } - $item->save(); - - return array("url" => rest::url("gallery", $item)); - } - - static function post($request) { - $parent = rest::resolve($request->url); - access::required("edit", $parent); - - $params = $request->params; - $item = ORM::factory("item"); - switch ($params->type) { - case "album": - $item->type = "album"; - $item->parent_id = $parent->id; - $item->name = $params->name; - $item->title = isset($params->title) ? $params->title : $name; - $item->description = isset($params->description) ? $params->description : null; - $item->slug = isset($params->slug) ? $params->slug : null; - $item->save(); - break; - - case "photo": - case "movie": - $item->type = $params->type; - $item->parent_id = $parent->id; - $item->set_data_file($request->file); - $item->name = $params->name; - $item->title = isset($params->title) ? $params->title : $params->name; - $item->description = isset($params->description) ? $params->description : null; - $item->slug = isset($params->slug) ? $params->slug : null; - $item->save(); - break; - - default: - throw new Rest_Exception("Invalid type: $params->type", 400); - } - - return array("url" => rest::url("gallery", $item)); - } - - static function delete($request) { - $item = rest::resolve($request->url); - access::required("edit", $item); - - $item->delete(); - } - - static function resolve($path) { - return url::get_item_from_uri($path); - } - - static function url($item) { - return url::abs_site("rest/gallery/" . $item->relative_url()); - } -} diff --git a/modules/gallery/helpers/item_rest.php b/modules/gallery/helpers/item_rest.php new file mode 100644 index 00000000..edc44c45 --- /dev/null +++ b/modules/gallery/helpers/item_rest.php @@ -0,0 +1,158 @@ + + * only return items where the name contains this substring + * + * random=true + * return a single random item + * + * type= + * limit the type to types in this list. eg, "type=photo,movie" + */ + static function get($request) { + $item = rest::resolve($request->url); + access::required("view", $item); + + $p = $request->params; + if (isset($p->random)) { + $orm = item::random_query()->offset(0)->limit(1); + } else { + $orm = ORM::factory("item")->viewable(); + } + + if (empty($p->scope)) { + $p->scope = "direct"; + } + + if (!in_array($p->scope, array("direct", "all"))) { + throw new Rest_Exception("Bad Request", 400); + } + + if ($p->scope == "direct") { + $orm->where("parent_id", "=", $item->id); + } else { + $orm->where("left_ptr", ">", $item->left_ptr); + $orm->where("right_ptr", "<", $item->right_ptr); + } + + if (isset($p->name)) { + $orm->where("name", "LIKE", "%{$p->name}%"); + } + + if (isset($p->type)) { + $orm->where("type", "IN", explode(",", $p->type)); + } + + $members = array(); + foreach ($orm->find_all() as $child) { + $members[] = rest::url("item", $child); + } + + return array( + "url" => $request->url, + "resource" => $item->as_array(), + "members" => $members, + "relationships" => rest::relationships("item", $item)); + } + + static function put($request) { + $item = rest::resolve($request->url); + access::required("edit", $item); + + $params = $request->params; + + // Only change fields from a whitelist. + foreach (array("album_cover_item_id", "captured", "description", + "height", "mime_type", "name", "parent_id", "rand_key", "resize_dirty", + "resize_height", "resize_width", "slug", "sort_column", "sort_order", + "thumb_dirty", "thumb_height", "thumb_width", "title", "view_count", + "weight", "width") as $key) { + if (property_exists($request->params, $key)) { + $item->$key = $request->params->$key; + } + } + if ($item->changed) { + $item->save(); + } + } + + static function post($request) { + $parent = rest::resolve($request->url); + access::required("edit", $parent); + + $params = $request->params; + $item = ORM::factory("item"); + switch ($params->type) { + case "album": + $item->type = "album"; + $item->parent_id = $parent->id; + $item->name = $params->name; + $item->title = isset($params->title) ? $params->title : $name; + $item->description = isset($params->description) ? $params->description : null; + $item->slug = isset($params->slug) ? $params->slug : null; + $item->save(); + break; + + case "photo": + case "movie": + $item->type = $params->type; + $item->parent_id = $parent->id; + $item->set_data_file($request->file); + $item->name = $params->name; + $item->title = isset($params->title) ? $params->title : $params->name; + $item->description = isset($params->description) ? $params->description : null; + $item->slug = isset($params->slug) ? $params->slug : null; + $item->save(); + break; + + default: + throw new Rest_Exception("Invalid type: $params->type", 400); + } + } + + static function delete($request) { + $item = rest::resolve($request->url); + access::required("edit", $item); + + $item->delete(); + } + + static function resolve($id) { + $item = ORM::factory("item")->where("id", "=", $id)->find(); + if (!access::can("view", $item)) { + throw new Kohana_404_Exception(); + } + return $item; + } + + static function url($item) { + return url::abs_site("rest/item/{$item->id}"); + } +} diff --git a/modules/rest/controllers/rest.php b/modules/rest/controllers/rest.php index 9f0bc5b3..ba996b84 100644 --- a/modules/rest/controllers/rest.php +++ b/modules/rest/controllers/rest.php @@ -56,7 +56,7 @@ class Rest_Controller extends Controller { $handler_method = $request->method; if (!method_exists($handler_class, $handler_method)) { - throw new Rest_Exception("Forbidden", 403); + throw new Rest_Exception("Bad Request", 400); } try { diff --git a/modules/rest/helpers/rest.php b/modules/rest/helpers/rest.php index 85987ca1..fe704a9e 100644 --- a/modules/rest/helpers/rest.php +++ b/modules/rest/helpers/rest.php @@ -22,8 +22,16 @@ class rest_Core { Session::abort_save(); if ($data) { - header("Content-type: application/json"); - print json_encode($data); + if (Input::instance()->get("output_type") == "html") { + header("Content-type: text/html"); + $html = preg_replace( + "#(^|[\n ])([\w]+?://[\w]+[^ \"\n\r\t<]*)#ise", "'\\1\\2'", + print_r($data, 1)); + print "
$html
"; + } else { + header("Content-type: application/json"); + print json_encode($data); + } } } @@ -64,7 +72,10 @@ class rest_Core { /** * Convert a REST url into an object. - * Eg: "http://example.com/gallery3/index.php/rest/gallery/Family/Wedding" -> Item_Model + * Eg: + * http://example.com/gallery3/index.php/rest/item/35 -> Item_Model + * http://example.com/gallery3/index.php/rest/tag/16 -> Tag_Model + * http://example.com/gallery3/index.php/rest/tagged_item/1,16 -> [Tag_Model, Item_Model] * * @param string the fully qualified REST url * @return mixed the corresponding object (usually a model of some kind) @@ -88,15 +99,38 @@ class rest_Core { /** * Return an absolute url used for REST resource location. - * @param string module name (eg, "gallery", "tags") + * @param string resource type (eg, "item", "tag") * @param object resource */ - static function url($module, $resource) { - $class = "{$module}_rest"; + static function url() { + $args = func_get_args(); + $resource_type = array_shift($args); + + $class = "{$resource_type}_rest"; if (!method_exists($class, "url")) { - throw new Exception("@todo MISSING REST CLASS: $class"); + throw new Rest_Exception("Bad Request", 400); + } + + $url = call_user_func_array(array($class, "url"), $args); + if (Input::instance()->get("output_type") == "html") { + $url .= "?output_type=html"; + } + return $url; + } + + static function relationships($resource_type, $resource) { + $results = array(); + foreach (module::active() as $module) { + foreach (glob(MODPATH . "{$module->name}/helpers/*_rest.php") as $filename) { + $class = str_replace(".php", "", basename($filename)); + if (method_exists($class, "relationships")) { + $results = array_merge( + $results, + call_user_func(array($class, "relationships"), $resource_type, $resource)); + } + } } - return call_user_func(array($class, "url"), $resource); + return $results; } } diff --git a/modules/tag/helpers/tag.php b/modules/tag/helpers/tag.php index c49a2d0f..9e59b527 100644 --- a/modules/tag/helpers/tag.php +++ b/modules/tag/helpers/tag.php @@ -91,16 +91,10 @@ class tag_Core { * @return array */ static function item_tags($item) { - $tags = array(); - foreach (db::build() - ->select("name") - ->from("tags") - ->join("items_tags", "tags.id", "items_tags.tag_id", "left") - ->where("items_tags.item_id", "=", $item->id) - ->execute() as $row) { - $tags[] = $row->name; - } - return $tags; + return ORM::factory("tag") + ->join("items_tags", "tags.id", "items_tags.tag_id", "left") + ->where("items_tags.item_id", "=", $item->id) + ->find_all(); } static function get_add_form($item) { diff --git a/modules/tag/helpers/tag_event.php b/modules/tag/helpers/tag_event.php index 6ee8e708..403ccd52 100644 --- a/modules/tag/helpers/tag_event.php +++ b/modules/tag/helpers/tag_event.php @@ -71,9 +71,13 @@ class tag_event_Core { $('form input[id=tags]').autocomplete( '$url', {max: 30, multiple: true, multipleSeparator: ',', cacheLength: 1}); });"); - $tag_value = implode(", ", tag::item_tags($item)); + + $tag_names = array(); + foreach (tag::item_tags($item) as $tag) { + $tag_names[] = $tag->name; + } $form->edit_item->input("tags")->label(t("Tags (comma separated)")) - ->value($tag_value); + ->value(implode(", ", $tag_names)); } static function item_edit_form_completed($item, $form) { @@ -95,7 +99,9 @@ class tag_event_Core { } static function item_index_data($item, $data) { - $data[] = join(" ", tag::item_tags($item)); + foreach (tag::item_tags($item) as $tag) { + $data[] = $tag->name; + } } static function add_photos_form($album, $form) { diff --git a/modules/tag/helpers/tag_item_rest.php b/modules/tag/helpers/tag_item_rest.php new file mode 100644 index 00000000..cd9bb6fe --- /dev/null +++ b/modules/tag/helpers/tag_item_rest.php @@ -0,0 +1,50 @@ +url); + return array( + "url" => $request->url, + "members" => array( + rest::url("tag", $tag), + rest::url("item", $item))); + } + + static function delete($request) { + list ($tag, $item) = rest::resolve($request->url); + $tag->remove($item); + $tag->save(); + } + + static function resolve($tuple) { + list ($tag_id, $item_id) = split(",", $tuple); + $tag = ORM::factory("tag")->where("id", "=", $tag_id)->find(); + $item = ORM::factory("item")->where("id", "=", $item_id)->find(); + if (!$tag->loaded() || !$item->loaded() || !$tag->has($item)) { + throw new Kohana_404_Exception(); + } + + return array($tag, $item); + } + + static function url($tag, $item) { + return url::abs_site("rest/tag_item/{$tag->id},{$item->id}"); + } +} diff --git a/modules/tag/helpers/tag_rest.php b/modules/tag/helpers/tag_rest.php index 0226c6d3..d68cb73d 100644 --- a/modules/tag/helpers/tag_rest.php +++ b/modules/tag/helpers/tag_rest.php @@ -20,12 +20,18 @@ class tag_rest_Core { static function get($request) { $tag = rest::resolve($request->url); - $items = array(); + $tag_items = array(); foreach ($tag->items() as $item) { - $items[] = rest::url("gallery", $item); + if (access::can("view", $item)) { + $tag_items[] = rest::url("tag_item", $tag, $item); + } } - return array("resource" => $tag->as_array(), "members" => $items); + return array( + "url" => $request->url, + "resource" => $tag->as_array(), + "relationships" => array( + "items" => $tag_items)); } static function post($request) { @@ -38,37 +44,34 @@ class tag_rest_Core { access::required("edit", $item); tag::add($item, $tag->name); - return array("url" => rest::url("tag", $tag)); } static function put($request) { $tag = rest::resolve($request->url); if (isset($request->params->name)) { $tag->name = $request->params->name; + $tag->save(); } - - $tag->save(); - return array("url" => rest::url("tag", $tag)); } static function delete($request) { $tag = rest::resolve($request->url); + $tag->delete(); + } - if (empty($request->params->url)) { - // Delete the tag - $tag->delete(); - } else { - // Remove an item from the tag - $item = rest::resolve($request->params->url); - access::required("edit", $item); - $tag->remove($item); - $tag->save(); - tag::compact(); + static function relationships($resource_type, $resource) { + switch ($resource_type) { + case "item": + $tags = array(); + foreach (tag::item_tags($resource) as $tag) { + $tags[] = rest::url("tag_item", $tag, $resource); + } + return array("tags" => $tags); } } - static function resolve($tag_name) { - $tag = ORM::factory("tag")->where("name", "=", $tag_name)->find(); + static function resolve($id) { + $tag = ORM::factory("tag")->where("id", "=", $id)->find(); if (!$tag->loaded()) { throw new Kohana_404_Exception(); } @@ -77,6 +80,6 @@ class tag_rest_Core { } static function url($tag) { - return url::abs_site("rest/tag/" . rawurlencode($tag->name)); + return url::abs_site("rest/tag/{$tag->id}"); } } diff --git a/modules/tag/helpers/tags_rest.php b/modules/tag/helpers/tags_rest.php index 41317ecd..57461125 100644 --- a/modules/tag/helpers/tags_rest.php +++ b/modules/tag/helpers/tags_rest.php @@ -21,7 +21,7 @@ class tags_rest_Core { static function get($request) { $tags = array(); foreach (ORM::factory("tag")->find_all() as $tag) { - $tags[$tag->name] = rest::url("tag", $tag); + $tags[] = rest::url("tag", $tag); } return array("members" => $tags); } @@ -33,7 +33,7 @@ class tags_rest_Core { access::required("edit", item::root()); if (empty($request->params->name)) { - throw new Rest_Exception("Bad Request: missing name", 400); + throw new Rest_Exception("Bad Request", 400); } $tag = ORM::factory("tag")->where("name", "=", $request->params->name)->find(); -- cgit v1.2.3