From e7c564133f6f50c2b14be9b0e2cad162f4531726 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Mon, 1 Feb 2010 11:02:36 -0800 Subject: Use single quotes on the -" ?> +' ?> Date: Mon, 1 Feb 2010 16:28:52 -0800 Subject: Add the scheduler component to the admin maintenance screen. --- modules/gallery/controllers/admin_maintenance.php | 2 ++ modules/gallery/views/admin_maintenance.html.php | 8 ++++++++ 2 files changed, 10 insertions(+) (limited to 'modules') diff --git a/modules/gallery/controllers/admin_maintenance.php b/modules/gallery/controllers/admin_maintenance.php index 3062ea09..d9363d30 100644 --- a/modules/gallery/controllers/admin_maintenance.php +++ b/modules/gallery/controllers/admin_maintenance.php @@ -44,6 +44,8 @@ class Admin_Maintenance_Controller extends Admin_Controller { $view->content->task_definitions = task::get_definitions(); $view->content->running_tasks = ORM::factory("task") ->where("done", "=", 0)->order_by("updated", "DESC")->find_all(); + $view->content->schedule_definitions = + module::is_active("scheduler") ? scheduler::get_definitions() : ""; $view->content->finished_tasks = ORM::factory("task") ->where("done", "=", 1)->order_by("updated", "DESC")->find_all(); print $view; diff --git a/modules/gallery/views/admin_maintenance.html.php b/modules/gallery/views/admin_maintenance.html.php index ac597715..15d04caa 100644 --- a/modules/gallery/views/admin_maintenance.html.php +++ b/modules/gallery/views/admin_maintenance.html.php @@ -33,12 +33,20 @@ class="g-dialog-link g-button ui-icon-left ui-state-default ui-corner-all"> + + callback?csrf=$csrf") ?>" + class="g-dialog-link g-button ui-icon-left ui-state-default ui-corner-all"> + + + + + count()): ?>

-- cgit v1.2.3 From 5ded9e8ac5935e41c08d1766974ce31890efd7f0 Mon Sep 17 00:00:00 2001 From: Tim Almdal Date: Mon, 1 Feb 2010 16:31:24 -0800 Subject: Refactor starting a task into the task helper so we can call it multiple times. --- modules/gallery/controllers/admin_maintenance.php | 5 +---- modules/gallery/helpers/task.php | 9 +++++++++ 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'modules') diff --git a/modules/gallery/controllers/admin_maintenance.php b/modules/gallery/controllers/admin_maintenance.php index d9363d30..487e77a6 100644 --- a/modules/gallery/controllers/admin_maintenance.php +++ b/modules/gallery/controllers/admin_maintenance.php @@ -58,13 +58,10 @@ class Admin_Maintenance_Controller extends Admin_Controller { public function start($task_callback) { access::verify_csrf(); - $tasks = task::get_definitions(); - $task = task::create($tasks[$task_callback], array()); + $task = task::start($task_callback); $view = new View("admin_maintenance_task.html"); $view->task = $task; - $task->log(t("Task %task_name started (task id %task_id)", - array("task_name" => $task->name, "task_id" => $task->id))); log::info("tasks", t("Task %task_name started (task id %task_id)", array("task_name" => $task->name, "task_id" => $task->id)), html::anchor("admin/maintenance", t("maintenance"))); diff --git a/modules/gallery/helpers/task.php b/modules/gallery/helpers/task.php index 645850d1..aa0eb94d 100644 --- a/modules/gallery/helpers/task.php +++ b/modules/gallery/helpers/task.php @@ -35,6 +35,15 @@ class task_Core { return $tasks; } + static function start($task_callback, $context=array()) { + $tasks = task::get_definitions(); + $task = task::create($tasks[$task_callback], array()); + + $task->log(t("Task %task_name started (task id %task_id)", + array("task_name" => $task->name, "task_id" => $task->id))); + return $task; + } + static function create($task_def, $context) { $task = ORM::factory("task"); $task->callback = $task_def->callback; -- cgit v1.2.3 From a10bdd8f0e436e8bb41430ca5d18fb2e1682773c Mon Sep 17 00:00:00 2001 From: Tim Almdal Date: Mon, 1 Feb 2010 17:13:29 -0800 Subject: Add a task scheduling module. In the admin maintenance, create an event from an existing task. When the task becomes due, it will run depending on traffic to the web site. It uses the gallery_shutdown event to time slice the task. --- modules/scheduler/controllers/admin_schedule.php | 105 ++++++++++++++++ modules/scheduler/helpers/scheduler.php | 133 +++++++++++++++++++++ modules/scheduler/helpers/scheduler_event.php | 64 ++++++++++ modules/scheduler/helpers/scheduler_installer.php | 42 +++++++ modules/scheduler/models/schedule.php | 33 +++++ modules/scheduler/module.info | 3 + modules/scheduler/views/admin_schedule.html.php | 11 ++ .../views/admin_schedule_confirm.html.php | 4 + .../scheduler/views/scheduler_definitions.html.php | 49 ++++++++ 9 files changed, 444 insertions(+) create mode 100644 modules/scheduler/controllers/admin_schedule.php create mode 100644 modules/scheduler/helpers/scheduler.php create mode 100644 modules/scheduler/helpers/scheduler_event.php create mode 100644 modules/scheduler/helpers/scheduler_installer.php create mode 100644 modules/scheduler/models/schedule.php create mode 100644 modules/scheduler/module.info create mode 100644 modules/scheduler/views/admin_schedule.html.php create mode 100644 modules/scheduler/views/admin_schedule_confirm.html.php create mode 100644 modules/scheduler/views/scheduler_definitions.html.php (limited to 'modules') diff --git a/modules/scheduler/controllers/admin_schedule.php b/modules/scheduler/controllers/admin_schedule.php new file mode 100644 index 00000000..6911cf86 --- /dev/null +++ b/modules/scheduler/controllers/admin_schedule.php @@ -0,0 +1,105 @@ +task_callback = $task_callback; + $schedule->next_run_datetime = time(); + $v = new View("admin_schedule.html"); + $v->form = scheduler::get_form("define", $schedule); + $v->method = "define"; + print $v; + } + + public function update_form($id) { + access::verify_csrf(); + + + $schedule = ORM::factory("schedule", $id); + $v = new View("admin_schedule.html"); + $v->form = scheduler::get_form("update", $schedule); + $v->method = "update"; + print $v; + } + + public function remove_form($id) { + access::verify_csrf(); + + $schedule = ORM::factory("schedule", $id); + + $v = new View("admin_schedule_confirm.html"); + $v->name = $schedule->name; + $v->form = new Forge("admin/schedule/remove/{$id}", "", "post", + array("id" => "g-remove-schedule")); + $group = $v->form->group("remove"); + $group->submit("")->value(t("Continue")); + print $v; + } + + public function remove($id) { + access::verify_csrf(); + $schedule = ORM::factory("schedule", $id); + $schedule->delete(); + + message::success(t("Removed scheduled task: %name", array("name" => $schedule->name))); + print json_encode(array("result" => "success", "reload" => 1)); + } + + public function define() { + $this->_handle_request("define"); + } + + public function update($id=null) { + $this->_handle_request("update", $id); + } + + private function _handle_request($method, $id=null) { + $schedule = ORM::factory("schedule", $id); + $form = scheduler::get_form($method, $schedule); + $valid = $form->validate(); + if ($valid) { + $schedule->name = $form->schedule_group->schedule_name->value; + $schedule->interval = $form->schedule_group->interval->value; + $schedule->next_run_datetime = + $this->_start_date($form->schedule_group->run_date->dow->selected, + $form->schedule_group->run_date->time->value); + $schedule->task_callback = $form->schedule_group->callback->value; + $schedule->save(); + if ($method == "define") { + message::success(t("Added scheduled task: %name", array("name" => $schedule->name))); + } else { + message::success(t("Updated scheduled task: %name", array("name" => $schedule->name))); + } + print json_encode(array("result" => "success", "reload" => 1)); + } else { + print json_encode(array("result" => "error", "form" => (string) $form)); + } + } + + private function _start_date($dow, $time) { + list ($hour, $minutes) = explode(":", $time); + $local_time = localtime(); + $days = ($dow < $local_time[6] ? 7 : 0) + $dow - $local_time[6]; + return + mktime($hour, $minutes, 0, $local_time[4] + 1, $local_time[3] + $days, 1900 + $local_time[5]); + } +} diff --git a/modules/scheduler/helpers/scheduler.php b/modules/scheduler/helpers/scheduler.php new file mode 100644 index 00000000..987b1b32 --- /dev/null +++ b/modules/scheduler/helpers/scheduler.php @@ -0,0 +1,133 @@ + t("Hourly"), "86400" => t("Daily"), + "604800" => t("Weekly"), "2419200" => t("Monthly")); + } + return $intervals; + } + + static function get_form($method, $schedule) { + if ($method == "define") { + $title = t("Create a scheduled event"); + $button_text = t("Create"); + } else { + $title = t("Update a scheduled event"); + $button_text = t("Update"); + } + + $id = empty($schedule->id) ? "" : "/$schedule->id"; + $form = new Forge("admin/schedule/$method{$id}", "", "post", + array("id" => "g-{$method}-schedule")); + $group = $form->group("schedule_group")->label($title); + $group->input("schedule_name") + ->label(t("Description")) + ->id("g-schedule-name") + ->rules("required|length[0, 128]") + ->error_messages("required", t("You must provide a description")) + ->error_messages("length", t("Your description is too long")) + ->value(!empty($schedule->name) ? $schedule->name : ""); + + list ($dow, $display_time) = scheduler::format_time($schedule->next_run_datetime); + $next = $group->group("run_date")->label(t("Scheduled Date")); + $next->dropdown("dow") + ->label(t("Day")) + ->id("g-schedule-day") + ->rules("required") + ->options(array(t("Sunday"), t("Monday"), t("Tuesday"), t("Wednesday"), + t("Thursday"), t("Friday"), t("Saturday"))) + ->selected($dow); + + $next->input("time") + ->label(t("Hour")) + ->id("g-schedule-time") + ->rules("required") + ->error_messages("required", t("You must provide a time")) + ->error_messages("time_invalid", t("Invalid time")) + ->callback("scheduler::valid_time") + ->value($display_time); + + // need to set the top padding to zero on g-define-schedule li.g-error + $group->dropdown("interval")->label(t("How often"))->id("g-schedule-frequency") + ->options(scheduler::intervals()) + ->rules("required") + ->error_messages("required", t("You must provide an interval")) + ->selected(!empty($schedule->interval) ? $schedule->interval : "2419200"); + $group->hidden("callback")->value($schedule->task_callback); + $group->submit("")->value($button_text); + + return $form; + } + + static function format_time($time) { + $local_time = localtime($time); + $display_time = str_pad($local_time[2], 2, "0", STR_PAD_LEFT) . ":" . + str_pad($local_time[1], 2, "0", STR_PAD_LEFT); + return array($local_time[6], $display_time); + } + + static function valid_time($field) { + if (preg_match("/([0-9]{1,2}):([0-9]{2})/", $field->value, $matches)) { + $hour = (int)$matches[1]; + $minutes = (int)$matches[2]; + if (!(0 <= $hour && $hour<= 23 || 0 <= $minutes && $minutes <= 59)) { + $field->add_error("time_invalid", 1); + } + } else { + $field->add_error("time_invalid", 1); + } + } + + static function get_definitions() { + $v = ""; + $events = ORM::factory("schedule") + ->order_by("next_run_datetime", "asc") + ->find_all(); + if ($events->count()) { + $v = new View("scheduler_definitions.html"); + $v->schedule_definitions = array(); + foreach ($events as $schedule) { + $entry[] = $schedule->id; + $entry[] = $schedule->name; + $run_date = strftime("%A, %b %e, %Y %H:%M ", $schedule->next_run_datetime); + $intervals = scheduler::intervals(); + $interval = $intervals[$schedule->interval]; + if (!empty($schedule->task_id)) { + $status = t("Running"); + } else if ($schedule->next_run_datetime < time()) { + $status = t("Overdue"); + } else { + $status = t("Scheduled"); + } + + $v->schedule_definitions[] = (object)array("id" => $schedule->id, + "name" => $schedule->name, + "run_date" => $run_date, + "interval" => $interval, + "status" => $status); + } + } + return $v; + } +} \ No newline at end of file diff --git a/modules/scheduler/helpers/scheduler_event.php b/modules/scheduler/helpers/scheduler_event.php new file mode 100644 index 00000000..365b1df1 --- /dev/null +++ b/modules/scheduler/helpers/scheduler_event.php @@ -0,0 +1,64 @@ +where("next_run_datetime", "<=", time()) + ->where("busy", "!=", 1) + ->order_by("next_run_datetime") + ->find_all(1); + + if ($schedule->count()) { + $schedule = $schedule->current(); + $schedule->busy = true; + $schedule->save(); + + try { + if (empty($schedule->task_id)) { + $task = task::start($schedule->task_callback); + $schedule->task_id = $task->id; + } + + $task = task::run($schedule->task_id); + + if ($task->done) { + $schedule->next_run_datetime += $schedule->interval; + $schedule->task_id = null; + } + + $schedule->busy = false; + $schedule->save(); + } catch (Exception $e) { + $schedule->busy = false; + $schedule->save(); + throw $e; + } + } + } catch (Exception $e) { + Kohana_Log::add("error", (string)$e); + } + } +} diff --git a/modules/scheduler/helpers/scheduler_installer.php b/modules/scheduler/helpers/scheduler_installer.php new file mode 100644 index 00000000..46be4a81 --- /dev/null +++ b/modules/scheduler/helpers/scheduler_installer.php @@ -0,0 +1,42 @@ +query("CREATE TABLE {schedules} ( + `id` int(9) NOT NULL auto_increment, + `name` varchar(128) NOT NULL, + `task_callback` varchar(128) NOT NULL, + `task_id` int(9) NULL, + `next_run_datetime` int(9) NOT NULL, + `interval` int(9) NOT NULL, + `busy` bool NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `run_date` (`next_run_datetime`, `busy`), + UNIQUE KEY (`name`)) + DEFAULT CHARSET=utf8;"); + module::set_version("scheduler", $version = 1); + } + + static function uninstall() { + $db = Database::instance(); + $db->query("DROP TABLE IF EXISTS {schedules}"); + } +} diff --git a/modules/scheduler/models/schedule.php b/modules/scheduler/models/schedule.php new file mode 100644 index 00000000..b83b5738 --- /dev/null +++ b/modules/scheduler/models/schedule.php @@ -0,0 +1,33 @@ +join("schedules_tasks", "task.id", "schedules_tasks.task_id") + ->where("schedule_id", "=", $this->id) + ->find_all(); + } + + public function add_task($task) { + db::build() + ->insert("schedules_tasks", array("schedule_id" => $this->id,"task_id" => $task->id)) + ->execute(); + } +} diff --git a/modules/scheduler/module.info b/modules/scheduler/module.info new file mode 100644 index 00000000..15355dfb --- /dev/null +++ b/modules/scheduler/module.info @@ -0,0 +1,3 @@ +name = "Scheduler" +description = "Schedule tasks to run at specific times and intervals" +version = 1 diff --git a/modules/scheduler/views/admin_schedule.html.php b/modules/scheduler/views/admin_schedule.html.php new file mode 100644 index 00000000..3d45dc53 --- /dev/null +++ b/modules/scheduler/views/admin_schedule.html.php @@ -0,0 +1,11 @@ + + + diff --git a/modules/scheduler/views/admin_schedule_confirm.html.php b/modules/scheduler/views/admin_schedule_confirm.html.php new file mode 100644 index 00000000..5d09654d --- /dev/null +++ b/modules/scheduler/views/admin_schedule_confirm.html.php @@ -0,0 +1,4 @@ + +

+

$name)) ?>

+ diff --git a/modules/scheduler/views/scheduler_definitions.html.php b/modules/scheduler/views/scheduler_definitions.html.php new file mode 100644 index 00000000..0ab46f2b --- /dev/null +++ b/modules/scheduler/views/scheduler_definitions.html.php @@ -0,0 +1,49 @@ + + -- cgit v1.2.3 From 6030d4eb6540d5c10eb1ab862d81a48420e1ec78 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Mon, 1 Feb 2010 21:13:07 -0800 Subject: Fix a bug in valid_name where it wasn't checking for name collisions on new users. --- modules/user/models/user.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'modules') diff --git a/modules/user/models/user.php b/modules/user/models/user.php index baac9315..9871ca00 100644 --- a/modules/user/models/user.php +++ b/modules/user/models/user.php @@ -122,7 +122,7 @@ class User_Model extends ORM implements User_Definition { public function valid_name(Validation $v, $field) { if (db::build()->from("users") ->where("name", "=", $this->name) - ->where("id", "<>", $this->id) + ->merge_where($this->id ? array(array("id", "<>", $this->id)) : null) ->count_records() == 1) { $v->add_error("name", "conflict"); } -- cgit v1.2.3 From 22ea03847ab8251a2f068b801599043014834e98 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Mon, 1 Feb 2010 21:27:01 -0800 Subject: Localize validation errors. --- modules/user/controllers/admin_users.php | 39 ++++++++++++++++++++++---------- modules/user/controllers/users.php | 1 + 2 files changed, 28 insertions(+), 12 deletions(-) (limited to 'modules') diff --git a/modules/user/controllers/admin_users.php b/modules/user/controllers/admin_users.php index 03d9858b..48847433 100644 --- a/modules/user/controllers/admin_users.php +++ b/modules/user/controllers/admin_users.php @@ -287,16 +287,22 @@ class Admin_Users_Controller extends Admin_Controller { $form = new Forge( "admin/users/edit_user/$user->id", "", "post", array("id" => "g-edit-user-form")); $group = $form->group("edit_user")->label(t("Edit user")); - $group->input("name")->label(t("Username"))->id("g-username")->value($user->name); - $group->inputs["name"]->error_messages( - "conflict", t("There is already a user with that username")); - $group->input("full_name")->label(t("Full name"))->id("g-fullname")->value($user->full_name); - self::_add_locale_dropdown($group, $user); - $group->password("password")->label(t("Password"))->id("g-password"); + $group->input("name")->label(t("Username"))->id("g-username")->value($user->name) + ->error_messages("conflict", t("There is already a user with that username")); + $group->input("full_name")->label(t("Full name"))->id("g-fullname")->value($user->full_name) + ->error_messages("length", t("This name is too long")); + $group->password("password")->label(t("Password"))->id("g-password") + ->error_messages("min_length", t("This password is too short")); $group->password("password2")->label(t("Confirm password"))->id("g-password2") + ->error_messages("matches", t("The passwords you entered do not match")) ->matches($group->password); - $group->input("email")->label(t("Email"))->id("g-email")->value($user->email); - $group->input("url")->label(t("URL"))->id("g-url")->value($user->url); + $group->input("email")->label(t("Email"))->id("g-email")->value($user->email) + ->error_messages("required", t("You must enter a valid email address")) + ->error_messages("length", t("This email address is too long")) + ->error_messages("email", t("You must enter a valid email address")); + $group->input("url")->label(t("URL"))->id("g-url")->value($user->url) + ->error_messages("url", t("You must enter a valid URL")); + self::_add_locale_dropdown($group, $user); $group->checkbox("admin")->label(t("Admin"))->id("g-admin")->checked($user->admin); module::event("user_edit_form_admin", $user, $form); @@ -308,13 +314,22 @@ class Admin_Users_Controller extends Admin_Controller { $form = new Forge("admin/users/add_user", "", "post", array("id" => "g-add-user-form")); $group = $form->group("add_user")->label(t("Add user")); $group->input("name")->label(t("Username"))->id("g-username") + ->error_messages("required", t("A name is required")) + ->error_messages("length", t("This name is too long")) ->error_messages("conflict", t("There is already a user with that username")); - $group->input("full_name")->label(t("Full name"))->id("g-fullname"); - $group->password("password")->label(t("Password"))->id("g-password"); + $group->input("full_name")->label(t("Full name"))->id("g-fullname") + ->error_messages("length", t("This name is too long")); + $group->password("password")->label(t("Password"))->id("g-password") + ->error_messages("min_length", t("This password is too short")); $group->password("password2")->label(t("Confirm password"))->id("g-password2") + ->error_messages("matches", t("The passwords you entered do not match")) ->matches($group->password); - $group->input("email")->label(t("Email"))->id("g-email"); - $group->input("url")->label(t("URL"))->id("g-url"); + $group->input("email")->label(t("Email"))->id("g-email") + ->error_messages("required", t("You must enter a valid email address")) + ->error_messages("length", t("This email address is too long")) + ->error_messages("email", t("You must enter a valid email address")); + $group->input("url")->label(t("URL"))->id("g-url") + ->error_messages("url", t("You must enter a valid URL")); self::_add_locale_dropdown($group); $group->checkbox("admin")->label(t("Admin"))->id("g-admin"); diff --git a/modules/user/controllers/users.php b/modules/user/controllers/users.php index d0c67dd1..43a92b44 100644 --- a/modules/user/controllers/users.php +++ b/modules/user/controllers/users.php @@ -90,6 +90,7 @@ class Users_Controller extends Controller { ->error_messages("matches", t("The passwords you entered do not match")); $group->input("email")->label(t("Email"))->id("g-email")->value($user->email) ->error_messages("email", t("You must enter a valid email address")) + ->error_messages("length", t("Your email address is too long")) ->error_messages("required", t("You must enter a valid email address")); $group->input("url")->label(t("URL"))->id("g-url")->value($user->url); -- cgit v1.2.3 From 81a1df4a504fdd7cdbef2b92e2b1257a65da98f9 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Mon, 1 Feb 2010 21:41:16 -0800 Subject: Localize the name "conflict" validation error when creating a new album. --- modules/gallery/helpers/album.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'modules') diff --git a/modules/gallery/helpers/album.php b/modules/gallery/helpers/album.php index 389f6e48..15e0c3ca 100644 --- a/modules/gallery/helpers/album.php +++ b/modules/gallery/helpers/album.php @@ -36,7 +36,8 @@ class album_Core { $group->input("name")->label(t("Directory name")) ->error_messages("no_slashes", t("The directory name can't contain the \"/\" character")) ->error_messages("required", t("You must provide a directory name")) - ->error_messages("length", t("Your directory name is too long")); + ->error_messages("length", t("Your directory name is too long")) + ->error_messages("conflict", t("There is already a movie, photo or album with this name")); $group->input("slug")->label(t("Internet Address")) ->error_messages( "not_url_safe", @@ -51,7 +52,8 @@ class album_Core { } static function get_edit_form($parent) { - $form = new Forge("albums/update/{$parent->id}", "", "post", array("id" => "g-edit-album-form")); + $form = new Forge( + "albums/update/{$parent->id}", "", "post", array("id" => "g-edit-album-form")); $form->hidden("from_id"); $group = $form->group("edit_item")->label(t("Edit Album")); @@ -61,8 +63,7 @@ class album_Core { $group->textarea("description")->label(t("Description"))->value($parent->description); if ($parent->id != 1) { $group->input("name")->label(t("Directory Name"))->value($parent->name) - ->error_messages( - "conflict", t("There is already a movie, photo or album with this name")) + ->error_messages("conflict", t("There is already a movie, photo or album with this name")) ->error_messages("no_slashes", t("The directory name can't contain a \"/\"")) ->error_messages("no_trailing_period", t("The directory name can't end in \".\"")) ->error_messages("required", t("You must provide a directory name")) -- cgit v1.2.3 From 19fee6b5e4ceb8a5f90cafe4ad770856ece108ef Mon Sep 17 00:00:00 2001 From: Tim Almdal Date: Tue, 2 Feb 2010 14:34:50 -0800 Subject: Refactor the admin maintenance screen so that events are used to pupluate the action buttons and other content such as the list of scheduled tasks. --- modules/gallery/controllers/admin_maintenance.php | 11 +++++++++-- modules/gallery/views/admin_maintenance.html.php | 16 +++++++--------- 2 files changed, 16 insertions(+), 11 deletions(-) (limited to 'modules') diff --git a/modules/gallery/controllers/admin_maintenance.php b/modules/gallery/controllers/admin_maintenance.php index 487e77a6..8e4845a9 100644 --- a/modules/gallery/controllers/admin_maintenance.php +++ b/modules/gallery/controllers/admin_maintenance.php @@ -44,10 +44,17 @@ class Admin_Maintenance_Controller extends Admin_Controller { $view->content->task_definitions = task::get_definitions(); $view->content->running_tasks = ORM::factory("task") ->where("done", "=", 0)->order_by("updated", "DESC")->find_all(); - $view->content->schedule_definitions = - module::is_active("scheduler") ? scheduler::get_definitions() : ""; $view->content->finished_tasks = ORM::factory("task") ->where("done", "=", 1)->order_by("updated", "DESC")->find_all(); + $task_buttons = + new ArrayObject(array((object)array("text" => t("run"), + "url" =>url::site("admin/maintenance/start")))); + module::event("admin_maintenance_task_buttons", $task_buttons); + $view->content->task_buttons = $task_buttons; + + $maintenance_content = new ArrayObject(); + module::event("admin_maintenance_content", $maintenance_content); + $view->content->task_maintenance_content = $maintenance_content; print $view; } diff --git a/modules/gallery/views/admin_maintenance.html.php b/modules/gallery/views/admin_maintenance.html.php index 15d04caa..444bf1fe 100644 --- a/modules/gallery/views/admin_maintenance.html.php +++ b/modules/gallery/views/admin_maintenance.html.php @@ -29,23 +29,21 @@ description ?> - callback?csrf=$csrf") ?>" + + url}/$task->callback?csrf=$csrf" ?>" class="g-dialog-link g-button ui-icon-left ui-state-default ui-corner-all"> - + text) ?> - - callback?csrf=$csrf") ?>" - class="g-dialog-link g-button ui-icon-left ui-state-default ui-corner-all"> - - - +
- + + + count()): ?>
-- cgit v1.2.3 From d5a231225d3b4f1fe6a08f350b93b6410b2d1914 Mon Sep 17 00:00:00 2001 From: Tim Almdal Date: Tue, 2 Feb 2010 14:37:26 -0800 Subject: Move the scheduler module to the -contrib repository for this release. --- modules/scheduler/controllers/admin_schedule.php | 105 ---------------- modules/scheduler/helpers/scheduler.php | 133 --------------------- modules/scheduler/helpers/scheduler_event.php | 64 ---------- modules/scheduler/helpers/scheduler_installer.php | 42 ------- modules/scheduler/models/schedule.php | 33 ----- modules/scheduler/module.info | 3 - modules/scheduler/views/admin_schedule.html.php | 11 -- .../views/admin_schedule_confirm.html.php | 4 - .../scheduler/views/scheduler_definitions.html.php | 49 -------- 9 files changed, 444 deletions(-) delete mode 100644 modules/scheduler/controllers/admin_schedule.php delete mode 100644 modules/scheduler/helpers/scheduler.php delete mode 100644 modules/scheduler/helpers/scheduler_event.php delete mode 100644 modules/scheduler/helpers/scheduler_installer.php delete mode 100644 modules/scheduler/models/schedule.php delete mode 100644 modules/scheduler/module.info delete mode 100644 modules/scheduler/views/admin_schedule.html.php delete mode 100644 modules/scheduler/views/admin_schedule_confirm.html.php delete mode 100644 modules/scheduler/views/scheduler_definitions.html.php (limited to 'modules') diff --git a/modules/scheduler/controllers/admin_schedule.php b/modules/scheduler/controllers/admin_schedule.php deleted file mode 100644 index 6911cf86..00000000 --- a/modules/scheduler/controllers/admin_schedule.php +++ /dev/null @@ -1,105 +0,0 @@ -task_callback = $task_callback; - $schedule->next_run_datetime = time(); - $v = new View("admin_schedule.html"); - $v->form = scheduler::get_form("define", $schedule); - $v->method = "define"; - print $v; - } - - public function update_form($id) { - access::verify_csrf(); - - - $schedule = ORM::factory("schedule", $id); - $v = new View("admin_schedule.html"); - $v->form = scheduler::get_form("update", $schedule); - $v->method = "update"; - print $v; - } - - public function remove_form($id) { - access::verify_csrf(); - - $schedule = ORM::factory("schedule", $id); - - $v = new View("admin_schedule_confirm.html"); - $v->name = $schedule->name; - $v->form = new Forge("admin/schedule/remove/{$id}", "", "post", - array("id" => "g-remove-schedule")); - $group = $v->form->group("remove"); - $group->submit("")->value(t("Continue")); - print $v; - } - - public function remove($id) { - access::verify_csrf(); - $schedule = ORM::factory("schedule", $id); - $schedule->delete(); - - message::success(t("Removed scheduled task: %name", array("name" => $schedule->name))); - print json_encode(array("result" => "success", "reload" => 1)); - } - - public function define() { - $this->_handle_request("define"); - } - - public function update($id=null) { - $this->_handle_request("update", $id); - } - - private function _handle_request($method, $id=null) { - $schedule = ORM::factory("schedule", $id); - $form = scheduler::get_form($method, $schedule); - $valid = $form->validate(); - if ($valid) { - $schedule->name = $form->schedule_group->schedule_name->value; - $schedule->interval = $form->schedule_group->interval->value; - $schedule->next_run_datetime = - $this->_start_date($form->schedule_group->run_date->dow->selected, - $form->schedule_group->run_date->time->value); - $schedule->task_callback = $form->schedule_group->callback->value; - $schedule->save(); - if ($method == "define") { - message::success(t("Added scheduled task: %name", array("name" => $schedule->name))); - } else { - message::success(t("Updated scheduled task: %name", array("name" => $schedule->name))); - } - print json_encode(array("result" => "success", "reload" => 1)); - } else { - print json_encode(array("result" => "error", "form" => (string) $form)); - } - } - - private function _start_date($dow, $time) { - list ($hour, $minutes) = explode(":", $time); - $local_time = localtime(); - $days = ($dow < $local_time[6] ? 7 : 0) + $dow - $local_time[6]; - return - mktime($hour, $minutes, 0, $local_time[4] + 1, $local_time[3] + $days, 1900 + $local_time[5]); - } -} diff --git a/modules/scheduler/helpers/scheduler.php b/modules/scheduler/helpers/scheduler.php deleted file mode 100644 index 987b1b32..00000000 --- a/modules/scheduler/helpers/scheduler.php +++ /dev/null @@ -1,133 +0,0 @@ - t("Hourly"), "86400" => t("Daily"), - "604800" => t("Weekly"), "2419200" => t("Monthly")); - } - return $intervals; - } - - static function get_form($method, $schedule) { - if ($method == "define") { - $title = t("Create a scheduled event"); - $button_text = t("Create"); - } else { - $title = t("Update a scheduled event"); - $button_text = t("Update"); - } - - $id = empty($schedule->id) ? "" : "/$schedule->id"; - $form = new Forge("admin/schedule/$method{$id}", "", "post", - array("id" => "g-{$method}-schedule")); - $group = $form->group("schedule_group")->label($title); - $group->input("schedule_name") - ->label(t("Description")) - ->id("g-schedule-name") - ->rules("required|length[0, 128]") - ->error_messages("required", t("You must provide a description")) - ->error_messages("length", t("Your description is too long")) - ->value(!empty($schedule->name) ? $schedule->name : ""); - - list ($dow, $display_time) = scheduler::format_time($schedule->next_run_datetime); - $next = $group->group("run_date")->label(t("Scheduled Date")); - $next->dropdown("dow") - ->label(t("Day")) - ->id("g-schedule-day") - ->rules("required") - ->options(array(t("Sunday"), t("Monday"), t("Tuesday"), t("Wednesday"), - t("Thursday"), t("Friday"), t("Saturday"))) - ->selected($dow); - - $next->input("time") - ->label(t("Hour")) - ->id("g-schedule-time") - ->rules("required") - ->error_messages("required", t("You must provide a time")) - ->error_messages("time_invalid", t("Invalid time")) - ->callback("scheduler::valid_time") - ->value($display_time); - - // need to set the top padding to zero on g-define-schedule li.g-error - $group->dropdown("interval")->label(t("How often"))->id("g-schedule-frequency") - ->options(scheduler::intervals()) - ->rules("required") - ->error_messages("required", t("You must provide an interval")) - ->selected(!empty($schedule->interval) ? $schedule->interval : "2419200"); - $group->hidden("callback")->value($schedule->task_callback); - $group->submit("")->value($button_text); - - return $form; - } - - static function format_time($time) { - $local_time = localtime($time); - $display_time = str_pad($local_time[2], 2, "0", STR_PAD_LEFT) . ":" . - str_pad($local_time[1], 2, "0", STR_PAD_LEFT); - return array($local_time[6], $display_time); - } - - static function valid_time($field) { - if (preg_match("/([0-9]{1,2}):([0-9]{2})/", $field->value, $matches)) { - $hour = (int)$matches[1]; - $minutes = (int)$matches[2]; - if (!(0 <= $hour && $hour<= 23 || 0 <= $minutes && $minutes <= 59)) { - $field->add_error("time_invalid", 1); - } - } else { - $field->add_error("time_invalid", 1); - } - } - - static function get_definitions() { - $v = ""; - $events = ORM::factory("schedule") - ->order_by("next_run_datetime", "asc") - ->find_all(); - if ($events->count()) { - $v = new View("scheduler_definitions.html"); - $v->schedule_definitions = array(); - foreach ($events as $schedule) { - $entry[] = $schedule->id; - $entry[] = $schedule->name; - $run_date = strftime("%A, %b %e, %Y %H:%M ", $schedule->next_run_datetime); - $intervals = scheduler::intervals(); - $interval = $intervals[$schedule->interval]; - if (!empty($schedule->task_id)) { - $status = t("Running"); - } else if ($schedule->next_run_datetime < time()) { - $status = t("Overdue"); - } else { - $status = t("Scheduled"); - } - - $v->schedule_definitions[] = (object)array("id" => $schedule->id, - "name" => $schedule->name, - "run_date" => $run_date, - "interval" => $interval, - "status" => $status); - } - } - return $v; - } -} \ No newline at end of file diff --git a/modules/scheduler/helpers/scheduler_event.php b/modules/scheduler/helpers/scheduler_event.php deleted file mode 100644 index 365b1df1..00000000 --- a/modules/scheduler/helpers/scheduler_event.php +++ /dev/null @@ -1,64 +0,0 @@ -where("next_run_datetime", "<=", time()) - ->where("busy", "!=", 1) - ->order_by("next_run_datetime") - ->find_all(1); - - if ($schedule->count()) { - $schedule = $schedule->current(); - $schedule->busy = true; - $schedule->save(); - - try { - if (empty($schedule->task_id)) { - $task = task::start($schedule->task_callback); - $schedule->task_id = $task->id; - } - - $task = task::run($schedule->task_id); - - if ($task->done) { - $schedule->next_run_datetime += $schedule->interval; - $schedule->task_id = null; - } - - $schedule->busy = false; - $schedule->save(); - } catch (Exception $e) { - $schedule->busy = false; - $schedule->save(); - throw $e; - } - } - } catch (Exception $e) { - Kohana_Log::add("error", (string)$e); - } - } -} diff --git a/modules/scheduler/helpers/scheduler_installer.php b/modules/scheduler/helpers/scheduler_installer.php deleted file mode 100644 index 46be4a81..00000000 --- a/modules/scheduler/helpers/scheduler_installer.php +++ /dev/null @@ -1,42 +0,0 @@ -query("CREATE TABLE {schedules} ( - `id` int(9) NOT NULL auto_increment, - `name` varchar(128) NOT NULL, - `task_callback` varchar(128) NOT NULL, - `task_id` int(9) NULL, - `next_run_datetime` int(9) NOT NULL, - `interval` int(9) NOT NULL, - `busy` bool NOT NULL DEFAULT 0, - PRIMARY KEY (`id`), - KEY `run_date` (`next_run_datetime`, `busy`), - UNIQUE KEY (`name`)) - DEFAULT CHARSET=utf8;"); - module::set_version("scheduler", $version = 1); - } - - static function uninstall() { - $db = Database::instance(); - $db->query("DROP TABLE IF EXISTS {schedules}"); - } -} diff --git a/modules/scheduler/models/schedule.php b/modules/scheduler/models/schedule.php deleted file mode 100644 index b83b5738..00000000 --- a/modules/scheduler/models/schedule.php +++ /dev/null @@ -1,33 +0,0 @@ -join("schedules_tasks", "task.id", "schedules_tasks.task_id") - ->where("schedule_id", "=", $this->id) - ->find_all(); - } - - public function add_task($task) { - db::build() - ->insert("schedules_tasks", array("schedule_id" => $this->id,"task_id" => $task->id)) - ->execute(); - } -} diff --git a/modules/scheduler/module.info b/modules/scheduler/module.info deleted file mode 100644 index 15355dfb..00000000 --- a/modules/scheduler/module.info +++ /dev/null @@ -1,3 +0,0 @@ -name = "Scheduler" -description = "Schedule tasks to run at specific times and intervals" -version = 1 diff --git a/modules/scheduler/views/admin_schedule.html.php b/modules/scheduler/views/admin_schedule.html.php deleted file mode 100644 index 3d45dc53..00000000 --- a/modules/scheduler/views/admin_schedule.html.php +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/modules/scheduler/views/admin_schedule_confirm.html.php b/modules/scheduler/views/admin_schedule_confirm.html.php deleted file mode 100644 index 5d09654d..00000000 --- a/modules/scheduler/views/admin_schedule_confirm.html.php +++ /dev/null @@ -1,4 +0,0 @@ - -

-

$name)) ?>

- diff --git a/modules/scheduler/views/scheduler_definitions.html.php b/modules/scheduler/views/scheduler_definitions.html.php deleted file mode 100644 index 0ab46f2b..00000000 --- a/modules/scheduler/views/scheduler_definitions.html.php +++ /dev/null @@ -1,49 +0,0 @@ - - -- cgit v1.2.3 From f69493d1384108bf65fc2b92d25fc3b854dee929 Mon Sep 17 00:00:00 2001 From: Tim Almdal Date: Tue, 2 Feb 2010 14:51:06 -0800 Subject: Update the xss golden file to reflect the changes to the admin screen. --- modules/gallery/tests/xss_data.txt | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'modules') diff --git a/modules/gallery/tests/xss_data.txt b/modules/gallery/tests/xss_data.txt index 65b45a08..8d7236fa 100644 --- a/modules/gallery/tests/xss_data.txt +++ b/modules/gallery/tests/xss_data.txt @@ -81,18 +81,20 @@ modules/gallery/views/admin_maintenance.html.php 24 DIRTY_ATTR log: modules/gallery/views/admin_maintenance.html.php 25 DIRTY_ATTR log::severity_class($task->severity) modules/gallery/views/admin_maintenance.html.php 26 DIRTY $task->name modules/gallery/views/admin_maintenance.html.php 29 DIRTY $task->description -modules/gallery/views/admin_maintenance.html.php 70 DIRTY_ATTR text::alternate("g-odd","g-even") -modules/gallery/views/admin_maintenance.html.php 70 DIRTY_ATTR $task->state=="stalled"?"g-warning":"" -modules/gallery/views/admin_maintenance.html.php 71 DIRTY_ATTR $task->state=="stalled"?"g-warning":"" -modules/gallery/views/admin_maintenance.html.php 72 DIRTY gallery::date_time($task->updated) -modules/gallery/views/admin_maintenance.html.php 75 DIRTY $task->name -modules/gallery/views/admin_maintenance.html.php 90 DIRTY $task->status -modules/gallery/views/admin_maintenance.html.php 141 DIRTY_ATTR text::alternate("g-odd","g-even") -modules/gallery/views/admin_maintenance.html.php 141 DIRTY_ATTR $task->state=="success"?"g-success":"g-error" -modules/gallery/views/admin_maintenance.html.php 142 DIRTY_ATTR $task->state=="success"?"g-success":"g-error" -modules/gallery/views/admin_maintenance.html.php 143 DIRTY gallery::date_time($task->updated) -modules/gallery/views/admin_maintenance.html.php 146 DIRTY $task->name -modules/gallery/views/admin_maintenance.html.php 158 DIRTY $task->status +modules/gallery/views/admin_maintenance.html.php 33 DIRTY_JS "{$button->url}/$task->callback?csrf=$csrf" +modules/gallery/views/admin_maintenance.html.php 45 DIRTY $content +modules/gallery/views/admin_maintenance.html.php 76 DIRTY_ATTR text::alternate("g-odd","g-even") +modules/gallery/views/admin_maintenance.html.php 76 DIRTY_ATTR $task->state=="stalled"?"g-warning":"" +modules/gallery/views/admin_maintenance.html.php 77 DIRTY_ATTR $task->state=="stalled"?"g-warning":"" +modules/gallery/views/admin_maintenance.html.php 78 DIRTY gallery::date_time($task->updated) +modules/gallery/views/admin_maintenance.html.php 81 DIRTY $task->name +modules/gallery/views/admin_maintenance.html.php 96 DIRTY $task->status +modules/gallery/views/admin_maintenance.html.php 147 DIRTY_ATTR text::alternate("g-odd","g-even") +modules/gallery/views/admin_maintenance.html.php 147 DIRTY_ATTR $task->state=="success"?"g-success":"g-error" +modules/gallery/views/admin_maintenance.html.php 148 DIRTY_ATTR $task->state=="success"?"g-success":"g-error" +modules/gallery/views/admin_maintenance.html.php 149 DIRTY gallery::date_time($task->updated) +modules/gallery/views/admin_maintenance.html.php 152 DIRTY $task->name +modules/gallery/views/admin_maintenance.html.php 164 DIRTY $task->status modules/gallery/views/admin_maintenance_show_log.html.php 8 DIRTY_JS url::site("admin/maintenance/save_log/$task->id?csrf=$csrf") modules/gallery/views/admin_maintenance_show_log.html.php 13 DIRTY $task->name modules/gallery/views/admin_maintenance_task.html.php 55 DIRTY $task->name -- cgit v1.2.3 From 1c0e5eaa0d9b65863c9fd04aff940c1a2b7e682d Mon Sep 17 00:00:00 2001 From: Tim Almdal Date: Tue, 2 Feb 2010 15:00:05 -0800 Subject: use html::purify to cleans the additional content on the admin maintence page. --- modules/gallery/tests/xss_data.txt | 1 - modules/gallery/views/admin_maintenance.html.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'modules') diff --git a/modules/gallery/tests/xss_data.txt b/modules/gallery/tests/xss_data.txt index 8d7236fa..e53502ee 100644 --- a/modules/gallery/tests/xss_data.txt +++ b/modules/gallery/tests/xss_data.txt @@ -82,7 +82,6 @@ modules/gallery/views/admin_maintenance.html.php 25 DIRTY_ATTR log: modules/gallery/views/admin_maintenance.html.php 26 DIRTY $task->name modules/gallery/views/admin_maintenance.html.php 29 DIRTY $task->description modules/gallery/views/admin_maintenance.html.php 33 DIRTY_JS "{$button->url}/$task->callback?csrf=$csrf" -modules/gallery/views/admin_maintenance.html.php 45 DIRTY $content modules/gallery/views/admin_maintenance.html.php 76 DIRTY_ATTR text::alternate("g-odd","g-even") modules/gallery/views/admin_maintenance.html.php 76 DIRTY_ATTR $task->state=="stalled"?"g-warning":"" modules/gallery/views/admin_maintenance.html.php 77 DIRTY_ATTR $task->state=="stalled"?"g-warning":"" diff --git a/modules/gallery/views/admin_maintenance.html.php b/modules/gallery/views/admin_maintenance.html.php index 444bf1fe..19375670 100644 --- a/modules/gallery/views/admin_maintenance.html.php +++ b/modules/gallery/views/admin_maintenance.html.php @@ -42,7 +42,7 @@
- + count()): ?> -- cgit v1.2.3 From 225fe81ce0121176dc11a646d78b3c3dcc479fa9 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Tue, 2 Feb 2010 20:50:34 -0800 Subject: Add an upgrade path to prevent the item title field from being empty. --- modules/gallery/helpers/gallery_installer.php | 14 +++++++++++++- modules/gallery/module.info | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'modules') diff --git a/modules/gallery/helpers/gallery_installer.php b/modules/gallery/helpers/gallery_installer.php index bffef8e6..761843b0 100644 --- a/modules/gallery/helpers/gallery_installer.php +++ b/modules/gallery/helpers/gallery_installer.php @@ -287,7 +287,7 @@ class gallery_installer { // @todo this string needs to be picked up by l10n_scanner module::set_var("gallery", "credits", "Powered by Gallery %version"); module::set_var("gallery", "simultaneous_upload_limit", 5); - module::set_version("gallery", 25); + module::set_version("gallery", 26); } static function upgrade($version) { @@ -514,6 +514,18 @@ class gallery_installer { } module::set_version("gallery", $version = 25); } + + if ($version == 25) { + db::build() + ->update("items") + ->set("title", new Database_Expression("`name`")) + ->and_open() + ->where("title", "IS", null) + ->or_where("title", "=", "") + ->close() + ->execute(); + module::set_version("gallery", $version = 26); + } } static function uninstall() { diff --git a/modules/gallery/module.info b/modules/gallery/module.info index 50a1505f..fd241066 100644 --- a/modules/gallery/module.info +++ b/modules/gallery/module.info @@ -1,3 +1,3 @@ name = "Gallery 3" description = "Gallery core application" -version = 25 +version = 26 -- cgit v1.2.3 From b351ee48fe09efd570e22a02b82174c39ad86d46 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Tue, 2 Feb 2010 21:34:20 -0800 Subject: Fix a bug in valid_password() where an empty password was considered valid. --- modules/user/models/user.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'modules') diff --git a/modules/user/models/user.php b/modules/user/models/user.php index 9871ca00..4404ee63 100644 --- a/modules/user/models/user.php +++ b/modules/user/models/user.php @@ -136,7 +136,7 @@ class User_Model extends ORM implements User_Definition { return; } - if (!$this->loaded() || $this->password_length) { + if (!$this->loaded() || isset($this->password_length)) { $minimum_length = module::get_var("user", "mininum_password_length", 5); if ($this->password_length < $minimum_length) { $v->add_error("password", "min_length"); -- cgit v1.2.3 From 6e1b761b12e13566875804c33efe2ae130ffa32e Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Tue, 2 Feb 2010 21:36:01 -0800 Subject: Require the current password to change your password. Fixes ticket #585. Separate out the password change form from the regular edit user form. Require the old password to enter a new one. While I'm at it, roll the password strength javascript into a Form_Script element so that we can get rid of the old view (which incidentally fixes a bug where the password strength meter would go away on form errors). --- modules/gallery/views/user_profile.html.php | 7 ++- modules/user/controllers/users.php | 78 +++++++++++++++++++++++------ modules/user/helpers/user.php | 6 +++ modules/user/views/user_form.html.php | 7 --- 4 files changed, 75 insertions(+), 23 deletions(-) delete mode 100644 modules/user/views/user_form.html.php (limited to 'modules') diff --git a/modules/gallery/views/user_profile.html.php b/modules/gallery/views/user_profile.html.php index f35f8c3f..78e1c579 100644 --- a/modules/gallery/views/user_profile.html.php +++ b/modules/gallery/views/user_profile.html.php @@ -57,13 +57,16 @@ - id}") ?>"> + id}") ?>"> + id}") ?>"> + + - \ No newline at end of file + diff --git a/modules/user/controllers/users.php b/modules/user/controllers/users.php index 43a92b44..c11f22ff 100644 --- a/modules/user/controllers/users.php +++ b/modules/user/controllers/users.php @@ -20,7 +20,6 @@ class Users_Controller extends Controller { public function update($id) { $user = user::lookup($id); - if ($user->guest || $user->id != identity::active_user()->id) { access::forbidden(); } @@ -29,9 +28,6 @@ class Users_Controller extends Controller { try { $valid = $form->validate(); $user->full_name = $form->edit_user->full_name->value; - if ($form->edit_user->password->value) { - $user->password = $form->edit_user->password->value; - } $user->email = $form->edit_user->email->value; $user->url = $form->edit_user->url->value; @@ -57,7 +53,38 @@ class Users_Controller extends Controller { $user->save(); module::event("user_edit_form_completed", $user, $form); - message::success(t("User information updated.")); + message::success(t("User information updated")); + print json_encode( + array("result" => "success", + "resource" => url::site("users/{$user->id}"))); + } else { + print json_encode(array("result" => "error", "form" => (string) $form)); + } + } + + public function change_password($id) { + $user = user::lookup($id); + if ($user->guest || $user->id != identity::active_user()->id) { + access::forbidden(); + } + + $form = $this->_get_change_password_form($user); + try { + $valid = $form->validate(); + $user->password = $form->change_password->password->value; + $user->validate(); + } catch (ORM_Validation_Exception $e) { + // Translate ORM validation errors into form error messages + foreach ($e->validation->errors() as $key => $error) { + $form->change_password->inputs[$key]->add_error($error, 1); + } + $valid = false; + } + + if ($valid) { + $user->save(); + module::event("user_change_password_form_completed", $user, $form); + message::success(t("Password changed")); print json_encode( array("result" => "success", "resource" => url::site("users/{$user->id}"))); @@ -72,22 +99,45 @@ class Users_Controller extends Controller { access::forbidden(); } - $v = new View("user_form.html"); - $v->form = $this->_get_edit_form($user); - print $v; + print $this->_get_edit_form($user); + } + + public function form_change_password($id) { + $user = user::lookup($id); + if ($user->guest || $user->id != identity::active_user()->id) { + access::forbidden(); + } + + print $this->_get_change_password_form($user); + } + + private function _get_change_password_form($user) { + $form = new Forge( + "users/change_password/$user->id", "", "post", array("id" => "g-change-password-user-form")); + $group = $form->group("change_password")->label(t("Change your password")); + $group->password("old_password")->label(t("Old password"))->id("g-password") + ->callback("user::valid_password") + ->error_messages("invalid", t("Incorrect password")); + $group->password("password")->label(t("New password"))->id("g-password") + ->error_messages("min_length", t("Your new password is too short")); + $group->script("") + ->text( + '$("form").ready(function(){$(\'input[name="password"]\').user_password_strength();});'); + $group->password("password2")->label(t("Confirm new password"))->id("g-password2") + ->matches($group->password) + ->error_messages("matches", t("The passwords you entered do not match")); + + module::event("user_change_password_form", $user, $form); + $group->submit("")->value(t("Save")); + return $form; } private function _get_edit_form($user) { $form = new Forge("users/update/$user->id", "", "post", array("id" => "g-edit-user-form")); - $group = $form->group("edit_user")->label(t("Edit User: %name", array("name" => $user->name))); + $group = $form->group("edit_user")->label(t("Edit your profile")); $group->input("full_name")->label(t("Full Name"))->id("g-fullname")->value($user->full_name) ->error_messages("length", t("Your name is too long")); self::_add_locale_dropdown($group, $user); - $group->password("password")->label(t("Password"))->id("g-password") - ->error_messages("min_length", t("Your password is too short")); - $group->password("password2")->label(t("Confirm Password"))->id("g-password2") - ->matches($group->password) - ->error_messages("matches", t("The passwords you entered do not match")); $group->input("email")->label(t("Email"))->id("g-email")->value($user->email) ->error_messages("email", t("You must enter a valid email address")) ->error_messages("length", t("Your email address is too long")) diff --git a/modules/user/helpers/user.php b/modules/user/helpers/user.php index 3561021f..7ceca6a5 100644 --- a/modules/user/helpers/user.php +++ b/modules/user/helpers/user.php @@ -70,6 +70,12 @@ class user_Core { return false; } + static function valid_password($password_input) { + if (!user::is_correct_password(identity::active_user(), $password_input->value)) { + $password_input->add_error("invalid", 1); + } + } + /** * Create the hashed passwords. * @param string $password a plaintext password diff --git a/modules/user/views/user_form.html.php b/modules/user/views/user_form.html.php deleted file mode 100644 index 4ce2b532..00000000 --- a/modules/user/views/user_form.html.php +++ /dev/null @@ -1,7 +0,0 @@ - - - -- cgit v1.2.3 From 99a7f470b93d35717f8d5979d05da6cf05a1dd20 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Tue, 2 Feb 2010 21:48:01 -0800 Subject: Protect password changes against brute force attacks. --- modules/gallery/helpers/auth.php | 10 ++++++++-- modules/gallery/helpers/gallery_event.php | 12 ++++++++++-- modules/user/controllers/users.php | 12 ++++++++++-- 3 files changed, 28 insertions(+), 6 deletions(-) (limited to 'modules') diff --git a/modules/gallery/helpers/auth.php b/modules/gallery/helpers/auth.php index 16f8915a..717cf40a 100644 --- a/modules/gallery/helpers/auth.php +++ b/modules/gallery/helpers/auth.php @@ -78,10 +78,16 @@ class auth_Core { } } + static function validate_too_many_failed_password_changes($password_input) { + if (self::too_many_failed_logins(identity::active_user()->name)) { + $password_input->add_error("too_many_failed_password_changes", 1); + } + } + /** * Record a failed login for this user */ - static function record_failed_login($name) { + static function record_failed_auth_attempts($name) { $failed_login = ORM::factory("failed_login") ->where("name", "=", $name) ->find(); @@ -96,7 +102,7 @@ class auth_Core { /** * Clear any failed logins for this user */ - static function record_successful_login($user) { + static function clear_failed_logins($user) { db::build() ->delete("failed_logins") ->where("name", "=", $user->name) diff --git a/modules/gallery/helpers/gallery_event.php b/modules/gallery/helpers/gallery_event.php index 6479e2c3..7b538c49 100644 --- a/modules/gallery/helpers/gallery_event.php +++ b/modules/gallery/helpers/gallery_event.php @@ -110,11 +110,19 @@ class gallery_event_Core { graphics::choose_default_toolkit(); module::clear_var("gallery", "choose_default_tookit"); } - auth::record_successful_login($user); + auth::clear_failed_auth_attempts($user); } static function user_login_failed($name) { - auth::record_failed_login($name); + auth::record_failed_auth_attempts($name); + } + + static function user_password_changed($user) { + auth::clear_failed_auth_attempts($user); + } + + static function user_password_change_failed($name) { + auth::record_failed_auth_attempts($name); } static function item_index_data($item, $data) { diff --git a/modules/user/controllers/users.php b/modules/user/controllers/users.php index c11f22ff..166ff8b2 100644 --- a/modules/user/controllers/users.php +++ b/modules/user/controllers/users.php @@ -77,7 +77,7 @@ class Users_Controller extends Controller { // Translate ORM validation errors into form error messages foreach ($e->validation->errors() as $key => $error) { $form->change_password->inputs[$key]->add_error($error, 1); - } + } $valid = false; } @@ -85,10 +85,14 @@ class Users_Controller extends Controller { $user->save(); module::event("user_change_password_form_completed", $user, $form); message::success(t("Password changed")); + module::event("user_password_change", $user); print json_encode( array("result" => "success", "resource" => url::site("users/{$user->id}"))); } else { + log::warning("user", t("Failed password change for %name", array("name" => $user->name))); + $name = $user->name; + module::event("user_password_change_failed", $name); print json_encode(array("result" => "error", "form" => (string) $form)); } } @@ -116,8 +120,12 @@ class Users_Controller extends Controller { "users/change_password/$user->id", "", "post", array("id" => "g-change-password-user-form")); $group = $form->group("change_password")->label(t("Change your password")); $group->password("old_password")->label(t("Old password"))->id("g-password") + ->callback("auth::validate_too_many_failed_password_changes") ->callback("user::valid_password") - ->error_messages("invalid", t("Incorrect password")); + ->error_messages("invalid", t("Incorrect password")) + ->error_messages( + "too_many_failed_password_changes", + t("Too many incorrect passwords. Try again later")); $group->password("password")->label(t("New password"))->id("g-password") ->error_messages("min_length", t("Your new password is too short")); $group->script("") -- cgit v1.2.3 From 1f51d663a0d651cfc8ff172357ce1b57823f8480 Mon Sep 17 00:00:00 2001 From: Tim Almdal Date: Wed, 3 Feb 2010 08:18:53 -0800 Subject: Correct missing function name. --- modules/gallery/helpers/auth.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'modules') diff --git a/modules/gallery/helpers/auth.php b/modules/gallery/helpers/auth.php index 717cf40a..45561861 100644 --- a/modules/gallery/helpers/auth.php +++ b/modules/gallery/helpers/auth.php @@ -102,7 +102,7 @@ class auth_Core { /** * Clear any failed logins for this user */ - static function clear_failed_logins($user) { + static function clear_failed_auth_attempts($user) { db::build() ->delete("failed_logins") ->where("name", "=", $user->name) -- cgit v1.2.3 From cc1d25ae0a706ea5969b88e871549800630b7e03 Mon Sep 17 00:00:00 2001 From: Tim Almdal Date: Thu, 4 Feb 2010 07:46:10 -0800 Subject: remove debugging statement --- modules/notification/helpers/notification.php | 2 -- 1 file changed, 2 deletions(-) (limited to 'modules') diff --git a/modules/notification/helpers/notification.php b/modules/notification/helpers/notification.php index dfeab9fc..3e4854d7 100644 --- a/modules/notification/helpers/notification.php +++ b/modules/notification/helpers/notification.php @@ -107,8 +107,6 @@ class notification { t("Photo \"%title\" updated", array("title" => $original->title)) : t("Movie \"%title\" updated", array("title" => $original->title))); - Kohana_Log::add("error",print_r($v->render(),1)); - self::_notify($subscribers, $item, $v->render(), $v->subject); } -- cgit v1.2.3 From aa4ed454737d45bf8140fd84f2eea2e04ed4a7d6 Mon Sep 17 00:00:00 2001 From: Tim Almdal Date: Wed, 3 Feb 2010 09:43:22 -0800 Subject: Differentiate between selected and non selected items. --- modules/organize/css/organize.css | 12 +++--------- modules/organize/views/organize_thumb_grid.html.php | 8 ++++---- themes/wind/css/screen.css | 1 + 3 files changed, 8 insertions(+), 13 deletions(-) (limited to 'modules') diff --git a/modules/organize/css/organize.css b/modules/organize/css/organize.css index d8923ea7..bc8e5b0c 100644 --- a/modules/organize/css/organize.css +++ b/modules/organize/css/organize.css @@ -87,16 +87,10 @@ } .g-organize-microthumb-grid-cell { - margin: 6px; - padding: 0 !important; - position: relative; -} - -.g-organize-microthumb { display: block; height: 100px; - margin: 0; - padding: .4em 0; + margin: 6px; + padding: .4em 0 !important; position: relative; text-align: center; width: 110px; @@ -106,7 +100,7 @@ z-index: 2000 !important; } -.g-organize-microthumb .ui-icon { +.g-organize-microthumb-grid-cell .ui-icon { bottom: 0; left: 0; position: absolute; diff --git a/modules/organize/views/organize_thumb_grid.html.php b/modules/organize/views/organize_thumb_grid.html.php index 9a9cd819..f29680f7 100644 --- a/modules/organize/views/organize_thumb_grid.html.php +++ b/modules/organize/views/organize_thumb_grid.html.php @@ -1,11 +1,11 @@ children(25, $offset) as $child): ?> -
  • -
    ui-state-active"> +
  • + thumb_img(array("class" => "g-thumbnail", "ref" => $child->id), 90, true) ?> is_album() ? " class=\"ui-icon ui-icon-note\"" : "" ?>> - +
  • diff --git a/themes/wind/css/screen.css b/themes/wind/css/screen.css index 12fa695d..630e8bbd 100644 --- a/themes/wind/css/screen.css +++ b/themes/wind/css/screen.css @@ -447,3 +447,4 @@ td { .rtl #g-view-menu #g-slideshow-link { background-image: url('../images/ico-view-slideshow-rtl.png'); } + -- cgit v1.2.3 From 95b26db82c3f835f66cdb32aef13aa84a6a206e5 Mon Sep 17 00:00:00 2001 From: Tim Almdal Date: Wed, 3 Feb 2010 13:56:04 -0800 Subject: 1) Simplify the layout of the organize dialog content. 2) Use CSS to layout the thmbnail grid to the full dialog size. Fixes ticker #848. 3) Separate css that can be themed into organize_theme.css. This isolates the themed components into a separate file for easy of overriding by a theme. --- modules/organize/css/organize.css | 47 ++++++++++++---------- modules/organize/css/organize_theme.css | 10 +++++ modules/organize/helpers/organize_theme.php | 1 + modules/organize/js/organize.js | 30 +++++++------- modules/organize/views/organize_dialog.html.php | 10 ++--- .../organize/views/organize_thumb_grid.html.php | 12 +++--- 6 files changed, 60 insertions(+), 50 deletions(-) create mode 100644 modules/organize/css/organize_theme.css (limited to 'modules') diff --git a/modules/organize/css/organize.css b/modules/organize/css/organize.css index bc8e5b0c..ef1f856b 100644 --- a/modules/organize/css/organize.css +++ b/modules/organize/css/organize.css @@ -3,18 +3,18 @@ */ #g-organize { - height: auto; margin: 0 !important; + min-height: auto; padding: 0 !important; position: relative; width: 100%; } #g-organize-content-pane { - height: auto; + height: 100%; margin: 0 !important; padding: 0 !important; - position: relative; + position: absolute; width: 100%; } @@ -23,11 +23,15 @@ */ #g-organize #g-organize-tree-container { - height: 100%; - overflow: auto; margin: 0; + min-height: 100%; padding: 0; - width: 19%; + position: relative; + width: 20%; +} + +#g-organize #g-organize-tree-container h3 { + margin-bottom: 0.1em; } #g-organize-album-tree { @@ -54,10 +58,10 @@ */ #g-organize #g-organize-detail { - height: 100%; margin: 0 !important; - overflow: hidden; + min-height: 100%; padding: 0 !important; + position: relative; width: 80%; } @@ -71,21 +75,17 @@ width: inherit; } -#g-organize-microthumb-panel { - height: 100%; - margin: 0 !important; - position: relative; - padding: 0 !important; - width: 100%; -} - #g-organize-microthumb-grid { - height: 100%; - overflow: auto; + bottom: 1.8em; + left: 0; + margin: 0 !important; + overflow-x: hidden; + overflow-y: auto; padding: .4em !important; - position: relative; + position: absolute; + right: 0; + top: 1.6em; } - .g-organize-microthumb-grid-cell { display: block; height: 100px; @@ -112,8 +112,13 @@ */ #g-organize-controls { + bottom: 0; + height: 1.9em; + left: 0; margin: 0 !important; - padding: .2em .4em; + padding: .1em .4em; + position: absolute; + right: 0; } #g-organize-controls select { diff --git a/modules/organize/css/organize_theme.css b/modules/organize/css/organize_theme.css new file mode 100644 index 00000000..727dca97 --- /dev/null +++ b/modules/organize/css/organize_theme.css @@ -0,0 +1,10 @@ +/** ******************************************************************* + * Organize styles that are theme overrideable + *********************************************************************/ +.g-organize-microthumb-grid-cell.ui-selected { + background: #DFEFFC !important; +} + +#g-organize-microthumb-grid { + border: 1px solid #79B7E7; +} diff --git a/modules/organize/helpers/organize_theme.php b/modules/organize/helpers/organize_theme.php index 61b6fe7d..c2914675 100644 --- a/modules/organize/helpers/organize_theme.php +++ b/modules/organize/helpers/organize_theme.php @@ -27,6 +27,7 @@ class organize_theme { // approach that lets us continue to use the Kohana cascading filesystem. $theme->script("organize.js"); $theme->css("organize.css"); + $theme->css("organize_theme.css"); } } } diff --git a/modules/organize/js/organize.js b/modules/organize/js/organize.js index 76eadf85..a89305f6 100644 --- a/modules/organize/js/organize.js +++ b/modules/organize/js/organize.js @@ -4,15 +4,15 @@ handle: ".ui-selected", distance: 10, cursorAt: { left: -10, top: -10}, - appendTo: "#g-organize-microthumb-panel", + appendTo: "#g-organize", helper: function(event, ui) { var selected = $(".ui-draggable.ui-selected img"); if (selected.length) { var set = $('
    ') .css({ - zIndex: 2000, - width: 80, - height: Math.ceil(selected.length / 5) * 16 + zIndex: 2000, + width: 80, + height: Math.ceil(selected.length / 5) * 16 }); var offset = $(this).offset(); var click = {left: event.pageX - offset.left, top: event.pageY - offset.top}; @@ -37,16 +37,16 @@ }, start: function(event, ui) { - $("#g-organize-microthumb-panel .ui-selected").hide(); + $("#g-organize-microthumb-grid .ui-selected").hide(); }, drag: function(event, ui) { - var top = $("#g-organize-microthumb-panel").offset().top; - var height = $("#g-organize-microthumb-panel").height(); + var top = $("#g-organize-microthumb-grid").offset().top; + var height = $("#g-organize-microthumb-grid").height(); if (ui.offset.top > height + top - 20) { - $("#g-organize-microthumb-panel").get(0).scrollTop += 100; + $("#g-organize-microthumb-grid").get(0).scrollTop += 100; } else if (ui.offset.top < top + 20) { - $("#g-organize-microthumb-panel").get(0).scrollTop = Math.max(0, $("#g-organize-microthumb-panel").get(0).scrollTop - 100); + $("#g-organize-microthumb-grid").get(0).scrollTop = Math.max(0, $("#g-organize-microthumb-grid").get(0).scrollTop - 100); } } }, @@ -63,7 +63,7 @@ target_id = $(".currentDropTarget").attr("ref"); } else { before_or_after = "after"; - target_id = $("#g-organize-microthumb-grid li:last").attr("ref"); + target_id = $("#g-organize-microthumb-grid div:last").attr("ref"); } $.organize.do_drop({ url: rearrange_url @@ -92,7 +92,7 @@ }, do_drop: function(options) { - $("#g-organize-microthumb-panel").selectable("destroy"); + $("#g-organize-microthumb-grid").selectable("destroy"); var source_ids = []; $(options.source).each(function(i) { source_ids.push($(this).attr("ref")); @@ -102,7 +102,7 @@ $.post(options.url, { "source_ids[]": source_ids }, function(data) { - $.organize._refresh(data); + $.organize._refresh(data); }, "json"); } @@ -141,8 +141,6 @@ $("#g-dialog").dialog("option", "zIndex", 70); $("#g-dialog").bind("dialogopen", function(event, ui) { $("#g-organize").height($("#g-dialog").innerHeight() - 20); - $("#g-organize-microthumb-grid").height($("#g-dialog").innerHeight() - 91); - $("#g-organize-tree-container").height($("#g-dialog").innerHeight() - 60); }); $("#g-dialog").bind("dialogclose", function(event, ui) { @@ -210,11 +208,11 @@ if ($(parent).hasClass("g-view-only")) { return; } - $("#g-organize-microthumb-panel").selectable("destroy"); + $("#g-organize-microthumb-grid").selectable("destroy"); var id = $(event.currentTarget).attr("ref"); $(".g-organize-album-text.ui-state-focus").removeClass("ui-state-focus"); $(".g-organize-album-text[ref=" + id + "]").addClass("ui-state-focus"); - var url = $("#g-organize-microthumb-panel").attr("ref").replace("__ITEM_ID__", id).replace("__OFFSET__", 0); + var url = $("#g-organize-microthumb-grid").attr("ref").replace("__ITEM_ID__", id).replace("__OFFSET__", 0); $.get(url, {}, function(data) { $("#g-organize-microthumb-grid").html(data.grid); diff --git a/modules/organize/views/organize_dialog.html.php b/modules/organize/views/organize_dialog.html.php index 435f5ae3..3d824886 100644 --- a/modules/organize/views/organize_dialog.html.php +++ b/modules/organize/views/organize_dialog.html.php @@ -15,14 +15,12 @@
    -
    +
  • + +
    "> -
      -
    • -
    -
      -
    children(25, $offset) as $child): ?> -
  • - - thumb_img(array("class" => "g-thumbnail", "ref" => $child->id), 90, true) ?> - is_album() ? " class=\"ui-icon ui-icon-note\"" : "" ?>> - -
  • +
    " + ref="id ?>"> + thumb_img(array("class" => "g-thumbnail", "ref" => $child->id), 90, true) ?> + is_album() ? " class=\"ui-icon ui-icon-note\"" : "" ?>> +
    children_count() > $offset): ?> -- cgit v1.2.3 From 48efd21556dbc4f9f4046f9d2f685726b1c006ff Mon Sep 17 00:00:00 2001 From: Tim Almdal Date: Wed, 3 Feb 2010 22:48:47 -0800 Subject: Provide visual feedback when over tree branches. --- modules/organize/css/organize.css | 7 +++++++ modules/organize/css/organize_theme.css | 5 +++-- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'modules') diff --git a/modules/organize/css/organize.css b/modules/organize/css/organize.css index ef1f856b..686ec034 100644 --- a/modules/organize/css/organize.css +++ b/modules/organize/css/organize.css @@ -53,6 +53,11 @@ width: auto; } +.g-organize-album-text:hover { + border-width: 1px; + border-style: dotted; +} + /******************************************************************* * Album panel styles */ @@ -76,6 +81,8 @@ } #g-organize-microthumb-grid { + border-width: 1px; + border-style: solid; bottom: 1.8em; left: 0; margin: 0 !important; diff --git a/modules/organize/css/organize_theme.css b/modules/organize/css/organize_theme.css index 727dca97..4248f095 100644 --- a/modules/organize/css/organize_theme.css +++ b/modules/organize/css/organize_theme.css @@ -5,6 +5,7 @@ background: #DFEFFC !important; } -#g-organize-microthumb-grid { - border: 1px solid #79B7E7; +#g-organize-microthumb-grid, +.g-organize-album-text:hover { + border-color: #79B7E7; } -- cgit v1.2.3 From c82061391717a746fb61696ff48152b1e907b49c Mon Sep 17 00:00:00 2001 From: Tim Almdal Date: Thu, 4 Feb 2010 08:19:38 -0800 Subject: Don't change the current album when items are dragged to a different album in the album tree. Fixes ticket #959. --- modules/organize/controllers/organize.php | 8 ++++++-- modules/organize/views/organize_tree.html.php | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'modules') diff --git a/modules/organize/controllers/organize.php b/modules/organize/controllers/organize.php index 4a4b9f13..8e2c0cee 100644 --- a/modules/organize/controllers/organize.php +++ b/modules/organize/controllers/organize.php @@ -48,8 +48,12 @@ class Organize_Controller extends Controller { access::required("view", $target_album); access::required("add", $target_album); + $source_album = null; foreach (Input::instance()->post("source_ids") as $source_id) { $source = ORM::factory("item", $source_id); + if (empty($source_album)) { // get the source_album + $source_album = $source->parent(); + } if (!$source->contains($target_album)) { access::required("edit", $source); item::move($source, $target_album); @@ -57,8 +61,8 @@ class Organize_Controller extends Controller { } print json_encode( - array("tree" => (string)self::_expanded_tree(ORM::factory("item", 1), $target_album), - "grid" => (string)self::_get_micro_thumb_grid($target_album, 0))); + array("tree" => (string)self::_expanded_tree(ORM::factory("item", 1), $source_album), + "grid" => (string)self::_get_micro_thumb_grid($source_album, 0))); } function rearrange($target_id, $before_or_after) { diff --git a/modules/organize/views/organize_tree.html.php b/modules/organize/views/organize_tree.html.php index 513c0625..49ed01fc 100644 --- a/modules/organize/views/organize_tree.html.php +++ b/modules/organize/views/organize_tree.html.php @@ -15,7 +15,7 @@
  • " ref="id ?>"> - + " ref="id ?>"> title) ?>
  • -- cgit v1.2.3 From 3d2a3bee572b8ccddf08982c5596b35a0e983b85 Mon Sep 17 00:00:00 2001 From: Tim Almdal Date: Thu, 4 Feb 2010 08:42:55 -0800 Subject: Provide better formating for the album tree in rtl mode. --- modules/organize/css/organize.css | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'modules') diff --git a/modules/organize/css/organize.css b/modules/organize/css/organize.css index 686ec034..a561a134 100644 --- a/modules/organize/css/organize.css +++ b/modules/organize/css/organize.css @@ -42,6 +42,10 @@ padding: 0 0 .2em 1.2em; } +.rtl #g-organize-album-tree ul li { + padding: 0 1.2em .2em 0; +} + .g-organize-album span { cursor: pointer; } @@ -53,6 +57,13 @@ width: auto; } +.rtl .g-organize-album-text { + cursor: pointer; + display: block; + margin: 2px 2px 1px 1px; + width: auto; +} + .g-organize-album-text:hover { border-width: 1px; border-style: dotted; -- cgit v1.2.3 From eff7de3852c1a11ee82fdb7467a6e51fa106af0f Mon Sep 17 00:00:00 2001 From: Tim Almdal Date: Thu, 4 Feb 2010 08:51:15 -0800 Subject: If the locale is rtl then change 'after' to 'before' or vice-versa. Fixes ticket #939. --- modules/organize/controllers/organize.php | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'modules') diff --git a/modules/organize/controllers/organize.php b/modules/organize/controllers/organize.php index 8e2c0cee..38e8ca58 100644 --- a/modules/organize/controllers/organize.php +++ b/modules/organize/controllers/organize.php @@ -73,6 +73,10 @@ class Organize_Controller extends Controller { access::required("view", $album); access::required("edit", $album); + if (locales::is_rtl()) { // invert the position if the locale is rtl + $before_or_after = $before_or_after == "after" ? "before" : "after"; + } + $source_ids = Input::instance()->post("source_ids", array()); if ($album->sort_column != "weight") { -- cgit v1.2.3 From a8d30dcecf40210096a4adaea0f5d231b69ead6a Mon Sep 17 00:00:00 2001 From: Tim Almdal Date: Fri, 5 Feb 2010 08:08:12 -0800 Subject: Improve the handling of the drop target marker when dragging items within the grid. Originally the borders disappeared not they don't. A single element is used to contain the drop target marker. This element contains all the position information for the drop handler to determine where the item should be positioned. In addition, the drop target marker is removed when the drag extends outside the grid and is re shown, when the cursor re-enters the thumbnail grid. --- modules/organize/css/organize_theme.css | 5 ++ modules/organize/js/organize.js | 100 +++++++++++++++++++++++--------- 2 files changed, 77 insertions(+), 28 deletions(-) (limited to 'modules') diff --git a/modules/organize/css/organize_theme.css b/modules/organize/css/organize_theme.css index 4248f095..3d289755 100644 --- a/modules/organize/css/organize_theme.css +++ b/modules/organize/css/organize_theme.css @@ -6,6 +6,11 @@ } #g-organize-microthumb-grid, +#g-organize-drop-target-marker, .g-organize-album-text:hover { border-color: #79B7E7; } + +#g-organize-drop-target-marker { + background-color: #79B7E7; +} diff --git a/modules/organize/js/organize.js b/modules/organize/js/organize.js index a89305f6..56377ce3 100644 --- a/modules/organize/js/organize.js +++ b/modules/organize/js/organize.js @@ -56,19 +56,11 @@ tolerance: "pointer", greedy: true, drop: function(event, ui) { - var before_or_after = null; - var target_id = null; - if ($(".currentDropTarget").length) { - before_or_after = $(".currentDropTarget").css("borderLeftStyle") == "solid" ? "before" : "after"; - target_id = $(".currentDropTarget").attr("ref"); - } else { - before_or_after = "after"; - target_id = $("#g-organize-microthumb-grid div:last").attr("ref"); - } + var target = $("#g-organize-drop-target-marker").data("drop_position"); $.organize.do_drop({ url: rearrange_url - .replace("__TARGET_ID__", target_id) - .replace("__BEFORE__", before_or_after), + .replace("__TARGET_ID__", target.id) + .replace("__BEFORE__", target.before ? "before" : "after"), source: $(ui.helper).children("img") }); } @@ -120,14 +112,58 @@ $.organize.set_handlers(); }, - mouse_move_handler: function(event) { + grid_mouse_leave_handler: function(event) { + if ($(".g-drag-helper").length && $("#g-organize-drop-target-marker").length) { + $("#g-organize-drop-target-marker").remove(); + } + }, + + grid_mouse_move_handler: function(event) { if ($(".g-drag-helper").length) { - $(".g-organize-microthumb-grid-cell").css({borderStyle: "hidden", margin: "6px"}); - $(".currentDropTarget").removeClass("currentDropTarget"); - var borderStyle = event.pageX < $(this).offset().left + $(this).width() / 2 ? - {borderLeftStyle: "solid", marginLeft: "2px"} : {borderRightStyle: "solid", marginRight: "2px"}; - $(this).addClass("currentDropTarget") - .css(borderStyle); + var cellSize = $("#g-organize").data("cellSize"); + var thumbnailCount = $(".g-organize-microthumb-grid-cell:visible").length; + var rows = Math.ceil(thumbnailCount / cellSize.columns); + + + var itemPos = { + col: Math.floor((event.pageX - $(this).offset().left) / cellSize.width), + row: Math.floor((event.pageY - $(this).offset().top) / cellSize.height) + }; + var itemIndex = itemPos.row * cellSize.columns + itemPos.col; + + var item; + if (itemIndex < thumbnailCount) { + item = $(".g-organize-microthumb-grid-cell:visible").get(itemIndex); + } else { + item = $(".g-organize-microthumb-grid-cell:visible:last"); + } + + var old_position = {top: 0, left: 0}; + if ($("#g-organize-drop-target-marker").length) { + old_position = $("#g-organize-drop-target-marker").position(); + } + var before = event.pageX < ($(item).offset().left + $(item).width() / 2); + + var left = (before && itemIndex < thumbnailCount ? $(item).position().left : $(item).position().left + cellSize.width) - 3; + var top = $(item).position().top + 6; + + if (old_position.top != top || old_position.left != left) { + if ($("#g-organize-drop-target-marker").length) { + $("#g-organize-drop-target-marker").remove(); + } + var set = $('
    ') + .css({zIndex: 2000, + width: 2, + height: 112, + borderWidth: 1, + borderStyle: "solid", + position: "absolute", + top: top, + left: left + }) + .data("drop_position", {id: $(item).attr("ref"), position: before}); + $("#g-organize-microthumb-grid").append(set); + } } }, @@ -140,7 +176,15 @@ $(".sf-menu li.sfHover ul").css("z-index", 68); $("#g-dialog").dialog("option", "zIndex", 70); $("#g-dialog").bind("dialogopen", function(event, ui) { - $("#g-organize").height($("#g-dialog").innerHeight() - 20); + var outerHeight = $(".g-organize-microthumb-grid-cell").outerHeight(true); + var outerWidth = $(".g-organize-microthumb-grid-cell").outerWidth(true); + $("#g-organize") + .height($("#g-dialog").innerHeight() - 20) + .data("cellSize", { + height: outerHeight, + width: outerWidth, + columns: Math.floor($("#g-organize-microthumb-grid").innerWidth() / outerWidth) + }); }); $("#g-dialog").bind("dialogclose", function(event, ui) { @@ -162,11 +206,11 @@ set_handlers: function() { $("#g-organize-microthumb-grid") .selectable({filter: ".g-organize-microthumb-grid-cell"}) + .mousemove($.organize.grid_mouse_move_handler) + .mouseleave($.organize.grid_mouse_leave_handler) .droppable($.organize.content_droppable); $(".g-organize-microthumb-grid-cell") - .draggable($.organize.micro_thumb_draggable) - .mouseleave($.organize.mouse_leave_handler) - .mousemove($.organize.mouse_move_handler); + .draggable($.organize.micro_thumb_draggable); $(".g-organize-album").droppable($.organize.branch_droppable); $(".g-organize-album-text").click($.organize.show_album); $("#g-organize-album-tree .ui-icon-plus,#g-organize-album-tree .ui-icon-minus").click($.organize.toggle_branch); @@ -214,12 +258,12 @@ $(".g-organize-album-text[ref=" + id + "]").addClass("ui-state-focus"); var url = $("#g-organize-microthumb-grid").attr("ref").replace("__ITEM_ID__", id).replace("__OFFSET__", 0); $.get(url, {}, - function(data) { - $("#g-organize-microthumb-grid").html(data.grid); - $("#g-organize-sort-column").attr("value", data.sort_column); - $("#g-organize-sort-order").attr("value", data.sort_order); - $.organize.set_handlers(); - }, + function(data) { + $("#g-organize-microthumb-grid").html(data.grid); + $("#g-organize-sort-column").attr("value", data.sort_column); + $("#g-organize-sort-order").attr("value", data.sort_order); + $.organize.set_handlers(); + }, "json"); }, -- cgit v1.2.3 From 8518fb267404c16fff54492b3e37fb03c64f0455 Mon Sep 17 00:00:00 2001 From: Tim Almdal Date: Fri, 5 Feb 2010 08:53:38 -0800 Subject: Add busy animation when doing the drop. --- modules/organize/js/organize.js | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'modules') diff --git a/modules/organize/js/organize.js b/modules/organize/js/organize.js index 56377ce3..9b9b6495 100644 --- a/modules/organize/js/organize.js +++ b/modules/organize/js/organize.js @@ -91,12 +91,24 @@ }); if (source_ids.length) { + var loading = $('
     
    ') + .css({bottom: 5, + opacity: .5, + left: 0, + position: "absolute", + right: 0, + top: 0, + zIndex: 2000 + }); + $("#g-organize-microthumb-grid").append(loading); + $.post(options.url, - { "source_ids[]": source_ids }, - function(data) { - $.organize._refresh(data); - }, - "json"); + { "source_ids[]": source_ids }, + function(data) { + $.organize._refresh(data); + $(".g-dialog-loading-large").remove(); + }, + "json"); } }, -- cgit v1.2.3 From 94cc061e8b51704c6cb38d637aa3082ee4adb1ca Mon Sep 17 00:00:00 2001 From: Tim Almdal Date: Fri, 5 Feb 2010 10:25:55 -0800 Subject: Correct alignment in IE7. fixes ticket #795 --- modules/organize/css/organize.css | 8 +++++++- modules/organize/views/organize_dialog.html.php | 8 +++++--- modules/organize/views/organize_tree.html.php | 12 ++++++------ 3 files changed, 18 insertions(+), 10 deletions(-) (limited to 'modules') diff --git a/modules/organize/css/organize.css b/modules/organize/css/organize.css index a561a134..22d6e051 100644 --- a/modules/organize/css/organize.css +++ b/modules/organize/css/organize.css @@ -88,7 +88,7 @@ #g-organize #g-organize-detail .g-message-block li { padding-bottom: .2em; padding-top: .2em; - width: inherit; + width: auto; } #g-organize-microthumb-grid { @@ -139,7 +139,13 @@ right: 0; } +#g-organize-controls #g-organize-sort-order-text { + padding: .2em 0 0 0; +} + + #g-organize-controls select { + margin-left: .42em; display: inline; } diff --git a/modules/organize/views/organize_dialog.html.php b/modules/organize/views/organize_dialog.html.php index 3d824886..e36264f9 100644 --- a/modules/organize/views/organize_dialog.html.php +++ b/modules/organize/views/organize_dialog.html.php @@ -26,9 +26,11 @@
    - - "g-organize-sort-column"), album::get_sort_order_options(), $album->sort_column) ?> - "g-organize-sort-order"), array("ASC" => "Ascending", "DESC" => "Descending"), $album->sort_order) ?> +
      +
    • +
    • + "g-organize-sort-column"), album::get_sort_order_options(), $album->sort_column) ?>
    • + "g-organize-sort-order"), array("ASC" => "Ascending", "DESC" => "Descending"), $album->sort_order) ?>
    diff --git a/modules/organize/views/organize_tree.html.php b/modules/organize/views/organize_tree.html.php index 49ed01fc..3912c1f6 100644 --- a/modules/organize/views/organize_tree.html.php +++ b/modules/organize/views/organize_tree.html.php @@ -1,9 +1,9 @@ -
  • " +
  • " ref="id ?>"> - + - " + " ref="id ?>"> title) ?> @@ -12,10 +12,10 @@ contains($selected)): ?> $selected, "album" => $child)); ?> -
  • " +
  • " ref="id ?>"> - - " ref="id ?>"> + + " ref="id ?>"> title) ?>
  • -- cgit v1.2.3 From bc14f33ae134dd9c93bf35ad90c91d84fdac587f Mon Sep 17 00:00:00 2001 From: Tim Almdal Date: Fri, 5 Feb 2010 11:15:28 -0800 Subject: Fix an issue where the dragged item was always inserted after the target image. --- modules/organize/js/organize.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'modules') diff --git a/modules/organize/js/organize.js b/modules/organize/js/organize.js index 9b9b6495..f85e02b5 100644 --- a/modules/organize/js/organize.js +++ b/modules/organize/js/organize.js @@ -60,7 +60,7 @@ $.organize.do_drop({ url: rearrange_url .replace("__TARGET_ID__", target.id) - .replace("__BEFORE__", target.before ? "before" : "after"), + .replace("__BEFORE__", target.position ? "before" : "after"), source: $(ui.helper).children("img") }); } -- cgit v1.2.3 From 64c37aed0787ee01c89df382f9645f35a7e6ba14 Mon Sep 17 00:00:00 2001 From: Tim Almdal Date: Fri, 5 Feb 2010 14:38:24 -0800 Subject: continuing cleaninup organize javascript especially the drag and drop functionality. --- modules/organize/js/organize.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'modules') diff --git a/modules/organize/js/organize.js b/modules/organize/js/organize.js index 9b9b6495..6f581208 100644 --- a/modules/organize/js/organize.js +++ b/modules/organize/js/organize.js @@ -4,7 +4,7 @@ handle: ".ui-selected", distance: 10, cursorAt: { left: -10, top: -10}, - appendTo: "#g-organize", + appendTo: "#g-organize-content-pane", helper: function(event, ui) { var selected = $(".ui-draggable.ui-selected img"); if (selected.length) { @@ -14,8 +14,6 @@ width: 80, height: Math.ceil(selected.length / 5) * 16 }); - var offset = $(this).offset(); - var click = {left: event.pageX - offset.left, top: event.pageY - offset.top}; selected.each(function(i) { var row = parseInt(i / 5); @@ -43,11 +41,13 @@ drag: function(event, ui) { var top = $("#g-organize-microthumb-grid").offset().top; var height = $("#g-organize-microthumb-grid").height(); + var scrollTop = $("#g-organize-microthumb-grid").scrollTop(); if (ui.offset.top > height + top - 20) { - $("#g-organize-microthumb-grid").get(0).scrollTop += 100; + scrollTop += 100; } else if (ui.offset.top < top + 20) { - $("#g-organize-microthumb-grid").get(0).scrollTop = Math.max(0, $("#g-organize-microthumb-grid").get(0).scrollTop - 100); + scrollTop = Math.max(0, scrollTop - 100); } + $("#g-organize-microthumb-grid").scrollTop(scrollTop); } }, -- cgit v1.2.3 From 73527fba611d7d441c6fd2e988510cb0c4e46f99 Mon Sep 17 00:00:00 2001 From: Tim Almdal Date: Fri, 5 Feb 2010 23:38:06 -0800 Subject: 1) Manually added the .selectee class on subsequent downloads. fixes ticket #970 2) Reworked the calculated of the drop target marker to account for scrolling. --- modules/organize/js/organize.js | 64 +++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 37 deletions(-) (limited to 'modules') diff --git a/modules/organize/js/organize.js b/modules/organize/js/organize.js index fd8a0fff..276fc3fa 100644 --- a/modules/organize/js/organize.js +++ b/modules/organize/js/organize.js @@ -59,8 +59,8 @@ var target = $("#g-organize-drop-target-marker").data("drop_position"); $.organize.do_drop({ url: rearrange_url - .replace("__TARGET_ID__", target.id) - .replace("__BEFORE__", target.position ? "before" : "after"), + .replace("__TARGET_ID__", target.id) + .replace("__BEFORE__", target.position ? "before" : "after"), source: $(ui.helper).children("img") }); } @@ -72,8 +72,8 @@ greedy: true, drop: function(event, ui) { if ($(event.target).hasClass("g-view-only")) { + $("#g-organize-drop-target-marker").remove(); $(".ui-selected").show(); - $(".g-organize-microthumb-grid-cell").css("borderStyle", "none"); } else { $.organize.do_drop({ url: move_url.replace("__ALBUM_ID__", $(event.target).attr("ref")), @@ -134,48 +134,35 @@ if ($(".g-drag-helper").length) { var cellSize = $("#g-organize").data("cellSize"); var thumbnailCount = $(".g-organize-microthumb-grid-cell:visible").length; - var rows = Math.ceil(thumbnailCount / cellSize.columns); - + var scrollTop = $("#g-organize-microthumb-grid").scrollTop(); var itemPos = { - col: Math.floor((event.pageX - $(this).offset().left) / cellSize.width), - row: Math.floor((event.pageY - $(this).offset().top) / cellSize.height) + col: Math.floor((event.pageX - $("#g-organize-microthumb-grid").offset().left) / cellSize.width), + row: Math.floor((event.pageY + scrollTop - $("#g-organize-microthumb-grid").offset().top) / cellSize.height) }; - var itemIndex = itemPos.row * cellSize.columns + itemPos.col; - var item; - if (itemIndex < thumbnailCount) { - item = $(".g-organize-microthumb-grid-cell:visible").get(itemIndex); - } else { - item = $(".g-organize-microthumb-grid-cell:visible:last"); - } + var itemIndex = itemPos.row * cellSize.columns + itemPos.col; + var item = itemIndex < thumbnailCount ? $(".g-organize-microthumb-grid-cell:visible").get(itemIndex) : + $(".g-organize-microthumb-grid-cell:visible:last"); - var old_position = {top: 0, left: 0}; - if ($("#g-organize-drop-target-marker").length) { - old_position = $("#g-organize-drop-target-marker").position(); - } var before = event.pageX < ($(item).offset().left + $(item).width() / 2); - var left = (before && itemIndex < thumbnailCount ? $(item).position().left : $(item).position().left + cellSize.width) - 3; - var top = $(item).position().top + 6; + var top = $(item).position().top + 6 + scrollTop; - if (old_position.top != top || old_position.left != left) { - if ($("#g-organize-drop-target-marker").length) { - $("#g-organize-drop-target-marker").remove(); - } - var set = $('
    ') - .css({zIndex: 2000, - width: 2, - height: 112, - borderWidth: 1, - borderStyle: "solid", - position: "absolute", - top: top, - left: left - }) - .data("drop_position", {id: $(item).attr("ref"), position: before}); - $("#g-organize-microthumb-grid").append(set); + if ($("#g-organize-drop-target-marker").length) { + $("#g-organize-drop-target-marker").remove(); } + var set = $('
    ') + .css({zIndex: 2000, + width: 2, + height: 112, + borderWidth: 1, + borderStyle: "solid", + position: "absolute", + top: top, left: left + }) + .data("drop_position", {id: $(item).attr("ref"), position: before}); + $("#g-organize-microthumb-grid").append(set); } }, @@ -190,12 +177,13 @@ $("#g-dialog").bind("dialogopen", function(event, ui) { var outerHeight = $(".g-organize-microthumb-grid-cell").outerHeight(true); var outerWidth = $(".g-organize-microthumb-grid-cell").outerWidth(true); + var gridInnerWidth = $("#g-organize-microthumb-grid").innerWidth() - 2 * parseFloat($("#g-organize-microthumb-grid").css("paddingLeft")); $("#g-organize") .height($("#g-dialog").innerHeight() - 20) .data("cellSize", { height: outerHeight, width: outerWidth, - columns: Math.floor($("#g-organize-microthumb-grid").innerWidth() / outerWidth) + columns: Math.floor(gridInnerWidth / outerWidth) }); }); @@ -222,6 +210,8 @@ .mouseleave($.organize.grid_mouse_leave_handler) .droppable($.organize.content_droppable); $(".g-organize-microthumb-grid-cell") + // need to manually add this class in case we care calling with additional elements + .addClass("ui-selectee") .draggable($.organize.micro_thumb_draggable); $(".g-organize-album").droppable($.organize.branch_droppable); $(".g-organize-album-text").click($.organize.show_album); -- cgit v1.2.3 From f631c2a0e5d1de4d17478993fc0cac2c9a989df2 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Sat, 6 Feb 2010 09:30:25 -0800 Subject: Fix up Admin_Users_Controller() form handling now that user_form.html is gone. Fixes ticket #1005. --- modules/user/controllers/admin_users.php | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'modules') diff --git a/modules/user/controllers/admin_users.php b/modules/user/controllers/admin_users.php index 48847433..df3d96c9 100644 --- a/modules/user/controllers/admin_users.php +++ b/modules/user/controllers/admin_users.php @@ -60,9 +60,7 @@ class Admin_Users_Controller extends Admin_Controller { } public function add_user_form() { - $v = new View("user_form.html"); - $v->form = $this->_get_user_add_form_admin(); - print $v; + print $this->_get_user_add_form_admin(); } public function delete_user($id) { @@ -147,13 +145,7 @@ class Admin_Users_Controller extends Admin_Controller { throw new Kohana_404_Exception(); } - $v = new View("user_form.html"); - $v->form = $this->_get_user_edit_form_admin($user); - // Don't allow the user to control their own admin bit, else you can lock yourself out - if ($user->id == identity::active_user()->id) { - $v->form->edit_user->admin->disabled(1); - } - print $v; + print $this->_get_user_edit_form_admin($user); } public function add_user_to_group($user_id, $group_id) { @@ -293,6 +285,9 @@ class Admin_Users_Controller extends Admin_Controller { ->error_messages("length", t("This name is too long")); $group->password("password")->label(t("Password"))->id("g-password") ->error_messages("min_length", t("This password is too short")); + $group->script("") + ->text( + '$("form").ready(function(){$(\'input[name="password"]\').user_password_strength();});'); $group->password("password2")->label(t("Confirm password"))->id("g-password2") ->error_messages("matches", t("The passwords you entered do not match")) ->matches($group->password); @@ -305,6 +300,11 @@ class Admin_Users_Controller extends Admin_Controller { self::_add_locale_dropdown($group, $user); $group->checkbox("admin")->label(t("Admin"))->id("g-admin")->checked($user->admin); + // Don't allow the user to control their own admin bit, else you can lock yourself out + if ($user->id == identity::active_user()->id) { + $group->admin->disabled(1); + } + module::event("user_edit_form_admin", $user, $form); $group->submit("")->value(t("Modify User")); return $form; @@ -321,6 +321,9 @@ class Admin_Users_Controller extends Admin_Controller { ->error_messages("length", t("This name is too long")); $group->password("password")->label(t("Password"))->id("g-password") ->error_messages("min_length", t("This password is too short")); + $group->script("") + ->text( + '$("form").ready(function(){$(\'input[name="password"]\').user_password_strength();});'); $group->password("password2")->label(t("Confirm password"))->id("g-password2") ->error_messages("matches", t("The passwords you entered do not match")) ->matches($group->password); -- cgit v1.2.3 From 83f61ff5cf9c51c998dac9dad23d74ab7339ead5 Mon Sep 17 00:00:00 2001 From: Tim Almdal Date: Sat, 6 Feb 2010 09:47:56 -0800 Subject: Insure that the 'Ascending'/'Descending' string are translated on the organize dialog. --- modules/organize/views/organize_dialog.html.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'modules') diff --git a/modules/organize/views/organize_dialog.html.php b/modules/organize/views/organize_dialog.html.php index e36264f9..7a16f56c 100644 --- a/modules/organize/views/organize_dialog.html.php +++ b/modules/organize/views/organize_dialog.html.php @@ -30,7 +30,9 @@
  • "g-organize-sort-column"), album::get_sort_order_options(), $album->sort_column) ?>
  • - "g-organize-sort-order"), array("ASC" => "Ascending", "DESC" => "Descending"), $album->sort_order) ?>
  • + "g-organize-sort-order"), + array("ASC" => t("Ascending"), "DESC" => t("Descending")), + $album->sort_order) ?>
    -- cgit v1.2.3 From 15e22076552929bf1f45888945f65de621aa2aac Mon Sep 17 00:00:00 2001 From: Tim Almdal Date: Sat, 6 Feb 2010 10:12:53 -0800 Subject: correct layout of html for readability --- modules/organize/views/organize_dialog.html.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'modules') diff --git a/modules/organize/views/organize_dialog.html.php b/modules/organize/views/organize_dialog.html.php index 7a16f56c..38d05b81 100644 --- a/modules/organize/views/organize_dialog.html.php +++ b/modules/organize/views/organize_dialog.html.php @@ -29,10 +29,14 @@
    • - "g-organize-sort-column"), album::get_sort_order_options(), $album->sort_column) ?>
    • - "g-organize-sort-order"), - array("ASC" => t("Ascending"), "DESC" => t("Descending")), - $album->sort_order) ?>
    + "g-organize-sort-column"), + album::get_sort_order_options(), $album->sort_column) ?> + +
  • + "g-organize-sort-order"), + array("ASC" => t("Ascending"), "DESC" => t("Descending")), $album->sort_order) ?> +
  • + -- cgit v1.2.3