diff options
Diffstat (limited to 'modules/server_add/controllers/server_add.php')
-rw-r--r-- | modules/server_add/controllers/server_add.php | 348 |
1 files changed, 162 insertions, 186 deletions
diff --git a/modules/server_add/controllers/server_add.php b/modules/server_add/controllers/server_add.php index e2b1b01a..288e6342 100644 --- a/modules/server_add/controllers/server_add.php +++ b/modules/server_add/controllers/server_add.php @@ -17,13 +17,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Server_Add_Controller extends Controller { +class Server_Add_Controller extends Admin_Controller { public function browse($id) { - if (!user::active()->admin) { - access::forbidden(); - } - - $paths = unserialize(module::get_var("server_add", "authorized_paths")); foreach (array_keys($paths) as $path) { $files[$path] = basename($path); @@ -37,28 +32,11 @@ class Server_Add_Controller extends Controller { print $view; } - private function _validate_path($path) { - if (!is_readable($path) || is_link($path)) { - throw new Exception("@todo BAD_PATH"); - } - - $authorized_paths = unserialize(module::get_var("server_add", "authorized_paths")); - foreach (array_keys($authorized_paths) as $valid_path) { - if (strpos($path, $valid_path) === 0) { - return; - } - } - - throw new Exception("@todo BAD_PATH"); - } - public function children() { - if (!user::active()->admin) { - access::forbidden(); - } - $path = $this->input->get("path"); - $this->_validate_path($path); + if (!server_add::is_valid_path($path)) { + throw new Exception("@todo BAD_PATH"); + } $tree = new View("server_add_tree.html"); $tree->files = array(); @@ -81,196 +59,194 @@ class Server_Add_Controller extends Controller { print $tree; } - public function add() { - if (!user::active()->admin) { - access::forbidden(); - } + public function start() { access::verify_csrf(); - $authorized_paths = unserialize(module::get_var("server_add", "authorized_paths")); + $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); - // The paths we receive are full pathnames. Convert that into a tree structure to save space - // in our task. - foreach (Input::instance()->post("path") as $path) { + // 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)) { - $dirs[$path] = array(); + $selections[$path] = array(); } else if (is_file($path)) { - $dir = dirname($path); - $file = basename($path); - $dirs[$dir][] = $file; + $selections[dirname($path)][] = $path; } } - Kohana::log("alert",print_r($dirs,1)); - } - - /* ================================================================================ */ - - function start($id) { - if (!user::active()->admin) { - access::forbidden(); - } - access::verify_csrf(); - - $item = ORM::factory("item", $id); - $paths = unserialize(module::get_var("server_add", "authorized_paths")); - $input_files = $this->input->post("path"); - $collapsed = $this->input->post("collapsed"); - $files = array(); - $total_count = 0; - foreach (array_keys($paths) as $valid_path) { - $path_length = strlen($valid_path); - foreach ($input_files as $key => $path) { - if (!empty($path)) { - if ($valid_path != $path && strpos($path, $valid_path) === 0) { - $relative_path = substr(dirname($path), $path_length); - $name = basename($path); - $files[$valid_path][] = array("path" => $relative_path, - "parent_id" => $id, "name" => basename($path), - "type" => is_dir($path) ? "album" : "file"); - $total_count++; - } - if ($collapsed[$key] === "true") { - $total_count += $this->_select_children($id, $valid_path, $path, $files[$valid_path]); - } - unset($input_files[$key]); - unset($collapsed[$key]); - } - } - } - - if ($total_count == 0) { - print json_encode(array("result" => "success", - "url" => "", - "task" => array( - "id" => -1, "done" => 1, "percent_complete" => 100, - "status" => t("No eligible files, import cancelled")))); - return; - } - $task_def = Task_Definition::factory() - ->callback("server_add_task::add_from_server") + ->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" => $id, "next_path" => 0, "files" => $files, - "counter" => 0, "position" => 0, "total" => $total_count)); + $task = task::create( + $task_def, array("item_id" => $item->id, "selections" => $selections)); - batch::start(); - print json_encode(array("result" => "started", - "url" => url::site("server_add/add_photo/{$task->id}?csrf=" . - access::csrf_token()), - "task" => array( - "id" => $task->id, - "percent_complete" => $task->percent_complete, - "status" => $task->status, - "done" => $task->done))); + print json_encode( + array("result" => "started", + "url" => url::site("server_add/run/$task->id?csrf=" . access::csrf_token()))); } - function add_photo($task_id) { - if (!user::active()->admin) { - access::forbidden(); - } + function run($task_id) { access::verify_csrf(); - $task = task::run($task_id); - // @todo the task is already run... its a little late to check the access - if (!$task->loaded || $task->owner_id != user::active()->id) { - access::forbidden(); - } - - if ($task->done) { - switch ($task->state) { - case "success": - message::success(t("Add from server completed")); - break; - - case "error": - message::warning(t("Add from server completed with errors")); - break; - } - print json_encode(array("result" => "success", - "task" => array( - "id" => $task->id, - "percent_complete" => $task->percent_complete, - "status" => $task->status, - "done" => $task->done))); - - } else { - print json_encode(array("result" => "in_progress", - "task" => array( - "id" => $task->id, - "percent_complete" => $task->percent_complete, - "status" => $task->status, - "done" => $task->done))); - } - } - - public function finish($id, $task_id) { - if (!user::active()->admin) { - access::forbidden(); - } - access::verify_csrf(); $task = ORM::factory("task", $task_id); - if (!$task->loaded || $task->owner_id != user::active()->id) { access::forbidden(); } - if (!$task->done) { - message::warning(t("Add from server was cancelled prior to completion")); - } - - batch::stop(); - print json_encode(array("result" => "success")); + $task = task::run($task_id); + print json_encode(array("done" => $task->done, + "percent_complete" => $task->percent_complete)); } - public function pause($id, $task_id) { - if (!user::active()->admin) { - access::forbidden(); - } - access::verify_csrf(); - $task = ORM::factory("task", $task_id); - if (!$task->loaded || $task->owner_id != user::active()->id) { - access::forbidden(); - } - - message::warning(t("Add from server was cancelled prior to completion")); - batch::stop(); - print json_encode(array("result" => "success")); - } + /** + * This is the task code that adds photos and albums. It first examines all the target files + * and creates a set of Server_Add_File_Models, then runs through the list of models and adds + * 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; + batch::start(); + break; + + 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. + $queue = $task->get("queue"); + Kohana::log("alert",print_r($queue,1)); + while ($queue && microtime(true) - $start < 0.5) { + $file = array_shift($queue); + $entry = ORM::factory("server_add_file"); + $entry->task_id = $task->id; + $entry->file = $file; + $entry->save(); + + if (is_dir($file)) { + $queue = array_merge( + $queue, empty($selections[$file]) ? glob("$file/*") : $selections[$file]); + } + } + // 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. + $task->set("queue", $queue); + $task->percent_complete = min($task->percent_complete + 0.1, 10); + + if (!$queue) { + $task->set("mode", "add-files"); + $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"); + $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. + $entries = ORM::factory("server_add_file") + ->where("task_id", $task->id) + ->orderby("id", "ASC") + ->limit(10) + ->find_all(); + if ($entries->count() == 0) { + $task->set("mode", "done"); + } - private function _select_children($id, $valid_path, $path, &$files) { - $count = 0; - $children = new RecursiveIteratorIterator( - new RecursiveDirectoryIterator($path), - RecursiveIteratorIterator::SELF_FIRST); + $item = model_cache::get("item", $item_id); + foreach ($entries as $entry) { + if (microtime(true) - $start > 0.5) { + break; + } - $path_length = strlen($valid_path); - foreach($children as $name => $file){ - if ($file->isLink()) { - continue; - } - $filename = $file->getFilename(); - if ($filename[0] != ".") { - if ($file->isDir()) { - $relative_path = substr(dirname($file->getPathname()), $path_length); - $files[] = array("path" => $relative_path, - "parent_id" => $id, "name" => $filename, "type" => "album"); - $count++; + $relative_path = self::_relative_path($entry->file); + $name = basename($relative_path); + $title = item::convert_filename_to_title($name); + if (is_dir($entry->file)) { + if (isset($albums[$relative_path]) && $parent_id = $albums[$relative_path]) { + $parent = ORM::factory("item", $parent_id); + } else { + $album = album::create($item, $name, $title, null, user::active()->id); + $albums[$relative_path] = $album->id; + $task->set("albums", $albums); + } } else { - $extension = strtolower(substr(strrchr($filename, '.'), 1)); - if ($file->isReadable() && - in_array($extension, array("gif", "jpeg", "jpg", "png", "flv", "mp4"))) { - $relative_path = substr(dirname($file->getPathname()), $path_length); - $files[] = array("path" => $relative_path, - "parent_id" => $id, "name" => $filename, "type" => "file"); - $count++; + if (strpos($relative_path, "/") !== false) { + $parent = ORM::factory("item", $albums[dirname($relative_path)]); + } else { + $parent = $item; + } + + $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); + } else if (in_array($extension, array("flv", "mp4"))) { + movie::create($parent, $entry->file, $name, $title, null, user::active()->id); + } else { + // Unsupported type + // @todo: $task->log this } } + + $completed_files++; + $entry->delete(); } + $task->set("completed_files", $completed_files); + $task->percent_complete = 10 + 100 * ($completed_files / $total_files); + Kohana::log("alert",print_r($task->as_array(),1)); + break; + + case "done": + batch::stop(); + $task->done = true; + $task->state = "success"; + $task->percent_complete = 100; + message::info(t2("Successfully added one photo", + "Successfully added %count photos", + $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); + } } - return $count; + throw new Exception("@todo BAD_PATH"); } -}
\ No newline at end of file +} |