summaryrefslogtreecommitdiff
path: root/modules/server_add/controllers/server_add.php
diff options
context:
space:
mode:
Diffstat (limited to 'modules/server_add/controllers/server_add.php')
-rw-r--r--modules/server_add/controllers/server_add.php386
1 files changed, 182 insertions, 204 deletions
diff --git a/modules/server_add/controllers/server_add.php b/modules/server_add/controllers/server_add.php
index 05ea5058..288e6342 100644
--- a/modules/server_add/controllers/server_add.php
+++ b/modules/server_add/controllers/server_add.php
@@ -17,258 +17,236 @@
* 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 {
- public function index($id) {
+class Server_Add_Controller extends Admin_Controller {
+ public function browse($id) {
$paths = unserialize(module::get_var("server_add", "authorized_paths"));
-
- if (!user::active()->admin) {
- access::forbidden();
+ foreach (array_keys($paths) as $path) {
+ $files[$path] = basename($path);
}
$item = ORM::factory("item", $id);
$view = new View("server_add_tree_dialog.html");
- $view->action = url::abs_site("__ARGS__/{$id}__TASK_ID__?csrf=" . access::csrf_token());
- $view->parents = $item->parents();
- $view->album_title = $item->title;
-
- $tree = new View("server_add_tree.html");
- $tree->data = array();
- $tree->checked = false;
- $tree->tree_id = "tree_$id";
- foreach (array_keys($paths) as $path) {
- $tree->data[$path] = array("path" => $path, "is_dir" => true);
- }
- $view->tree = $tree->__toString();
+ $view->item = $item;
+ $view->tree = new View("server_add_tree.html");
+ $view->tree->files = $files;
print $view;
}
public function children() {
- if (!user::active()->admin) {
- access::forbidden();
+ $path = $this->input->get("path");
+ if (!server_add::is_valid_path($path)) {
+ throw new Exception("@todo BAD_PATH");
}
- $paths = unserialize(module::get_var("server_add", "authorized_paths"));
- $path_valid = false;
- $path = $this->input->post("path");
- $checked = $this->input->post("checked") == "true";
+ $tree = new View("server_add_tree.html");
+ $tree->files = array();
+ $tree->tree_id = substr(md5($path), 10);
- foreach (array_keys($paths) as $valid_path) {
- if ($path_valid = strpos($path, $valid_path) === 0) {
- break;
+ foreach (glob("$path/*") as $file) {
+ if (!is_readable($file)) {
+ continue;
}
- }
- if (empty($path_valid)) {
- throw new Exception("@todo BAD_PATH");
- }
- if (!is_readable($path) || is_link($path)) {
- kohana::show_404();
- }
+ if (!is_dir($file)) {
+ $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
+ if (!in_array($ext, array("gif", "jpeg", "jpg", "png", "flv", "mp4"))) {
+ continue;
+ }
+ }
- $tree = new View("server_add_tree.html");
- $tree->data = $this->_get_children($path);
- $tree->checked = $checked;
- $tree->tree_id = "tree_" . md5($path);
+ $tree->files[$file] = basename($file);
+ }
print $tree;
}
- function start($id) {
- if (!user::active()->admin) {
- access::forbidden();
- }
+ public function start() {
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]);
- }
+ $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 ($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")));
}
-
- return $count;
}
- private function _get_children($path) {
- $directory_list = $file_list = array();
- $files = new DirectoryIterator($path);
- foreach ($files as $file) {
- if ($file->isDot() || $file->isLink()) {
- continue;
- }
- $filename = $file->getFilename();
- if ($filename[0] != ".") {
- if ($file->isDir()) {
- $directory_list[$filename] = array("path" => $file->getPathname(), "is_dir" => true);
- } else {
- $extension = strtolower(substr(strrchr($filename, '.'), 1));
- if ($file->isReadable() &&
- in_array($extension, array("gif", "jpeg", "jpg", "png", "flv", "mp4"))) {
- $file_list[$filename] = array("path" => $file->getPathname(), "is_dir" => false);
- }
- }
+ /**
+ * 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);
}
}
- ksort($directory_list);
- ksort($file_list);
-
- // We can't use array_merge here because if a file name is numeric, it will
- // get renumbered, so lets do it ourselves
- foreach ($file_list as $file => $fileinfo) {
- $directory_list[$file] = $fileinfo;
- }
- return $directory_list;
+ throw new Exception("@todo BAD_PATH");
}
-} \ No newline at end of file
+}