From 8da16ca4a6faae17c896919c31d0011e719fb28f Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Tue, 28 Sep 2010 08:53:03 +0200 Subject: update php-gettext to 1.0.10 --- src/php-gettext/Makefile | 14 +- src/php-gettext/README | 74 +--- .../examples/locale/sr_CS/LC_MESSAGES/messages.mo | Bin 834 -> 829 bytes .../examples/locale/sr_CS/LC_MESSAGES/messages.po | 3 +- src/php-gettext/examples/pigs_dropin.php | 10 +- src/php-gettext/examples/pigs_fallback.php | 10 +- src/php-gettext/examples/update | 4 +- src/php-gettext/gettext.inc | 478 +++++++++++++++------ src/php-gettext/gettext.php | 179 +++++--- src/php-gettext/streams.php | 32 +- src/php-gettext/tests/LocalesTest.php | 66 +++ src/php-gettext/tests/ParsingTest.php | 43 ++ 12 files changed, 642 insertions(+), 271 deletions(-) create mode 100644 src/php-gettext/tests/LocalesTest.php create mode 100644 src/php-gettext/tests/ParsingTest.php (limited to 'src') diff --git a/src/php-gettext/Makefile b/src/php-gettext/Makefile index 2dba911..a6cce12 100644 --- a/src/php-gettext/Makefile +++ b/src/php-gettext/Makefile @@ -1,12 +1,11 @@ PACKAGE = php-gettext-$(VERSION) -VERSION = 1.0.7 +VERSION = 1.0.10 DIST_FILES = \ gettext.php \ gettext.inc \ streams.php \ AUTHORS \ - ChangeLog \ README \ COPYING \ Makefile \ @@ -17,9 +16,14 @@ DIST_FILES = \ examples/locale/sr_CS/LC_MESSAGES/messages.mo \ examples/locale/de_CH/LC_MESSAGES/messages.po \ examples/locale/de_CH/LC_MESSAGES/messages.mo \ - examples/update + examples/update \ + tests/LocalesTest.php \ + tests/ParsingTest.php -dist: +check: + phpunit --verbose tests + +dist: check if [ -d $(PACKAGE) ]; then \ rm -rf $(PACKAGE); \ fi; \ @@ -30,3 +34,5 @@ dist: rm -rf $(PACKAGE); \ fi; +clean: + rm -f $(PACKAGE).tar.gz diff --git a/src/php-gettext/README b/src/php-gettext/README index c7525e2..bca4f91 100644 --- a/src/php-gettext/README +++ b/src/php-gettext/README @@ -1,9 +1,9 @@ -PHP-gettext 1.0 +PHP-gettext 1.0 (https://launchpad.net/php-gettext) -Copyright 2003, 2006 -- Danilo "angry with PHP[1]" Segan +Copyright 2003, 2006, 2009 -- Danilo "angry with PHP[1]" Segan Licensed under GPLv2 (or any later version, see COPYING) -[1] PHP is actually cyrillic, and translates roughly to +[1] PHP is actually cyrillic, and translates roughly to "works-doesn't-work" (UTF-8: Ради-Не-Ради) @@ -50,36 +50,16 @@ Features file data, I used imaginary abstract class StreamReader to do all the input (check streams.php). For your convenience, I've already provided two classes for reading files: FileReader and - StringReader (CachedFileReader is a combination of the two: it - loads entire file contents into a string, and then works on that). - See example below for usage. You can for instance use StringReader - when you read in data from a database, or you can create your own - derivative of StreamReader for anything you like. - + StringReader (CachedFileReader is a combination of the two: it + loads entire file contents into a string, and then works on that). + See example below for usage. You can for instance use StringReader + when you read in data from a database, or you can create your own + derivative of StreamReader for anything you like. -Bugs - - Plural-forms field in MO header (translation for empty string, - i.e. "") is treated according to PHP syntactic rules (it's - eval()ed). Since these should actually follow C syntax, there are - some problems. - For instance, I'm used to using this: - Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : \ - n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2; - but it fails with PHP (it sets $plural=2 instead of 0 for $n==1). - - The fix is usually simple, but I'm lazy to go into the details of - PHP operator precedence, and maybe try to fix it. In here, I had - to put everything after the first ':' in parenthesis: - Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : \ - (n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); - That works, and I'm satisfied. +Bugs - Besides this one, there are probably a bunch of other bugs, since - I hate PHP (did I mention it already? no? strange), and don't - know it very well. So, feel free to fix any of those and report - them back to me at . + Report them on https://bugs.launchpad.net/php-gettext Usage @@ -94,19 +74,19 @@ Usage Then, use that as a parameter to gettext_reader constructor: $wohoo = new gettext_reader($streamer); - If you want to disable pre-loading of entire message catalog in - memory (if, for example, you have a multi-thousand message catalog - which you'll use only occasionally), use "false" for second + If you want to disable pre-loading of entire message catalog in + memory (if, for example, you have a multi-thousand message catalog + which you'll use only occasionally), use "false" for second parameter to gettext_reader constructor: $wohoo = new gettext_reader($streamer, false); From now on, you have all the benefits of gettext data at your - disposal, so may run: + disposal, so may run: print $wohoo->translate("This is a test"); print $wohoo->ngettext("%d bird", "%d birds", $birds); You might need to pass parameter "-k" to xgettext to make it - extract all the strings. In above example, try with + extract all the strings. In above example, try with xgettext -ktranslate -kngettext:1,2 file.php what should create messages.po which contains two messages for translation. @@ -118,8 +98,8 @@ Usage Usage with gettext.inc (standard gettext interfaces emulation) - Check example in examples/pig_dropin.php, basically you include - gettext.inc and use all the standard gettext interfaces as + Check example in examples/pig_dropin.php, basically you include + gettext.inc and use all the standard gettext interfaces as documented on: http://www.php.net/gettext @@ -137,20 +117,12 @@ Example There is also simple "update" script that can be used to generate POT file and to update the translation using msgmerge. -Interesting TODO: +TODO: - o Try to parse "plural-forms" header field, and to follow C syntax - rules. This won't be easy. + o Improve speed to be even more comparable to the native gettext + implementation. -Boring TODO: - - o Learn PHP and fix bugs, slowness and other stuff resulting from - my lack of knowledge (but *maybe*, it's not my knowledge that is - bad, but PHP itself ;-). - - (This is mostly done thanks to Nico Kaiser.) - - o Try to use hash tables in MO files: with pre-loading, would it + o Try to use hash tables in MO files: with pre-loading, would it be useful at all? Never-asked-questions: @@ -160,7 +132,7 @@ Never-asked-questions: Well, it's quite simple. I consider that the first released thing should be labeled "version 1" (first, right?). Zero is there to - indicate that there's zero improvement and/or change compared to + indicate that there's zero improvement and/or change compared to "version 1". I plan to use version numbers 1.0.* for small bugfixes, and to @@ -173,7 +145,7 @@ Never-asked-questions: Mozart's 40th Symphony (there is one like that, right?). o Can I...? - + Yes, you can. This is free software (as in freedom, free speech), and you might do whatever you wish with it, provided you do not limit freedom of others (GPL). diff --git a/src/php-gettext/examples/locale/sr_CS/LC_MESSAGES/messages.mo b/src/php-gettext/examples/locale/sr_CS/LC_MESSAGES/messages.mo index 6ffccfd..497c883 100644 Binary files a/src/php-gettext/examples/locale/sr_CS/LC_MESSAGES/messages.mo and b/src/php-gettext/examples/locale/sr_CS/LC_MESSAGES/messages.mo differ diff --git a/src/php-gettext/examples/locale/sr_CS/LC_MESSAGES/messages.po b/src/php-gettext/examples/locale/sr_CS/LC_MESSAGES/messages.po index 7e620cc..e5da0e9 100644 --- a/src/php-gettext/examples/locale/sr_CS/LC_MESSAGES/messages.po +++ b/src/php-gettext/examples/locale/sr_CS/LC_MESSAGES/messages.po @@ -12,7 +12,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : (n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" #: pigs.php:19 msgid "" diff --git a/src/php-gettext/examples/pigs_dropin.php b/src/php-gettext/examples/pigs_dropin.php index edd2b0d..94fd850 100644 --- a/src/php-gettext/examples/pigs_dropin.php +++ b/src/php-gettext/examples/pigs_dropin.php @@ -1,6 +1,6 @@ . + Copyright (c) 2003,2004,2005,2009 Danilo Segan . Copyright (c) 2005,2006 Steven Armstrong This file is part of PHP-gettext. @@ -21,10 +21,12 @@ */ +error_reporting(E_ALL | E_STRICT); + // define constants -define(PROJECT_DIR, realpath('./')); -define(LOCALE_DIR, PROJECT_DIR .'/locale'); -define(DEFAULT_LOCALE, 'en_US'); +define('PROJECT_DIR', realpath('./')); +define('LOCALE_DIR', PROJECT_DIR .'/locale'); +define('DEFAULT_LOCALE', 'en_US'); require_once('../gettext.inc'); diff --git a/src/php-gettext/examples/pigs_fallback.php b/src/php-gettext/examples/pigs_fallback.php index b50f752..353190d 100644 --- a/src/php-gettext/examples/pigs_fallback.php +++ b/src/php-gettext/examples/pigs_fallback.php @@ -1,6 +1,6 @@ . + Copyright (c) 2003,2004,2005,2009 Danilo Segan . Copyright (c) 2005,2006 Steven Armstrong This file is part of PHP-gettext. @@ -21,10 +21,12 @@ */ +error_reporting(E_ALL | E_STRICT); + // define constants -define(PROJECT_DIR, realpath('./')); -define(LOCALE_DIR, PROJECT_DIR .'/locale'); -define(DEFAULT_LOCALE, 'en_US'); +define('PROJECT_DIR', realpath('./')); +define('LOCALE_DIR', PROJECT_DIR .'/locale'); +define('DEFAULT_LOCALE', 'en_US'); require_once('../gettext.inc'); diff --git a/src/php-gettext/examples/update b/src/php-gettext/examples/update index c8d8b61..76b4308 100644 --- a/src/php-gettext/examples/update +++ b/src/php-gettext/examples/update @@ -1,7 +1,7 @@ #!/bin/sh TEMPLATE=pigs.pot -xgettext -kT_ngettext:1,2 -kT_ -L PHP -o $TEMPLATE pigs.php -if [ x$1 == 'x-p' ]; then +xgettext -kT_ngettext:1,2 -kT_ -L PHP -o $TEMPLATE pigs_dropin.php +if [ "x$1" = "x-p" ]; then msgfmt --statistics $TEMPLATE else if [ -f $1.po ]; then diff --git a/src/php-gettext/gettext.inc b/src/php-gettext/gettext.inc index a67811f..399a0f2 100644 --- a/src/php-gettext/gettext.inc +++ b/src/php-gettext/gettext.inc @@ -1,9 +1,10 @@ - + Copyright (c) 2009 Danilo Segan + Drop in replacement for native gettext. - + This file is part of PHP-gettext. PHP-gettext is free software; you can redistribute it and/or modify @@ -22,15 +23,22 @@ */ /* -LC_CTYPE 0 -LC_NUMERIC 1 -LC_TIME 2 -LC_COLLATE 3 -LC_MONETARY 4 -LC_MESSAGES 5 -LC_ALL 6 +LC_CTYPE 0 +LC_NUMERIC 1 +LC_TIME 2 +LC_COLLATE 3 +LC_MONETARY 4 +LC_MESSAGES 5 +LC_ALL 6 */ + +// LC_MESSAGES is not available if php-gettext is not loaded +// while the other constants are already available from session extension. +if (!defined('LC_MESSAGES')) { + define('LC_MESSAGES', 5); +} + require('streams.php'); require('gettext.php'); @@ -44,29 +52,96 @@ $LC_CATEGORIES = array('LC_CTYPE', 'LC_NUMERIC', 'LC_TIME', 'LC_COLLATE', 'LC_MO $EMULATEGETTEXT = 0; $CURRENTLOCALE = ''; +/* Class to hold a single domain included in $text_domains. */ +class domain { + var $l10n; + var $path; + var $codeset; +} // Utility functions +/** + * Return a list of locales to try for any POSIX-style locale specification. + */ +function get_list_of_locales($locale) { + /* Figure out all possible locale names and start with the most + * specific ones. I.e. for sr_CS.UTF-8@latin, look through all of + * sr_CS.UTF-8@latin, sr_CS@latin, sr@latin, sr_CS.UTF-8, sr_CS, sr. + */ + $locale_names = array(); + $lang = NULL; + $country = NULL; + $charset = NULL; + $modifier = NULL; + if ($locale) { + if (preg_match("/^(?P[a-z]{2,3})" // language code + ."(?:_(?P[A-Z]{2}))?" // country code + ."(?:\.(?P[-A-Za-z0-9_]+))?" // charset + ."(?:@(?P[-A-Za-z0-9_]+))?$/", // @ modifier + $locale, $matches)) { + + if (isset($matches["lang"])) $lang = $matches["lang"]; + if (isset($matches["country"])) $country = $matches["country"]; + if (isset($matches["charset"])) $charset = $matches["charset"]; + if (isset($matches["modifier"])) $modifier = $matches["modifier"]; + + if ($modifier) { + if ($country) { + if ($charset) + array_push($locale_names, "${lang}_$country.$charset@$modifier"); + array_push($locale_names, "${lang}_$country@$modifier"); + } elseif ($charset) + array_push($locale_names, "${lang}.$charset@$modifier"); + array_push($locale_names, "$lang@$modifier"); + } + if ($country) { + if ($charset) + array_push($locale_names, "${lang}_$country.$charset"); + array_push($locale_names, "${lang}_$country"); + } elseif ($charset) + array_push($locale_names, "${lang}.$charset"); + array_push($locale_names, $lang); + } + + // If the locale name doesn't match POSIX style, just include it as-is. + if (!in_array($locale, $locale_names)) + array_push($locale_names, $locale); + } + return $locale_names; +} + /** * Utility function to get a StreamReader for the given text domain. */ function _get_reader($domain=null, $category=5, $enable_cache=true) { - global $text_domains, $default_domain, $LC_CATEGORIES; - if (!isset($domain)) $domain = $default_domain; - if (!isset($text_domains[$domain]->l10n)) { - // get the current locale - $locale = _setlocale(LC_MESSAGES, 0); - $p = isset($text_domains[$domain]->path) ? $text_domains[$domain]->path : './'; - $path = $p . "$locale/". $LC_CATEGORIES[$category] ."/$domain.mo"; - if (file_exists($path)) { - $input = new FileReader($path); - } - else { - $input = null; - } - $text_domains[$domain]->l10n = new gettext_reader($input, $enable_cache); - } - return $text_domains[$domain]->l10n; + global $text_domains, $default_domain, $LC_CATEGORIES; + if (!isset($domain)) $domain = $default_domain; + if (!isset($text_domains[$domain]->l10n)) { + // get the current locale + $locale = _setlocale(LC_MESSAGES, 0); + $bound_path = isset($text_domains[$domain]->path) ? + $text_domains[$domain]->path : './'; + $subpath = $LC_CATEGORIES[$category] ."/$domain.mo"; + + $locale_names = get_list_of_locales($locale); + $input = null; + foreach ($locale_names as $locale) { + $full_path = $bound_path . $locale . "/" . $subpath; + if (file_exists($full_path)) { + $input = new FileReader($full_path); + break; + } + } + + if (!array_key_exists($domain, $text_domains)) { + // Initialize an empty domain object. + $text_domains[$domain] = new domain(); + } + $text_domains[$domain]->l10n = new gettext_reader($input, + $enable_cache); + } + return $text_domains[$domain]->l10n; } /** @@ -80,8 +155,10 @@ function locale_emulation() { /** * Checks if the current locale is supported on this system. */ -function _check_locale() { +function _check_locale_and_function($function=false) { global $EMULATEGETTEXT; + if ($function and !function_exists($function)) + return false; return !$EMULATEGETTEXT; } @@ -89,56 +166,70 @@ function _check_locale() { * Get the codeset for the given domain. */ function _get_codeset($domain=null) { - global $text_domains, $default_domain, $LC_CATEGORIES; - if (!isset($domain)) $domain = $default_domain; - return (isset($text_domains[$domain]->codeset))? $text_domains[$domain]->codeset : ini_get('mbstring.internal_encoding'); + global $text_domains, $default_domain, $LC_CATEGORIES; + if (!isset($domain)) $domain = $default_domain; + return (isset($text_domains[$domain]->codeset))? $text_domains[$domain]->codeset : ini_get('mbstring.internal_encoding'); } /** * Convert the given string to the encoding set by bind_textdomain_codeset. */ function _encode($text) { - $source_encoding = mb_detect_encoding($text); - $target_encoding = _get_codeset(); - if ($source_encoding != $target_encoding) { - return mb_convert_encoding($text, $target_encoding, $source_encoding); - } - else { - return $text; - } + $source_encoding = mb_detect_encoding($text); + $target_encoding = _get_codeset(); + if ($source_encoding != $target_encoding) { + return mb_convert_encoding($text, $target_encoding, $source_encoding); + } + else { + return $text; + } } - - // Custom implementation of the standard gettext related functions +/** + * Returns passed in $locale, or environment variable $LANG if $locale == ''. + */ +function _get_default_locale($locale) { + if ($locale == '') // emulate variable support + return getenv('LANG'); + else + return $locale; +} + /** * Sets a requested locale, if needed emulates it. */ function _setlocale($category, $locale) { global $CURRENTLOCALE, $EMULATEGETTEXT; if ($locale === 0) { // use === to differentiate between string "0" - if ($CURRENTLOCALE != '') + if ($CURRENTLOCALE != '') return $CURRENTLOCALE; - else + else // obey LANG variable, maybe extend to support all of LC_* vars // even if we tried to read locale without setting it first return _setlocale($category, $CURRENTLOCALE); } else { - $ret = 0; - if (function_exists('setlocale')) // I don't know if this ever happens ;) - $ret = @setlocale($category, $locale); //the @ hides warning messages on few installations - if (($ret and $locale == '') or ($ret == $locale)) { - $EMULATEGETTEXT = 0; + if (function_exists('setlocale')) { + $ret = setlocale($category, $locale); + if (($locale == '' and !$ret) or // failed setting it by env + ($locale != '' and $ret != $locale)) { // failed setting it + // Failed setting it according to environment. + $CURRENTLOCALE = _get_default_locale($locale); + $EMULATEGETTEXT = 1; + } else { $CURRENTLOCALE = $ret; + $EMULATEGETTEXT = 0; + } } else { - if ($locale == '') // emulate variable support - $CURRENTLOCALE = getenv('LANG'); - else - $CURRENTLOCALE = $locale; - $EMULATEGETTEXT = 1; + // No function setlocale(), emulate it all. + $CURRENTLOCALE = _get_default_locale($locale); + $EMULATEGETTEXT = 1; } + // Allow locale to be changed on the go for one translation domain. + global $text_domains, $default_domain; + unset($text_domains[$default_domain]->l10n); return $CURRENTLOCALE; } } @@ -147,135 +238,240 @@ function _setlocale($category, $locale) { * Sets the path for a domain. */ function _bindtextdomain($domain, $path) { - global $text_domains; - // ensure $path ends with a slash - if ($path[strlen($path) - 1] != '/') $path .= '/'; - elseif ($path[strlen($path) - 1] != '\\') $path .= '\\'; - $text_domains[$domain]->path = $path; + global $text_domains; + // ensure $path ends with a slash ('/' should work for both, but lets still play nice) + if (substr(php_uname(), 0, 7) == "Windows") { + if ($path[strlen($path)-1] != '\\' and $path[strlen($path)-1] != '/') + $path .= '\\'; + } else { + if ($path[strlen($path)-1] != '/') + $path .= '/'; + } + if (!array_key_exists($domain, $text_domains)) { + // Initialize an empty domain object. + $text_domains[$domain] = new domain(); + } + $text_domains[$domain]->path = $path; } /** * Specify the character encoding in which the messages from the DOMAIN message catalog will be returned. */ function _bind_textdomain_codeset($domain, $codeset) { - global $text_domains; - $text_domains[$domain]->codeset = $codeset; + global $text_domains; + $text_domains[$domain]->codeset = $codeset; } /** * Sets the default domain. */ function _textdomain($domain) { - global $default_domain; - $default_domain = $domain; + global $default_domain; + $default_domain = $domain; } /** * Lookup a message in the current domain. */ function _gettext($msgid) { - $l10n = _get_reader(); - //return $l10n->translate($msgid); - return _encode($l10n->translate($msgid)); + $l10n = _get_reader(); + return _encode($l10n->translate($msgid)); } + /** * Alias for gettext. */ function __($msgid) { - return _gettext($msgid); + return _gettext($msgid); } + /** * Plural version of gettext. */ function _ngettext($single, $plural, $number) { - $l10n = _get_reader(); - //return $l10n->ngettext($single, $plural, $number); - return _encode($l10n->ngettext($single, $plural, $number)); + $l10n = _get_reader(); + return _encode($l10n->ngettext($single, $plural, $number)); } /** * Override the current domain. */ function _dgettext($domain, $msgid) { - $l10n = _get_reader($domain); - //return $l10n->translate($msgid); - return _encode($l10n->translate($msgid)); + $l10n = _get_reader($domain); + return _encode($l10n->translate($msgid)); } + /** * Plural version of dgettext. */ function _dngettext($domain, $single, $plural, $number) { - $l10n = _get_reader($domain); - //return $l10n->ngettext($single, $plural, $number); - return _encode($l10n->ngettext($single, $plural, $number)); + $l10n = _get_reader($domain); + return _encode($l10n->ngettext($single, $plural, $number)); } /** * Overrides the domain and category for a single lookup. */ function _dcgettext($domain, $msgid, $category) { - $l10n = _get_reader($domain, $category); - //return $l10n->translate($msgid); - return _encode($l10n->translate($msgid)); + $l10n = _get_reader($domain, $category); + return _encode($l10n->translate($msgid)); } /** * Plural version of dcgettext. */ function _dcngettext($domain, $single, $plural, $number, $category) { - $l10n = _get_reader($domain, $category); - //return $l10n->ngettext($single, $plural, $number); - return _encode($l10n->ngettext($single, $plural, $number)); + $l10n = _get_reader($domain, $category); + return _encode($l10n->ngettext($single, $plural, $number)); +} + +/** + * Context version of gettext. + */ +function _pgettext($context, $msgid) { + $l10n = _get_reader(); + return _encode($l10n->pgettext($context, $msgid)); +} + +/** + * Override the current domain in a context gettext call. + */ +function _dpgettext($domain, $context, $msgid) { + $l10n = _get_reader($domain); + return _encode($l10n->pgettext($context, $msgid)); +} + +/** + * Overrides the domain and category for a single context-based lookup. + */ +function _dcpgettext($domain, $context, $msgid, $category) { + $l10n = _get_reader($domain, $category); + return _encode($l10n->pgettext($context, $msgid)); +} + +/** + * Context version of ngettext. + */ +function _npgettext($context, $singular, $plural) { + $l10n = _get_reader(); + return _encode($l10n->npgettext($context, $singular, $plural)); +} + +/** + * Override the current domain in a context ngettext call. + */ +function _dnpgettext($domain, $context, $singular, $plural) { + $l10n = _get_reader($domain); + return _encode($l10n->npgettext($context, $singular, $plural)); } +/** + * Overrides the domain and category for a plural context-based lookup. + */ +function _dcnpgettext($domain, $context, $singular, $plural, $category) { + $l10n = _get_reader($domain, $category); + return _encode($l10n->npgettext($context, $singular, $plural)); +} -// Wrappers to use if the standard gettext functions are available, but the current locale is not supported by the system. -// Use the standard impl if the current locale is supported, use the custom impl otherwise. + +// Wrappers to use if the standard gettext functions are available, +// but the current locale is not supported by the system. +// Use the standard impl if the current locale is supported, use the +// custom impl otherwise. function T_setlocale($category, $locale) { return _setlocale($category, $locale); } function T_bindtextdomain($domain, $path) { - if (_check_locale()) return bindtextdomain($domain, $path); - else return _bindtextdomain($domain, $path); + if (_check_locale_and_function()) return bindtextdomain($domain, $path); + else return _bindtextdomain($domain, $path); } function T_bind_textdomain_codeset($domain, $codeset) { // bind_textdomain_codeset is available only in PHP 4.2.0+ - if (_check_locale() and function_exists('bind_textdomain_codeset')) return bind_textdomain_codeset($domain, $codeset); - else return _bind_textdomain_codeset($domain, $codeset); + if (_check_locale_and_function('bind_textdomain_codeset')) + return bind_textdomain_codeset($domain, $codeset); + else return _bind_textdomain_codeset($domain, $codeset); } function T_textdomain($domain) { - if (_check_locale()) return textdomain($domain); - else return _textdomain($domain); + if (_check_locale_and_function()) return textdomain($domain); + else return _textdomain($domain); } function T_gettext($msgid) { - if (_check_locale()) return gettext($msgid); - else return _gettext($msgid); + if (_check_locale_and_function()) return gettext($msgid); + else return _gettext($msgid); } function T_($msgid) { - if (_check_locale()) return _($msgid); - return __($msgid); + if (_check_locale_and_function()) return _($msgid); + return __($msgid); } function T_ngettext($single, $plural, $number) { - if (_check_locale()) return ngettext($single, $plural, $number); - else return _ngettext($single, $plural, $number); + if (_check_locale_and_function()) + return ngettext($single, $plural, $number); + else return _ngettext($single, $plural, $number); } function T_dgettext($domain, $msgid) { - if (_check_locale()) return dgettext($domain, $msgid); - else return _dgettext($domain, $msgid); + if (_check_locale_and_function()) return dgettext($domain, $msgid); + else return _dgettext($domain, $msgid); } function T_dngettext($domain, $single, $plural, $number) { - if (_check_locale()) return dngettext($domain, $single, $plural, $number); - else return _dngettext($domain, $single, $plural, $number); + if (_check_locale_and_function()) + return dngettext($domain, $single, $plural, $number); + else return _dngettext($domain, $single, $plural, $number); } function T_dcgettext($domain, $msgid, $category) { - if (_check_locale()) return dcgettext($domain, $msgid, $category); - else return _dcgettext($domain, $msgid, $category); + if (_check_locale_and_function()) + return dcgettext($domain, $msgid, $category); + else return _dcgettext($domain, $msgid, $category); } function T_dcngettext($domain, $single, $plural, $number, $category) { - if (_check_locale()) return dcngettext($domain, $single, $plural, $number, $category); - else return _dcngettext($domain, $single, $plural, $number, $category); + if (_check_locale_and_function()) + return dcngettext($domain, $single, $plural, $number, $category); + else return _dcngettext($domain, $single, $plural, $number, $category); +} + +function T_pgettext($context, $msgid) { + if (_check_locale_and_function('pgettext')) + return pgettext($context, $msgid); + else + return _pgettext($context, $msgid); +} + +function T_dpgettext($domain, $context, $msgid) { + if (_check_locale_and_function('dpgettext')) + return dpgettext($domain, $context, $msgid); + else + return _dpgettext($domain, $context, $msgid); +} + +function T_dcpgettext($domain, $context, $msgid, $category) { + if (_check_locale_and_function('dcpgettext')) + return dcpgettext($domain, $context, $msgid, $category); + else + return _dcpgettext($domain, $context, $msgid, $category); +} + +function T_npgettext($context, $singular, $plural) { + if (_check_locale_and_function('npgettext')) + return npgettext($context, $single, $plural, $number); + else + return _npgettext($context, $single, $plural, $number); +} + +function T_dnpgettext($domain, $context, $singular, $plural) { + if (_check_locale_and_function('dnpgettext')) + return dnpgettext($domain, $context, $single, $plural, $number); + else + return _dnpgettext($domain, $context, $single, $plural, $number); +} + +function T_dcnpgettext($domain, $context, $singular, $plural, $category) { + if (_check_locale_and_function('dcnpgettext')) + return dcnpgettext($domain, $context, $single, + $plural, $number, $category); + else + return _dcnpgettext($domain, $context, $single, + $plural, $number, $category); } @@ -283,36 +479,56 @@ function T_dcngettext($domain, $single, $plural, $number, $category) { // Wrappers used as a drop in replacement for the standard gettext functions if (!function_exists('gettext')) { - function bindtextdomain($domain, $path) { - return _bindtextdomain($domain, $path); - } - function bind_textdomain_codeset($domain, $codeset) { - return _bind_textdomain_codeset($domain, $codeset); - } - function textdomain($domain) { - return _textdomain($domain); - } - function gettext($msgid) { - return _gettext($msgid); - } - function _($msgid) { - return __($msgid); - } - function ngettext($single, $plural, $number) { - return _ngettext($single, $plural, $number); - } - function dgettext($domain, $msgid) { - return _dgettext($domain, $msgid); - } - function dngettext($domain, $single, $plural, $number) { - return _dngettext($domain, $single, $plural, $number); - } - function dcgettext($domain, $msgid, $category) { - return _dcgettext($domain, $msgid, $category); - } - function dcngettext($domain, $single, $plural, $number, $category) { - return _dcngettext($domain, $single, $plural, $number, $category); - } + function bindtextdomain($domain, $path) { + return _bindtextdomain($domain, $path); + } + function bind_textdomain_codeset($domain, $codeset) { + return _bind_textdomain_codeset($domain, $codeset); + } + function textdomain($domain) { + return _textdomain($domain); + } + function gettext($msgid) { + return _gettext($msgid); + } + function _($msgid) { + return __($msgid); + } + function ngettext($single, $plural, $number) { + return _ngettext($single, $plural, $number); + } + function dgettext($domain, $msgid) { + return _dgettext($domain, $msgid); + } + function dngettext($domain, $single, $plural, $number) { + return _dngettext($domain, $single, $plural, $number); + } + function dcgettext($domain, $msgid, $category) { + return _dcgettext($domain, $msgid, $category); + } + function dcngettext($domain, $single, $plural, $number, $category) { + return _dcngettext($domain, $single, $plural, $number, $category); + } + function pgettext($context, $msgid) { + return _pgettext($context, $msgid); + } + function npgettext($context, $single, $plural, $number) { + return _npgettext($context, $single, $plural, $number); + } + function dpgettext($domain, $context, $msgid) { + return _dpgettext($domain, $context, $msgid); + } + function dnpgettext($domain, $context, $single, $plural, $number) { + return _dnpgettext($domain, $context, $single, $plural, $number); + } + function dcpgettext($domain, $context, $msgid, $category) { + return _dcpgettext($domain, $context, $msgid, $category); + } + function dcnpgettext($domain, $context, $single, $plural, + $number, $category) { + return _dcnpgettext($domain, $context, $single, $plural, + $number, $category); + } } ?> diff --git a/src/php-gettext/gettext.php b/src/php-gettext/gettext.php index 978794c..a121f9c 100644 --- a/src/php-gettext/gettext.php +++ b/src/php-gettext/gettext.php @@ -1,8 +1,8 @@ . + Copyright (c) 2003, 2009 Danilo Segan . Copyright (c) 2005 Nico Kaiser - + This file is part of PHP-gettext. PHP-gettext is free software; you can redistribute it and/or modify @@ -20,13 +20,13 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - + /** * Provides a simple gettext replacement that works independently from * the system's gettext abilities. * It can read MO files and use them for translating strings. * The files are passed to gettext_reader as a Stream (see streams.php) - * + * * This version has the ability to cache all strings and translations to * speed up the string lookup. * While the cache is enabled by default, it can be switched off with the @@ -36,7 +36,7 @@ class gettext_reader { //public: var $error = 0; // public variable that holds error code (0 if no error) - + //private: var $BYTEORDER = 0; // 0: low endian, 1: big endian var $STREAM = NULL; @@ -52,27 +52,33 @@ class gettext_reader { /* Methods */ - - + + /** * Reads a 32bit Integer from the Stream - * + * * @access private * @return Integer from the Stream */ function readint() { if ($this->BYTEORDER == 0) { // low endian - return array_shift(unpack('V', $this->STREAM->read(4))); + $input=unpack('V', $this->STREAM->read(4)); + return array_shift($input); } else { // big endian - return array_shift(unpack('N', $this->STREAM->read(4))); + $input=unpack('N', $this->STREAM->read(4)); + return array_shift($input); } } + function read($bytes) { + return $this->STREAM->read($bytes); + } + /** * Reads an array of Integers from the Stream - * + * * @param int count How many elements should be read * @return Array of Integers */ @@ -85,10 +91,10 @@ class gettext_reader { return unpack('N'.$count, $this->STREAM->read(4 * $count)); } } - + /** * Constructor - * + * * @param object Reader the StreamReader object * @param boolean enable_cache Enable or disable caching of strings (default on) */ @@ -98,39 +104,37 @@ class gettext_reader { $this->short_circuit = true; return; } - + // Caching can be turned off $this->enable_cache = $enable_cache; - // $MAGIC1 = (int)0x950412de; //bug in PHP 5 - $MAGIC1 = (int) - 1794895138; - // $MAGIC2 = (int)0xde120495; //bug - $MAGIC2 = (int) - 569244523; + $MAGIC1 = "\x95\x04\x12\xde"; + $MAGIC2 = "\xde\x12\x04\x95"; $this->STREAM = $Reader; - $magic = $this->readint(); - if ($magic == $MAGIC1 || $magic == ($MAGIC1 & 0xFFFFFFFF)) { - $this->BYTEORDER = 0; - } elseif ($magic == $MAGIC2 || $magic == ($MAGIC2 & 0xFFFFFFFF)) { + $magic = $this->read(4); + if ($magic == $MAGIC1) { $this->BYTEORDER = 1; + } elseif ($magic == $MAGIC2) { + $this->BYTEORDER = 0; } else { $this->error = 1; // not MO file return false; } - + // FIXME: Do we care about revision? We should. $revision = $this->readint(); - + $this->total = $this->readint(); $this->originals = $this->readint(); $this->translations = $this->readint(); } - + /** * Loads the translation tables from the MO file into the cache * If caching is enabled, also loads all strings into a cache * to speed up translation lookups - * + * * @access private */ function load_tables() { @@ -138,13 +142,17 @@ class gettext_reader { is_array($this->table_originals) && is_array($this->table_translations)) return; - + /* get original and translations tables */ - $this->STREAM->seekto($this->originals); - $this->table_originals = $this->readintarray($this->total * 2); - $this->STREAM->seekto($this->translations); - $this->table_translations = $this->readintarray($this->total * 2); - + if (!is_array($this->table_originals)) { + $this->STREAM->seekto($this->originals); + $this->table_originals = $this->readintarray($this->total * 2); + } + if (!is_array($this->table_translations)) { + $this->STREAM->seekto($this->translations); + $this->table_translations = $this->readintarray($this->total * 2); + } + if ($this->enable_cache) { $this->cache_translations = array (); /* read all strings in the cache */ @@ -157,10 +165,10 @@ class gettext_reader { } } } - + /** * Returns a string from the "originals" table - * + * * @access private * @param int num Offset number of original string * @return string Requested string if found, otherwise '' @@ -174,10 +182,10 @@ class gettext_reader { $data = $this->STREAM->read($length); return (string)$data; } - + /** * Returns a string from the "translations" table - * + * * @access private * @param int num Offset number of original string * @return string Requested string if found, otherwise '' @@ -191,10 +199,10 @@ class gettext_reader { $data = $this->STREAM->read($length); return (string)$data; } - + /** * Binary search for string - * + * * @access private * @param string string * @param int start (internally used in recursive function) @@ -232,10 +240,10 @@ class gettext_reader { return $this->find_string($string, $half, $end); } } - + /** * Translates a string - * + * * @access public * @param string string to be translated * @return string translated string (or original, if not found) @@ -243,8 +251,8 @@ class gettext_reader { function translate($string) { if ($this->short_circuit) return $string; - $this->load_tables(); - + $this->load_tables(); + if ($this->enable_cache) { // Caching enabled, get translated string from cache if (array_key_exists($string, $this->cache_translations)) @@ -261,17 +269,66 @@ class gettext_reader { } } + /** + * Sanitize plural form expression for use in PHP eval call. + * + * @access private + * @return string sanitized plural form expression + */ + function sanitize_plural_expression($expr) { + // Get rid of disallowed characters. + $expr = preg_replace('@[^a-zA-Z0-9_:;\(\)\?\|\&=!<>+*/\%-]@', '', $expr); + + // Add parenthesis for tertiary '?' operator. + $expr .= ';'; + $res = ''; + $p = 0; + for ($i = 0; $i < strlen($expr); $i++) { + $ch = $expr[$i]; + switch ($ch) { + case '?': + $res .= ' ? ('; + $p++; + break; + case ':': + $res .= ') : ('; + break; + case ';': + $res .= str_repeat( ')', $p) . ';'; + $p = 0; + break; + default: + $res .= $ch; + } + } + return $res; + } + + /** + * Parse full PO header and extract only plural forms line. + * + * @access private + * @return string verbatim plural form header field + */ + function extract_plural_forms_header_from_po_header($header) { + if (preg_match("/(^|\n)plural-forms: ([^\n]*)\n/i", $header, $regs)) + $expr = $regs[2]; + else + $expr = "nplurals=2; plural=n == 1 ? 0 : 1;"; + return $expr; + } + /** * Get possible plural forms from MO header - * + * * @access private * @return string plural form header */ function get_plural_forms() { - // lets assume message number 0 is header + // lets assume message number 0 is header // this is true, right? $this->load_tables(); - + // cache header field for plural forms if (! is_string($this->pluralheader)) { if ($this->enable_cache) { @@ -279,18 +336,15 @@ class gettext_reader { } else { $header = $this->get_translation_string(0); } - if (eregi("plural-forms: ([^\n]*)\n", $header, $regs)) - $expr = $regs[1]; - else - $expr = "nplurals=2; plural=n == 1 ? 0 : 1;"; - $this->pluralheader = $expr; + $expr = $this->extract_plural_forms_header_from_po_header($header); + $this->pluralheader = $this->sanitize_plural_expression($expr); } return $this->pluralheader; } /** * Detects which plural form to take - * + * * @access private * @param n count * @return int array index of the right plural form @@ -300,7 +354,7 @@ class gettext_reader { $string = str_replace('nplurals',"\$total",$string); $string = str_replace("n",$n,$string); $string = str_replace('plural',"\$plural",$string); - + $total = 0; $plural = 0; @@ -311,7 +365,7 @@ class gettext_reader { /** * Plural version of gettext - * + * * @access public * @param string single * @param string plural @@ -327,12 +381,12 @@ class gettext_reader { } // find out the appropriate form - $select = $this->select_string($number); - + $select = $this->select_string($number); + // this should contains all strings separated by NULLs - $key = $single.chr(0).$plural; - - + $key = $single . chr(0) . $plural; + + if ($this->enable_cache) { if (! array_key_exists($key, $this->cache_translations)) { return ($number != 1) ? $plural : $single; @@ -353,6 +407,15 @@ class gettext_reader { } } + function pgettext($context, $msgid) { + $key = $context . chr(4) . $msgid; + return $this->translate($key); + } + + function npgettext($context, $singular, $plural, $number) { + $singular = $context . chr(4) . $singular; + return $this->ngettext($singular, $plural, $number); + } } ?> diff --git a/src/php-gettext/streams.php b/src/php-gettext/streams.php index 4237de1..3cdc158 100644 --- a/src/php-gettext/streams.php +++ b/src/php-gettext/streams.php @@ -1,6 +1,6 @@ . + Copyright (c) 2003, 2005, 2006, 2009 Danilo Segan . This file is part of PHP-gettext. @@ -21,29 +21,29 @@ */ -// Simple class to wrap file streams, string streams, etc. -// seek is essential, and it should be byte stream + // Simple class to wrap file streams, string streams, etc. + // seek is essential, and it should be byte stream class StreamReader { // should return a string [FIXME: perhaps return array of bytes?] function read($bytes) { return false; } - + // should return new position function seekto($position) { return false; } - + // returns current position function currentpos() { return false; } - + // returns length of entire stream (limit for seekto()s) function length() { return false; } -} +}; class StringReader { var $_pos; @@ -78,7 +78,7 @@ class StringReader { return strlen($this->_str); } -} +}; class FileReader { @@ -93,8 +93,8 @@ class FileReader { $this->_pos = 0; $this->_fd = fopen($filename,'rb'); if (!$this->_fd) { - $this->error = 3; // Cannot read file, probably permissions - return false; + $this->error = 3; // Cannot read file, probably permissions + return false; } } else { $this->error = 2; // File doesn't exist @@ -115,7 +115,7 @@ class FileReader { $bytes -= strlen($chunk); } $this->_pos = ftell($this->_fd); - + return $data; } else return ''; } @@ -138,9 +138,9 @@ class FileReader { fclose($this->_fd); } -} +}; -// Preloads entire file in memory first, then creates a StringReader +// Preloads entire file in memory first, then creates a StringReader // over it (it assumes knowledge of StringReader internals) class CachedFileReader extends StringReader { function CachedFileReader($filename) { @@ -150,8 +150,8 @@ class CachedFileReader extends StringReader { $fd = fopen($filename,'rb'); if (!$fd) { - $this->error = 3; // Cannot read file, probably permissions - return false; + $this->error = 3; // Cannot read file, probably permissions + return false; } $this->_str = fread($fd, $length); fclose($fd); @@ -161,7 +161,7 @@ class CachedFileReader extends StringReader { return false; } } -} +}; ?> diff --git a/src/php-gettext/tests/LocalesTest.php b/src/php-gettext/tests/LocalesTest.php new file mode 100644 index 0000000..3000286 --- /dev/null +++ b/src/php-gettext/tests/LocalesTest.php @@ -0,0 +1,66 @@ +assertEquals('sr_RS', _setlocale(LC_MESSAGES, 0)); + + // For an existing locale, it never needs emulation. + putenv("LANG=C"); + _setlocale(LC_MESSAGES, ""); + $this->assertEquals(0, locale_emulation()); + + // If we set it to a non-existent locale, it still works, but uses + // emulation. + _setlocale(LC_MESSAGES, "xxx_XXX"); + $this->assertEquals('xxx_XXX', _setlocale(LC_MESSAGES, 0)); + $this->assertEquals(1, locale_emulation()); + } + + public function test_get_list_of_locales() + { + // For a locale containing country code, we prefer + // full locale name, but if that's not found, fall back + // to the language only locale name. + $this->assertEquals(array("sr_RS", "sr"), + get_list_of_locales("sr_RS")); + + // If language code is used, it's the only thing returned. + $this->assertEquals(array("sr"), + get_list_of_locales("sr")); + + // There is support for language and charset only. + $this->assertEquals(array("sr.UTF-8", "sr"), + get_list_of_locales("sr.UTF-8")); + + // It can also split out character set from the full locale name. + $this->assertEquals(array("sr_RS.UTF-8", "sr_RS", "sr"), + get_list_of_locales("sr_RS.UTF-8")); + + // There is support for @modifier in locale names as well. + $this->assertEquals(array("sr_RS.UTF-8@latin", "sr_RS@latin", "sr@latin", + "sr_RS.UTF-8", "sr_RS", "sr"), + get_list_of_locales("sr_RS.UTF-8@latin")); + + // We can pass in only language and modifier. + $this->assertEquals(array("sr@latin", "sr"), + get_list_of_locales("sr@latin")); + + + // If locale name is not following the regular POSIX pattern, + // it's used verbatim. + $this->assertEquals(array("something"), + get_list_of_locales("something")); + + // Passing in an empty string returns an empty array. + $this->assertEquals(array(), + get_list_of_locales("")); + } +} + +?> diff --git a/src/php-gettext/tests/ParsingTest.php b/src/php-gettext/tests/ParsingTest.php new file mode 100644 index 0000000..9b350b2 --- /dev/null +++ b/src/php-gettext/tests/ParsingTest.php @@ -0,0 +1,43 @@ +assertEquals( + 'nplurals=2; plural=n == 1 ? 0 : 1;', + $parser->extract_plural_forms_header_from_po_header("")); + + // Extracting it from the middle of the header works. + $this->assertEquals( + 'nplurals=1; plural=0;', + $parser->extract_plural_forms_header_from_po_header( + "Content-type: text/html; charset=UTF-8\n" + ."Plural-Forms: nplurals=1; plural=0;\n" + ."Last-Translator: nobody\n" + )); + + // It's also case-insensitive. + $this->assertEquals( + 'nplurals=1; plural=0;', + $parser->extract_plural_forms_header_from_po_header( + "PLURAL-forms: nplurals=1; plural=0;\n" + )); + + // It falls back to default if it's not on a separate line. + $this->assertEquals( + 'nplurals=2; plural=n == 1 ? 0 : 1;', + $parser->extract_plural_forms_header_from_po_header( + "Content-type: text/html; charset=UTF-8" // note the missing \n here + ."Plural-Forms: nplurals=1; plural=0;\n" + ."Last-Translator: nobody\n" + )); + + } + +} +?> -- cgit v1.2.3-54-g00ecf From 95c05e49a70d007f41be60f90a7ae0ed7e5996a2 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Thu, 30 Sep 2010 07:58:07 +0200 Subject: remove deprecate split() --- src/SemanticScuttle/Service/User.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/SemanticScuttle/Service/User.php b/src/SemanticScuttle/Service/User.php index 6fc3bb7..d74d104 100644 --- a/src/SemanticScuttle/Service/User.php +++ b/src/SemanticScuttle/Service/User.php @@ -359,7 +359,7 @@ class SemanticScuttle_Service_User extends SemanticScuttle_DbService return $_SESSION[$this->getSessionKey()]; } else if (isset($_COOKIE[$this->getCookieKey()])) { - $cook = split(':', $_COOKIE[$this->getCookieKey()]); + $cook = explode(':', $_COOKIE[$this->getCookieKey()]); //cookie looks like this: 'id:md5(username+password)' $query = 'SELECT * FROM '. $this->getTableName() . ' WHERE MD5(CONCAT('.$this->getFieldName('username') . -- cgit v1.2.3-54-g00ecf From 04d360dadbdcd492d3a1e5b7b6105007ad841987 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Fri, 1 Oct 2010 18:21:16 +0200 Subject: CS, docblocks --- src/SemanticScuttle/Service/Tag2Tag.php | 45 ++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/SemanticScuttle/Service/Tag2Tag.php b/src/SemanticScuttle/Service/Tag2Tag.php index 8666209..33d211b 100644 --- a/src/SemanticScuttle/Service/Tag2Tag.php +++ b/src/SemanticScuttle/Service/Tag2Tag.php @@ -14,7 +14,7 @@ */ /** - * SemanticScuttle tag-to-tag service. + * SemanticScuttle tag-to-tag service which works with tag relations. * * @category Bookmarking * @package SemanticScuttle @@ -102,18 +102,49 @@ class SemanticScuttle_Service_Tag2Tag extends SemanticScuttle_DbService return true; } - // Return linked tags just for admin users - function getAdminLinkedTags($tag, $relationType, $inverseRelation = false, $stopList = array()) { + + + /** + * Same as getLinkedTags(), but only tags that have been created + * by admin users are returned. + * + * @param string $tag Tag to get related tags for + * @param string $relationType Type of tag-to-tag relation: >, < or = + * @param boolean $inverseRelation Reverse relation (parent -> child) + * @param array $stopList Array of tags that shall not be returned + * + * @return array Array of tag names + * + * @see getLinkedTags() + */ + public function getAdminLinkedTags( + $tag, $relationType, $inverseRelation = false, $stopList = array() + ) { // look for admin ids $userservice = SemanticScuttle_Service_Factory :: get('User'); - $adminIds = $userservice->getAdminIds(); + $adminIds = $userservice->getAdminIds(); //ask for their linked tags - return $this->getLinkedTags($tag, $relationType, $adminIds, $inverseRelation, $stopList); + return $this->getLinkedTags( + $tag, $relationType, $adminIds, $inverseRelation, $stopList + ); } - // Return the target linked tags. If inverseRelation is true, return the source linked tags. - function getLinkedTags($tag, $relationType, $uId = null, $inverseRelation = false, $stopList = array()) { + + + /** + * Returns an array of tags that are in relation to the given $tag. + * + * @param string $tag Tag to get related tags for + * @param string $relationType Type of tag-to-tag relation: >, < or = + * @param boolean $inverseRelation Reverse relation (parent -> child) + * @param array $stopList Array of tags that shall not be returned + * + * @return array Array of tag names + */ + public function getLinkedTags( + $tag, $relationType, $uId = null, $inverseRelation = false, $stopList = array() + ) { // Set up the SQL query. if($inverseRelation) { $queriedTag = "tag1"; -- cgit v1.2.3-54-g00ecf From b812deefa1ab85c542c13dc97432ca2fe90958f1 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Thu, 7 Oct 2010 18:59:27 +0200 Subject: give admin tags a link href :) --- src/SemanticScuttle/constants.php | 4 +++- www/ajax/getadminlinkedtags.php | 21 +++++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/SemanticScuttle/constants.php b/src/SemanticScuttle/constants.php index 95c4384..aebc059 100644 --- a/src/SemanticScuttle/constants.php +++ b/src/SemanticScuttle/constants.php @@ -17,7 +17,9 @@ if (!isset($GLOBALS['root'])) { $rootTmp = '/'; foreach ($pieces as $piece) { //we eliminate possible sscuttle subfolders (like gsearch for example) - if ($piece != '' && !strstr($piece, '.php') && $piece != 'gsearch') { + if ($piece != '' && !strstr($piece, '.php') + && $piece != 'gsearch' && $piece != 'ajax' + ) { $rootTmp .= $piece .'/'; } } diff --git a/www/ajax/getadminlinkedtags.php b/www/ajax/getadminlinkedtags.php index 5581c43..1004f29 100644 --- a/www/ajax/getadminlinkedtags.php +++ b/www/ajax/getadminlinkedtags.php @@ -41,11 +41,28 @@ function assembleTagData($tag, SemanticScuttle_Service_Tag2Tag $t2t) return $tagData; } +/** + * Creates an jsTree json array for the given tag + * + * @param string $tag Tag name + * @param boolean $hasChildren If the tag has subtags (children) or not + * + * @return array Array to be sent back to the browser as json + */ function createTagArray($tag, $hasChildren = true) { $ar = array( - 'data' => $tag, - 'attr' => array('rel' => $tag), + 'data' => array( + // attributes + 'title' => $tag, + 'attr' => array( + 'href' => createUrl('tags', $tag) + ) + ), + //
  • attributes + 'attr' => array( + 'rel' => $tag,//needed for identifying the tag in html + ), ); if ($hasChildren) { //jstree needs that to show the arrows -- cgit v1.2.3-54-g00ecf From aa6b6863b8b23aad2198b301d2b0979987f8b087 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Thu, 7 Oct 2010 19:02:06 +0200 Subject: CS on constants --- src/SemanticScuttle/constants.php | 70 ++++++++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/SemanticScuttle/constants.php b/src/SemanticScuttle/constants.php index aebc059..c76f7b2 100644 --- a/src/SemanticScuttle/constants.php +++ b/src/SemanticScuttle/constants.php @@ -1,35 +1,49 @@ + * @author Christian Weiske + * @author Eric Dane + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle */ // Debug managament -if(isset($GLOBALS['debugMode'])) { - define('DEBUG_MODE', $GLOBALS['debugMode']); - define('DEBUG_EXTRA', $GLOBALS['debugMode']); // Constant used exclusively into db/ directory +if (isset($GLOBALS['debugMode'])) { + define('DEBUG_MODE', $GLOBALS['debugMode']); + // Constant used exclusively into db/ directory + define('DEBUG_EXTRA', $GLOBALS['debugMode']); } // Determine the base URL as ROOT if (!isset($GLOBALS['root'])) { - $pieces = explode('/', $_SERVER['SCRIPT_NAME']); - - $rootTmp = '/'; - foreach ($pieces as $piece) { - //we eliminate possible sscuttle subfolders (like gsearch for example) + $pieces = explode('/', $_SERVER['SCRIPT_NAME']); + + $rootTmp = '/'; + foreach ($pieces as $piece) { + //we eliminate possible sscuttle subfolders (like gsearch for example) if ($piece != '' && !strstr($piece, '.php') && $piece != 'gsearch' && $piece != 'ajax' ) { - $rootTmp .= $piece .'/'; - } - } - if (($rootTmp != '/') && (substr($rootTmp, -1, 1) != '/')) { - $rootTmp .= '/'; - } + $rootTmp .= $piece .'/'; + } + } + if (($rootTmp != '/') && (substr($rootTmp, -1, 1) != '/')) { + $rootTmp .= '/'; + } - define('ROOT', 'http://'. $_SERVER['HTTP_HOST'] . $rootTmp); + define('ROOT', 'http://'. $_SERVER['HTTP_HOST'] . $rootTmp); } else { - define('ROOT', $GLOBALS['root']); + define('ROOT', $GLOBALS['root']); } // Error codes @@ -46,19 +60,21 @@ define('PAGE_WATCHLIST', "watchlist"); // Miscellanous -// INSTALLATION_ID is based on directory DB and used as prefix (in session and cookie) to prevent mutual login for different installations on the same host server +// INSTALLATION_ID is based on directory DB and used as prefix +// (in session and cookie) to prevent mutual login for different +// installations on the same host server define('INSTALLATION_ID', md5($GLOBALS['dbname'].$GLOBALS['tableprefix'])); // Correct bugs with PATH_INFO (maybe for Apache 1 or CGI) -- for 1&1 host... if (isset($_SERVER['PATH_INFO']) && isset($_SERVER['ORIG_PATH_INFO'])) { - if(strlen($_SERVER["PATH_INFO"]) -- cgit v1.2.3-54-g00ecf From be126cc958212ed74fe916eabcb1e6ac928df5e4 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Fri, 8 Oct 2010 17:43:05 +0200 Subject: tests for getadminlinkedtags --- src/SemanticScuttle/Service/User.php | 18 ++-- tests/TestBase.php | 28 ++++++ tests/ajax/GetAdminLinkedTagsTest.php | 160 ++++++++++++++++++++++++++++++++++ 3 files changed, 201 insertions(+), 5 deletions(-) create mode 100644 tests/ajax/GetAdminLinkedTagsTest.php (limited to 'src') diff --git a/src/SemanticScuttle/Service/User.php b/src/SemanticScuttle/Service/User.php index d74d104..fd9d84f 100644 --- a/src/SemanticScuttle/Service/User.php +++ b/src/SemanticScuttle/Service/User.php @@ -203,18 +203,26 @@ class SemanticScuttle_Service_User extends SemanticScuttle_DbService } } - /* Takes an numerical "id" or a string "username" - and returns the numerical "id" if the user exists else returns NULL */ - function getIdFromUser($user) { + /** + * Obtains the ID of the given user name. + * If a user ID is passed, it is returned. + * In case the user does not exist, NULL is returned. + * + * @param string|integer $user User name or user ID + * + * @return integer NULL if not found or the user ID + */ + public function getIdFromUser($user) + { if (is_int($user)) { return intval($user); } else { $objectUser = $this->getObjectUserByUsername($user); - if($objectUser != NULL) { + if ($objectUser != null) { return $objectUser->getId(); } } - return NULL; + return null; } /** diff --git a/tests/TestBase.php b/tests/TestBase.php index 402330b..c0acd58 100644 --- a/tests/TestBase.php +++ b/tests/TestBase.php @@ -104,6 +104,34 @@ class TestBase extends PHPUnit_Framework_TestCase return $uid; } + + + /** + * Retrieves the UID of an admin user. + * If that user does not exist in the database, it is created. + * + * @return integer UID of admin user + */ + protected function getAdminUser() + { + if (count($GLOBALS['admin_users']) == 0) { + $this->fail('No admin users configured'); + } + $adminUserName = reset($GLOBALS['admin_users']); + + $us = SemanticScuttle_Service_Factory::get('User'); + $uid = $us->getIdFromUser($adminUserName); + if ($uid === null) { + //that user does not exist in the database; create it + $uid = $us->addUser( + $adminUserName, + rand(), + 'unittest-admin-' . $adminUserName . '@example.org' + ); + } + + return $uid; + } } ?> \ No newline at end of file diff --git a/tests/ajax/GetAdminLinkedTagsTest.php b/tests/ajax/GetAdminLinkedTagsTest.php new file mode 100644 index 0000000..d8ec447 --- /dev/null +++ b/tests/ajax/GetAdminLinkedTagsTest.php @@ -0,0 +1,160 @@ + + * @author Christian Weiske + * @author Eric Dane + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle + */ + +require_once dirname(__FILE__) . '/../prepare.php'; +require_once 'HTTP/Request2.php'; + +if (!defined('PHPUnit_MAIN_METHOD')) { + define('PHPUnit_MAIN_METHOD', 'ajax_GetAdminLinkedTagsTest::main'); +} + +/** + * Unit tests for the ajax linked admin tags script + * + * @category Bookmarking + * @package SemanticScuttle + * @author Christian Weiske + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle + */ +class ajax_GetAdminLinkedTagsTest extends TestBaseApi +{ + protected $urlPart = 'ajax/getadminlinkedtags.php'; + + + + /** + * Used to run this test class standalone + * + * @return void + */ + public static function main() + { + require_once 'PHPUnit/TextUI/TestRunner.php'; + PHPUnit_TextUI_TestRunner::run( + new PHPUnit_Framework_TestSuite(__CLASS__) + ); + } + + + + /** + * Verify that we get the configured root tags if + * we do not pass any parameters + */ + public function testRootTags() + { + $req = $this->getRequest(); + $res = $req->send(); + + $this->assertEquals(200, $res->getStatus()); + $this->assertEquals( + 'application/json; charset=utf-8', + $res->getHeader('content-type') + ); + + $data = json_decode($res->getBody()); + $this->assertType('array', $data); + + //same number of elements as the menu2Tags array + $this->assertEquals( + count($GLOBALS['menu2Tags']), + count($data) + ); + + //and the same contents + foreach ($data as $tagObj) { + $tagName = $tagObj->data->title; + $this->assertContains($tagName, $GLOBALS['menu2Tags']); + } + } + + /** + * Verify that we get subtags of a given tag + */ + public function testSubTags() + { + $t2t = SemanticScuttle_Service_Factory::get('Tag2Tag'); + $t2t->deleteAll(); + + $menu2Tag = reset($GLOBALS['menu2Tags']); + //we have a subtag now + $this->addBookmark( + $this->getAdminUser(), + null, + 0, + $menu2Tag . '>adminsubtag' + ); + + $res = $this->getRequest('?tag=' . $menu2Tag)->send(); + $this->assertEquals(200, $res->getStatus()); + $this->assertEquals( + 'application/json; charset=utf-8', + $res->getHeader('content-type') + ); + + $data = json_decode($res->getBody()); + $this->assertType('array', $data); + + //only one subtag + $this->assertEquals(1, count($data)); + $this->assertEquals('adminsubtag', $data[0]->data->title); + } + + /** + * Verify that we only get admin tags, not tags from + * non-admin people + */ + public function testOnlyAdminTags() + { + $t2t = SemanticScuttle_Service_Factory::get('Tag2Tag'); + $t2t->deleteAll(); + + $menu2Tag = reset($GLOBALS['menu2Tags']); + //we have a subtag now + $this->addBookmark( + $this->getAdminUser(), + null, + 0, + $menu2Tag . '>adminsubtag' + ); + //add another bookmark now, but for a normal user + $this->addBookmark( + null, + null, + 0, + $menu2Tag . '>normalsubtag' + ); + + $res = $this->getRequest('?tag=' . $menu2Tag)->send(); + $this->assertEquals(200, $res->getStatus()); + $this->assertEquals( + 'application/json; charset=utf-8', + $res->getHeader('content-type') + ); + + $data = json_decode($res->getBody()); + $this->assertType('array', $data); + + //we should have only one subtag now, the admin one + $this->assertEquals(1, count($data)); + $this->assertEquals('adminsubtag', $data[0]->data->title); + } +} + +if (PHPUnit_MAIN_METHOD == 'ajax_GetAdminLinkedTagsTest::main') { + ajax_GetAdminLinkedTagsTest::main(); +} +?> \ No newline at end of file -- cgit v1.2.3-54-g00ecf From f359fd685f164855a34c85d164e0d38da12a2434 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Sat, 9 Oct 2010 12:09:39 +0200 Subject: make the page valid and keep chromium happy --- data/templates/sidebar.block.linked.php | 6 +++--- data/templates/top.inc.php | 3 ++- src/SemanticScuttle/header.php | 5 ++++- 3 files changed, 9 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/data/templates/sidebar.block.linked.php b/data/templates/sidebar.block.linked.php index db0d087..54e9260 100644 --- a/data/templates/sidebar.block.linked.php +++ b/data/templates/sidebar.block.linked.php @@ -32,7 +32,7 @@ if ($editingMode) { ?> - +]]> \ No newline at end of file diff --git a/data/templates/top.inc.php b/data/templates/top.inc.php index 8be978f..c3cbc5d 100644 --- a/data/templates/top.inc.php +++ b/data/templates/top.inc.php @@ -1,7 +1,8 @@ + - + <?php echo filter($GLOBALS['sitename'] .(isset($pagetitle) ? ' » ' . $pagetitle : '')); ?> diff --git a/src/SemanticScuttle/header.php b/src/SemanticScuttle/header.php index d1a5c29..3caeb35 100644 --- a/src/SemanticScuttle/header.php +++ b/src/SemanticScuttle/header.php @@ -120,7 +120,10 @@ $tplVars['userservice'] = $userservice; if (!defined('UNIT_TEST_MODE')) { //API files define that, so we need a way to support both of them if (!isset($httpContentType)) { - $httpContentType = 'text/html'; + //$httpContentType = 'text/html'; + //using that mime type makes all javascript nice in Chromium + // it also serves as test base if the pages really validate + $httpContentType = 'application/xhtml+xml'; } if ($httpContentType !== false) { header('Content-Type: ' . $httpContentType . '; charset=utf-8'); -- cgit v1.2.3-54-g00ecf From efdcf11bb08e424cfbe140e167d8ece398ab74fa Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Sat, 9 Oct 2010 13:09:46 +0200 Subject: activate strict xhtml mode only on debug mode --- src/SemanticScuttle/header.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/SemanticScuttle/header.php b/src/SemanticScuttle/header.php index 3caeb35..dc25581 100644 --- a/src/SemanticScuttle/header.php +++ b/src/SemanticScuttle/header.php @@ -120,10 +120,15 @@ $tplVars['userservice'] = $userservice; if (!defined('UNIT_TEST_MODE')) { //API files define that, so we need a way to support both of them if (!isset($httpContentType)) { - //$httpContentType = 'text/html'; - //using that mime type makes all javascript nice in Chromium - // it also serves as test base if the pages really validate - $httpContentType = 'application/xhtml+xml'; + if (DEBUG_MODE) { + //using that mime type makes all javascript nice in Chromium + // it also serves as test base if the pages really validate + $httpContentType = 'application/xhtml+xml'; + } else { + //until we are sure that all pages validate, we + // keep the non-strict mode on for normal installations + $httpContentType = 'text/html'; + } } if ($httpContentType !== false) { header('Content-Type: ' . $httpContentType . '; charset=utf-8'); -- cgit v1.2.3-54-g00ecf From 5e1b9533eed98da03a06cd06bb13c8143b7fc91e Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Wed, 13 Oct 2010 18:58:46 +0200 Subject: load minified javascript if debug mode is off; add minified version (generated with "yuicompressor --nomunge") of jstree --- data/templates/top.inc.php | 9 +++++++-- src/SemanticScuttle/constants.php | 1 + www/js/jstree-1.0-rc2/jquery.jstree.min.js | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 www/js/jstree-1.0-rc2/jquery.jstree.min.js (limited to 'src') diff --git a/data/templates/top.inc.php b/data/templates/top.inc.php index 428f3d4..bdd4b1a 100644 --- a/data/templates/top.inc.php +++ b/data/templates/top.inc.php @@ -19,8 +19,13 @@ if (isset($rsschannels)) { ?> - - + + + + + + + diff --git a/src/SemanticScuttle/constants.php b/src/SemanticScuttle/constants.php index c76f7b2..b023840 100644 --- a/src/SemanticScuttle/constants.php +++ b/src/SemanticScuttle/constants.php @@ -45,6 +45,7 @@ if (!isset($GLOBALS['root'])) { } else { define('ROOT', $GLOBALS['root']); } +define('ROOT_JS', ROOT . 'js/jstree-1.0-rc2/'); // Error codes define('GENERAL_MESSAGE', 200); diff --git a/www/js/jstree-1.0-rc2/jquery.jstree.min.js b/www/js/jstree-1.0-rc2/jquery.jstree.min.js new file mode 100644 index 0000000..4443655 --- /dev/null +++ b/www/js/jstree-1.0-rc2/jquery.jstree.min.js @@ -0,0 +1 @@ +"use strict";(function($){$.vakata={};$.vakata.css={get_css:function(rule_name,delete_flag,sheet){rule_name=rule_name.toLowerCase();var css_rules=sheet.cssRules||sheet.rules,j=0;do{if(css_rules.length&&j>css_rules.length+5){return false}if(css_rules[j].selectorText&&css_rules[j].selectorText.toLowerCase()==rule_name){if(delete_flag===true){if(sheet.removeRule){sheet.removeRule(j)}if(sheet.deleteRule){sheet.deleteRule(j)}return true}else{return css_rules[j]}}}while(css_rules[++j]);return false},add_css:function(rule_name,sheet){if($.jstree.css.get_css(rule_name,false,sheet)){return false}if(sheet.insertRule){sheet.insertRule(rule_name+" { }",0)}else{sheet.addRule(rule_name,null,0)}return $.vakata.css.get_css(rule_name)},remove_css:function(rule_name,sheet){return $.vakata.css.get_css(rule_name,true,sheet)},add_sheet:function(opts){var tmp;if(opts.str){tmp=document.createElement("style");tmp.setAttribute("type","text/css");if(tmp.styleSheet){document.getElementsByTagName("head")[0].appendChild(tmp);tmp.styleSheet.cssText=opts.str}else{tmp.appendChild(document.createTextNode(opts.str));document.getElementsByTagName("head")[0].appendChild(tmp)}return tmp.sheet||tmp.styleSheet}if(opts.url){if(document.createStyleSheet){try{tmp=document.createStyleSheet(opts.url)}catch(e){}}else{tmp=document.createElement("link");tmp.rel="stylesheet";tmp.type="text/css";tmp.media="all";tmp.href=opts.url;document.getElementsByTagName("head")[0].appendChild(tmp);return tmp.styleSheet}}}}})(jQuery);(function($){var instances=[],focused_instance=-1,plugins={},prepared_move={},is_ie6=false;$.fn.jstree=function(settings){var isMethodCall=(typeof settings=="string"),args=Array.prototype.slice.call(arguments,1),returnValue=this;if(!isMethodCall&&$.meta){args.push($.metadata.get(this).jstree)}settings=!isMethodCall&&args.length?$.extend.apply(null,[true,settings].concat(args)):settings;if(isMethodCall&&settings.substring(0,1)=="_"){return returnValue}if(isMethodCall){this.each(function(){var instance=instances[$.data(this,"jstree-instance-id")],methodValue=(instance&&$.isFunction(instance[settings]))?instance[settings].apply(instance,args):instance;if(typeof methodValue!=="undefined"&&(settings.indexOf("is_"===0)||(methodValue!==true&&methodValue!==false))){returnValue=methodValue;return false}})}else{this.each(function(){var instance_id=$.data(this,"jstree-instance-id"),s=false;if(typeof instance_id!=="undefined"&&instances[instance_id]){instances[instance_id].destroy()}instance_id=parseInt(instances.push({}),10)-1;$.data(this,"jstree-instance-id",instance_id);if(!settings){settings={}}settings.plugins=$.isArray(settings.plugins)?settings.plugins:$.jstree.defaults.plugins;if($.inArray("core",settings.plugins)===-1){settings.plugins.unshift("core")}s=$.extend(true,{},$.jstree.defaults,settings);s.plugins=settings.plugins;$.each(plugins,function(i,val){if($.inArray(i,s.plugins)===-1){s[i]=null;delete s[i]}});instances[instance_id]=new $.jstree._instance(instance_id,$(this).addClass("jstree jstree-"+instance_id),s);$.each(instances[instance_id]._get_settings().plugins,function(i,val){instances[instance_id].data[val]={}});$.each(instances[instance_id]._get_settings().plugins,function(i,val){if(plugins[val]){plugins[val].__init.apply(instances[instance_id])}});instances[instance_id].init()})}return returnValue};$.jstree={defaults:{plugins:[]},_focused:function(){return instances[focused_instance]||null},_reference:function(needle){if(instances[needle]){return instances[needle]}var o=$(needle);if(!o.length&&typeof needle==="string"){o=$("#"+needle)}if(!o.length){return null}return instances[o.closest(".jstree").data("jstree-instance-id")]||null},_instance:function(index,container,settings){this.data={core:{}};this.get_settings=function(){return $.extend(true,{},settings)};this._get_settings=function(){return settings};this.get_index=function(){return index};this.get_container=function(){return container};this._set_settings=function(s){settings=$.extend(true,{},settings,s)}},_fn:{},plugin:function(pname,pdata){pdata=$.extend({},{__init:$.noop,__destroy:$.noop,_fn:{},defaults:false},pdata);plugins[pname]=pdata;$.jstree.defaults[pname]=pdata.defaults;$.each(pdata._fn,function(i,val){val.plugin=pname;val.old=$.jstree._fn[i];$.jstree._fn[i]=function(){var rslt,func=val,args=Array.prototype.slice.call(arguments),evnt=new $.Event("before.jstree"),rlbk=false;do{if(func&&func.plugin&&$.inArray(func.plugin,this._get_settings().plugins)!==-1){break}func=func.old}while(func);if(!func){return}rslt=this.get_container().triggerHandler(evnt,{func:i,inst:this,args:args});if(rslt===false){return}if(typeof rslt!=="undefined"){args=rslt}if(i.indexOf("_")===0){rslt=func.apply(this,args)}else{rslt=func.apply($.extend({},this,{__callback:function(data){this.get_container().triggerHandler(i+".jstree",{inst:this,args:args,rslt:data,rlbk:rlbk})},__rollback:function(){rlbk=this.get_rollback();return rlbk},__call_old:function(replace_arguments){return func.old.apply(this,(replace_arguments?Array.prototype.slice.call(arguments,1):args))}}),args)}return rslt};$.jstree._fn[i].old=val.old;$.jstree._fn[i].plugin=pname})},rollback:function(rb){if(rb){if(!$.isArray(rb)){rb=[rb]}$.each(rb,function(i,val){instances[val.i].set_rollback(val.h,val.d)})}}};$.jstree._fn=$.jstree._instance.prototype={};$(function(){var u=navigator.userAgent.toLowerCase(),v=(u.match(/.+?(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[0,"0"])[1],css_string=".jstree ul, .jstree li { display:block; margin:0 0 0 0; padding:0 0 0 0; list-style-type:none; } .jstree li { display:block; min-height:18px; line-height:18px; white-space:nowrap; margin-left:18px; } .jstree-rtl li { margin-left:0; margin-right:18px; } .jstree > ul > li { margin-left:0px; } .jstree-rtl > ul > li { margin-right:0px; } .jstree ins { display:inline-block; text-decoration:none; width:18px; height:18px; margin:0 0 0 0; padding:0; } .jstree a { display:inline-block; line-height:16px; height:16px; color:black; white-space:nowrap; text-decoration:none; padding:1px 2px; margin:0; } .jstree a:focus { outline: none; } .jstree a > ins { height:16px; width:16px; } .jstree a > .jstree-icon { margin-right:3px; } .jstree-rtl a > .jstree-icon { margin-left:3px; margin-right:0; } li.jstree-open > ul { display:block; } li.jstree-closed > ul { display:none; } ";if(/msie/.test(u)&&parseInt(v,10)==6){is_ie6=true;css_string+=".jstree li { height:18px; margin-left:0; margin-right:0; } .jstree li li { margin-left:18px; } .jstree-rtl li li { margin-left:0px; margin-right:18px; } li.jstree-open ul { display:block; } li.jstree-closed ul { display:none !important; } .jstree li a { display:inline; border-width:0 !important; padding:0px 2px !important; } .jstree li a ins { height:16px; width:16px; margin-right:3px; } .jstree-rtl li a ins { margin-right:0px; margin-left:3px; } "}if(/msie/.test(u)&&parseInt(v,10)==7){css_string+=".jstree li a { border-width:0 !important; padding:0px 2px !important; } "}$.vakata.css.add_sheet({str:css_string})});$.jstree.plugin("core",{__init:function(){this.data.core.to_open=$.map($.makeArray(this.get_settings().core.initially_open),function(n){return"#"+n.toString().replace(/^#/,"").replace("\\/","/").replace("/","\\/")})},defaults:{html_titles:false,animation:500,initially_open:[],rtl:false,strings:{loading:"Loading ...",new_node:"New node"}},_fn:{init:function(){this.set_focus();if(this._get_settings().core.rtl){this.get_container().addClass("jstree-rtl").css("direction","rtl")}this.get_container().html("");this.data.core.li_height=this.get_container().find("ul li.jstree-closed, ul li.jstree-leaf").eq(0).height()||18;this.get_container().delegate("li > ins","click.jstree",$.proxy(function(event){var trgt=$(event.target);if(trgt.is("ins")&&event.pageY-trgt.offset().top ul > li:first-child")}if(!obj.length){return false}if(strict){return(obj.nextAll("li").size()>0)?obj.nextAll("li:eq(0)"):false}if(obj.hasClass("jstree-open")){return obj.find("li:eq(0)")}else{if(obj.nextAll("li").size()>0){return obj.nextAll("li:eq(0)")}else{return obj.parentsUntil(".jstree","li").next("li").eq(0)}}},_get_prev:function(obj,strict){obj=this._get_node(obj);if(obj===-1){return this.get_container().find("> ul > li:last-child")}if(!obj.length){return false}if(strict){return(obj.prevAll("li").length>0)?obj.prevAll("li:eq(0)"):false}if(obj.prev("li").length){obj=obj.prev("li").eq(0);while(obj.hasClass("jstree-open")){obj=obj.children("ul:eq(0)").children("li:last")}return obj}else{var o=obj.parentsUntil(".jstree","li:eq(0)");return o.length?o:false}},_get_parent:function(obj){obj=this._get_node(obj);if(obj==-1||!obj.length){return false}var o=obj.parentsUntil(".jstree","li:eq(0)");return o.length?o:-1},_get_children:function(obj){obj=this._get_node(obj);if(obj===-1){return this.get_container().children("ul:eq(0)").children("li")}if(!obj.length){return false}return obj.children("ul:eq(0)").children("li")},get_path:function(obj,id_mode){var p=[],_this=this;obj=this._get_node(obj);if(obj===-1||!obj||!obj.length){return false}obj.parentsUntil(".jstree","li").each(function(){p.push(id_mode?this.id:_this.get_text(this))});p.reverse();p.push(id_mode?obj.attr("id"):this.get_text(obj));return p},is_open:function(obj){obj=this._get_node(obj);return obj&&obj!==-1&&obj.hasClass("jstree-open")},is_closed:function(obj){obj=this._get_node(obj);return obj&&obj!==-1&&obj.hasClass("jstree-closed")},is_leaf:function(obj){obj=this._get_node(obj);return obj&&obj!==-1&&obj.hasClass("jstree-leaf")},open_node:function(obj,callback,skip_animation){obj=this._get_node(obj);if(!obj.length){return false}if(!obj.hasClass("jstree-closed")){if(callback){callback.call()}return false}var s=skip_animation||is_ie6?0:this._get_settings().core.animation,t=this;if(!this._is_loaded(obj)){obj.children("a").addClass("jstree-loading");this.load_node(obj,function(){t.open_node(obj,callback,skip_animation)},callback)}else{if(s){obj.children("ul").css("display","none")}obj.removeClass("jstree-closed").addClass("jstree-open").children("a").removeClass("jstree-loading");if(s){obj.children("ul").stop(true).slideDown(s,function(){this.style.display=""})}this.__callback({obj:obj});if(callback){callback.call()}}},close_node:function(obj,skip_animation){obj=this._get_node(obj);var s=skip_animation||is_ie6?0:this._get_settings().core.animation;if(!obj.length||!obj.hasClass("jstree-open")){return false}if(s){obj.children("ul").attr("style","display:block !important")}obj.removeClass("jstree-open").addClass("jstree-closed");if(s){obj.children("ul").stop(true).slideUp(s,function(){this.style.display=""})}this.__callback({obj:obj})},toggle_node:function(obj){obj=this._get_node(obj);if(obj.hasClass("jstree-closed")){return this.open_node(obj)}if(obj.hasClass("jstree-open")){return this.close_node(obj)}},open_all:function(obj,original_obj){obj=obj?this._get_node(obj):this.get_container();if(!obj||obj===-1){obj=this.get_container()}if(original_obj){obj=obj.find("li.jstree-closed")}else{original_obj=obj;if(obj.is(".jstree-closed")){obj=obj.find("li.jstree-closed").andSelf()}else{obj=obj.find("li.jstree-closed")}}var _this=this;obj.each(function(){var __this=this;if(!_this._is_loaded(this)){_this.open_node(this,function(){_this.open_all(__this,original_obj)},true)}else{_this.open_node(this,false,true)}});if(original_obj.find("li.jstree-closed").length===0){this.__callback({obj:original_obj})}},close_all:function(obj){var _this=this;obj=obj?this._get_node(obj):this.get_container();if(!obj||obj===-1){obj=this.get_container()}obj.find("li.jstree-open").andSelf().each(function(){_this.close_node(this)});this.__callback({obj:obj})},clean_node:function(obj){obj=obj&&obj!=-1?$(obj):this.get_container();obj=obj.is("li")?obj.find("li").andSelf():obj.find("li");obj.removeClass("jstree-last").filter("li:last-child").addClass("jstree-last").end().filter(":has(li)").not(".jstree-open").removeClass("jstree-leaf").addClass("jstree-closed");obj.not(".jstree-open, .jstree-closed").addClass("jstree-leaf").children("ul").remove();this.__callback({obj:obj})},get_rollback:function(){this.__callback();return{i:this.get_index(),h:this.get_container().children("ul").clone(true),d:this.data}},set_rollback:function(html,data){this.get_container().empty().append(html);this.data=data;this.__callback()},load_node:function(obj,s_call,e_call){this.__callback({obj:obj})},_is_loaded:function(obj){return true},create_node:function(obj,position,js,callback,is_loaded){obj=this._get_node(obj);position=typeof position==="undefined"?"last":position;var d=$("
  • "),s=this._get_settings().core,tmp;if(obj!==-1&&!obj.length){return false}if(!is_loaded&&!this._is_loaded(obj)){this.load_node(obj,function(){this.create_node(obj,position,js,callback,true)});return false}this.__rollback();if(typeof js==="string"){js={data:js}}if(!js){js={}}if(js.attr){d.attr(js.attr)}if(js.state){d.addClass("jstree-"+js.state)}if(!js.data){js.data=s.strings.new_node}if(!$.isArray(js.data)){tmp=js.data;js.data=[];js.data.push(tmp)}$.each(js.data,function(i,m){tmp=$("");if($.isFunction(m)){m=m.call(this,js)}if(typeof m=="string"){tmp.attr("href","#")[s.html_titles?"html":"text"](m)}else{if(!m.attr){m.attr={}}if(!m.attr.href){m.attr.href="#"}tmp.attr(m.attr)[s.html_titles?"html":"text"](m.title);if(m.language){tmp.addClass(m.language)}}tmp.prepend(" ");if(m.icon){if(m.icon.indexOf("/")===-1){tmp.children("ins").addClass(m.icon)}else{tmp.children("ins").css("background","url('"+m.icon+"') center center no-repeat")}}d.append(tmp)});d.prepend(" ");if(obj===-1){obj=this.get_container();if(position==="before"){position="first"}if(position==="after"){position="last"}}switch(position){case"before":obj.before(d);tmp=this._get_parent(obj);break;case"after":obj.after(d);tmp=this._get_parent(obj);break;case"inside":case"first":if(!obj.children("ul").length){obj.append("