summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorTim Almdal <tnalmdal@shaw.ca>2009-08-05 09:23:32 -0700
committerTim Almdal <tnalmdal@shaw.ca>2009-08-05 09:23:32 -0700
commit397468c47b8fc3fefeb54ff19a73980ed1dd8c20 (patch)
tree1dd6668463cecb755325f0644945897bdbe98423 /modules
parente37526f94df74a52a9cf36f0a5a5e641958ebbb3 (diff)
Revert "Checkpoint the organize module rewrite. At this point, it doesn't really do"
This reverts commit a25f08d433d504a53763feb358a1aa7f5f798de6.
Diffstat (limited to 'modules')
-rw-r--r--modules/organize/controllers/organize.php507
-rw-r--r--modules/organize/css/organize.css184
-rw-r--r--modules/organize/helpers/organize_task.php131
-rw-r--r--modules/organize/helpers/organize_theme.php3
-rw-r--r--modules/organize/js/organize.js697
-rw-r--r--modules/organize/js/organize_init.js29
-rw-r--r--modules/organize/views/organize.html.php53
-rw-r--r--modules/organize/views/organize_album.html.php17
-rw-r--r--modules/organize/views/organize_button_pane.html.php49
-rw-r--r--modules/organize/views/organize_dialog.html.php37
-rw-r--r--modules/organize/views/organize_edit.html.php14
-rw-r--r--modules/organize/views/organize_thumb_grid.html.php23
-rw-r--r--modules/organize/views/organize_tree.html.php20
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__&amp;height=__HEIGHT__&amp;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 ?>