summaryrefslogtreecommitdiff
path: root/modules/gallery/libraries
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gallery/libraries')
-rw-r--r--modules/gallery/libraries/Admin_View.php120
-rw-r--r--modules/gallery/libraries/Block.php30
-rw-r--r--modules/gallery/libraries/Breadcrumb.php70
-rw-r--r--modules/gallery/libraries/Form_Script.php66
-rw-r--r--modules/gallery/libraries/Form_Uploadify.php72
-rw-r--r--modules/gallery/libraries/Form_Uploadify_buttons.php25
-rw-r--r--modules/gallery/libraries/Gallery_I18n.php472
-rw-r--r--modules/gallery/libraries/Gallery_View.php243
-rw-r--r--modules/gallery/libraries/IdentityProvider.php283
-rw-r--r--modules/gallery/libraries/InPlaceEdit.php91
-rw-r--r--modules/gallery/libraries/MY_Database.php101
-rw-r--r--modules/gallery/libraries/MY_Forge.php45
-rw-r--r--modules/gallery/libraries/MY_Input.php31
-rw-r--r--modules/gallery/libraries/MY_Kohana_Exception.php101
-rw-r--r--modules/gallery/libraries/MY_ORM.php52
-rw-r--r--modules/gallery/libraries/MY_View.php85
-rw-r--r--modules/gallery/libraries/Menu.php257
-rw-r--r--modules/gallery/libraries/ORM_MPTT.php341
-rw-r--r--modules/gallery/libraries/SafeString.php162
-rw-r--r--modules/gallery/libraries/Sendmail.php98
-rw-r--r--modules/gallery/libraries/Task_Definition.php50
-rw-r--r--modules/gallery/libraries/Theme_View.php271
-rw-r--r--modules/gallery/libraries/drivers/Cache/Database.php166
-rw-r--r--modules/gallery/libraries/drivers/IdentityProvider.php134
24 files changed, 3366 insertions, 0 deletions
diff --git a/modules/gallery/libraries/Admin_View.php b/modules/gallery/libraries/Admin_View.php
new file mode 100644
index 0000000..62645d1
--- /dev/null
+++ b/modules/gallery/libraries/Admin_View.php
@@ -0,0 +1,120 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2013 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+class Admin_View_Core extends Gallery_View {
+ /**
+ * Attempts to load a view and pre-load view data.
+ *
+ * @throws Kohana_Exception if the requested view cannot be found
+ * @param string $name view name
+ * @param string $theme_name view name
+ * @return void
+ */
+ public function __construct($name) {
+ parent::__construct($name);
+
+ $this->theme_name = module::get_var("gallery", "active_admin_theme");
+ if (identity::active_user()->admin) {
+ $theme_name = Input::instance()->get("theme");
+ if ($theme_name &&
+ file_exists(THEMEPATH . $theme_name) &&
+ strpos(realpath(THEMEPATH . $theme_name), THEMEPATH) == 0) {
+ $this->theme_name = $theme_name;
+ }
+ }
+ $this->sidebar = "";
+ $this->set_global(array("theme" => $this,
+ "user" => identity::active_user(),
+ "page_type" => "admin",
+ "page_subtype" => $name,
+ "page_title" => null));
+ }
+
+ public function admin_menu() {
+ $menu = Menu::factory("root");
+ module::event("admin_menu", $menu, $this);
+
+ $settings_menu = $menu->get("settings_menu");
+ uasort($settings_menu->elements, array("Menu", "title_comparator"));
+
+ return $menu->render();
+ }
+
+ public function user_menu() {
+ $menu = Menu::factory("root")
+ ->css_id("g-login-menu")
+ ->css_class("g-inline ui-helper-clear-fix");
+ module::event("user_menu", $menu, $this);
+ return $menu->render();
+ }
+
+ /**
+ * Print out any site wide status information.
+ */
+ public function site_status() {
+ return site_status::get();
+ }
+
+ /**
+ * Print out any messages waiting for this user.
+ */
+ public function messages() {
+ return message::get();
+ }
+
+ /**
+ * Handle all theme functions that insert module content.
+ */
+ public function __call($function, $args) {
+ switch ($function) {
+ case "admin_credits";
+ case "admin_footer":
+ case "admin_header_top":
+ case "admin_header_bottom":
+ case "admin_page_bottom":
+ case "admin_page_top":
+ case "admin_head":
+ case "body_attributes":
+ case "html_attributes":
+ $blocks = array();
+ foreach (module::active() as $module) {
+ $helper_class = "{$module->name}_theme";
+ if (class_exists($helper_class) && method_exists($helper_class, $function)) {
+ $blocks[] = call_user_func_array(
+ array($helper_class, $function),
+ array_merge(array($this), $args));
+ }
+ }
+
+ if (Session::instance()->get("debug")) {
+ if ($function != "admin_head") {
+ array_unshift(
+ $blocks, "<div class=\"g-annotated-theme-block g-annotated-theme-block_$function\">" .
+ "<div class=\"title\">$function</div>");
+ $blocks[] = "</div>";
+ }
+ }
+
+ return implode("\n", $blocks);
+
+ default:
+ throw new Exception("@todo UNKNOWN_THEME_FUNCTION: $function");
+ }
+ }
+} \ No newline at end of file
diff --git a/modules/gallery/libraries/Block.php b/modules/gallery/libraries/Block.php
new file mode 100644
index 0000000..818af9c
--- /dev/null
+++ b/modules/gallery/libraries/Block.php
@@ -0,0 +1,30 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2013 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+class Block_Core {
+ public $content = null;
+ public $css_id = null;
+ public $id = null;
+ public $title = null;
+ public $anchor = null;
+
+ public function __toString() {
+ return View::factory("block.html", get_object_vars($this))->__toString();
+ }
+}
diff --git a/modules/gallery/libraries/Breadcrumb.php b/modules/gallery/libraries/Breadcrumb.php
new file mode 100644
index 0000000..3805c5d
--- /dev/null
+++ b/modules/gallery/libraries/Breadcrumb.php
@@ -0,0 +1,70 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2013 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+class Breadcrumb_Core {
+ public $title;
+ public $url;
+ public $first;
+ public $last;
+
+ static function instance($title, $url) {
+ return new Breadcrumb($title, $url);
+ }
+
+ public function __construct($title, $url) {
+ $this->title = $title;
+ $this->url = $url;
+ $this->first = false;
+ $this->last = false;
+ }
+
+ /**
+ * Return an array of Breadcrumb instances build from the parents of a given item.
+ * The first and last Breadcrumb instances will be marked first/last as appropriate.
+ * Each breadcrumb will have a ?show= query parameter that refers to the id of the next
+ * item in line.
+ *
+ * @return array Breadcrumb instances
+ */
+ static function array_from_item_parents($item) {
+ if ($item->id == item::root()->id) {
+ return array();
+ }
+
+ $bc = array_merge($item->parents()->as_array(), array($item));
+ for ($i = 0; $i < count($bc) - 1; $i++) {
+ $bc[$i] = new Breadcrumb($bc[$i]->title, $bc[$i]->url("show={$bc[$i+1]->id}"));
+ }
+ $bc[$i] = new Breadcrumb($item->title, $item->url());
+
+ $bc[0]->set_first();
+ end($bc)->set_last();
+ return $bc;
+ }
+
+ public function set_first() {
+ $this->first = true;
+ return $this;
+ }
+
+ public function set_last() {
+ $this->last = true;
+ return $this;
+ }
+}
diff --git a/modules/gallery/libraries/Form_Script.php b/modules/gallery/libraries/Form_Script.php
new file mode 100644
index 0000000..2b5ec3e
--- /dev/null
+++ b/modules/gallery/libraries/Form_Script.php
@@ -0,0 +1,66 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2013 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+class Form_Script_Core extends Forge {
+ protected $data = array(
+ "name" => false,
+ "type" => "script",
+ "url" => "",
+ "text" => "");
+
+ public function __construct($name) {
+ // Set dummy data so we don"t get errors
+ $this->attr["action"] = "";
+ $this->attr["method"] = "post";
+ $this->data["name"] = $name;
+ }
+
+ public function __get($key) {
+ return isset($this->data[$key]) ? $this->data[$key] : null;
+ }
+
+ /**
+ * Sets url attribute
+ */
+ public function url($url) {
+ $this->data["url"] = $url;
+
+ return $this;
+ }
+
+ public function text($script_text) {
+ $this->data["text"] = $script_text;
+
+ return $this;
+ }
+
+ public function render($template="forge_template", $custom=false) {
+ $script = array();
+ if (!empty($this->data["url"])) {
+ $script[] = html::script($this->data["url"]);
+ }
+
+ if (!empty($this->data["text"])) {
+ $script[] = "<script type=\"text/javascript\">\n{$this->data['text']}\n</script>\n";
+ }
+
+ return implode("\n", $script);
+ }
+
+} \ No newline at end of file
diff --git a/modules/gallery/libraries/Form_Uploadify.php b/modules/gallery/libraries/Form_Uploadify.php
new file mode 100644
index 0000000..1e58018
--- /dev/null
+++ b/modules/gallery/libraries/Form_Uploadify.php
@@ -0,0 +1,72 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2013 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+class Form_Uploadify_Core extends Form_Input {
+ protected $data = array(
+ "name" => false,
+ "type" => "UNKNOWN",
+ "url" => "",
+ "text" => "");
+
+ public function __construct($name) {
+ parent::__construct($name);
+ $this->data["script_data"] = array(
+ "g3sid" => Session::instance()->id(),
+ "user_agent" => Input::instance()->server("HTTP_USER_AGENT"),
+ "csrf" => access::csrf_token());
+ }
+
+ public function album(Item_Model $album) {
+ $this->data["album"] = $album;
+ return $this;
+ }
+
+ public function script_data($key, $value) {
+ $this->data["script_data"][$key] = $value;
+ }
+
+ public function render() {
+ $v = new View("form_uploadify.html");
+ $v->album = $this->data["album"];
+ $v->script_data = $this->data["script_data"];
+ $v->simultaneous_upload_limit = module::get_var("gallery", "simultaneous_upload_limit");
+ $v->movies_allowed = movie::allow_uploads();
+ $v->extensions = legal_file::get_filters();
+ $v->suhosin_session_encrypt = (bool) ini_get("suhosin.session.encrypt");
+
+ list ($toolkit_max_filesize_bytes, $toolkit_max_filesize) = graphics::max_filesize();
+
+ $upload_max_filesize = trim(ini_get("upload_max_filesize"));
+ $upload_max_filesize_bytes = num::convert_to_bytes($upload_max_filesize);
+
+ if ($upload_max_filesize_bytes < $toolkit_max_filesize_bytes) {
+ $v->size_limit_bytes = $upload_max_filesize_bytes;
+ $v->size_limit = $upload_max_filesize;
+ } else {
+ $v->size_limit_bytes = $toolkit_max_filesize_bytes;
+ $v->size_limit = $toolkit_max_filesize;
+ }
+
+ return $v;
+ }
+
+ public function validate() {
+ return true;
+ }
+} \ No newline at end of file
diff --git a/modules/gallery/libraries/Form_Uploadify_buttons.php b/modules/gallery/libraries/Form_Uploadify_buttons.php
new file mode 100644
index 0000000..2e0327e
--- /dev/null
+++ b/modules/gallery/libraries/Form_Uploadify_buttons.php
@@ -0,0 +1,25 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2013 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+class Form_Uploadify_buttons_Core extends Form_Input {
+ public function render() {
+ $v = new View("form_uploadify_buttons.html");
+ return $v;
+ }
+} \ No newline at end of file
diff --git a/modules/gallery/libraries/Gallery_I18n.php b/modules/gallery/libraries/Gallery_I18n.php
new file mode 100644
index 0000000..6b216a2
--- /dev/null
+++ b/modules/gallery/libraries/Gallery_I18n.php
@@ -0,0 +1,472 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2013 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * Translates a localizable message.
+ * @param $message String The message to be translated. E.g. "Hello world"
+ * @param $options array (optional) Options array for key value pairs which are used
+ * for pluralization and interpolation. Special key: "locale" to override the
+ * currently configured locale.
+ * @return String The translated message string.
+ */
+function t($message, $options=array()) {
+ return Gallery_I18n::instance()->translate($message, $options);
+}
+
+/**
+ * Translates a localizable message with plural forms.
+ * @param $singular String The message to be translated. E.g. "There is one album."
+ * @param $plural String The plural message to be translated. E.g.
+ * "There are %count albums."
+ * @param $count Number The number which is inserted for the %count placeholder and
+ * which is used to select the proper plural form ($singular or $plural).
+ * @param $options array (optional) Options array for key value pairs which are used
+ * for pluralization and interpolation. Special key: "locale" to override the
+ * currently configured locale.
+ * @return String The translated message string.
+ */
+function t2($singular, $plural, $count, $options=array()) {
+ return Gallery_I18n::instance()->translate(array("one" => $singular, "other" => $plural),
+ array_merge($options, array("count" => $count)));
+}
+
+class Gallery_I18n_Core {
+ private static $_instance;
+ private $_config = array();
+ private $_call_log = array();
+ private $_cache = array();
+
+ private function __construct($config) {
+ $this->_config = $config;
+ $this->locale($config['default_locale']);
+ }
+
+ public static function instance($config=null) {
+ if (self::$_instance == NULL || isset($config)) {
+ $config = isset($config) ? $config : Kohana::config('locale');
+ if (empty($config['default_locale'])) {
+ $config['default_locale'] = module::get_var('gallery', 'default_locale');
+ }
+ self::$_instance = new Gallery_I18n_Core($config);
+ }
+
+ return self::$_instance;
+ }
+
+ public function locale($locale=null) {
+ if ($locale) {
+ $this->_config['default_locale'] = $locale;
+ $php_locale = setlocale(LC_ALL, 0);
+ list ($php_locale, $unused) = explode('.', $php_locale . '.');
+ if ($php_locale != $locale) {
+ // Attempt to set PHP's locale as well (for number formatting, collation, etc.)
+ $locale_prefs = array($locale);
+ // Try appending some character set names; some systems (like FreeBSD) need this.
+ // Some systems require a format with hyphen (eg. Gentoo) and others without (eg. FreeBSD).
+ $charsets = array('utf8', 'UTF-8', 'UTF8', 'ISO8859-1', 'ISO-8859-1');
+ if (substr($locale, 0, 2) != 'en') {
+ $charsets = array_merge($charsets, array(
+ 'EUC', 'Big5', 'euc', 'ISO8859-2', 'ISO8859-5', 'ISO8859-7',
+ 'ISO8859-9', 'ISO-8859-2', 'ISO-8859-5', 'ISO-8859-7', 'ISO-8859-9'));
+ }
+ foreach ($charsets as $charset) {
+ $locale_prefs[] = $locale . '.' . $charset;
+ }
+ $locale_prefs[] = 'en_US';
+ $php_locale = setlocale(LC_ALL, $locale_prefs);
+ }
+ if (is_string($php_locale) && substr($php_locale, 0, 2) == 'tr') {
+ // Make PHP 5 work with Turkish (the localization results are mixed though).
+ // Hack for http://bugs.php.net/18556
+ setlocale(LC_CTYPE, 'C');
+ }
+ }
+ return $this->_config['default_locale'];
+ }
+
+ public function is_rtl($locale=null) {
+ $is_rtl = !empty($this->_config["force_rtl"]);
+ if (empty($is_rtl)) {
+ $locale or $locale = $this->locale();
+ list ($language, $territory) = explode('_', $locale . "_");
+ $is_rtl = in_array($language, array("he", "fa", "ar"));
+ }
+ return $is_rtl;
+ }
+
+ /**
+ * Translates a localizable message.
+ *
+ * Security:
+ * The returned string is safe for use in HTML (it contains a safe subset of HTML and
+ * interpolation parameters are converted to HTML entities).
+ * For use in JavaScript, please call ->for_js() on it.
+ *
+ * @param $message String|array The message to be translated. E.g. "Hello world"
+ * or array("one" => "One album", "other" => "%count albums")
+ * @param $options array (optional) Options array for key value pairs which are used
+ * for pluralization and interpolation. Special keys are "count" and "locale",
+ * the latter to override the currently configured locale.
+ * @return String The translated message string.
+ */
+ public function translate($message, $options=array()) {
+ $locale = empty($options['locale']) ? $this->_config['default_locale'] : $options['locale'];
+ $count = isset($options['count']) ? $options['count'] : null;
+ $values = $options;
+ unset($values['locale']);
+ $this->log($message, $options);
+
+ $entry = $this->lookup($locale, $message);
+
+ if (null === $entry) {
+ // Default to the root locale.
+ $entry = $message;
+ $locale = $this->_config['root_locale'];
+ }
+
+ $entry = $this->pluralize($locale, $entry, $count);
+
+ $entry = $this->interpolate($locale, $entry, $values);
+
+ return SafeString::of_safe_html($entry);
+ }
+
+ private function lookup($locale, $message) {
+ if (!isset($this->_cache[$locale])) {
+ $this->_cache[$locale] = self::load_translations($locale);
+ }
+
+ $key = self::get_message_key($message);
+
+ if (isset($this->_cache[$locale][$key])) {
+ return $this->_cache[$locale][$key];
+ } else {
+ return null;
+ }
+ }
+
+ private static function load_translations($locale) {
+ $cache_key = "translation|" . $locale;
+ $cache = Cache::instance();
+ $translations = $cache->get($cache_key);
+ if (!isset($translations) || !is_array($translations)) {
+ $translations = array();
+ foreach (db::build()
+ ->select("key", "translation")
+ ->from("incoming_translations")
+ ->where("locale", "=", $locale)
+ ->execute() as $row) {
+ $translations[$row->key] = unserialize($row->translation);
+ }
+
+ // Override incoming with outgoing...
+ foreach (db::build()
+ ->select("key", "translation")
+ ->from("outgoing_translations")
+ ->where("locale", "=", $locale)
+ ->execute() as $row) {
+ $translations[$row->key] = unserialize($row->translation);
+ }
+
+ $cache->set($cache_key, $translations, array("translation"), 0);
+ }
+ return $translations;
+ }
+
+ public function has_translation($message, $options=null) {
+ $locale = empty($options['locale']) ? $this->_config['default_locale'] : $options['locale'];
+
+ $entry = $this->lookup($locale, $message);
+
+ if (null === $entry) {
+ return false;
+ } else if (!is_array($message)) {
+ return $entry !== '';
+ } else {
+ if (!is_array($entry) || empty($entry)) {
+ return false;
+ }
+ // It would be better to verify that all the locale's plural forms have a non-empty
+ // translation, but this is fine for now.
+ foreach ($entry as $value) {
+ if ($value === '') {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ static function get_message_key($message) {
+ $as_string = is_array($message) ? implode('|', $message) : $message;
+ return md5($as_string);
+ }
+
+ static function is_plural_message($message) {
+ return is_array($message);
+ }
+
+ private function interpolate($locale, $string, $key_values) {
+ // TODO: Handle locale specific number formatting.
+
+ // Replace x_y before replacing x.
+ krsort($key_values, SORT_STRING);
+
+ $keys = array();
+ $values = array();
+ foreach ($key_values as $key => $value) {
+ $keys[] = "%$key";
+ $values[] = new SafeString($value);
+ }
+ return str_replace($keys, $values, $string);
+ }
+
+ private function pluralize($locale, $entry, $count) {
+ if (!is_array($entry)) {
+ return $entry;
+ }
+
+ $plural_key = self::get_plural_key($locale, $count);
+ if (!isset($entry[$plural_key])) {
+ // Fallback to the default plural form.
+ $plural_key = 'other';
+ }
+
+ if (isset($entry[$plural_key])) {
+ return $entry[$plural_key];
+ } else {
+ // Fallback to just any plural form.
+ list ($plural_key, $string) = each($entry);
+ return $string;
+ }
+ }
+
+ private function log($message, $options) {
+ $key = self::get_message_key($message);
+ isset($this->_call_log[$key]) or $this->_call_log[$key] = array($message, $options);
+ }
+
+ public function call_log() {
+ return $this->_call_log;
+ }
+
+ public static function clear_cache($locale=null) {
+ $cache = Cache::instance();
+ if ($locale) {
+ $cache->delete("translation|" . $locale);
+ } else {
+ $cache->delete_tag("translation");
+ }
+ }
+
+ private static function get_plural_key($locale, $count) {
+ $parts = explode('_', $locale);
+ $language = $parts[0];
+
+ // Data from CLDR 1.6 (http://unicode.org/cldr/data/common/supplemental/plurals.xml).
+ // Docs: http://www.unicode.org/cldr/data/charts/supplemental/language_plural_rules.html
+ switch ($language) {
+ case 'az':
+ case 'fa':
+ case 'hu':
+ case 'ja':
+ case 'ko':
+ case 'my':
+ case 'to':
+ case 'tr':
+ case 'vi':
+ case 'yo':
+ case 'zh':
+ case 'bo':
+ case 'dz':
+ case 'id':
+ case 'jv':
+ case 'ka':
+ case 'km':
+ case 'kn':
+ case 'ms':
+ case 'th':
+ return 'other';
+
+ case 'ar':
+ if ($count == 0) {
+ return 'zero';
+ } else if ($count == 1) {
+ return 'one';
+ } else if ($count == 2) {
+ return 'two';
+ } else if (is_int($count) && ($i = $count % 100) >= 3 && $i <= 10) {
+ return 'few';
+ } else if (is_int($count) && ($i = $count % 100) >= 11 && $i <= 99) {
+ return 'many';
+ } else {
+ return 'other';
+ }
+
+ case 'pt':
+ case 'am':
+ case 'bh':
+ case 'fil':
+ case 'tl':
+ case 'guw':
+ case 'hi':
+ case 'ln':
+ case 'mg':
+ case 'nso':
+ case 'ti':
+ case 'wa':
+ if ($count == 0 || $count == 1) {
+ return 'one';
+ } else {
+ return 'other';
+ }
+
+ case 'fr':
+ if ($count >= 0 and $count < 2) {
+ return 'one';
+ } else {
+ return 'other';
+ }
+
+ case 'lv':
+ if ($count == 0) {
+ return 'zero';
+ } else if ($count % 10 == 1 && $count % 100 != 11) {
+ return 'one';
+ } else {
+ return 'other';
+ }
+
+ case 'ga':
+ case 'se':
+ case 'sma':
+ case 'smi':
+ case 'smj':
+ case 'smn':
+ case 'sms':
+ if ($count == 1) {
+ return 'one';
+ } else if ($count == 2) {
+ return 'two';
+ } else {
+ return 'other';
+ }
+
+ case 'ro':
+ case 'mo':
+ if ($count == 1) {
+ return 'one';
+ } else if (is_int($count) && $count == 0 && ($i = $count % 100) >= 1 && $i <= 19) {
+ return 'few';
+ } else {
+ return 'other';
+ }
+
+ case 'lt':
+ if (is_int($count) && $count % 10 == 1 && $count % 100 != 11) {
+ return 'one';
+ } else if (is_int($count) && ($i = $count % 10) >= 2 && $i <= 9 && ($i = $count % 100) < 11 && $i > 19) {
+ return 'few';
+ } else {
+ return 'other';
+ }
+
+ case 'hr':
+ case 'ru':
+ case 'sr':
+ case 'uk':
+ case 'be':
+ case 'bs':
+ case 'sh':
+ if (is_int($count) && $count % 10 == 1 && $count % 100 != 11) {
+ return 'one';
+ } else if (is_int($count) && ($i = $count % 10) >= 2 && $i <= 4 && ($i = $count % 100) < 12 && $i > 14) {
+ return 'few';
+ } else if (is_int($count) && ($count % 10 == 0 || (($i = $count % 10) >= 5 && $i <= 9) || (($i = $count % 100) >= 11 && $i <= 14))) {
+ return 'many';
+ } else {
+ return 'other';
+ }
+
+ case 'cs':
+ case 'sk':
+ if ($count == 1) {
+ return 'one';
+ } else if (is_int($count) && $count >= 2 && $count <= 4) {
+ return 'few';
+ } else {
+ return 'other';
+ }
+
+ case 'pl':
+ if ($count == 1) {
+ return 'one';
+ } else if (is_int($count) && ($i = $count % 10) >= 2 && $i <= 4 &&
+ ($i = $count % 100) < 12 && $i > 14 && ($i = $count % 100) < 22 && $i > 24) {
+ return 'few';
+ } else {
+ return 'other';
+ }
+
+ case 'sl':
+ if ($count % 100 == 1) {
+ return 'one';
+ } else if ($count % 100 == 2) {
+ return 'two';
+ } else if (is_int($count) && ($i = $count % 100) >= 3 && $i <= 4) {
+ return 'few';
+ } else {
+ return 'other';
+ }
+
+ case 'mt':
+ if ($count == 1) {
+ return 'one';
+ } else if ($count == 0 || is_int($count) && ($i = $count % 100) >= 2 && $i <= 10) {
+ return 'few';
+ } else if (is_int($count) && ($i = $count % 100) >= 11 && $i <= 19) {
+ return 'many';
+ } else {
+ return 'other';
+ }
+
+ case 'mk':
+ if ($count % 10 == 1) {
+ return 'one';
+ } else {
+ return 'other';
+ }
+
+ case 'cy':
+ if ($count == 1) {
+ return 'one';
+ } else if ($count == 2) {
+ return 'two';
+ } else if ($count == 8 || $count == 11) {
+ return 'many';
+ } else {
+ return 'other';
+ }
+
+ default: // en, de, etc.
+ return $count == 1 ? 'one' : 'other';
+ }
+ }
+}
diff --git a/modules/gallery/libraries/Gallery_View.php b/modules/gallery/libraries/Gallery_View.php
new file mode 100644
index 0000000..8f02b53
--- /dev/null
+++ b/modules/gallery/libraries/Gallery_View.php
@@ -0,0 +1,243 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2013 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+class Gallery_View_Core extends View {
+ protected $theme_name = null;
+ protected $combine_queue = array();
+
+ /**
+ * Provide a url to a resource within the current theme. This allows us to refer to theme
+ * resources without naming the theme itself which makes themes easier to copy.
+ */
+ public function url($path, $absolute_url=false) {
+ $arg = "themes/{$this->theme_name}/$path";
+ return $absolute_url ? url::abs_file($arg) : url::file($arg);
+ }
+
+ /**
+ * Set up the data and render a pager.
+ *
+ * See themes/wind/views/pager.html for documentation on the variables generated here.
+ */
+ public function paginator() {
+ $v = new View("paginator.html");
+ $v->page_type = $this->page_type;
+ $v->page_subtype = $this->page_subtype;
+ $v->first_page_url = null;
+ $v->previous_page_url = null;
+ $v->next_page_url = null;
+ $v->last_page_url = null;
+
+ if ($this->page_type == "collection") {
+ $v->page = $this->page;
+ $v->max_pages = $this->max_pages;
+ $v->total = $this->children_count;
+
+ if ($this->page != 1) {
+ $v->first_page_url = url::site(url::merge(array("page" => 1)));
+ $v->previous_page_url = url::site(url::merge(array("page" => $this->page - 1)));
+ }
+
+ if ($this->page != $this->max_pages) {
+ $v->next_page_url = url::site(url::merge(array("page" => $this->page + 1)));
+ $v->last_page_url = url::site(url::merge(array("page" => $this->max_pages)));
+ }
+
+ $v->first_visible_position = ($this->page - 1) * $this->page_size + 1;
+ $v->last_visible_position = min($this->page * $this->page_size, $v->total);
+ } else if ($this->page_type == "item") {
+ $v->position = $this->position;
+ $v->total = $this->sibling_count;
+ if ($this->previous_item) {
+ $v->previous_page_url = $this->previous_item->url();
+ }
+
+ if ($this->next_item) {
+ $v->next_page_url = $this->next_item->url();
+ }
+ }
+
+ return $v;
+ }
+
+ /**
+ * Begin gather up scripts or css files so that they can be combined into a single request.
+ *
+ * @param $types a comma separated list of types to combine, eg "script,css"
+ */
+ public function start_combining($types) {
+ if (gallery::allow_css_and_js_combining()) {
+ foreach (explode(",", $types) as $type) {
+ $this->combine_queue[$type] = array();
+ }
+ }
+ }
+
+ /**
+ * If script combining is enabled, add this script to the list of scripts that will be
+ * combined into a single script element. When combined, the order of scripts is preserved.
+ *
+ * @param $file the file name or path of the script to include. If a path is specified then
+ * it needs to be relative to DOCROOT. Just specifying a file name will result
+ * in searching Kohana's cascading file system.
+ * @param $group the group of scripts to combine this with. defaults to "core"
+ */
+ public function script($file, $group="core") {
+ if (($path = gallery::find_file("js", $file, false))) {
+ if (isset($this->combine_queue["script"])) {
+ $this->combine_queue["script"][$group][$path] = 1;
+ } else {
+ return html::script($path);
+ }
+ } else {
+ Kohana_Log::add("error", "Can't find script file: $file");
+ }
+ }
+
+ /**
+ * If css combining is enabled, add this css to the list of css that will be
+ * combined into a single style element. When combined, the order of style elements
+ * is preserved.
+ *
+ * @param $file the file name or path of the css to include. If a path is specified then
+ * it needs to be relative to DOCROOT. Just specifying a file name will result
+ * in searching Kohana's cascading file system.
+ * @param $group the group of css to combine this with. defaults to "core"
+ */
+ public function css($file, $group="core") {
+ if (($path = gallery::find_file("css", $file, false))) {
+ if (isset($this->combine_queue["css"])) {
+ $this->combine_queue["css"][$group][$path] = 1;
+ } else {
+ return html::stylesheet($path);
+ }
+ } else {
+ Kohana_Log::add("error", "Can't find css file: $file");
+ }
+ }
+
+ /**
+ * Combine a series of files into a single one and cache it in the database.
+ * @param $type the data type (script or css)
+ * @param $group the group of scripts or css we want
+ */
+ public function get_combined($type, $group="core") {
+ $links = array();
+
+ if (empty($this->combine_queue[$type][$group])) {
+ return;
+ }
+
+ // Include the url in the cache key so that if the Gallery moves, we don't use old cached
+ // entries.
+ $key = array(url::abs_file(""));
+
+ foreach (array_keys($this->combine_queue[$type][$group]) as $path) {
+ $stats = stat($path);
+ // 7 == size, 9 == mtime, see http://php.net/stat
+ $key[] = "$path $stats[7] $stats[9]";
+ }
+
+ $key = md5(join(" ", $key));
+ $cache = Cache::instance();
+ $contents = $cache->get($key);
+
+ if (empty($contents)) {
+ $combine_data = new stdClass();
+ $combine_data->type = $type;
+ $combine_data->contents = $this->combine_queue[$type][$group];
+ module::event("before_combine", $combine_data);
+
+ $contents = "";
+ foreach (array_keys($this->combine_queue[$type][$group]) as $path) {
+ if ($type == "css") {
+ $contents .= "/* $path */\n" . $this->process_css($path) . "\n";
+ } else {
+ $contents .= "/* $path */\n" . file_get_contents($path) . "\n";
+ }
+ }
+
+ $combine_data = new stdClass();
+ $combine_data->type = $type;
+ $combine_data->contents = $contents;
+ module::event("after_combine", $combine_data);
+
+ $cache->set($key, $combine_data->contents, array($type), 30 * 84600);
+
+ $use_gzip = function_exists("gzencode") &&
+ (int) ini_get("zlib.output_compression") === 0;
+ if ($use_gzip) {
+ $cache->set("{$key}_gz", gzencode($combine_data->contents, 9, FORCE_GZIP),
+ array($type, "gzip"), 30 * 84600);
+ }
+
+ }
+
+ unset($this->combine_queue[$type][$group]);
+ if (empty($this->combine_queue[$type])) {
+ unset($this->combine_queue[$type]);
+ }
+
+ if ($type == "css") {
+ return html::stylesheet("combined/css/$key", "screen,print,projection", true);
+ } else {
+ return html::script("combined/javascript/$key", true);
+ }
+ }
+
+ /**
+ * Convert relative references inside a CSS file to absolute ones so that when it's served from
+ * a new location as part of a combined bundle the references are still correct.
+ * @param string the path to the css file
+ */
+ private function process_css($css_file) {
+ static $PATTERN = "#url\(\s*['|\"]{0,1}(.*?)['|\"]{0,1}\s*\)#";
+ $docroot_length = strlen(DOCROOT);
+
+ $css = file_get_contents($css_file);
+ if (preg_match_all($PATTERN, $css, $matches, PREG_SET_ORDER)) {
+ $search = $replace = array();
+ foreach ($matches as $match) {
+ $relative = dirname($css_file) . "/$match[1]";
+ if (!empty($relative)) {
+ $search[] = $match[0];
+ $replace[] = "url('" . url::abs_file($relative) . "')";
+ } else {
+ Kohana_Log::add("error", "Missing URL reference '{$match[1]}' in CSS file '$css_file'");
+
+ }
+ }
+ $replace = str_replace(DIRECTORY_SEPARATOR, "/", $replace);
+ $css = str_replace($search, $replace, $css);
+ }
+ $imports = preg_match_all("#@import\s*['|\"]{0,1}(.*?)['|\"]{0,1};#",
+ $css, $matches, PREG_SET_ORDER);
+
+ if ($imports) {
+ $search = $replace = array();
+ foreach ($matches as $match) {
+ $search[] = $match[0];
+ $replace[] = $this->process_css(dirname($css_file) . "/$match[1]");
+ }
+ $css = str_replace($search, $replace, $css);
+ }
+
+ return $css;
+ }
+} \ No newline at end of file
diff --git a/modules/gallery/libraries/IdentityProvider.php b/modules/gallery/libraries/IdentityProvider.php
new file mode 100644
index 0000000..525e169
--- /dev/null
+++ b/modules/gallery/libraries/IdentityProvider.php
@@ -0,0 +1,283 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2013 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * Provides a driver-based interface for managing users and groups.
+ */
+class IdentityProvider_Core {
+ protected static $instance;
+
+ // Configuration
+ protected $config;
+
+ // Driver object
+ protected $driver;
+
+ /**
+ * Returns a singleton instance of Identity.
+ * There can only be one Identity driver configured at a given point
+ *
+ * @param string configuration
+ * @return Identity_Core
+ */
+ static function &instance() {
+ if (empty(self::$instance)) {
+ // Create a new instance
+ self::$instance = new IdentityProvider();
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * Frees the current instance of the identity provider so the next call to instance will reload
+ *
+ * @param string configuration
+ * @return Identity_Core
+ */
+ static function reset() {
+ self::$instance = null;
+ Kohana_Config::instance()->clear("identity");
+ }
+
+ /**
+ * Return a commen confirmation message
+ */
+ static function confirmation_message() {
+ return t("Are you sure you want to change your Identity Provider? Continuing will delete all existing users.");
+ }
+
+ static function change_provider($new_provider) {
+ if (!identity::active_user()->admin && PHP_SAPI != "cli") {
+ // Below, the active user is set to the primary admin.
+ access::forbidden();
+ }
+
+ $current_provider = module::get_var("gallery", "identity_provider");
+ if (!empty($current_provider)) {
+ module::uninstall($current_provider);
+ }
+
+ try {
+ IdentityProvider::reset();
+ $provider = new IdentityProvider($new_provider);
+
+ module::set_var("gallery", "identity_provider", $new_provider);
+
+ if (class_exists("{$new_provider}_installer") &&
+ method_exists("{$new_provider}_installer", "initialize")) {
+ call_user_func("{$new_provider}_installer::initialize");
+ }
+
+ if (!$provider->admin_user()) {
+ throw new Exception("IdentityProvider $new_provider: Couldn't find the admin user!");
+ }
+
+ module::event("identity_provider_changed", $current_provider, $new_provider);
+
+ identity::set_active_user($provider->admin_user());
+ Session::instance()->regenerate();
+ } catch (Exception $e) {
+ static $restore_already_running;
+
+ // In case of error, make an attempt to restore the old provider. Since that's calling into
+ // this function again and can fail, we should be sure not to get into an infinite recursion.
+ if (!$restore_already_running) {
+ $restore_already_running = true;
+
+ // Make sure new provider is not in the database
+ try {
+ module::uninstall($new_provider);
+ } catch (Exception $e2) {
+ Kohana_Log::add("error", "Error uninstalling failed new provider\n" .
+ $e2->getMessage() . "\n" . $e2->getTraceAsString());
+ }
+
+ try {
+ // Lets reset to the current provider so that the gallery installation is still
+ // working.
+ module::set_var("gallery", "identity_provider", null);
+ IdentityProvider::change_provider($current_provider);
+ module::activate($current_provider);
+ } catch (Exception $e2) {
+ Kohana_Log::add("error", "Error restoring original identity provider\n" .
+ $e2->getMessage() . "\n" . $e2->getTraceAsString());
+ }
+
+ message::error(
+ t("Error attempting to enable \"%new_provider\" identity provider, reverted to \"%old_provider\" identity provider",
+ array("new_provider" => $new_provider, "old_provider" => $current_provider)));
+
+ $restore_already_running = false;
+ }
+ throw $e;
+ }
+ }
+
+ /**
+ * Loads the configured driver and validates it.
+ *
+ * @return void
+ */
+ public function __construct($config=null) {
+ if (empty($config)) {
+ $config = module::get_var("gallery", "identity_provider", "user");
+ }
+
+ // Test the config group name
+ if (($this->config = Kohana::config("identity." . $config)) === NULL) {
+ throw new Exception("@todo NO_USER_LIBRARY_CONFIGURATION_FOR: $config");
+ }
+
+ // Set driver name
+ $driver = "IdentityProvider_" . ucfirst($this->config["driver"]) ."_Driver";
+
+ // Load the driver
+ if ( ! Kohana::auto_load($driver)) {
+ throw new Kohana_Exception("core.driver_not_found", $this->config["driver"],
+ get_class($this));
+ }
+
+ // Initialize the driver
+ $this->driver = new $driver($this->config["params"]);
+
+ // Validate the driver
+ if ( !($this->driver instanceof IdentityProvider_Driver)) {
+ throw new Kohana_Exception("core.driver_implements", $this->config["driver"],
+ get_class($this), "IdentityProvider_Driver");
+ }
+
+ Kohana_Log::add("debug", "Identity Library initialized");
+ }
+
+ /**
+ * Determine if if the current driver supports updates.
+ *
+ * @return boolean true if the driver supports updates; false if read only
+ */
+ public function is_writable() {
+ return !empty($this->config["allow_updates"]);
+ }
+
+ /**
+ * @see IdentityProvider_Driver::guest.
+ */
+ public function guest() {
+ return $this->driver->guest();
+ }
+
+ /**
+ * @see IdentityProvider_Driver::admin_user.
+ */
+ public function admin_user() {
+ return $this->driver->admin_user();
+ }
+
+ /**
+ * @see IdentityProvider_Driver::create_user.
+ */
+ public function create_user($name, $full_name, $password, $email) {
+ return $this->driver->create_user($name, $full_name, $password, $email);
+ }
+
+ /**
+ * @see IdentityProvider_Driver::is_correct_password.
+ */
+ public function is_correct_password($user, $password) {
+ return $this->driver->is_correct_password($user, $password);
+ }
+
+ /**
+ * @see IdentityProvider_Driver::lookup_user.
+ */
+ public function lookup_user($id) {
+ return $this->driver->lookup_user($id);
+ }
+
+ /**
+ * @see IdentityProvider_Driver::lookup_user_by_name.
+ */
+ public function lookup_user_by_name($name) {
+ return $this->driver->lookup_user_by_name($name);
+ }
+
+ /**
+ * @see IdentityProvider_Driver::create_group.
+ */
+ public function create_group($name) {
+ return $this->driver->create_group($name);
+ }
+
+ /**
+ * @see IdentityProvider_Driver::everybody.
+ */
+ public function everybody() {
+ return $this->driver->everybody();
+ }
+
+ /**
+ * @see IdentityProvider_Driver::registered_users.
+ */
+ public function registered_users() {
+ return $this->driver->registered_users();
+ }
+
+ /**
+ * @see IdentityProvider_Driver::lookup_group.
+ */
+ public function lookup_group($id) {
+ return $this->driver->lookup_group($id);
+ }
+
+ /**
+ * @see IdentityProvider_Driver::lookup_group_by_name.
+ */
+ public function lookup_group_by_name($name) {
+ return $this->driver->lookup_group_by_name($name);
+ }
+
+ /**
+ * @see IdentityProvider_Driver::get_user_list.
+ */
+ public function get_user_list($ids) {
+ return $this->driver->get_user_list($ids);
+ }
+
+ /**
+ * @see IdentityProvider_Driver::groups.
+ */
+ public function groups() {
+ return $this->driver->groups();
+ }
+
+ /**
+ * @see IdentityProvider_Driver::add_user_to_group.
+ */
+ public function add_user_to_group($user, $group) {
+ return $this->driver->add_user_to_group($user, $group);
+ }
+
+ /**
+ * @see IdentityProvider_Driver::remove_user_to_group.
+ */
+ public function remove_user_from_group($user, $group) {
+ return $this->driver->remove_user_from_group($user, $group);
+ }
+} // End Identity
diff --git a/modules/gallery/libraries/InPlaceEdit.php b/modules/gallery/libraries/InPlaceEdit.php
new file mode 100644
index 0000000..cd177c2
--- /dev/null
+++ b/modules/gallery/libraries/InPlaceEdit.php
@@ -0,0 +1,91 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2013 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+class InPlaceEdit_Core {
+ private $rules = array();
+ private $messages = array();
+ private $callback = array();
+ private $initial_value;
+ private $action = "";
+ private $errors;
+ private $form;
+
+ static function factory($initial_value) {
+ $instance = new InPlaceEdit();
+ $instance->initial_value = $initial_value;
+ $instance->form = array("input" => $initial_value);
+ $instance->errors = array("input" => "");
+
+ return $instance;
+ }
+
+ public function action($action) {
+ $this->action = $action;
+ return $this;
+ }
+
+ public function rules($rules) {
+ $this->rules += $rules;
+ return $this;
+ }
+
+ public function messages($messages) {
+ $this->messages += $messages;
+ return $this;
+ }
+
+ public function callback($callback) {
+ $this->callback = $callback;
+ return $this;
+ }
+
+ public function validate() {
+ $post = Validation::factory($_POST);
+
+ if (!empty($this->callback)) {
+ $post->add_callbacks("input", $this->callback);
+ }
+
+ foreach ($this->rules as $rule) {
+ $post->add_rules("input", $rule);
+ }
+
+ $valid = $post->validate();
+ $this->form = array_merge($this->form, $post->as_array());
+ $this->errors = array_merge($this->errors, $post->errors());
+ return $valid;
+ }
+
+ public function render() {
+ $v = new View("in_place_edit.html");
+ $v->action = $this->action;
+ $v->form = $this->form;
+ $v->errors = $this->errors;
+ foreach ($v->errors as $key => $error) {
+ if (!empty($error)) {
+ $v->errors[$key] = $this->messages[$error];
+ }
+ }
+ return $v->render();
+ }
+
+ public function value() {
+ return $this->form["input"];
+ }
+} \ No newline at end of file
diff --git a/modules/gallery/libraries/MY_Database.php b/modules/gallery/libraries/MY_Database.php
new file mode 100644
index 0000000..33759b6
--- /dev/null
+++ b/modules/gallery/libraries/MY_Database.php
@@ -0,0 +1,101 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2013 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+abstract class Database extends Database_Core {
+ protected $_table_names;
+
+ /**
+ * Kohana 2.4 introduces a new connection parameter. If it's not specified, make sure that we
+ * define it here to avoid an error later on.
+ *
+ * @todo: add an upgrade path to modify var/database.php so that we can avoid doing this at
+ * runtime.
+ */
+ protected function __construct(array $config) {
+ if (!isset($config["connection"]["params"])) {
+ $config["connection"]["params"] = null;
+ }
+ parent::__construct($config);
+ if (gallery::show_profiler()) {
+ $this->config['benchmark'] = true;
+ }
+ }
+
+ /**
+ * Parse the query string and convert any strings of the form `\([a-zA-Z0-9_]*?)\]
+ * table prefix . $1
+ */
+ public function query($sql) {
+ if (!empty($sql)) {
+ $sql = $this->add_table_prefixes($sql);
+ }
+ return parent::query($sql);
+ }
+
+ public function add_table_prefixes($sql) {
+ $prefix = $this->config["table_prefix"];
+ if (strpos($sql, "SHOW TABLES") === 0) {
+ /*
+ * Don't ignore "show tables", otherwise we could have a infinite
+ * @todo this may have to be changed if we support more than mysql
+ */
+ return $sql;
+ } else if (strpos($sql, "CREATE TABLE") === 0) {
+ // Creating a new table; add it to the table cache.
+ $open_brace = strpos($sql, "{") + 1;
+ $close_brace = strpos($sql, "}", $open_brace);
+ $name = substr($sql, $open_brace, $close_brace - $open_brace);
+ $this->_table_names["{{$name}}"] = "`{$prefix}$name`";
+ } else if (strpos($sql, "RENAME TABLE") === 0) {
+ // Renaming a table; add it to the table cache.
+ // You must use the form "TO {new_table_name}" exactly for this to work.
+ $open_brace = strpos($sql, "TO {") + 4;
+ $close_brace = strpos($sql, "}", $open_brace);
+ $name = substr($sql, $open_brace, $close_brace - $open_brace);
+ $this->_table_names["{{$name}}"] = "`{$prefix}$name`";
+ }
+
+ if (!isset($this->_table_names)) {
+ // This should only run once on the first query
+ $this->_table_names = array();
+ foreach($this->list_tables() as $table_name) {
+ $this->_table_names["{{$table_name}}"] = "`{$prefix}{$table_name}`";
+ }
+ }
+
+ return strtr($sql, $this->_table_names);
+ }
+
+ /**
+ * This is used by the unit test code to switch the active database connection.
+ */
+ static function set_default_instance($db) {
+ self::$instances["default"] = $db;
+ }
+
+ /**
+ * Escape LIKE queries, add wildcards. In MySQL queries using LIKE, _ and % characters are
+ * treated as wildcards similar to ? and *, respectively. Therefore, we need to escape _, %,
+ * and \ (the escape character itself).
+ */
+ static function escape_for_like($value) {
+ // backslash must go first to avoid double-escaping
+ return addcslashes($value, '\_%');
+ }
+} \ No newline at end of file
diff --git a/modules/gallery/libraries/MY_Forge.php b/modules/gallery/libraries/MY_Forge.php
new file mode 100644
index 0000000..635dc2d
--- /dev/null
+++ b/modules/gallery/libraries/MY_Forge.php
@@ -0,0 +1,45 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2013 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+class Forge extends Forge_Core {
+ /**
+ * Force a CSRF element into every form.
+ */
+ public function __construct($action=null, $title='', $method=null, $attr=array()) {
+ parent::__construct($action, $title, $method, $attr);
+ $this->hidden("csrf")->value(access::csrf_token());
+ }
+
+ /**
+ * Use our own template
+ */
+ public function render($template="form.html", $custom=false) {
+ return parent::render($template, $custom);
+ }
+
+ /**
+ * Validate our CSRF value as a mandatory part of all form validation.
+ */
+ public function validate() {
+ $status = parent::validate();
+ access::verify_csrf();
+ return $status;
+ }
+} \ No newline at end of file
diff --git a/modules/gallery/libraries/MY_Input.php b/modules/gallery/libraries/MY_Input.php
new file mode 100644
index 0000000..2f0a727
--- /dev/null
+++ b/modules/gallery/libraries/MY_Input.php
@@ -0,0 +1,31 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2013 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+class Input extends Input_Core {
+ /**
+ * Modified form of Input::clean_input_keys() that replaces malformed values
+ * instead of dying on bad input.
+ *
+ * @param string string to clean
+ * @return string
+ */
+ public function clean_input_keys($str) {
+ return preg_replace('#[^a-zA-Z0-9:_.-]+#', '_', $str);
+ }
+}
diff --git a/modules/gallery/libraries/MY_Kohana_Exception.php b/modules/gallery/libraries/MY_Kohana_Exception.php
new file mode 100644
index 0000000..51490a6
--- /dev/null
+++ b/modules/gallery/libraries/MY_Kohana_Exception.php
@@ -0,0 +1,101 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2013 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+class Kohana_Exception extends Kohana_Exception_Core {
+ /**
+ * Dump out the full stack trace as part of the text representation of the exception.
+ */
+ public static function text($e) {
+ if ($e instanceof Kohana_404_Exception) {
+ return "File not found: " . rawurlencode(Router::$complete_uri);
+ } else {
+ return sprintf(
+ "%s [ %s ]: %s\n%s [ %s ]\n%s",
+ get_class($e), $e->getCode(), strip_tags($e->getMessage()),
+ $e->getFile(), $e->getLine(),
+ $e->getTraceAsString());
+ }
+ }
+
+ /**
+ * @see Kohana_Exception::dump()
+ */
+ public static function dump($value, $length=128, $max_level=5) {
+ return self::safe_dump($value, null, $length, $max_level);
+ }
+
+ /**
+ * A safer version of dump(), eliding sensitive information in the dumped
+ * data, such as session ids and passwords / hashes.
+ */
+ public static function safe_dump($value, $key, $length=128, $max_level=5) {
+ return parent::dump(self::_sanitize_for_dump($value, $key, $max_level), $length, $max_level);
+ }
+
+ /**
+ * Elides sensitive data which shouldn't be echoed to the client,
+ * such as passwords, and other secrets.
+ */
+ /* Visible for testing*/ static function _sanitize_for_dump($value, $key=null, $max_level) {
+ // Better elide too much than letting something through.
+ // Note: unanchored match is intended.
+ if (!$max_level) {
+ // Too much recursion; give up. We gave it our best shot.
+ return $value;
+ }
+
+ $sensitive_info_pattern =
+ '/(password|pass|email|hash|private_key|session_id|session|g3sid|csrf|secret)/i';
+ if (preg_match($sensitive_info_pattern, $key) ||
+ (is_string($value) && preg_match('/[a-f0-9]{20,}/i', $value))) {
+ return 'removed for display';
+ } else if (is_object($value)) {
+ if ($value instanceof Database) {
+ // Elide database password, host, name, user, etc.
+ return get_class($value) . ' object - details omitted for display';
+ } else if ($value instanceof User_Model) {
+ return get_class($value) . ' object for "' . $value->name . '" - details omitted for display';
+ }
+ return self::_sanitize_for_dump((array) $value, $key, $max_level - 1);
+ } else if (is_array($value)) {
+ $result = array();
+ foreach ($value as $k => $v) {
+ $actual_key = $k;
+ $key_for_display = $k;
+ if ($k[0] === "\x00") {
+ // Remove the access level from the variable name
+ $actual_key = substr($k, strrpos($k, "\x00") + 1);
+ $access = $k[1] === '*' ? 'protected' : 'private';
+ $key_for_display = "$access: $actual_key";
+ }
+ if (is_object($v)) {
+ $key_for_display .= ' (type: ' . get_class($v) . ')';
+ }
+ $result[$key_for_display] = self::_sanitize_for_dump($v, $actual_key, $max_level - 1);
+ }
+ } else {
+ $result = $value;
+ }
+ return $result;
+ }
+
+ public static function debug_path($file) {
+ return html::clean(parent::debug_path($file));
+ }
+} \ No newline at end of file
diff --git a/modules/gallery/libraries/MY_ORM.php b/modules/gallery/libraries/MY_ORM.php
new file mode 100644
index 0000000..6e538d5
--- /dev/null
+++ b/modules/gallery/libraries/MY_ORM.php
@@ -0,0 +1,52 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2013 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+class ORM extends ORM_Core {
+
+ /**
+ * Make sure that we're only using integer ids.
+ */
+ static function factory($model, $id=null) {
+ if ($id && !is_int($id) && !is_string($id)) {
+ throw new Exception("@todo ORM::factory requires integer ids");
+ }
+ return ORM_Core::factory($model, (int) $id);
+ }
+
+ public function save() {
+ model_cache::clear();
+ return parent::save();
+ }
+}
+
+/**
+ * Slide this in here for convenience. We won't ever be overloading ORM_Iterator without ORM.
+ */
+class ORM_Iterator extends ORM_Iterator_Core {
+ /**
+ * Cache the result row
+ */
+ public function current() {
+ $row = parent::current();
+ if (is_object($row)) {
+ model_cache::set($row);
+ }
+ return $row;
+ }
+} \ No newline at end of file
diff --git a/modules/gallery/libraries/MY_View.php b/modules/gallery/libraries/MY_View.php
new file mode 100644
index 0000000..1e88611
--- /dev/null
+++ b/modules/gallery/libraries/MY_View.php
@@ -0,0 +1,85 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2013 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+class View extends View_Core {
+ static $global_data = array();
+
+ /**
+ * Reimplement Kohana 2.3's View::set_global() functionality.
+ */
+ public function set_global($key, $value = NULL) {
+ if (is_array($key)) {
+ foreach ($key as $key2 => $value) {
+ View::$global_data[$key2] = $value;
+ }
+ } else {
+ View::$global_data[$key] = $value;
+ }
+ }
+
+ public function is_set($key=null) {
+ return parent::is_set($key) ? true : array_key_exists($key, View::$global_data);
+ }
+
+ /**
+ * Completely replace View_Core::__get() so that local data trumps global data, trumps members.
+ * This simulates the Kohana 2.3 behavior.
+ */
+ public function &__get($key) {
+ if (isset($this->kohana_local_data[$key])) {
+ return $this->kohana_local_data[$key];
+ } else if (isset(View::$global_data[$key])) {
+ return View::$global_data[$key];
+ } else if (isset($this->$key)) {
+ return $this->$key;
+ } else {
+ throw new Kohana_Exception('Undefined view variable: :var', array(':var' => $key));
+ }
+ }
+
+ /**
+ * Override View_Core::__construct so that we can set the csrf value into all views.
+ *
+ * @see View_Core::__construct
+ */
+ public function __construct($name = NULL, $data = NULL, $type = NULL) {
+ parent::__construct($name, $data, $type);
+ $this->set_global("csrf", access::csrf_token());
+ }
+
+ /**
+ * Override View_Core::render so that we trap errors stemming from bad PHP includes and show a
+ * visible stack trace to help developers.
+ *
+ * @see View_Core::render
+ */
+ public function render($print=false, $renderer=false, $modifier=false) {
+ try {
+ $this->kohana_local_data = array_merge(View::$global_data, $this->kohana_local_data);
+ return parent::render($print, $renderer, $modifier);
+ } catch (ORM_Validation_Exception $e) {
+ Kohana_Log::add("error", $e->getMessage() . "\n" . $e->getTraceAsString());
+ Kohana_Log::add("error", "Validation errors: " . print_r($e->validation->errors(), 1));
+ return "";
+ } catch (Exception $e) {
+ Kohana_Log::add("error", $e->getMessage() . "\n" . $e->getTraceAsString());
+ return "";
+ }
+ }
+}
diff --git a/modules/gallery/libraries/Menu.php b/modules/gallery/libraries/Menu.php
new file mode 100644
index 0000000..24a05cd
--- /dev/null
+++ b/modules/gallery/libraries/Menu.php
@@ -0,0 +1,257 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2013 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+class Menu_Element {
+ public $label;
+ public $url;
+ public $css_id;
+ public $css_class;
+ public $id;
+ public $type;
+
+ public function __construct($type) {
+ $this->type = $type;
+ }
+
+ /**
+ * Set the id
+ * @chainable
+ */
+ public function id($id) {
+ $this->id = $id;
+ return $this;
+ }
+
+ /**
+ * Set the label
+ * @chainable
+ */
+ public function label($label) {
+ // Guard against developers who forget to internationalize label strings
+ if (!($label instanceof SafeString)) {
+ $label = new SafeString($label);
+ }
+
+ $this->label = $label;
+ return $this;
+ }
+
+ /**
+ * Set the url
+ * @chainable
+ */
+ public function url($url) {
+ $this->url = $url;
+ return $this;
+ }
+
+ /**
+ * Set the css id
+ * @chainable
+ */
+ public function css_id($css_id) {
+ $this->css_id = $css_id;
+ return $this;
+ }
+
+ /**
+ * Set the css class
+ * @chainable
+ */
+ public function css_class($css_class) {
+ $this->css_class = $css_class;
+ return $this;
+ }
+
+ /**
+ * Specifiy a view for this menu item
+ * @chainable
+ */
+ public function view($view) {
+ $this->view = $view;
+ return $this;
+ }
+
+}
+
+/**
+ * Menu element that provides a link to a new page.
+ */
+class Menu_Element_Link extends Menu_Element {
+ public function render() {
+ $view = new View(isset($this->view) ? $this->view : "menu_link.html");
+ $view->menu = $this;
+ return $view;
+ }
+}
+
+/**
+ * Menu element that provides an AJAX link.
+ */
+class Menu_Element_Ajax_Link extends Menu_Element {
+ public $ajax_handler;
+
+ /**
+ * Set the AJAX handler
+ * @chainable
+ */
+ public function ajax_handler($ajax_handler) {
+ $this->ajax_handler = $ajax_handler;
+ return $this;
+ }
+
+ public function render() {
+ $view = new View(isset($this->view) ? $this->view : "menu_ajax_link.html");
+ $view->menu = $this;
+ return $view;
+ }
+}
+
+/**
+ * Menu element that provides a pop-up dialog
+ */
+class Menu_Element_Dialog extends Menu_Element {
+ public function render() {
+ $view = new View(isset($this->view) ? $this->view : "menu_dialog.html");
+ $view->menu = $this;
+ return $view;
+ }
+}
+
+/**
+ * Root menu or submenu
+ */
+class Menu_Core extends Menu_Element {
+ public $elements;
+ public $is_root = false;
+
+ /**
+ * Return an instance of a Menu_Element
+ * @chainable
+ */
+ public static function factory($type) {
+ switch($type) {
+ case "link":
+ return new Menu_Element_Link($type);
+
+ case "ajax_link":
+ return new Menu_Element_Ajax_Link($type);
+
+ case "dialog":
+ return new Menu_Element_Dialog($type);
+
+ case "root":
+ $menu = new Menu("root");
+ $menu->css_class("g-menu");
+ return $menu;
+
+ case "submenu":
+ return new Menu("submenu");
+
+ default:
+ throw Exception("@todo UNKNOWN_MENU_TYPE");
+ }
+ }
+
+ public function __construct($type) {
+ parent::__construct($type);
+ $this->elements = array();
+ $this->is_root = $type == "root";
+ }
+
+ /**
+ * Add a new element to this menu
+ */
+ public function append($menu_element) {
+ $this->elements[$menu_element->id] = $menu_element;
+ return $this;
+ }
+
+ /**
+ * Add a new element to this menu, after the specific element
+ */
+ public function add_after($target_id, $new_menu_element) {
+ $copy = array();
+ foreach ($this->elements as $id => $menu_element) {
+ $copy[$id] = $menu_element;
+ if ($id == $target_id) {
+ $copy[$new_menu_element->id] = $new_menu_element;
+ }
+ }
+ $this->elements = $copy;
+ return $this;
+ }
+
+ /**
+ * Add a new element to this menu, before the specific element
+ */
+ public function add_before($target_id, $new_menu_element) {
+ $copy = array();
+ foreach ($this->elements as $id => $menu_element) {
+ if ($id == $target_id) {
+ $copy[$new_menu_element->id] = $new_menu_element;
+ }
+ $copy[$id] = $menu_element;
+ }
+ $this->elements = $copy;
+ return $this;
+ }
+
+ /**
+ * Remove an element from the menu
+ */
+ public function remove($target_id) {
+ unset($this->elements[$target_id]);
+ }
+
+ /**
+ * Retrieve a Menu_Element by id
+ */
+ public function &get($id) {
+ if (array_key_exists($id, $this->elements)) {
+ return $this->elements[$id];
+ }
+
+ $null = null;
+ return $null;
+ }
+
+ public function is_empty() {
+ foreach ($this->elements as $element) {
+ if ($element instanceof Menu) {
+ if (!$element->is_empty()) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public function render() {
+ $view = new View(isset($this->view) ? $this->view : "menu.html");
+ $view->menu = $this;
+ return $view;
+ }
+
+ static function title_comparator($a, $b) {
+ return strnatcasecmp((string)$a->label, (string)$b->label);
+ }
+}
diff --git a/modules/gallery/libraries/ORM_MPTT.php b/modules/gallery/libraries/ORM_MPTT.php
new file mode 100644
index 0000000..0ad8133
--- /dev/null
+++ b/modules/gallery/libraries/ORM_MPTT.php
@@ -0,0 +1,341 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2013 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+/**
+ * Implement Modified Preorder Tree Traversal on top of ORM.
+ *
+ * MPTT is an efficient way to store and retrieve hierarchical data in a single database table.
+ * For a good description, read http://www.sitepoint.com/article/hierarchical-data-database/3/
+ *
+ * This code was heavily influenced by code from:
+ * - http://code.google.com/p/kohana-mptt/
+ * - http://code.google.com/p/kohana-mptt/wiki/Documentation
+ * - http://code.google.com/p/s7ncms/source/browse/trunk/modules/s7ncms/libraries/ORM_MPTT.php
+ *
+ * Unfortunately that code was not ready for production and I did not want to absorb their code
+ * and licensing issues so I've reimplemented just the features that we need.
+ */
+class ORM_MPTT_Core extends ORM {
+ private $model_name = null;
+
+ function __construct($id=null) {
+ parent::__construct($id);
+ $this->model_name = inflector::singular($this->table_name);
+ }
+
+ /**
+ * Overload ORM::save() to update the MPTT tree when we add new items to the hierarchy.
+ *
+ * @chainable
+ * @return ORM
+ */
+ function save() {
+ if (!$this->loaded()) {
+ $this->lock();
+ $parent = ORM::factory("item", $this->parent_id);
+
+ try {
+ // Make a hole in the parent for this new item
+ db::build()
+ ->update($this->table_name)
+ ->set("left_ptr", db::expr("`left_ptr` + 2"))
+ ->where("left_ptr", ">=", $parent->right_ptr)
+ ->execute();
+ db::build()
+ ->update($this->table_name)
+ ->set("right_ptr", db::expr("`right_ptr` + 2"))
+ ->where("right_ptr", ">=", $parent->right_ptr)
+ ->execute();
+ $parent->right_ptr += 2;
+
+ // Insert this item into the hole
+ $this->left_ptr = $parent->right_ptr - 2;
+ $this->right_ptr = $parent->right_ptr - 1;
+ $this->parent_id = $parent->id;
+ $this->level = $parent->level + 1;
+ } catch (Exception $e) {
+ $this->unlock();
+ throw $e;
+ }
+ parent::save();
+ $this->unlock();
+ } else {
+ parent::save();
+ }
+
+ return $this;
+ }
+
+ /**
+ * Delete this node and all of its children.
+ */
+ public function delete($ignored_id=null) {
+ $children = $this->children();
+ if ($children) {
+ foreach ($this->children() as $item) {
+ // Deleting children affects the MPTT tree, so we have to reload each child before we
+ // delete it so that we have current left_ptr/right_ptr pointers. This is inefficient.
+ // @todo load each child once, not twice.
+ set_time_limit(30);
+ $item->reload()->delete();
+ }
+
+ // Deleting children has affected this item, but we'll reload it below.
+ }
+
+ $this->lock();
+ $this->reload(); // Assume that the prior lock holder may have changed this entry
+ if (!$this->loaded()) {
+ // Concurrent deletes may result in this item already being gone. Ignore it.
+ return;
+ }
+
+ try {
+ db::build()
+ ->update($this->table_name)
+ ->set("left_ptr", db::expr("`left_ptr` - 2"))
+ ->where("left_ptr", ">", $this->right_ptr)
+ ->execute();
+ db::build()
+ ->update($this->table_name)
+ ->set("right_ptr", db::expr("`right_ptr` - 2"))
+ ->where("right_ptr", ">", $this->right_ptr)
+ ->execute();
+ } catch (Exception $e) {
+ $this->unlock();
+ throw $e;
+ }
+
+ $this->unlock();
+ parent::delete();
+ }
+
+ /**
+ * Return true if the target is descendant of this item.
+ * @param ORM $target
+ * @return boolean
+ */
+ function contains($target) {
+ return ($this->left_ptr <= $target->left_ptr && $this->right_ptr >= $target->right_ptr);
+ }
+
+ /**
+ * Return the parent of this node
+ *
+ * @return ORM
+ */
+ function parent() {
+ if (!$this->parent_id) {
+ return null;
+ }
+ return model_cache::get($this->model_name, $this->parent_id);
+ }
+
+ /**
+ * Return all the parents of this node, in order from root to this node's immediate parent.
+ *
+ * @return array ORM
+ */
+ function parents($where=null) {
+ return $this
+ ->merge_where($where)
+ ->where("left_ptr", "<=", $this->left_ptr)
+ ->where("right_ptr", ">=", $this->right_ptr)
+ ->where("id", "<>", $this->id)
+ ->order_by("left_ptr", "ASC")
+ ->find_all();
+ }
+
+ /**
+ * Return all of the children of this node, ordered by id.
+ *
+ * @chainable
+ * @param integer SQL limit
+ * @param integer SQL offset
+ * @param array additional where clauses
+ * @param array order_by
+ * @return array ORM
+ */
+ function children($limit=null, $offset=null, $where=null, $order_by=array("id" => "ASC")) {
+ return $this
+ ->merge_where($where)
+ ->where("parent_id", "=", $this->id)
+ ->order_by($order_by)
+ ->find_all($limit, $offset);
+ }
+
+ /**
+ * Return the number of children of this node.
+ *
+ * @chainable
+ * @param array additional where clauses
+ * @return array ORM
+ */
+ function children_count($where=null) {
+ return $this
+ ->merge_where($where)
+ ->where("parent_id", "=", $this->id)
+ ->count_all();
+ }
+
+ /**
+ * Return all of the decendents of the specified type, ordered by id.
+ *
+ * @param integer SQL limit
+ * @param integer SQL offset
+ * @param array additional where clauses
+ * @param array order_by
+ * @return object ORM_Iterator
+ */
+ function descendants($limit=null, $offset=null, $where=null, $order_by=array("id" => "ASC")) {
+ return $this
+ ->merge_where($where)
+ ->where("left_ptr", ">", $this->left_ptr)
+ ->where("right_ptr", "<=", $this->right_ptr)
+ ->order_by($order_by)
+ ->find_all($limit, $offset);
+ }
+
+ /**
+ * Return the count of all the children of the specified type.
+ *
+ * @param array additional where clauses
+ * @return integer child count
+ */
+ function descendants_count($where=null) {
+ return $this
+ ->merge_where($where)
+ ->where("left_ptr", ">", $this->left_ptr)
+ ->where("right_ptr", "<=", $this->right_ptr)
+ ->count_all();
+ }
+
+ /**
+ * Move this item to the specified target.
+ *
+ * @chainable
+ * @param Item_Model $target Target node
+ * @return ORM_MTPP
+ */
+ protected function move_to($target) {
+ if ($this->contains($target)) {
+ throw new Exception("@todo INVALID_TARGET can't move item inside itself");
+ }
+
+ $this->lock();
+ $this->reload(); // Assume that the prior lock holder may have changed this entry
+ $target->reload();
+
+ $number_to_move = (int)(($this->right_ptr - $this->left_ptr) / 2 + 1);
+ $size_of_hole = $number_to_move * 2;
+ $original_left_ptr = $this->left_ptr;
+ $original_right_ptr = $this->right_ptr;
+ $target_right_ptr = $target->right_ptr;
+ $level_delta = ($target->level + 1) - $this->level;
+
+ try {
+ if ($level_delta) {
+ // Update the levels for the to-be-moved items
+ db::build()
+ ->update($this->table_name)
+ ->set("level", db::expr("`level` + $level_delta"))
+ ->where("left_ptr", ">=", $original_left_ptr)
+ ->where("right_ptr", "<=", $original_right_ptr)
+ ->execute();
+ }
+
+ // Make a hole in the target for the move
+ db::build()
+ ->update($this->table_name)
+ ->set("left_ptr", db::expr("`left_ptr` + $size_of_hole"))
+ ->where("left_ptr", ">=", $target_right_ptr)
+ ->execute();
+ db::build()
+ ->update($this->table_name)
+ ->set("right_ptr", db::expr("`right_ptr` + $size_of_hole"))
+ ->where("right_ptr", ">=", $target_right_ptr)
+ ->execute();
+
+ // Change the parent.
+ db::build()
+ ->update($this->table_name)
+ ->set("parent_id", $target->id)
+ ->where("id", "=", $this->id)
+ ->execute();
+
+ // If the source is to the right of the target then we just adjusted its left_ptr and
+ // right_ptr above.
+ $left_ptr = $original_left_ptr;
+ $right_ptr = $original_right_ptr;
+ if ($original_left_ptr > $target_right_ptr) {
+ $left_ptr += $size_of_hole;
+ $right_ptr += $size_of_hole;
+ }
+
+ $new_offset = $target->right_ptr - $left_ptr;
+ db::build()
+ ->update($this->table_name)
+ ->set("left_ptr", db::expr("`left_ptr` + $new_offset"))
+ ->set("right_ptr", db::expr("`right_ptr` + $new_offset"))
+ ->where("left_ptr", ">=", $left_ptr)
+ ->where("right_ptr", "<=", $right_ptr)
+ ->execute();
+
+ // Close the hole in the source's parent after the move
+ db::build()
+ ->update($this->table_name)
+ ->set("left_ptr", db::expr("`left_ptr` - $size_of_hole"))
+ ->where("left_ptr", ">", $right_ptr)
+ ->execute();
+ db::build()
+ ->update($this->table_name)
+ ->set("right_ptr", db::expr("`right_ptr` - $size_of_hole"))
+ ->where("right_ptr", ">", $right_ptr)
+ ->execute();
+ } catch (Exception $e) {
+ $this->unlock();
+ throw $e;
+ }
+
+ $this->unlock();
+
+ // Lets reload to get the changes.
+ $this->reload();
+ $target->reload();
+ return $this;
+ }
+
+ /**
+ * Lock the tree to prevent concurrent modification.
+ */
+ protected function lock() {
+ $timeout = module::get_var("gallery", "lock_timeout", 1);
+ $result = $this->db->query("SELECT GET_LOCK('{$this->table_name}', $timeout) AS l")->current();
+ if (empty($result->l)) {
+ throw new Exception("@todo UNABLE_TO_LOCK_EXCEPTION");
+ }
+ }
+
+ /**
+ * Unlock the tree.
+ */
+ protected function unlock() {
+ $this->db->query("SELECT RELEASE_LOCK('{$this->table_name}')");
+ }
+}
diff --git a/modules/gallery/libraries/SafeString.php b/modules/gallery/libraries/SafeString.php
new file mode 100644
index 0000000..179cbd4
--- /dev/null
+++ b/modules/gallery/libraries/SafeString.php
@@ -0,0 +1,162 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2013 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * Safe string representation (regarding security - cross site scripting).
+ */
+class SafeString_Core {
+ private $_raw_string;
+ protected $_is_safe_html = false;
+
+ /** Constructor */
+ function __construct($string) {
+ if ($string instanceof SafeString) {
+ $this->_is_safe_html = $string->_is_safe_html;
+ $string = $string->unescaped();
+ }
+ $this->_raw_string = (string) $string;
+ }
+
+ /**
+ * Factory method returning a new SafeString instance for the given string.
+ */
+ static function of($string) {
+ return new SafeString($string);
+ }
+
+ /**
+ * Factory method returning a new SafeString instance after HTML purifying
+ * the given string.
+ */
+ static function purify($string) {
+ if ($string instanceof SafeString) {
+ if ($string->_is_safe_html) {
+ return $string;
+ } else {
+ $string = $string->unescaped();
+ }
+ }
+ $safe_string = self::of_safe_html(self::_purify_for_html($string));
+ return $safe_string;
+ }
+
+ /**
+ * Factory method returning a new SafeString instance which won't HTML escape.
+ */
+ static function of_safe_html($string) {
+ $safe_string = new SafeString($string);
+ $safe_string->_is_safe_html = true;
+ return $safe_string;
+ }
+
+ /**
+ * Safe for use in HTML.
+ * @see #for_html()
+ */
+ function __toString() {
+ if ($this->_is_safe_html) {
+ return $this->_raw_string;
+ } else {
+ return self::_escape_for_html($this->_raw_string);
+ }
+ }
+
+ /**
+ * Safe for use in HTML.
+ *
+ * Example:<pre>
+ * <div><?= $php_var ?>
+ * </pre>
+ * @return the string escaped for use in HTML.
+ */
+ function for_html() {
+ return $this;
+ }
+
+ /**
+ * Safe for use as JavaScript string.
+ *
+ * Example:<pre>
+ * <script type="text/javascript>"
+ * var some_js_var = <?= $php_var->for_js() ?>;
+ * </script>
+ * </pre>
+ * @return the string escaped for use in JavaScript.
+ */
+ function for_js() {
+ return json_encode((string) $this->_raw_string);
+ }
+
+ /**
+ * Safe for use in HTML element attributes.
+ *
+ * Assumes that the HTML element attribute is already
+ * delimited by single or double quotes
+ *
+ * Example:<pre>
+ * <a title="<?= $php_var->for_html_attr() ?>">;
+ * </script>
+ * </pre>
+ * @return the string escaped for use in HTML attributes.
+ */
+ function for_html_attr() {
+ $string = (string) $this->for_html();
+ return strtr($string,
+ array("'"=>"&#039;",
+ '"'=>'&quot;'));
+ }
+
+ /**
+ * Safe for use HTML (purified HTML)
+ *
+ * Example:<pre>
+ * <div><?= $php_var->purified_html() ?>
+ * </pre>
+ * @return the string escaped for use in HTML.
+ */
+ function purified_html() {
+ return self::purify($this);
+ }
+
+ /**
+ * Returns the raw, unsafe string. Do not use lightly.
+ */
+ function unescaped() {
+ return $this->_raw_string;
+ }
+
+ /**
+ * Escape special HTML chars ("<", ">", "&", etc.) to HTML entities.
+ */
+ private static function _escape_for_html($dirty_html) {
+ return html::chars($dirty_html);
+ }
+
+ /**
+ * Purify the string, removing any potentially malicious or unsafe HTML / JavaScript.
+ */
+ private static function _purify_for_html($dirty_html) {
+ if (class_exists("purifier") && method_exists("purifier", "purify")) {
+ return purifier::purify($dirty_html);
+ } else {
+ return self::_escape_for_html($dirty_html);
+ }
+ }
+}
diff --git a/modules/gallery/libraries/Sendmail.php b/modules/gallery/libraries/Sendmail.php
new file mode 100644
index 0000000..69a7c32
--- /dev/null
+++ b/modules/gallery/libraries/Sendmail.php
@@ -0,0 +1,98 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2013 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+class Sendmail_Core {
+ protected $to;
+ protected $subject;
+ protected $message;
+ protected $headers;
+ protected $line_length = 70;
+ protected $header_separator = "\r\n";
+
+ /**
+ * Return an instance of Sendmail
+ * @chainable
+ */
+ static function factory() {
+ return new Sendmail();
+ }
+
+ public function __construct() {
+ $this->headers = array();
+ $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" : unserialize($separator));
+ }
+
+ public function __get($key) {
+ return null;
+ }
+
+ public function __call($key, $value) {
+ switch ($key) {
+ case "to":
+ $this->to = is_array($value[0]) ? $value[0] : array($value[0]);
+ break;
+ case "header":
+ if (count($value) != 2) {
+ Kohana_Log::add("error", wordwrap("Invalid header parameters\n" . Kohana::debug($value)));
+ throw new Exception("@todo INVALID_HEADER_PARAMETERS");
+ }
+ $this->headers[$value[0]] = $value[1];
+ break;
+ case "from":
+ $this->headers["From"] = $value[0];
+ break;
+ case "reply_to":
+ $this->headers["Reply-To"] = $value[0];
+ break;
+ default:
+ $this->$key = $value[0];
+ }
+ return $this;
+ }
+
+ public function send() {
+ if (empty($this->to)) {
+ Kohana_Log::add("error", wordwrap("Sending mail failed:\nNo to address specified"));
+ throw new Exception("@todo TO_IS_REQUIRED_FOR_MAIL");
+ }
+ $to = implode(", ", $this->to);
+ $headers = array();
+ foreach ($this->headers as $key => $value) {
+ $key = ucfirst($key);
+ $headers[] = "$key: $value";
+ }
+
+ // The docs say headers should be separated by \r\n, but occasionaly that doesn't work and you
+ // need to use a single \n. This can be set in config/sendmail.php
+ $headers = implode($this->header_separator, $headers);
+ $message = wordwrap($this->message, $this->line_length, "\n");
+ if (!$this->mail($to, $this->subject, $message, $headers)) {
+ throw new Exception("@todo SEND_MAIL_FAILED");
+ }
+ return $this;
+ }
+
+ public function mail($to, $subject, $message, $headers) {
+ return mail($to, $subject, $message, $headers);
+ }
+}
diff --git a/modules/gallery/libraries/Task_Definition.php b/modules/gallery/libraries/Task_Definition.php
new file mode 100644
index 0000000..f695fe3
--- /dev/null
+++ b/modules/gallery/libraries/Task_Definition.php
@@ -0,0 +1,50 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2013 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+class Task_Definition_Core {
+ public $callback;
+ public $description;
+ public $name;
+ public $severity;
+
+ static function factory() {
+ return new Task_Definition();
+ }
+
+ function callback($callback) {
+ $this->callback = $callback;
+ return $this;
+ }
+
+ function description($description) {
+ $this->description = $description;
+ return $this;
+ }
+
+ function name($name) {
+ $this->name = $name;
+ return $this;
+ }
+
+ function severity($severity) {
+ $this->severity = $severity;
+ return $this;
+ }
+}
diff --git a/modules/gallery/libraries/Theme_View.php b/modules/gallery/libraries/Theme_View.php
new file mode 100644
index 0000000..9118375
--- /dev/null
+++ b/modules/gallery/libraries/Theme_View.php
@@ -0,0 +1,271 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2013 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+class Theme_View_Core extends Gallery_View {
+ /**
+ * Attempts to load a view and pre-load view data.
+ *
+ * @throws Kohana_Exception if the requested view cannot be found
+ * @param string $name view name
+ * @param string $page_type page type: collection, item, or other
+ * @param string $page_subtype page sub type: album, photo, tags, etc
+ * @param string $theme_name view name
+ * @return void
+ */
+ public function __construct($name, $page_type, $page_subtype) {
+ parent::__construct($name);
+
+ $this->theme_name = module::get_var("gallery", "active_site_theme");
+ if (identity::active_user()->admin) {
+ $theme_name = Input::instance()->get("theme");
+ if ($theme_name &&
+ file_exists(THEMEPATH . $theme_name) &&
+ strpos(realpath(THEMEPATH . $theme_name), THEMEPATH) == 0) {
+ $this->theme_name = $theme_name;
+ }
+ }
+ $this->item = null;
+ $this->tag = null;
+ $this->set_global(array("theme" => $this,
+ "theme_info" => theme::get_info($this->theme_name),
+ "user" => identity::active_user(),
+ "page_type" => $page_type,
+ "page_subtype" => $page_subtype,
+ "page_title" => null));
+
+ if (module::get_var("gallery", "maintenance_mode", 0)) {
+ if (identity::active_user()->admin) {
+ message::warning(t("This site is currently in maintenance mode. Visit the <a href=\"%maintenance_url\">maintenance page</a>", array("maintenance_url" => url::site("admin/maintenance"))));
+ } else
+ message::warning(t("This site is currently in maintenance mode."));
+ }
+ }
+
+ /**
+ * Proportion of the current thumb_size's to default
+ * @param object Item_Model (optional) check the proportions for this item
+ * @return int
+ */
+ public function thumb_proportion($item=null) {
+ // If the item is an album with children, grab the first item in that album instead. We're
+ // interested in the size of the thumbnails in this album, not the thumbnail of the
+ // album itself.
+ if ($item && $item->is_album() && $item->children_count()) {
+ $item = $item->children(1)->current();
+ }
+
+ // By default we have a globally fixed thumbnail size In core code, we just return a fixed
+ // proportion based on the global thumbnail size, but since modules can override that, we
+ // return the actual proportions when we have them.
+ if ($item && $item->has_thumb()) {
+ return max($item->thumb_width, $item->thumb_height) / 200;
+ } else {
+ // @TODO change the 200 to a theme supplied value when and if we come up with an
+ // API to allow the theme to set defaults.
+ return module::get_var("gallery", "thumb_size", 200) / 200;
+ }
+ }
+
+ public function item() {
+ return $this->item;
+ }
+
+ public function siblings($limit=null, $offset=null) {
+ return call_user_func_array(
+ $this->siblings_callback[0],
+ array_merge($this->siblings_callback[1], array($limit, $offset)));
+ }
+
+ public function tag() {
+ return $this->tag;
+ }
+
+ public function page_type() {
+ return $this->page_type;
+ }
+
+ public function page_subtype() {
+ return $this->page_subtype;
+ }
+
+ public function user_menu() {
+ $menu = Menu::factory("root")
+ ->css_id("g-login-menu")
+ ->css_class("g-inline ui-helper-clear-fix");
+ module::event("user_menu", $menu, $this);
+ return $menu->render();
+ }
+
+ public function site_menu($item_css_selector) {
+ $menu = Menu::factory("root");
+ module::event("site_menu", $menu, $this, $item_css_selector);
+ return $menu->render();
+ }
+
+ public function album_menu() {
+ $menu = Menu::factory("root");
+ module::event("album_menu", $menu, $this);
+ return $menu->render();
+ }
+
+ public function tag_menu() {
+ $menu = Menu::factory("root");
+ module::event("tag_menu", $menu, $this);
+ return $menu->render();
+ }
+
+ public function photo_menu() {
+ $menu = Menu::factory("root");
+ if (access::can("view_full", $this->item())) {
+ $menu->append(Menu::factory("link")
+ ->id("fullsize")
+ ->label(t("View full size"))
+ ->url($this->item()->file_url())
+ ->css_class("g-fullsize-link"));
+ }
+
+ module::event("photo_menu", $menu, $this);
+ return $menu->render();
+ }
+
+ public function movie_menu() {
+ $menu = Menu::factory("root");
+ module::event("movie_menu", $menu, $this);
+ return $menu->render();
+ }
+
+ public function context_menu($item, $thumbnail_css_selector) {
+ $menu = Menu::factory("root")
+ ->append(Menu::factory("submenu")
+ ->id("context_menu")
+ ->label(t("Options")))
+ ->css_class("g-context-menu");
+
+ module::event("context_menu", $menu, $this, $item, $thumbnail_css_selector);
+ return $menu->render();
+ }
+
+ /**
+ * Print out any site wide status information.
+ */
+ public function site_status() {
+ return site_status::get();
+ }
+
+ /**
+ * Print out any messages waiting for this user.
+ */
+ public function messages() {
+ return message::get();
+ }
+
+ /**
+ * Print out the sidebar.
+ */
+ public function sidebar_blocks() {
+ $sidebar = block_manager::get_html("site_sidebar", $this);
+ if (empty($sidebar) && identity::active_user()->admin) {
+ $sidebar = new View("no_sidebar.html");
+ }
+ return $sidebar;
+ }
+
+ /**
+ * Handle all theme functions that insert module content.
+ */
+ public function __call($function, $args) {
+ switch ($function) {
+ case "album_blocks":
+ case "album_bottom":
+ case "album_top":
+ case "body_attributes":
+ case "credits";
+ case "dynamic_bottom":
+ case "dynamic_top":
+ case "footer":
+ case "head":
+ case "header_bottom":
+ case "header_top":
+ case "html_attributes":
+ case "page_bottom":
+ case "page_top":
+ case "photo_blocks":
+ case "photo_bottom":
+ case "photo_top":
+ case "resize_bottom":
+ case "resize_top":
+ case "sidebar_bottom":
+ case "sidebar_top":
+ case "thumb_bottom":
+ case "thumb_info":
+ case "thumb_top":
+ $blocks = array();
+ if (method_exists("gallery_theme", $function)) {
+ switch (count($args)) {
+ case 0:
+ $blocks[] = gallery_theme::$function($this);
+ break;
+ case 1:
+ $blocks[] = gallery_theme::$function($this, $args[0]);
+ break;
+ case 2:
+ $blocks[] = gallery_theme::$function($this, $args[0], $args[1]);
+ break;
+ default:
+ $blocks[] = call_user_func_array(
+ array("gallery_theme", $function),
+ array_merge(array($this), $args));
+ }
+ }
+
+ foreach (module::active() as $module) {
+ if ($module->name == "gallery") {
+ continue;
+ }
+ $helper_class = "{$module->name}_theme";
+ if (class_exists($helper_class) && method_exists($helper_class, $function)) {
+ $blocks[] = call_user_func_array(
+ array($helper_class, $function),
+ array_merge(array($this), $args));
+ }
+ }
+
+ $helper_class = theme::$site_theme_name . "_theme";
+ if (class_exists($helper_class) && method_exists($helper_class, $function)) {
+ $blocks[] = call_user_func_array(
+ array($helper_class, $function),
+ array_merge(array($this), $args));
+ }
+
+ if (Session::instance()->get("debug")) {
+ if ($function != "head" && $function != "body_attributes") {
+ array_unshift(
+ $blocks,
+ "<div class=\"g-annotated-theme-block g-annotated-theme-block_$function g-clear-fix\">" .
+ "<div class=\"title\">$function</div>");
+ $blocks[] = "</div>";
+ }
+ }
+ return implode("\n", $blocks);
+
+ default:
+ throw new Exception("@todo UNKNOWN_THEME_FUNCTION: $function");
+ }
+ }
+} \ No newline at end of file
diff --git a/modules/gallery/libraries/drivers/Cache/Database.php b/modules/gallery/libraries/drivers/Cache/Database.php
new file mode 100644
index 0000000..8790d0e
--- /dev/null
+++ b/modules/gallery/libraries/drivers/Cache/Database.php
@@ -0,0 +1,166 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2013 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+/*
+ * Based on the Cache_Sqlite_Driver developed by the Kohana Team
+ */
+class Cache_Database_Driver extends Cache_Driver {
+ // Kohana database instance
+ protected $db;
+
+ /**
+ * Sets a cache item to the given data, tags, and lifetime.
+ *
+ * @param array assoc array of key => value pairs
+ * @param array cache tags
+ * @param integer lifetime
+ * @return bool
+ */
+ public function set($items, $tags=null, $lifetime=null) {
+ if (!empty($tags)) {
+ // Escape the tags, adding brackets so the tag can be explicitly matched
+ $tags = "<" . implode(">,<", $tags) . ">";
+ } else {
+ $tags = null;
+ }
+
+ // Cache Database driver expects unix timestamp
+ if ($lifetime !== 0) {
+ $lifetime += time();
+ }
+
+ $db = Database::instance();
+ $tags = $db->escape($tags);
+ foreach ($items as $id => $data) {
+ $id = $db->escape($id);
+ $data = $db->escape(serialize($data));
+ $db->query("INSERT INTO {caches} (`key`, `tags`, `expiration`, `cache`)
+ VALUES ('$id', '$tags', $lifetime, '$data')
+ ON DUPLICATE KEY UPDATE `tags` = VALUES(tags), `expiration` = VALUES(expiration),
+ `cache` = VALUES(cache)");
+ }
+
+ return true;
+ }
+
+ /**
+ * Get cache items by tag
+ * @param array cache tags
+ * @return array cached data
+ */
+ public function get_tag($tags) {
+ $db = db::build()
+ ->select()
+ ->from("caches");
+ foreach ($tags as $tag) {
+ $db->where("tags", "LIKE", "%" . Database::escape_for_like("<$tag>") . "%");
+ }
+ $db_result = $db->execute();
+
+ // An array will always be returned
+ $result = array();
+
+ // Disable notices for unserializing
+ $ER = error_reporting(~E_NOTICE);
+ if ($db_result->count() > 0) {
+ foreach ($db_result as $row) {
+ // Add each cache to the array
+ $result[$row->key] = unserialize($row->cache);
+ }
+ }
+ error_reporting($ER);
+
+ return $result;
+ }
+
+ /**
+ * Fetches a cache item. This will delete the item if it is expired or if
+ * the hash does not match the stored hash.
+ *
+ * @param string cache id
+ * @return mixed|NULL
+ */
+ public function get($keys, $single=false) {
+ $data = null;
+ $result = db::build()
+ ->select()
+ ->from("caches")
+ ->where("key", "IN", $keys)
+ ->execute();
+
+ if (count($result) > 0) {
+ $cache = $result->current();
+ // Make sure the expiration is valid and that the hash matches
+ if ($cache->expiration != 0 && $cache->expiration <= time()) {
+ // Cache is not valid, delete it now
+ $this->delete(array($cache->id));
+ } else {
+ // Disable notices for unserializing
+ $ER = error_reporting(~E_NOTICE);
+
+ // Return the valid cache data
+ $data = unserialize($cache->cache);
+
+ // Turn notices back on
+ error_reporting($ER);
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * Deletes a cache item by id or tag
+ *
+ * @param string cache id or tag, or true for "all items"
+ * @param bool delete a tag
+ * @return bool
+ */
+ public function delete($keys, $is_tag=false) {
+ $db = db::build()
+ ->delete("caches");
+ if ($keys === true) {
+ // Delete all caches
+ } else if ($is_tag === true) {
+ foreach ($keys as $tag) {
+ $db->where("tags", "LIKE", "%" . Database::escape_for_like("<$tag>") . "%");
+ }
+ } else {
+ $db->where("key", "IN", $keys);
+ }
+
+ $status = $db->execute();
+
+ return count($status) > 0;
+ }
+
+ /**
+ * Delete cache items by tag
+ */
+ public function delete_tag($tags) {
+ return $this->delete($tags, true);
+ }
+
+ /**
+ * Empty the cache
+ */
+ public function delete_all() {
+ Database::instance()->query("TRUNCATE {caches}");
+ }
+} \ No newline at end of file
diff --git a/modules/gallery/libraries/drivers/IdentityProvider.php b/modules/gallery/libraries/drivers/IdentityProvider.php
new file mode 100644
index 0000000..5256236
--- /dev/null
+++ b/modules/gallery/libraries/drivers/IdentityProvider.php
@@ -0,0 +1,134 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2013 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+interface IdentityProvider_Driver {
+ /**
+ * Return the guest user.
+ *
+ * @return User_Definition the user object
+ */
+ public function guest();
+
+ /**
+ * Return the primary admin user.
+ *
+ * @return User_Definition the user object
+ */
+ public function admin_user();
+
+ /**
+ * Create a new user.
+ *
+ * @param string $name
+ * @param string $full_name
+ * @param string $password
+ * @param string $email
+ * @return User_Definition the user object
+ */
+ public function create_user($name, $full_name, $password, $email);
+
+ /**
+ * Is the password provided correct?
+ *
+ * @param user User_Definition the user object
+ * @param string $password a plaintext password
+ * @return boolean true if the password is correct
+ */
+ public function is_correct_password($user, $password);
+
+ /**
+ * Look up a user by id.
+ * @param integer $id
+ * @return User_Definition the user object, or null if the name was invalid.
+ */
+ public function lookup_user($id);
+
+ /**
+ * Look up a user by name.
+ * @param string $name
+ * @return User_Definition the user object, or null if the name was invalid.
+ */
+ public function lookup_user_by_name($name);
+
+ /**
+ * Create a new group.
+ *
+ * @param string $name
+ * @return Group_Definition the group object
+ */
+ public function create_group($name);
+
+ /**
+ * The group of all possible visitors. This includes the guest user.
+ *
+ * @return Group_Definition the group object
+ */
+ public function everybody();
+
+ /**
+ * The group of all logged-in visitors. This does not include guest users.
+ *
+ * @return Group_Definition the group object
+ */
+ public function registered_users();
+
+ /**
+ * List the users
+ * @param array $ids array of ids to return the user objects for
+ * @return array the user list.
+ */
+ public function get_user_list($ids);
+
+ /**
+ * Look up a group by id.
+ * @param integer $id id
+ * @return Group_Definition the user object, or null if the name was invalid.
+ */
+ public function lookup_group($id);
+
+ /**
+ * Look up the group by name.
+ * @param string $name the name of the group to locate
+ * @return Group_Definition
+ */
+ public function lookup_group_by_name($name);
+
+ /**
+ * List the groups defined in the Identity Provider
+ */
+ public function groups();
+
+ /**
+ * Add the user to the specified group
+ * @param User_Definition the user to add
+ * @param Group_Definition the target group
+ */
+ public function add_user_to_group($user, $group);
+
+ /**
+ * Remove the user to the specified group
+ * @param User_Definition the user to remove
+ * @param Group_Definition the owning group
+ */
+ public function remove_user_from_group($user, $group);
+} // End Identity Driver Definition
+
+interface Group_Definition {}
+
+interface User_Definition {}