diff options
Diffstat (limited to 'modules')
-rw-r--r-- | modules/server_add/controllers/server_add.php | 199 | ||||
-rw-r--r-- | modules/server_add/helpers/server_add_installer.php | 8 | ||||
-rw-r--r-- | modules/server_add/js/server_add.js | 67 | ||||
-rw-r--r-- | modules/server_add/module.info | 2 | ||||
-rw-r--r-- | modules/server_add/views/server_add_tree.html.php | 56 |
5 files changed, 152 insertions, 180 deletions
diff --git a/modules/server_add/controllers/server_add.php b/modules/server_add/controllers/server_add.php index 08e01858..6056d7cd 100644 --- a/modules/server_add/controllers/server_add.php +++ b/modules/server_add/controllers/server_add.php @@ -29,51 +29,58 @@ class Server_Add_Controller extends Admin_Controller { $view->item = $item; $view->tree = new View("server_add_tree.html"); $view->tree->files = $files; + $view->tree->parents = array(); print $view; } public function children() { $path = $this->input->get("path"); - if (!server_add::is_valid_path($path)) { - throw new Exception("@todo BAD_PATH"); - } $tree = new View("server_add_tree.html"); $tree->files = array(); - $tree->tree_id = substr(md5($path), 10); + $tree->parents = array(); - foreach (glob("$path/*") as $file) { - if (!is_readable($file)) { - continue; + // Make a tree with the parents back up to the authorized path, and all the children under the + // current path. + if (server_add::is_valid_path($path)) { + $tree->parents[] = $path; + while (server_add::is_valid_path(dirname($tree->parents[0]))) { + array_unshift($tree->parents, dirname($tree->parents[0])); } - if (!is_dir($file)) { - $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION)); - if (!in_array($ext, array("gif", "jpeg", "jpg", "png", "flv", "mp4"))) { + foreach (glob("$path/*") as $file) { + if (!is_readable($file)) { continue; } - } + if (!is_dir($file)) { + $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION)); + if (!in_array($ext, array("gif", "jpeg", "jpg", "png", "flv", "mp4"))) { + continue; + } + } - $tree->files[] = $file; + $tree->files[] = $file; + } + } else { + // Missing or invalid path; print out the list of authorized path + $paths = unserialize(module::get_var("server_add", "authorized_paths")); + foreach (array_keys($paths) as $path) { + $tree->files[] = $path; + } } print $tree; } + /** + * Begin the task of adding photos. + */ public function start() { access::verify_csrf(); - $item = ORM::factory("item", Input::instance()->get("item_id")); - // We're an admin so this isn't necessary, but we'll eventually open this up to non-admins and - // this also verifies that the item was loaded properly. - access::required("edit", $item); - // Gather up all the paths and associate them by directory, so that we can locate any empty - // directories for the next round. foreach (Input::instance()->post("paths") as $path) { - if (is_dir($path)) { - $selections[$path] = array(); - } else if (is_file($path)) { - $selections[dirname($path)][] = $path; + if (server_add::is_valid_path($path)) { + $paths[] = array($path, null); } } @@ -81,8 +88,7 @@ class Server_Add_Controller extends Admin_Controller { ->callback("Server_Add_Controller::add") ->description(t("Add photos or movies from the local server")) ->name(t("Add from server")); - $task = task::create( - $task_def, array("item_id" => $item->id, "selections" => $selections)); + $task = task::create($task_def, array("item_id" => $item->id, "queue" => $paths)); print json_encode( array("result" => "started", @@ -90,6 +96,9 @@ class Server_Add_Controller extends Admin_Controller { "url" => url::site("server_add/run/$task->id?csrf=" . access::csrf_token()))); } + /** + * Run the task of adding photos + */ function run($task_id) { access::verify_csrf(); @@ -110,49 +119,48 @@ class Server_Add_Controller extends Admin_Controller { * them one at a time. */ static function add($task) { - $selections = $task->get("selections"); $mode = $task->get("mode", "init"); $start = microtime(true); - $item_id = $task->get("item_id"); switch ($mode) { case "init": $task->set("mode", "build-file-list"); - $task->set("queue", array_keys($selections)); $task->percent_complete = 0; $task->status = t("Starting up"); batch::start(); break; - case "build-file-list": /* 0% to 10% */ + case "build-file-list": // 0% to 10% // We can't fit an arbitrary number of paths in a task, so store them in a separate table. // Don't use an iterator here because we can't get enough control over it when we're dealing - // with a deep hierarchy and we don't want to go over our time quota. + // with a deep hierarchy and we don't want to go over our time quota. The queue is in the + // form [path, parent_id] where the parent_id refers to another Server_Add_File_Model. We + // have this extra level of abstraction because we don't know its Item_Model id yet. $queue = $task->get("queue"); while ($queue && microtime(true) - $start < 0.5) { - $file = array_shift($queue); - if (is_dir($file)) { - $entry = ORM::factory("server_add_file"); - $entry->task_id = $task->id; - $entry->file = $file; - $entry->save(); - - $children = empty($selections[$file]) ? glob("$file/*") : $selections[$file]; - } else { - $children = array($file); - } - - foreach ($children as $child) { - $entry = ORM::factory("server_add_file"); - $entry->task_id = $task->id; - $entry->file = $child; - $entry->save(); + list($file, $parent_entry_id) = array_shift($queue); + $entry = ORM::factory("server_add_file"); + $entry->task_id = $task->id; + $entry->file = $file; + $entry->parent_id = $parent_entry_id; + $entry->save(); + foreach (glob("$file/*") as $child) { if (is_dir($child)) { - $queue[] = $child; + $queue[] = array($child, $entry->id); + } else { + $ext = strtolower(pathinfo($child, PATHINFO_EXTENSION)); + if (in_array($ext, array("gif", "jpeg", "jpg", "png", "flv", "mp4"))) { + $child_entry = ORM::factory("server_add_file"); + $child_entry->task_id = $task->id; + $child_entry->file = $child; + $child_entry->parent_id = $entry->id; + $child_entry->save(); + } } } } + // We have no idea how long this can take because we have no idea how deep the tree // hierarchy rabbit hole goes. Leave ourselves room here for 100 iterations and don't go // over 10% in percent_complete. @@ -168,79 +176,67 @@ class Server_Add_Controller extends Admin_Controller { $task->set( "total_files", database::instance()->count_records( "server_add_files", array("task_id" => $task->id))); - $task->set("albums", array()); - $task->set("completed", 0); $task->percent_complete = 10; } break; - case "add-files": /* 10% to 100% */ - $completed_files = $task->get("completed_files"); + case "add-files": // 10% to 100% + $completed_files = $task->get("completed_files", 0); $total_files = $task->get("total_files"); - $albums = $task->get("albums"); // Ordering by id ensures that we add them in the order that we created the entries, which - // will create albums first. + // will create albums first. Ignore entries which already have an Item_Model attached, + // they're done. $entries = ORM::factory("server_add_file") ->where("task_id", $task->id) + ->where("item_id", null) ->orderby("id", "ASC") ->limit(10) ->find_all(); if ($entries->count() == 0) { + // Out of entries, we're done. $task->set("mode", "done"); } - $item = model_cache::get("item", $item_id); + $owner_id = user::active()->id; foreach ($entries as $entry) { if (microtime(true) - $start > 0.5) { break; } - $relative_path = self::_relative_path($entry->file); - $name = basename($relative_path); + // Look up the parent item for this entry. By now it should exist, but if none was + // specified, then this belongs as a child of the current item. + $parent_entry = ORM::factory("server_add_file", $entry->parent_id); + if (!$parent_entry->loaded) { + $parent = ORM::factory("item", $task->get("item_id")); + } else { + $parent = ORM::factory("item", $parent_entry->item_id); + } + + $name = basename($entry->file); $title = item::convert_filename_to_title($name); if (is_dir($entry->file)) { - $parent_path = dirname($relative_path); - if (isset($albums[$parent_path]) && $parent_id = $albums[$parent_path]) { - $parent = ORM::factory("item", $parent_id); - } else { - $parent = $item; - } - $album = album::create($parent, $name, $title, null, user::active()->id); - $albums[$relative_path] = $album->id; - $task->set("albums", $albums); + $album = album::create($parent, $name, $title, null, $owner_id); + $entry->item_id = $album->id; } else { - // Find the nearest selected parent. We check to see if any of the candidate parents - // were selected in the UI and if so, we use that. Otherwise, we fall back to making - // the parent the current item. - $parent_path = $relative_path; - $parent = null; - do { - if (strpos($parent_path, "/") !== false) { - if (array_key_exists($parent_path, $albums)) { - $parent = ORM::factory("item", $albums[$parent_path]); - } else { - $parent_path = dirname($parent_path); - } - } else { - $parent = $item; - } - } while (!$parent); - $extension = strtolower(pathinfo($name, PATHINFO_EXTENSION)); if (in_array($extension, array("gif", "png", "jpg", "jpeg"))) { - photo::create($parent, $entry->file, $name, $title, null, user::active()->id); + $photo = photo::create($parent, $entry->file, $name, $title, null, $owner_id); + $entry->item_id = $photo->id; } else if (in_array($extension, array("flv", "mp4"))) { - movie::create($parent, $entry->file, $name, $title, null, user::active()->id); + $movie = movie::create($parent, $entry->file, $name, $title, null, $owner_id); + $entry->item_id = $movie->id; } else { - $task->log("Skipping unknown file type: $relative_path"); - // Unsupported type - // @todo: $task->log this + // This should never happen, because we don't add stuff to the list that we can't + // process. But just in, case.. set this to a non-null value so that we skip this + // entry. + $entry->item_id = 0; + $task->log("Skipping unknown file type: $entry->file"); } } $completed_files++; - $entry->delete(); + $entry->save(); } $task->set("completed_files", $completed_files); $task->status = t("Adding photos and albums (%completed of %total)", @@ -254,33 +250,10 @@ class Server_Add_Controller extends Admin_Controller { $task->done = true; $task->state = "success"; $task->percent_complete = 100; + ORM::factory("server_add_file")->where("task_id", $task->id)->delete_all(); message::info(t2("Successfully added one photo", - "Successfully added %count photos", + "Successfully added %count photos and albums", $task->get("completed_files"))); } } - - /** - * Given a path that's somewhere in our authorized_paths list, return just the part that's - * relative to the nearest authorized path. - */ - static function _relative_path($path) { - static $authorized_paths; - // @todo this doesn't deal well with overlapping authorized paths, it'll just use the first one - // that matches. If we sort $authorized_paths by length in descending order, that should take - // care of the problem. - if (!$authorized_paths) { - $authorized_paths = - array_keys(unserialize(module::get_var("server_add", "authorized_paths"))); - } - - foreach ($authorized_paths as $candidate) { - $candidate = dirname($candidate); - if (strpos($path, $candidate) === 0) { - return substr($path, strlen($candidate) + 1); - } - } - - throw new Exception("@todo BAD_PATH"); - } } diff --git a/modules/server_add/helpers/server_add_installer.php b/modules/server_add/helpers/server_add_installer.php index 6956a72c..4cc34207 100644 --- a/modules/server_add/helpers/server_add_installer.php +++ b/modules/server_add/helpers/server_add_installer.php @@ -22,8 +22,10 @@ class server_add_installer { $db = Database::instance(); $db->query("CREATE TABLE {server_add_files} ( `id` int(9) NOT NULL auto_increment, - `task_id` int(9) NOT NULL, `file` varchar(255) NOT NULL, + `item_id` int(9), + `parent_id` int(9), + `task_id` int(9) NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;"); module::set_version("server_add", 2); @@ -40,6 +42,10 @@ class server_add_installer { PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;"); module::set_version("server_add", $version = 2); + } else if ($version == 2) { + $db->query("ALTER TABLE {server_add_files} ADD COLUMN `item_id` int(9)"); + $db->query("ALTER TABLE {server_add_files} ADD COLUMN `parent_id` int(9)"); + module::set_version("server_add", $version = 3); } } diff --git a/modules/server_add/js/server_add.js b/modules/server_add/js/server_add.js index 1a78d733..989555cc 100644 --- a/modules/server_add/js/server_add.js +++ b/modules/server_add/js/server_add.js @@ -1,51 +1,9 @@ /** - * We've clicked the + icon next to a directory. Load up children of this - * directory from the server and display them. + * Manage file selection state. */ -function open_close_branch(path, id) { - var parent = $("#file_" + id); - var children = $("#tree_" + id); - var icon = parent.find(".ui-icon:first"); - - if (!children.html()) { - parent.addClass("gLoadingSmall"); - $.ajax({ - url: GET_CHILDREN_URL.replace("__PATH__", path), - success: function(data, textStatus) { - children.html(data); - parent.removeClass("gLoadingSmall"); - - // Propagate checkbox value - children.find("input[type=checkbox]").attr( - "checked", parent.find("input[type=checkbox]:first").attr("checked")); - } - }); - } - - children.slideToggle("fast", function() { - if (children.is(":hidden")) { - icon.addClass("ui-icon-plus"); - icon.removeClass("ui-icon-minus"); - } else { - icon.addClass("ui-icon-minus"); - icon.removeClass("ui-icon-plus"); - parent.removeClass("gCollapsed"); - } - }); -} - -/** - * We've clicked a checkbox. Propagate the value downwards as necessary. - */ -function click_node(checkbox) { - var parent = $(checkbox).parents("li").get(0); - var checked = $(checkbox).attr("checked"); - $(parent).find("input[type=checkbox]").attr("checked", checked); - - // @todo if we uncheck all the children for a parent, we should uncheck the - // parent itself, otherwise in the code we'll add the entire parent since if - // we find an album as a leaf, we assume that it's never been expanded in the UI. - if ($("#gServerAddTree").find("input[type=checkbox]").is(":checked")) { +function select_file(li) { + $(li).toggleClass("selected"); + if ($("#gServerAdd span.selected").length) { $("#gServerAddAddButton").enable(true); $("#gServerAddAddButton").removeClass("ui-state-disabled"); } else { @@ -54,11 +12,24 @@ function click_node(checkbox) { } } +/** + * Load a new directory + */ +function open_dir(path) { + $.ajax({ + url: GET_CHILDREN_URL.replace("__PATH__", path), + success: function(data, textStatus) { + $("#gServerAddTree").html(data); + } + }); +} + function start_add() { var paths = []; - $.each($("#gServerAdd :checkbox[checked]"), function () { - paths.push(this.value); + $.each($("#gServerAdd span.selected"), function () { + paths.push($(this).attr("file")); }); + $.ajax({ url: START_URL, type: "POST", diff --git a/modules/server_add/module.info b/modules/server_add/module.info index 1c99d280..295d3beb 100644 --- a/modules/server_add/module.info +++ b/modules/server_add/module.info @@ -1,3 +1,3 @@ name = Server Add description = Allows authorized users to load images directly from your web server -version = 2 +version = 3 diff --git a/modules/server_add/views/server_add_tree.html.php b/modules/server_add/views/server_add_tree.html.php index 74bc40e0..254a9da0 100644 --- a/modules/server_add/views/server_add_tree.html.php +++ b/modules/server_add/views/server_add_tree.html.php @@ -1,19 +1,41 @@ <?php defined("SYSPATH") or die("No direct script access.") ?> -<? foreach ($files as $file): ?> -<? $id = substr(md5($file), 10) ?> -<li id="file_<?= $id ?>" class="<?= is_file($file) ? "gFile" : "gDirectory gCollapsed ui-icon-left" ?>"> - <? if (is_dir($file)): ?> - <span onclick="open_close_branch('<?=$file?>', '<?=$id?>')" class="ui-icon ui-icon-plus"></span> - <? endif ?> - <label> - <?= form::checkbox("path[]", $file, false, "onclick=click_node(this)") ?> - <?= p::clean(basename($file)) ?> - </label> - <? if (is_dir($file)): ?> - <ul id="tree_<?= $id ?>" style="display: none"></ul> - <? endif ?> +<li class="ui-icon-left"> + <span class="ui-icon ui-icon-folder-open"></span> + <span ondblclick="open_dir('')"> + <?= t("All") ?> + </span> + <ul> + + <? foreach ($parents as $dir): ?> + <li class="ui-icon-left"> + <span class="ui-icon ui-icon-folder-open"></span> + <span ondblclick="open_dir('<?= $dir ?>')"> + <?= basename($dir) ?> + </span> + <ul> + <? endforeach ?> + + <? foreach ($files as $file): ?> + <li class="ui-icon-left"> + <span class="ui-icon <?= is_dir($file) ? "ui-icon-folder-collapsed" : "ui-icon-document" ?>"></span> + <span onclick="select_file(this)" + <? if (is_dir($file)): ?> + ondblclick="open_dir($(this).attr('file'))" + <? endif ?> + file="<?= $file ?>" + > + <?= p::clean(basename($file)) ?> + </span> + </li> + <? endforeach ?> + <? if (!$files): ?> + <li> <i> <?= t("empty") ?> </i> </li> + <? endif ?> + + <? foreach ($parents as $dir): ?> + </ul> + </li> + <? endforeach ?> + + </ul> </li> -<? endforeach ?> -<? if (!$files): ?> -<li class="gFile"> <i> <?= t("empty") ?> </i> </li> -<? endif ?> |