diff options
Diffstat (limited to 'modules/gallery/libraries')
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("'"=>"'", + '"'=>'"')); + } + + /** + * 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 {} |
