diff options
-rw-r--r-- | modules/organize/controllers/organize.php | 507 | ||||
-rw-r--r-- | modules/organize/css/organize.css | 184 | ||||
-rw-r--r-- | modules/organize/helpers/organize_task.php | 131 | ||||
-rw-r--r-- | modules/organize/helpers/organize_theme.php | 3 | ||||
-rw-r--r-- | modules/organize/js/organize.js | 697 | ||||
-rw-r--r-- | modules/organize/js/organize_init.js | 29 | ||||
-rw-r--r-- | modules/organize/views/organize.html.php | 53 | ||||
-rw-r--r-- | modules/organize/views/organize_album.html.php | 17 | ||||
-rw-r--r-- | modules/organize/views/organize_button_pane.html.php | 49 | ||||
-rw-r--r-- | modules/organize/views/organize_dialog.html.php | 37 | ||||
-rw-r--r-- | modules/organize/views/organize_edit.html.php | 14 | ||||
-rw-r--r-- | modules/organize/views/organize_thumb_grid.html.php | 23 | ||||
-rw-r--r-- | modules/organize/views/organize_tree.html.php | 20 |
13 files changed, 1532 insertions, 232 deletions
diff --git a/modules/organize/controllers/organize.php b/modules/organize/controllers/organize.php index d7854c53..898be509 100644 --- a/modules/organize/controllers/organize.php +++ b/modules/organize/controllers/organize.php @@ -19,48 +19,54 @@ */ class Organize_Controller extends Controller { private static $_MICRO_THUMB_SIZE = 90; - private static $_MICRO_THUMB_PADDING = 10; + private static $_MICRO_THUMB_PADDING = 5; - function index($item_id) { + function index($item_id=1) { $item = ORM::factory("item", $item_id); $root = ($item->id == 1) ? $item : ORM::factory("item", 1); access::required("view", $item); access::required("edit", $item); - $v = new View("organize_dialog.html"); + $v = new View("organize.html"); $v->root = $root; $v->item = $item; - $v->album_tree = $this->_tree($item, $root); - $v->micro_thumb_grid = $this->_get_micro_thumb_grid($item); + $v->album_tree = $this->tree($item, $root); $v->button_pane = new View("organize_button_pane.html"); - $buttons = (object)array("left" => array(), "middle" =>array(), "right" => array(), - "item" => $item); - module::event("organize_format_button_pane", $buttons); - - $v->button_pane->buttons = $buttons; print $v; } - function content($item_id, $offset=0) { + function content($item_id) { $item = ORM::factory("item", $item_id); access::required("view", $item); access::required("edit", $item); - $v = $this->_get_micro_thumb_grid($item, $offset); - print $v->__toString(); - } + $width = $this->input->get("width"); + $height = $this->input->get("height"); + $offset = $this->input->get("offset", 0); + $thumbsize = self::$_MICRO_THUMB_SIZE + 2 * self::$_MICRO_THUMB_PADDING; + $page_size = ceil($width / $thumbsize) * ceil($height / $thumbsize); - private function _get_micro_thumb_grid($item, $offset=0) { $v = new View("organize_thumb_grid.html"); - $v->item_id = $item->id; - $v->children = $item->children(25, $offset); + $v->children = $item->children($page_size, $offset); $v->thumbsize = self::$_MICRO_THUMB_SIZE; - $v->offset = $offset + 25; + $v->padding = self::$_MICRO_THUMB_PADDING; + $v->offset = $offset; + + print json_encode(array("count" => $v->children->count(), + "data" => $v->__toString())); + } + + function header($item_id) { + $item = ORM::factory("item", $item_id); + access::required("view", $item); + access::required("edit", $item); - return $v; + print json_encode( + array("title" => p::purify($item->title), + "description" => empty($item->description) ? "" : p::purify($item->description))); } - private function _tree($item, $parent, $selected=false) { + function tree($item, $parent) { access::required("view", $item); access::required("edit", $item); @@ -69,25 +75,466 @@ class Organize_Controller extends Controller { ->orderby(array("title" => "ASC")) ->find_all(); - $v = new View("organize_tree.html"); + $v = new View("organize_album.html"); $v->album = $parent; + $v->selected = $parent->id == $item->id; + + if ($albums->count()) { + $v->album_icon = $parent->id == 1 || $v->selected ? "ui-icon-minus" : "ui-icon-plus"; + } else { + $v->album_icon = ""; + } - $v->selected = false; $v->children = ""; - $v->album_icon = "ui-icon-plus"; - if (!$selected) { - $v->selected = $parent->id == $item->id; + foreach ($albums as $album) { + $v->children .= $this->tree($item, $album); + } + return $v->__toString(); + } + + function startTask($operation, $id) { + access::verify_csrf(); + $items = $this->input->post("item"); + + $item = ORM::factory("item", $id); + access::required("view", $item); + access::required("edit", $item); + + $definition = $this->_getOperationDefinition($item, $operation); + + $task_def = Task_Definition::factory() + ->callback("organize_task::run") + ->description($definition["description"]) + ->name($definition["name"]); + $task = task::create($task_def, array("items" => $items, "position" => 0, "target" => $id, + "type" => $definition["type"], + "batch" => ceil(count($items) * .1))); + // @todo If there is only one item then call task_run($task->id); Maybe even change js so + // we can call finish as well. + batch::start(); + print json_encode( + array("result" => "started", + "runningMsg" => $definition["runningMsg"], + "pauseMsg" => "<div class=\"gWarning\">{$definition['pauseMsg']}</div>", + "resumeMsg" => "<div class=\"gWarning\">{$definition['resumeMsg']}</div>", + "task" => array("id" => $task->id, + "percent_complete" => $task->percent_complete, + "type" => $task->get("type"), + "status" => $task->status, + "state" => $task->state, + "done" => $task->done))); + } + + function runTask($task_id) { + access::verify_csrf(); + + $task = task::run($task_id); + if (!$task->loaded || $task->owner_id != user::active()->id) { + access::forbidden(); + } + + print json_encode(array("result" => $task->done ? $task->state : "in_progress", + "task" => array("id" => $task->id, + "percent_complete" => $task->percent_complete, + "type" => $task->get("type"), + "post_process" => $task->get("post_process"), + "status" => $task->status, + "state" => $task->state, + "done" => $task->done))); + } + + function finishTask($task_id) { + access::verify_csrf(); + + $task = ORM::factory("task", $task_id); + if (!$task->loaded || $task->owner_id != user::active()->id) { + access::forbidden(); + } - if ($albums->count() && ($parent->id == 1 || $v->selected) ) { - $v->album_icon = "ui-icon-minus"; + if ($task->done) { + $item = ORM::factory("item", (int)$task->get("target")); + $type = $task->get("type"); + switch ($type) { + case "albumCover": + $task->status = t("Album cover set for '%album'", array("album" => $item->title)); + break; + case "delete": + $task->status = t("Selection deleted"); + break; + case "move": + $task->status = t("Move to '%album' completed", array("album" => $item->title)); + break; + case "rearrange": + try { + $item->sort_column = "weight"; + $item->save(); + $task->status = t("Rearrange for '%album' completed", array("album" => $item->title)); + } catch (Exception $e) { + $task->state = "error"; + $task->status = $e->getMessage(); + } + break; + case "rotateCcw": + case "rotateCw": + $task->status = t("Rotation completed"); + break; } + $task->save(); + } - foreach ($albums as $album) { - $v->children .= $this->_tree($item, $album, $v->selected); + batch::stop(); + print json_encode(array("result" => "success", + "task" => array( + "id" => $task->id, + "percent_complete" => $task->percent_complete, + "status" => $task->status, + "state" => $task->state, + "done" => $task->done))); + } + + function cancelTask($task_id) { + access::verify_csrf(); + + $task = ORM::factory("task", $task_id); + if (!$task->loaded || $task->owner_id != user::active()->id) { + access::forbidden(); + } + + if (!$task->done) { + $task->done = 1; + $task->state = "cancelled"; + $type = $task->get("type"); + switch ($type) { + case "move": + $task->status = t("Move to album was cancelled prior to completion"); + break; + case "rearrange": + $task->status = t("Rearrange album was cancelled prior to completion"); + case "rotateCcw": + case "rotateCw": + $task->status = t("Rotation was cancelled prior to completion"); + break; } + $task->save(); } - return $v->__toString(); + + batch::stop(); + print json_encode(array("result" => "success", + "task" => array( + "id" => $task->id, + "percent_complete" => $task->percent_complete, + "status" => $task->status, + "state" => $task->state, + "done" => $task->done))); + } + + function editForm() { + $event_parms = new stdClass(); + $event_parms->panes = array(); + $event_parms->itemids = $this->input->get("item"); + + // The following code should be done more dynamically i.e. use the event mechanism + if (count($event_parms->itemids) == 1) { + $item = ORM::factory("item") + ->in("id", $event_parms->itemids[0]) + ->find(); + + access::required("view", $item); + access::required("edit", $item); + + $event_parms->panes[] = array( + "label" => $item->is_album() ? t("Edit Album") : t("Edit Photo"), + "content" => organize::get_general_edit_form($item)); + + if ($item->is_album()) { + $event_parms->panes[] = array("label" => t("Sort Order"), + "content" => organize::get_sort_edit_form($item)); + } + } + + $event_parms->panes[] = array("label" => t("Manage Tags"), + "content" => organize::get_tag_form($event_parms->itemids)); + + $v = new View("organize_edit.html"); + $v->panes = $event_parms->panes; + print $v->render(); + } + + // Handlers for the album/photo edit. Probably should be in modules/gallery + public function general() { + access::verify_csrf(); + + $itemids = $this->input->post("item"); + $item = ORM::factory("item") + ->in("id", $itemids[0]) + ->find(); + access::required("view", $item); + access::required("edit", $item); + + $form = organize::get_general_edit_form($item); + if ($form->validate()) { + $orig = clone $item; + $item->title = $form->title->value; + $item->description = $form->description->value; + $item->rename($form->dirname->value); + $item->save(); + + if ($item->is_album()) { + log::success("content", "Updated album", "<a href=\"albums/$item->id\">view</a>"); + $message = t("Saved album %album_title", array("album_title" => p::purify($item->title))); + } else { + log::success("content", "Updated photo", "<a href=\"photos/$item->id\">view</a>"); + $message = t("Saved photo %photo_title", array("photo_title" => p::purify($item->title))); + } + print json_encode(array("form" => $form->__toString(), "message" => $message)); + } else { + print json_encode(array("form" => $form->__toString())); + } + } + + public function reset_general() { + $itemids = Input::instance()->get("item"); + $item = ORM::factory("item") + ->in("id", $itemids[0]) + ->find(); + access::required("view", $item); + access::required("edit", $item); + + print organize::get_general_edit_form($item); + } + + public function sort() { + access::verify_csrf(); + + $itemids = $this->input->post("item"); + $item = ORM::factory("item") + ->in("id", $itemids[0]) + ->find(); + access::required("view", $item); + access::required("edit", $item); + + $form = organize::get_sort_edit_form($item); + if ($form->validate()) { + $orig = clone $item; + $item->sort_column = $form->column->value; + $item->sort_order = $form->direction->value; + $item->save(); + + log::success("content", "Updated album", "<a href=\"albums/$item->id\">view</a>"); + $message = t("Saved album %album_title", array("album_title" => p::purify($item->title))); + print json_encode(array("form" => $form->__toString(), "message" => $message)); + } else { + print json_encode(array("form" => $form->__toString())); + } + } + + public function reset_sort() { + $itemids = Input::instance()->get("item"); + $item = ORM::factory("item") + ->in("id", $itemids[0]) + ->find(); + access::required("view", $item); + access::required("edit", $item); + + print organize::get_sort_edit_form($item); + } + + public function edit_tags() { + access::verify_csrf(); + + $itemids = explode("|", $this->input->post("item")); + $form = organize::get_tag_form($itemids); + $old_tags = $form->tags->value; + if ($form->validate()) { + + $old_tags = preg_split("/[;,\s]+/", $old_tags); + sort($old_tags); + $new_tags = preg_split("/[;,\s]+/", $form->tags->value); + sort($new_tags); + + $HIGH_VALUE_STRING = "\256"; + for ($old_index = $new_index = 0;;) { + $old_tag = $old_index >= count($old_tags) ? $HIGH_VALUE_STRING : $old_tags[$old_index]; + $new_tag = $new_index >= count($new_tags) ? $HIGH_VALUE_STRING : $new_tags[$new_index]; + if ($old_tag == $HIGH_VALUE_STRING && $new_tag == $HIGH_VALUE_STRING) { + break; + } + $matches = array(); + $old_star = false; + if (preg_match("/(.*)(\*)$/", $old_tag, $matches)) { + $old_star = true; + $old_tag = $matches[1]; + } + $new_star = false; + if (preg_match("/(.*)(\*)$/", $new_tag, $matches)) { + $new_star = true; + $new_tag = $matches[1]; + } + if ($old_tag > $new_tag) { + // Its missing in the old list so add it + $this->_add_tag($new_tag, $itemids); + $new_index++; + } else if ($old_tag < $new_tag) { + // Its missing in the new list so its been removed + $this->_delete_tag($old_tag, $itemids); + $old_index++; + } else { + if ($old_star && !$new_star) { + // User wants tag to apply to all items, originally only on some of selected + $this->_update_tag($old_tag, $itemids); + } // Not changed ignore + $old_index++; + $new_index++; + } + } + } + print json_encode(array("form" => $form->__toString(), "message" => t("Tags updated"))); } + public function reset_edit_tags() { + $itemids = $this->input->get("item"); + + print organize::get_tag_form($itemids); + } + + private function _add_tag($new_tag, $itemids) { + // Super lame security stopgap. This code is going to get rewritten anyway. + foreach ($itemids as $item_id) { + $item = ORM::factory("item", $item_id); + access::required("view", $item); + access::required("edit", $item); + } + $tag = ORM::factory("tag") + ->where("name", $new_tag) + ->find(); + if ($tag->loaded) { + $tag->count += count($itemids); + } else { + $tag->name = $new_tag; + $tag->count = count($itemids); + } + $tag->save(); + + $db = Database::instance(); + foreach ($itemids as $item_id) { + $db->query("INSERT INTO {items_tags} SET item_id = $item_id, tag_id = {$tag->id};"); + } + } + + private function _delete_tag($new_tag, $itemids) { + // Super lame security stopgap. This code is going to get rewritten anyway. + foreach ($itemids as $item_id) { + $item = ORM::factory("item", $item_id); + access::required("view", $item); + access::required("edit", $item); + } + + $tag = ORM::factory("tag") + ->where("name", $new_tag) + ->find(); + $tag->count -= count($itemids); + if ($tag->count > 0) { + $tag->save(); + } else { + $tag->delete(); + } + + $ids = implode(", ", $itemids); + Database::instance()->query( + "DELETE FROM {items_tags} WHERE tag_id = {$tag->id} AND item_id IN ($ids);"); + } + + private function _update_tag($new_tag, $itemids) { + // Super lame security stopgap. This code is going to get rewritten anyway. + foreach ($itemids as $item_id) { + $item = ORM::factory("item", $item_id); + access::required("view", $item); + access::required("edit", $item); + } + + $tag = ORM::factory("tag") + ->where("name", $new_tag) + ->find(); + + $db = Database::instance(); + $ids = implode(", ", $itemids); + $result = $db->query( + "SELECT item_id FROM {items_tags} + WHERE tag_id = {$tag->id} + AND item_id IN ($ids)"); + + $add_items = array_fill_keys($itemids, 1); + foreach($result as $row) { + unset($add_items[$row->item_id]); + } + $add_items = array_keys($add_items); + $tag->count += count($add_items); + $tag->save(); + foreach ($add_items as $item_id) { + $db->query("INSERT INTO {items_tags} SET item_id = $item_id, tag_id = {$tag->id};"); + } + } + + private function _getOperationDefinition($item, $operation) { + switch ($operation) { + case "move": + return array("description" => + t("Move albums and photos to '%name'", array("name" => $item->title)), + "name" => t("Move to '%name'", array("name" => $item->title)), + "type" => "move", + "runningMsg" => t("Move in progress"), + "pauseMsg" => t("The move operation was paused"), + "resumeMsg" => t("The move operation was resumed")); + break; + + case "rearrange": + return array("description" => t("Rearrange the order of albums and photos"), + "name" => t("Rearrange: %name", array("name" => $item->title)), + "type" => "rearrange", + "runningMsg" => t("Rearrange in progress"), + "pauseMsg" => t("The rearrange operation was paused"), + "resumeMsg" => t("The rearrange operation was resumed")); + break; + + case "rotateCcw": + return array("description" => t("Rotate the selected photos counter clockwise"), + "name" => t("Rotate images in %name", array("name" => $item->title)), + "type" => "rotateCcw", + "runningMsg" => t("Rotate Counter Clockwise in progress"), + "pauseMsg" => t("The rotate operation was paused"), + "resumeMsg" => t("The rotate operation was resumed")); + break; + + case "rotateCw": + return array("description" => t("Rotate the selected photos clockwise"), + "name" => t("Rotate images in %name", array("name" => $item->title)), + "type" => "rotateCw", + "runningMsg" => t("Rotate Clockwise in progress"), + "pauseMsg" => t("The rotate operation was paused"), + "resumeMsg" => t("The rotate operation was resumed")); + break; + + case "delete": + return array("description" => t("Delete selected photos / albums"), + "name" => t("Delete images in %name", array("name" => $item->title)), + "type" => "delete", + "runningMsg" => t("Delete images in progress"), + "pauseMsg" => t("The delete operation was paused"), + "resumeMsg" => t("The delete operation was resumed")); + break; + + case "albumCover": + return array("description" => t("Reset Album Cover"), + "name" => t("Reset Album cover for %name", array("name" => $item->title)), + "type" => "albumCover", + "runningMsg" => t("Reset Album Cover in progress"), + "pauseMsg" => t("Reset album cover was paused"), + "resumeMsg" => t("Reset album cover was resumed")); + break; + + default: + throw new Exception("Operation '$operation' is not implmented"); + } + } } diff --git a/modules/organize/css/organize.css b/modules/organize/css/organize.css index 4568a707..e58cd5a5 100644 --- a/modules/organize/css/organize.css +++ b/modules/organize/css/organize.css @@ -1,44 +1,41 @@ +/* @todo move to theme css */ /******************************************************************* * Dialog wide stylings */ -#gOrganizeDialog { - text-align: left; -} - -#gOrganize { - overflow: hidden; +#gMessage { + margin-bottom: .4em; } -#gOrganize .yui-u { - width: 75%; +#gMessage .gInfo { + background-color: transparent; + background-image: none; + padding-left: .4em; } -#gOrganize .yui-gf .first { - width: 25%; +#gOrganizeProgressDialog { + text-align: left; } -#gOrganize .yui-gf #gMessage { - margin-bottom: .4em; - width: 75%; +#gDialog .yui-gf div.first { + width: 20%; } -#gMessage .gInfo { - font-weight: bold; - padding-left: 2em; +#gDialog .yui-gf .yui-u { + width: 80%; } - /******************************************************************* * Album Tree styling */ #gOrganizeTreeContainer { - overflow: auto; + overflow-y: auto; margin: 0 !important; padding: 0 !important; } -#gOrganizeTreeContainer ul ul li { - padding-left: 1.2em; +#gOrganizeAlbumDescription { + height: 2em; + overflow-y: auto; } .gBranchSelected { @@ -48,21 +45,37 @@ padding: .3em 0; } +.gBranchDroppable { + border: 1px dotted; +} + +.gBranchText { + cursor: pointer; + width: auto; +} + .gBranchCollapsed { display: none; } -.gOrganizeBranch span { - cursor: pointer; +.gBranchEmpty { + visibility: hidden; } -.gBranchText { - cursor: pointer; - width: auto; +#gOrganizeTreeContainer ul ul li { + padding-left: 1.2em; } + /******************************************************************* * Album Panel Styles */ + +#gMicroThumbUnselectAll, +#gMicroThumbSelectAll { + font-size: 1em; + font-weight: bold; +} + #gMicroThumbPanel { margin: 0 !important; padding: 0 !important; @@ -70,27 +83,33 @@ border: 1px solid #999 !important; border-top: none !important; border-left: none !important; + margin-left: -1em !important; overflow-x: hidden; overflow-y: auto; } #gMicroThumbGrid { + padding: .5em; } .gMicroThumbContainer { -// padding: 0 .5em; -// opacity: .4; -} - -.gMicroThumb { display: block; float: left; -// font-size: .7em; + font-size: .7em; height: 9em; margin-bottom: 1em; margin-left: 1em; - text-align: center; + opacity: .4; + padding: 0 .5em; +} + +.gMicroThumb { + height: 9em; width: 9em; + background-color: #fff; + display: block; + float: left; + text-align: center; } #gMicroThumbPanel #gMicroThumbGrid .gAlbum { @@ -101,12 +120,35 @@ opacity: 1; } +.gMicroThumbContainer.ui-selected { + opacity: 1; +} + +#gDragHelper .gMicroThumbGrid { + background-color: transparent; + padding: 0; + overflow: visible; +} + +#gDragHelper .gMicroThumbContainer { + display: block; + margin: 0; + padding: 0; +} + +#gDragHelper .gMicroThumb { + background-color: transparent; + height: auto; + width: auto; +} + + /**************************************************************** * Organize Edit Drawer styling */ #gOrganizeEditDrawer { background-color: #13A; - width: 100% !important; + width: 90%; } #gOrganizeEditDrawerPanel { @@ -162,3 +204,79 @@ height: 30px; width: 15px; } + +#gOrganizeFormButtons { + bottom: 0.5em; +} + +#gOrganizeFormButtons .submit { + display: inline; + float: none; + left: 0.5em; + position: relative; +} + +/* yui-u gives 80% width, but then we wrap so do it ourselves */ +#gOrganizeEditForm { + float: right; + width: 79%; + // height: 100px; +} + +#gOrganizeFormThumbs { + overflow: hidden; +} + +#gOrganizeFormThumbs div { + margin: 0; + text-align: center; + background: transparent none repeat scroll 0 0; +} + +#gOrganizeFormThumbs .gMicroThumbContainer { + display: block; + float: left; + opacity: 1; + position: absolute; +} + +/**************************************************************** + * Organize Edit From tabs styling + */ +#gOrganizeEditForm.ui-tabs .ui-tabs-hide { + display: block !important; + left: -10000px; + position: absolute; +} + +#gOrganizeEditForm.ui-widget { + font-size: .75em; +} + +.gOrganizeEditPane { + height: 135px; + overflow-y: auto; +} + +.textbox, +.textarea { + border: 1px solid #e8e8e8; + border-top-color: #ccc; + border-left-color: #ccc; + color: #333; + width: 100%; +} + +.textarea { + height: 6em; +} + +.textbox { + height: 1.3em; + width: 50% +} + +.gTagGroup { + float:left; + margin: .5em; +} diff --git a/modules/organize/helpers/organize_task.php b/modules/organize/helpers/organize_task.php new file mode 100644 index 00000000..dc474818 --- /dev/null +++ b/modules/organize/helpers/organize_task.php @@ -0,0 +1,131 @@ +<?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 organize_task_Core { + static function available_tasks() { + // Return empty array so nothing appears in the maintenance screen + return array(); + } + + static function run($task) { + $context = unserialize($task->context); + $taskType = $context["type"]; + + try { + $target = ORM::factory("item", $context["target"]); + $total = count($context["items"]); + $stop = min($total - $context["position"], $context["batch"]); + $context["post_process"] = array(); + for ($offset = 0; $offset < $stop; $offset++) { + $current_id = $context["position"] + $offset; + $id = $context["items"][$current_id]; + switch ($taskType) { + case "move": + $source = ORM::factory("item", $id); + access::required("view", $source); + access::required("view", $target); + access::required("edit", $source); + access::required("edit", $target); + + item::move($source, $target); + break; + + case "rearrange": + $item = ORM::factory("item", $id); + access::required("view", $item); + access::required("edit", $item); + + Database::instance() + ->query("Update {items} set weight = {$context["position"]} where id=$id;"); + break; + + case "rotateCcw": + case "rotateCw": + $item = ORM::factory("item", $id); + access::required("view", $item); + access::required("edit", $item); + + if ($item->is_photo()) { + $context["post_process"]["reload"][] = + self::_do_rotation($item, $taskType == "rotateCcw" ? -90 : 90); + } + break; + + case "albumCover": + $item = ORM::factory("item", $id); + access::required("view", $item); + access::required("view", $item->parent()); + access::required("edit", $item->parent()); + + item::make_album_cover($item); + break; + + case "delete": + $item = ORM::factory("item", $id); + access::required("view", $item); + access::required("edit", $item); + + $item->delete(); + $context["post_process"]["remove"][] = array("id" => $id); + break; + + default: + throw new Exception("Task '$taskType' is not implemented"); + } + } + $context["position"] += $stop; + $task->state = "success"; + } catch(Exception $e) { + $task->status = $e->getMessage(); + $task->state = "error"; + $task->save(); + throw $e; + } + $task->context = serialize($context); + $total = count($context["items"]); + $task->percent_complete = $context["position"] / (float)$total * 100; + $task->done = $context["position"] == $total || $task->state == "error"; + } + + private static function _do_rotation($item, $degrees) { + // This code is copied from Quick_Controller::rotate + graphics::rotate($item->file_path(), $item->file_path(), array("degrees" => $degrees)); + + list($item->width, $item->height) = getimagesize($item->file_path()); + $item->resize_dirty= 1; + $item->thumb_dirty= 1; + $item->save(); + + graphics::generate($item); + + $parent = $item->parent(); + if ($parent->album_cover_item_id == $item->id) { + copy($item->thumb_path(), $parent->thumb_path()); + $parent->thumb_width = $item->thumb_width; + $parent->thumb_height = $item->thumb_height; + $parent->save(); + } + list ($height, $width) = $item->scale_dimensions(90); + $margin_top = (90 - $height) / 20; + + return array("src" => $item->thumb_url() . "?rnd=" . rand(), + "id" => $item->id, + "marginTop" => "{$margin_top}em", "width" => $width, "height" => $height); + } +}
\ No newline at end of file diff --git a/modules/organize/helpers/organize_theme.php b/modules/organize/helpers/organize_theme.php index f01ab88b..e4feba2b 100644 --- a/modules/organize/helpers/organize_theme.php +++ b/modules/organize/helpers/organize_theme.php @@ -19,7 +19,8 @@ */ class organize_theme { static function head($theme) { - //$theme->script("organize_init.js"); + // @tdo remove the addition css and organize.js (just here to test) + $theme->script("organize_init.js"); $theme->script("organize.js"); $theme->css("organize.css"); } diff --git a/modules/organize/js/organize.js b/modules/organize/js/organize.js index e84afd03..12d8a5b5 100644 --- a/modules/organize/js/organize.js +++ b/modules/organize/js/organize.js @@ -1,116 +1,621 @@ -(function($) { - $.fn.organize = function(options) { - var size = $.getViewportSize(); - var height = size.height() - 100; // Leave 50 pixels on the top and bottom of the dialog - var width = size.width() - 100; // Leave 50 pixels on the left and right of the dialog - var opts = $.extend({}, $.fn.organize.defaults, {width: width, height: height}, options); - return this.each(function() { - $(this).click(function(event) { - var href = event.target.href; - var size = $.getViewportSize(); - - $("body").append('<div id="gOrganizeDialog"></div>'); - - $("#gOrganizeDialog").dialog(opts); - // Pass the approx height and width of the thumb grid to optimize thumb retrieval - $.get(href, _init); - return false; +/* + * @todo Trap resize of dialog and resize the child areas (tree, grid and edit form) + */ +var url; +var paused = false; +var task = null; +var transitItems = []; +var heightMicroThumbPanel; + +// ************************************************************************** +// JQuery UI Widgets +// Draggable +var draggable = { + handle: ".gMicroThumbContainer.ui-selected", + revert: true, + zindex: 2000, + distance: 10, + helper: function(event, ui) { + if (!$(event.currentTarget).hasClass("ui-selected")) { + $(event.currentTarget).addClass("ui-selected"); + setDrawerButtonState(); + } + $("#gMicroThumbPanel").append("<div id=\"gDragHelper\"><ul></ul></div>"); + var beginTop = event.pageY; + var beginLeft = event.pageX; + var zindex = $(".gMicroThumbContainer").draggable("option", "zindex"); + $("#gDragHelper").css('top', event.pageY - 22.5); + $("#gDragHelper").css('left', event.pageX + 22.5); + var placeHolder = $(this).clone(); + $(placeHolder).attr("id", "gPlaceHolder"); + $(placeHolder).css("visibility", "hidden"); + $(placeHolder).removeClass("ui-selected"); + $(placeHolder).removeClass("ui-draggable"); + $(this).after(placeHolder); + + $("li.ui-selected").each(function(i) { + var clone = $(this).clone(); + $(clone).attr("id", "drag_clone_" + $(this).attr("ref")); + $("#gDragHelper ul").append(clone); + $(clone).css("position", "absolute"); + $(clone).css("top", beginTop); + $(clone).css("left", beginLeft); + $(clone).css("z-index", zindex--); + $(this).hide(); + + var children = $(clone).find(".gMicroThumb .gThumbnail"); + var width = new String(children.css("width")).replace(/[^0-9]/g,"") * .5; + var height = new String(children.css("height")).replace(/[^0-9]/g,"") * .5; + var marginTop = new String(children.css("margin-top")).replace(/[^\.0-9]/g,"") * .5; + children.attr("width", width); + children.attr("height", height); + children.css("margin-top", marginTop); + if (i < 9) { + beginTop -= 5; + beginLeft += 5; + } + }); + return $("#gDragHelper"); + }, + stop: function(event, ui) { + $("#gDragHelper li").each(function(i) { + $("#thumb_" + $(this).attr("ref")).show(); + }); + $(".gMicroThumbContainer.ui-selected").css("z-index", null); + $("#gDragHelper").remove(); + $("#gPlaceHolder").remove(); + } +}; + +// Thumbnail Grid Droppable +var thumbDroppable = { + tolerance: "pointer", + over: function(event, ui) { + $("#gPlaceHolder").show(); + }, + out: function(event, ui) { + $("#gPlaceHolder").hide(); + }, + drop: function(event, ui) { + $("#gDragHelper").hide(); + $("#gPlaceHolder").hide(); + var newOrder = ""; + $("#gMicroThumbGrid .gMicroThumbContainer").each(function(i) { + if ($(this).attr("id") == "gPlaceHolder") { + $("#gDragHelper li").each(function(i) { + newOrder += "&item[]=" + $(this).attr("ref"); + }); + } else if ($(this).css("display") != "none") { + newOrder += "&item[]=" + $(this).attr("ref"); + } else { + // If its not displayed then its one of the ones being moved so ignore. + } + }); + $("#gDragHelper li").each(function(i) { + $("#gPlaceHolder").before($("#thumb_" + $(this).attr("ref")).show()); + }); + $.ajax({ + data: newOrder, + dataType: "json", + success: operationCallback, + type: "POST", + url: get_organize_url("organize/startTask/rearrange", {item_id: item_id}) + }); + } +}; + +// Album Tree Droppable +var treeDroppable = { + tolerance: "pointer", + greedy: true, + hoverClass: "gBranchDroppable", + drop: function(event, ui) { + $("#gDragHelper").hide(); + var targetItemId = $(this).attr("ref"); + if ($(this).hasClass("gBranchSelected")) { + $("#gMessage").empty().append(INVALID_DROP_TARGET); + ui.draggable.trigger("stop", event); + return false; + } + var postData = serializeItemIds("#gDragHelper li"); + var okToMove = true; + $("#gDragHelper li").each(function(i) { + okToMove &= targetItemId != $(this).attr("ref"); + }); + if (!okToMove) { + $("#gMessage").empty().append(INVALID_DROP_TARGET); + ui.draggable.trigger("stop", event); + return false; + } + $("#gDragHelper li").each(function(i) { + $("#thumb_" + $(this).attr("ref")).remove(); + }); + $.ajax({ + data: postData, + dataType: "json", + success: operationCallback, + type: "POST", + url: get_organize_url("organize/startTask/move", {item_id: targetItemId}) + }); + return true; + } +}; + +// Selectable +var selectable = { + filter: ".gMicroThumbContainer", + selected: function(event, ui) { + setDrawerButtonState(); + }, + unselected: function(event, ui) { + setDrawerButtonState(); + }, + stop: function(event, ui) { + getEditForm(); + } +}; + +// ************************************************************************** +// Event Handlers +// MicroThumbContainer mouseup +var onMicroThumbContainerMouseup = function(event) { + // For simplicity always remove the ui-selected class. If it was unselected + // it will get added back + $(this).toggleClass("ui-selected"); + + setDrawerButtonState(); + if ($("#gMicroThumbGrid li.ui-selected").length > 0) { + getEditForm(); + } +}; + +// MicroThumbContainer mousemove +var onMicroThumbContainerMousemove = function(event) { + if ($("#gDragHelper").length > 0 && $(this).attr("id") != "gPlaceHolder") { + if (event.pageX < $(this).offset().left + $(this).width() / 2) { + $(this).before($("#gPlaceHolder")); + } else { + $(this).after($("#gPlaceHolder")); + } + var container = $("#gMicroThumbPanel").get(0); + var scrollHeight = container.scrollHeight; + var scrollTop = container.scrollTop; + var height = $(container).height(); + if (event.pageY > height + scrollTop) { + container.scrollTop = this.offsetTop; + } else if (event.pageY < scrollTop) { + container.scrollTop -= height; + } + } +}; + +// Handle click events on the buttons on the drawer handle +function drawerHandleButtonsClick(event) { + event.preventDefault(); + if (!$(this).attr("disabled")) { + var operation = $(this).attr("ref"); + switch (operation) { + case "edit": + case "close": + $("#gOrganizeEditDrawerPanel").animate( + {"height": "toggle", "display": "block"}, + {duration: "fast", + complete: function() { + setSelectedThumbs(); + if (operation == "close") { + $("#gOrganizeEditHandleButtonsLeft a[ref='edit']").css("display", "inline-block"); + $("#gOrganizeEditHandleButtonsLeft a[ref='close']").css("display", "none"); + $("#gOrganizeEditHandleButtonsMiddle a").css("display", "none"); + } else { + $("#gOrganizeEditHandleButtonsLeft a[ref='edit']").css("display", "none"); + $("#gOrganizeEditHandleButtonsLeft a[ref='close']").css("display", "inline-block"); + $("#gOrganizeEditHandleButtonsMiddle a").css("display", "inline-block"); + } + }, + step: function() { + $("#gMicroThumbPanel").height(heightMicroThumbPanel - $(this).height()); + } + }); + break; + case "select-all": + $("#gMicroThumbGrid li").addClass("ui-selected"); + $("#gMicroThumbSelectAll").hide(); + $("#gMicroThumbUnselectAll").show(); + setDrawerButtonState(); + getEditForm(); + break; + case "unselect-all": + $("#gMicroThumbGrid li").removeClass("ui-selected"); + $("#gMicroThumbSelectAll").show(); + $("#gMicroThumbUnselectAll").hide(); + setDrawerButtonState(); + break; + case "done": + $("#gDialog").dialog("close"); + break; + case "submit": + var currentTab = $("#gOrganizeEditForm").tabs("option", "selected"); + var form = $("#pane-"+currentTab+" form"); + var url = $(form).attr("action") + .replace("__FUNCTION__", $(form).attr("ref")); + $.ajax({ + data: $(form).serialize(), + dataType: "json", + success: function (data, textStatus) { + $("#pane-"+currentTab).children("form").replaceWith(data.form); + if (data.message) { + $("#gMessage").empty().append("<div class='gSuccess'>" + data.message + "</div>"); + } + }, + type: "POST", + url: url + }); + break; + case "reset": + currentTab = $("#gOrganizeEditForm").tabs("option", "selected"); + form = $("#pane-"+currentTab+" form"); + $.ajax({ + data: serializeItemIds("#gMicroThumbPanel li.ui-selected"), + dataType: "html", + success: function (data, textStatus) { + $("#pane-"+currentTab + " form").replaceWith(data); + }, + type: "GET", + url: $(form).attr("action").replace("__FUNCTION__", "reset_" + $(form).attr("ref")) }); + break; + case "delete": + if (!confirm(CONFIRM_DELETE)) { + break; + } + default: + $.ajax({ + data: serializeItemIds("#gMicroThumbPanel li.ui-selected"), + dataType: "json", + success: operationCallback, + type: "POST", + url: get_organize_url("organize/startTask/" + operation, {item_id: item_id}) + }); + break; + } + } +}; + +// ************************************************************************** +// AJAX Callbacks +// MicroThumbContainer click +var getMicroThumbsCallback = function(json, textStatus) { + if (json.count > 0) { + $("#gMicroThumbGrid").append(json.data); + retrieveMicroThumbs(); + $(".gMicroThumbContainer").mouseup(onMicroThumbContainerMouseup); + $(".gMicroThumbContainer").mousemove(onMicroThumbContainerMousemove); + $(".gMicroThumbContainer").draggable(draggable); + } +}; + +var operationCallback = function (data, textStatus) { + var done = false; + if (!paused) { + createProgressDialog(data.runningMsg); + task = data.task; + task.pauseMsg = data.pauseMsg; + task.resumeMsg = data.resumeMsg; + done = data.task.done; + } + $(".gMicroThumbContainer").draggable("disable"); + paused = false; + while (!done && !paused) { + $.ajax({async: false, + success: function(data, textStatus) { + $(".gProgressBar").progressbar("value", data.task.percent_complete); + done = data.task.done; + if (data.task.post_process.reload) { + $.each(data.task.post_process.reload, function() { + var selector = "#gMicroThumb-" + this.id + " img"; + $(selector).attr("height", this.height); + $(selector).attr("width", this.width); + $(selector).attr("src", this.src); + $(selector).css("margin-top", this.marginTop); + }); + } + if (data.task.post_process.remove) { + $.each(data.task.post_process.remove, function() { + $("#thumb_" + this.id).remove(); + }); + } + }, + error: function(XMLHttpRequest, textStatus, errorThrown) { + paused = true; + displayAjaxError(XMLHttpRequest.responseText); + }, + dataType: "json", + type: "POST", + url: get_organize_url("organize/runTask", {task_id: task.id}) }); - }; + } + if (!paused) { + $("#gOrganizeProgressDialog").dialog("destroy").remove(); + $.ajax({async: false, + success: function(data, textStatus) { + setDrawerButtonState(); + task = null; + $("#gMessage").empty().append("<div class='gSuccess'>" + data.task.status + "</div>"); + }, + dataType: "json", + type: "POST", + url: get_organize_url("organize/finishTask", {task_id: task.id}) + }); + } + $(".gMicroThumbContainer").draggable("enable"); +}; - $.fn.organize.defaults = { - autoOpen: false, - modal: true, - resizable: false, - minWidth: 600, - minHeight: 500, - position: "center", - close: function () { - $("#gOrganizeDialog").trigger("organize_close"); - $("#gOrganizeDialog").dialog("destroy").remove(); - }, - zIndex: 75 - }; +// ************************************************************************** + +/** + * Dynamically initialize the organize dialog when it is displayed + */ +function organize_dialog_init() { + var size = getViewportSize(); + heightMicroThumbPanel = size.height() - 100; + var width = size.width() - 100; + + // Deal with ui.jquery bug: http://dev.jqueryui.com/ticket/4475 + $(".sf-menu li.sfHover ul").css("z-index", 70); + + $("#gDialog").dialog("option", "width", width); + $("#gDialog").dialog("option", "height", heightMicroThumbPanel); + + $("#gDialog").dialog("open"); + if ($("#gDialog h1").length) { + $("#gDialog").dialog('option', 'title', $("#gDialog h1:eq(0)").html()); + } else if ($("#gDialog fieldset legend").length) { + $("#gDialog").dialog('option', 'title', $("#gDialog fieldset legend:eq(0)").html()); + } + + $("#gDialog").bind("organize_close", function(target) { + $.gallery_reload(); + }); + + heightMicroThumbPanel -= 2 * parseFloat($("#gDialog").css("padding-top")); + heightMicroThumbPanel -= 2 * parseFloat($("#gDialog").css("padding-bottom")); + heightMicroThumbPanel -= $("#gMicroThumbPanel").position().top; + heightMicroThumbPanel -= $("#gDialog #ft").height(); + heightMicroThumbPanel -= $("#gOrganizeEditDrawerHandle").height(); + heightMicroThumbPanel = Math.round(heightMicroThumbPanel); + + $("#gMicroThumbPanel").height(heightMicroThumbPanel); + $("#gOrganizeTreeContainer").height(heightMicroThumbPanel); + + $(".gOrganizeBranch .ui-icon").click(organizeToggleChildren); + $(".gBranchText").droppable(treeDroppable); + $(".gBranchText").click(organizeOpenFolder); + retrieveMicroThumbs(item_id); + //showLoading("#gDialog"); - /** - * Dynamically initialize the organize dialog when it is displayed - */ - function _init(data) { + $("#gMicroThumbPanel").droppable(thumbDroppable); + $("#gMicroThumbPanel").selectable(selectable); + $("#gOrganizeEditDrawerHandle a").click(drawerHandleButtonsClick); +} - // Deal with ui.jquery bug: http://dev.jqueryui.com/ticket/4475 - $(".sf-menu li.sfHover ul").css("z-index", 70); +function retrieveMicroThumbs() { + var offset = $("#gMicroThumbGrid li").length; + if (url == null) { + var grid_width = $("#gMicroThumbPanel").width(); + url = $("#gMicroThumbPanel").attr("ref"); + url = url.replace("__WIDTH__", grid_width); + url = url.replace("__HEIGHT__", heightMicroThumbPanel); + } + var url_data = url.replace("__OFFSET__", offset); + url_data = url_data.replace("__ITEM_ID__", item_id); + $.getJSON(url_data, getMicroThumbsCallback); +} - $("#gOrganizeDialog").html(data); - $("#gOrganizeDialog").dialog("open"); +function organizeToggleChildren(event) { + var id = $(this).attr("ref"); + var span_children = $("#gOrganizeChildren-" + id); + if ($(this).hasClass("ui-icon-plus")) { + $(this).removeClass("ui-icon-plus"); + $(this).addClass("ui-icon-minus"); + $("#gOrganizeChildren-" + id).removeClass("gBranchCollapsed"); + } else { + $(this).removeClass("ui-icon-minus"); + $(this).addClass("ui-icon-plus"); + $("#gOrganizeChildren-" + id).addClass("gBranchCollapsed"); + } + event.preventDefault(); +} - var heightMicroThumbPanel = $("#gOrganizeDialog").innerHeight(); - heightMicroThumbPanel -= 2 * parseFloat($("#gOrganizeDialog").css("padding-bottom")); - heightMicroThumbPanel -= $("#gMessage").outerHeight(); - heightMicroThumbPanel = Math.floor(heightMicroThumbPanel); - $("#gOrganizeTreeContainer").height(heightMicroThumbPanel); +function organizeOpenFolder(event) { + var selected = $(".gBranchSelected"); + if ($(selected).attr("id") != $(this).attr("id")) { + $(selected).removeClass("gBranchSelected"); + $(this).addClass("gBranchSelected"); + item_id = $(this).attr("ref"); + $("#gMicroThumbGrid").empty(); + retrieveMicroThumbs(); + } + event.preventDefault(); +} - heightMicroThumbPanel -= $("#gOrganizeEditDrawerHandle").outerHeight(); - $("#gMicroThumbPanel").height(heightMicroThumbPanel); +function get_organize_url(uri, parms) { + var url = rearrangeUrl; + url = url.replace("__URI__", uri); + url = url.replace("__ITEM_ID__", !parms.item_id ? "" : parms.item_id); + url += (parms.item_id && parms.task_id) ? "/" : ""; + url = url.replace("__TASK_ID__", !parms.task_id ? "" : parms.task_id); + return url; +} - if ($("#gOrganizeDialog h1").length) { - $("#gOrganizeDialog").dialog('option', 'title', $("#gOrganizeDialog h1:eq(0)").html()); - } else if ($("#gOrganizeDialog fieldset legend").length) { - $("#gOrganizeDialog").dialog('option', 'title', $("#gOrganizeDialog fieldset legend:eq(0)").html()); +/** + * Set the enabled/disabled state of the buttons. The album cover is only enabled if + * there is only 1 image selected + */ +function setDrawerButtonState() { + $("#gOrganizeFormThumbStack").empty(); + $("#gOrganizeEditForm").empty(); + var selectedCount = $("#gMicroThumbGrid li.ui-selected").length; + if (selectedCount) { + $("#gOrganizeEditHandleButtonsLeft a").removeAttr("disabled"); + $("#gOrganizeEditHandleButtonsLeft a").removeClass("ui-state-disabled"); + + if (selectedCount > 1) { + $("#gOrganizeEditHandleButtonsLeft a[ref='albumCover']").attr("disabled", true); + $("#gOrganizeEditHandleButtonsLeft a[ref='albumCover']").addClass("ui-state-disabled"); + } + setSelectedThumbs(); + } else { + if ($("#gOrganizeEditDrawerPanel::visible").length) { + $("#gOrganizeEditHandleButtonsLeft a[ref='close']").trigger("click"); } + $("#gOrganizeEditHandleButtonsLeft a").attr("disabled", true); + $("#gOrganizeEditHandleButtonsLeft a").addClass("ui-state-disabled"); + } +} - $("#gOrganizeDialog #gMicroThumbDone").click(_dialog_close); - $("#gOrganizeDialog").bind("organize_close", function(target) { - $.gallery_reload(); +function setSelectedThumbs() { + if (!$("#gOrganizeEditDrawerPanel::visible").length) { + return; + } + var position = $("#gOrganizeFormThumbStack").position(); + var beginLeft = position.left; + var beginTop = 50; + var zindex = 2000; + $("li.ui-selected").each(function(i) { + var clone = $(this).clone(); + $(clone).attr("id", "edit_clone_" + $(this).attr("ref")); + $("#gOrganizeFormThumbStack").append(clone); + $(clone).removeClass("ui-draggable"); + $(clone).removeClass("ui-selected"); + $(clone).css("margin-top", beginTop); + $(clone).css("left", beginLeft); + $(clone).css("z-index", zindex--); + + if (i < 9) { + beginTop -= 5; + beginLeft += 5; + } + }); +} + +function getEditForm() { + if ($("#gMicroThumbGrid li.ui-selected").length > 0) { + var postData = serializeItemIds("li.ui-selected"); + var url_data = get_organize_url("organize/editForm", {}) + postData; + $.get(url_data, function(data, textStatus) { + $("#gOrganizeEditForm").tabs("destroy"); + $("#gOrganizeEditForm").html(data); + if ($("#gOrganizeEditForm ul li").length) { + $("#gOrganizeEditForm").tabs(); + $("#gOrganizeEditHandleButtonsMiddle a").removeAttr("disabled"); + $("#gOrganizeEditHandleButtonsMiddle a").removeClass("ui-state-disabled"); + } else { + $("#gOrganizeEditHandleButtonsMiddle a").attr("disabled", true); + $("#gOrganizeEditHandleButtonsMiddle a").addClass("ui-state-disabled"); + } }); + } else { + $("#gOrganizeEditForm").tabs("destroy"); + $("#gOrganizeEditForm").empty(); + } +} - //$(".gOrganizeBranch .ui-icon").click(organizeToggleChildren); - //$(".gBranchText").droppable(treeDroppable); - //$(".gBranchText").click(organizeOpenFolder); - //retrieveMicroThumbs(item_id); - //showLoading("#gOrganizeDialog"); +function serializeItemIds(selector) { + var postData = ""; + $(selector).each(function(i) { + postData += "&item[]=" + $(this).attr("ref"); + }); - //$("#gMicroThumbPanel").droppable(thumbDroppable); - //$("#gMicroThumbPanel").selectable(selectable); - //$("#gOrganizeEditDrawerHandle a").click(drawerHandleButtonsClick); + return postData; +} - $(window).bind("resize", _size_dialog); - }; +function submitCurrentForm(event) { + console.log("submitCurrentForm"); + return false; +} - /** - * Dynamically initialize the organize dialog when it is displayed - */ - function _size_dialog(event) { - var size = $.getViewportSize(); - var h = $("#gOrganizeDialog").dialog("option", "minHeight"); - var sh = size.height() - 100; - var height = Math.max(sh, h); - var w = $("#gOrganizeDialog").dialog("option", "minWidth"); - var sw = size.width() - 100; - var width = Math.max(w, sw); - - $("#gOrganizeDialog").parent().css("height", height); - $("#gOrganizeDialog").parent().css("width", width); - $("#gOrganizeDialog").parent().css("left", "50px"); - $("#gOrganizeDialog").parent().css("top", "50px"); - - var heightMicroThumbPanel = height - 50; - heightMicroThumbPanel -= 2 * parseFloat($("#gOrganizeDialog").css("padding-bottom")); - heightMicroThumbPanel -= $("#gMessage").outerHeight(); - heightMicroThumbPanel = Math.floor(heightMicroThumbPanel); - $("#gOrganizeTreeContainer").height(heightMicroThumbPanel); - - heightMicroThumbPanel -= $("#gOrganizeEditDrawerHandle").outerHeight(); - $("#gMicroThumbPanel").height(heightMicroThumbPanel); - }; +function resetCurrentForm(event) { + console.log("resetCurrentForm"); + return false; +} - function _dialog_close(event) { - event.preventDefault(); - $("#gOrganizeDialog").dialog("close"); +function createProgressDialog(title) { + $("body").append("<div id='gOrganizeProgressDialog'>" + + "<div class='gProgressBar'></div>" + + "<button id='gOrganizeTaskPause' class='ui-state-default ui-corner-all'>" + PAUSE_BUTTON + "</button>" + + "<button id='gOrganizeTaskResume' class='ui-state-default ui-corner-all' style='display: none'>" + RESUME_BUTTON + "</button>" + + "<button id='gOrganizeTaskCancel' class='ui-state-default ui-corner-all' style='display: none'>" + CANCEL_BUTTON + "</button>" + + "</div>"); + $("#gOrganizeProgressDialog").dialog({ + autoOpen: true, + autoResize: false, + modal: true, + resizable: false, + title: title + }); + + $(".gProgressBar").progressbar(); + $("#gOrganizeTaskPause").click(function(event) { + paused = true; + $("#gOrganizeTaskPause").hide(); + $("#gOrganizeTaskResume").show(); + $("#gOrganizeTaskCancel").show(); + $("#gMessage").empty().append(task.pauseMsg); + }); + $("#gOrganizeTaskResume").click(function(event) { + $("#gOrganizeTaskPause").show(); + $("#gOrganizeTaskResume").hide(); + $("#gOrganizeTaskCancel").hide(); + $("#gMessage").empty().append(task.resumeMsg); + operationCallback(); + //startRearrangeCallback(); + }); + $("#gOrganizeTaskCancel").click(function(event) { + $("#gOrganizeTaskPause").show(); + $("#gOrganizeTaskResume").hide(); + $("#gOrganizeTaskCancel").hide(); + + $.ajax({async: false, + success: function(data, textStatus) { + task = null; + paused = false; + transitItems = []; + $("#gMessage").empty().append("<div class='gWarning'>" + data.task.status + "</div>"); + $("#gOrganizeProgressDialog").dialog("destroy").remove(); + }, + dataType: "json", + type: "POST", + url: get_organize_url("organize/cancelTask", {task_id: task.id}) + }); + }); +} + +// ************************************************************************** +// Functions that should probably be in a gallery namespace +function getViewportSize() { + return { + width : function() { + return window.innerWidth + || document.documentElement && document.documentElement.clientWidth + || document.body.clientWidth; + }, + height : function() { + return window.innerHeight + || document.documentElement && document.documentElement.clientHeight + || document.body.clientHeight; + } }; +} -})(jQuery); +function displayAjaxError(error) { + $("body").append("<div id=\"gAjaxError\" title=\"" + FATAL_ERROR + "\">" + error + "</div>"); -$("document").ready(function() { - $("#gOrganizeLink").organize(); -}); + $("#gAjaxError").dialog({ + autoOpen: true, + autoResize: false, + modal: true, + resizable: true, + width: 610, + height: $("#gDialog").height() + }); +} diff --git a/modules/organize/js/organize_init.js b/modules/organize/js/organize_init.js new file mode 100644 index 00000000..ed036fdb --- /dev/null +++ b/modules/organize/js/organize_init.js @@ -0,0 +1,29 @@ +$("document").ready(function() { + $("#gOrganizeLink").click(function(event) { + event.preventDefault(); + var href = event.target.href; + + $("body").append('<div id="gDialog"></div>'); + + $("#gDialog").dialog({ + autoOpen: false, + autoResize: false, + modal: true, + resizable: false, + close: function () { + $("#gDialog").trigger("organize_close"); + $("#gDialog").dialog("destroy").remove(); + }, + zIndex: 75 + }); + + //showLoading("#gDialog"); + + $.get(href, function(data) { + $("#gDialog").html(data); + }); + return false; + }); +}); + + diff --git a/modules/organize/views/organize.html.php b/modules/organize/views/organize.html.php new file mode 100644 index 00000000..1686d255 --- /dev/null +++ b/modules/organize/views/organize.html.php @@ -0,0 +1,53 @@ +<?php defined("SYSPATH") or die("No direct script access.") ?> +<!-- ?= html::script("modules/organize/js/organize.js") ? --> +<script> + var FATAL_ERROR = "<?= t("Fatal Error") ?>"; + var PAUSE_BUTTON = "<?= t("Pause") ?>"; + var RESUME_BUTTON = "<?= t("Resume") ?>"; + var CANCEL_BUTTON = "<?= t("Cancel") ?>"; + var INVALID_DROP_TARGET = "<div class=\"gError\"><?= t("Drop cancelled as it would result in a recursive move") ?></div>"; +var CONFIRM_DELETE = "<?= t("Do you really want to delete the selected albums and/or photos") ?>" + var item_id = <?= $item->id ?>; + + var csrf = "<?= $csrf ?>"; + var rearrangeUrl = "<?= url::site("__URI__/__ITEM_ID____TASK_ID__?csrf=$csrf") ?>"; + $("#doc3").ready(function() { + organize_dialog_init(); + }); +</script> +<fieldset style="display: none"> + <legend><?= t("Organize %name", array("name" => p::purify($item->title))) ?></legend> +</fieldset> +<div id="doc3" class="yui-t7"> + <div id="bd"> + <div class="yui-gf"> + <div class="yui-u first"> + <h3><?= t("Albums") ?></h3> + </div> + <div id="gMessage" class="yui-u"> + <div class="gInfo"><?= t("Select one or more items to edit; drag and drop items to re-order or move between albums") ?></div> + </div> + </div> + <div class="yui-gf"> + <div id="gOrganizeTreeContainer" class="yui-u first"> + <?= $album_tree ?> + </div> + <div id="gMicroThumbPanel" class="yui-u" + ref="<?= url::site("organize/content/__ITEM_ID__?width=__WIDTH__&height=__HEIGHT__&offset=__OFFSET__") ?>"> + <ul id="gMicroThumbGrid"></ul> + </div> + <div id="gOrganizeEditDrawer" class="yui-u"> + <div id="gOrganizeEditDrawerPanel" class="yui-gf"> + <div id="gOrganizeFormThumbs" class="yui-u first"> + <ul id="gOrganizeFormThumbStack" /> + </div> + <div id="gOrganizeEditForm"> + </div> + </div> + <div id="gOrganizeEditDrawerHandle"> + <?= $button_pane ?> + </div> + </div> + </div> + </div> +</div> diff --git a/modules/organize/views/organize_album.html.php b/modules/organize/views/organize_album.html.php new file mode 100644 index 00000000..ae2d5d51 --- /dev/null +++ b/modules/organize/views/organize_album.html.php @@ -0,0 +1,17 @@ +<?php defined("SYSPATH") or die("No direct script access.") ?> +<ul> + <li class="gOrganizeBranch ui-icon-left" ref="<?= $album->id ?>"> + <span id="gOrganizeIcon-<?= $album->id ?>" ref="<?= $album->id ?>" + class="ui-icon <?= $album_icon ?> <?= $album_icon ? "" : "gBranchEmpty" ?>"> + </span> + + <div id="gOrganizeBranch-<?= $album->id ?>" ref="<?= $album->id ?>" + class="<?= $selected ? "gBranchSelected" : "" ?> gBranchText"> + <?= p::clean($album->title) ?> + </div> + <div id="gOrganizeChildren-<?= $album->id ?>" + class="<?= $album_icon == "ui-icon-plus" ? "gBranchCollapsed" : "" ?>"> + <?= $children ?> + <div> + </li> +</ul> diff --git a/modules/organize/views/organize_button_pane.html.php b/modules/organize/views/organize_button_pane.html.php index 8eced107..c5839a44 100644 --- a/modules/organize/views/organize_button_pane.html.php +++ b/modules/organize/views/organize_button_pane.html.php @@ -1,5 +1,50 @@ <?php defined("SYSPATH") or die("No direct script access.") ?> +<div id="gOrganizeEditHandleButtonsLeft"> + <a class="gButtonLink ui-corner-all ui-state-default ui-state-disabled" href="#" ref="edit" + disabled="1" title="<?= t("Open Drawer") ?>"> + <span class="ui-icon ui-icon-arrowthickstop-1-n"><?= t("Open Drawer") ?></span> + </a> + + <a class="gButtonLink ui-corner-all ui-state-default ui-state-disabled" href="#" ref="close" + disabled="1" title="<?= t("Close Drawer") ?>" style="display: none"> + <span class="ui-icon ui-icon-arrowthickstop-1-s"><?= t("Close Drawer") ?></span> + </a> + + <? if (graphics::can("rotate")): ?> + <a class="gButtonLink ui-corner-all ui-state-default ui-state-disabled" href="#" ref="rotateCcw" + disabled="1" title="<?= t("Rotate 90 degrees counter clockwise") ?>"> + <span class="ui-icon ui-icon-rotate-ccw"><?= t("Rotate 90 degrees counter clockwise") ?></span> + </a> + + <a class="gButtonLink ui-corner-all ui-state-default ui-state-disabled" href="#" ref="rotateCw" + disabled="1" title="<?= t("Rotate 90 degrees clockwise") ?>"> + <span class="ui-icon ui-icon-rotate-cw"> <?= t("Rotate 90 degrees clockwise") ?></span> + </a> + <? endif ?> + + <a class="gButtonLink ui-corner-all ui-state-default ui-state-disabled" href="#" ref="albumCover" + disabled="1" title="<?= t("Choose this photo as the album cover") ?>"> + <span class="ui-icon ui-icon-star"><?= t("Choose this photo as the album cover") ?></span> + </a> + + <a class="gButtonLink ui-corner-all ui-state-default ui-state-disabled" href="#" ref="delete" + disabled="1" title="<?= t("Delete selection") ?>"> + <span class="ui-icon ui-icon-trash"><?= t("Delete selection") ?></span> + </a> +</div> +<div id="gOrganizeEditHandleButtonsMiddle"> + <a class="gButtonLink ui-corner-all ui-state-default" href="#" ref="submit" + title="<?= t("Apply Changes") ?>" style="display: none" > + <span class="ui-icon ui-icon-check"><?= t("Apply Changes") ?></span> + </a> + + <a class="gButtonLink ui-corner-all ui-state-default" href="#" ref="reset" + title="<?= t("Reset Form") ?>" style="display: none" > + <span class="ui-icon ui-icon-closethick"><?= t("Reset Form") ?></span> + </a> +</div> <div id="gOrganizeEditHandleButtonsRight"> - <a id="gMicroThumbDone" href="#" ref="done" - class="gButtonLink ui-corner-all ui-state-default"><?= t("Close") ?></a> + <a id="gMicroThumbSelectAll" href="#" ref="select-all" class="gButtonLink ui-corner-all ui-state-default"><?= t("Select all") ?></a> + <a id="gMicroThumbUnselectAll" href="#" ref="unselect-all" style="display: none" class="gButtonLink ui-corner-all ui-state-default"><?= t("Deselect all") ?></a> + <a id="gMicroThumbDone" href="#" ref="done" class="gButtonLink ui-corner-all ui-state-default"><?= t("Close") ?></a> </div> diff --git a/modules/organize/views/organize_dialog.html.php b/modules/organize/views/organize_dialog.html.php deleted file mode 100644 index cf3fd478..00000000 --- a/modules/organize/views/organize_dialog.html.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php defined("SYSPATH") or die("No direct script access.") ?> -<div id="gOrganize"> - <h1 style="display:none"><?= t("Organize %name", array("name" => p::purify($item->title))) ?></h1> - <div id="bd"> - <div class="yui-gf"> - <div class="yui-u first"> - <h3><?= t("Albums") ?></h3> - </div> - <div id="gMessage" class="yui-u"> - <div class="gInfo"><?= t("Select one or more items to edit; drag and drop items to re-order or move between albums") ?></div> - </div> - </div> - <div class="yui-gf"> - <div id="gOrganizeTreeContainer" class="yui-u first"> - <ul id="gOrganizeAlbumTree"> - <?= $album_tree ?> - </ul> - </div> - <div id="gOrganizeDetail" class="yui-u"> - <div id="gMicroThumbPanel" - ref="<?= url::site("organize/content/__ITEM_ID__/__OFFSET__") ?>"> - <ul id="gMicroThumbGrid"> - <?= $micro_thumb_grid ?> - </ul> - </div> - <div id="gOrganizeEditDrawer" class="yui-u"> - <div id="gOrganizeEditDrawerPanel" class="yui-gf"> - </div> - <div id="gOrganizeEditDrawerHandle"> - <?= $button_pane ?> - </div> - </div> - </div> - </div> - </div> -</div> - diff --git a/modules/organize/views/organize_edit.html.php b/modules/organize/views/organize_edit.html.php new file mode 100644 index 00000000..1adf290f --- /dev/null +++ b/modules/organize/views/organize_edit.html.php @@ -0,0 +1,14 @@ +<?php defined("SYSPATH") or die("No direct script access.") ?> +<ul> +<? foreach ($panes as $idx => $pane): ?> + <li><a href="#pane-<?= $idx ?>"><?= $pane["label"] ?></a></li> +<? endforeach?> +</ul> + +<? if (count($panes) > 0): ?> + <? foreach ($panes as $idx => $pane): ?> + <div id="pane-<?= $idx ?>" class="gOrganizeEditPane ui-tabs-hide"><?= $pane["content"] ?></div> + <? endforeach?> +<? else: ?> +<div class="gWarning"><?= t("No Edit pages apply to the selected items") ?></div> +<? endif ?>
\ No newline at end of file diff --git a/modules/organize/views/organize_thumb_grid.html.php b/modules/organize/views/organize_thumb_grid.html.php index e6b7aec0..c80696ad 100644 --- a/modules/organize/views/organize_thumb_grid.html.php +++ b/modules/organize/views/organize_thumb_grid.html.php @@ -1,19 +1,12 @@ <?php defined("SYSPATH") or die("No direct script access.") ?> <? foreach ($children as $i => $child): ?> - <? $item_class = "gPhoto"; ?> - <? if ($child->is_album()): ?> - <? $item_class = "gAlbum"; ?> - <? endif ?> - <li id="gMicroThumb_<?= $child->id ?>" class="gMicroThumb <?= $item_class ?>" ref="<?= $child->id ?>"> +<? $item_class = "gPhoto"; ?> +<? if ($child->is_album()): ?> + <? $item_class = "gAlbum"; ?> +<? endif ?> +<li id="thumb_<?= $child->id ?>" class="gMicroThumbContainer" ref="<?= $child->id ?>"> + <div id="gMicroThumb-<?= $child->id ?>" class="gMicroThumb <?= $item_class ?>"> <?= $child->thumb_img(array("class" => "gThumbnail"), $thumbsize, true) ?> - </li> + </div> +</li> <? endforeach ?> -<? if (count($children) >= 25): ?> -<script> - $.get("<?= url::site("organize/content/{$item_id}/$offset") ?>", - function(data) { - $("#gMicroThumbGrid").append(data); - } - ); -</script> -<? endif ?>
\ No newline at end of file diff --git a/modules/organize/views/organize_tree.html.php b/modules/organize/views/organize_tree.html.php index 28b45be0..d2cdd957 100644 --- a/modules/organize/views/organize_tree.html.php +++ b/modules/organize/views/organize_tree.html.php @@ -1,20 +1,4 @@ <?php defined("SYSPATH") or die("No direct script access.") ?> -<li class="gOrganizeBranch ui-icon-left" ref="<?= $album->id ?>"> - <span id="gOrganizeIcon-<?= $album->id ?>" ref="<?= $album->id ?>" - class="ui-icon <?= $album_icon ?> <?= $album_icon ? "" : "gBranchEmpty" ?>"> - </span> - - <div id="gOrganizeBranch-<?= $album->id ?>" ref="<?= $album->id ?>" - class="<?= $selected ? "gBranchSelected" : "" ?> gBranchText"> - <?= p::clean($album->title) ?> - </div> - <? if (empty($children)): ?> - <div id="gOrganizeChildren-<?= $album->id ?>"></div> - <? else: ?> - <ul id="gOrganizeChildren-<?= $album->id ?>" - class="<?= $album_icon == "ui-icon-plus" ? "gBranchCollapsed" : "" ?>"> - <?= $children ?> - </ul> - <? endif ?> -</li> +<? foreach ($children as $i => $child): ?> +<? endforeach ?> |