diff options
Diffstat (limited to 'modules')
21 files changed, 1580 insertions, 112 deletions
diff --git a/modules/gallery/controllers/albums.php b/modules/gallery/controllers/albums.php index 2134a419..fde5c02d 100644 --- a/modules/gallery/controllers/albums.php +++ b/modules/gallery/controllers/albums.php @@ -134,40 +134,32 @@ class Albums_Controller extends Items_Controller { $form->edit_item->dirname->value != $album->name || $form->edit_item->slug->value != $album->slug) { // Make sure that there's not a conflict - if ($row = db::build() - ->select(array("name", "slug")) - ->from("items") - ->where("parent_id", "=", $album->parent_id) - ->where("id", "<>", $album->id) - ->and_open() - ->where("name", "=", $form->edit_item->dirname->value) - ->or_where("slug", "=", $form->edit_item->slug->value) - ->close() - ->execute() - ->current()) { - if ($row->name == $form->edit_item->dirname->value) { - $form->edit_item->dirname->add_error("name_conflict", 1); - } - if ($row->slug == $form->edit_item->slug->value) { - $form->edit_item->slug->add_error("slug_conflict", 1); - } - $valid = false; + $errors = item::check_for_conflicts( + $album, $form->edit_item->dirname->value, $form->edit_item->slug->value); + + if (!empty($errors["name_conflict"])) { + $form->edit_item->dirname->add_error("name_conflict", 1); + } + if (!empty($errors["slug_conflict"])) { + $form->edit_item->slug->add_error("slug_conflict", 1); } + $valid = empty($errors); } } if ($valid) { $watching_album = $album->url() != ($location = parse_url(request::referrer(), PHP_URL_PATH)); - $album->title = $form->edit_item->title->value; - $album->description = $form->edit_item->description->value; - $album->sort_column = $form->edit_item->sort_order->column->value; - $album->sort_order = $form->edit_item->sort_order->direction->value; + $new_values = array("title" => $form->edit_item->title->value, + "description" => $form->edit_item->description->value, + "sort_column" => $form->edit_item->sort_order->column->value, + "sort_order" => $form->edit_item->sort_order->direction->value, + "slug" => $form->edit_item->slug->value); if ($album->id != 1) { - $album->rename($form->edit_item->dirname->value); + $new_values["name"] = $form->edit_item->dirname->value; } - $album->slug = $form->edit_item->slug->value; - $album->save(); + item::update($album, $new_values); + module::event("item_edit_form_completed", $album, $form); log::success("content", "Updated album", "<a href=\"albums/$album->id\">view</a>"); diff --git a/modules/gallery/controllers/movies.php b/modules/gallery/controllers/movies.php index b5785ecf..2d429910 100644 --- a/modules/gallery/controllers/movies.php +++ b/modules/gallery/controllers/movies.php @@ -76,34 +76,26 @@ class Movies_Controller extends Items_Controller { if ($form->edit_item->filename->value != $movie->name || $form->edit_item->slug->value != $movie->slug) { // Make sure that there's not a name or slug conflict - if ($row = db::build() - ->select(array("name", "slug")) - ->from("items") - ->where("parent_id", "=", $movie->parent_id) - ->where("id", "<>", $movie->id) - ->and_open() - ->where("name", "=", $form->edit_item->filename->value) - ->or_where("slug", "=", $form->edit_item->slug->value) - ->close() - ->execute() - ->current()) { - if ($row->name == $form->edit_item->filename->value) { - $form->edit_item->filename->add_error("name_conflict", 1); - } - if ($row->slug == $form->edit_item->slug->value) { - $form->edit_item->slug->add_error("slug_conflict", 1); - } - $valid = false; + $errors = item::check_for_conflicts( + $movie, $form->edit_item->filename->value, $form->edit_item->slug->value); + + if (!empty($errors["name_conflict"])) { + $form->edit_item->filename->add_error("name_conflict", 1); + } + if (!empty($errors["slug_conflict"])) { + $form->edit_item->slug->add_error("slug_conflict", 1); } + $valid = empty($errors); } } if ($valid) { - $movie->title = $form->edit_item->title->value; - $movie->description = $form->edit_item->description->value; - $movie->slug = $form->edit_item->slug->value; - $movie->rename($form->edit_item->filename->value); - $movie->save(); + $new_values = array("title" => $form->edit_item->title->value, + "description" => $form->edit_item->description->value, + "name" => $form->edit_item->filename->value, + "slug" => $form->edit_item->slug->value); + item::update($movie, $new_values); + module::event("item_edit_form_completed", $movie, $form); log::success("content", "Updated movie", "<a href=\"{$movie->url()}\">view</a>"); diff --git a/modules/gallery/controllers/photos.php b/modules/gallery/controllers/photos.php index ced9da2f..7c2c266c 100644 --- a/modules/gallery/controllers/photos.php +++ b/modules/gallery/controllers/photos.php @@ -76,42 +76,33 @@ class Photos_Controller extends Items_Controller { if ($form->edit_item->filename->value != $photo->name || $form->edit_item->slug->value != $photo->slug) { // Make sure that there's not a name or slug conflict - if ($row = db::build() - ->select(array("name", "slug")) - ->from("items") - ->where("parent_id", "=", $photo->parent_id) - ->where("id", "<>", $photo->id) - ->and_open() - ->where("name", "=", $form->edit_item->filename->value) - ->or_where("slug", "=", $form->edit_item->slug->value) - ->close() - ->execute() - ->current()) { - if ($row->name == $form->edit_item->filename->value) { - $form->edit_item->filename->add_error("name_conflict", 1); - } - if ($row->slug == $form->edit_item->slug->value) { - $form->edit_item->slug->add_error("slug_conflict", 1); - } - $valid = false; + $errors = item::check_for_conflicts( + $photo, $form->edit_item->filename->value, $form->edit_item->slug->value); + + if (!empty($errors["name_conflict"])) { + $form->edit_item->filename->add_error("name_conflict", 1); + } + if (!empty($errors["slug_conflict"])) { + $form->edit_item->slug->add_error("slug_conflict", 1); } + $valid = empty($errors); } } if ($valid) { $watching_album = $photo->url() != ($location = parse_url(request::referrer(), PHP_URL_PATH)); - $photo->title = $form->edit_item->title->value; - $photo->description = $form->edit_item->description->value; - $photo->slug = $form->edit_item->slug->value; - $photo->rename($form->edit_item->filename->value); - $photo->save(); + $new_values = array("title" => $form->edit_item->title->value, + "description" => $form->edit_item->description->value, + "name" => $form->edit_item->filename->value, + "slug" => $form->edit_item->slug->value); + item::update($photo, $new_values); + module::event("item_edit_form_completed", $photo, $form); log::success("content", "Updated photo", "<a href=\"{$photo->url()}\">view</a>"); message::success( - t("Saved photo %photo_title", - array("photo_title" => html::purify($photo->title)))); + t("Saved photo %photo_title", array("photo_title" => html::purify($photo->title)))); print json_encode( array("result" => "success", diff --git a/modules/gallery/helpers/gallery_rest.php b/modules/gallery/helpers/gallery_rest.php new file mode 100644 index 00000000..227a6f02 --- /dev/null +++ b/modules/gallery/helpers/gallery_rest.php @@ -0,0 +1,245 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2009 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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 = ORM::factory("item") + ->where("relative_url_cache", $path) + ->viewable() + ->find(); + + if (!$item->loaded) { + return rest::not_found("Resource: {$path} missing."); + } + + $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); + + $children = self::_get_children($item, $request); + if (!empty($children) || $item->is_album()) { + $response_data["children"] = $children; + } + return rest::success(array("resource" => $response_data)); + } + + static function put($request) { + if (empty($request->arguments)) { + return rest::invalid_request(); + } + $path = implode("/", $request->arguments); + + $item = ORM::factory("item") + ->where("relative_url_cache", $path) + ->viewable() + ->find(); + + if (!$item->loaded) { + return rest::not_found("Resource: {$path} missing."); + } + + if (!access::can("edit", $item)) { + return rest::not_found("Resource: {$path} permission denied."); + } + + // Validate the request data + $new_values = gallery_rest::_validate($request, $item); + $errors = $new_values->errors(); + if (empty($errors)) { + item::update($item, $new_values->as_array()); + + log::success("content", "Updated $item->type", "<a href=\"{$item->type}s/$item->id\">view</a>"); + + return rest::success(); + } else { + return rest::validation_error($errors); + } + } + + static function post($request) { + if (empty($request->arguments)) { + return rest::invalid_request(); + } + $path = implode("/", $request->arguments); + + $components = explode("/", $path); + $name = urldecode(array_pop($components)); + + $parent = ORM::factory("item") + ->where("relative_url_cache", implode("/", $components)) + ->viewable() + ->find(); + + if (!$parent->loaded) { + return rest::not_found("Resource: {$path} missing."); + } + + if (!access::can("edit", $parent)) { + return rest::not_found("Resource: {$path} permission denied."); + } + + // Validate the request data + $new_values = gallery_rest::_validate($request); + $errors = $new_values->errors(); + if (!empty($errors)) { + return rest::validation_error($errors); + } + + if (empty($new_values["image"])) { + $new_item = album::create( + $parent, + $name, + empty($request->title) ? $name : $request->title, + empty($request->description) ? null : $request->description, + identity::active_user()->id, + empty($request->slug) ? $name : $request->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"); + } + } + + log::success("content", $log_message, "<a href=\"{$new_item->type}s/$new_item->id\">view</a>"); + + return rest::success(array("path" => $new_item->relative_url())); + } + + static function delete($request) { + if (empty($request->arguments)) { + return rest::invalid_request(); + } + $path = implode("/", $request->arguments); + + $item = ORM::factory("item") + ->where("relative_url_cache", $path) + ->viewable() + ->find(); + + if (!$item->loaded) { + return rest::success(); + } + + if (!access::can("edit", $item)) { + return rest::not_found("Resource: {$path} permission denied."); + } + + if ($item->id == 1) { + return rest::invalid_request("Attempt to delete the root album"); + } + + $parent = $item->parent(); + $item->delete(); + + if ($item->is_album()) { + $msg = t("Deleted album <b>%title</b>", array("title" => html::purify($item->title))); + } else { + $msg = t("Deleted photo <b>%title</b>", 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_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, $item=null) { + $new_values = array(); + $fields = array("title", "description", "name", "slug", "image"); + if (empty($item)) { + $item = ORM::factory("item"); + $item->id = 0; + } + if ($item->id == 1) { + unset($request["name"]); + unset($request["slug"]); + } + foreach ($fields as $field) { + if (isset($request->$field)) { + $new_values[$field] = $request->$field; + } else if (isset($item->$field)) { + $new_values[$field] = $item->$field; + } + } + + $new_values = new Validation($new_values); + foreach ($item->rules as $field => $rules) { + foreach (explode("|", $rules) as $rule) { + $new_values->add_rules($field, $rule); + } + } + if (isset($new_values["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 = item::check_for_conflicts($item, $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; + } +} diff --git a/modules/gallery/helpers/item.php b/modules/gallery/helpers/item.php index f6181f8a..e8119027 100644 --- a/modules/gallery/helpers/item.php +++ b/modules/gallery/helpers/item.php @@ -96,6 +96,51 @@ class item_Core { } } + + static function update($item, $fields) { + $dirty = false; + if ($item->id != 1 && !empty($fields["name"]) && $fields["name"] != $item->name) { + $item->rename($fields["name"]); + unset($fields["name"]); + $dirty = true; + } + foreach ($fields as $field => $value) { + if ($value !== $item->$field) { + $item->$field = $value; + $dirty = true; + } + } + + if ($dirty) { + $item->save(); + } + } + + static function check_for_conflicts($item, $new_name, $new_slug) { + $errors = array(); + + if ($row = Database::instance() + ->select(array("name", "slug")) + ->from("items") + ->where("parent_id", $item->parent_id) + ->where("id <>", $item->id) + ->open_paren() + ->where("name", $new_name) + ->orwhere("slug", $new_slug) + ->close_paren() + ->get() + ->current()) { + if ($row->name == $new_name) { + $errors["name_conflict"] = 1; + } + if ($row->slug == $new_slug) { + $errors["slug_conflict"] = 1; + } + } + + return $errors; + } + /** * Sanitize a filename into something presentable as an item title * @param string $filename diff --git a/modules/gallery/tests/Gallery_Rest_Helper_Test.php b/modules/gallery/tests/Gallery_Rest_Helper_Test.php new file mode 100644 index 00000000..14c73248 --- /dev/null +++ b/modules/gallery/tests/Gallery_Rest_Helper_Test.php @@ -0,0 +1,243 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2009 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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_Helper_Test extends Unit_Test_Case { + public function setup() { + $this->_save = array($_GET, $_POST, $_SERVER, $_FILES); + $this->_saved_active_user = identity::active_user(); + + $this->_user = identity::create_user("access_test", "Access Test", "password"); + $key = ORM::factory("user_access_token"); + $this->_access_key = $key->access_key = md5($this->_user->name . rand()); + $key->user_id = $this->_user->id; + $key->save(); + + $root = ORM::factory("item", 1); + $this->_album = album::create($root, "album", "Test Album", rand()); + $this->_child = album::create($this->_album, "child", "Test Child Album", rand()); + + $filename = MODPATH . "gallery/tests/test.jpg"; + $rand = rand(); + $this->_photo = photo::create($this->_child, $filename, "$rand.jpg", $rand); + + $filename = MODPATH . "gallery/tests/test.jpg"; + $rand = rand(); + $this->_sibling = photo::create($this->_album, $filename, "$rand.jpg", $rand); + } + + public function teardown() { + list($_GET, $_POST, $_SERVER, $_FILES) = $this->_save; + identity::set_active_user($this->_saved_active_user); + + try { + if (!empty($this->_user)) { + $this->_user->delete(); + } + if (!empty($this->_album)) { + $this->_album->delete(); + } + } catch (Exception $e) { } + } + + public function gallery_rest_get_album_test() { + $request = (object)array("arguments" => explode("/", $this->_child->relative_url())); + + $this->assert_equal( + json_encode(array("status" => "OK", + "resource" => + array("type" => $this->_child->type, + "name" => $this->_child->name, + "path" => $this->_child->relative_url(), + "parent_path" => $this->_album->relative_url(), + "title" => $this->_child->title, + "thumb_url" => $this->_child->thumb_url(), + "thumb_size" => array("height" => $this->_child->thumb_height, + "width" => $this->_child->thumb_width), + "resize_url" => $this->_child->resize_url(), + "resize_size" => array("height" => 0, + "width" => 0), + "url" => $this->_child->file_url(), + "size" => array("height" => $this->_child->height, + "width" => $this->_child->width), + "description" => $this->_child->description, + "slug" => $this->_child->slug, + "children" => array(array( + "type" => "photo", + "has_children" => false, + "path" => $this->_photo->relative_url(), + "thumb_url" => $this->_photo->thumb_url(), + "thumb_dimensions" => array( + "width" => $this->_photo->thumb_width, + "height" => $this->_photo->thumb_height), + "has_thumb" => true, + "title" => $this->_photo->title))))), + gallery_rest::get($request)); + } + + public function gallery_rest_get_photo_test() { + $request = (object)array("arguments" => explode("/", $this->_photo->relative_url())); + + $this->assert_equal( + json_encode(array("status" => "OK", + "resource" => + array("type" => $this->_photo->type, + "name" => $this->_photo->name, + "path" => $this->_photo->relative_url(), + "parent_path" => $this->_child->relative_url(), + "title" => $this->_photo->title, + "thumb_url" => $this->_photo->thumb_url(), + "thumb_size" => array("height" => $this->_photo->thumb_height, + "width" => $this->_photo->thumb_width), + "resize_url" => $this->_photo->resize_url(), + "resize_size" => array("height" => $this->_photo->resize_height, + "width" => $this->_photo->resize_width), + "url" => $this->_photo->file_url(), + "size" => array("height" => $this->_photo->height, + "width" => $this->_photo->width), + "description" => $this->_photo->description, + "slug" => $this->_photo->slug))), + gallery_rest::get($request)); + } + + public function gallery_rest_put_album_no_path_test() { + access::allow(identity::registered_users(), "edit", $this->_child); + + identity::set_active_user($this->_user); + $request = (object)array("description" => "Updated description", + "title" => "Updated Title", + "name" => "new name"); + + $this->assert_equal(json_encode(array("status" => "ERROR", "message" => "Invalid request")), + gallery_rest::put($request)); + } + + public function gallery_rest_put_album_not_found_test() { + access::allow(identity::registered_users(), "edit", $this->_child); + + identity::set_active_user($this->_user); + $request = (object)array("arguments" => explode("/", $this->_photo->relative_url() . rand()), + "description" => "Updated description", + "title" => "Updated Title", + "name" => "new name"); + + $this->assert_equal(json_encode(array("status" => "ERROR", "message" => "Resource not found")), + gallery_rest::put($request)); + } + + public function gallery_rest_put_album_no_edit_permission_test() { + identity::set_active_user($this->_user); + $request = (object)array("arguments" => explode("/", $this->_child->relative_url()), + "description" => "Updated description", + "title" => "Updated Title", + "name" => "new name"); + + $this->assert_equal(json_encode(array("status" => "ERROR", "message" => "Resource not found")), + gallery_rest::put($request)); + } + + public function gallery_rest_put_album_rename_conflict_test() { + access::allow(identity::registered_users(), "edit", $this->_child); + identity::set_active_user($this->_user); + $request = (object)array("arguments" => explode("/", $this->_child->relative_url()), + "description" => "Updated description", + "title" => "Updated Title", + "name" => $this->_sibling->name); + + $this->assert_equal( + json_encode(array("status" => "VALIDATE_ERROR", + "fields" => array("slug" => "Duplicate Internet Address"))), + gallery_rest::put($request)); + } + + public function gallery_rest_put_album_test() { + access::allow(identity::registered_users(), "edit", $this->_child); + + identity::set_active_user($this->_user); + $request = (object)array("arguments" => explode("/", $this->_child->relative_url()), + "description" => "Updated description", + "title" => "Updated Title", + "name" => "new name"); + + $this->assert_equal(json_encode(array("status" => "OK")), gallery_rest::put($request)); + $this->_child->reload(); + $this->assert_equal("Updated description", $this->_child->description); + $this->assert_equal("Updated Title", $this->_child->title); + $this->assert_equal("new name", $this->_child->name); + } + + public function gallery_rest_put_photo_test() { + access::allow(identity::registered_users(), "edit", $this->_child); + + identity::set_active_user($this->_user); + $request = (object)array("arguments" => explode("/", $this->_photo->relative_url()), + "description" => "Updated description", + "title" => "Updated Title", + "name" => "new name"); + + $this->assert_equal(json_encode(array("status" => "OK")), gallery_rest::put($request)); + $this->_photo->reload(); + $this->assert_equal("Updated description", $this->_photo->description); + $this->assert_equal("Updated Title", $this->_photo->title); + $this->assert_equal("new name", $this->_photo->name); + } + + public function gallery_rest_delete_album_test() { + access::allow(identity::registered_users(), "edit", $this->_album); + + identity::set_active_user($this->_user); + $request = (object)array("arguments" => explode("/", $this->_child->relative_url())); + + $this->assert_equal(json_encode(array("status" => "OK", + "resource" => array( + "parent_path" => $this->_album->relative_url()))), + gallery_rest::delete($request)); + $this->_child->reload(); + $this->assert_false($this->_child->loaded); + } + + public function gallery_rest_delete_photo_test() { + access::allow(identity::registered_users(), "edit", $this->_album); + + identity::set_active_user($this->_user); + $request = (object)array("arguments" => explode("/", $this->_sibling->relative_url())); + + $this->assert_equal(json_encode(array("status" => "OK", + "resource" => array( + "parent_path" => $this->_album->relative_url()))), + gallery_rest::delete($request)); + $this->_sibling->reload(); + $this->assert_false($this->_sibling->loaded); + } + + public function gallery_rest_post_album_test() { + access::allow(identity::registered_users(), "edit", $this->_album); + + $new_path = $this->_child->relative_url() . "/new%20child"; + identity::set_active_user($this->_user); + $request = (object)array("arguments" => explode("/", $new_path)); + + $this->assert_equal(json_encode(array("status" => "OK", "path" => $new_path)), + gallery_rest::post($request)); + $album = ORM::factory("item") + ->where("relative_url_cache", $new_path) + ->find(); + $this->assert_true($album->loaded); + $this->assert_equal("new child", $album->slug); + } +} diff --git a/modules/image_block/helpers/image_block_rest.php b/modules/image_block/helpers/image_block_rest.php new file mode 100644 index 00000000..45f849b1 --- /dev/null +++ b/modules/image_block/helpers/image_block_rest.php @@ -0,0 +1,62 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2009 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ +class image_block_rest_Core { + static function get($request) { + $type = empty($request->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) + ->orderby(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) + ->orderby(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::success(array("resource" => $response_data)); + } else { + return rest::fail("No Image found"); + } + } +} diff --git a/modules/organize/css/organize.css b/modules/organize/css/organize.css index 16645c37..d8923ea7 100644 --- a/modules/organize/css/organize.css +++ b/modules/organize/css/organize.css @@ -30,9 +30,8 @@ width: 19%; } -#g-organize-album-tree .g-selected { - background-color: #eee; - border-bottom: 1px solid #999; +#g-organize-album-tree { + overflow: auto; } #g-organize-album-tree ul li { @@ -50,10 +49,6 @@ width: auto; } -.g-organize-album-text:hover { - background: #eee; -} - /******************************************************************* * Album panel styles */ @@ -77,8 +72,6 @@ } #g-organize-microthumb-panel { - background-color: #eee; - border: 1px solid #999; height: 100%; margin: 0 !important; position: relative; @@ -100,8 +93,6 @@ } .g-organize-microthumb { - background-color: #fff; - border: 1px solid #ccc; display: block; height: 100px; margin: 0; @@ -111,16 +102,7 @@ width: 110px; } -.g-organize-microthumb-grid-cell.ui-selecting, -.g-organize-microthumb-grid-cell.ui-selected { - border: 2px solid #13A; - margin: 4px; -} - .ui-selectable-helper { - background: #13A; - border: 1px dashed #00F; - opacity: 0.25; z-index: 2000 !important; } @@ -131,21 +113,13 @@ z-index: 4000; } -.g-organize-microthumb-grid-cell:hover { - border: 2px solid #13A; - margin: 4px; -} - /**************************************************************** * Controls styles */ #g-organize-controls { - background-color: #666; - color: #eee; margin: 0 !important; padding: .2em .4em; - width: 100%; } #g-organize-controls select { diff --git a/modules/organize/js/organize.js b/modules/organize/js/organize.js index afa9e17a..556aa7e7 100644 --- a/modules/organize/js/organize.js +++ b/modules/organize/js/organize.js @@ -163,7 +163,15 @@ set_handlers: function() { $("#g-organize-microthumb-panel") - .selectable({filter: ".g-organize-microthumb-grid-cell"}) + .selectable({ + filter: ".g-organize-microthumb-grid-cell", + selected: function(event, ui) { + $(ui.selected).children(".g-organize-microthumb").addClass("ui-state-highlight"); + }, + unselected: function(event, ui) { + $(ui.unselected).children(".g-organize-microthumb").removeClass("ui-state-highlight"); + } + }) .droppable($.organize.content_droppable); $(".g-organize-microthumb-grid-cell") .draggable($.organize.micro_thumb_draggable) @@ -203,7 +211,7 @@ */ show_album: function(event) { event.preventDefault(); - if ($(event.currentTarget).hasClass("g-selected")) { + if ($(event.currentTarget).hasClass("ui-state-focus")) { return; } var parent = $(event.currentTarget).parents(".g-organize-branch"); @@ -212,8 +220,8 @@ } $("#g-organize-microthumb-panel").selectable("destroy"); var id = $(event.currentTarget).attr("ref"); - $("#g-organize-album-tree .g-selected").removeClass("g-selected"); - $(".g-organize-album-text[ref=" + id + "]").addClass("g-selected"); + $(".g-organize-album-text.ui-state-focus").removeClass("ui-state-focus"); + $(".g-organize-album-text[ref=" + id + "]").addClass("ui-state-focus"); var url = $("#g-organize-microthumb-panel").attr("ref").replace("__ITEM_ID__", id).replace("__OFFSET__", 0); $.get(url, {}, function(data) { @@ -230,7 +238,7 @@ */ resort: function(column, dir) { var url = sort_order_url - .replace("__ALBUM_ID__", $("#g-organize-album-tree .g-selected").attr("ref")) + .replace("__ALBUM_ID__", $("#g-organize-album-tree .ui-state-focus").attr("ref")) .replace("__COL__", column) .replace("__DIR__", dir); $.get(url, {}, diff --git a/modules/organize/views/organize_dialog.html.php b/modules/organize/views/organize_dialog.html.php index 31f788ad..435f5ae3 100644 --- a/modules/organize/views/organize_dialog.html.php +++ b/modules/organize/views/organize_dialog.html.php @@ -15,16 +15,16 @@ </ul> </div> <div id="g-organize-detail" class="g-left ui-helper-clearfix"> - <div id="g-organize-microthumb-panel" + <div id="g-organize-microthumb-panel" class="ui-widget" ref="<?= url::site("organize/album/__ITEM_ID__/__OFFSET__") ?>"> <ul id="g-action-status" class="g-message-block"> <li class="g-info"><?= t("Drag and drop photos to re-order or move between albums") ?></li> </ul> - <ul id="g-organize-microthumb-grid"> + <ul id="g-organize-microthumb-grid" class="ui-widget-content"> <?= $micro_thumb_grid ?> </ul> </div> - <div id="g-organize-controls"> + <div id="g-organize-controls" class="ui-widget-header"> <a id="g-organize-close" href="#" ref="done" class="g-button g-right ui-corner-all ui-state-default"><?= t("Close") ?></a> <form> diff --git a/modules/organize/views/organize_thumb_grid.html.php b/modules/organize/views/organize_thumb_grid.html.php index 3ac32ce0..9a9cd819 100644 --- a/modules/organize/views/organize_thumb_grid.html.php +++ b/modules/organize/views/organize_thumb_grid.html.php @@ -1,8 +1,8 @@ <?php defined("SYSPATH") or die("No direct script access.") ?> <? foreach ($album->children(25, $offset) as $child): ?> -<li class="g-organize-microthumb-grid-cell g-left" ref="<?= $child->id ?>"> +<li class="g-organize-microthumb-grid-cell g-left ui-state-default" ref="<?= $child->id ?>"> <div id="g-organize-microthumb-<?= $child->id ?>" - class="g-organize-microthumb <?= $child->is_album() ? "g-album" : "g-photo" ?>"> + class="g-organize-microthumb <?= $child->is_album() ? "g-album" : "g-photo" ?> ui-state-active"> <?= $child->thumb_img(array("class" => "g-thumbnail", "ref" => $child->id), 90, true) ?> <span<?= $child->is_album() ? " class=\"ui-icon ui-icon-note\"" : "" ?>></span> </div> diff --git a/modules/organize/views/organize_tree.html.php b/modules/organize/views/organize_tree.html.php index c5257956..513c0625 100644 --- a/modules/organize/views/organize_tree.html.php +++ b/modules/organize/views/organize_tree.html.php @@ -3,7 +3,7 @@ ref="<?= $album->id ?>"> <span class="ui-icon ui-icon-minus"> </span> - <span class="g-organize-album-text <?= $selected && $album->id == $selected->id ? "selected" : "" ?>" + <span class="g-organize-album-text <?= $selected && $album->id == $selected->id ? "ui-state-focus" : "" ?>" ref="<?= $album->id ?>"> <?= html::clean($album->title) ?> </span> diff --git a/modules/rest/controllers/rest.php b/modules/rest/controllers/rest.php new file mode 100644 index 00000000..1289d62b --- /dev/null +++ b/modules/rest/controllers/rest.php @@ -0,0 +1,113 @@ +<?php defined("SYSPATH") or die("No direct script access.");/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2009 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ +class Rest_Controller extends Controller { + public function access_key() { + $request = (object)$this->input->get(); + if (empty($request->user) || empty($request->password)) { + print rest::forbidden("No user or password supplied"); + return; + } + + $user = identity::lookup_user_by_name($request->user); + if (empty($user)) { + print rest::forbidden("User '{$request->user}' not found"); + return; + } + + if (!identity::is_correct_password($user, $request->password)) { + print rest::forbidden("Invalid password for '{$request->user}'."); + return; + } + + $key = ORM::factory("user_access_token") + ->where("user_id", $user->id) + ->find(); + if (!$key->loaded) { + $key->user_id = $user->id; + $key->access_key = md5($user->name . rand()); + $key->save(); + Kohana::log("alert", Kohana::debug($key->as_array())); + } + print rest::success(array("token" => $key->access_key)); + } + + public function __call($function, $args) { + $request = $this->_normalize_request($args); + try { + if ($this->_set_active_user($request->access_token)) { + $handler_class = "{$function}_rest"; + $handler_method = $request->method; + + if (!method_exists($handler_class, $handler_method)) { + print rest::not_implemented("$handler_class::$handler_method is not implemented"); + return; + } + + print call_user_func(array($handler_class, $handler_method), $request); + } + } catch (Exception $e) { + print rest::internal_error($e->__toString()); + } + } + + private function _normalize_request($args=array()) { + $method = strtolower($this->input->server("REQUEST_METHOD")); + $request = new stdClass(); + foreach (array_keys($this->input->get()) as $key) { + $request->$key = $this->input->get($key); + } + if ($method != "get") { + foreach (array_keys($this->input->post()) as $key) { + $request->$key = $this->input->post($key); + } + foreach (array_keys($_FILES) as $key) { + $request->$key = $_FILES[$key]; + } + } + + $request->method = strtolower($this->input->server("HTTP_X_GALLERY_REQUEST_METHOD", $method)); + $request->access_token = $this->input->server("HTTP_X_GALLERY_REQUEST_KEY"); + $request->arguments = $args; // Let the rest handler figure out what the arguments mean + + return $request; + } + + private function _set_active_user($access_token) { + if (empty($access_token)) { + $user = identity::guest(); + } else { + $key = ORM::factory("user_access_token") + ->where("access_key", $access_token) + ->find(); + + if ($key->loaded) { + $user = identity::lookup_user($key->user_id); + if (empty($user)) { + print rest::forbidden("User not found: {$key->user_id}"); + return false;; + } + } else { + print rest::forbidden("Invalid user access token supplied: {$key->user_id}"); + return false; + } + } + identity::set_active_user($user); + return true; + } +}
\ No newline at end of file diff --git a/modules/rest/helpers/rest.php b/modules/rest/helpers/rest.php new file mode 100644 index 00000000..ad6ca7c7 --- /dev/null +++ b/modules/rest/helpers/rest.php @@ -0,0 +1,97 @@ +<?php defined("SYSPATH") or die("No direct script access.");/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2009 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ +class rest_Core { + /** + * Authorization Failure + */ + static function forbidden($log_message=null) { + return self::_format_failure_response(t("Authorization failed"), $log_message); + } + + /** + * Invalid Failure + */ + static function invalid_request($log_message=null) { + return self::_format_failure_response(t("Invalid request"), $log_message); + } + + /** + * Not Implemented + */ + static function not_implemented($log_message=null) { + return self::_format_failure_response(t("Service not implemented"), $log_message); + } + + /** + * Internal Error + */ + static function internal_error($log_message=null) { + return self::_format_failure_response(t("Internal error"), $log_message); + } + + /** + * Resource Not Found + */ + static function not_found($log_message=null) { + return self::_format_failure_response(t("Resource not found"), $log_message); + } + + /** + * Resource Not Found + */ + static function fail($log_message=null) { + return self::_format_failure_response($log_message, $log_message); + } + + /** + * Success + */ + static function success($response_data=array(), $message=null) { + $response = array("status" => "OK"); + if (!empty($message)) { + $response["message"] = (string)$message; + } + $response = array_merge($response, $response_data); + + // We don't need to save the session for this request + Session::abort_save(); + return json_encode($response); + } + + /** + * Validation Error + */ + static function validation_error($error_data) { + $response = array("status" => "VALIDATE_ERROR"); + $response = array_merge($response, array("fields" => $error_data)); + + // We don't need to save the session for this request + Session::abort_save(); + return json_encode($response); + } + + private static function _format_failure_response($message, $log_message) { + if (!empty($log_message)) { + Kohana::log("info", $log_message); + } + // We don't need to save the session for this request + Session::abort_save(); + return json_encode(array("status" => "ERROR", "message" => (string)$message)); + } +} diff --git a/modules/rest/helpers/rest_event.php b/modules/rest/helpers/rest_event.php new file mode 100644 index 00000000..ce926107 --- /dev/null +++ b/modules/rest/helpers/rest_event.php @@ -0,0 +1,75 @@ +<?php defined("SYSPATH") or die("No direct script access.");/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2009 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ +class rest_event { + /** + * Called just before a user is deleted. This will remove the user from + * the user_homes directory. + */ + static function user_before_delete($user) { + ORM::factory("user_access_token") + ->where("id", $user->id) + ->delete_all(); + } + + /** + * Called after a user has been added. Just add a remote access key + * on every add. + */ + static function user_add_form_admin_completed($user, $form) { + $key = ORM::factory("user_access_token"); + $key->user_id = $user->id; + $key->access_key = md5($user->name . rand()); + $key->save(); + } + + /** + * Called when admin is editing a user + */ + static function user_edit_form_admin($user, $form) { + self::_get_access_key_form($user, $form); + } + + /** + * Called when user is editing their own form + */ + static function user_edit_form($user, $form) { + self::_get_access_key_form($user, $form); + } + + /** + * Get the form fields for user edit + */ + static function _get_access_key_form($user, $form) { + $key = ORM::factory("user_access_token") + ->where("user_id", $user->id) + ->find(); + + if (!$key->loaded) { + $key->user_id = $user->id; + $key->access_key = md5($user->name . rand()); + $key->save(); + } + + $form->edit_user->input("user_access_token") + ->value($key->access_key) + ->readonly("readonly") + ->class("g-form-static") + ->label(t("Remote access key")); + } +} diff --git a/modules/rest/helpers/rest_installer.php b/modules/rest/helpers/rest_installer.php new file mode 100644 index 00000000..9fbc5b2e --- /dev/null +++ b/modules/rest/helpers/rest_installer.php @@ -0,0 +1,37 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2009 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ +class rest_installer { + static function install() { + Database::instance() + ->query("CREATE TABLE {user_access_tokens} ( + `id` int(9) NOT NULL auto_increment, + `user_id` int(9) NOT NULL, + `access_key` char(32) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY(`access_key`), + UNIQUE KEY(`user_id`)) + DEFAULT CHARSET=utf8;"); + module::set_version("rest", 1); + } + + static function uninstall() { + Database::instance()->query("DROP TABLE IF EXISTS {user_access_tokens}"); + } +} diff --git a/modules/rest/models/user_access_token.php b/modules/rest/models/user_access_token.php new file mode 100644 index 00000000..5669d8d1 --- /dev/null +++ b/modules/rest/models/user_access_token.php @@ -0,0 +1,21 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2009 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ +class User_Access_Token_Model extends ORM { +} diff --git a/modules/rest/module.info b/modules/rest/module.info new file mode 100644 index 00000000..5576ec81 --- /dev/null +++ b/modules/rest/module.info @@ -0,0 +1,4 @@ +name = REST Access Module +description = "The RESTful implementation/interface to Gallery3" + +version = 1 diff --git a/modules/rest/tests/Rest_Controller_Test.php b/modules/rest/tests/Rest_Controller_Test.php new file mode 100644 index 00000000..b7fbd5a3 --- /dev/null +++ b/modules/rest/tests/Rest_Controller_Test.php @@ -0,0 +1,186 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2009 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ +class Rest_Controller_Test extends Unit_Test_Case { + public function setup() { + $this->_save = array($_GET, $_POST, $_SERVER); + $this->_user = identity::create_user("access_test", "Access Test", "password"); + $key = ORM::factory("user_access_token"); + $this->_access_key = $key->access_key = md5($this->_user->name . rand()); + $key->user_id = $this->_user->id; + $key->save(); + + $root = ORM::factory("item", 1); + $this->_album = album::create($root, "album", "Test Album", rand()); + $this->_child = album::create($this->_album, "child", "Test Child Album", rand()); + + $filename = MODPATH . "gallery/tests/test.jpg"; + $rand = rand(); + $this->_photo = photo::create($this->_child, $filename, "$rand.jpg", $rand); + } + + public function teardown() { + list($_GET, $_POST, $_SERVER) = $this->_save; + + try { + if (!empty($this->_user)) { + $this->_user->delete(); + } + if (!empty($this->_album)) { + $this->_album->delete(); + } + } catch (Exception $e) { } + } + + public function rest_access_key_exists_test() { + $_SERVER["REQUEST_METHOD"] = "GET"; + $_GET["user"] = "access_test"; + $_GET["password"] = "password"; + + $this->assert_equal( + json_encode(array("status" => "OK", "token" => $this->_access_key)), + $this->_call_controller()); + } + + public function rest_access_key_generated_test() { + ORM::factory("user_access_token") + ->where("access_key", $this->_access_key) + ->delete(); + $_SERVER["REQUEST_METHOD"] = "GET"; + $_GET["user"] = "access_test"; + $_GET["password"] = "password"; + + $results = json_decode($this->_call_controller()); + + $this->assert_equal("OK", $results->status); + $this->assert_false(empty($results->token)); + } + + public function rest_access_key_no_parameters_test() { + $_SERVER["REQUEST_METHOD"] = "GET"; + + $this->assert_equal( + json_encode(array("status" => "ERROR", "message" => (string)t("Authorization failed"))), + $this->_call_controller()); + } + + public function rest_access_key_user_not_found_test() { + $_SERVER["REQUEST_METHOD"] = "POST"; + $_POST["request"] = json_encode(array("user" => "access_test2", "password" => "password")); + + $this->assert_equal( + json_encode(array("status" => "ERROR", "message" => (string)t("Authorization failed"))), + $this->_call_controller()); + } + + public function rest_access_key_invalid_password_test() { + $_SERVER["REQUEST_METHOD"] = "POST"; + + $this->assert_equal( + json_encode(array("status" => "ERROR", "message" => (string)t("Authorization failed"))), + $this->_call_controller()); + } + + public function rest_get_resource_no_request_key_test() { + $_SERVER["REQUEST_METHOD"] = "GET"; + + $this->assert_equal( + json_encode(array("status" => "OK", "message" => (string)t("Processed"), + "photo" => array("path" => $this->_photo->relative_url(), + "title" => $this->_photo->title, + "thumb_url" => $this->_photo->thumb_url(), + "description" => $this->_photo->description, + "internet_address" => $this->_photo->slug))), + $this->_call_controller("rest", explode("/", $this->_photo->relative_url()))); + } + + public function rest_get_resource_invalid_key_test() { + $_SERVER["HTTP_X_GALLERY_REQUEST_KEY"] = md5($this->_access_key); // screw up the access key; + $_SERVER["REQUEST_METHOD"] = "GET"; + + $this->assert_equal( + json_encode(array("status" => "ERROR", "message" => (string)t("Authorization failed"))), + $this->_call_controller()); + } + + public function rest_get_resource_no_user_for_key_test() { + $_SERVER["REQUEST_METHOD"] = "GET"; + $_SERVER["HTTP_X_GALLERY_REQUEST_KEY"] = $this->_access_key; + + $this->_user->delete(); + unset($this->_user); + + $this->assert_equal( + json_encode(array("status" => "ERROR", "message" => (string)t("Authorization failed"))), + $this->_call_controller("rest", explode("/", $this->_photo->relative_url()))); + } + + public function rest_get_resource_no_handler_test() { + $_SERVER["REQUEST_METHOD"] = "GET"; + $_SERVER["HTTP_X_GALLERY_REQUEST_KEY"] = $this->_access_key; + $_SERVER["HTTP_X_GALLERY_REQUEST_METHOD"] = "PUT"; + + $this->assert_equal( + json_encode(array("status" => "ERROR", "message" => (string)t("Service not implemented"))), + $this->_call_controller("rest", explode("/", $this->_photo->relative_url()))); + } + + public function rest_get_resource_test() { + $_SERVER["REQUEST_METHOD"] = "GET"; + $_SERVER["HTTP_X_GALLERY_REQUEST_KEY"] = $this->_access_key; + + $this->assert_equal( + json_encode(array("status" => "OK", "message" => (string)t("Processed"), + "photo" => array("path" => $this->_photo->relative_url(), + "title" => $this->_photo->title, + "thumb_url" => $this->_photo->thumb_url(), + "description" => $this->_photo->description, + "internet_address" => $this->_photo->slug))), + $this->_call_controller("rest", explode("/", $this->_photo->relative_url()))); + } + + private function _call_controller($method="access_key", $arg=null) { + $controller = new Rest_Controller(); + + ob_start(); + call_user_func_array(array($controller, $method), $arg); + $results = ob_get_contents(); + ob_end_clean(); + + return $results; + } +} + +class rest_rest { + static $request = null; + + static function get($request) { + self::$request = $request; + $item = ORM::factory("item") + ->where("relative_url_cache", implode("/", $request->arguments)) + ->find(); + $response["path"] = $item->relative_url(); + $response["title"] = $item->title; + $response["thumb_url"] = $item->thumb_url(); + $response["description"] = $item->description; + $response["internet_address"] = $item->slug; + return rest::success(array($item->type => $response), t("Processed")); + } + +} diff --git a/modules/tag/helpers/tag_rest.php b/modules/tag/helpers/tag_rest.php new file mode 100644 index 00000000..d62c0231 --- /dev/null +++ b/modules/tag/helpers/tag_rest.php @@ -0,0 +1,156 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2009 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ +class tag_rest_Core { + static function get($request) { + if (empty($request->arguments)) { + $tags = ORM::factory("tag") + ->select("name", "count") + ->orderby("count", "DESC"); + if (!empty($request->limit)) { + $tags->limit($request->limit); + } + if (!empty($request->offset)) { + $tags->offset($request->offset); + } + $response = array("tags" => array()); + foreach ($tags->find_all() as $row) { + $response["tags"][] = array("name" => $row->name, "count" => $row->count); + } + } else { + $path = implode("/", $request->arguments); + if (strpos($path, ",") === false) { + $item = ORM::factory("item") + ->where("relative_url_cache", $path) + ->viewable() + ->find(); + // If we didn't find it and there was only one argument, retry as a tag not a path + if ($item->loaded || count($request->arguments) != 1) { + $response = array("tags" => $item->loaded ? tag::item_tags($item) : array()); + } else { + $response = array("resources" => tag_rest::_get_items($request)); + } + } else { + $response = array("resources" => tag_rest::_get_items($request)); + } + } + + return rest::success($response); + } + + static function post($request) { + if (empty($request->arguments) || count($request->arguments) != 1 || empty($request->path)) { + return rest::invalid_request(); + } + $path = $request->path; + $tags = explode(",", $request->arguments[0]); + + $item = ORM::factory("item") + ->where("relative_url_cache", $path) + ->viewable() + ->find(); + if (!$item->loaded) { + return rest::not_found("Resource: {$path} missing."); + } + + if (!access::can("edit", $item)) { + return rest::not_found("Resource: {$path} permission denied."); + } + + foreach ($tags as $tag) { + tag::add($item, $tag); + } + return rest::success(); + } + + static function put($request) { + if (empty($request->arguments[0]) || empty($request->new_name)) { + return rest::invalid_request(); + } + + $name = $request->arguments[0]; + + $tag = ORM::factory("tag") + ->where("name", $name) + ->find(); + if (!$tag->loaded) { + return rest::not_found("Tag: {$name} not found."); + } + + $tag->name = $request->new_name; + $tag->save(); + + return rest::success(); + } + + static function delete($request) { + if (empty($request->arguments[0])) { + return rest::invalid_request(); + } + $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") + ->in("tags.name", $tags) + ->where("relative_url_cache", $request->path) + ->viewable() + ->find_all(); + } else { + $tag_list = ORM::factory("tag") + ->in("name", $tags) + ->find_all(); + } + + foreach ($tag_list as $row) { + $row->delete(); + }; + + tag::compact(); + return rest::success(); + } + + 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") + ->in("tags.name", $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); + } + + return $resources; + } +} diff --git a/modules/tag/tests/Tag_Rest_Helper_Test.php b/modules/tag/tests/Tag_Rest_Helper_Test.php new file mode 100644 index 00000000..1c550366 --- /dev/null +++ b/modules/tag/tests/Tag_Rest_Helper_Test.php @@ -0,0 +1,227 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2009 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ +class Tag_Rest_Helper_Test extends Unit_Test_Case { + public function setup() { + $this->_save = array($_GET, $_POST, $_SERVER, $_FILES); + $this->_saved_active_user = identity::active_user(); + + $this->_user = identity::create_user("access_test", "Access Test", "password"); + $key = ORM::factory("user_access_token"); + $this->_access_key = $key->access_key = md5($this->_user->name . rand()); + $key->user_id = $this->_user->id; + $key->save(); + + $root = ORM::factory("item", 1); + $this->_album = album::create($root, "album", "Test Album", rand()); + tag::add($this->_album, "albums"); + tag::add($this->_album, "A1"); + tag::add($this->_album, "T1"); + $this->_child = album::create($this->_album, "child", "Test Child Album", rand()); + tag::add($this->_child, "albums"); + tag::add($this->_child, "C1"); + tag::add($this->_child, "T1"); + + $filename = MODPATH . "gallery/tests/test.jpg"; + $rand = rand(); + $this->_photo = photo::create($this->_child, $filename, "$rand.jpg", $rand); + tag::add($this->_photo, "photos"); + tag::add($this->_photo, "P1"); + tag::add($this->_photo, "T1"); + + $filename = MODPATH . "gallery/tests/test.jpg"; + $rand = rand(); + $this->_sibling = photo::create($this->_album, $filename, "$rand.jpg", $rand); + tag::add($this->_sibling, "photos"); + tag::add($this->_sibling, "P3"); + } + + public function teardown() { + list($_GET, $_POST, $_SERVER, $_FILES) = $this->_save; + identity::set_active_user($this->_saved_active_user); + + try { + if (!empty($this->_user)) { + $this->_user->delete(); + } + if (!empty($this->_album)) { + $this->_album->delete(); + } + Database::instance()->query("TRUNCATE {tags}"); + Database::instance()->query("TRUNCATE {items_tags}"); + + } catch (Exception $e) { } + } + + public function tag_rest_get_all_test() { + $request = (object)array("arguments" => array(), "limit" => 2, "offset" => 1); + + $this->assert_equal( + json_encode(array("status" => "OK", + "tags" => array(array("name" => "albums", "count" => 2), + array("name" => "photos", "count" => 2)))), + tag_rest::get($request)); + } + + public function tag_rest_get_tags_for_item_test() { + $request = (object)array("arguments" => explode("/", $this->_photo->relative_url())); + + $this->assert_equal( + json_encode(array("status" => "OK", + "tags" => array("photos", "P1", "T1"))), + tag_rest::get($request)); + } + + public function tag_rest_get_items_test() { + $request = (object)array("arguments" => array("albums")); + + $resources = array(); + foreach (array($this->_album, $this->_child) as $resource) { + $resources[] = array("type" => $resource->type, + "has_children" => $resource->children_count() > 0, + "path" => $resource->relative_url(), + "thumb_url" => $resource->thumb_url(), + "thumb_dimensions" => array( + "width" => $resource->thumb_width, + "height" => $resource->thumb_height), + "has_thumb" => $resource->has_thumb(), + "title" => $resource->title); + + } + $this->assert_equal(json_encode(array("status" => "OK", "resources" => $resources)), + tag_rest::get($request)); + } + + public function tag_rest_add_tags_for_item_no_path_test() { + $request = (object)array("arguments" => array("new,one")); + + $this->assert_equal( + json_encode(array("status" => "ERROR", "message" => "Invalid request")), + tag_rest::post($request)); + } + + public function tag_rest_add_tags_for_item_not_found_test() { + $request = (object)array("path" => $this->_photo->relative_url() . "b", + "arguments" => array("new,one")); + $this->assert_equal( + json_encode(array("status" => "ERROR", "message" => "Resource not found")), + tag_rest::post($request)); + } + + public function tag_rest_add_tags_for_item_no_access_test() { + identity::set_active_user($this->_user); + $request = (object)array("path" => $this->_photo->relative_url(), + "arguments" => array("new,one")); + + $this->assert_equal( + json_encode(array("status" => "ERROR", "message" => "Resource not found")), + tag_rest::post($request)); + } + + public function tag_rest_add_tags_for_item_test() { + access::allow(identity::registered_users(), "edit", $this->_child); + identity::set_active_user($this->_user); + $request = (object)array("path" => $this->_photo->relative_url(), + "arguments" => array("new,one")); + + $this->assert_equal( + json_encode(array("status" => "OK")), + tag_rest::post($request)); + $request = (object)array("arguments" => explode("/", $this->_photo->relative_url())); + $this->assert_equal( + json_encode(array("status" => "OK", + "tags" => array("photos", "P1", "T1", "new", "one"))), + tag_rest::get($request)); + } + + public function tag_rest_update_tag_no_arguments_test() { + $request = (object)array("arguments" => array()); + + $this->assert_equal( + json_encode(array("status" => "ERROR", "message" => "Invalid request")), + tag_rest::put($request)); + } + + public function tag_rest_update_tag_one_arguments_test() { + $request = (object)array("arguments" => array("photos")); + + $this->assert_equal( + json_encode(array("status" => "ERROR", "message" => "Invalid request")), + tag_rest::put($request)); + + $request = (object)array("arguments" => array(), "new_name" => "valid"); + + $this->assert_equal( + json_encode(array("status" => "ERROR", "message" => "Invalid request")), + tag_rest::put($request)); + } + + public function tag_rest_update_tags_not_found_test() { + $request = (object)array("arguments" => array("not"), "new_name" => "found"); + + $this->assert_equal( + json_encode(array("status" => "ERROR", "message" => "Resource not found")), + tag_rest::put($request)); + } + + public function tag_rest_update_tags_test() { + $request = (object)array("arguments" => array("albums"), "new_name" => "new name"); + + $this->assert_equal(json_encode(array("status" => "OK")), tag_rest::put($request)); + + $request = (object)array("arguments" => array("new name")); + $resources = array(); + foreach (array($this->_album, $this->_child) as $resource) { + $resources[] = array("type" => $resource->type, + "has_children" => $resource->children_count() > 0, + "path" => $resource->relative_url(), + "thumb_url" => $resource->thumb_url(), + "thumb_dimensions" => array( + "width" => $resource->thumb_width, + "height" => $resource->thumb_height), + "has_thumb" => $resource->has_thumb(), + "title" => $resource->title); + + } + $this->assert_equal( + json_encode(array("status" => "OK", "resources" => $resources)), + tag_rest::get($request)); + } + + public function tag_rest_delete_tag_test() { + $request = (object)array("arguments" => array("T1,P1")); + + $this->assert_equal(json_encode(array("status" => "OK")), tag_rest::delete($request)); + + $request = (object)array("arguments" => array("T1,P1")); + $this->assert_equal(json_encode(array("status" => "OK", "resources" => array())), + tag_rest::get($request)); + } + + public function tag_rest_delete_tag_from_item_test() { + $request = (object)array("arguments" => array("T1,P1"), + $this->_photo->relative_url()); + + $this->assert_equal(json_encode(array("status" => "OK")), tag_rest::delete($request)); + + $request = (object)array("arguments" => explode("/", $this->_photo->relative_url())); + $this->assert_equal(json_encode(array("status" => "OK", "tags" => array("photos"))), + tag_rest::get($request)); + } +} |