diff options
Diffstat (limited to 'modules/gallery')
37 files changed, 516 insertions, 223 deletions
| diff --git a/modules/gallery/controllers/admin_modules.php b/modules/gallery/controllers/admin_modules.php index f5af9a5a..650b7e9e 100644 --- a/modules/gallery/controllers/admin_modules.php +++ b/modules/gallery/controllers/admin_modules.php @@ -95,12 +95,17 @@ class Admin_Modules_Controller extends Admin_Controller {            $activated_names[] = t($info->name);          }        } catch (Exception $e) { +        message::warning(t("An error occurred while installing the <b>%module_name</b> module", +                           array("module_name" => $info->name)));          Kohana_Log::add("error", (string)$e);        }      }      module::event("module_change", $changes); +    // If modules need upgrading, this will get recreated +    site_status::clear("upgrade_now"); +      // @todo this type of collation is questionable from an i18n perspective      if ($activated_names) {        message::success(t("Activated: %names", array("names" => join(", ", $activated_names)))); diff --git a/modules/gallery/controllers/items.php b/modules/gallery/controllers/items.php index f205bf86..39b0f638 100644 --- a/modules/gallery/controllers/items.php +++ b/modules/gallery/controllers/items.php @@ -31,4 +31,13 @@ class Items_Controller extends Controller {      access::required("view", $item);      url::redirect($item->abs_url());    } + +  // Return the width/height dimensinons for the given item +  public function dimensions($id) { +    $item = ORM::factory("item", $id); +    access::required("view", $item); +    json::reply(array("thumb" => array((int)$item->thumb_width, (int)$item->thumb_height), +                      "resize" => array((int)$item->resize_width, (int)$item->resize_height), +                      "full" => array((int)$item->width, (int)$item->height))); +  }  } diff --git a/modules/gallery/controllers/quick.php b/modules/gallery/controllers/quick.php index c34209da..3db4f5df 100644 --- a/modules/gallery/controllers/quick.php +++ b/modules/gallery/controllers/quick.php @@ -36,7 +36,8 @@ class Quick_Controller extends Controller {      }      if ($degrees) { -      $tmpfile = tempnam(TMPPATH, "rotate"); +      $tmpfile = tempnam(TMPPATH, "rotate") . "." . +        pathinfo($item->file_path(), PATHINFO_EXTENSION);        gallery_graphics::rotate($item->file_path(), $tmpfile, array("degrees" => $degrees));        $item->set_data_file($tmpfile);        $item->save(); diff --git a/modules/gallery/controllers/reauthenticate.php b/modules/gallery/controllers/reauthenticate.php index 0486c0fe..53a96374 100644 --- a/modules/gallery/controllers/reauthenticate.php +++ b/modules/gallery/controllers/reauthenticate.php @@ -19,12 +19,19 @@   */  class Reauthenticate_Controller extends Controller {    public function index() { +    $is_ajax = Session::instance()->get_once("is_ajax_request", request::is_ajax());      if (!identity::active_user()->admin) { -      access::forbidden(); +      if ($is_ajax) { +        // We should never be able to get here since Admin_Controller::_reauth_check() won't work +        // for non-admins. +        access::forbidden(); +      } else { +        url::redirect(item::root()->abs_url()); +      }      } +      // On redirects from the admin controller, the ajax request indicator is lost,      // so we store it in the session. -    $is_ajax = Session::instance()->get_once("is_ajax_request", request::is_ajax());      if ($is_ajax) {        $v = new View("reauthenticate.html");        $v->form = self::_form(); diff --git a/modules/gallery/controllers/upgrader.php b/modules/gallery/controllers/upgrader.php index cb940b46..b2646874 100644 --- a/modules/gallery/controllers/upgrader.php +++ b/modules/gallery/controllers/upgrader.php @@ -39,10 +39,12 @@ class Upgrader_Controller extends Controller {        }      } +    $failed = Input::instance()->get("failed");      $view = new View("upgrader.html");      $view->can_upgrade = identity::active_user()->admin || $session->get("can_upgrade");      $view->upgrade_token = $upgrade_token;      $view->available = module::available(); +    $view->failed = $failed ? explode(",", $failed) : array();      $view->done = $available_upgrades == 0;      print $view;    } @@ -52,8 +54,16 @@ class Upgrader_Controller extends Controller {        // @todo this may screw up some module installers, but we don't have a better answer at        // this time.        $_SERVER["HTTP_HOST"] = "example.com"; -    } else if (!identity::active_user()->admin && !Session::instance()->get("can_upgrade", false)) { -      access::forbidden(); +    } else { +      if (!identity::active_user()->admin && !Session::instance()->get("can_upgrade", false)) { +        access::forbidden(); +      } + +      try { +        access::verify_csrf(); +      } catch (Exception $e) { +        url::redirect("upgrader"); +      }      }      $available = module::available(); @@ -65,20 +75,36 @@ class Upgrader_Controller extends Controller {      }      // Then upgrade the rest +    $failed = array();      foreach (module::available() as $id => $module) {        if ($id == "gallery") {          continue;        }        if ($module->active && $module->code_version != $module->version) { -        module::upgrade($id); +        try { +          module::upgrade($id); +        } catch (Exception $e) { +          // @todo assume it's MODULE_FAILED_TO_UPGRADE for now +          $failed[] = $id; +        }        }      } +    // If the upgrade failed, this will get recreated +    site_status::clear("upgrade_now"); +      if (php_sapi_name() == "cli") { -      print "Upgrade complete\n"; +      if ($failed) { +        print "Upgrade completed ** WITH FAILURES **\n"; +        print "The following modules were not successfully upgraded:\n"; +        print "  " . implode($failed, "\n  ") . "\n"; +        print "Try getting newer versions or deactivating those modules\n"; +      } else { +        print "Upgrade complete\n"; +      }      } else { -      url::redirect("upgrader"); +      url::redirect("upgrader?failed=" . join(",", $failed));      }    }  } diff --git a/modules/gallery/controllers/uploader.php b/modules/gallery/controllers/uploader.php index fb496f60..168e8b2d 100644 --- a/modules/gallery/controllers/uploader.php +++ b/modules/gallery/controllers/uploader.php @@ -103,11 +103,14 @@ class Uploader_Controller extends Controller {    }    public function status($success_count, $error_count) { -    // The "errors" won't be properly pluralized :-/ -    print t2("Uploaded %count photo (%error errors)", -             "Uploaded %count photos (%error errors)", -             $success_count, -             array("error" => $error_count)); +    if ($error_count) { +      // The "errors" won't be properly pluralized :-/ +      print t2("Uploaded %count photo (%error errors)", +               "Uploaded %count photos (%error errors)", +               $success_count, +               array("error" => $error_count)); +    } else { +      print t2("Uploaded %count photo", "Uploaded %count photos", $success_count);}    }    public function finish() { diff --git a/modules/gallery/controllers/user_profile.php b/modules/gallery/controllers/user_profile.php index e992655b..4922416c 100644 --- a/modules/gallery/controllers/user_profile.php +++ b/modules/gallery/controllers/user_profile.php @@ -18,6 +18,7 @@   * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA  02110-1301, USA.   */  class User_Profile_Controller extends Controller { +    public function show($id) {      // If we get here, then we should have a user id other than guest.      $user = identity::lookup_user($id); @@ -25,6 +26,10 @@ class User_Profile_Controller extends Controller {        throw new Kohana_404_Exception();      } +    if (!$this->_can_view_profile_pages($user)) { +      throw new Kohana_404_Exception(); +    } +      $v = new Theme_View("page.html", "other", "profile");      $v->page_title = t("%name Profile", array("name" => $user->display_name()));      $v->content = new View("user_profile.html"); @@ -44,12 +49,20 @@ class User_Profile_Controller extends Controller {    public function contact($id) {      $user = identity::lookup_user($id); +    if (!$this->_can_view_profile_pages($user)) { +      throw new Kohana_404_Exception(); +    } +      print user_profile::get_contact_form($user);    }    public function send($id) {      access::verify_csrf();      $user = identity::lookup_user($id); +    if (!$this->_can_view_profile_pages($user)) { +      throw new Kohana_404_Exception(); +    } +      $form = user_profile::get_contact_form($user);      if ($form->validate()) {        Sendmail::factory() @@ -66,4 +79,30 @@ class User_Profile_Controller extends Controller {        json::reply(array("result" => "error", "html" => (string)$form));      }    } + +  private function _can_view_profile_pages($user) { +    if (!$user->loaded()) { +      return false; +    } + +    if ($user->id == identity::active_user()->id) { +      // You can always view your own profile +      return true; +    } + +    switch (module::get_var("gallery", "show_user_profiles_to")) { +    case "admin_users": +      return identity::active_user()->admin; + +    case "registered_users": +      return !identity::active_user()->guest; + +    case "everybody": +      return true; + +    default: +      // Fail in private mode on an invalid setting +      return false; +    } +  }  } diff --git a/modules/gallery/css/upgrader.css b/modules/gallery/css/upgrader.css index d1b74c31..8610016e 100644 --- a/modules/gallery/css/upgrader.css +++ b/modules/gallery/css/upgrader.css @@ -58,6 +58,10 @@ tr.upgradeable td.gallery {    color: #00d;  } +tr.failed td { +  color: red; +} +  p {    font-size: .9em;  } @@ -120,12 +124,28 @@ div#dialog div {    opacity: 0.5;  } +.failed { +  color: red; +} +  pre {    display: inline;    margin: 0px;    padding: 0px;  } +div#upgrade_button { +  margin-bottom: 20px; +} + +div#welcome_message { +  margin-left: 30px; +} + +#logo { +  margin-left: 14px; +} +  .rtl {    direction: rtl;  } @@ -153,3 +173,11 @@ pre {  .rtl div#dialog a.close {    float: left;  } + +.rtl div#welcome_message { +  padding-right: 30px; +} + +.rtl #logo { +  padding-right: 12px; +} diff --git a/modules/gallery/helpers/access.php b/modules/gallery/helpers/access.php index f1ea00c0..52a36298 100644 --- a/modules/gallery/helpers/access.php +++ b/modules/gallery/helpers/access.php @@ -263,15 +263,15 @@ class access_Core {    }    /** -   * Recalculate the permissions for a given item and its hierarchy.  $item must be an album. +   * Recalculate the permissions for an album's hierarchy.     */ -  static function recalculate_permissions($item) { +  static function recalculate_album_permissions($album) {      foreach (self::_get_all_groups() as $group) {        foreach (ORM::factory("permission")->find_all() as $perm) {          if ($perm->name == "view") { -          self::_update_access_view_cache($group, $item); +          self::_update_access_view_cache($group, $album);          } else { -          self::_update_access_non_view_cache($group, $perm->name, $item); +          self::_update_access_non_view_cache($group, $perm->name, $album);          }        }      } @@ -279,6 +279,28 @@ class access_Core {    }    /** +   * Recalculate the permissions for a single photo. +   */ +  static function recalculate_photo_permissions($photo) { +    $parent = $photo->parent(); +    $parent_access_cache = ORM::factory("access_cache")->where("item_id", "=", $parent->id)->find(); +    $photo_access_cache = ORM::factory("access_cache")->where("item_id", "=", $photo->id)->find(); +    foreach (self::_get_all_groups() as $group) { +      foreach (ORM::factory("permission")->find_all() as $perm) { +        $field = "{$perm->name}_{$group->id}"; +        if ($perm->name == "view") { +          $photo->$field = $parent->$field; +        } else { +          $photo_access_cache->$field = $parent_access_cache->$field; +        } +      } +    } +    $photo_access_cache->save(); +    $photo->save(); +    model_cache::clear(); +  } + +  /**     * Register a permission so that modules can use it.     *     * @param  string $name           The internal name for for this permission @@ -694,6 +716,7 @@ class access_Core {      @mkdir(VARPATH . "security_test");      try {        if ($fp = @fopen(VARPATH . "security_test/.htaccess", "w+")) { +        fwrite($fp, "Options +FollowSymLinks\n");          fwrite($fp, "RewriteEngine On\n");          fwrite($fp, "RewriteRule verify $success_url [L]\n");          fclose($fp); diff --git a/modules/gallery/helpers/album.php b/modules/gallery/helpers/album.php index 0ac5e8b0..89185e50 100644 --- a/modules/gallery/helpers/album.php +++ b/modules/gallery/helpers/album.php @@ -114,6 +114,7 @@ class album_Core {                   "captured" => t("Date captured"),                   "created" => t("Date uploaded"),                   "title" => t("Title"), +                 "name" => t("File name"),                   "updated" => t("Date modified"),                   "view_count" => t("Number of views"),                   "rand_key" => t("Random")); diff --git a/modules/gallery/helpers/gallery.php b/modules/gallery/helpers/gallery.php index 3f83b23d..ca8c92c9 100644 --- a/modules/gallery/helpers/gallery.php +++ b/modules/gallery/helpers/gallery.php @@ -37,6 +37,30 @@ class gallery_Core {    }    /** +   * If the gallery is only available to registered users and the user is not logged in, present +   * the login page. +   */ +  static function private_gallery() { +    if (Router::$controller != "login" && +        Router::$controller != "combined" && +        identity::active_user()->guest && +        !access::user_can(identity::guest(), "view", item::root()) && +        php_sapi_name() != "cli") { +      if (Router::$controller == "admin") { +        // At this point we're in the admin theme and it doesn't have a themed login page, so +        // we can't just swap in the login controller and have it work.  So redirect back to the +        // root item where we'll run this code again with the site theme. +        url::redirect(item::root()->abs_url()); +      } else { +        Session::instance()->set("continue_url", url::abs_current()); +        Router::$controller = "login"; +        Router::$controller_path = MODPATH . "gallery/controllers/login.php"; +        Router::$method = "html"; +      } +    } +  } + +  /**     * This function is called when the Gallery is fully initialized.  We relay it to modules as the     * "gallery_ready" event.  Any module that wants to perform an action at the start of every     * request should implement the <module>_event::gallery_ready() handler. diff --git a/modules/gallery/helpers/gallery_event.php b/modules/gallery/helpers/gallery_event.php index df5394c9..b59bb9b9 100644 --- a/modules/gallery/helpers/gallery_event.php +++ b/modules/gallery/helpers/gallery_event.php @@ -63,6 +63,8 @@ class gallery_event_Core {        ->update("logs")        ->set("user_id", $admin->id)        ->execute(); +    module::set_var("gallery", "email_from", $admin->email); +    module::set_var("gallery", "email_reply_to", $admin->email);    }    static function group_created($group) { @@ -116,8 +118,8 @@ class gallery_event_Core {          $batch_missing_album_cover[$parent->id] = 1;          Session::instance()->set("batch_missing_album_cover", $batch_missing_album_cover);        } else { -        // Choose the first child as the new cover. -        if ($child = $parent->children(1)->current()) { +        // Choose the first viewable child as the new cover. +        if ($child = $parent->viewable()->children(1)->current()) {            item::make_album_cover($child);          }        } @@ -155,7 +157,11 @@ class gallery_event_Core {    }    static function item_moved($item, $old_parent) { -    access::recalculate_permissions($item->parent()); +    if ($item->is_album()) { +      access::recalculate_album_permissions($item->parent()); +    } else { +      access::recalculate_photo_permissions($item); +    }      // If the new parent doesn't have an album cover, make this it.      if (!$item->parent()->album_cover_item_id) { @@ -208,10 +214,10 @@ class gallery_event_Core {                        ->label($user->display_name()));          if (Router::$controller == "admin") { -          $continue_url = url::site(""); -        } else if (isset($theme->item)) { +          $continue_url = url::abs_site(""); +        } else if ($item = $theme->item()) {            if (access::user_can(identity::guest(), "view", $theme->item)) { -            $continue_url = $theme->item->abs_url(); +            $continue_url = $item->abs_url();            } else {              $continue_url = item::root()->abs_url();            } @@ -547,4 +553,17 @@ class gallery_event_Core {      $data->content[] = (object) array("title" => t("User information"), "view" => $v);    } + +  static function user_updated($original_user, $updated_user) { +    // If the default from/reply-to email address is set to the install time placeholder value +    // of unknown@unknown.com then adopt the value from the first admin to set their own email +    // address so that we at least have a valid address for the Gallery. +    if ($updated_user->admin) { +      $email = module::get_var("gallery", "email_from", ""); +      if ($email == "unknown@unknown.com") { +        module::set_var("gallery", "email_from", $updated_user->email); +        module::set_var("gallery", "email_reply_to", $updated_user->email); +      } +    } +  }  } diff --git a/modules/gallery/helpers/gallery_installer.php b/modules/gallery/helpers/gallery_installer.php index d5264fcc..83961d6b 100644 --- a/modules/gallery/helpers/gallery_installer.php +++ b/modules/gallery/helpers/gallery_installer.php @@ -302,14 +302,14 @@ class gallery_installer {      module::set_var("gallery", "maintenance_mode", 0);      module::set_var("gallery", "visible_title_length", 15);      module::set_var("gallery", "favicon_url", "lib/images/favicon.ico"); - -    // Sendmail configuration -    module::set_var("gallery", "email_from", "admin@example.com"); -    module::set_var("gallery", "email_reply_to", "public@example.com"); +    module::set_var("gallery", "email_from", ""); +    module::set_var("gallery", "email_reply_to", "");      module::set_var("gallery", "email_line_length", 70);      module::set_var("gallery", "email_header_separator", serialize("\n")); +    module::set_var("gallery", "show_user_profiles_to", "registered_users"); +    module::set_var("gallery", "extra_binary_paths", "/usr/local/bin:/opt/local/bin:/opt/bin"); -    module::set_version("gallery", 37); +    module::set_version("gallery", 40);    }    static function upgrade($version) { @@ -550,7 +550,9 @@ class gallery_installer {      }      if ($version == 26) { -      $db->query("RENAME TABLE {failed_logins} TO {failed_auths}"); +      if (in_array("failed_logins", Database::instance()->list_tables())) { +        $db->query("RENAME TABLE {failed_logins} TO {failed_auths}"); +      }        module::set_version("gallery", $version = 27);      } @@ -611,6 +613,30 @@ class gallery_installer {        module::set_var("gallery", "email_header_separator", serialize("\n"));        module::set_version("gallery", $version = 37);      } + +    // Changed our minds and decided that the initial value should be empty +    // But don't just reset it blindly, only do it if the value is version 37 default +    if ($version == 37) { +      $email = module::get_var("gallery", "email_from", ""); +      if ($email == "admin@example.com") { +        module::set_var("gallery", "email_from", ""); +      } +      $email = module::get_var("gallery", "email_reply_to", ""); +      if ($email == "admin@example.com") { +        module::set_var("gallery", "email_reply_to", ""); +      } +      module::set_version("gallery", $version = 38); +    } + +    if ($version == 38) { +      module::set_var("gallery", "show_user_profiles_to", "registered_users"); +      module::set_version("gallery", $version = 39); +    } + +    if ($version == 39) { +      module::set_var("gallery", "extra_binary_paths", "/usr/local/bin:/opt/local/bin:/opt/bin"); +      module::set_version("gallery", $version = 40); +    }    }    static function uninstall() { diff --git a/modules/gallery/helpers/gallery_rss.php b/modules/gallery/helpers/gallery_rss.php index bec34912..fb617934 100644 --- a/modules/gallery/helpers/gallery_rss.php +++ b/modules/gallery/helpers/gallery_rss.php @@ -40,7 +40,7 @@ class gallery_rss_Core {          ->order_by("created", "DESC");        $feed->max_pages = ceil($all_items->find_all()->count() / $limit); -      $feed->title = t("Recent updates"); +      $feed->title = t("%site_title - Recent updates", array("site_title" => item::root()->title));        $feed->description = t("Recent updates");        return $feed; @@ -53,7 +53,13 @@ class gallery_rss_Core {          ->descendants($limit, $offset, array(array("type", "=", "photo")));        $feed->max_pages = ceil(          $item->viewable()->descendants_count(array(array("type", "=", "photo"))) / $limit); -      $feed->title = html::purify($item->title); +      if ($item->id == item::root()->id) { +        $feed->title = html::purify($item->title); +      } else { +        $feed->title = t("%site_title - %item_title", +                         array("site_title" => item::root()->title, +                               "item_title" => $item->title)); +      }        $feed->description = nl2br(html::purify($item->description));        return $feed; diff --git a/modules/gallery/helpers/gallery_task.php b/modules/gallery/helpers/gallery_task.php index 0886aad0..3b173928 100644 --- a/modules/gallery/helpers/gallery_task.php +++ b/modules/gallery/helpers/gallery_task.php @@ -571,7 +571,7 @@ class gallery_task_Core {            // The new cache rows are there, but they're incorrectly populated so we have to fix            // them.  If this turns out to be too slow, we'll have to refactor            // access::recalculate_permissions to allow us to do it in slices. -          access::recalculate_permissions(item::root()); +          access::recalculate_album_permissions(item::root());            $state = self::FIX_STATE_DONE;          }          break; @@ -596,7 +596,7 @@ class gallery_task_Core {    static function find_dupe_slugs() {      return db::build()        ->select_distinct( -        array("parent_slug" => new Database_Expression("CONCAT(`parent_id`, ':', `slug`)"))) +        array("parent_slug" => new Database_Expression("CONCAT(`parent_id`, ':', LOWER(`slug`))")))        ->select("id")        ->select(array("C" => "COUNT(\"*\")"))        ->from("items") @@ -608,7 +608,7 @@ class gallery_task_Core {    static function find_dupe_names() {      return db::build()        ->select_distinct( -        array("parent_name" => new Database_Expression("CONCAT(`parent_id`, ':', `name`)"))) +        array("parent_name" => new Database_Expression("CONCAT(`parent_id`, ':', LOWER(`name`))")))        ->select("id")        ->select(array("C" => "COUNT(\"*\")"))        ->from("items") diff --git a/modules/gallery/helpers/graphics.php b/modules/gallery/helpers/graphics.php index bb085ea5..2868a28d 100644 --- a/modules/gallery/helpers/graphics.php +++ b/modules/gallery/helpers/graphics.php @@ -314,9 +314,10 @@ class graphics_Core {        $toolkits->graphicsmagick->error = t("GraphicsMagick requires the <b>exec</b> function");      } else {        $graphics_path = module::get_var("gallery", "graphics_toolkit_path", null); +      $extra_binary_paths = module::get_var("gallery", "extra_binary_paths", null);        putenv("PATH=" . getenv("PATH") . (empty($graphics_path) ? "" : ":$graphics_path") . -             ":/usr/local/bin:/opt/local/bin:/opt/bin"); +             ":" . $extra_binary_paths);        // @todo: consider refactoring the two segments below into a loop since they are so        // similar. diff --git a/modules/gallery/helpers/identity.php b/modules/gallery/helpers/identity.php index 5de05948..5ca024e9 100644 --- a/modules/gallery/helpers/identity.php +++ b/modules/gallery/helpers/identity.php @@ -233,14 +233,14 @@ class identity_Core {    /**     * @see IdentityProvider_Driver::add_user_to_group.     */ -  static function add_user_to_group($user, $group_id) { -    return IdentityProvider::instance()->add_user_to_group($user, $group_id); +  static function add_user_to_group($user, $group) { +    return IdentityProvider::instance()->add_user_to_group($user, $group);    }    /**     * @see IdentityProvider_Driver::remove_user_to_group.     */ -  static function remove_user_from_group($user, $group_id) { -    return IdentityProvider::instance()->remove_user_from_group($user, $group_id); +  static function remove_user_from_group($user, $group) { +    return IdentityProvider::instance()->remove_user_from_group($user, $group);    }  }
\ No newline at end of file diff --git a/modules/gallery/helpers/locales.php b/modules/gallery/helpers/locales.php index aacb37ca..d1e72260 100644 --- a/modules/gallery/helpers/locales.php +++ b/modules/gallery/helpers/locales.php @@ -92,6 +92,7 @@ class locales_Core {      $l["ko_KR"] = "한국어";                    // Korean      $l["lt_LT"] = "Lietuvių";                 // Lithuanian      $l["lv_LV"] = "Latviešu";                 // Latvian +    $l["mk_MK"] = "Македонски јазик";         // Macedonian      $l["nl_NL"] = "Nederlands";               // Dutch      $l["no_NO"] = "Norsk bokmål";             // Norwegian      $l["pl_PL"] = "Polski";                   // Polish @@ -103,6 +104,7 @@ class locales_Core {      $l["sl_SI"] = "Slovenščina";              // Slovenian      $l["sr_CS"] = "Srpski";                   // Serbian      $l["sv_SE"] = "Svenska";                  // Swedish +    $l["tn_ZA"] = "Setswana";                 // Setswana      $l["tr_TR"] = "Türkçe";                   // Turkish      $l["uk_UA"] = "українська";         // Ukrainian      $l["vi_VN"] = "Tiếng Việt";               // Vietnamese diff --git a/modules/gallery/helpers/module.php b/modules/gallery/helpers/module.php index ca6651f1..7863520e 100644 --- a/modules/gallery/helpers/module.php +++ b/modules/gallery/helpers/module.php @@ -99,6 +99,10 @@ class module_Core {          $m->code_version = $m->version;          $m->version = self::get_version($module_name);          $m->locked = false; + +        if ($m->active && $m->version != $m->code_version) { +          site_status::warning(t("Some of your modules are out of date.  <a href=\"%upgrader_url\">Upgrade now!</a>", array("upgrader_url" => url::site("upgrader"))), "upgrade_now"); +        }        }        // Lock certain modules @@ -139,7 +143,7 @@ class module_Core {    }    /** -   * Allow modules to indicate the impact of deactivating the specifeid module +   * Allow modules to indicate the impact of deactivating the specified module     * @param string $module_name     * @return array an array of warning or error messages to be displayed     */ @@ -214,10 +218,10 @@ class module_Core {    static function upgrade($module_name) {      $version_before = module::get_version($module_name);      $installer_class = "{$module_name}_installer"; +    $available = module::available();      if (method_exists($installer_class, "upgrade")) {        call_user_func_array(array($installer_class, "upgrade"), array($version_before));      } else { -      $available = module::available();        if (isset($available->$module_name->code_version)) {          module::set_version($module_name, $available->$module_name->code_version);        } else { @@ -234,6 +238,10 @@ class module_Core {                            "version_before" => $version_before,                            "version_after" => $version_after)));      } + +    if ($version_after != $available->$module_name->code_version) { +      throw new Exception("@todo MODULE_FAILED_TO_UPGRADE"); +    }    }    /** @@ -448,7 +456,17 @@ class module_Core {          $cache->module_name = "gallery";          $cache->name = "_cache";          $cache->value = serialize(self::$var_cache); -        $cache->save(); +        try { +          $cache->save(); +        } catch (Database_Exception $e) { +          // There's a potential race condition here.  Don't fail if that happens because it's +          // bound to be transient and not a huge deal, but at least put something in the logs. +          if (stristr($e->getMessage(), "duplicate entry")) { +            Kohana_Log::add("error", "Failed to cache vars"); +          } else { +            throw $e; +          } +        }        }      } diff --git a/modules/gallery/helpers/movie.php b/modules/gallery/helpers/movie.php index 3e55eefe..50339541 100644 --- a/modules/gallery/helpers/movie.php +++ b/modules/gallery/helpers/movie.php @@ -86,9 +86,10 @@ class movie_Core {    static function find_ffmpeg() {      if (!($ffmpeg_path = module::get_var("gallery", "ffmpeg_path")) || !file_exists($ffmpeg_path)) {        $graphics_path = module::get_var("gallery", "graphics_toolkit_path", null); +      $extra_binary_paths = module::get_var("gallery", "extra_binary_paths", null);        putenv("PATH=" . getenv("PATH") . (empty($graphics_path) ? "" : ":$graphics_path") . -             ":/usr/local/bin:/opt/local/bin:/opt/bin"); +             ":" . $extra_binary_paths);        if (function_exists("exec")) {          $ffmpeg_path = exec("which ffmpeg");        } diff --git a/modules/gallery/hooks/init_gallery.php b/modules/gallery/hooks/init_gallery.php index 64e44b56..10a74733 100644 --- a/modules/gallery/hooks/init_gallery.php +++ b/modules/gallery/hooks/init_gallery.php @@ -38,6 +38,7 @@ Event::add("system.ready", array("module", "load_modules"));  Event::add("system.ready", array("gallery", "ready"));  Event::add("system.post_routing", array("url", "parse_url"));  Event::add("system.post_routing", array("gallery", "maintenance_mode")); +Event::add("system.post_routing", array("gallery", "private_gallery"));  Event::add("system.shutdown", array("gallery", "shutdown"));  // @todo once we convert to Kohana 2.4 this doesn't have to be here diff --git a/modules/gallery/libraries/Gallery_I18n.php b/modules/gallery/libraries/Gallery_I18n.php index f8068eec..6cb36f07 100644 --- a/modules/gallery/libraries/Gallery_I18n.php +++ b/modules/gallery/libraries/Gallery_I18n.php @@ -44,7 +44,7 @@ function t($message, $options=array()) {   */  function t2($singular, $plural, $count, $options=array()) {    return Gallery_I18n::instance()->translate(array("one" => $singular, "other" => $plural), -                                     array_merge($options, array("count" => $count))); +                                             array_merge($options, array("count" => $count)));  }  class Gallery_I18n_Core { @@ -175,7 +175,7 @@ class Gallery_I18n_Core {                 ->execute() as $row) {          $translations[$row->key] = unserialize($row->translation);        } -       +        // Override incoming with outgoing...        foreach (db::build()                 ->select("key", "translation") @@ -184,7 +184,7 @@ class Gallery_I18n_Core {                 ->execute() as $row) {          $translations[$row->key] = unserialize($row->translation);        } -       +        $cache->set($cache_key, $translations, array("translation"), 0);      }      return $translations; diff --git a/modules/gallery/libraries/Sendmail.php b/modules/gallery/libraries/Sendmail.php index 0fa554b4..a93be736 100644 --- a/modules/gallery/libraries/Sendmail.php +++ b/modules/gallery/libraries/Sendmail.php @@ -35,12 +35,11 @@ class Sendmail_Core {    public function __construct() {      $this->headers = array(); -    $domain = Input::instance()->server("HTTP_HOST"); -    $this->from(module::get_var("gallery", "email_from", "admin@$domain")); -    $this->reply_to(module::get_var("gallery", "email_reply_to", "public@$domain")); +    $this->from(module::get_var("gallery", "email_from", "")); +    $this->reply_to(module::get_var("gallery", "email_reply_to", ""));      $this->line_length(module::get_var("gallery", "email_line_length", 70));      $separator = module::get_var("gallery", "email_header_separator", null); -    $this->header_separator(empty($separator) ? "\n" : deserialize($separator)); +    $this->header_separator(empty($separator) ? "\n" : unserialize($separator));    }    public function __get($key) { diff --git a/modules/gallery/libraries/drivers/IdentityProvider.php b/modules/gallery/libraries/drivers/IdentityProvider.php index 3e85a57b..ac2473f5 100644 --- a/modules/gallery/libraries/drivers/IdentityProvider.php +++ b/modules/gallery/libraries/drivers/IdentityProvider.php @@ -116,17 +116,17 @@ interface IdentityProvider_Driver {    /**     * Add the user to the specified group -   * @param User_Definition the user to add to the group -   * @param int             the group_id +   * @param User_Definition  the user to add +   * @param Group_Definition the target group     */ -  static function add_user_to_group($user, $group_id); +  public function add_user_to_group($user, $group);    /**     * Remove the user to the specified group -   * @param User_Definition the user to add to the group -   * @param int             the group id +   * @param User_Definition  the user to remove +   * @param Group_Definition the owning group     */ -  static function remove_user_from_group($user, $group_id); +  public function remove_user_from_group($user, $group);  } // End Identity Driver Definition  interface Group_Definition {} diff --git a/modules/gallery/models/item.php b/modules/gallery/models/item.php index 1db766e9..7bcf1f31 100644 --- a/modules/gallery/models/item.php +++ b/modules/gallery/models/item.php @@ -17,7 +17,7 @@   * along with this program; if not, write to the Free Software   * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA  02110-1301, USA.   */ -class Item_Model extends ORM_MPTT { +class Item_Model_Core extends ORM_MPTT {    protected $children = "items";    protected $sorting = array();    protected $data_file = null; @@ -357,26 +357,7 @@ class Item_Model extends ORM_MPTT {            }          } -        // Randomize the name or slug if there's a conflict.  Preserve the extension. -        // @todo Improve this.  Random numbers are not user friendly -        $base_name = pathinfo($this->name, PATHINFO_FILENAME); -        $base_ext = pathinfo($this->name, PATHINFO_EXTENSION); -        $base_slug = $this->slug; -        while (ORM::factory("item") -               ->where("parent_id", "=", $this->parent_id) -               ->and_open() -               ->where("name", "=", $this->name) -               ->or_where("slug", "=", $this->slug) -               ->close() -               ->find()->id) { -          $rand = rand(); -          if ($base_ext) { -            $this->name = "$base_name-$rand.$base_ext"; -          } else { -            $this->name = "$base_name-$rand"; -          } -          $this->slug = "$base_slug-$rand"; -        } +        $this->_randomize_name_or_slug_on_conflict();          parent::save(); @@ -427,6 +408,8 @@ class Item_Model extends ORM_MPTT {            $this->relative_url_cache = null;          } +        $this->_randomize_name_or_slug_on_conflict(); +          parent::save();          // Now update the filesystem and any database caches if there were significant value @@ -505,6 +488,33 @@ class Item_Model extends ORM_MPTT {    }    /** +   * Check to see if there's another item that occupies the same name or slug that this item +   * intends to use, and if so choose a new name/slug while preserving the extension. +   * @todo Improve this.  Random numbers are not user friendly +   */ +  private function _randomize_name_or_slug_on_conflict() { +    $base_name = pathinfo($this->name, PATHINFO_FILENAME); +    $base_ext = pathinfo($this->name, PATHINFO_EXTENSION); +    $base_slug = $this->slug; +    while (ORM::factory("item") +           ->where("parent_id", "=", $this->parent_id) +           ->where("id", "<>", $this->id) +           ->and_open() +           ->where("name", "=", $this->name) +           ->or_where("slug", "=", $this->slug) +           ->close() +           ->find()->id) { +      $rand = rand(); +      if ($base_ext) { +        $this->name = "$base_name-$rand.$base_ext"; +      } else { +        $this->name = "$base_name-$rand"; +      } +      $this->slug = "$base_slug-$rand"; +    } +  } + +  /**     * Return the Item_Model representing the cover for this album.     * @return Item_Model or null if there's no cover     */ @@ -530,7 +540,7 @@ class Item_Model extends ORM_MPTT {     * the first child in the album is at position 1.     */    public function get_position($child, $where=array()) { -    if ($this->sort_order == "DESC") { +    if (!strcasecmp($this->sort_order, "DESC")) {        $comp = ">";      } else {        $comp = "<"; @@ -975,23 +985,32 @@ class Item_Model extends ORM_MPTT {      }      unset($data["album_cover_item_id"]); -    if (access::can("view_full", $this) && !$this->is_album()) { -      $data["file_url"] = rest::url("data", $this, "full"); -    } -    if (access::user_can(identity::guest(), "view_full", $this)) { -      $data["file_url_public"] = $this->file_url(true); +    $data["web_url"] = $this->abs_url(); + +    if (!$this->is_album()) { +      if (access::can("view_full", $this)) { +        $data["file_url"] = rest::url("data", $this, "full"); +        $data["file_size"] = filesize($this->file_path()); +      } +      if (access::user_can(identity::guest(), "view_full", $this)) { +        $data["file_url_public"] = $this->file_url(true); +      }      }      if ($this->is_photo()) {        $data["resize_url"] = rest::url("data", $this, "resize"); +      $data["resize_size"] = filesize($this->resize_path());        if (access::user_can(identity::guest(), "view", $this)) {          $data["resize_url_public"] = $this->resize_url(true);        }      } -    $data["thumb_url"] = rest::url("data", $this, "thumb"); -    if (access::user_can(identity::guest(), "view", $this)) { -      $data["thumb_url_public"] = $this->thumb_url(true); +    if ($this->has_thumb()) { +      $data["thumb_url"] = rest::url("data", $this, "thumb"); +      $data["thumb_size"] = filesize($this->thumb_path()); +      if (access::user_can(identity::guest(), "view", $this)) { +        $data["thumb_url_public"] = $this->thumb_url(true); +      }      }      $data["can_edit"] = access::can("edit", $this); diff --git a/modules/gallery/module.info b/modules/gallery/module.info index 901158b5..1155ddf7 100644 --- a/modules/gallery/module.info +++ b/modules/gallery/module.info @@ -1,3 +1,3 @@  name = "Gallery 3"  description = "Gallery core application" -version = 37 +version = 40 diff --git a/modules/gallery/tests/Access_Helper_Test.php b/modules/gallery/tests/Access_Helper_Test.php index c092e3fd..32b3020f 100644 --- a/modules/gallery/tests/Access_Helper_Test.php +++ b/modules/gallery/tests/Access_Helper_Test.php @@ -359,11 +359,13 @@ class Access_Helper_Test extends Gallery_Unit_Test_Case {      $public_album = test::random_album();      $public_photo = test::random_photo($public_album);      access::allow(identity::everybody(), "view", $public_album); +    access::allow(identity::everybody(), "edit", $public_album);      item::root()->reload();  // Account for MPTT changes      $private_album = test::random_album();      access::deny(identity::everybody(), "view", $private_album); +    access::deny(identity::everybody(), "edit", $private_album);      $private_photo = test::random_photo($private_album);      // Make sure that we now have a public photo and private photo. @@ -385,6 +387,8 @@ class Access_Helper_Test extends Gallery_Unit_Test_Case {      // Make sure that the public_photo is now private, and the private_photo is now public.      $this->assert_false(access::group_can(identity::everybody(), "view", $public_photo)); +    $this->assert_false(access::group_can(identity::everybody(), "edit", $public_photo));      $this->assert_true(access::group_can(identity::everybody(), "view", $private_photo)); +    $this->assert_true(access::group_can(identity::everybody(), "edit", $private_photo));    }  } diff --git a/modules/gallery/tests/Item_Model_Test.php b/modules/gallery/tests/Item_Model_Test.php index bd123098..90c54e3c 100644 --- a/modules/gallery/tests/Item_Model_Test.php +++ b/modules/gallery/tests/Item_Model_Test.php @@ -136,20 +136,17 @@ class Item_Model_Test extends Gallery_Unit_Test_Case {      $this->assert_true(false, "Shouldn't get here");    } -  public function item_rename_fails_with_existing_name_test() { +  public function item_rename_over_existing_name_gets_uniqified_test() {      // Create a test photo      $item = test::random_photo();      $item2 = test::random_photo(); -    try { -      $item->name = $item2->name; -      $item->save(); -    } catch (ORM_Validation_Exception $e) { -      $this->assert_true(in_array("conflict", $e->validation->errors())); -      return; -    } +    $item->name = $item2->name; +    $item->save(); -    $this->assert_false(true, "rename should conflict"); +    // foo.jpg should become foo-####.jpg +    $this->assert_true( +      preg_match("/" . str_replace(".jpg", "", $item2->name) . "-\d+\.jpg/", $item->name));    }    public function move_album_test() { @@ -208,24 +205,21 @@ class Item_Model_Test extends Gallery_Unit_Test_Case {      $this->assert_equal("file", file_get_contents($photo->file_path()));    } -  public function move_album_fails_conflicting_target_test() { +  public function move_album_with_conflicting_target_gets_uniqified_test() {      $album = test::random_album();      $source = test::random_album_unsaved($album);      $source->name = $album->name;      $source->save();      // $source and $album have the same name, so if we move $source into the root they should -    // conflict. +    // conflict and get randomized -    try { -      $source->parent_id = item::root()->id; -      $source->save(); -    } catch (ORM_Validation_Exception $e) { -      $this->assert_equal( -        array("name" => "conflict", "slug" => "conflict"), $e->validation->errors()); -      return; -    } -    $this->assert_true(false, "Shouldn't get here"); +    $source->parent_id = item::root()->id; +    $source->save(); + +    // foo should become foo-#### +    $this->assert_true(preg_match("/{$album->name}-\d+/", $source->name)); +    $this->assert_true(preg_match("/{$album->slug}-\d+/", $source->slug));    }    public function move_album_fails_wrong_target_type_test() { @@ -245,7 +239,7 @@ class Item_Model_Test extends Gallery_Unit_Test_Case {      $this->assert_true(false, "Shouldn't get here");    } -  public function move_photo_fails_conflicting_target_test() { +  public function move_photo_with_conflicting_target_gets_uniqified_test() {      $photo1 = test::random_photo();      $album = test::random_album();      $photo2 = test::random_photo_unsaved($album); @@ -253,18 +247,17 @@ class Item_Model_Test extends Gallery_Unit_Test_Case {      $photo2->save();      // $photo1 and $photo2 have the same name, so if we move $photo1 into the root they should -    // conflict. +    // conflict and get uniqified. -    try { -      $photo2->parent_id = item::root()->id; -      $photo2->save(); -    } catch (Exception $e) { -      // pass -      $this->assert_equal( -        array("name" => "conflict", "slug" => "conflict"), $e->validation->errors()); -      return; -    } -    $this->assert_true(false, "Shouldn't get here"); +    $photo2->parent_id = item::root()->id; +    $photo2->save(); + +    // foo.jpg should become foo-####.jpg +    $this->assert_true( +      preg_match("/" . str_replace(".jpg", "", $photo1->name) . "-\d+\.jpg/", $photo2->name)); + +    // foo should become foo +    $this->assert_true(preg_match("/{$photo1->slug}/", $photo2->name));    }    public function move_album_inside_descendent_fails_test() { diff --git a/modules/gallery/tests/Sendmail_Test.php b/modules/gallery/tests/Sendmail_Test.php index 92974e5c..aee6abf5 100644 --- a/modules/gallery/tests/Sendmail_Test.php +++ b/modules/gallery/tests/Sendmail_Test.php @@ -19,24 +19,18 @@   */  class Sendmail_Test extends Gallery_Unit_Test_Case {    public function setup() { -    Kohana_Config::instance()->set("sendmail.from", "from@gallery3.com"); +    module::set_var("gallery", "email_from", "from@gallery3.com"); +    module::set_var("gallery", "email_reply_to", "reply_to@gallery3.com");    } -  public function sendmail_test() { -    $domain = Input::instance()->server("HTTP_HOST"); +  public function sendmail_basic_test() {      $expected = "To: receiver@someemail.com\r\n" .                  "From: from@gallery3.com\n" . -                "Reply-To: public@$domain\r\n" . +                "Reply-To: reply_to@gallery3.com\r\n" .                  "Subject: Test Email Unit test\r\n\r\n" .                  "The mail message body";      $result = Sendmail_For_Test::factory()        ->to("receiver@someemail.com") -      /* -       * @todo figure out why this test fails so badly, when the following -       * line is not supplied. It doesn't seem to be set by setup method -       * as you would expect. -       */ -      ->from("from@gallery3.com")        ->subject("Test Email Unit test")        ->message("The mail message body")        ->send() @@ -46,16 +40,15 @@ class Sendmail_Test extends Gallery_Unit_Test_Case {    }    public function sendmail_reply_to_test() { -    $domain = Input::instance()->server("HTTP_HOST");      $expected = "To: receiver@someemail.com\r\n" . -                "From: admin@$domain\n" . -                "Reply-To: reply-to@gallery3.com\r\n" . +                "From: from@gallery3.com\n" . +                "Reply-To: reply_to@gallery3.com\r\n" .                  "Subject: Test Email Unit test\r\n\r\n" .                  "The mail message body";      $result = Sendmail_For_Test::factory()        ->to("receiver@someemail.com")        ->subject("Test Email Unit test") -      ->reply_to("reply-to@gallery3.com") +      ->reply_to("reply_to@gallery3.com")        ->message("The mail message body")        ->send()        ->send_text; @@ -63,10 +56,9 @@ class Sendmail_Test extends Gallery_Unit_Test_Case {    }    public function sendmail_html_message_test() { -    $domain = Input::instance()->server("HTTP_HOST");      $expected = "To: receiver@someemail.com\r\n" . -                "From: admin@$domain\n" . -                "Reply-To: public@$domain\n" . +                "From: from@gallery3.com\n" . +                "Reply-To: reply_to@gallery3.com\n" .                  "MIME-Version: 1.0\n" .                  "Content-Type: text/html; charset=UTF-8\r\n" .                  "Subject: Test Email Unit test\r\n\r\n" . @@ -85,8 +77,8 @@ class Sendmail_Test extends Gallery_Unit_Test_Case {    public function sendmail_wrapped_message_test() {      $domain = Input::instance()->server("HTTP_HOST");      $expected = "To: receiver@someemail.com\r\n" . -                "From: admin@$domain\n" . -                "Reply-To: public@$domain\r\n" . +                "From: from@gallery3.com\n" . +                "Reply-To: reply_to@gallery3.com\r\n" .                  "Subject: Test Email Unit test\r\n\r\n" .                  "This is a long message that needs to go\n" .                  "over forty characters If we get lucky we\n" . diff --git a/modules/gallery/tests/controller_auth_data.txt b/modules/gallery/tests/controller_auth_data.txt index 8b776fb9..03032fd9 100644 --- a/modules/gallery/tests/controller_auth_data.txt +++ b/modules/gallery/tests/controller_auth_data.txt @@ -15,8 +15,9 @@ modules/gallery/controllers/login.php                        html  modules/gallery/controllers/login.php                        auth_html            DIRTY_AUTH  modules/gallery/controllers/logout.php                       index                DIRTY_AUTH  modules/gallery/controllers/quick.php                        form_edit            DIRTY_CSRF -modules/gallery/controllers/upgrader.php                     index                DIRTY_AUTH +modules/gallery/controllers/upgrader.php                     index                DIRTY_CSRF|DIRTY_AUTH  modules/gallery/controllers/uploader.php                     start                DIRTY_AUTH +modules/gallery/controllers/uploader.php                     status               DIRTY_AUTH  modules/gallery/controllers/uploader.php                     finish               DIRTY_AUTH  modules/gallery/controllers/user_profile.php                 show                 DIRTY_AUTH  modules/gallery/controllers/user_profile.php                 contact              DIRTY_AUTH diff --git a/modules/gallery/tests/xss_data.txt b/modules/gallery/tests/xss_data.txt index 3eae3d07..6821c963 100644 --- a/modules/gallery/tests/xss_data.txt +++ b/modules/gallery/tests/xss_data.txt @@ -42,7 +42,7 @@ modules/digibug/views/digibug_form.html.php                  4   DIRTY    form::  modules/digibug/views/digibug_form.html.php                  6   DIRTY    form::hidden($key,$value)  modules/exif/views/exif_dialog.html.php                      14  DIRTY    $details[$i]["caption"]  modules/exif/views/exif_dialog.html.php                      21  DIRTY    $details[$i]["caption"] -modules/g2_import/views/admin_g2_import.html.php             30  DIRTY    $form +modules/g2_import/views/admin_g2_import.html.php             9   DIRTY    $form  modules/gallery/views/admin_advanced_settings.html.php       21  DIRTY_ATTR text::alternate("g-odd","g-even")  modules/gallery/views/admin_advanced_settings.html.php       22  DIRTY    $var->module_name  modules/gallery/views/admin_block_log_entries.html.php       4   DIRTY_ATTR log::severity_class($entry->severity) @@ -88,15 +88,15 @@ modules/gallery/views/admin_maintenance.html.php             87  DIRTY_ATTR $tas  modules/gallery/views/admin_maintenance.html.php             88  DIRTY    gallery::date_time($task->updated)  modules/gallery/views/admin_maintenance.html.php             91  DIRTY    $task->name  modules/gallery/views/admin_maintenance.html.php             106 DIRTY    $task->status -modules/gallery/views/admin_maintenance.html.php             157 DIRTY_ATTR text::alternate("g-odd","g-even") -modules/gallery/views/admin_maintenance.html.php             157 DIRTY_ATTR $task->state=="success"?"g-success":"g-error" -modules/gallery/views/admin_maintenance.html.php             158 DIRTY_ATTR $task->state=="success"?"g-success":"g-error" -modules/gallery/views/admin_maintenance.html.php             159 DIRTY    gallery::date_time($task->updated) -modules/gallery/views/admin_maintenance.html.php             162 DIRTY    $task->name -modules/gallery/views/admin_maintenance.html.php             174 DIRTY    $task->status +modules/gallery/views/admin_maintenance.html.php             162 DIRTY_ATTR text::alternate("g-odd","g-even") +modules/gallery/views/admin_maintenance.html.php             162 DIRTY_ATTR $task->state=="success"?"g-success":"g-error" +modules/gallery/views/admin_maintenance.html.php             163 DIRTY_ATTR $task->state=="success"?"g-success":"g-error" +modules/gallery/views/admin_maintenance.html.php             164 DIRTY    gallery::date_time($task->updated) +modules/gallery/views/admin_maintenance.html.php             167 DIRTY    $task->name +modules/gallery/views/admin_maintenance.html.php             179 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 +modules/gallery/views/admin_maintenance_task.html.php        75  DIRTY    $task->name  modules/gallery/views/admin_modules.html.php                 51  DIRTY    access::csrf_form_field()  modules/gallery/views/admin_modules.html.php                 60  DIRTY_ATTR text::alternate("g-odd","g-even")  modules/gallery/views/admin_modules.html.php                 63  DIRTY    form::checkbox($data,'1',module::is_active($module_name)) @@ -109,7 +109,7 @@ modules/gallery/views/admin_sidebar.html.php                 50  DIRTY    $avail  modules/gallery/views/admin_sidebar.html.php                 58  DIRTY    $active  modules/gallery/views/admin_sidebar_blocks.html.php          4   DIRTY_ATTR $ref  modules/gallery/views/admin_sidebar_blocks.html.php          4   DIRTY    $text -modules/gallery/views/admin_theme_options.html.php           36  DIRTY    $form +modules/gallery/views/admin_theme_options.html.php           5   DIRTY    $form  modules/gallery/views/admin_themes.html.php                  3   DIRTY_JS url::site("admin/themes/choose")  modules/gallery/views/admin_themes.html.php                  5   DIRTY_JS $csrf  modules/gallery/views/admin_themes.html.php                  22  DIRTY    $themes[$site]->name @@ -168,10 +168,13 @@ modules/gallery/views/error_admin.html.php                   284 DIRTY    $var  modules/gallery/views/error_admin.html.php                   285 DIRTY_ATTR $env_id  modules/gallery/views/error_admin.html.php                   291 DIRTY    $key  modules/gallery/views/error_admin.html.php                   295 DIRTY    Kohana_Exception::safe_dump($value,$key) -modules/gallery/views/form_uploadify.html.php                9   DIRTY_JS url::file("lib/uploadify/uploadify.swf") -modules/gallery/views/form_uploadify.html.php                10  DIRTY_JS url::site("uploader/add_photo/{$album->id}") -modules/gallery/views/form_uploadify.html.php                14  DIRTY_JS url::file("lib/uploadify/cancel.png") -modules/gallery/views/form_uploadify.html.php                15  DIRTY_JS $simultaneous_upload_limit +modules/gallery/views/form_uploadify.html.php                16  DIRTY_JS url::site("uploader/status/_S/_E") +modules/gallery/views/form_uploadify.html.php                24  DIRTY_JS $flash_minimum_version +modules/gallery/views/form_uploadify.html.php                28  DIRTY_JS url::file("lib/uploadify/uploadify.swf") +modules/gallery/views/form_uploadify.html.php                29  DIRTY_JS url::site("uploader/add_photo/{$album->id}") +modules/gallery/views/form_uploadify.html.php                33  DIRTY_JS url::file("lib/uploadify/cancel.png") +modules/gallery/views/form_uploadify.html.php                34  DIRTY_JS $simultaneous_upload_limit +modules/gallery/views/form_uploadify.html.php                160 DIRTY_ATTR request::protocol()  modules/gallery/views/in_place_edit.html.php                 2   DIRTY    form::open($action,array("method"=>"post","id"=>"g-in-place-edit-form","class"=>"g-short-form"))  modules/gallery/views/in_place_edit.html.php                 3   DIRTY    access::csrf_form_field()  modules/gallery/views/in_place_edit.html.php                 6   DIRTY    form::input("input",$form["input"]," class=\"textbox\"") @@ -207,17 +210,6 @@ modules/gallery/views/menu_dialog.html.php                   5   DIRTY_JS $menu-  modules/gallery/views/menu_link.html.php                     3   DIRTY    $menu->css_id?"id='{$menu->css_id}'":""  modules/gallery/views/menu_link.html.php                     4   DIRTY_ATTR $menu->css_class  modules/gallery/views/menu_link.html.php                     5   DIRTY_JS $menu->url -modules/gallery/views/move_browse.html.php                   5   DIRTY_JS url::site("move/show_sub_tree/{$source->id}/__TARGETID__") -modules/gallery/views/move_browse.html.php                   40  DIRTY    $tree -modules/gallery/views/move_browse.html.php                   44  DIRTY    access::csrf_form_field() -modules/gallery/views/move_tree.html.php                     2   DIRTY    $parent->thumb_img(array(),25); -modules/gallery/views/move_tree.html.php                     4   DIRTY_JS $parent->id -modules/gallery/views/move_tree.html.php                     6   DIRTY_JS $parent->id -modules/gallery/views/move_tree.html.php                     8   DIRTY_ATTR $parent->id -modules/gallery/views/move_tree.html.php                     10  DIRTY_ATTR $child->id -modules/gallery/views/move_tree.html.php                     11  DIRTY    $child->thumb_img(array(),25); -modules/gallery/views/move_tree.html.php                     13  DIRTY_JS $child->id -modules/gallery/views/move_tree.html.php                     15  DIRTY_JS $child->id  modules/gallery/views/movieplayer.html.php                   2   DIRTY    html::anchor($item->file_url(true),"",$attrs)  modules/gallery/views/movieplayer.html.php                   5   DIRTY_JS $attrs["id"]  modules/gallery/views/movieplayer.html.php                   7   DIRTY_JS url::abs_file("lib/flowplayer.swf") @@ -256,14 +248,15 @@ modules/gallery/views/permissions_form.html.php              80  DIRTY_JS $permi  modules/gallery/views/permissions_form.html.php              80  DIRTY_JS $item->id  modules/gallery/views/quick_delete_confirm.html.php          11  DIRTY    $form  modules/gallery/views/reauthenticate.html.php                9   DIRTY    $form -modules/gallery/views/upgrader.html.php                      59  DIRTY_ATTR $done?"muted":"" -modules/gallery/views/upgrader.html.php                      63  DIRTY_ATTR $done?"muted":"" -modules/gallery/views/upgrader.html.php                      71  DIRTY_ATTR $module->version==$module->code_version?"current":"upgradeable" -modules/gallery/views/upgrader.html.php                      72  DIRTY_ATTR $id -modules/gallery/views/upgrader.html.php                      76  DIRTY    $module->version -modules/gallery/views/upgrader.html.php                      79  DIRTY    $module->code_version -modules/gallery/views/upgrader.html.php                      101 DIRTY_ATTR $done?"muted":"" -modules/gallery/views/upgrader.html.php                      104 DIRTY_ATTR $done?"muted":"" +modules/gallery/views/upgrader.html.php                      76  DIRTY_ATTR $done?"muted":"" +modules/gallery/views/upgrader.html.php                      94  DIRTY_ATTR $done?"muted":"" +modules/gallery/views/upgrader.html.php                      102 DIRTY_ATTR $module->version==$module->code_version?"current":"upgradeable" +modules/gallery/views/upgrader.html.php                      102 DIRTY_ATTR in_array($id,$failed)?"failed":"" +modules/gallery/views/upgrader.html.php                      103 DIRTY_ATTR $id +modules/gallery/views/upgrader.html.php                      107 DIRTY    $module->version +modules/gallery/views/upgrader.html.php                      110 DIRTY    $module->code_version +modules/gallery/views/upgrader.html.php                      120 DIRTY_ATTR $done?"muted":"" +modules/gallery/views/upgrader.html.php                      123 DIRTY_ATTR $done?"muted":""  modules/gallery/views/user_languages_block.html.php          2   DIRTY    form::dropdown("g-select-session-locale",$installed_locales,$selected)  modules/gallery/views/user_profile.html.php                  34  DIRTY_ATTR $user->avatar_url(40,$theme->url(,true))  modules/gallery/views/user_profile.html.php                  43  DIRTY    $info->view @@ -288,10 +281,11 @@ modules/organize/views/organize_dialog.html.php              93  DIRTY_JS $file_  modules/organize/views/organize_dialog.html.php              94  DIRTY_JS $sort_order  modules/organize/views/organize_dialog.html.php              95  DIRTY_JS $sort_fields  modules/organize/views/organize_dialog.html.php              96  DIRTY_JS $album->id -modules/organize/views/organize_dialog.html.php              97  DIRTY_JS $rest_uri -modules/organize/views/organize_dialog.html.php              98  DIRTY_JS $controller_uri -modules/organize/views/organize_dialog.html.php              104 DIRTY_JS $flash_minimum_version="10.0.0" -modules/organize/views/organize_dialog.html.php              122 DIRTY_JS $swf_uri +modules/organize/views/organize_dialog.html.php              97  DIRTY_JS $selected_id +modules/organize/views/organize_dialog.html.php              98  DIRTY_JS $rest_uri +modules/organize/views/organize_dialog.html.php              99  DIRTY_JS $controller_uri +modules/organize/views/organize_dialog.html.php              105 DIRTY_JS $flash_minimum_version="10.0.0" +modules/organize/views/organize_dialog.html.php              123 DIRTY_JS $swf_uri  modules/organize/views/organize_dialog.html.php              136 DIRTY_ATTR request::protocol()  modules/recaptcha/views/admin_recaptcha.html.php             11  DIRTY    $form  modules/recaptcha/views/admin_recaptcha.html.php             23  DIRTY_JS $public_key @@ -337,23 +331,25 @@ modules/server_add/views/server_add_tree_dialog.html.php     4   DIRTY_JS url::s  modules/server_add/views/server_add_tree_dialog.html.php     21  DIRTY    $tree  modules/tag/views/admin_tags.html.php                        45  DIRTY_ATTR $tag->id  modules/tag/views/admin_tags.html.php                        46  DIRTY    $tag->count -modules/tag/views/tag_block.html.php                         25  DIRTY    $cloud -modules/tag/views/tag_block.html.php                         27  DIRTY    $form +modules/tag/views/tag_block.html.php                         26  DIRTY    $cloud +modules/tag/views/tag_block.html.php                         28  DIRTY    $form  modules/tag/views/tag_cloud.html.php                         4   DIRTY_ATTR (int)(($tag->count/$max_count)*7)  modules/tag/views/tag_cloud.html.php                         5   DIRTY    $tag->count  modules/tag/views/tag_cloud.html.php                         6   DIRTY_JS $tag->url()  modules/user/views/admin_users.html.php                      3   DIRTY_JS url::site("admin/users/add_user_to_group/__USERID__/__GROUPID__?csrf=$csrf")  modules/user/views/admin_users.html.php                      26  DIRTY_JS url::site("admin/users/group/__GROUPID__")  modules/user/views/admin_users.html.php                      36  DIRTY_JS url::site("admin/users/remove_user_from_group/__USERID__/__GROUPID__?csrf=$csrf") -modules/user/views/admin_users.html.php                      71  DIRTY_ATTR $user->id -modules/user/views/admin_users.html.php                      71  DIRTY_ATTR text::alternate("g-odd","g-even") -modules/user/views/admin_users.html.php                      71  DIRTY_ATTR $user->admin?"g-admin":""  modules/user/views/admin_users.html.php                      72  DIRTY_ATTR $user->id -modules/user/views/admin_users.html.php                      73  DIRTY_ATTR $user->avatar_url(20,$theme->url(,true)) -modules/user/views/admin_users.html.php                      87  DIRTY    ($user->last_login==0)?"":gallery::date($user->last_login) -modules/user/views/admin_users.html.php                      123 DIRTY_ATTR $group->id -modules/user/views/admin_users.html.php                      123 DIRTY_ATTR ($group->special?"g-default-group":"") -modules/user/views/admin_users.html.php                      125 DIRTY    $v +modules/user/views/admin_users.html.php                      72  DIRTY_ATTR text::alternate("g-odd","g-even") +modules/user/views/admin_users.html.php                      72  DIRTY_ATTR $user->admin?"g-admin":"" +modules/user/views/admin_users.html.php                      73  DIRTY_ATTR $user->id +modules/user/views/admin_users.html.php                      74  DIRTY_ATTR $user->avatar_url(20,$theme->url(,true)) +modules/user/views/admin_users.html.php                      88  DIRTY    ($user->last_login==0)?"":gallery::date($user->last_login) +modules/user/views/admin_users.html.php                      91  DIRTY    db::build()->from("items")->where("owner_id","=",$user->id)->count_records() +modules/user/views/admin_users.html.php                      127 DIRTY_ATTR $group->id +modules/user/views/admin_users.html.php                      127 DIRTY_ATTR ($group->special?"g-default-group":"") +modules/user/views/admin_users.html.php                      129 DIRTY    $v +modules/user/views/admin_users_delete_user.html.php          6   DIRTY    $form  modules/user/views/admin_users_group.html.php                24  DIRTY_JS $user->id  modules/user/views/admin_users_group.html.php                24  DIRTY_JS $group->id  modules/watermark/views/admin_watermarks.html.php            20  DIRTY_ATTR $width @@ -385,10 +381,10 @@ themes/admin_wind/views/pager.html.php                       37  DIRTY_JS str_re  themes/wind/views/album.html.php                             16  DIRTY_ATTR $child->id  themes/wind/views/album.html.php                             16  DIRTY_ATTR $item_class  themes/wind/views/album.html.php                             18  DIRTY_JS $child->url() -themes/wind/views/album.html.php                             19  DIRTY    $child->thumb_img(array("class"=>"g-thumbnail")) -themes/wind/views/album.html.php                             23  DIRTY_ATTR $item_class -themes/wind/views/album.html.php                             24  DIRTY_JS $child->url() -themes/wind/views/album.html.php                             42  DIRTY    $theme->paginator() +themes/wind/views/album.html.php                             20  DIRTY    $child->thumb_img(array("class"=>"g-thumbnail")) +themes/wind/views/album.html.php                             25  DIRTY_ATTR $item_class +themes/wind/views/album.html.php                             26  DIRTY_JS $child->url() +themes/wind/views/album.html.php                             44  DIRTY    $theme->paginator()  themes/wind/views/block.html.php                             3   DIRTY_ATTR $anchor  themes/wind/views/block.html.php                             5   DIRTY_ATTR $css_id  themes/wind/views/block.html.php                             6   DIRTY    $title @@ -410,16 +406,17 @@ themes/wind/views/page.html.php                              44  DIRTY    $thumb  themes/wind/views/page.html.php                              81  DIRTY    $header_text  themes/wind/views/page.html.php                              83  DIRTY_JS item::root()->url()  themes/wind/views/page.html.php                              87  DIRTY    $theme->user_menu() -themes/wind/views/page.html.php                              108 DIRTY_JS $parent->url($parent==$theme->item()->parent()?"show={$theme->item()->id}":null) -themes/wind/views/page.html.php                              126 DIRTY    $content -themes/wind/views/page.html.php                              132 DIRTY    newView("sidebar.html") -themes/wind/views/page.html.php                              139 DIRTY    $footer_text +themes/wind/views/page.html.php                              108 DIRTY_JS $parent->url($parent->id==$theme->item()->parent_id?"show={$theme->item()->id}":null) +themes/wind/views/page.html.php                              129 DIRTY    $content +themes/wind/views/page.html.php                              135 DIRTY    newView("sidebar.html") +themes/wind/views/page.html.php                              142 DIRTY    $footer_text  themes/wind/views/paginator.html.php                         33  DIRTY_JS $first_page_url  themes/wind/views/paginator.html.php                         42  DIRTY_JS $previous_page_url  themes/wind/views/paginator.html.php                         70  DIRTY_JS $next_page_url  themes/wind/views/paginator.html.php                         79  DIRTY_JS $last_page_url -themes/wind/views/photo.html.php                             8   DIRTY_JS $theme->item()->width -themes/wind/views/photo.html.php                             8   DIRTY_JS $theme->item()->height -themes/wind/views/photo.html.php                             18  DIRTY    $theme->paginator() -themes/wind/views/photo.html.php                             23  DIRTY_JS $item->file_url() -themes/wind/views/photo.html.php                             25  DIRTY    $item->resize_img(array("id"=>"g-item-id-{$item->id}","class"=>"g-resize")) +themes/wind/views/photo.html.php                             7   DIRTY_JS $theme->item()->width +themes/wind/views/photo.html.php                             7   DIRTY_JS $theme->item()->height +themes/wind/views/photo.html.php                             17  DIRTY_JS url::site("items/dimensions/".$theme->item()->id) +themes/wind/views/photo.html.php                             31  DIRTY    $theme->paginator() +themes/wind/views/photo.html.php                             36  DIRTY_JS $item->file_url() +themes/wind/views/photo.html.php                             38  DIRTY    $item->resize_img(array("id"=>"g-item-id-{$item->id}","class"=>"g-resize")) diff --git a/modules/gallery/views/admin_block_platform.html.php b/modules/gallery/views/admin_block_platform.html.php index b1b8a2f9..379ab0aa 100644 --- a/modules/gallery/views/admin_block_platform.html.php +++ b/modules/gallery/views/admin_block_platform.html.php @@ -18,4 +18,7 @@    <li>      <?= t("Server load: %load_average", array("load_average" => $load_average)) ?>    </li> +  <li> +    <?= t("Graphics toolkit: %toolkit", array("toolkit" => module::get_var("gallery", "graphics_toolkit"))) ?> +  </li>  </ul> diff --git a/modules/gallery/views/admin_graphics.html.php b/modules/gallery/views/admin_graphics.html.php index 3a48e087..ae76f1e1 100644 --- a/modules/gallery/views/admin_graphics.html.php +++ b/modules/gallery/views/admin_graphics.html.php @@ -16,6 +16,7 @@    <h1> <?= t("Graphics settings") ?> </h1>    <p>      <?= t("Gallery needs a graphics toolkit in order to manipulate your photos.  Please choose one from the list below.") ?> +    <?= t("Can't decide which toolkit to choose?  <a href=\"%url\">We can help!</a>", array("url" => "http://codex.gallery2.org/Gallery3:Choosing_A_Graphics_Toolkit")) ?>    </p>    <div class="g-block-content"> diff --git a/modules/gallery/views/admin_maintenance.html.php b/modules/gallery/views/admin_maintenance.html.php index 4bfc57f0..c28def1d 100644 --- a/modules/gallery/views/admin_maintenance.html.php +++ b/modules/gallery/views/admin_maintenance.html.php @@ -109,16 +109,21 @@              <?= html::clean($task->owner()->name) ?>            </td>            <td> -            <a href="<?= url::site("admin/maintenance/cancel/$task->id?csrf=$csrf") ?>" -               class="g-button g-right ui-icon-left ui-state-default ui-corner-all"> -              <?= t("cancel") ?> -            </a>              <? if ($task->state == "stalled"): ?>              <a class="g-dialog-link g-button ui-icon-left ui-state-default ui-corner-all"                 href="<?= url::site("admin/maintenance/resume/$task->id?csrf=$csrf") ?>">                <?= t("resume") ?>              </a>              <? endif ?> +            <? if ($task->get_log()): ?> +            <a href="<?= url::site("admin/maintenance/show_log/$task->id?csrf=$csrf") ?>" class="g-dialog-link g-button ui-state-default ui-corner-all"> +              <?= t("view log") ?> +            </a> +            <? endif ?> +            <a href="<?= url::site("admin/maintenance/cancel/$task->id?csrf=$csrf") ?>" +               class="g-button ui-icon-left ui-state-default ui-corner-all"> +              <?= t("cancel") ?> +            </a>            </td>          </tr>          <? endforeach ?> @@ -183,7 +188,7 @@              </a>              <? if ($task->get_log()): ?>              <a href="<?= url::site("admin/maintenance/show_log/$task->id?csrf=$csrf") ?>" class="g-dialog-link g-button ui-state-default ui-corner-all"> -              <?= t("browse log") ?> +              <?= t("view log") ?>              </a>              <? endif ?>              <? else: ?> diff --git a/modules/gallery/views/admin_maintenance_task.html.php b/modules/gallery/views/admin_maintenance_task.html.php index 76756b66..013ac01f 100644 --- a/modules/gallery/views/admin_maintenance_task.html.php +++ b/modules/gallery/views/admin_maintenance_task.html.php @@ -3,6 +3,7 @@    var target_value;    var animation = null;    var delta = 1; +  var consecutive_error_count = 0;    animate_progress_bar = function() {      var current_value = parseInt($(".g-progress-bar div").css("width").replace("%", ""));      if (target_value > current_value) { @@ -26,12 +27,15 @@      $.fn.gallery_hover_init();    } +  var FAILED_MSG = <?= t("Something went wrong...sorry!  <a>Retry</a> or check the task log for details")->for_js() ?>; +  var ERROR_MSG = <?= t("Something went wrong!  Trying again in a moment... (__COUNT__)")->for_js() ?>;    update = function() {      $.ajax({        url: <?= html::js_string(url::site("admin/maintenance/run/$task->id?csrf=$csrf")) ?>,        dataType: "json",        success: function(data) {          target_value = data.task.percent_complete; +        consecutive_error_count = 0;          if (!animation) {            animate_progress_bar();          } @@ -42,6 +46,22 @@          } else {            setTimeout(update, 100);          } +      }, +      error: function(req, textStatus, errorThrown) { +        if (textStatus == "timeout" || textStatus == "parsererror") { +          consecutive_error_count++; +          if (consecutive_error_count == 5) { +            $("#g-status").html(FAILED_MSG); +            $("#g-pause-button").hide(); +            $("#g-done-button").show(); +            consecutive_error_count = 0;  // in case of a manual retry +            $("#g-status a").attr("href", "javascript:update()"); +          } else { +            $("#g-status").html(ERROR_MSG.replace("__COUNT__", consecutive_error_count)); +            // Give a little time to back off before retrying +            setTimeout(update, 1500 * consecutive_error_count); +          } +        }        }      });    } diff --git a/modules/gallery/views/form_uploadify.html.php b/modules/gallery/views/form_uploadify.html.php index 36f5f284..893bb3b9 100644 --- a/modules/gallery/views/form_uploadify.html.php +++ b/modules/gallery/views/form_uploadify.html.php @@ -60,7 +60,7 @@            $("#g-add-photos-status ul").append(              "<li id=\"q" + queueID + "\" class=\"g-success\">" + fileObj.name + " - " +              <?= t("Completed")->for_js() ?> + "</li>"); -          setTimeout(function() { $("#q" + queueID).slideUp("slow") }, 5000); +          setTimeout(function() { $("#q" + queueID).slideUp("slow").remove() }, 5000);            success_count++;            update_status();            return true; @@ -87,8 +87,8 @@                .replace("__TYPE__", errorObj.type));            }            $("#g-add-photos-status ul").append( -            "<li class=\"g-error\">" + fileObj.name + msg + "</li>"); -          $("#g-uploadify" + queueID).remove(); +            "<li id=\"q" + queueID + "\" class=\"g-error\">" + fileObj.name + msg + "</li>"); +          $("#g-uploadify").uploadifyCancel(queueID);            error_count++;            update_status();          }, diff --git a/modules/gallery/views/upgrader.html.php b/modules/gallery/views/upgrader.html.php index 0ce24ef8..1ec49c77 100644 --- a/modules/gallery/views/upgrader.html.php +++ b/modules/gallery/views/upgrader.html.php @@ -10,7 +10,7 @@    </head>    <body<? if (locales::is_rtl()) { echo ' class="rtl"'; } ?>>      <div id="outer"> -      <img src="<?= url::file("modules/gallery/images/gallery.png") ?>" /> +      <img id="logo" src="<?= url::file("modules/gallery/images/gallery.png") ?>" />        <div id="inner">          <? if ($can_upgrade): ?>          <div id="dialog" style="visibility: hidden"> @@ -31,6 +31,12 @@                      array("url" => html::mark_clean(url::base()))) ?>              </p>            </div> +          <div id="failed" style="display: none"> +            <h1> <?= t("Some modules failed to upgrade!") ?> </h1> +            <p> +              <?= t("Failed modules are <span class=\"failed\">highlighted</span>.  Try getting newer versions or <a href=\"%admin_modules\">deactivating those modules</a>.", array("admin_modules" => url::site("admin/modules"))) ?> +            </p> +          </div>          </div>          <script type="text/javascript">            $(document).ready(function() { @@ -41,6 +47,10 @@              <? if ($done): ?>              show_done();              <? endif ?> + +            <? if ($failed): ?> +            show_failed(); +            <? endif ?>            });            var show_busy = function() { @@ -55,10 +65,31 @@              $("#done").show();              $("#dialog_close_link").show();            } + +          var show_failed = function() { +            $("#dialog").css("visibility", "visible"); +            $("#failed").show(); +            $("#dialog_close_link").show(); +          }          </script> -        <p class="<?= $done ? "muted" : "" ?>"> -          <?= t("Welcome to the Gallery upgrader.  One click and you're done!") ?> -        </p> +        <div id="welcome_message"> +          <p class="<?= $done ? "muted" : "" ?>"> +            <?= t("Welcome to the Gallery upgrader.  One click and you're done!") ?> +          </p> +        </div> + +        <? if ($done): ?> +        <div id="upgrade_button" class="button muted"> +          <?= t("Upgrade all") ?> +        </div> +        <? else: ?> +        <div id="upgrade_button" class="button button-active"> +          <a id="upgrade_link" href="<?= url::site("upgrader/upgrade?csrf=" . access::csrf_token()) ?>"> +            <?= t("Upgrade all") ?> +          </a> +        </div> +        <? endif ?> +          <table>            <tr class="<?= $done ? "muted" : "" ?>">              <th class="name"> <?= t("Module name") ?> </th> @@ -68,7 +99,7 @@            <? foreach ($available as $id => $module): ?>            <? if ($module->active): ?> -          <tr class="<?= $module->version == $module->code_version ? "current" : "upgradeable" ?>" > +          <tr class="<?= $module->version == $module->code_version ? "current" : "upgradeable" ?> <?= in_array($id, $failed) ? "failed" : "" ?>" >              <td class="name <?= $id ?>">                <?= t($module->name) ?>              </td> @@ -85,18 +116,6 @@            <? endforeach ?>          </table> -        <? if ($done): ?> -        <div class="button muted"> -          <?= t("Upgrade all") ?> -        </div> -        <? else: ?> -        <div class="button button-active"> -          <a id="upgrade_link" href="<?= url::site("upgrader/upgrade") ?>"> -            <?= t("Upgrade all") ?> -          </a> -        </div> -        <? endif ?> -          <? if (@$inactive): ?>          <p class="<?= $done ? "muted" : "" ?>">            <?= t("The following modules are inactive and don't require an upgrade.") ?> | 
