From f6c9baab3efeec1d0efa151e276fc08d5b58f9e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Fri, 2 Aug 2013 21:40:29 +0200 Subject: [PATCH 01/49] rename myTool -> pocheTool and delete some stuff --- inc/MyTool.class.php | 265 ---------------------------------------- inc/class.messages.php | 231 ---------------------------------- inc/pocheTool.class.php | 101 +++++++++++++++ 3 files changed, 101 insertions(+), 496 deletions(-) delete mode 100644 inc/MyTool.class.php delete mode 100644 inc/class.messages.php create mode 100644 inc/pocheTool.class.php diff --git a/inc/MyTool.class.php b/inc/MyTool.class.php deleted file mode 100644 index b11f18e..0000000 --- a/inc/MyTool.class.php +++ /dev/null @@ -1,265 +0,0 @@ - - * @copyright 2013 - * @license http://www.wtfpl.net/ see COPYING file - */ - -class MyTool -{ - public static function initPhp() - { - define('START_TIME', microtime(true)); - - if (phpversion() < 5) { - die(_('Oops, it seems you don\'t have PHP 5.')); - } - - error_reporting(E_ALL); - - function stripslashesDeep($value) { - return is_array($value) - ? array_map('stripslashesDeep', $value) - : stripslashes($value); - } - - if (get_magic_quotes_gpc()) { - $_POST = array_map('stripslashesDeep', $_POST); - $_GET = array_map('stripslashesDeep', $_GET); - $_COOKIE = array_map('stripslashesDeep', $_COOKIE); - } - - ob_start(); - register_shutdown_function('ob_end_flush'); - } - - public static function isUrl($url) - { - // http://neo22s.com/check-if-url-exists-and-is-online-php/ - $pattern='|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i'; - - return preg_match($pattern, $url); - } - - public static function isEmail($email) - { - $pattern = "/^[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2, 4}$/i"; - - return (preg_match($pattern, $email)); - } - - public static function formatBBCode($text) - { - $replace = array( - '/\[m\](.+?)\[\/m\]/is' - => '/* moderate */', - '/\[b\](.+?)\[\/b\]/is' - => '$1', - '/\[i\](.+?)\[\/i\]/is' - => '$1', - '/\[s\](.+?)\[\/s\]/is' - => '$1', - '/\[u\](.+?)\[\/u\]/is' - => '$1', - '/\[url\](.+?)\[\/url]/is' - => '$1', - '/\[url=(\w+:\/\/[^\]]+)\](.+?)\[\/url]/is' - => '$2', - '/\[quote\](.+?)\[\/quote\]/is' - => '
$1
', - '/\[code\](.+?)\[\/code\]/is' - => '$1', - '/\[([^[]+)\|([^[]+)\]/is' - => '$1' - ); - $text = preg_replace( - array_keys($replace), - array_values($replace), - $text - ); - - return $text; - } - - public static function formatText($text) - { - $text = preg_replace_callback( - '/(.*?)<\/code_html>/is', - create_function( - '$matches', - 'return htmlspecialchars($matches[1]);' - ), - $text - ); - $text = preg_replace_callback( - '/(.*?)<\/code_php>/is', - create_function( - '$matches', - 'return highlight_string("", true);' - ), - $text - ); - $text = preg_replace('/
/is', '', $text); - - $text = preg_replace( - '#(^|\s)([a-z]+://([^\s\w/]?[\w/])*)(\s|$)#im', - '\\1\\2\\4', - $text - ); - $text = preg_replace( - '#(^|\s)wp:?([a-z]{2}|):([\w]+)#im', - '\\1\\3', - $text - ); - $text = str_replace( - 'http://.wikipedia.org/wiki/', - 'http://www.wikipedia.org/wiki/', - $text - ); - $text = str_replace('\wp:', 'wp:', $text); - $text = str_replace('\http:', 'http:', $text); - $text = MyTool::formatBBCode($text); - $text = nl2br($text); - - return $text; - } - - public static function getUrl() - { - $https = (!empty($_SERVER['HTTPS']) - && (strtolower($_SERVER['HTTPS']) == 'on')) - || (isset($_SERVER["SERVER_PORT"]) - && $_SERVER["SERVER_PORT"] == '443'); // HTTPS detection. - $serverport = (!isset($_SERVER["SERVER_PORT"]) - || $_SERVER["SERVER_PORT"] == '80' - || ($https && $_SERVER["SERVER_PORT"] == '443') - ? '' - : ':' . $_SERVER["SERVER_PORT"]); - - $scriptname = str_replace('/index.php', '/', $_SERVER["SCRIPT_NAME"]); - - if (!isset($_SERVER["SERVER_NAME"])) { - return $scriptname; - } - - return 'http' . ($https ? 's' : '') . '://' - . $_SERVER["SERVER_NAME"] . $serverport . $scriptname; - } - - public static function rrmdir($dir) - { - if (is_dir($dir) && ($d = @opendir($dir))) { - while (($file = @readdir($d)) !== false) { - if ( $file == '.' || $file == '..' ) { - continue; - } else { - unlink($dir . '/' . $file); - } - } - } - } - - public static function humanBytes($bytes) - { - $siPrefix = array( 'bytes', 'KB', 'MB', 'GB', 'TB', 'EB', 'ZB', 'YB' ); - $base = 1024; - $class = min((int) log($bytes, $base), count($siPrefix) - 1); - $val = sprintf('%1.2f', $bytes / pow($base, $class)); - - return $val . ' ' . $siPrefix[$class]; - } - - public static function returnBytes($val) - { - $val = trim($val); - $last = strtolower($val[strlen($val)-1]); - switch($last) - { - case 'g': $val *= 1024; - case 'm': $val *= 1024; - case 'k': $val *= 1024; - } - - return $val; - } - - public static function getMaxFileSize() - { - $sizePostMax = MyTool::returnBytes(ini_get('post_max_size')); - $sizeUploadMax = MyTool::returnBytes(ini_get('upload_max_filesize')); - - // Return the smaller of two: - return min($sizePostMax, $sizeUploadMax); - } - - public static function smallHash($text) - { - $t = rtrim(base64_encode(hash('crc32', $text, true)), '='); - // Get rid of characters which need encoding in URLs. - $t = str_replace('+', '-', $t); - $t = str_replace('/', '_', $t); - $t = str_replace('=', '@', $t); - - return $t; - } - - public static function renderJson($data) - { - header('Cache-Control: no-cache, must-revalidate'); - header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); - header('Content-type: application/json; charset=UTF-8'); - - echo json_encode($data); - exit(); - } - - public static function grabToLocal($url, $file, $force = false) - { - if ((!file_exists($file) || $force) && in_array('curl', get_loaded_extensions())){ - $ch = curl_init ($url); - curl_setopt($ch, CURLOPT_HEADER, false); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_BINARYTRANSFER, true); - $raw = curl_exec($ch); - if (curl_getinfo($ch, CURLINFO_HTTP_CODE) == 200) { - $fp = fopen($file, 'x'); - fwrite($fp, $raw); - fclose($fp); - } - curl_close ($ch); - } - } - - public static function redirect($rurl = '') - { - if ($rurl === '') { - // if (!empty($_SERVER['HTTP_REFERER']) && strcmp(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_HOST),$_SERVER['SERVER_NAME'])==0) - $rurl = (empty($_SERVER['HTTP_REFERER'])?'?':$_SERVER['HTTP_REFERER']); - if (isset($_POST['returnurl'])) { - $rurl = $_POST['returnurl']; - } - } - - // prevent loop - if (empty($rurl) || parse_url($rurl, PHP_URL_QUERY) === $_SERVER['QUERY_STRING']) { - $rurl = MyTool::getUrl(); - } - - if (substr($rurl, 0, 1) !== '?') { - $ref = MyTool::getUrl(); - if (substr($rurl, 0, strlen($ref)) !== $ref) { - $rurl = $ref; - } - } - header('Location: '.$rurl); - exit(); - } - - public static function silence_errors($num, $str) - { -// No-op - } -} \ No newline at end of file diff --git a/inc/class.messages.php b/inc/class.messages.php deleted file mode 100644 index 6d515bf..0000000 --- a/inc/class.messages.php +++ /dev/null @@ -1,231 +0,0 @@ -\n%s\n"; - var $msgBefore = '

'; - var $msgAfter = "

\n"; - - - /** - * Constructor - * @author Mike Everhart - */ - public function __construct() { - - // Generate a unique ID for this user and session - $this->msgId = md5(uniqid()); - - // Create the session array if it doesnt already exist - if( !array_key_exists('flash_messages', $_SESSION) ) $_SESSION['flash_messages'] = array(); - - } - - /** - * Add a message to the queue - * - * @author Mike Everhart - * - * @param string $type The type of message to add - * @param string $message The message - * @param string $redirect_to (optional) If set, the user will be redirected to this URL - * @return bool - * - */ - public function add($type, $message, $redirect_to=null) { - - if( !isset($_SESSION['flash_messages']) ) return false; - - if( !isset($type) || !isset($message[0]) ) return false; - - // Replace any shorthand codes with their full version - if( strlen(trim($type)) == 1 ) { - $type = str_replace( array('h', 'i', 'w', 'e', 's'), array('help', 'info', 'warning', 'error', 'success'), $type ); - - // Backwards compatibility... - } elseif( $type == 'information' ) { - $type = 'info'; - } - - // Make sure it's a valid message type - if( !in_array($type, $this->msgTypes) ) die('"' . strip_tags($type) . '" is not a valid message type!' ); - - // If the session array doesn't exist, create it - if( !array_key_exists( $type, $_SESSION['flash_messages'] ) ) $_SESSION['flash_messages'][$type] = array(); - - $_SESSION['flash_messages'][$type][] = $message; - - if( !is_null($redirect_to) ) { - header("Location: $redirect_to"); - exit(); - } - - return true; - - } - - //----------------------------------------------------------------------------------------------- - // display() - // print queued messages to the screen - //----------------------------------------------------------------------------------------------- - /** - * Display the queued messages - * - * @author Mike Everhart - * - * @param string $type Which messages to display - * @param bool $print True = print the messages on the screen - * @return mixed - * - */ - public function display($type='all', $print=true) { - $messages = ''; - $data = ''; - - if( !isset($_SESSION['flash_messages']) ) return false; - - if( $type == 'g' || $type == 'growl' ) { - $this->displayGrowlMessages(); - return true; - } - - // Print a certain type of message? - if( in_array($type, $this->msgTypes) ) { - foreach( $_SESSION['flash_messages'][$type] as $msg ) { - $messages .= $this->msgBefore . $msg . $this->msgAfter; - } - - $data .= sprintf($this->msgWrapper, $this->msgClass, $type, $messages); - - // Clear the viewed messages - $this->clear($type); - - // Print ALL queued messages - } elseif( $type == 'all' ) { - foreach( $_SESSION['flash_messages'] as $type => $msgArray ) { - $messages = ''; - foreach( $msgArray as $msg ) { - $messages .= $this->msgBefore . $msg . $this->msgAfter; - } - $data .= sprintf($this->msgWrapper, $this->msgClass, $type, $messages); - } - - // Clear ALL of the messages - $this->clear(); - - // Invalid Message Type? - } else { - return false; - } - - // Print everything to the screen or return the data - if( $print ) { - echo $data; - } else { - return $data; - } - } - - - /** - * Check to see if there are any queued error messages - * - * @author Mike Everhart - * - * @return bool true = There ARE error messages - * false = There are NOT any error messages - * - */ - public function hasErrors() { - return empty($_SESSION['flash_messages']['error']) ? false : true; - } - - /** - * Check to see if there are any ($type) messages queued - * - * @author Mike Everhart - * - * @param string $type The type of messages to check for - * @return bool - * - */ - public function hasMessages($type=null) { - if( !is_null($type) ) { - if( !empty($_SESSION['flash_messages'][$type]) ) return $_SESSION['flash_messages'][$type]; - } else { - foreach( $this->msgTypes as $type ) { - if( !empty($_SESSION['flash_messages']) ) return true; - } - } - return false; - } - - /** - * Clear messages from the session data - * - * @author Mike Everhart - * - * @param string $type The type of messages to clear - * @return bool - * - */ - public function clear($type='all') { - if( $type == 'all' ) { - unset($_SESSION['flash_messages']); - } else { - unset($_SESSION['flash_messages'][$type]); - } - return true; - } - - public function __toString() { return $this->hasMessages(); } - - public function __destruct() { - //$this->clear(); - } - - -} // end class -?> \ No newline at end of file diff --git a/inc/pocheTool.class.php b/inc/pocheTool.class.php new file mode 100644 index 0000000..9aab76b --- /dev/null +++ b/inc/pocheTool.class.php @@ -0,0 +1,101 @@ + + * @copyright 2013 + * @license http://www.wtfpl.net/ see COPYING file + */ + +class pocheTools +{ + public static function initPhp() + { + define('START_TIME', microtime(true)); + + if (phpversion() < 5) { + die(_('Oops, it seems you don\'t have PHP 5.')); + } + + error_reporting(E_ALL); + + function stripslashesDeep($value) { + return is_array($value) + ? array_map('stripslashesDeep', $value) + : stripslashes($value); + } + + if (get_magic_quotes_gpc()) { + $_POST = array_map('stripslashesDeep', $_POST); + $_GET = array_map('stripslashesDeep', $_GET); + $_COOKIE = array_map('stripslashesDeep', $_COOKIE); + } + + ob_start(); + register_shutdown_function('ob_end_flush'); + } + + public static function isUrl($url) + { + // http://neo22s.com/check-if-url-exists-and-is-online-php/ + $pattern='|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i'; + + return preg_match($pattern, $url); + } + + public static function getUrl() + { + $https = (!empty($_SERVER['HTTPS']) + && (strtolower($_SERVER['HTTPS']) == 'on')) + || (isset($_SERVER["SERVER_PORT"]) + && $_SERVER["SERVER_PORT"] == '443'); // HTTPS detection. + $serverport = (!isset($_SERVER["SERVER_PORT"]) + || $_SERVER["SERVER_PORT"] == '80' + || ($https && $_SERVER["SERVER_PORT"] == '443') + ? '' : ':' . $_SERVER["SERVER_PORT"]); + + $scriptname = str_replace('/index.php', '/', $_SERVER["SCRIPT_NAME"]); + + if (!isset($_SERVER["SERVER_NAME"])) { + return $scriptname; + } + + return 'http' . ($https ? 's' : '') . '://' + . $_SERVER["SERVER_NAME"] . $serverport . $scriptname; + } + + public static function renderJson($data) + { + header('Cache-Control: no-cache, must-revalidate'); + header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); + header('Content-type: application/json; charset=UTF-8'); + + echo json_encode($data); + exit(); + } + + public static function redirect($rurl = '') + { + if ($rurl === '') { + $rurl = (empty($_SERVER['HTTP_REFERER'])?'?':$_SERVER['HTTP_REFERER']); + if (isset($_POST['returnurl'])) { + $rurl = $_POST['returnurl']; + } + } + + // prevent loop + if (empty($rurl) || parse_url($rurl, PHP_URL_QUERY) === $_SERVER['QUERY_STRING']) { + $rurl = pocheTool::getUrl(); + } + + if (substr($rurl, 0, 1) !== '?') { + $ref = pocheTool::getUrl(); + if (substr($rurl, 0, strlen($ref)) !== $ref) { + $rurl = $ref; + } + } + header('Location: '.$rurl); + exit(); + } +} \ No newline at end of file From a4565e88edbc8e3bd092a475469769c86a4c350c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Fri, 2 Aug 2013 22:40:51 +0200 Subject: [PATCH 02/49] add Twig & refactor poche --- inc/{ => 3rdparty}/Encoding.php | 0 inc/{ => 3rdparty}/JSLikeHTMLElement.php | 0 inc/{ => 3rdparty}/Readability.php | 0 inc/{ => 3rdparty}/Session.class.php | 0 inc/{ => 3rdparty}/simple_html_dom.php | 0 inc/Twig/Autoloader.php | 48 + inc/Twig/Compiler.php | 267 ++++ inc/Twig/CompilerInterface.php | 35 + inc/Twig/Environment.php | 1224 +++++++++++++++ inc/Twig/Error.php | 239 +++ inc/Twig/Error/Loader.php | 31 + inc/Twig/Error/Runtime.php | 20 + inc/Twig/Error/Syntax.php | 20 + inc/Twig/ExistsLoaderInterface.php | 28 + inc/Twig/ExpressionParser.php | 600 ++++++++ inc/Twig/Extension.php | 93 ++ inc/Twig/Extension/Core.php | 1355 +++++++++++++++++ inc/Twig/Extension/Debug.php | 71 + inc/Twig/Extension/Escaper.php | 107 ++ inc/Twig/Extension/Optimizer.php | 35 + inc/Twig/Extension/Sandbox.php | 112 ++ inc/Twig/Extension/Staging.php | 113 ++ inc/Twig/Extension/StringLoader.php | 64 + inc/Twig/ExtensionInterface.php | 83 + inc/Twig/Extensions/Autoloader.php | 45 + inc/Twig/Extensions/Extension/Debug.php | 34 + inc/Twig/Extensions/Extension/I18n.php | 44 + inc/Twig/Extensions/Extension/Intl.php | 66 + inc/Twig/Extensions/Extension/Text.php | 109 ++ inc/Twig/Extensions/Gettext/Extractor.php | 95 ++ .../Extensions/Gettext/Loader/Filesystem.php | 58 + .../Routing/Generator/UrlGenerator.php | 39 + .../Extensions/Gettext/Test/ExtractorTest.php | 123 ++ .../Gettext/Test/Fixtures/twig/empty.twig | 1 + .../Gettext/Test/Fixtures/twig/plural.twig | 5 + .../Gettext/Test/Fixtures/twig/singular.twig | 9 + inc/Twig/Extensions/Grammar.php | 30 + inc/Twig/Extensions/Grammar/Arguments.php | 22 + inc/Twig/Extensions/Grammar/Array.php | 22 + inc/Twig/Extensions/Grammar/Body.php | 39 + inc/Twig/Extensions/Grammar/Boolean.php | 24 + inc/Twig/Extensions/Grammar/Constant.php | 37 + inc/Twig/Extensions/Grammar/Expression.php | 22 + inc/Twig/Extensions/Grammar/Hash.php | 22 + inc/Twig/Extensions/Grammar/Number.php | 24 + inc/Twig/Extensions/Grammar/Optional.php | 69 + inc/Twig/Extensions/Grammar/Switch.php | 24 + inc/Twig/Extensions/Grammar/Tag.php | 56 + inc/Twig/Extensions/GrammarInterface.php | 18 + inc/Twig/Extensions/Node/Debug.php | 69 + inc/Twig/Extensions/Node/Trans.php | 133 ++ inc/Twig/Extensions/SimpleTokenParser.php | 132 ++ inc/Twig/Extensions/TokenParser/Debug.php | 42 + inc/Twig/Extensions/TokenParser/Trans.php | 80 + inc/Twig/Filter.php | 81 + inc/Twig/Filter/Function.php | 37 + inc/Twig/Filter/Method.php | 39 + inc/Twig/Filter/Node.php | 39 + inc/Twig/FilterCallableInterface.php | 23 + inc/Twig/FilterInterface.php | 42 + inc/Twig/Function.php | 71 + inc/Twig/Function/Function.php | 38 + inc/Twig/Function/Method.php | 40 + inc/Twig/Function/Node.php | 39 + inc/Twig/FunctionCallableInterface.php | 23 + inc/Twig/FunctionInterface.php | 39 + inc/Twig/Gettext/Extractor.php | 95 ++ inc/Twig/Gettext/Loader/Filesystem.php | 58 + .../Routing/Generator/UrlGenerator.php | 39 + inc/Twig/Gettext/Test/ExtractorTest.php | 123 ++ .../Gettext/Test/Fixtures/twig/empty.twig | 1 + .../Gettext/Test/Fixtures/twig/plural.twig | 5 + .../Gettext/Test/Fixtures/twig/singular.twig | 9 + inc/Twig/Lexer.php | 408 +++++ inc/Twig/LexerInterface.php | 29 + inc/Twig/Loader/Array.php | 98 ++ inc/Twig/Loader/Chain.php | 139 ++ inc/Twig/Loader/Filesystem.php | 223 +++ inc/Twig/Loader/String.php | 59 + inc/Twig/LoaderInterface.php | 52 + inc/Twig/Markup.php | 37 + inc/Twig/Node.php | 226 +++ inc/Twig/Node/AutoEscape.php | 39 + inc/Twig/Node/Block.php | 44 + inc/Twig/Node/BlockReference.php | 37 + inc/Twig/Node/Body.php | 19 + inc/Twig/Node/Do.php | 38 + inc/Twig/Node/Embed.php | 38 + inc/Twig/Node/Expression.php | 20 + inc/Twig/Node/Expression/Array.php | 86 ++ inc/Twig/Node/Expression/AssignName.php | 28 + inc/Twig/Node/Expression/Binary.php | 40 + inc/Twig/Node/Expression/Binary/Add.php | 18 + inc/Twig/Node/Expression/Binary/And.php | 18 + .../Node/Expression/Binary/BitwiseAnd.php | 18 + inc/Twig/Node/Expression/Binary/BitwiseOr.php | 18 + .../Node/Expression/Binary/BitwiseXor.php | 18 + inc/Twig/Node/Expression/Binary/Concat.php | 18 + inc/Twig/Node/Expression/Binary/Div.php | 18 + inc/Twig/Node/Expression/Binary/Equal.php | 17 + inc/Twig/Node/Expression/Binary/FloorDiv.php | 29 + inc/Twig/Node/Expression/Binary/Greater.php | 17 + .../Node/Expression/Binary/GreaterEqual.php | 17 + inc/Twig/Node/Expression/Binary/In.php | 33 + inc/Twig/Node/Expression/Binary/Less.php | 17 + inc/Twig/Node/Expression/Binary/LessEqual.php | 17 + inc/Twig/Node/Expression/Binary/Mod.php | 18 + inc/Twig/Node/Expression/Binary/Mul.php | 18 + inc/Twig/Node/Expression/Binary/NotEqual.php | 17 + inc/Twig/Node/Expression/Binary/NotIn.php | 33 + inc/Twig/Node/Expression/Binary/Or.php | 18 + inc/Twig/Node/Expression/Binary/Power.php | 33 + inc/Twig/Node/Expression/Binary/Range.php | 33 + inc/Twig/Node/Expression/Binary/Sub.php | 18 + inc/Twig/Node/Expression/BlockReference.php | 51 + inc/Twig/Node/Expression/Call.php | 178 +++ inc/Twig/Node/Expression/Conditional.php | 31 + inc/Twig/Node/Expression/Constant.php | 23 + .../Node/Expression/ExtensionReference.php | 33 + inc/Twig/Node/Expression/Filter.php | 36 + inc/Twig/Node/Expression/Filter/Default.php | 43 + inc/Twig/Node/Expression/Function.php | 35 + inc/Twig/Node/Expression/GetAttr.php | 53 + inc/Twig/Node/Expression/MethodCall.php | 41 + inc/Twig/Node/Expression/Name.php | 88 ++ inc/Twig/Node/Expression/Parent.php | 47 + inc/Twig/Node/Expression/TempName.php | 26 + inc/Twig/Node/Expression/Test.php | 32 + inc/Twig/Node/Expression/Test/Constant.php | 46 + inc/Twig/Node/Expression/Test/Defined.php | 54 + inc/Twig/Node/Expression/Test/Divisibleby.php | 33 + inc/Twig/Node/Expression/Test/Even.php | 32 + inc/Twig/Node/Expression/Test/Null.php | 31 + inc/Twig/Node/Expression/Test/Odd.php | 32 + inc/Twig/Node/Expression/Test/Sameas.php | 29 + inc/Twig/Node/Expression/Unary.php | 30 + inc/Twig/Node/Expression/Unary/Neg.php | 18 + inc/Twig/Node/Expression/Unary/Not.php | 18 + inc/Twig/Node/Expression/Unary/Pos.php | 18 + inc/Twig/Node/Flush.php | 36 + inc/Twig/Node/For.php | 112 ++ inc/Twig/Node/ForLoop.php | 55 + inc/Twig/Node/If.php | 66 + inc/Twig/Node/Import.php | 50 + inc/Twig/Node/Include.php | 99 ++ inc/Twig/Node/Macro.php | 96 ++ inc/Twig/Node/Module.php | 371 +++++ inc/Twig/Node/Print.php | 39 + inc/Twig/Node/Sandbox.php | 47 + inc/Twig/Node/SandboxedModule.php | 60 + inc/Twig/Node/SandboxedPrint.php | 59 + inc/Twig/Node/Set.php | 101 ++ inc/Twig/Node/SetTemp.php | 35 + inc/Twig/Node/Spaceless.php | 40 + inc/Twig/Node/Text.php | 39 + inc/Twig/NodeInterface.php | 30 + inc/Twig/NodeOutputInterface.php | 19 + inc/Twig/NodeTraverser.php | 88 ++ inc/Twig/NodeVisitor/Escaper.php | 167 ++ inc/Twig/NodeVisitor/Optimizer.php | 246 +++ inc/Twig/NodeVisitor/SafeAnalysis.php | 131 ++ inc/Twig/NodeVisitor/Sandbox.php | 92 ++ inc/Twig/NodeVisitorInterface.php | 47 + inc/Twig/Parser.php | 394 +++++ inc/Twig/ParserInterface.php | 28 + inc/Twig/Sandbox/SecurityError.php | 19 + inc/Twig/Sandbox/SecurityPolicy.php | 119 ++ inc/Twig/Sandbox/SecurityPolicyInterface.php | 24 + inc/Twig/SimpleFilter.php | 94 ++ inc/Twig/SimpleFunction.php | 84 + inc/Twig/SimpleTest.php | 46 + inc/Twig/Template.php | 455 ++++++ inc/Twig/TemplateInterface.php | 47 + inc/Twig/Test.php | 34 + inc/Twig/Test/Function.php | 35 + inc/Twig/Test/IntegrationTestCase.php | 154 ++ inc/Twig/Test/Method.php | 37 + inc/Twig/Test/Node.php | 37 + inc/Twig/Test/NodeTestCase.php | 58 + inc/Twig/TestCallableInterface.php | 21 + inc/Twig/TestInterface.php | 26 + inc/Twig/Token.php | 218 +++ inc/Twig/TokenParser.php | 33 + inc/Twig/TokenParser/AutoEscape.php | 89 ++ inc/Twig/TokenParser/Block.php | 83 + inc/Twig/TokenParser/Do.php | 42 + inc/Twig/TokenParser/Embed.php | 66 + inc/Twig/TokenParser/Extends.php | 52 + inc/Twig/TokenParser/Filter.php | 61 + inc/Twig/TokenParser/Flush.php | 42 + inc/Twig/TokenParser/For.php | 136 ++ inc/Twig/TokenParser/From.php | 74 + inc/Twig/TokenParser/If.php | 94 ++ inc/Twig/TokenParser/Import.php | 49 + inc/Twig/TokenParser/Include.php | 80 + inc/Twig/TokenParser/Macro.php | 68 + inc/Twig/TokenParser/Sandbox.php | 68 + inc/Twig/TokenParser/Set.php | 84 + inc/Twig/TokenParser/Spaceless.php | 59 + inc/Twig/TokenParser/Use.php | 82 + inc/Twig/TokenParserBroker.php | 136 ++ inc/Twig/TokenParserBrokerInterface.php | 45 + inc/Twig/TokenParserInterface.php | 41 + inc/Twig/TokenStream.php | 144 ++ inc/config.php | 76 +- inc/functions.php | 400 ----- inc/poche/pocheCore.php | 257 ++++ inc/poche/pochePictures.php | 114 ++ inc/{ => poche}/pocheTool.class.php | 69 +- inc/rain.tpl.class.php | 1043 ------------- index.php | 46 +- locale/fr_FR.UTF8/LC_MESSAGES/fr_FR.UTF8.mo | Bin 2775 -> 2777 bytes locale/fr_FR.UTF8/LC_MESSAGES/fr_FR.UTF8.po | 20 +- tpl/home.html | 6 +- tpl/install.twig | 28 + tpl/js.html | 38 +- tpl/layout.twig | 58 + tpl/login.twig | 31 + 218 files changed, 16372 insertions(+), 1555 deletions(-) rename inc/{ => 3rdparty}/Encoding.php (100%) rename inc/{ => 3rdparty}/JSLikeHTMLElement.php (100%) rename inc/{ => 3rdparty}/Readability.php (100%) rename inc/{ => 3rdparty}/Session.class.php (100%) rename inc/{ => 3rdparty}/simple_html_dom.php (100%) create mode 100644 inc/Twig/Autoloader.php create mode 100644 inc/Twig/Compiler.php create mode 100644 inc/Twig/CompilerInterface.php create mode 100644 inc/Twig/Environment.php create mode 100644 inc/Twig/Error.php create mode 100644 inc/Twig/Error/Loader.php create mode 100644 inc/Twig/Error/Runtime.php create mode 100644 inc/Twig/Error/Syntax.php create mode 100644 inc/Twig/ExistsLoaderInterface.php create mode 100644 inc/Twig/ExpressionParser.php create mode 100644 inc/Twig/Extension.php create mode 100644 inc/Twig/Extension/Core.php create mode 100644 inc/Twig/Extension/Debug.php create mode 100644 inc/Twig/Extension/Escaper.php create mode 100644 inc/Twig/Extension/Optimizer.php create mode 100644 inc/Twig/Extension/Sandbox.php create mode 100644 inc/Twig/Extension/Staging.php create mode 100644 inc/Twig/Extension/StringLoader.php create mode 100644 inc/Twig/ExtensionInterface.php create mode 100644 inc/Twig/Extensions/Autoloader.php create mode 100644 inc/Twig/Extensions/Extension/Debug.php create mode 100644 inc/Twig/Extensions/Extension/I18n.php create mode 100644 inc/Twig/Extensions/Extension/Intl.php create mode 100644 inc/Twig/Extensions/Extension/Text.php create mode 100644 inc/Twig/Extensions/Gettext/Extractor.php create mode 100644 inc/Twig/Extensions/Gettext/Loader/Filesystem.php create mode 100644 inc/Twig/Extensions/Gettext/Routing/Generator/UrlGenerator.php create mode 100644 inc/Twig/Extensions/Gettext/Test/ExtractorTest.php create mode 100644 inc/Twig/Extensions/Gettext/Test/Fixtures/twig/empty.twig create mode 100644 inc/Twig/Extensions/Gettext/Test/Fixtures/twig/plural.twig create mode 100644 inc/Twig/Extensions/Gettext/Test/Fixtures/twig/singular.twig create mode 100644 inc/Twig/Extensions/Grammar.php create mode 100644 inc/Twig/Extensions/Grammar/Arguments.php create mode 100644 inc/Twig/Extensions/Grammar/Array.php create mode 100644 inc/Twig/Extensions/Grammar/Body.php create mode 100644 inc/Twig/Extensions/Grammar/Boolean.php create mode 100644 inc/Twig/Extensions/Grammar/Constant.php create mode 100644 inc/Twig/Extensions/Grammar/Expression.php create mode 100644 inc/Twig/Extensions/Grammar/Hash.php create mode 100644 inc/Twig/Extensions/Grammar/Number.php create mode 100644 inc/Twig/Extensions/Grammar/Optional.php create mode 100644 inc/Twig/Extensions/Grammar/Switch.php create mode 100644 inc/Twig/Extensions/Grammar/Tag.php create mode 100644 inc/Twig/Extensions/GrammarInterface.php create mode 100644 inc/Twig/Extensions/Node/Debug.php create mode 100644 inc/Twig/Extensions/Node/Trans.php create mode 100644 inc/Twig/Extensions/SimpleTokenParser.php create mode 100644 inc/Twig/Extensions/TokenParser/Debug.php create mode 100644 inc/Twig/Extensions/TokenParser/Trans.php create mode 100644 inc/Twig/Filter.php create mode 100644 inc/Twig/Filter/Function.php create mode 100644 inc/Twig/Filter/Method.php create mode 100644 inc/Twig/Filter/Node.php create mode 100644 inc/Twig/FilterCallableInterface.php create mode 100644 inc/Twig/FilterInterface.php create mode 100644 inc/Twig/Function.php create mode 100644 inc/Twig/Function/Function.php create mode 100644 inc/Twig/Function/Method.php create mode 100644 inc/Twig/Function/Node.php create mode 100644 inc/Twig/FunctionCallableInterface.php create mode 100644 inc/Twig/FunctionInterface.php create mode 100644 inc/Twig/Gettext/Extractor.php create mode 100644 inc/Twig/Gettext/Loader/Filesystem.php create mode 100644 inc/Twig/Gettext/Routing/Generator/UrlGenerator.php create mode 100644 inc/Twig/Gettext/Test/ExtractorTest.php create mode 100644 inc/Twig/Gettext/Test/Fixtures/twig/empty.twig create mode 100644 inc/Twig/Gettext/Test/Fixtures/twig/plural.twig create mode 100644 inc/Twig/Gettext/Test/Fixtures/twig/singular.twig create mode 100644 inc/Twig/Lexer.php create mode 100644 inc/Twig/LexerInterface.php create mode 100644 inc/Twig/Loader/Array.php create mode 100644 inc/Twig/Loader/Chain.php create mode 100644 inc/Twig/Loader/Filesystem.php create mode 100644 inc/Twig/Loader/String.php create mode 100644 inc/Twig/LoaderInterface.php create mode 100644 inc/Twig/Markup.php create mode 100644 inc/Twig/Node.php create mode 100644 inc/Twig/Node/AutoEscape.php create mode 100644 inc/Twig/Node/Block.php create mode 100644 inc/Twig/Node/BlockReference.php create mode 100644 inc/Twig/Node/Body.php create mode 100644 inc/Twig/Node/Do.php create mode 100644 inc/Twig/Node/Embed.php create mode 100644 inc/Twig/Node/Expression.php create mode 100644 inc/Twig/Node/Expression/Array.php create mode 100644 inc/Twig/Node/Expression/AssignName.php create mode 100644 inc/Twig/Node/Expression/Binary.php create mode 100644 inc/Twig/Node/Expression/Binary/Add.php create mode 100644 inc/Twig/Node/Expression/Binary/And.php create mode 100644 inc/Twig/Node/Expression/Binary/BitwiseAnd.php create mode 100644 inc/Twig/Node/Expression/Binary/BitwiseOr.php create mode 100644 inc/Twig/Node/Expression/Binary/BitwiseXor.php create mode 100644 inc/Twig/Node/Expression/Binary/Concat.php create mode 100644 inc/Twig/Node/Expression/Binary/Div.php create mode 100644 inc/Twig/Node/Expression/Binary/Equal.php create mode 100644 inc/Twig/Node/Expression/Binary/FloorDiv.php create mode 100644 inc/Twig/Node/Expression/Binary/Greater.php create mode 100644 inc/Twig/Node/Expression/Binary/GreaterEqual.php create mode 100644 inc/Twig/Node/Expression/Binary/In.php create mode 100644 inc/Twig/Node/Expression/Binary/Less.php create mode 100644 inc/Twig/Node/Expression/Binary/LessEqual.php create mode 100644 inc/Twig/Node/Expression/Binary/Mod.php create mode 100644 inc/Twig/Node/Expression/Binary/Mul.php create mode 100644 inc/Twig/Node/Expression/Binary/NotEqual.php create mode 100644 inc/Twig/Node/Expression/Binary/NotIn.php create mode 100644 inc/Twig/Node/Expression/Binary/Or.php create mode 100644 inc/Twig/Node/Expression/Binary/Power.php create mode 100644 inc/Twig/Node/Expression/Binary/Range.php create mode 100644 inc/Twig/Node/Expression/Binary/Sub.php create mode 100644 inc/Twig/Node/Expression/BlockReference.php create mode 100644 inc/Twig/Node/Expression/Call.php create mode 100644 inc/Twig/Node/Expression/Conditional.php create mode 100644 inc/Twig/Node/Expression/Constant.php create mode 100644 inc/Twig/Node/Expression/ExtensionReference.php create mode 100644 inc/Twig/Node/Expression/Filter.php create mode 100644 inc/Twig/Node/Expression/Filter/Default.php create mode 100644 inc/Twig/Node/Expression/Function.php create mode 100644 inc/Twig/Node/Expression/GetAttr.php create mode 100644 inc/Twig/Node/Expression/MethodCall.php create mode 100644 inc/Twig/Node/Expression/Name.php create mode 100644 inc/Twig/Node/Expression/Parent.php create mode 100644 inc/Twig/Node/Expression/TempName.php create mode 100644 inc/Twig/Node/Expression/Test.php create mode 100644 inc/Twig/Node/Expression/Test/Constant.php create mode 100644 inc/Twig/Node/Expression/Test/Defined.php create mode 100644 inc/Twig/Node/Expression/Test/Divisibleby.php create mode 100644 inc/Twig/Node/Expression/Test/Even.php create mode 100644 inc/Twig/Node/Expression/Test/Null.php create mode 100644 inc/Twig/Node/Expression/Test/Odd.php create mode 100644 inc/Twig/Node/Expression/Test/Sameas.php create mode 100644 inc/Twig/Node/Expression/Unary.php create mode 100644 inc/Twig/Node/Expression/Unary/Neg.php create mode 100644 inc/Twig/Node/Expression/Unary/Not.php create mode 100644 inc/Twig/Node/Expression/Unary/Pos.php create mode 100644 inc/Twig/Node/Flush.php create mode 100644 inc/Twig/Node/For.php create mode 100644 inc/Twig/Node/ForLoop.php create mode 100644 inc/Twig/Node/If.php create mode 100644 inc/Twig/Node/Import.php create mode 100644 inc/Twig/Node/Include.php create mode 100644 inc/Twig/Node/Macro.php create mode 100644 inc/Twig/Node/Module.php create mode 100644 inc/Twig/Node/Print.php create mode 100644 inc/Twig/Node/Sandbox.php create mode 100644 inc/Twig/Node/SandboxedModule.php create mode 100644 inc/Twig/Node/SandboxedPrint.php create mode 100644 inc/Twig/Node/Set.php create mode 100644 inc/Twig/Node/SetTemp.php create mode 100644 inc/Twig/Node/Spaceless.php create mode 100644 inc/Twig/Node/Text.php create mode 100644 inc/Twig/NodeInterface.php create mode 100644 inc/Twig/NodeOutputInterface.php create mode 100644 inc/Twig/NodeTraverser.php create mode 100644 inc/Twig/NodeVisitor/Escaper.php create mode 100644 inc/Twig/NodeVisitor/Optimizer.php create mode 100644 inc/Twig/NodeVisitor/SafeAnalysis.php create mode 100644 inc/Twig/NodeVisitor/Sandbox.php create mode 100644 inc/Twig/NodeVisitorInterface.php create mode 100644 inc/Twig/Parser.php create mode 100644 inc/Twig/ParserInterface.php create mode 100644 inc/Twig/Sandbox/SecurityError.php create mode 100644 inc/Twig/Sandbox/SecurityPolicy.php create mode 100644 inc/Twig/Sandbox/SecurityPolicyInterface.php create mode 100644 inc/Twig/SimpleFilter.php create mode 100644 inc/Twig/SimpleFunction.php create mode 100644 inc/Twig/SimpleTest.php create mode 100644 inc/Twig/Template.php create mode 100644 inc/Twig/TemplateInterface.php create mode 100644 inc/Twig/Test.php create mode 100644 inc/Twig/Test/Function.php create mode 100644 inc/Twig/Test/IntegrationTestCase.php create mode 100644 inc/Twig/Test/Method.php create mode 100644 inc/Twig/Test/Node.php create mode 100644 inc/Twig/Test/NodeTestCase.php create mode 100644 inc/Twig/TestCallableInterface.php create mode 100644 inc/Twig/TestInterface.php create mode 100644 inc/Twig/Token.php create mode 100644 inc/Twig/TokenParser.php create mode 100644 inc/Twig/TokenParser/AutoEscape.php create mode 100644 inc/Twig/TokenParser/Block.php create mode 100644 inc/Twig/TokenParser/Do.php create mode 100644 inc/Twig/TokenParser/Embed.php create mode 100644 inc/Twig/TokenParser/Extends.php create mode 100644 inc/Twig/TokenParser/Filter.php create mode 100644 inc/Twig/TokenParser/Flush.php create mode 100644 inc/Twig/TokenParser/For.php create mode 100644 inc/Twig/TokenParser/From.php create mode 100644 inc/Twig/TokenParser/If.php create mode 100644 inc/Twig/TokenParser/Import.php create mode 100644 inc/Twig/TokenParser/Include.php create mode 100644 inc/Twig/TokenParser/Macro.php create mode 100644 inc/Twig/TokenParser/Sandbox.php create mode 100644 inc/Twig/TokenParser/Set.php create mode 100644 inc/Twig/TokenParser/Spaceless.php create mode 100644 inc/Twig/TokenParser/Use.php create mode 100644 inc/Twig/TokenParserBroker.php create mode 100644 inc/Twig/TokenParserBrokerInterface.php create mode 100644 inc/Twig/TokenParserInterface.php create mode 100644 inc/Twig/TokenStream.php delete mode 100644 inc/functions.php create mode 100644 inc/poche/pocheCore.php create mode 100644 inc/poche/pochePictures.php rename inc/{ => poche}/pocheTool.class.php (64%) delete mode 100644 inc/rain.tpl.class.php create mode 100644 tpl/install.twig create mode 100644 tpl/layout.twig create mode 100644 tpl/login.twig diff --git a/inc/Encoding.php b/inc/3rdparty/Encoding.php similarity index 100% rename from inc/Encoding.php rename to inc/3rdparty/Encoding.php diff --git a/inc/JSLikeHTMLElement.php b/inc/3rdparty/JSLikeHTMLElement.php similarity index 100% rename from inc/JSLikeHTMLElement.php rename to inc/3rdparty/JSLikeHTMLElement.php diff --git a/inc/Readability.php b/inc/3rdparty/Readability.php similarity index 100% rename from inc/Readability.php rename to inc/3rdparty/Readability.php diff --git a/inc/Session.class.php b/inc/3rdparty/Session.class.php similarity index 100% rename from inc/Session.class.php rename to inc/3rdparty/Session.class.php diff --git a/inc/simple_html_dom.php b/inc/3rdparty/simple_html_dom.php similarity index 100% rename from inc/simple_html_dom.php rename to inc/3rdparty/simple_html_dom.php diff --git a/inc/Twig/Autoloader.php b/inc/Twig/Autoloader.php new file mode 100644 index 0000000..7007d31 --- /dev/null +++ b/inc/Twig/Autoloader.php @@ -0,0 +1,48 @@ + + */ +class Twig_Autoloader +{ + /** + * Registers Twig_Autoloader as an SPL autoloader. + * + * @param Boolean $prepend Whether to prepend the autoloader or not. + */ + public static function register($prepend = false) + { + if (version_compare(phpversion(), '5.3.0', '>=')) { + spl_autoload_register(array(new self, 'autoload'), true, $prepend); + } else { + spl_autoload_register(array(new self, 'autoload')); + } + } + + /** + * Handles autoloading of classes. + * + * @param string $class A class name. + */ + public static function autoload($class) + { + if (0 !== strpos($class, 'Twig')) { + return; + } + + if (is_file($file = dirname(__FILE__).'/../'.str_replace(array('_', "\0"), array('/', ''), $class).'.php')) { + require $file; + } + } +} diff --git a/inc/Twig/Compiler.php b/inc/Twig/Compiler.php new file mode 100644 index 0000000..99aecbc --- /dev/null +++ b/inc/Twig/Compiler.php @@ -0,0 +1,267 @@ + + */ +class Twig_Compiler implements Twig_CompilerInterface +{ + protected $lastLine; + protected $source; + protected $indentation; + protected $env; + protected $debugInfo; + protected $sourceOffset; + protected $sourceLine; + protected $filename; + + /** + * Constructor. + * + * @param Twig_Environment $env The twig environment instance + */ + public function __construct(Twig_Environment $env) + { + $this->env = $env; + $this->debugInfo = array(); + } + + public function getFilename() + { + return $this->filename; + } + + /** + * Returns the environment instance related to this compiler. + * + * @return Twig_Environment The environment instance + */ + public function getEnvironment() + { + return $this->env; + } + + /** + * Gets the current PHP code after compilation. + * + * @return string The PHP code + */ + public function getSource() + { + return $this->source; + } + + /** + * Compiles a node. + * + * @param Twig_NodeInterface $node The node to compile + * @param integer $indentation The current indentation + * + * @return Twig_Compiler The current compiler instance + */ + public function compile(Twig_NodeInterface $node, $indentation = 0) + { + $this->lastLine = null; + $this->source = ''; + $this->sourceOffset = 0; + // source code starts at 1 (as we then increment it when we encounter new lines) + $this->sourceLine = 1; + $this->indentation = $indentation; + + if ($node instanceof Twig_Node_Module) { + $this->filename = $node->getAttribute('filename'); + } + + $node->compile($this); + + return $this; + } + + public function subcompile(Twig_NodeInterface $node, $raw = true) + { + if (false === $raw) { + $this->addIndentation(); + } + + $node->compile($this); + + return $this; + } + + /** + * Adds a raw string to the compiled code. + * + * @param string $string The string + * + * @return Twig_Compiler The current compiler instance + */ + public function raw($string) + { + $this->source .= $string; + + return $this; + } + + /** + * Writes a string to the compiled code by adding indentation. + * + * @return Twig_Compiler The current compiler instance + */ + public function write() + { + $strings = func_get_args(); + foreach ($strings as $string) { + $this->addIndentation(); + $this->source .= $string; + } + + return $this; + } + + /** + * Appends an indentation to the current PHP code after compilation. + * + * @return Twig_Compiler The current compiler instance + */ + public function addIndentation() + { + $this->source .= str_repeat(' ', $this->indentation * 4); + + return $this; + } + + /** + * Adds a quoted string to the compiled code. + * + * @param string $value The string + * + * @return Twig_Compiler The current compiler instance + */ + public function string($value) + { + $this->source .= sprintf('"%s"', addcslashes($value, "\0\t\"\$\\")); + + return $this; + } + + /** + * Returns a PHP representation of a given value. + * + * @param mixed $value The value to convert + * + * @return Twig_Compiler The current compiler instance + */ + public function repr($value) + { + if (is_int($value) || is_float($value)) { + if (false !== $locale = setlocale(LC_NUMERIC, 0)) { + setlocale(LC_NUMERIC, 'C'); + } + + $this->raw($value); + + if (false !== $locale) { + setlocale(LC_NUMERIC, $locale); + } + } elseif (null === $value) { + $this->raw('null'); + } elseif (is_bool($value)) { + $this->raw($value ? 'true' : 'false'); + } elseif (is_array($value)) { + $this->raw('array('); + $i = 0; + foreach ($value as $key => $value) { + if ($i++) { + $this->raw(', '); + } + $this->repr($key); + $this->raw(' => '); + $this->repr($value); + } + $this->raw(')'); + } else { + $this->string($value); + } + + return $this; + } + + /** + * Adds debugging information. + * + * @param Twig_NodeInterface $node The related twig node + * + * @return Twig_Compiler The current compiler instance + */ + public function addDebugInfo(Twig_NodeInterface $node) + { + if ($node->getLine() != $this->lastLine) { + $this->write("// line {$node->getLine()}\n"); + + // when mbstring.func_overload is set to 2 + // mb_substr_count() replaces substr_count() + // but they have different signatures! + if (((int) ini_get('mbstring.func_overload')) & 2) { + // this is much slower than the "right" version + $this->sourceLine += mb_substr_count(mb_substr($this->source, $this->sourceOffset), "\n"); + } else { + $this->sourceLine += substr_count($this->source, "\n", $this->sourceOffset); + } + $this->sourceOffset = strlen($this->source); + $this->debugInfo[$this->sourceLine] = $node->getLine(); + + $this->lastLine = $node->getLine(); + } + + return $this; + } + + public function getDebugInfo() + { + return $this->debugInfo; + } + + /** + * Indents the generated code. + * + * @param integer $step The number of indentation to add + * + * @return Twig_Compiler The current compiler instance + */ + public function indent($step = 1) + { + $this->indentation += $step; + + return $this; + } + + /** + * Outdents the generated code. + * + * @param integer $step The number of indentation to remove + * + * @return Twig_Compiler The current compiler instance + */ + public function outdent($step = 1) + { + // can't outdent by more steps than the current indentation level + if ($this->indentation < $step) { + throw new LogicException('Unable to call outdent() as the indentation would become negative'); + } + + $this->indentation -= $step; + + return $this; + } +} diff --git a/inc/Twig/CompilerInterface.php b/inc/Twig/CompilerInterface.php new file mode 100644 index 0000000..e293ec9 --- /dev/null +++ b/inc/Twig/CompilerInterface.php @@ -0,0 +1,35 @@ + + * @deprecated since 1.12 (to be removed in 2.0) + */ +interface Twig_CompilerInterface +{ + /** + * Compiles a node. + * + * @param Twig_NodeInterface $node The node to compile + * + * @return Twig_CompilerInterface The current compiler instance + */ + public function compile(Twig_NodeInterface $node); + + /** + * Gets the current PHP code after compilation. + * + * @return string The PHP code + */ + public function getSource(); +} diff --git a/inc/Twig/Environment.php b/inc/Twig/Environment.php new file mode 100644 index 0000000..3afa73d --- /dev/null +++ b/inc/Twig/Environment.php @@ -0,0 +1,1224 @@ + + */ +class Twig_Environment +{ + const VERSION = '1.13.1'; + + protected $charset; + protected $loader; + protected $debug; + protected $autoReload; + protected $cache; + protected $lexer; + protected $parser; + protected $compiler; + protected $baseTemplateClass; + protected $extensions; + protected $parsers; + protected $visitors; + protected $filters; + protected $tests; + protected $functions; + protected $globals; + protected $runtimeInitialized; + protected $extensionInitialized; + protected $loadedTemplates; + protected $strictVariables; + protected $unaryOperators; + protected $binaryOperators; + protected $templateClassPrefix = '__TwigTemplate_'; + protected $functionCallbacks; + protected $filterCallbacks; + protected $staging; + + /** + * Constructor. + * + * Available options: + * + * * debug: When set to true, it automatically set "auto_reload" to true as + * well (default to false). + * + * * charset: The charset used by the templates (default to UTF-8). + * + * * base_template_class: The base template class to use for generated + * templates (default to Twig_Template). + * + * * cache: An absolute path where to store the compiled templates, or + * false to disable compilation cache (default). + * + * * auto_reload: Whether to reload the template is the original source changed. + * If you don't provide the auto_reload option, it will be + * determined automatically base on the debug value. + * + * * strict_variables: Whether to ignore invalid variables in templates + * (default to false). + * + * * autoescape: Whether to enable auto-escaping (default to html): + * * false: disable auto-escaping + * * true: equivalent to html + * * html, js: set the autoescaping to one of the supported strategies + * * PHP callback: a PHP callback that returns an escaping strategy based on the template "filename" + * + * * optimizations: A flag that indicates which optimizations to apply + * (default to -1 which means that all optimizations are enabled; + * set it to 0 to disable). + * + * @param Twig_LoaderInterface $loader A Twig_LoaderInterface instance + * @param array $options An array of options + */ + public function __construct(Twig_LoaderInterface $loader = null, $options = array()) + { + if (null !== $loader) { + $this->setLoader($loader); + } + + $options = array_merge(array( + 'debug' => false, + 'charset' => 'UTF-8', + 'base_template_class' => 'Twig_Template', + 'strict_variables' => false, + 'autoescape' => 'html', + 'cache' => false, + 'auto_reload' => null, + 'optimizations' => -1, + ), $options); + + $this->debug = (bool) $options['debug']; + $this->charset = strtoupper($options['charset']); + $this->baseTemplateClass = $options['base_template_class']; + $this->autoReload = null === $options['auto_reload'] ? $this->debug : (bool) $options['auto_reload']; + $this->strictVariables = (bool) $options['strict_variables']; + $this->runtimeInitialized = false; + $this->setCache($options['cache']); + $this->functionCallbacks = array(); + $this->filterCallbacks = array(); + + $this->addExtension(new Twig_Extension_Core()); + $this->addExtension(new Twig_Extension_Escaper($options['autoescape'])); + $this->addExtension(new Twig_Extension_Optimizer($options['optimizations'])); + $this->extensionInitialized = false; + $this->staging = new Twig_Extension_Staging(); + } + + /** + * Gets the base template class for compiled templates. + * + * @return string The base template class name + */ + public function getBaseTemplateClass() + { + return $this->baseTemplateClass; + } + + /** + * Sets the base template class for compiled templates. + * + * @param string $class The base template class name + */ + public function setBaseTemplateClass($class) + { + $this->baseTemplateClass = $class; + } + + /** + * Enables debugging mode. + */ + public function enableDebug() + { + $this->debug = true; + } + + /** + * Disables debugging mode. + */ + public function disableDebug() + { + $this->debug = false; + } + + /** + * Checks if debug mode is enabled. + * + * @return Boolean true if debug mode is enabled, false otherwise + */ + public function isDebug() + { + return $this->debug; + } + + /** + * Enables the auto_reload option. + */ + public function enableAutoReload() + { + $this->autoReload = true; + } + + /** + * Disables the auto_reload option. + */ + public function disableAutoReload() + { + $this->autoReload = false; + } + + /** + * Checks if the auto_reload option is enabled. + * + * @return Boolean true if auto_reload is enabled, false otherwise + */ + public function isAutoReload() + { + return $this->autoReload; + } + + /** + * Enables the strict_variables option. + */ + public function enableStrictVariables() + { + $this->strictVariables = true; + } + + /** + * Disables the strict_variables option. + */ + public function disableStrictVariables() + { + $this->strictVariables = false; + } + + /** + * Checks if the strict_variables option is enabled. + * + * @return Boolean true if strict_variables is enabled, false otherwise + */ + public function isStrictVariables() + { + return $this->strictVariables; + } + + /** + * Gets the cache directory or false if cache is disabled. + * + * @return string|false + */ + public function getCache() + { + return $this->cache; + } + + /** + * Sets the cache directory or false if cache is disabled. + * + * @param string|false $cache The absolute path to the compiled templates, + * or false to disable cache + */ + public function setCache($cache) + { + $this->cache = $cache ? $cache : false; + } + + /** + * Gets the cache filename for a given template. + * + * @param string $name The template name + * + * @return string The cache file name + */ + public function getCacheFilename($name) + { + if (false === $this->cache) { + return false; + } + + $class = substr($this->getTemplateClass($name), strlen($this->templateClassPrefix)); + + return $this->getCache().'/'.substr($class, 0, 2).'/'.substr($class, 2, 2).'/'.substr($class, 4).'.php'; + } + + /** + * Gets the template class associated with the given string. + * + * @param string $name The name for which to calculate the template class name + * @param integer $index The index if it is an embedded template + * + * @return string The template class name + */ + public function getTemplateClass($name, $index = null) + { + return $this->templateClassPrefix.md5($this->getLoader()->getCacheKey($name)).(null === $index ? '' : '_'.$index); + } + + /** + * Gets the template class prefix. + * + * @return string The template class prefix + */ + public function getTemplateClassPrefix() + { + return $this->templateClassPrefix; + } + + /** + * Renders a template. + * + * @param string $name The template name + * @param array $context An array of parameters to pass to the template + * + * @return string The rendered template + */ + public function render($name, array $context = array()) + { + return $this->loadTemplate($name)->render($context); + } + + /** + * Displays a template. + * + * @param string $name The template name + * @param array $context An array of parameters to pass to the template + */ + public function display($name, array $context = array()) + { + $this->loadTemplate($name)->display($context); + } + + /** + * Loads a template by name. + * + * @param string $name The template name + * @param integer $index The index if it is an embedded template + * + * @return Twig_TemplateInterface A template instance representing the given template name + */ + public function loadTemplate($name, $index = null) + { + $cls = $this->getTemplateClass($name, $index); + + if (isset($this->loadedTemplates[$cls])) { + return $this->loadedTemplates[$cls]; + } + + if (!class_exists($cls, false)) { + if (false === $cache = $this->getCacheFilename($name)) { + eval('?>'.$this->compileSource($this->getLoader()->getSource($name), $name)); + } else { + if (!is_file($cache) || ($this->isAutoReload() && !$this->isTemplateFresh($name, filemtime($cache)))) { + $this->writeCacheFile($cache, $this->compileSource($this->getLoader()->getSource($name), $name)); + } + + require_once $cache; + } + } + + if (!$this->runtimeInitialized) { + $this->initRuntime(); + } + + return $this->loadedTemplates[$cls] = new $cls($this); + } + + /** + * Returns true if the template is still fresh. + * + * Besides checking the loader for freshness information, + * this method also checks if the enabled extensions have + * not changed. + * + * @param string $name The template name + * @param timestamp $time The last modification time of the cached template + * + * @return Boolean true if the template is fresh, false otherwise + */ + public function isTemplateFresh($name, $time) + { + foreach ($this->extensions as $extension) { + $r = new ReflectionObject($extension); + if (filemtime($r->getFileName()) > $time) { + return false; + } + } + + return $this->getLoader()->isFresh($name, $time); + } + + public function resolveTemplate($names) + { + if (!is_array($names)) { + $names = array($names); + } + + foreach ($names as $name) { + if ($name instanceof Twig_Template) { + return $name; + } + + try { + return $this->loadTemplate($name); + } catch (Twig_Error_Loader $e) { + } + } + + if (1 === count($names)) { + throw $e; + } + + throw new Twig_Error_Loader(sprintf('Unable to find one of the following templates: "%s".', implode('", "', $names))); + } + + /** + * Clears the internal template cache. + */ + public function clearTemplateCache() + { + $this->loadedTemplates = array(); + } + + /** + * Clears the template cache files on the filesystem. + */ + public function clearCacheFiles() + { + if (false === $this->cache) { + return; + } + + foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->cache), RecursiveIteratorIterator::LEAVES_ONLY) as $file) { + if ($file->isFile()) { + @unlink($file->getPathname()); + } + } + } + + /** + * Gets the Lexer instance. + * + * @return Twig_LexerInterface A Twig_LexerInterface instance + */ + public function getLexer() + { + if (null === $this->lexer) { + $this->lexer = new Twig_Lexer($this); + } + + return $this->lexer; + } + + /** + * Sets the Lexer instance. + * + * @param Twig_LexerInterface A Twig_LexerInterface instance + */ + public function setLexer(Twig_LexerInterface $lexer) + { + $this->lexer = $lexer; + } + + /** + * Tokenizes a source code. + * + * @param string $source The template source code + * @param string $name The template name + * + * @return Twig_TokenStream A Twig_TokenStream instance + */ + public function tokenize($source, $name = null) + { + return $this->getLexer()->tokenize($source, $name); + } + + /** + * Gets the Parser instance. + * + * @return Twig_ParserInterface A Twig_ParserInterface instance + */ + public function getParser() + { + if (null === $this->parser) { + $this->parser = new Twig_Parser($this); + } + + return $this->parser; + } + + /** + * Sets the Parser instance. + * + * @param Twig_ParserInterface A Twig_ParserInterface instance + */ + public function setParser(Twig_ParserInterface $parser) + { + $this->parser = $parser; + } + + /** + * Parses a token stream. + * + * @param Twig_TokenStream $tokens A Twig_TokenStream instance + * + * @return Twig_Node_Module A Node tree + */ + public function parse(Twig_TokenStream $tokens) + { + return $this->getParser()->parse($tokens); + } + + /** + * Gets the Compiler instance. + * + * @return Twig_CompilerInterface A Twig_CompilerInterface instance + */ + public function getCompiler() + { + if (null === $this->compiler) { + $this->compiler = new Twig_Compiler($this); + } + + return $this->compiler; + } + + /** + * Sets the Compiler instance. + * + * @param Twig_CompilerInterface $compiler A Twig_CompilerInterface instance + */ + public function setCompiler(Twig_CompilerInterface $compiler) + { + $this->compiler = $compiler; + } + + /** + * Compiles a Node. + * + * @param Twig_NodeInterface $node A Twig_NodeInterface instance + * + * @return string The compiled PHP source code + */ + public function compile(Twig_NodeInterface $node) + { + return $this->getCompiler()->compile($node)->getSource(); + } + + /** + * Compiles a template source code. + * + * @param string $source The template source code + * @param string $name The template name + * + * @return string The compiled PHP source code + */ + public function compileSource($source, $name = null) + { + try { + return $this->compile($this->parse($this->tokenize($source, $name))); + } catch (Twig_Error $e) { + $e->setTemplateFile($name); + throw $e; + } catch (Exception $e) { + throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the compilation of a template ("%s").', $e->getMessage()), -1, $name, $e); + } + } + + /** + * Sets the Loader instance. + * + * @param Twig_LoaderInterface $loader A Twig_LoaderInterface instance + */ + public function setLoader(Twig_LoaderInterface $loader) + { + $this->loader = $loader; + } + + /** + * Gets the Loader instance. + * + * @return Twig_LoaderInterface A Twig_LoaderInterface instance + */ + public function getLoader() + { + if (null === $this->loader) { + throw new LogicException('You must set a loader first.'); + } + + return $this->loader; + } + + /** + * Sets the default template charset. + * + * @param string $charset The default charset + */ + public function setCharset($charset) + { + $this->charset = strtoupper($charset); + } + + /** + * Gets the default template charset. + * + * @return string The default charset + */ + public function getCharset() + { + return $this->charset; + } + + /** + * Initializes the runtime environment. + */ + public function initRuntime() + { + $this->runtimeInitialized = true; + + foreach ($this->getExtensions() as $extension) { + $extension->initRuntime($this); + } + } + + /** + * Returns true if the given extension is registered. + * + * @param string $name The extension name + * + * @return Boolean Whether the extension is registered or not + */ + public function hasExtension($name) + { + return isset($this->extensions[$name]); + } + + /** + * Gets an extension by name. + * + * @param string $name The extension name + * + * @return Twig_ExtensionInterface A Twig_ExtensionInterface instance + */ + public function getExtension($name) + { + if (!isset($this->extensions[$name])) { + throw new Twig_Error_Runtime(sprintf('The "%s" extension is not enabled.', $name)); + } + + return $this->extensions[$name]; + } + + /** + * Registers an extension. + * + * @param Twig_ExtensionInterface $extension A Twig_ExtensionInterface instance + */ + public function addExtension(Twig_ExtensionInterface $extension) + { + if ($this->extensionInitialized) { + throw new LogicException(sprintf('Unable to register extension "%s" as extensions have already been initialized.', $extension->getName())); + } + + $this->extensions[$extension->getName()] = $extension; + } + + /** + * Removes an extension by name. + * + * This method is deprecated and you should not use it. + * + * @param string $name The extension name + * + * @deprecated since 1.12 (to be removed in 2.0) + */ + public function removeExtension($name) + { + if ($this->extensionInitialized) { + throw new LogicException(sprintf('Unable to remove extension "%s" as extensions have already been initialized.', $name)); + } + + unset($this->extensions[$name]); + } + + /** + * Registers an array of extensions. + * + * @param array $extensions An array of extensions + */ + public function setExtensions(array $extensions) + { + foreach ($extensions as $extension) { + $this->addExtension($extension); + } + } + + /** + * Returns all registered extensions. + * + * @return array An array of extensions + */ + public function getExtensions() + { + return $this->extensions; + } + + /** + * Registers a Token Parser. + * + * @param Twig_TokenParserInterface $parser A Twig_TokenParserInterface instance + */ + public function addTokenParser(Twig_TokenParserInterface $parser) + { + if ($this->extensionInitialized) { + throw new LogicException('Unable to add a token parser as extensions have already been initialized.'); + } + + $this->staging->addTokenParser($parser); + } + + /** + * Gets the registered Token Parsers. + * + * @return Twig_TokenParserBrokerInterface A broker containing token parsers + */ + public function getTokenParsers() + { + if (!$this->extensionInitialized) { + $this->initExtensions(); + } + + return $this->parsers; + } + + /** + * Gets registered tags. + * + * Be warned that this method cannot return tags defined by Twig_TokenParserBrokerInterface classes. + * + * @return Twig_TokenParserInterface[] An array of Twig_TokenParserInterface instances + */ + public function getTags() + { + $tags = array(); + foreach ($this->getTokenParsers()->getParsers() as $parser) { + if ($parser instanceof Twig_TokenParserInterface) { + $tags[$parser->getTag()] = $parser; + } + } + + return $tags; + } + + /** + * Registers a Node Visitor. + * + * @param Twig_NodeVisitorInterface $visitor A Twig_NodeVisitorInterface instance + */ + public function addNodeVisitor(Twig_NodeVisitorInterface $visitor) + { + if ($this->extensionInitialized) { + throw new LogicException('Unable to add a node visitor as extensions have already been initialized.', $extension->getName()); + } + + $this->staging->addNodeVisitor($visitor); + } + + /** + * Gets the registered Node Visitors. + * + * @return Twig_NodeVisitorInterface[] An array of Twig_NodeVisitorInterface instances + */ + public function getNodeVisitors() + { + if (!$this->extensionInitialized) { + $this->initExtensions(); + } + + return $this->visitors; + } + + /** + * Registers a Filter. + * + * @param string|Twig_SimpleFilter $name The filter name or a Twig_SimpleFilter instance + * @param Twig_FilterInterface|Twig_SimpleFilter $filter A Twig_FilterInterface instance or a Twig_SimpleFilter instance + */ + public function addFilter($name, $filter = null) + { + if (!$name instanceof Twig_SimpleFilter && !($filter instanceof Twig_SimpleFilter || $filter instanceof Twig_FilterInterface)) { + throw new LogicException('A filter must be an instance of Twig_FilterInterface or Twig_SimpleFilter'); + } + + if ($name instanceof Twig_SimpleFilter) { + $filter = $name; + $name = $filter->getName(); + } + + if ($this->extensionInitialized) { + throw new LogicException(sprintf('Unable to add filter "%s" as extensions have already been initialized.', $name)); + } + + $this->staging->addFilter($name, $filter); + } + + /** + * Get a filter by name. + * + * Subclasses may override this method and load filters differently; + * so no list of filters is available. + * + * @param string $name The filter name + * + * @return Twig_Filter|false A Twig_Filter instance or false if the filter does not exist + */ + public function getFilter($name) + { + if (!$this->extensionInitialized) { + $this->initExtensions(); + } + + if (isset($this->filters[$name])) { + return $this->filters[$name]; + } + + foreach ($this->filters as $pattern => $filter) { + $pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count); + + if ($count) { + if (preg_match('#^'.$pattern.'$#', $name, $matches)) { + array_shift($matches); + $filter->setArguments($matches); + + return $filter; + } + } + } + + foreach ($this->filterCallbacks as $callback) { + if (false !== $filter = call_user_func($callback, $name)) { + return $filter; + } + } + + return false; + } + + public function registerUndefinedFilterCallback($callable) + { + $this->filterCallbacks[] = $callable; + } + + /** + * Gets the registered Filters. + * + * Be warned that this method cannot return filters defined with registerUndefinedFunctionCallback. + * + * @return Twig_FilterInterface[] An array of Twig_FilterInterface instances + * + * @see registerUndefinedFilterCallback + */ + public function getFilters() + { + if (!$this->extensionInitialized) { + $this->initExtensions(); + } + + return $this->filters; + } + + /** + * Registers a Test. + * + * @param string|Twig_SimpleTest $name The test name or a Twig_SimpleTest instance + * @param Twig_TestInterface|Twig_SimpleTest $test A Twig_TestInterface instance or a Twig_SimpleTest instance + */ + public function addTest($name, $test = null) + { + if (!$name instanceof Twig_SimpleTest && !($test instanceof Twig_SimpleTest || $test instanceof Twig_TestInterface)) { + throw new LogicException('A test must be an instance of Twig_TestInterface or Twig_SimpleTest'); + } + + if ($name instanceof Twig_SimpleTest) { + $test = $name; + $name = $test->getName(); + } + + if ($this->extensionInitialized) { + throw new LogicException(sprintf('Unable to add test "%s" as extensions have already been initialized.', $name)); + } + + $this->staging->addTest($name, $test); + } + + /** + * Gets the registered Tests. + * + * @return Twig_TestInterface[] An array of Twig_TestInterface instances + */ + public function getTests() + { + if (!$this->extensionInitialized) { + $this->initExtensions(); + } + + return $this->tests; + } + + /** + * Gets a test by name. + * + * @param string $name The test name + * + * @return Twig_Test|false A Twig_Test instance or false if the test does not exist + */ + public function getTest($name) + { + if (!$this->extensionInitialized) { + $this->initExtensions(); + } + + if (isset($this->tests[$name])) { + return $this->tests[$name]; + } + + return false; + } + + /** + * Registers a Function. + * + * @param string|Twig_SimpleFunction $name The function name or a Twig_SimpleFunction instance + * @param Twig_FunctionInterface|Twig_SimpleFunction $function A Twig_FunctionInterface instance or a Twig_SimpleFunction instance + */ + public function addFunction($name, $function = null) + { + if (!$name instanceof Twig_SimpleFunction && !($function instanceof Twig_SimpleFunction || $function instanceof Twig_FunctionInterface)) { + throw new LogicException('A function must be an instance of Twig_FunctionInterface or Twig_SimpleFunction'); + } + + if ($name instanceof Twig_SimpleFunction) { + $function = $name; + $name = $function->getName(); + } + + if ($this->extensionInitialized) { + throw new LogicException(sprintf('Unable to add function "%s" as extensions have already been initialized.', $name)); + } + + $this->staging->addFunction($name, $function); + } + + /** + * Get a function by name. + * + * Subclasses may override this method and load functions differently; + * so no list of functions is available. + * + * @param string $name function name + * + * @return Twig_Function|false A Twig_Function instance or false if the function does not exist + */ + public function getFunction($name) + { + if (!$this->extensionInitialized) { + $this->initExtensions(); + } + + if (isset($this->functions[$name])) { + return $this->functions[$name]; + } + + foreach ($this->functions as $pattern => $function) { + $pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count); + + if ($count) { + if (preg_match('#^'.$pattern.'$#', $name, $matches)) { + array_shift($matches); + $function->setArguments($matches); + + return $function; + } + } + } + + foreach ($this->functionCallbacks as $callback) { + if (false !== $function = call_user_func($callback, $name)) { + return $function; + } + } + + return false; + } + + public function registerUndefinedFunctionCallback($callable) + { + $this->functionCallbacks[] = $callable; + } + + /** + * Gets registered functions. + * + * Be warned that this method cannot return functions defined with registerUndefinedFunctionCallback. + * + * @return Twig_FunctionInterface[] An array of Twig_FunctionInterface instances + * + * @see registerUndefinedFunctionCallback + */ + public function getFunctions() + { + if (!$this->extensionInitialized) { + $this->initExtensions(); + } + + return $this->functions; + } + + /** + * Registers a Global. + * + * New globals can be added before compiling or rendering a template; + * but after, you can only update existing globals. + * + * @param string $name The global name + * @param mixed $value The global value + */ + public function addGlobal($name, $value) + { + if ($this->extensionInitialized || $this->runtimeInitialized) { + if (null === $this->globals) { + $this->globals = $this->initGlobals(); + } + + /* This condition must be uncommented in Twig 2.0 + if (!array_key_exists($name, $this->globals)) { + throw new LogicException(sprintf('Unable to add global "%s" as the runtime or the extensions have already been initialized.', $name)); + } + */ + } + + if ($this->extensionInitialized || $this->runtimeInitialized) { + // update the value + $this->globals[$name] = $value; + } else { + $this->staging->addGlobal($name, $value); + } + } + + /** + * Gets the registered Globals. + * + * @return array An array of globals + */ + public function getGlobals() + { + if (!$this->runtimeInitialized && !$this->extensionInitialized) { + return $this->initGlobals(); + } + + if (null === $this->globals) { + $this->globals = $this->initGlobals(); + } + + return $this->globals; + } + + /** + * Merges a context with the defined globals. + * + * @param array $context An array representing the context + * + * @return array The context merged with the globals + */ + public function mergeGlobals(array $context) + { + // we don't use array_merge as the context being generally + // bigger than globals, this code is faster. + foreach ($this->getGlobals() as $key => $value) { + if (!array_key_exists($key, $context)) { + $context[$key] = $value; + } + } + + return $context; + } + + /** + * Gets the registered unary Operators. + * + * @return array An array of unary operators + */ + public function getUnaryOperators() + { + if (!$this->extensionInitialized) { + $this->initExtensions(); + } + + return $this->unaryOperators; + } + + /** + * Gets the registered binary Operators. + * + * @return array An array of binary operators + */ + public function getBinaryOperators() + { + if (!$this->extensionInitialized) { + $this->initExtensions(); + } + + return $this->binaryOperators; + } + + public function computeAlternatives($name, $items) + { + $alternatives = array(); + foreach ($items as $item) { + $lev = levenshtein($name, $item); + if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) { + $alternatives[$item] = $lev; + } + } + asort($alternatives); + + return array_keys($alternatives); + } + + protected function initGlobals() + { + $globals = array(); + foreach ($this->extensions as $extension) { + $extGlob = $extension->getGlobals(); + if (!is_array($extGlob)) { + throw new UnexpectedValueException(sprintf('"%s::getGlobals()" must return an array of globals.', get_class($extension))); + } + + $globals[] = $extGlob; + } + + $globals[] = $this->staging->getGlobals(); + + return call_user_func_array('array_merge', $globals); + } + + protected function initExtensions() + { + if ($this->extensionInitialized) { + return; + } + + $this->extensionInitialized = true; + $this->parsers = new Twig_TokenParserBroker(); + $this->filters = array(); + $this->functions = array(); + $this->tests = array(); + $this->visitors = array(); + $this->unaryOperators = array(); + $this->binaryOperators = array(); + + foreach ($this->extensions as $extension) { + $this->initExtension($extension); + } + $this->initExtension($this->staging); + } + + protected function initExtension(Twig_ExtensionInterface $extension) + { + // filters + foreach ($extension->getFilters() as $name => $filter) { + if ($name instanceof Twig_SimpleFilter) { + $filter = $name; + $name = $filter->getName(); + } elseif ($filter instanceof Twig_SimpleFilter) { + $name = $filter->getName(); + } + + $this->filters[$name] = $filter; + } + + // functions + foreach ($extension->getFunctions() as $name => $function) { + if ($name instanceof Twig_SimpleFunction) { + $function = $name; + $name = $function->getName(); + } elseif ($function instanceof Twig_SimpleFunction) { + $name = $function->getName(); + } + + $this->functions[$name] = $function; + } + + // tests + foreach ($extension->getTests() as $name => $test) { + if ($name instanceof Twig_SimpleTest) { + $test = $name; + $name = $test->getName(); + } elseif ($test instanceof Twig_SimpleTest) { + $name = $test->getName(); + } + + $this->tests[$name] = $test; + } + + // token parsers + foreach ($extension->getTokenParsers() as $parser) { + if ($parser instanceof Twig_TokenParserInterface) { + $this->parsers->addTokenParser($parser); + } elseif ($parser instanceof Twig_TokenParserBrokerInterface) { + $this->parsers->addTokenParserBroker($parser); + } else { + throw new LogicException('getTokenParsers() must return an array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances'); + } + } + + // node visitors + foreach ($extension->getNodeVisitors() as $visitor) { + $this->visitors[] = $visitor; + } + + // operators + if ($operators = $extension->getOperators()) { + if (2 !== count($operators)) { + throw new InvalidArgumentException(sprintf('"%s::getOperators()" does not return a valid operators array.', get_class($extension))); + } + + $this->unaryOperators = array_merge($this->unaryOperators, $operators[0]); + $this->binaryOperators = array_merge($this->binaryOperators, $operators[1]); + } + } + + protected function writeCacheFile($file, $content) + { + $dir = dirname($file); + if (!is_dir($dir)) { + if (false === @mkdir($dir, 0777, true) && !is_dir($dir)) { + throw new RuntimeException(sprintf("Unable to create the cache directory (%s).", $dir)); + } + } elseif (!is_writable($dir)) { + throw new RuntimeException(sprintf("Unable to write in the cache directory (%s).", $dir)); + } + + $tmpFile = tempnam(dirname($file), basename($file)); + if (false !== @file_put_contents($tmpFile, $content)) { + // rename does not work on Win32 before 5.2.6 + if (@rename($tmpFile, $file) || (@copy($tmpFile, $file) && unlink($tmpFile))) { + @chmod($file, 0666 & ~umask()); + + return; + } + } + + throw new RuntimeException(sprintf('Failed to write cache file "%s".', $file)); + } +} diff --git a/inc/Twig/Error.php b/inc/Twig/Error.php new file mode 100644 index 0000000..72d91a9 --- /dev/null +++ b/inc/Twig/Error.php @@ -0,0 +1,239 @@ + + */ +class Twig_Error extends Exception +{ + protected $lineno; + protected $filename; + protected $rawMessage; + protected $previous; + + /** + * Constructor. + * + * Set both the line number and the filename to false to + * disable automatic guessing of the original template name + * and line number. + * + * Set the line number to -1 to enable its automatic guessing. + * Set the filename to null to enable its automatic guessing. + * + * By default, automatic guessing is enabled. + * + * @param string $message The error message + * @param integer $lineno The template line where the error occurred + * @param string $filename The template file name where the error occurred + * @param Exception $previous The previous exception + */ + public function __construct($message, $lineno = -1, $filename = null, Exception $previous = null) + { + if (version_compare(PHP_VERSION, '5.3.0', '<')) { + $this->previous = $previous; + parent::__construct(''); + } else { + parent::__construct('', 0, $previous); + } + + $this->lineno = $lineno; + $this->filename = $filename; + + if (-1 === $this->lineno || null === $this->filename) { + $this->guessTemplateInfo(); + } + + $this->rawMessage = $message; + + $this->updateRepr(); + } + + /** + * Gets the raw message. + * + * @return string The raw message + */ + public function getRawMessage() + { + return $this->rawMessage; + } + + /** + * Gets the filename where the error occurred. + * + * @return string The filename + */ + public function getTemplateFile() + { + return $this->filename; + } + + /** + * Sets the filename where the error occurred. + * + * @param string $filename The filename + */ + public function setTemplateFile($filename) + { + $this->filename = $filename; + + $this->updateRepr(); + } + + /** + * Gets the template line where the error occurred. + * + * @return integer The template line + */ + public function getTemplateLine() + { + return $this->lineno; + } + + /** + * Sets the template line where the error occurred. + * + * @param integer $lineno The template line + */ + public function setTemplateLine($lineno) + { + $this->lineno = $lineno; + + $this->updateRepr(); + } + + public function guess() + { + $this->guessTemplateInfo(); + $this->updateRepr(); + } + + /** + * For PHP < 5.3.0, provides access to the getPrevious() method. + * + * @param string $method The method name + * @param array $arguments The parameters to be passed to the method + * + * @return Exception The previous exception or null + * + * @throws BadMethodCallException + */ + public function __call($method, $arguments) + { + if ('getprevious' == strtolower($method)) { + return $this->previous; + } + + throw new BadMethodCallException(sprintf('Method "Twig_Error::%s()" does not exist.', $method)); + } + + protected function updateRepr() + { + $this->message = $this->rawMessage; + + $dot = false; + if ('.' === substr($this->message, -1)) { + $this->message = substr($this->message, 0, -1); + $dot = true; + } + + if ($this->filename) { + if (is_string($this->filename) || (is_object($this->filename) && method_exists($this->filename, '__toString'))) { + $filename = sprintf('"%s"', $this->filename); + } else { + $filename = json_encode($this->filename); + } + $this->message .= sprintf(' in %s', $filename); + } + + if ($this->lineno && $this->lineno >= 0) { + $this->message .= sprintf(' at line %d', $this->lineno); + } + + if ($dot) { + $this->message .= '.'; + } + } + + protected function guessTemplateInfo() + { + $template = null; + + if (version_compare(phpversion(), '5.3.6', '>=')) { + $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT); + } else { + $backtrace = debug_backtrace(); + } + + foreach ($backtrace as $trace) { + if (isset($trace['object']) && $trace['object'] instanceof Twig_Template && 'Twig_Template' !== get_class($trace['object'])) { + if (null === $this->filename || $this->filename == $trace['object']->getTemplateName()) { + $template = $trace['object']; + } + } + } + + // update template filename + if (null !== $template && null === $this->filename) { + $this->filename = $template->getTemplateName(); + } + + if (null === $template || $this->lineno > -1) { + return; + } + + $r = new ReflectionObject($template); + $file = $r->getFileName(); + + $exceptions = array($e = $this); + while (($e instanceof self || method_exists($e, 'getPrevious')) && $e = $e->getPrevious()) { + $exceptions[] = $e; + } + + while ($e = array_pop($exceptions)) { + $traces = $e->getTrace(); + while ($trace = array_shift($traces)) { + if (!isset($trace['file']) || !isset($trace['line']) || $file != $trace['file']) { + continue; + } + + foreach ($template->getDebugInfo() as $codeLine => $templateLine) { + if ($codeLine <= $trace['line']) { + // update template line + $this->lineno = $templateLine; + + return; + } + } + } + } + } +} diff --git a/inc/Twig/Error/Loader.php b/inc/Twig/Error/Loader.php new file mode 100644 index 0000000..68efb57 --- /dev/null +++ b/inc/Twig/Error/Loader.php @@ -0,0 +1,31 @@ + + */ +class Twig_Error_Loader extends Twig_Error +{ + public function __construct($message, $lineno = -1, $filename = null, Exception $previous = null) + { + parent::__construct($message, false, false, $previous); + } +} diff --git a/inc/Twig/Error/Runtime.php b/inc/Twig/Error/Runtime.php new file mode 100644 index 0000000..8b6cedd --- /dev/null +++ b/inc/Twig/Error/Runtime.php @@ -0,0 +1,20 @@ + + */ +class Twig_Error_Runtime extends Twig_Error +{ +} diff --git a/inc/Twig/Error/Syntax.php b/inc/Twig/Error/Syntax.php new file mode 100644 index 0000000..0f5c579 --- /dev/null +++ b/inc/Twig/Error/Syntax.php @@ -0,0 +1,20 @@ + + */ +class Twig_Error_Syntax extends Twig_Error +{ +} diff --git a/inc/Twig/ExistsLoaderInterface.php b/inc/Twig/ExistsLoaderInterface.php new file mode 100644 index 0000000..ce43476 --- /dev/null +++ b/inc/Twig/ExistsLoaderInterface.php @@ -0,0 +1,28 @@ + + * @deprecated since 1.12 (to be removed in 2.0) + */ +interface Twig_ExistsLoaderInterface +{ + /** + * Check if we have the source code of a template, given its name. + * + * @param string $name The name of the template to check if we can load + * + * @return boolean If the template source code is handled by this loader or not + */ + public function exists($name); +} diff --git a/inc/Twig/ExpressionParser.php b/inc/Twig/ExpressionParser.php new file mode 100644 index 0000000..9cf1934 --- /dev/null +++ b/inc/Twig/ExpressionParser.php @@ -0,0 +1,600 @@ + + */ +class Twig_ExpressionParser +{ + const OPERATOR_LEFT = 1; + const OPERATOR_RIGHT = 2; + + protected $parser; + protected $unaryOperators; + protected $binaryOperators; + + public function __construct(Twig_Parser $parser, array $unaryOperators, array $binaryOperators) + { + $this->parser = $parser; + $this->unaryOperators = $unaryOperators; + $this->binaryOperators = $binaryOperators; + } + + public function parseExpression($precedence = 0) + { + $expr = $this->getPrimary(); + $token = $this->parser->getCurrentToken(); + while ($this->isBinary($token) && $this->binaryOperators[$token->getValue()]['precedence'] >= $precedence) { + $op = $this->binaryOperators[$token->getValue()]; + $this->parser->getStream()->next(); + + if (isset($op['callable'])) { + $expr = call_user_func($op['callable'], $this->parser, $expr); + } else { + $expr1 = $this->parseExpression(self::OPERATOR_LEFT === $op['associativity'] ? $op['precedence'] + 1 : $op['precedence']); + $class = $op['class']; + $expr = new $class($expr, $expr1, $token->getLine()); + } + + $token = $this->parser->getCurrentToken(); + } + + if (0 === $precedence) { + return $this->parseConditionalExpression($expr); + } + + return $expr; + } + + protected function getPrimary() + { + $token = $this->parser->getCurrentToken(); + + if ($this->isUnary($token)) { + $operator = $this->unaryOperators[$token->getValue()]; + $this->parser->getStream()->next(); + $expr = $this->parseExpression($operator['precedence']); + $class = $operator['class']; + + return $this->parsePostfixExpression(new $class($expr, $token->getLine())); + } elseif ($token->test(Twig_Token::PUNCTUATION_TYPE, '(')) { + $this->parser->getStream()->next(); + $expr = $this->parseExpression(); + $this->parser->getStream()->expect(Twig_Token::PUNCTUATION_TYPE, ')', 'An opened parenthesis is not properly closed'); + + return $this->parsePostfixExpression($expr); + } + + return $this->parsePrimaryExpression(); + } + + protected function parseConditionalExpression($expr) + { + while ($this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, '?')) { + $this->parser->getStream()->next(); + if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, ':')) { + $expr2 = $this->parseExpression(); + if ($this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, ':')) { + $this->parser->getStream()->next(); + $expr3 = $this->parseExpression(); + } else { + $expr3 = new Twig_Node_Expression_Constant('', $this->parser->getCurrentToken()->getLine()); + } + } else { + $this->parser->getStream()->next(); + $expr2 = $expr; + $expr3 = $this->parseExpression(); + } + + $expr = new Twig_Node_Expression_Conditional($expr, $expr2, $expr3, $this->parser->getCurrentToken()->getLine()); + } + + return $expr; + } + + protected function isUnary(Twig_Token $token) + { + return $token->test(Twig_Token::OPERATOR_TYPE) && isset($this->unaryOperators[$token->getValue()]); + } + + protected function isBinary(Twig_Token $token) + { + return $token->test(Twig_Token::OPERATOR_TYPE) && isset($this->binaryOperators[$token->getValue()]); + } + + public function parsePrimaryExpression() + { + $token = $this->parser->getCurrentToken(); + switch ($token->getType()) { + case Twig_Token::NAME_TYPE: + $this->parser->getStream()->next(); + switch ($token->getValue()) { + case 'true': + case 'TRUE': + $node = new Twig_Node_Expression_Constant(true, $token->getLine()); + break; + + case 'false': + case 'FALSE': + $node = new Twig_Node_Expression_Constant(false, $token->getLine()); + break; + + case 'none': + case 'NONE': + case 'null': + case 'NULL': + $node = new Twig_Node_Expression_Constant(null, $token->getLine()); + break; + + default: + if ('(' === $this->parser->getCurrentToken()->getValue()) { + $node = $this->getFunctionNode($token->getValue(), $token->getLine()); + } else { + $node = new Twig_Node_Expression_Name($token->getValue(), $token->getLine()); + } + } + break; + + case Twig_Token::NUMBER_TYPE: + $this->parser->getStream()->next(); + $node = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine()); + break; + + case Twig_Token::STRING_TYPE: + case Twig_Token::INTERPOLATION_START_TYPE: + $node = $this->parseStringExpression(); + break; + + default: + if ($token->test(Twig_Token::PUNCTUATION_TYPE, '[')) { + $node = $this->parseArrayExpression(); + } elseif ($token->test(Twig_Token::PUNCTUATION_TYPE, '{')) { + $node = $this->parseHashExpression(); + } else { + throw new Twig_Error_Syntax(sprintf('Unexpected token "%s" of value "%s"', Twig_Token::typeToEnglish($token->getType(), $token->getLine()), $token->getValue()), $token->getLine(), $this->parser->getFilename()); + } + } + + return $this->parsePostfixExpression($node); + } + + public function parseStringExpression() + { + $stream = $this->parser->getStream(); + + $nodes = array(); + // a string cannot be followed by another string in a single expression + $nextCanBeString = true; + while (true) { + if ($stream->test(Twig_Token::STRING_TYPE) && $nextCanBeString) { + $token = $stream->next(); + $nodes[] = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine()); + $nextCanBeString = false; + } elseif ($stream->test(Twig_Token::INTERPOLATION_START_TYPE)) { + $stream->next(); + $nodes[] = $this->parseExpression(); + $stream->expect(Twig_Token::INTERPOLATION_END_TYPE); + $nextCanBeString = true; + } else { + break; + } + } + + $expr = array_shift($nodes); + foreach ($nodes as $node) { + $expr = new Twig_Node_Expression_Binary_Concat($expr, $node, $node->getLine()); + } + + return $expr; + } + + public function parseArrayExpression() + { + $stream = $this->parser->getStream(); + $stream->expect(Twig_Token::PUNCTUATION_TYPE, '[', 'An array element was expected'); + + $node = new Twig_Node_Expression_Array(array(), $stream->getCurrent()->getLine()); + $first = true; + while (!$stream->test(Twig_Token::PUNCTUATION_TYPE, ']')) { + if (!$first) { + $stream->expect(Twig_Token::PUNCTUATION_TYPE, ',', 'An array element must be followed by a comma'); + + // trailing ,? + if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ']')) { + break; + } + } + $first = false; + + $node->addElement($this->parseExpression()); + } + $stream->expect(Twig_Token::PUNCTUATION_TYPE, ']', 'An opened array is not properly closed'); + + return $node; + } + + public function parseHashExpression() + { + $stream = $this->parser->getStream(); + $stream->expect(Twig_Token::PUNCTUATION_TYPE, '{', 'A hash element was expected'); + + $node = new Twig_Node_Expression_Array(array(), $stream->getCurrent()->getLine()); + $first = true; + while (!$stream->test(Twig_Token::PUNCTUATION_TYPE, '}')) { + if (!$first) { + $stream->expect(Twig_Token::PUNCTUATION_TYPE, ',', 'A hash value must be followed by a comma'); + + // trailing ,? + if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '}')) { + break; + } + } + $first = false; + + // a hash key can be: + // + // * a number -- 12 + // * a string -- 'a' + // * a name, which is equivalent to a string -- a + // * an expression, which must be enclosed in parentheses -- (1 + 2) + if ($stream->test(Twig_Token::STRING_TYPE) || $stream->test(Twig_Token::NAME_TYPE) || $stream->test(Twig_Token::NUMBER_TYPE)) { + $token = $stream->next(); + $key = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine()); + } elseif ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) { + $key = $this->parseExpression(); + } else { + $current = $stream->getCurrent(); + + throw new Twig_Error_Syntax(sprintf('A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "%s" of value "%s"', Twig_Token::typeToEnglish($current->getType(), $current->getLine()), $current->getValue()), $current->getLine(), $this->parser->getFilename()); + } + + $stream->expect(Twig_Token::PUNCTUATION_TYPE, ':', 'A hash key must be followed by a colon (:)'); + $value = $this->parseExpression(); + + $node->addElement($value, $key); + } + $stream->expect(Twig_Token::PUNCTUATION_TYPE, '}', 'An opened hash is not properly closed'); + + return $node; + } + + public function parsePostfixExpression($node) + { + while (true) { + $token = $this->parser->getCurrentToken(); + if ($token->getType() == Twig_Token::PUNCTUATION_TYPE) { + if ('.' == $token->getValue() || '[' == $token->getValue()) { + $node = $this->parseSubscriptExpression($node); + } elseif ('|' == $token->getValue()) { + $node = $this->parseFilterExpression($node); + } else { + break; + } + } else { + break; + } + } + + return $node; + } + + public function getFunctionNode($name, $line) + { + switch ($name) { + case 'parent': + $args = $this->parseArguments(); + if (!count($this->parser->getBlockStack())) { + throw new Twig_Error_Syntax('Calling "parent" outside a block is forbidden', $line, $this->parser->getFilename()); + } + + if (!$this->parser->getParent() && !$this->parser->hasTraits()) { + throw new Twig_Error_Syntax('Calling "parent" on a template that does not extend nor "use" another template is forbidden', $line, $this->parser->getFilename()); + } + + return new Twig_Node_Expression_Parent($this->parser->peekBlockStack(), $line); + case 'block': + return new Twig_Node_Expression_BlockReference($this->parseArguments()->getNode(0), false, $line); + case 'attribute': + $args = $this->parseArguments(); + if (count($args) < 2) { + throw new Twig_Error_Syntax('The "attribute" function takes at least two arguments (the variable and the attributes)', $line, $this->parser->getFilename()); + } + + return new Twig_Node_Expression_GetAttr($args->getNode(0), $args->getNode(1), count($args) > 2 ? $args->getNode(2) : new Twig_Node_Expression_Array(array(), $line), Twig_TemplateInterface::ANY_CALL, $line); + default: + if (null !== $alias = $this->parser->getImportedSymbol('function', $name)) { + $arguments = new Twig_Node_Expression_Array(array(), $line); + foreach ($this->parseArguments() as $n) { + $arguments->addElement($n); + } + + $node = new Twig_Node_Expression_MethodCall($alias['node'], $alias['name'], $arguments, $line); + $node->setAttribute('safe', true); + + return $node; + } + + $args = $this->parseArguments(true); + $class = $this->getFunctionNodeClass($name, $line); + + return new $class($name, $args, $line); + } + } + + public function parseSubscriptExpression($node) + { + $stream = $this->parser->getStream(); + $token = $stream->next(); + $lineno = $token->getLine(); + $arguments = new Twig_Node_Expression_Array(array(), $lineno); + $type = Twig_TemplateInterface::ANY_CALL; + if ($token->getValue() == '.') { + $token = $stream->next(); + if ( + $token->getType() == Twig_Token::NAME_TYPE + || + $token->getType() == Twig_Token::NUMBER_TYPE + || + ($token->getType() == Twig_Token::OPERATOR_TYPE && preg_match(Twig_Lexer::REGEX_NAME, $token->getValue())) + ) { + $arg = new Twig_Node_Expression_Constant($token->getValue(), $lineno); + + if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) { + $type = Twig_TemplateInterface::METHOD_CALL; + foreach ($this->parseArguments() as $n) { + $arguments->addElement($n); + } + } + } else { + throw new Twig_Error_Syntax('Expected name or number', $lineno, $this->parser->getFilename()); + } + + if ($node instanceof Twig_Node_Expression_Name && null !== $this->parser->getImportedSymbol('template', $node->getAttribute('name'))) { + if (!$arg instanceof Twig_Node_Expression_Constant) { + throw new Twig_Error_Syntax(sprintf('Dynamic macro names are not supported (called on "%s")', $node->getAttribute('name')), $token->getLine(), $this->parser->getFilename()); + } + + $node = new Twig_Node_Expression_MethodCall($node, 'get'.$arg->getAttribute('value'), $arguments, $lineno); + $node->setAttribute('safe', true); + + return $node; + } + } else { + $type = Twig_TemplateInterface::ARRAY_CALL; + + // slice? + $slice = false; + if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ':')) { + $slice = true; + $arg = new Twig_Node_Expression_Constant(0, $token->getLine()); + } else { + $arg = $this->parseExpression(); + } + + if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ':')) { + $slice = true; + $stream->next(); + } + + if ($slice) { + if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ']')) { + $length = new Twig_Node_Expression_Constant(null, $token->getLine()); + } else { + $length = $this->parseExpression(); + } + + $class = $this->getFilterNodeClass('slice', $token->getLine()); + $arguments = new Twig_Node(array($arg, $length)); + $filter = new $class($node, new Twig_Node_Expression_Constant('slice', $token->getLine()), $arguments, $token->getLine()); + + $stream->expect(Twig_Token::PUNCTUATION_TYPE, ']'); + + return $filter; + } + + $stream->expect(Twig_Token::PUNCTUATION_TYPE, ']'); + } + + return new Twig_Node_Expression_GetAttr($node, $arg, $arguments, $type, $lineno); + } + + public function parseFilterExpression($node) + { + $this->parser->getStream()->next(); + + return $this->parseFilterExpressionRaw($node); + } + + public function parseFilterExpressionRaw($node, $tag = null) + { + while (true) { + $token = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE); + + $name = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine()); + if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, '(')) { + $arguments = new Twig_Node(); + } else { + $arguments = $this->parseArguments(true); + } + + $class = $this->getFilterNodeClass($name->getAttribute('value'), $token->getLine()); + + $node = new $class($node, $name, $arguments, $token->getLine(), $tag); + + if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, '|')) { + break; + } + + $this->parser->getStream()->next(); + } + + return $node; + } + + /** + * Parses arguments. + * + * @param Boolean $namedArguments Whether to allow named arguments or not + * @param Boolean $definition Whether we are parsing arguments for a function definition + */ + public function parseArguments($namedArguments = false, $definition = false) + { + $args = array(); + $stream = $this->parser->getStream(); + + $stream->expect(Twig_Token::PUNCTUATION_TYPE, '(', 'A list of arguments must begin with an opening parenthesis'); + while (!$stream->test(Twig_Token::PUNCTUATION_TYPE, ')')) { + if (!empty($args)) { + $stream->expect(Twig_Token::PUNCTUATION_TYPE, ',', 'Arguments must be separated by a comma'); + } + + if ($definition) { + $token = $stream->expect(Twig_Token::NAME_TYPE, null, 'An argument must be a name'); + $value = new Twig_Node_Expression_Name($token->getValue(), $this->parser->getCurrentToken()->getLine()); + } else { + $value = $this->parseExpression(); + } + + $name = null; + if ($namedArguments && $stream->test(Twig_Token::OPERATOR_TYPE, '=')) { + $token = $stream->next(); + if (!$value instanceof Twig_Node_Expression_Name) { + throw new Twig_Error_Syntax(sprintf('A parameter name must be a string, "%s" given', get_class($value)), $token->getLine(), $this->parser->getFilename()); + } + $name = $value->getAttribute('name'); + + if ($definition) { + $value = $this->parsePrimaryExpression(); + + if (!$this->checkConstantExpression($value)) { + throw new Twig_Error_Syntax(sprintf('A default value for an argument must be a constant (a boolean, a string, a number, or an array).'), $token->getLine(), $this->parser->getFilename()); + } + } else { + $value = $this->parseExpression(); + } + } + + if ($definition) { + if (null === $name) { + $name = $value->getAttribute('name'); + $value = new Twig_Node_Expression_Constant(null, $this->parser->getCurrentToken()->getLine()); + } + $args[$name] = $value; + } else { + if (null === $name) { + $args[] = $value; + } else { + $args[$name] = $value; + } + } + } + $stream->expect(Twig_Token::PUNCTUATION_TYPE, ')', 'A list of arguments must be closed by a parenthesis'); + + return new Twig_Node($args); + } + + public function parseAssignmentExpression() + { + $targets = array(); + while (true) { + $token = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE, null, 'Only variables can be assigned to'); + if (in_array($token->getValue(), array('true', 'false', 'none'))) { + throw new Twig_Error_Syntax(sprintf('You cannot assign a value to "%s"', $token->getValue()), $token->getLine(), $this->parser->getFilename()); + } + $targets[] = new Twig_Node_Expression_AssignName($token->getValue(), $token->getLine()); + + if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, ',')) { + break; + } + $this->parser->getStream()->next(); + } + + return new Twig_Node($targets); + } + + public function parseMultitargetExpression() + { + $targets = array(); + while (true) { + $targets[] = $this->parseExpression(); + if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, ',')) { + break; + } + $this->parser->getStream()->next(); + } + + return new Twig_Node($targets); + } + + protected function getFunctionNodeClass($name, $line) + { + $env = $this->parser->getEnvironment(); + + if (false === $function = $env->getFunction($name)) { + $message = sprintf('The function "%s" does not exist', $name); + if ($alternatives = $env->computeAlternatives($name, array_keys($env->getFunctions()))) { + $message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives)); + } + + throw new Twig_Error_Syntax($message, $line, $this->parser->getFilename()); + } + + if ($function instanceof Twig_SimpleFunction) { + return $function->getNodeClass(); + } + + return $function instanceof Twig_Function_Node ? $function->getClass() : 'Twig_Node_Expression_Function'; + } + + protected function getFilterNodeClass($name, $line) + { + $env = $this->parser->getEnvironment(); + + if (false === $filter = $env->getFilter($name)) { + $message = sprintf('The filter "%s" does not exist', $name); + if ($alternatives = $env->computeAlternatives($name, array_keys($env->getFilters()))) { + $message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives)); + } + + throw new Twig_Error_Syntax($message, $line, $this->parser->getFilename()); + } + + if ($filter instanceof Twig_SimpleFilter) { + return $filter->getNodeClass(); + } + + return $filter instanceof Twig_Filter_Node ? $filter->getClass() : 'Twig_Node_Expression_Filter'; + } + + // checks that the node only contains "constant" elements + protected function checkConstantExpression(Twig_NodeInterface $node) + { + if (!($node instanceof Twig_Node_Expression_Constant || $node instanceof Twig_Node_Expression_Array)) { + return false; + } + + foreach ($node as $n) { + if (!$this->checkConstantExpression($n)) { + return false; + } + } + + return true; + } +} diff --git a/inc/Twig/Extension.php b/inc/Twig/Extension.php new file mode 100644 index 0000000..931fc03 --- /dev/null +++ b/inc/Twig/Extension.php @@ -0,0 +1,93 @@ +dateFormats[0] = $format; + } + + if (null !== $dateIntervalFormat) { + $this->dateFormats[1] = $dateIntervalFormat; + } + } + + /** + * Gets the default format to be used by the date filter. + * + * @return array The default date format string and the default date interval format string + */ + public function getDateFormat() + { + return $this->dateFormats; + } + + /** + * Sets the default timezone to be used by the date filter. + * + * @param DateTimeZone|string $timezone The default timezone string or a DateTimeZone object + */ + public function setTimezone($timezone) + { + $this->timezone = $timezone instanceof DateTimeZone ? $timezone : new DateTimeZone($timezone); + } + + /** + * Gets the default timezone to be used by the date filter. + * + * @return DateTimeZone The default timezone currently in use + */ + public function getTimezone() + { + if (null === $this->timezone) { + $this->timezone = new DateTimeZone(date_default_timezone_get()); + } + + return $this->timezone; + } + + /** + * Sets the default format to be used by the number_format filter. + * + * @param integer $decimal The number of decimal places to use. + * @param string $decimalPoint The character(s) to use for the decimal point. + * @param string $thousandSep The character(s) to use for the thousands separator. + */ + public function setNumberFormat($decimal, $decimalPoint, $thousandSep) + { + $this->numberFormat = array($decimal, $decimalPoint, $thousandSep); + } + + /** + * Get the default format used by the number_format filter. + * + * @return array The arguments for number_format() + */ + public function getNumberFormat() + { + return $this->numberFormat; + } + + /** + * Returns the token parser instance to add to the existing list. + * + * @return array An array of Twig_TokenParser instances + */ + public function getTokenParsers() + { + return array( + new Twig_TokenParser_For(), + new Twig_TokenParser_If(), + new Twig_TokenParser_Extends(), + new Twig_TokenParser_Include(), + new Twig_TokenParser_Block(), + new Twig_TokenParser_Use(), + new Twig_TokenParser_Filter(), + new Twig_TokenParser_Macro(), + new Twig_TokenParser_Import(), + new Twig_TokenParser_From(), + new Twig_TokenParser_Set(), + new Twig_TokenParser_Spaceless(), + new Twig_TokenParser_Flush(), + new Twig_TokenParser_Do(), + new Twig_TokenParser_Embed(), + ); + } + + /** + * Returns a list of filters to add to the existing list. + * + * @return array An array of filters + */ + public function getFilters() + { + $filters = array( + // formatting filters + new Twig_SimpleFilter('date', 'twig_date_format_filter', array('needs_environment' => true)), + new Twig_SimpleFilter('date_modify', 'twig_date_modify_filter', array('needs_environment' => true)), + new Twig_SimpleFilter('format', 'sprintf'), + new Twig_SimpleFilter('replace', 'strtr'), + new Twig_SimpleFilter('number_format', 'twig_number_format_filter', array('needs_environment' => true)), + new Twig_SimpleFilter('abs', 'abs'), + + // encoding + new Twig_SimpleFilter('url_encode', 'twig_urlencode_filter'), + new Twig_SimpleFilter('json_encode', 'twig_jsonencode_filter'), + new Twig_SimpleFilter('convert_encoding', 'twig_convert_encoding'), + + // string filters + new Twig_SimpleFilter('title', 'twig_title_string_filter', array('needs_environment' => true)), + new Twig_SimpleFilter('capitalize', 'twig_capitalize_string_filter', array('needs_environment' => true)), + new Twig_SimpleFilter('upper', 'strtoupper'), + new Twig_SimpleFilter('lower', 'strtolower'), + new Twig_SimpleFilter('striptags', 'strip_tags'), + new Twig_SimpleFilter('trim', 'trim'), + new Twig_SimpleFilter('nl2br', 'nl2br', array('pre_escape' => 'html', 'is_safe' => array('html'))), + + // array helpers + new Twig_SimpleFilter('join', 'twig_join_filter'), + new Twig_SimpleFilter('split', 'twig_split_filter'), + new Twig_SimpleFilter('sort', 'twig_sort_filter'), + new Twig_SimpleFilter('merge', 'twig_array_merge'), + new Twig_SimpleFilter('batch', 'twig_array_batch'), + + // string/array filters + new Twig_SimpleFilter('reverse', 'twig_reverse_filter', array('needs_environment' => true)), + new Twig_SimpleFilter('length', 'twig_length_filter', array('needs_environment' => true)), + new Twig_SimpleFilter('slice', 'twig_slice', array('needs_environment' => true)), + new Twig_SimpleFilter('first', 'twig_first', array('needs_environment' => true)), + new Twig_SimpleFilter('last', 'twig_last', array('needs_environment' => true)), + + // iteration and runtime + new Twig_SimpleFilter('default', '_twig_default_filter', array('node_class' => 'Twig_Node_Expression_Filter_Default')), + new Twig_SimpleFilter('keys', 'twig_get_array_keys_filter'), + + // escaping + new Twig_SimpleFilter('escape', 'twig_escape_filter', array('needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe')), + new Twig_SimpleFilter('e', 'twig_escape_filter', array('needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe')), + ); + + if (function_exists('mb_get_info')) { + $filters[] = new Twig_SimpleFilter('upper', 'twig_upper_filter', array('needs_environment' => true)); + $filters[] = new Twig_SimpleFilter('lower', 'twig_lower_filter', array('needs_environment' => true)); + } + + return $filters; + } + + /** + * Returns a list of global functions to add to the existing list. + * + * @return array An array of global functions + */ + public function getFunctions() + { + return array( + new Twig_SimpleFunction('range', 'range'), + new Twig_SimpleFunction('constant', 'twig_constant'), + new Twig_SimpleFunction('cycle', 'twig_cycle'), + new Twig_SimpleFunction('random', 'twig_random', array('needs_environment' => true)), + new Twig_SimpleFunction('date', 'twig_date_converter', array('needs_environment' => true)), + new Twig_SimpleFunction('include', 'twig_include', array('needs_environment' => true, 'needs_context' => true, 'is_safe' => array('all'))), + ); + } + + /** + * Returns a list of tests to add to the existing list. + * + * @return array An array of tests + */ + public function getTests() + { + return array( + new Twig_SimpleTest('even', null, array('node_class' => 'Twig_Node_Expression_Test_Even')), + new Twig_SimpleTest('odd', null, array('node_class' => 'Twig_Node_Expression_Test_Odd')), + new Twig_SimpleTest('defined', null, array('node_class' => 'Twig_Node_Expression_Test_Defined')), + new Twig_SimpleTest('sameas', null, array('node_class' => 'Twig_Node_Expression_Test_Sameas')), + new Twig_SimpleTest('none', null, array('node_class' => 'Twig_Node_Expression_Test_Null')), + new Twig_SimpleTest('null', null, array('node_class' => 'Twig_Node_Expression_Test_Null')), + new Twig_SimpleTest('divisibleby', null, array('node_class' => 'Twig_Node_Expression_Test_Divisibleby')), + new Twig_SimpleTest('constant', null, array('node_class' => 'Twig_Node_Expression_Test_Constant')), + new Twig_SimpleTest('empty', 'twig_test_empty'), + new Twig_SimpleTest('iterable', 'twig_test_iterable'), + ); + } + + /** + * Returns a list of operators to add to the existing list. + * + * @return array An array of operators + */ + public function getOperators() + { + return array( + array( + 'not' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'), + '-' => array('precedence' => 500, 'class' => 'Twig_Node_Expression_Unary_Neg'), + '+' => array('precedence' => 500, 'class' => 'Twig_Node_Expression_Unary_Pos'), + ), + array( + 'or' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + 'and' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + 'b-or' => array('precedence' => 16, 'class' => 'Twig_Node_Expression_Binary_BitwiseOr', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + 'b-xor' => array('precedence' => 17, 'class' => 'Twig_Node_Expression_Binary_BitwiseXor', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + 'b-and' => array('precedence' => 18, 'class' => 'Twig_Node_Expression_Binary_BitwiseAnd', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '==' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Equal', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '!=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '<' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Less', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '>' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Greater', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '>=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_GreaterEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '<=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_LessEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + 'not in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotIn', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + 'in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_In', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '..' => array('precedence' => 25, 'class' => 'Twig_Node_Expression_Binary_Range', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '+' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Add', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '-' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Sub', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '~' => array('precedence' => 40, 'class' => 'Twig_Node_Expression_Binary_Concat', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '*' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mul', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '/' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Div', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '//' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_FloorDiv', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '%' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mod', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + 'is' => array('precedence' => 100, 'callable' => array($this, 'parseTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + 'is not' => array('precedence' => 100, 'callable' => array($this, 'parseNotTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + '**' => array('precedence' => 200, 'class' => 'Twig_Node_Expression_Binary_Power', 'associativity' => Twig_ExpressionParser::OPERATOR_RIGHT), + ), + ); + } + + public function parseNotTestExpression(Twig_Parser $parser, $node) + { + return new Twig_Node_Expression_Unary_Not($this->parseTestExpression($parser, $node), $parser->getCurrentToken()->getLine()); + } + + public function parseTestExpression(Twig_Parser $parser, $node) + { + $stream = $parser->getStream(); + $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue(); + $arguments = null; + if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) { + $arguments = $parser->getExpressionParser()->parseArguments(true); + } + + $class = $this->getTestNodeClass($parser, $name, $node->getLine()); + + return new $class($node, $name, $arguments, $parser->getCurrentToken()->getLine()); + } + + protected function getTestNodeClass(Twig_Parser $parser, $name, $line) + { + $env = $parser->getEnvironment(); + $testMap = $env->getTests(); + if (!isset($testMap[$name])) { + $message = sprintf('The test "%s" does not exist', $name); + if ($alternatives = $env->computeAlternatives($name, array_keys($env->getTests()))) { + $message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives)); + } + + throw new Twig_Error_Syntax($message, $line, $parser->getFilename()); + } + + if ($testMap[$name] instanceof Twig_SimpleTest) { + return $testMap[$name]->getNodeClass(); + } + + return $testMap[$name] instanceof Twig_Test_Node ? $testMap[$name]->getClass() : 'Twig_Node_Expression_Test'; + } + + /** + * Returns the name of the extension. + * + * @return string The extension name + */ + public function getName() + { + return 'core'; + } +} + +/** + * Cycles over a value. + * + * @param ArrayAccess|array $values An array or an ArrayAccess instance + * @param integer $position The cycle position + * + * @return string The next value in the cycle + */ +function twig_cycle($values, $position) +{ + if (!is_array($values) && !$values instanceof ArrayAccess) { + return $values; + } + + return $values[$position % count($values)]; +} + +/** + * Returns a random value depending on the supplied parameter type: + * - a random item from a Traversable or array + * - a random character from a string + * - a random integer between 0 and the integer parameter + * + * @param Twig_Environment $env A Twig_Environment instance + * @param Traversable|array|integer|string $values The values to pick a random item from + * + * @throws Twig_Error_Runtime When $values is an empty array (does not apply to an empty string which is returned as is). + * + * @return mixed A random value from the given sequence + */ +function twig_random(Twig_Environment $env, $values = null) +{ + if (null === $values) { + return mt_rand(); + } + + if (is_int($values) || is_float($values)) { + return $values < 0 ? mt_rand($values, 0) : mt_rand(0, $values); + } + + if ($values instanceof Traversable) { + $values = iterator_to_array($values); + } elseif (is_string($values)) { + if ('' === $values) { + return ''; + } + if (null !== $charset = $env->getCharset()) { + if ('UTF-8' != $charset) { + $values = twig_convert_encoding($values, 'UTF-8', $charset); + } + + // unicode version of str_split() + // split at all positions, but not after the start and not before the end + $values = preg_split('/(? $value) { + $values[$i] = twig_convert_encoding($value, $charset, 'UTF-8'); + } + } + } else { + return $values[mt_rand(0, strlen($values) - 1)]; + } + } + + if (!is_array($values)) { + return $values; + } + + if (0 === count($values)) { + throw new Twig_Error_Runtime('The random function cannot pick from an empty array.'); + } + + return $values[array_rand($values, 1)]; +} + +/** + * Converts a date to the given format. + * + *
+ *   {{ post.published_at|date("m/d/Y") }}
+ * 
+ * + * @param Twig_Environment $env A Twig_Environment instance + * @param DateTime|DateInterval|string $date A date + * @param string $format A format + * @param DateTimeZone|string $timezone A timezone + * + * @return string The formatted date + */ +function twig_date_format_filter(Twig_Environment $env, $date, $format = null, $timezone = null) +{ + if (null === $format) { + $formats = $env->getExtension('core')->getDateFormat(); + $format = $date instanceof DateInterval ? $formats[1] : $formats[0]; + } + + if ($date instanceof DateInterval) { + return $date->format($format); + } + + return twig_date_converter($env, $date, $timezone)->format($format); +} + +/** + * Returns a new date object modified + * + *
+ *   {{ post.published_at|date_modify("-1day")|date("m/d/Y") }}
+ * 
+ * + * @param Twig_Environment $env A Twig_Environment instance + * @param DateTime|string $date A date + * @param string $modifier A modifier string + * + * @return DateTime A new date object + */ +function twig_date_modify_filter(Twig_Environment $env, $date, $modifier) +{ + $date = twig_date_converter($env, $date, false); + $date->modify($modifier); + + return $date; +} + +/** + * Converts an input to a DateTime instance. + * + *
+ *    {% if date(user.created_at) < date('+2days') %}
+ *      {# do something #}
+ *    {% endif %}
+ * 
+ * + * @param Twig_Environment $env A Twig_Environment instance + * @param DateTime|string $date A date + * @param DateTimeZone|string $timezone A timezone + * + * @return DateTime A DateTime instance + */ +function twig_date_converter(Twig_Environment $env, $date = null, $timezone = null) +{ + // determine the timezone + if (!$timezone) { + $defaultTimezone = $env->getExtension('core')->getTimezone(); + } elseif (!$timezone instanceof DateTimeZone) { + $defaultTimezone = new DateTimeZone($timezone); + } else { + $defaultTimezone = $timezone; + } + + if ($date instanceof DateTime) { + $date = clone $date; + if (false !== $timezone) { + $date->setTimezone($defaultTimezone); + } + + return $date; + } + + $asString = (string) $date; + if (ctype_digit($asString) || (!empty($asString) && '-' === $asString[0] && ctype_digit(substr($asString, 1)))) { + $date = '@'.$date; + } + + $date = new DateTime($date, $defaultTimezone); + if (false !== $timezone) { + $date->setTimezone($defaultTimezone); + } + + return $date; +} + +/** + * Number format filter. + * + * All of the formatting options can be left null, in that case the defaults will + * be used. Supplying any of the parameters will override the defaults set in the + * environment object. + * + * @param Twig_Environment $env A Twig_Environment instance + * @param mixed $number A float/int/string of the number to format + * @param integer $decimal The number of decimal points to display. + * @param string $decimalPoint The character(s) to use for the decimal point. + * @param string $thousandSep The character(s) to use for the thousands separator. + * + * @return string The formatted number + */ +function twig_number_format_filter(Twig_Environment $env, $number, $decimal = null, $decimalPoint = null, $thousandSep = null) +{ + $defaults = $env->getExtension('core')->getNumberFormat(); + if (null === $decimal) { + $decimal = $defaults[0]; + } + + if (null === $decimalPoint) { + $decimalPoint = $defaults[1]; + } + + if (null === $thousandSep) { + $thousandSep = $defaults[2]; + } + + return number_format((float) $number, $decimal, $decimalPoint, $thousandSep); +} + +/** + * URL encodes a string as a path segment or an array as a query string. + * + * @param string|array $url A URL or an array of query parameters + * @param bool $raw true to use rawurlencode() instead of urlencode + * + * @return string The URL encoded value + */ +function twig_urlencode_filter($url, $raw = false) +{ + if (is_array($url)) { + return http_build_query($url, '', '&'); + } + + if ($raw) { + return rawurlencode($url); + } + + return urlencode($url); +} + +if (version_compare(PHP_VERSION, '5.3.0', '<')) { + /** + * JSON encodes a variable. + * + * @param mixed $value The value to encode. + * @param integer $options Not used on PHP 5.2.x + * + * @return mixed The JSON encoded value + */ + function twig_jsonencode_filter($value, $options = 0) + { + if ($value instanceof Twig_Markup) { + $value = (string) $value; + } elseif (is_array($value)) { + array_walk_recursive($value, '_twig_markup2string'); + } + + return json_encode($value); + } +} else { + /** + * JSON encodes a variable. + * + * @param mixed $value The value to encode. + * @param integer $options Bitmask consisting of JSON_HEX_QUOT, JSON_HEX_TAG, JSON_HEX_AMP, JSON_HEX_APOS, JSON_NUMERIC_CHECK, JSON_PRETTY_PRINT, JSON_UNESCAPED_SLASHES, JSON_FORCE_OBJECT + * + * @return mixed The JSON encoded value + */ + function twig_jsonencode_filter($value, $options = 0) + { + if ($value instanceof Twig_Markup) { + $value = (string) $value; + } elseif (is_array($value)) { + array_walk_recursive($value, '_twig_markup2string'); + } + + return json_encode($value, $options); + } +} + +function _twig_markup2string(&$value) +{ + if ($value instanceof Twig_Markup) { + $value = (string) $value; + } +} + +/** + * Merges an array with another one. + * + *
+ *  {% set items = { 'apple': 'fruit', 'orange': 'fruit' } %}
+ *
+ *  {% set items = items|merge({ 'peugeot': 'car' }) %}
+ *
+ *  {# items now contains { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'car' } #}
+ * 
+ * + * @param array $arr1 An array + * @param array $arr2 An array + * + * @return array The merged array + */ +function twig_array_merge($arr1, $arr2) +{ + if (!is_array($arr1) || !is_array($arr2)) { + throw new Twig_Error_Runtime('The merge filter only works with arrays or hashes.'); + } + + return array_merge($arr1, $arr2); +} + +/** + * Slices a variable. + * + * @param Twig_Environment $env A Twig_Environment instance + * @param mixed $item A variable + * @param integer $start Start of the slice + * @param integer $length Size of the slice + * @param Boolean $preserveKeys Whether to preserve key or not (when the input is an array) + * + * @return mixed The sliced variable + */ +function twig_slice(Twig_Environment $env, $item, $start, $length = null, $preserveKeys = false) +{ + if ($item instanceof Traversable) { + $item = iterator_to_array($item, false); + } + + if (is_array($item)) { + return array_slice($item, $start, $length, $preserveKeys); + } + + $item = (string) $item; + + if (function_exists('mb_get_info') && null !== $charset = $env->getCharset()) { + return mb_substr($item, $start, null === $length ? mb_strlen($item, $charset) - $start : $length, $charset); + } + + return null === $length ? substr($item, $start) : substr($item, $start, $length); +} + +/** + * Returns the first element of the item. + * + * @param Twig_Environment $env A Twig_Environment instance + * @param mixed $item A variable + * + * @return mixed The first element of the item + */ +function twig_first(Twig_Environment $env, $item) +{ + $elements = twig_slice($env, $item, 0, 1, false); + + return is_string($elements) ? $elements[0] : current($elements); +} + +/** + * Returns the last element of the item. + * + * @param Twig_Environment $env A Twig_Environment instance + * @param mixed $item A variable + * + * @return mixed The last element of the item + */ +function twig_last(Twig_Environment $env, $item) +{ + $elements = twig_slice($env, $item, -1, 1, false); + + return is_string($elements) ? $elements[0] : current($elements); +} + +/** + * Joins the values to a string. + * + * The separator between elements is an empty string per default, you can define it with the optional parameter. + * + *
+ *  {{ [1, 2, 3]|join('|') }}
+ *  {# returns 1|2|3 #}
+ *
+ *  {{ [1, 2, 3]|join }}
+ *  {# returns 123 #}
+ * 
+ * + * @param array $value An array + * @param string $glue The separator + * + * @return string The concatenated string + */ +function twig_join_filter($value, $glue = '') +{ + if ($value instanceof Traversable) { + $value = iterator_to_array($value, false); + } + + return implode($glue, (array) $value); +} + +/** + * Splits the string into an array. + * + *
+ *  {{ "one,two,three"|split(',') }}
+ *  {# returns [one, two, three] #}
+ *
+ *  {{ "one,two,three,four,five"|split(',', 3) }}
+ *  {# returns [one, two, "three,four,five"] #}
+ *
+ *  {{ "123"|split('') }}
+ *  {# returns [1, 2, 3] #}
+ *
+ *  {{ "aabbcc"|split('', 2) }}
+ *  {# returns [aa, bb, cc] #}
+ * 
+ * + * @param string $value A string + * @param string $delimiter The delimiter + * @param integer $limit The limit + * + * @return array The split string as an array + */ +function twig_split_filter($value, $delimiter, $limit = null) +{ + if (empty($delimiter)) { + return str_split($value, null === $limit ? 1 : $limit); + } + + return null === $limit ? explode($delimiter, $value) : explode($delimiter, $value, $limit); +} + +// The '_default' filter is used internally to avoid using the ternary operator +// which costs a lot for big contexts (before PHP 5.4). So, on average, +// a function call is cheaper. +function _twig_default_filter($value, $default = '') +{ + if (twig_test_empty($value)) { + return $default; + } + + return $value; +} + +/** + * Returns the keys for the given array. + * + * It is useful when you want to iterate over the keys of an array: + * + *
+ *  {% for key in array|keys %}
+ *      {# ... #}
+ *  {% endfor %}
+ * 
+ * + * @param array $array An array + * + * @return array The keys + */ +function twig_get_array_keys_filter($array) +{ + if (is_object($array) && $array instanceof Traversable) { + return array_keys(iterator_to_array($array)); + } + + if (!is_array($array)) { + return array(); + } + + return array_keys($array); +} + +/** + * Reverses a variable. + * + * @param Twig_Environment $env A Twig_Environment instance + * @param array|Traversable|string $item An array, a Traversable instance, or a string + * @param Boolean $preserveKeys Whether to preserve key or not + * + * @return mixed The reversed input + */ +function twig_reverse_filter(Twig_Environment $env, $item, $preserveKeys = false) +{ + if (is_object($item) && $item instanceof Traversable) { + return array_reverse(iterator_to_array($item), $preserveKeys); + } + + if (is_array($item)) { + return array_reverse($item, $preserveKeys); + } + + if (null !== $charset = $env->getCharset()) { + $string = (string) $item; + + if ('UTF-8' != $charset) { + $item = twig_convert_encoding($string, 'UTF-8', $charset); + } + + preg_match_all('/./us', $item, $matches); + + $string = implode('', array_reverse($matches[0])); + + if ('UTF-8' != $charset) { + $string = twig_convert_encoding($string, $charset, 'UTF-8'); + } + + return $string; + } + + return strrev((string) $item); +} + +/** + * Sorts an array. + * + * @param array $array An array + */ +function twig_sort_filter($array) +{ + asort($array); + + return $array; +} + +/* used internally */ +function twig_in_filter($value, $compare) +{ + if (is_array($compare)) { + return in_array($value, $compare, is_object($value)); + } elseif (is_string($compare)) { + if (!strlen($value)) { + return empty($compare); + } + + return false !== strpos($compare, (string) $value); + } elseif ($compare instanceof Traversable) { + return in_array($value, iterator_to_array($compare, false), is_object($value)); + } + + return false; +} + +/** + * Escapes a string. + * + * @param Twig_Environment $env A Twig_Environment instance + * @param string $string The value to be escaped + * @param string $strategy The escaping strategy + * @param string $charset The charset + * @param Boolean $autoescape Whether the function is called by the auto-escaping feature (true) or by the developer (false) + */ +function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false) +{ + if ($autoescape && $string instanceof Twig_Markup) { + return $string; + } + + if (!is_string($string)) { + if (is_object($string) && method_exists($string, '__toString')) { + $string = (string) $string; + } else { + return $string; + } + } + + if (null === $charset) { + $charset = $env->getCharset(); + } + + switch ($strategy) { + case 'html': + // see http://php.net/htmlspecialchars + + // Using a static variable to avoid initializing the array + // each time the function is called. Moving the declaration on the + // top of the function slow downs other escaping strategies. + static $htmlspecialcharsCharsets = array( + 'ISO-8859-1' => true, 'ISO8859-1' => true, + 'ISO-8859-15' => true, 'ISO8859-15' => true, + 'utf-8' => true, 'UTF-8' => true, + 'CP866' => true, 'IBM866' => true, '866' => true, + 'CP1251' => true, 'WINDOWS-1251' => true, 'WIN-1251' => true, + '1251' => true, + 'CP1252' => true, 'WINDOWS-1252' => true, '1252' => true, + 'KOI8-R' => true, 'KOI8-RU' => true, 'KOI8R' => true, + 'BIG5' => true, '950' => true, + 'GB2312' => true, '936' => true, + 'BIG5-HKSCS' => true, + 'SHIFT_JIS' => true, 'SJIS' => true, '932' => true, + 'EUC-JP' => true, 'EUCJP' => true, + 'ISO8859-5' => true, 'ISO-8859-5' => true, 'MACROMAN' => true, + ); + + if (isset($htmlspecialcharsCharsets[$charset])) { + return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset); + } + + if (isset($htmlspecialcharsCharsets[strtoupper($charset)])) { + // cache the lowercase variant for future iterations + $htmlspecialcharsCharsets[$charset] = true; + + return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset); + } + + $string = twig_convert_encoding($string, 'UTF-8', $charset); + $string = htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); + + return twig_convert_encoding($string, $charset, 'UTF-8'); + + case 'js': + // escape all non-alphanumeric characters + // into their \xHH or \uHHHH representations + if ('UTF-8' != $charset) { + $string = twig_convert_encoding($string, 'UTF-8', $charset); + } + + if (0 == strlen($string) ? false : (1 == preg_match('/^./su', $string) ? false : true)) { + throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.'); + } + + $string = preg_replace_callback('#[^a-zA-Z0-9,\._]#Su', '_twig_escape_js_callback', $string); + + if ('UTF-8' != $charset) { + $string = twig_convert_encoding($string, $charset, 'UTF-8'); + } + + return $string; + + case 'css': + if ('UTF-8' != $charset) { + $string = twig_convert_encoding($string, 'UTF-8', $charset); + } + + if (0 == strlen($string) ? false : (1 == preg_match('/^./su', $string) ? false : true)) { + throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.'); + } + + $string = preg_replace_callback('#[^a-zA-Z0-9]#Su', '_twig_escape_css_callback', $string); + + if ('UTF-8' != $charset) { + $string = twig_convert_encoding($string, $charset, 'UTF-8'); + } + + return $string; + + case 'html_attr': + if ('UTF-8' != $charset) { + $string = twig_convert_encoding($string, 'UTF-8', $charset); + } + + if (0 == strlen($string) ? false : (1 == preg_match('/^./su', $string) ? false : true)) { + throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.'); + } + + $string = preg_replace_callback('#[^a-zA-Z0-9,\.\-_]#Su', '_twig_escape_html_attr_callback', $string); + + if ('UTF-8' != $charset) { + $string = twig_convert_encoding($string, $charset, 'UTF-8'); + } + + return $string; + + case 'url': + // hackish test to avoid version_compare that is much slower, this works unless PHP releases a 5.10.* + // at that point however PHP 5.2.* support can be removed + if (PHP_VERSION < '5.3.0') { + return str_replace('%7E', '~', rawurlencode($string)); + } + + return rawurlencode($string); + + default: + throw new Twig_Error_Runtime(sprintf('Invalid escaping strategy "%s" (valid ones: html, js, url, css, and html_attr).', $strategy)); + } +} + +/* used internally */ +function twig_escape_filter_is_safe(Twig_Node $filterArgs) +{ + foreach ($filterArgs as $arg) { + if ($arg instanceof Twig_Node_Expression_Constant) { + return array($arg->getAttribute('value')); + } + + return array(); + } + + return array('html'); +} + +if (function_exists('mb_convert_encoding')) { + function twig_convert_encoding($string, $to, $from) + { + return mb_convert_encoding($string, $to, $from); + } +} elseif (function_exists('iconv')) { + function twig_convert_encoding($string, $to, $from) + { + return iconv($from, $to, $string); + } +} else { + function twig_convert_encoding($string, $to, $from) + { + throw new Twig_Error_Runtime('No suitable convert encoding function (use UTF-8 as your encoding or install the iconv or mbstring extension).'); + } +} + +function _twig_escape_js_callback($matches) +{ + $char = $matches[0]; + + // \xHH + if (!isset($char[1])) { + return '\\x'.strtoupper(substr('00'.bin2hex($char), -2)); + } + + // \uHHHH + $char = twig_convert_encoding($char, 'UTF-16BE', 'UTF-8'); + + return '\\u'.strtoupper(substr('0000'.bin2hex($char), -4)); +} + +function _twig_escape_css_callback($matches) +{ + $char = $matches[0]; + + // \xHH + if (!isset($char[1])) { + $hex = ltrim(strtoupper(bin2hex($char)), '0'); + if (0 === strlen($hex)) { + $hex = '0'; + } + + return '\\'.$hex.' '; + } + + // \uHHHH + $char = twig_convert_encoding($char, 'UTF-16BE', 'UTF-8'); + + return '\\'.ltrim(strtoupper(bin2hex($char)), '0').' '; +} + +/** + * This function is adapted from code coming from Zend Framework. + * + * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +function _twig_escape_html_attr_callback($matches) +{ + /* + * While HTML supports far more named entities, the lowest common denominator + * has become HTML5's XML Serialisation which is restricted to the those named + * entities that XML supports. Using HTML entities would result in this error: + * XML Parsing Error: undefined entity + */ + static $entityMap = array( + 34 => 'quot', /* quotation mark */ + 38 => 'amp', /* ampersand */ + 60 => 'lt', /* less-than sign */ + 62 => 'gt', /* greater-than sign */ + ); + + $chr = $matches[0]; + $ord = ord($chr); + + /** + * The following replaces characters undefined in HTML with the + * hex entity for the Unicode replacement character. + */ + if (($ord <= 0x1f && $chr != "\t" && $chr != "\n" && $chr != "\r") || ($ord >= 0x7f && $ord <= 0x9f)) { + return '�'; + } + + /** + * Check if the current character to escape has a name entity we should + * replace it with while grabbing the hex value of the character. + */ + if (strlen($chr) == 1) { + $hex = strtoupper(substr('00'.bin2hex($chr), -2)); + } else { + $chr = twig_convert_encoding($chr, 'UTF-16BE', 'UTF-8'); + $hex = strtoupper(substr('0000'.bin2hex($chr), -4)); + } + + $int = hexdec($hex); + if (array_key_exists($int, $entityMap)) { + return sprintf('&%s;', $entityMap[$int]); + } + + /** + * Per OWASP recommendations, we'll use hex entities for any other + * characters where a named entity does not exist. + */ + + return sprintf('&#x%s;', $hex); +} + +// add multibyte extensions if possible +if (function_exists('mb_get_info')) { + /** + * Returns the length of a variable. + * + * @param Twig_Environment $env A Twig_Environment instance + * @param mixed $thing A variable + * + * @return integer The length of the value + */ + function twig_length_filter(Twig_Environment $env, $thing) + { + return is_scalar($thing) ? mb_strlen($thing, $env->getCharset()) : count($thing); + } + + /** + * Converts a string to uppercase. + * + * @param Twig_Environment $env A Twig_Environment instance + * @param string $string A string + * + * @return string The uppercased string + */ + function twig_upper_filter(Twig_Environment $env, $string) + { + if (null !== ($charset = $env->getCharset())) { + return mb_strtoupper($string, $charset); + } + + return strtoupper($string); + } + + /** + * Converts a string to lowercase. + * + * @param Twig_Environment $env A Twig_Environment instance + * @param string $string A string + * + * @return string The lowercased string + */ + function twig_lower_filter(Twig_Environment $env, $string) + { + if (null !== ($charset = $env->getCharset())) { + return mb_strtolower($string, $charset); + } + + return strtolower($string); + } + + /** + * Returns a titlecased string. + * + * @param Twig_Environment $env A Twig_Environment instance + * @param string $string A string + * + * @return string The titlecased string + */ + function twig_title_string_filter(Twig_Environment $env, $string) + { + if (null !== ($charset = $env->getCharset())) { + return mb_convert_case($string, MB_CASE_TITLE, $charset); + } + + return ucwords(strtolower($string)); + } + + /** + * Returns a capitalized string. + * + * @param Twig_Environment $env A Twig_Environment instance + * @param string $string A string + * + * @return string The capitalized string + */ + function twig_capitalize_string_filter(Twig_Environment $env, $string) + { + if (null !== ($charset = $env->getCharset())) { + return mb_strtoupper(mb_substr($string, 0, 1, $charset), $charset). + mb_strtolower(mb_substr($string, 1, mb_strlen($string, $charset), $charset), $charset); + } + + return ucfirst(strtolower($string)); + } +} +// and byte fallback +else { + /** + * Returns the length of a variable. + * + * @param Twig_Environment $env A Twig_Environment instance + * @param mixed $thing A variable + * + * @return integer The length of the value + */ + function twig_length_filter(Twig_Environment $env, $thing) + { + return is_scalar($thing) ? strlen($thing) : count($thing); + } + + /** + * Returns a titlecased string. + * + * @param Twig_Environment $env A Twig_Environment instance + * @param string $string A string + * + * @return string The titlecased string + */ + function twig_title_string_filter(Twig_Environment $env, $string) + { + return ucwords(strtolower($string)); + } + + /** + * Returns a capitalized string. + * + * @param Twig_Environment $env A Twig_Environment instance + * @param string $string A string + * + * @return string The capitalized string + */ + function twig_capitalize_string_filter(Twig_Environment $env, $string) + { + return ucfirst(strtolower($string)); + } +} + +/* used internally */ +function twig_ensure_traversable($seq) +{ + if ($seq instanceof Traversable || is_array($seq)) { + return $seq; + } + + return array(); +} + +/** + * Checks if a variable is empty. + * + *
+ * {# evaluates to true if the foo variable is null, false, or the empty string #}
+ * {% if foo is empty %}
+ *     {# ... #}
+ * {% endif %}
+ * 
+ * + * @param mixed $value A variable + * + * @return Boolean true if the value is empty, false otherwise + */ +function twig_test_empty($value) +{ + if ($value instanceof Countable) { + return 0 == count($value); + } + + return '' === $value || false === $value || null === $value || array() === $value; +} + +/** + * Checks if a variable is traversable. + * + *
+ * {# evaluates to true if the foo variable is an array or a traversable object #}
+ * {% if foo is traversable %}
+ *     {# ... #}
+ * {% endif %}
+ * 
+ * + * @param mixed $value A variable + * + * @return Boolean true if the value is traversable + */ +function twig_test_iterable($value) +{ + return $value instanceof Traversable || is_array($value); +} + +/** + * Renders a template. + * + * @param string $template The template to render + * @param array $variables The variables to pass to the template + * @param Boolean $with_context Whether to pass the current context variables or not + * @param Boolean $ignore_missing Whether to ignore missing templates or not + * @param Boolean $sandboxed Whether to sandbox the template or not + * + * @return string The rendered template + */ +function twig_include(Twig_Environment $env, $context, $template, $variables = array(), $withContext = true, $ignoreMissing = false, $sandboxed = false) +{ + if ($withContext) { + $variables = array_merge($context, $variables); + } + + if ($isSandboxed = $sandboxed && $env->hasExtension('sandbox')) { + $sandbox = $env->getExtension('sandbox'); + if (!$alreadySandboxed = $sandbox->isSandboxed()) { + $sandbox->enableSandbox(); + } + } + + try { + return $env->resolveTemplate($template)->render($variables); + } catch (Twig_Error_Loader $e) { + if (!$ignoreMissing) { + throw $e; + } + } + + if ($isSandboxed && !$alreadySandboxed) { + $sandbox->disableSandbox(); + } +} + +/** + * Provides the ability to get constants from instances as well as class/global constants. + * + * @param string $constant The name of the constant + * @param null|object $object The object to get the constant from + * + * @return string + */ +function twig_constant($constant, $object = null) +{ + if (null !== $object) { + $constant = get_class($object).'::'.$constant; + } + + return constant($constant); +} + +/** + * Batches item. + * + * @param array $items An array of items + * @param integer $size The size of the batch + * @param string $fill A string to fill missing items + * + * @return array + */ +function twig_array_batch($items, $size, $fill = null) +{ + if ($items instanceof Traversable) { + $items = iterator_to_array($items, false); + } + + $size = ceil($size); + + $result = array_chunk($items, $size, true); + + if (null !== $fill) { + $last = count($result) - 1; + $result[$last] = array_merge( + $result[$last], + array_fill(0, $size - count($result[$last]), $fill) + ); + } + + return $result; +} diff --git a/inc/Twig/Extension/Debug.php b/inc/Twig/Extension/Debug.php new file mode 100644 index 0000000..e3a85bf --- /dev/null +++ b/inc/Twig/Extension/Debug.php @@ -0,0 +1,71 @@ + $isDumpOutputHtmlSafe ? array('html') : array(), 'needs_context' => true, 'needs_environment' => true)), + ); + } + + /** + * Returns the name of the extension. + * + * @return string The extension name + */ + public function getName() + { + return 'debug'; + } +} + +function twig_var_dump(Twig_Environment $env, $context) +{ + if (!$env->isDebug()) { + return; + } + + ob_start(); + + $count = func_num_args(); + if (2 === $count) { + $vars = array(); + foreach ($context as $key => $value) { + if (!$value instanceof Twig_Template) { + $vars[$key] = $value; + } + } + + var_dump($vars); + } else { + for ($i = 2; $i < $count; $i++) { + var_dump(func_get_arg($i)); + } + } + + return ob_get_clean(); +} diff --git a/inc/Twig/Extension/Escaper.php b/inc/Twig/Extension/Escaper.php new file mode 100644 index 0000000..c9a7f68 --- /dev/null +++ b/inc/Twig/Extension/Escaper.php @@ -0,0 +1,107 @@ +setDefaultStrategy($defaultStrategy); + } + + /** + * Returns the token parser instances to add to the existing list. + * + * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances + */ + public function getTokenParsers() + { + return array(new Twig_TokenParser_AutoEscape()); + } + + /** + * Returns the node visitor instances to add to the existing list. + * + * @return array An array of Twig_NodeVisitorInterface instances + */ + public function getNodeVisitors() + { + return array(new Twig_NodeVisitor_Escaper()); + } + + /** + * Returns a list of filters to add to the existing list. + * + * @return array An array of filters + */ + public function getFilters() + { + return array( + new Twig_SimpleFilter('raw', 'twig_raw_filter', array('is_safe' => array('all'))), + ); + } + + /** + * Sets the default strategy to use when not defined by the user. + * + * The strategy can be a valid PHP callback that takes the template + * "filename" as an argument and returns the strategy to use. + * + * @param mixed $defaultStrategy An escaping strategy + */ + public function setDefaultStrategy($defaultStrategy) + { + // for BC + if (true === $defaultStrategy) { + $defaultStrategy = 'html'; + } + + $this->defaultStrategy = $defaultStrategy; + } + + /** + * Gets the default strategy to use when not defined by the user. + * + * @param string $filename The template "filename" + * + * @return string The default strategy to use for the template + */ + public function getDefaultStrategy($filename) + { + // disable string callables to avoid calling a function named html or js, + // or any other upcoming escaping strategy + if (!is_string($this->defaultStrategy) && is_callable($this->defaultStrategy)) { + return call_user_func($this->defaultStrategy, $filename); + } + + return $this->defaultStrategy; + } + + /** + * Returns the name of the extension. + * + * @return string The extension name + */ + public function getName() + { + return 'escaper'; + } +} + +/** + * Marks a variable as being safe. + * + * @param string $string A PHP variable + */ +function twig_raw_filter($string) +{ + return $string; +} diff --git a/inc/Twig/Extension/Optimizer.php b/inc/Twig/Extension/Optimizer.php new file mode 100644 index 0000000..013fcb6 --- /dev/null +++ b/inc/Twig/Extension/Optimizer.php @@ -0,0 +1,35 @@ +optimizers = $optimizers; + } + + /** + * {@inheritdoc} + */ + public function getNodeVisitors() + { + return array(new Twig_NodeVisitor_Optimizer($this->optimizers)); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'optimizer'; + } +} diff --git a/inc/Twig/Extension/Sandbox.php b/inc/Twig/Extension/Sandbox.php new file mode 100644 index 0000000..bf76c11 --- /dev/null +++ b/inc/Twig/Extension/Sandbox.php @@ -0,0 +1,112 @@ +policy = $policy; + $this->sandboxedGlobally = $sandboxed; + } + + /** + * Returns the token parser instances to add to the existing list. + * + * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances + */ + public function getTokenParsers() + { + return array(new Twig_TokenParser_Sandbox()); + } + + /** + * Returns the node visitor instances to add to the existing list. + * + * @return array An array of Twig_NodeVisitorInterface instances + */ + public function getNodeVisitors() + { + return array(new Twig_NodeVisitor_Sandbox()); + } + + public function enableSandbox() + { + $this->sandboxed = true; + } + + public function disableSandbox() + { + $this->sandboxed = false; + } + + public function isSandboxed() + { + return $this->sandboxedGlobally || $this->sandboxed; + } + + public function isSandboxedGlobally() + { + return $this->sandboxedGlobally; + } + + public function setSecurityPolicy(Twig_Sandbox_SecurityPolicyInterface $policy) + { + $this->policy = $policy; + } + + public function getSecurityPolicy() + { + return $this->policy; + } + + public function checkSecurity($tags, $filters, $functions) + { + if ($this->isSandboxed()) { + $this->policy->checkSecurity($tags, $filters, $functions); + } + } + + public function checkMethodAllowed($obj, $method) + { + if ($this->isSandboxed()) { + $this->policy->checkMethodAllowed($obj, $method); + } + } + + public function checkPropertyAllowed($obj, $method) + { + if ($this->isSandboxed()) { + $this->policy->checkPropertyAllowed($obj, $method); + } + } + + public function ensureToStringAllowed($obj) + { + if (is_object($obj)) { + $this->policy->checkMethodAllowed($obj, '__toString'); + } + + return $obj; + } + + /** + * Returns the name of the extension. + * + * @return string The extension name + */ + public function getName() + { + return 'sandbox'; + } +} diff --git a/inc/Twig/Extension/Staging.php b/inc/Twig/Extension/Staging.php new file mode 100644 index 0000000..8ab0f45 --- /dev/null +++ b/inc/Twig/Extension/Staging.php @@ -0,0 +1,113 @@ + + */ +class Twig_Extension_Staging extends Twig_Extension +{ + protected $functions = array(); + protected $filters = array(); + protected $visitors = array(); + protected $tokenParsers = array(); + protected $globals = array(); + protected $tests = array(); + + public function addFunction($name, $function) + { + $this->functions[$name] = $function; + } + + /** + * {@inheritdoc} + */ + public function getFunctions() + { + return $this->functions; + } + + public function addFilter($name, $filter) + { + $this->filters[$name] = $filter; + } + + /** + * {@inheritdoc} + */ + public function getFilters() + { + return $this->filters; + } + + public function addNodeVisitor(Twig_NodeVisitorInterface $visitor) + { + $this->visitors[] = $visitor; + } + + /** + * {@inheritdoc} + */ + public function getNodeVisitors() + { + return $this->visitors; + } + + public function addTokenParser(Twig_TokenParserInterface $parser) + { + $this->tokenParsers[] = $parser; + } + + /** + * {@inheritdoc} + */ + public function getTokenParsers() + { + return $this->tokenParsers; + } + + public function addGlobal($name, $value) + { + $this->globals[$name] = $value; + } + + /** + * {@inheritdoc} + */ + public function getGlobals() + { + return $this->globals; + } + + public function addTest($name, $test) + { + $this->tests[$name] = $test; + } + + /** + * {@inheritdoc} + */ + public function getTests() + { + return $this->tests; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'staging'; + } +} diff --git a/inc/Twig/Extension/StringLoader.php b/inc/Twig/Extension/StringLoader.php new file mode 100644 index 0000000..20f3f99 --- /dev/null +++ b/inc/Twig/Extension/StringLoader.php @@ -0,0 +1,64 @@ + true)), + ); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'string_loader'; + } +} + +/** + * Loads a template from a string. + * + *
+ * {{ include(template_from_string("Hello {{ name }}")) }}
+ * 
+ * + * @param Twig_Environment $env A Twig_Environment instance + * @param string $template A template as a string + * + * @return Twig_Template A Twig_Template instance + */ +function twig_template_from_string(Twig_Environment $env, $template) +{ + static $loader; + + if (null === $loader) { + $loader = new Twig_Loader_String(); + } + + $current = $env->getLoader(); + $env->setLoader($loader); + try { + $template = $env->loadTemplate($template); + } catch (Exception $e) { + $env->setLoader($current); + + throw $e; + } + $env->setLoader($current); + + return $template; +} diff --git a/inc/Twig/ExtensionInterface.php b/inc/Twig/ExtensionInterface.php new file mode 100644 index 0000000..f189e9d --- /dev/null +++ b/inc/Twig/ExtensionInterface.php @@ -0,0 +1,83 @@ + + */ +interface Twig_ExtensionInterface +{ + /** + * Initializes the runtime environment. + * + * This is where you can load some file that contains filter functions for instance. + * + * @param Twig_Environment $environment The current Twig_Environment instance + */ + public function initRuntime(Twig_Environment $environment); + + /** + * Returns the token parser instances to add to the existing list. + * + * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances + */ + public function getTokenParsers(); + + /** + * Returns the node visitor instances to add to the existing list. + * + * @return array An array of Twig_NodeVisitorInterface instances + */ + public function getNodeVisitors(); + + /** + * Returns a list of filters to add to the existing list. + * + * @return array An array of filters + */ + public function getFilters(); + + /** + * Returns a list of tests to add to the existing list. + * + * @return array An array of tests + */ + public function getTests(); + + /** + * Returns a list of functions to add to the existing list. + * + * @return array An array of functions + */ + public function getFunctions(); + + /** + * Returns a list of operators to add to the existing list. + * + * @return array An array of operators + */ + public function getOperators(); + + /** + * Returns a list of global variables to add to the existing list. + * + * @return array An array of global variables + */ + public function getGlobals(); + + /** + * Returns the name of the extension. + * + * @return string The extension name + */ + public function getName(); +} diff --git a/inc/Twig/Extensions/Autoloader.php b/inc/Twig/Extensions/Autoloader.php new file mode 100644 index 0000000..f23cced --- /dev/null +++ b/inc/Twig/Extensions/Autoloader.php @@ -0,0 +1,45 @@ + + */ +class Twig_Extensions_Autoloader +{ + /** + * Registers Twig_Extensions_Autoloader as an SPL autoloader. + */ + static public function register() + { + spl_autoload_register(array(new self, 'autoload')); + } + + /** + * Handles autoloading of classes. + * + * @param string $class A class name. + * + * @return boolean Returns true if the class has been loaded + */ + static public function autoload($class) + { + if (0 !== strpos($class, 'Twig_Extensions')) { + return; + } + + if (file_exists($file = dirname(__FILE__).'/../../'.str_replace('_', '/', $class).'.php')) { + require $file; + } + } +} diff --git a/inc/Twig/Extensions/Extension/Debug.php b/inc/Twig/Extensions/Extension/Debug.php new file mode 100644 index 0000000..8974ce2 --- /dev/null +++ b/inc/Twig/Extensions/Extension/Debug.php @@ -0,0 +1,34 @@ + new Twig_Filter_Function('gettext'), + ); + } + + /** + * Returns the name of the extension. + * + * @return string The extension name + */ + public function getName() + { + return 'i18n'; + } +} diff --git a/inc/Twig/Extensions/Extension/Intl.php b/inc/Twig/Extensions/Extension/Intl.php new file mode 100644 index 0000000..40f7fc2 --- /dev/null +++ b/inc/Twig/Extensions/Extension/Intl.php @@ -0,0 +1,66 @@ + new Twig_Filter_Function('twig_localized_date_filter', array('needs_environment' => true)), + ); + } + + /** + * Returns the name of the extension. + * + * @return string The extension name + */ + public function getName() + { + return 'intl'; + } +} + +function twig_localized_date_filter(Twig_Environment $env, $date, $dateFormat = 'medium', $timeFormat = 'medium', $locale = null, $timezone = null, $format = null) +{ + $date = twig_date_converter($env, $date, $timezone); + + $formatValues = array( + 'none' => IntlDateFormatter::NONE, + 'short' => IntlDateFormatter::SHORT, + 'medium' => IntlDateFormatter::MEDIUM, + 'long' => IntlDateFormatter::LONG, + 'full' => IntlDateFormatter::FULL, + ); + + $formatter = IntlDateFormatter::create( + $locale !== null ? $locale : Locale::getDefault(), + $formatValues[$dateFormat], + $formatValues[$timeFormat], + $date->getTimezone()->getName(), + IntlDateFormatter::GREGORIAN, + $format + ); + + return $formatter->format($date->getTimestamp()); +} diff --git a/inc/Twig/Extensions/Extension/Text.php b/inc/Twig/Extensions/Extension/Text.php new file mode 100644 index 0000000..0a3dc35 --- /dev/null +++ b/inc/Twig/Extensions/Extension/Text.php @@ -0,0 +1,109 @@ + + * @package Twig + * @subpackage Twig-extensions + */ +class Twig_Extensions_Extension_Text extends Twig_Extension +{ + /** + * Returns a list of filters. + * + * @return array + */ + public function getFilters() + { + $filters = array( + 'truncate' => new Twig_Filter_Function('twig_truncate_filter', array('needs_environment' => true)), + 'wordwrap' => new Twig_Filter_Function('twig_wordwrap_filter', array('needs_environment' => true)), + ); + + if (version_compare(Twig_Environment::VERSION, '1.5.0-DEV', '<')) { + $filters['nl2br'] = new Twig_Filter_Function('twig_nl2br_filter', array('pre_escape' => 'html', 'is_safe' => array('html'))); + } + + return $filters; + } + + /** + * Name of this extension + * + * @return string + */ + public function getName() + { + return 'Text'; + } +} + +function twig_nl2br_filter($value, $sep = '
') +{ + return str_replace("\n", $sep."\n", $value); +} + +if (function_exists('mb_get_info')) { + function twig_truncate_filter(Twig_Environment $env, $value, $length = 30, $preserve = false, $separator = '...') + { + if (mb_strlen($value, $env->getCharset()) > $length) { + if ($preserve) { + if (false !== ($breakpoint = mb_strpos($value, ' ', $length, $env->getCharset()))) { + $length = $breakpoint; + } + } + + return rtrim(mb_substr($value, 0, $length, $env->getCharset())) . $separator; + } + + return $value; + } + + function twig_wordwrap_filter(Twig_Environment $env, $value, $length = 80, $separator = "\n", $preserve = false) + { + $sentences = array(); + + $previous = mb_regex_encoding(); + mb_regex_encoding($env->getCharset()); + + $pieces = mb_split($separator, $value); + mb_regex_encoding($previous); + + foreach ($pieces as $piece) { + while(!$preserve && mb_strlen($piece, $env->getCharset()) > $length) { + $sentences[] = mb_substr($piece, 0, $length, $env->getCharset()); + $piece = mb_substr($piece, $length, 2048, $env->getCharset()); + } + + $sentences[] = $piece; + } + + return implode($separator, $sentences); + } +} else { + function twig_truncate_filter(Twig_Environment $env, $value, $length = 30, $preserve = false, $separator = '...') + { + if (strlen($value) > $length) { + if ($preserve) { + if (false !== ($breakpoint = strpos($value, ' ', $length))) { + $length = $breakpoint; + } + } + + return rtrim(substr($value, 0, $length)) . $separator; + } + + return $value; + } + + function twig_wordwrap_filter(Twig_Environment $env, $value, $length = 80, $separator = "\n", $preserve = false) + { + return wordwrap($value, $length, $separator, !$preserve); + } +} \ No newline at end of file diff --git a/inc/Twig/Extensions/Gettext/Extractor.php b/inc/Twig/Extensions/Gettext/Extractor.php new file mode 100644 index 0000000..e7fa1af --- /dev/null +++ b/inc/Twig/Extensions/Gettext/Extractor.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Twig\Gettext; + +use Symfony\Component\Filesystem\Filesystem; + +/** + * Extracts translations from twig templates. + * + * @author Саша Стаменковић + */ +class Extractor +{ + /** + * @var \Twig_Environment + */ + protected $environment; + + /** + * Template cached file names. + * + * @var string[] + */ + protected $templates; + + /** + * Gettext parameters. + * + * @var string[] + */ + protected $parameters; + + public function __construct(\Twig_Environment $environment) + { + $this->environment = $environment; + $this->reset(); + } + + protected function reset() + { + $this->templates = array(); + $this->parameters = array(); + } + + public function addTemplate($path) + { + $this->environment->loadTemplate($path); + $this->templates[] = $this->environment->getCacheFilename($path); + } + + public function addGettextParameter($parameter) + { + $this->parameters[] = $parameter; + } + + public function setGettextParameters(array $parameters) + { + $this->parameters = $parameters; + } + + public function extract() + { + $command = 'xgettext'; + $command .= ' '.join(' ', $this->parameters); + $command .= ' '.join(' ', $this->templates); + + $error = 0; + $output = system($command, $error); + if (0 !== $error) { + throw new \RuntimeException(sprintf( + 'Gettext command "%s" failed with error code %s and output: %s', + $command, + $error, + $output + )); + } + + $this->reset(); + } + + public function __destruct() + { + $filesystem = new Filesystem(); + $filesystem->remove($this->environment->getCache()); + } +} diff --git a/inc/Twig/Extensions/Gettext/Loader/Filesystem.php b/inc/Twig/Extensions/Gettext/Loader/Filesystem.php new file mode 100644 index 0000000..b011b03 --- /dev/null +++ b/inc/Twig/Extensions/Gettext/Loader/Filesystem.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Twig\Gettext\Loader; + +/** + * Loads template from the filesystem. + * + * @author Саша Стаменковић + */ +class Filesystem extends \Twig_Loader_Filesystem +{ + /** + * Hacked find template to allow loading templates by absolute path. + * + * @param string $name template name or absolute path + */ + protected function findTemplate($name) + { + // normalize name + $name = preg_replace('#/{2,}#', '/', strtr($name, '\\', '/')); + + if (isset($this->cache[$name])) { + return $this->cache[$name]; + } + + $this->validateName($name); + + $namespace = '__main__'; + if (isset($name[0]) && '@' == $name[0]) { + if (false === $pos = strpos($name, '/')) { + throw new \InvalidArgumentException(sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name)); + } + + $namespace = substr($name, 1, $pos - 1); + + $name = substr($name, $pos + 1); + } + + if (!isset($this->paths[$namespace])) { + throw new \Twig_Error_Loader(sprintf('There are no registered paths for namespace "%s".', $namespace)); + } + + if (is_file($name)) { + return $this->cache[$name] = $name; + } + + return __DIR__.'/../Test/Fixtures/twig/empty.twig'; + } +} diff --git a/inc/Twig/Extensions/Gettext/Routing/Generator/UrlGenerator.php b/inc/Twig/Extensions/Gettext/Routing/Generator/UrlGenerator.php new file mode 100644 index 0000000..9e3431b --- /dev/null +++ b/inc/Twig/Extensions/Gettext/Routing/Generator/UrlGenerator.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Twig\Gettext\Routing\Generator; + +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Routing\RequestContext; + +/** + * Dummy url generator. + * + * @author Саша Стаменковић + */ +class UrlGenerator implements UrlGeneratorInterface +{ + protected $context; + + public function generate($name, $parameters = array(), $absolute = false) + { + } + + public function getContext() + { + return $this->context; + } + + public function setContext(RequestContext $context) + { + $this->context = $context; + } +} diff --git a/inc/Twig/Extensions/Gettext/Test/ExtractorTest.php b/inc/Twig/Extensions/Gettext/Test/ExtractorTest.php new file mode 100644 index 0000000..d467835 --- /dev/null +++ b/inc/Twig/Extensions/Gettext/Test/ExtractorTest.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Twig\Gettext\Test; + +use Twig\Gettext\Extractor; +use Twig\Gettext\Loader\Filesystem; +use Symfony\Component\Translation\Loader\PoFileLoader; + +/** + * @author Саша Стаменковић + */ +class ExtractorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Twig_Environment + */ + protected $twig; + + /** + * @var PoFileLoader + */ + protected $loader; + + protected function setUp() + { + $this->twig = new \Twig_Environment(new Filesystem('/'), array( + 'cache' => '/tmp/cache/'.uniqid(), + 'auto_reload' => true + )); + $this->twig->addExtension(new \Twig_Extensions_Extension_I18n()); + + $this->loader = new PoFileLoader(); + } + + /** + * @dataProvider testExtractDataProvider + */ + public function testExtract(array $templates, array $parameters, array $messages) + { + $extractor = new Extractor($this->twig); + + foreach ($templates as $template) { + $extractor->addTemplate($template); + } + foreach ($parameters as $parameter) { + $extractor->addGettextParameter($parameter); + } + + $extractor->extract(); + + $catalog = $this->loader->load($this->getPotFile(), null); + + foreach ($messages as $message) { + $this->assertTrue( + $catalog->has($message), + sprintf('Message "%s" not found in catalog.', $message) + ); + } + } + + public function testExtractDataProvider() + { + return array( + array( + array( + __DIR__.'/Fixtures/twig/singular.twig', + __DIR__.'/Fixtures/twig/plural.twig', + ), + $this->getGettextParameters(), + array( + 'Hello %name%!', + 'Hello World!', + 'Hey %name%, I have one apple.', + 'Hey %name%, I have %count% apples.', + ), + ), + ); + } + + public function testExtractNoTranslations() + { + $extractor = new Extractor($this->twig); + + $extractor->addTemplate(__DIR__.'/Fixtures/twig/empty.twig'); + $extractor->setGettextParameters($this->getGettextParameters()); + + $extractor->extract(); + + $catalog = $this->loader->load($this->getPotFile(), null); + + $this->assertEmpty($catalog->all('messages')); + } + + private function getPotFile() + { + return __DIR__.'/Fixtures/messages.pot'; + } + + private function getGettextParameters() + { + return array( + '--force-po', + '-o', + $this->getPotFile(), + ); + } + + protected function tearDown() + { + if (file_exists($this->getPotFile())) { + unlink($this->getPotFile()); + } + } +} diff --git a/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/empty.twig b/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/empty.twig new file mode 100644 index 0000000..05f0d26 --- /dev/null +++ b/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/empty.twig @@ -0,0 +1 @@ +Nothing to translate here. diff --git a/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/plural.twig b/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/plural.twig new file mode 100644 index 0000000..f9754ff --- /dev/null +++ b/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/plural.twig @@ -0,0 +1,5 @@ +{% trans %} + Hey {{ name }}, I have one apple. +{% plural apple_count %} + Hey {{ name }}, I have {{ count }} apples. +{% endtrans %} diff --git a/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/singular.twig b/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/singular.twig new file mode 100644 index 0000000..d757cf9 --- /dev/null +++ b/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/singular.twig @@ -0,0 +1,9 @@ +{% trans "Hello World!" %} + +{% trans %} + Hello World! +{% endtrans %} + +{% trans %} + Hello {{ name }}! +{% endtrans %} diff --git a/inc/Twig/Extensions/Grammar.php b/inc/Twig/Extensions/Grammar.php new file mode 100644 index 0000000..4d031b1 --- /dev/null +++ b/inc/Twig/Extensions/Grammar.php @@ -0,0 +1,30 @@ +name = $name; + } + + public function setParser(Twig_ParserInterface $parser) + { + $this->parser = $parser; + } + + public function getName() + { + return $this->name; + } +} diff --git a/inc/Twig/Extensions/Grammar/Arguments.php b/inc/Twig/Extensions/Grammar/Arguments.php new file mode 100644 index 0000000..158c05a --- /dev/null +++ b/inc/Twig/Extensions/Grammar/Arguments.php @@ -0,0 +1,22 @@ +', $this->name); + } + + public function parse(Twig_Token $token) + { + return $this->parser->getExpressionParser()->parseArguments(); + } +} diff --git a/inc/Twig/Extensions/Grammar/Array.php b/inc/Twig/Extensions/Grammar/Array.php new file mode 100644 index 0000000..34aece0 --- /dev/null +++ b/inc/Twig/Extensions/Grammar/Array.php @@ -0,0 +1,22 @@ +', $this->name); + } + + public function parse(Twig_Token $token) + { + return $this->parser->getExpressionParser()->parseArrayExpression(); + } +} diff --git a/inc/Twig/Extensions/Grammar/Body.php b/inc/Twig/Extensions/Grammar/Body.php new file mode 100644 index 0000000..540cfc7 --- /dev/null +++ b/inc/Twig/Extensions/Grammar/Body.php @@ -0,0 +1,39 @@ +end = null === $end ? 'end'.$name : $end; + } + + public function __toString() + { + return sprintf('<%s:body>', $this->name); + } + + public function parse(Twig_Token $token) + { + $stream = $this->parser->getStream(); + $stream->expect(Twig_Token::BLOCK_END_TYPE); + + return $this->parser->subparse(array($this, 'decideBlockEnd'), true); + } + + public function decideBlockEnd(Twig_Token $token) + { + return $token->test($this->end); + } +} diff --git a/inc/Twig/Extensions/Grammar/Boolean.php b/inc/Twig/Extensions/Grammar/Boolean.php new file mode 100644 index 0000000..c004809 --- /dev/null +++ b/inc/Twig/Extensions/Grammar/Boolean.php @@ -0,0 +1,24 @@ +', $this->name); + } + + public function parse(Twig_Token $token) + { + $this->parser->getStream()->expect(Twig_Token::NAME_TYPE, array('true', 'false')); + + return new Twig_Node_Expression_Constant('true' === $token->getValue() ? true : false, $token->getLine()); + } +} diff --git a/inc/Twig/Extensions/Grammar/Constant.php b/inc/Twig/Extensions/Grammar/Constant.php new file mode 100644 index 0000000..9df6045 --- /dev/null +++ b/inc/Twig/Extensions/Grammar/Constant.php @@ -0,0 +1,37 @@ +name = $name; + $this->type = null === $type ? Twig_Token::NAME_TYPE : $type; + } + + public function __toString() + { + return $this->name; + } + + public function parse(Twig_Token $token) + { + $this->parser->getStream()->expect($this->type, $this->name); + + return $this->name; + } + + public function getType() + { + return $this->type; + } +} diff --git a/inc/Twig/Extensions/Grammar/Expression.php b/inc/Twig/Extensions/Grammar/Expression.php new file mode 100644 index 0000000..4c33df0 --- /dev/null +++ b/inc/Twig/Extensions/Grammar/Expression.php @@ -0,0 +1,22 @@ +', $this->name); + } + + public function parse(Twig_Token $token) + { + return $this->parser->getExpressionParser()->parseExpression(); + } +} diff --git a/inc/Twig/Extensions/Grammar/Hash.php b/inc/Twig/Extensions/Grammar/Hash.php new file mode 100644 index 0000000..98b07d2 --- /dev/null +++ b/inc/Twig/Extensions/Grammar/Hash.php @@ -0,0 +1,22 @@ +', $this->name); + } + + public function parse(Twig_Token $token) + { + return $this->parser->getExpressionParser()->parseHashExpression(); + } +} diff --git a/inc/Twig/Extensions/Grammar/Number.php b/inc/Twig/Extensions/Grammar/Number.php new file mode 100644 index 0000000..f0857d2 --- /dev/null +++ b/inc/Twig/Extensions/Grammar/Number.php @@ -0,0 +1,24 @@ +', $this->name); + } + + public function parse(Twig_Token $token) + { + $this->parser->getStream()->expect(Twig_Token::NUMBER_TYPE); + + return new Twig_Node_Expression_Constant($token->getValue(), $token->getLine()); + } +} diff --git a/inc/Twig/Extensions/Grammar/Optional.php b/inc/Twig/Extensions/Grammar/Optional.php new file mode 100644 index 0000000..da42748 --- /dev/null +++ b/inc/Twig/Extensions/Grammar/Optional.php @@ -0,0 +1,69 @@ +grammar = array(); + foreach (func_get_args() as $grammar) { + $this->addGrammar($grammar); + } + } + + public function __toString() + { + $repr = array(); + foreach ($this->grammar as $grammar) { + $repr[] = (string) $grammar; + } + + return sprintf('[%s]', implode(' ', $repr)); + } + + public function addGrammar(Twig_Extensions_GrammarInterface $grammar) + { + $this->grammar[] = $grammar; + } + + public function parse(Twig_Token $token) + { + // test if we have the optional element before consuming it + if ($this->grammar[0] instanceof Twig_Extensions_Grammar_Constant) { + if (!$this->parser->getStream()->test($this->grammar[0]->getType(), $this->grammar[0]->getName())) { + return array(); + } + } elseif ($this->grammar[0] instanceof Twig_Extensions_Grammar_Name) { + if (!$this->parser->getStream()->test(Twig_Token::NAME_TYPE)) { + return array(); + } + } elseif ($this->parser->getStream()->test(Twig_Token::BLOCK_END_TYPE)) { + // if this is not a Constant or a Name, it must be the last element of the tag + + return array(); + } + + $elements = array(); + foreach ($this->grammar as $grammar) { + $grammar->setParser($this->parser); + + $element = $grammar->parse($token); + if (is_array($element)) { + $elements = array_merge($elements, $element); + } else { + $elements[$grammar->getName()] = $element; + } + } + + return $elements; + } +} diff --git a/inc/Twig/Extensions/Grammar/Switch.php b/inc/Twig/Extensions/Grammar/Switch.php new file mode 100644 index 0000000..4245f2c --- /dev/null +++ b/inc/Twig/Extensions/Grammar/Switch.php @@ -0,0 +1,24 @@ +', $this->name); + } + + public function parse(Twig_Token $token) + { + $this->parser->getStream()->expect(Twig_Token::NAME_TYPE, $this->name); + + return new Twig_Node_Expression_Constant(true, $token->getLine()); + } +} diff --git a/inc/Twig/Extensions/Grammar/Tag.php b/inc/Twig/Extensions/Grammar/Tag.php new file mode 100644 index 0000000..727f261 --- /dev/null +++ b/inc/Twig/Extensions/Grammar/Tag.php @@ -0,0 +1,56 @@ +grammar = array(); + foreach (func_get_args() as $grammar) { + $this->addGrammar($grammar); + } + } + + public function __toString() + { + $repr = array(); + foreach ($this->grammar as $grammar) { + $repr[] = (string) $grammar; + } + + return implode(' ', $repr); + } + + public function addGrammar(Twig_Extensions_GrammarInterface $grammar) + { + $this->grammar[] = $grammar; + } + + public function parse(Twig_Token $token) + { + $elements = array(); + foreach ($this->grammar as $grammar) { + $grammar->setParser($this->parser); + + $element = $grammar->parse($token); + if (is_array($element)) { + $elements = array_merge($elements, $element); + } else { + $elements[$grammar->getName()] = $element; + } + } + + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + + return $elements; + } +} diff --git a/inc/Twig/Extensions/GrammarInterface.php b/inc/Twig/Extensions/GrammarInterface.php new file mode 100644 index 0000000..22713bf --- /dev/null +++ b/inc/Twig/Extensions/GrammarInterface.php @@ -0,0 +1,18 @@ + + * @version SVN: $Id$ + */ +class Twig_Extensions_Node_Debug extends Twig_Node +{ + public function __construct(Twig_Node_Expression $expr = null, $lineno, $tag = null) + { + parent::__construct(array('expr' => $expr), array(), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler->addDebugInfo($this); + + $compiler + ->write("if (\$this->env->isDebug()) {\n") + ->indent() + ; + + if (null === $this->getNode('expr')) { + // remove embedded templates (macros) from the context + $compiler + ->write("\$vars = array();\n") + ->write("foreach (\$context as \$key => \$value) {\n") + ->indent() + ->write("if (!\$value instanceof Twig_Template) {\n") + ->indent() + ->write("\$vars[\$key] = \$value;\n") + ->outdent() + ->write("}\n") + ->outdent() + ->write("}\n") + ->write("var_dump(\$vars);\n") + ; + } else { + $compiler + ->write("var_dump(") + ->subcompile($this->getNode('expr')) + ->raw(");\n") + ; + } + + $compiler + ->outdent() + ->write("}\n") + ; + } +} diff --git a/inc/Twig/Extensions/Node/Trans.php b/inc/Twig/Extensions/Node/Trans.php new file mode 100644 index 0000000..d12564a --- /dev/null +++ b/inc/Twig/Extensions/Node/Trans.php @@ -0,0 +1,133 @@ + + */ +class Twig_Extensions_Node_Trans extends Twig_Node +{ + public function __construct(Twig_NodeInterface $body, Twig_NodeInterface $plural = null, Twig_Node_Expression $count = null, $lineno, $tag = null) + { + parent::__construct(array('count' => $count, 'body' => $body, 'plural' => $plural), array(), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler->addDebugInfo($this); + + list($msg, $vars) = $this->compileString($this->getNode('body')); + + if (null !== $this->getNode('plural')) { + list($msg1, $vars1) = $this->compileString($this->getNode('plural')); + + $vars = array_merge($vars, $vars1); + } + + $function = null === $this->getNode('plural') ? 'gettext' : 'ngettext'; + + if ($vars) { + $compiler + ->write('echo strtr('.$function.'(') + ->subcompile($msg) + ; + + if (null !== $this->getNode('plural')) { + $compiler + ->raw(', ') + ->subcompile($msg1) + ->raw(', abs(') + ->subcompile($this->getNode('count')) + ->raw(')') + ; + } + + $compiler->raw('), array('); + + foreach ($vars as $var) { + if ('count' === $var->getAttribute('name')) { + $compiler + ->string('%count%') + ->raw(' => abs(') + ->subcompile($this->getNode('count')) + ->raw('), ') + ; + } else { + $compiler + ->string('%'.$var->getAttribute('name').'%') + ->raw(' => ') + ->subcompile($var) + ->raw(', ') + ; + } + } + + $compiler->raw("));\n"); + } else { + $compiler + ->write('echo '.$function.'(') + ->subcompile($msg) + ; + + if (null !== $this->getNode('plural')) { + $compiler + ->raw(', ') + ->subcompile($msg1) + ->raw(', abs(') + ->subcompile($this->getNode('count')) + ->raw(')') + ; + } + + $compiler->raw(");\n"); + } + } + + protected function compileString(Twig_NodeInterface $body) + { + if ($body instanceof Twig_Node_Expression_Name || $body instanceof Twig_Node_Expression_Constant || $body instanceof Twig_Node_Expression_TempName) { + return array($body, array()); + } + + $vars = array(); + if (count($body)) { + $msg = ''; + + foreach ($body as $node) { + if (get_class($node) === 'Twig_Node' && $node->getNode(0) instanceof Twig_Node_SetTemp) { + $node = $node->getNode(1); + } + + if ($node instanceof Twig_Node_Print) { + $n = $node->getNode('expr'); + while ($n instanceof Twig_Node_Expression_Filter) { + $n = $n->getNode('node'); + } + $msg .= sprintf('%%%s%%', $n->getAttribute('name')); + $vars[] = new Twig_Node_Expression_Name($n->getAttribute('name'), $n->getLine()); + } else { + $msg .= $node->getAttribute('data'); + } + } + } else { + $msg = $body->getAttribute('data'); + } + + return array(new Twig_Node(array(new Twig_Node_Expression_Constant(trim($msg), $body->getLine()))), $vars); + } +} diff --git a/inc/Twig/Extensions/SimpleTokenParser.php b/inc/Twig/Extensions/SimpleTokenParser.php new file mode 100644 index 0000000..4954648 --- /dev/null +++ b/inc/Twig/Extensions/SimpleTokenParser.php @@ -0,0 +1,132 @@ +getGrammar(); + if (!is_object($grammar)) { + $grammar = self::parseGrammar($grammar); + } + + $grammar->setParser($this->parser); + $values = $grammar->parse($token); + + return $this->getNode($values, $token->getLine()); + } + + /** + * Gets the grammar as an object or as a string. + * + * @return string|Twig_Extensions_Grammar A Twig_Extensions_Grammar instance or a string + */ + abstract protected function getGrammar(); + + /** + * Gets the nodes based on the parsed values. + * + * @param array $values An array of values + * @param integer $line The parser line + */ + abstract protected function getNode(array $values, $line); + + protected function getAttribute($node, $attribute, $arguments = array(), $type = Twig_Node_Expression_GetAttr::TYPE_ANY, $line = -1) + { + return new Twig_Node_Expression_GetAttr( + $node instanceof Twig_NodeInterface ? $node : new Twig_Node_Expression_Name($node, $line), + $attribute instanceof Twig_NodeInterface ? $attribute : new Twig_Node_Expression_Constant($attribute, $line), + $arguments instanceof Twig_NodeInterface ? $arguments : new Twig_Node($arguments), + $type, + $line + ); + } + + protected function call($node, $attribute, $arguments = array(), $line = -1) + { + return $this->getAttribute($node, $attribute, $arguments, Twig_Node_Expression_GetAttr::TYPE_METHOD, $line); + } + + protected function markAsSafe(Twig_NodeInterface $node, $line = -1) + { + return new Twig_Node_Expression_Filter( + $node, + new Twig_Node_Expression_Constant('raw', $line), + new Twig_Node(), + $line + ); + } + + protected function output(Twig_NodeInterface $node, $line = -1) + { + return new Twig_Node_Print($node, $line); + } + + protected function getNodeValues(array $values) + { + $nodes = array(); + foreach ($values as $value) { + if ($value instanceof Twig_NodeInterface) { + $nodes[] = $value; + } + } + + return $nodes; + } + + static public function parseGrammar($str, $main = true) + { + static $cursor; + + if (true === $main) { + $cursor = 0; + $grammar = new Twig_Extensions_Grammar_Tag(); + } else { + $grammar = new Twig_Extensions_Grammar_Optional(); + } + + while ($cursor < strlen($str)) { + if (preg_match('/\s+/A', $str, $match, null, $cursor)) { + $cursor += strlen($match[0]); + } elseif (preg_match('/<(\w+)(?:\:(\w+))?>/A', $str, $match, null, $cursor)) { + $class = sprintf('Twig_Extensions_Grammar_%s', ucfirst(isset($match[2]) ? $match[2] : 'Expression')); + if (!class_exists($class)) { + throw new Twig_Error_Runtime(sprintf('Unable to understand "%s" in grammar (%s class does not exist)', $match[0], $class)); + } + $grammar->addGrammar(new $class($match[1])); + $cursor += strlen($match[0]); + } elseif (preg_match('/\w+/A', $str, $match, null, $cursor)) { + $grammar->addGrammar(new Twig_Extensions_Grammar_Constant($match[0])); + $cursor += strlen($match[0]); + } elseif (preg_match('/,/A', $str, $match, null, $cursor)) { + $grammar->addGrammar(new Twig_Extensions_Grammar_Constant($match[0], Twig_Token::PUNCTUATION_TYPE)); + $cursor += strlen($match[0]); + } elseif (preg_match('/\[/A', $str, $match, null, $cursor)) { + $cursor += strlen($match[0]); + $grammar->addGrammar(self::parseGrammar($str, false)); + } elseif (true !== $main && preg_match('/\]/A', $str, $match, null, $cursor)) { + $cursor += strlen($match[0]); + + return $grammar; + } else { + throw new Twig_Error_Runtime(sprintf('Unable to parse grammar "%s" near "...%s..."', $str, substr($str, $cursor, 10))); + } + } + + return $grammar; + } +} diff --git a/inc/Twig/Extensions/TokenParser/Debug.php b/inc/Twig/Extensions/TokenParser/Debug.php new file mode 100644 index 0000000..4a7dfcc --- /dev/null +++ b/inc/Twig/Extensions/TokenParser/Debug.php @@ -0,0 +1,42 @@ +getLine(); + + $expr = null; + if (!$this->parser->getStream()->test(Twig_Token::BLOCK_END_TYPE)) { + $expr = $this->parser->getExpressionParser()->parseExpression(); + } + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + + return new Twig_Extensions_Node_Debug($expr, $lineno, $this->getTag()); + } + + /** + * Gets the tag name associated with this token parser. + * + * @param string The tag name + */ + public function getTag() + { + return 'debug'; + } +} diff --git a/inc/Twig/Extensions/TokenParser/Trans.php b/inc/Twig/Extensions/TokenParser/Trans.php new file mode 100644 index 0000000..5e2dc46 --- /dev/null +++ b/inc/Twig/Extensions/TokenParser/Trans.php @@ -0,0 +1,80 @@ +getLine(); + $stream = $this->parser->getStream(); + $count = null; + $plural = null; + + if (!$stream->test(Twig_Token::BLOCK_END_TYPE)) { + $body = $this->parser->getExpressionParser()->parseExpression(); + } else { + $stream->expect(Twig_Token::BLOCK_END_TYPE); + $body = $this->parser->subparse(array($this, 'decideForFork')); + if ('plural' === $stream->next()->getValue()) { + $count = $this->parser->getExpressionParser()->parseExpression(); + $stream->expect(Twig_Token::BLOCK_END_TYPE); + $plural = $this->parser->subparse(array($this, 'decideForEnd'), true); + } + } + + $stream->expect(Twig_Token::BLOCK_END_TYPE); + + $this->checkTransString($body, $lineno); + + return new Twig_Extensions_Node_Trans($body, $plural, $count, $lineno, $this->getTag()); + } + + public function decideForFork(Twig_Token $token) + { + return $token->test(array('plural', 'endtrans')); + } + + public function decideForEnd(Twig_Token $token) + { + return $token->test('endtrans'); + } + + /** + * Gets the tag name associated with this token parser. + * + * @param string The tag name + */ + public function getTag() + { + return 'trans'; + } + + protected function checkTransString(Twig_NodeInterface $body, $lineno) + { + foreach ($body as $i => $node) { + if ( + $node instanceof Twig_Node_Text + || + ($node instanceof Twig_Node_Print && $node->getNode('expr') instanceof Twig_Node_Expression_Name) + ) { + continue; + } + + throw new Twig_Error_Syntax(sprintf('The text to be translated with "trans" can only contain references to simple variables'), $lineno); + } + } +} diff --git a/inc/Twig/Filter.php b/inc/Twig/Filter.php new file mode 100644 index 0000000..5cfbb66 --- /dev/null +++ b/inc/Twig/Filter.php @@ -0,0 +1,81 @@ + + * @deprecated since 1.12 (to be removed in 2.0) + */ +abstract class Twig_Filter implements Twig_FilterInterface, Twig_FilterCallableInterface +{ + protected $options; + protected $arguments = array(); + + public function __construct(array $options = array()) + { + $this->options = array_merge(array( + 'needs_environment' => false, + 'needs_context' => false, + 'pre_escape' => null, + 'preserves_safety' => null, + 'callable' => null, + ), $options); + } + + public function setArguments($arguments) + { + $this->arguments = $arguments; + } + + public function getArguments() + { + return $this->arguments; + } + + public function needsEnvironment() + { + return $this->options['needs_environment']; + } + + public function needsContext() + { + return $this->options['needs_context']; + } + + public function getSafe(Twig_Node $filterArgs) + { + if (isset($this->options['is_safe'])) { + return $this->options['is_safe']; + } + + if (isset($this->options['is_safe_callback'])) { + return call_user_func($this->options['is_safe_callback'], $filterArgs); + } + } + + public function getPreservesSafety() + { + return $this->options['preserves_safety']; + } + + public function getPreEscape() + { + return $this->options['pre_escape']; + } + + public function getCallable() + { + return $this->options['callable']; + } +} diff --git a/inc/Twig/Filter/Function.php b/inc/Twig/Filter/Function.php new file mode 100644 index 0000000..ad374a5 --- /dev/null +++ b/inc/Twig/Filter/Function.php @@ -0,0 +1,37 @@ + + * @deprecated since 1.12 (to be removed in 2.0) + */ +class Twig_Filter_Function extends Twig_Filter +{ + protected $function; + + public function __construct($function, array $options = array()) + { + $options['callable'] = $function; + + parent::__construct($options); + + $this->function = $function; + } + + public function compile() + { + return $this->function; + } +} diff --git a/inc/Twig/Filter/Method.php b/inc/Twig/Filter/Method.php new file mode 100644 index 0000000..63c8c3b --- /dev/null +++ b/inc/Twig/Filter/Method.php @@ -0,0 +1,39 @@ + + * @deprecated since 1.12 (to be removed in 2.0) + */ +class Twig_Filter_Method extends Twig_Filter +{ + protected $extension; + protected $method; + + public function __construct(Twig_ExtensionInterface $extension, $method, array $options = array()) + { + $options['callable'] = array($extension, $method); + + parent::__construct($options); + + $this->extension = $extension; + $this->method = $method; + } + + public function compile() + { + return sprintf('$this->env->getExtension(\'%s\')->%s', $this->extension->getName(), $this->method); + } +} diff --git a/inc/Twig/Filter/Node.php b/inc/Twig/Filter/Node.php new file mode 100644 index 0000000..8744c5e --- /dev/null +++ b/inc/Twig/Filter/Node.php @@ -0,0 +1,39 @@ + + * @deprecated since 1.12 (to be removed in 2.0) + */ +class Twig_Filter_Node extends Twig_Filter +{ + protected $class; + + public function __construct($class, array $options = array()) + { + parent::__construct($options); + + $this->class = $class; + } + + public function getClass() + { + return $this->class; + } + + public function compile() + { + } +} diff --git a/inc/Twig/FilterCallableInterface.php b/inc/Twig/FilterCallableInterface.php new file mode 100644 index 0000000..145534d --- /dev/null +++ b/inc/Twig/FilterCallableInterface.php @@ -0,0 +1,23 @@ + + * @deprecated since 1.12 (to be removed in 2.0) + */ +interface Twig_FilterCallableInterface +{ + public function getCallable(); +} diff --git a/inc/Twig/FilterInterface.php b/inc/Twig/FilterInterface.php new file mode 100644 index 0000000..5319ecc --- /dev/null +++ b/inc/Twig/FilterInterface.php @@ -0,0 +1,42 @@ + + * @deprecated since 1.12 (to be removed in 2.0) + */ +interface Twig_FilterInterface +{ + /** + * Compiles a filter. + * + * @return string The PHP code for the filter + */ + public function compile(); + + public function needsEnvironment(); + + public function needsContext(); + + public function getSafe(Twig_Node $filterArgs); + + public function getPreservesSafety(); + + public function getPreEscape(); + + public function setArguments($arguments); + + public function getArguments(); +} diff --git a/inc/Twig/Function.php b/inc/Twig/Function.php new file mode 100644 index 0000000..b5ffb2b --- /dev/null +++ b/inc/Twig/Function.php @@ -0,0 +1,71 @@ + + * @deprecated since 1.12 (to be removed in 2.0) + */ +abstract class Twig_Function implements Twig_FunctionInterface, Twig_FunctionCallableInterface +{ + protected $options; + protected $arguments = array(); + + public function __construct(array $options = array()) + { + $this->options = array_merge(array( + 'needs_environment' => false, + 'needs_context' => false, + 'callable' => null, + ), $options); + } + + public function setArguments($arguments) + { + $this->arguments = $arguments; + } + + public function getArguments() + { + return $this->arguments; + } + + public function needsEnvironment() + { + return $this->options['needs_environment']; + } + + public function needsContext() + { + return $this->options['needs_context']; + } + + public function getSafe(Twig_Node $functionArgs) + { + if (isset($this->options['is_safe'])) { + return $this->options['is_safe']; + } + + if (isset($this->options['is_safe_callback'])) { + return call_user_func($this->options['is_safe_callback'], $functionArgs); + } + + return array(); + } + + public function getCallable() + { + return $this->options['callable']; + } +} diff --git a/inc/Twig/Function/Function.php b/inc/Twig/Function/Function.php new file mode 100644 index 0000000..d1e1b96 --- /dev/null +++ b/inc/Twig/Function/Function.php @@ -0,0 +1,38 @@ + + * @deprecated since 1.12 (to be removed in 2.0) + */ +class Twig_Function_Function extends Twig_Function +{ + protected $function; + + public function __construct($function, array $options = array()) + { + $options['callable'] = $function; + + parent::__construct($options); + + $this->function = $function; + } + + public function compile() + { + return $this->function; + } +} diff --git a/inc/Twig/Function/Method.php b/inc/Twig/Function/Method.php new file mode 100644 index 0000000..67039a9 --- /dev/null +++ b/inc/Twig/Function/Method.php @@ -0,0 +1,40 @@ + + * @deprecated since 1.12 (to be removed in 2.0) + */ +class Twig_Function_Method extends Twig_Function +{ + protected $extension; + protected $method; + + public function __construct(Twig_ExtensionInterface $extension, $method, array $options = array()) + { + $options['callable'] = array($extension, $method); + + parent::__construct($options); + + $this->extension = $extension; + $this->method = $method; + } + + public function compile() + { + return sprintf('$this->env->getExtension(\'%s\')->%s', $this->extension->getName(), $this->method); + } +} diff --git a/inc/Twig/Function/Node.php b/inc/Twig/Function/Node.php new file mode 100644 index 0000000..06a0d0d --- /dev/null +++ b/inc/Twig/Function/Node.php @@ -0,0 +1,39 @@ + + * @deprecated since 1.12 (to be removed in 2.0) + */ +class Twig_Function_Node extends Twig_Function +{ + protected $class; + + public function __construct($class, array $options = array()) + { + parent::__construct($options); + + $this->class = $class; + } + + public function getClass() + { + return $this->class; + } + + public function compile() + { + } +} diff --git a/inc/Twig/FunctionCallableInterface.php b/inc/Twig/FunctionCallableInterface.php new file mode 100644 index 0000000..0aab4f5 --- /dev/null +++ b/inc/Twig/FunctionCallableInterface.php @@ -0,0 +1,23 @@ + + * @deprecated since 1.12 (to be removed in 2.0) + */ +interface Twig_FunctionCallableInterface +{ + public function getCallable(); +} diff --git a/inc/Twig/FunctionInterface.php b/inc/Twig/FunctionInterface.php new file mode 100644 index 0000000..67f4f89 --- /dev/null +++ b/inc/Twig/FunctionInterface.php @@ -0,0 +1,39 @@ + + * @deprecated since 1.12 (to be removed in 2.0) + */ +interface Twig_FunctionInterface +{ + /** + * Compiles a function. + * + * @return string The PHP code for the function + */ + public function compile(); + + public function needsEnvironment(); + + public function needsContext(); + + public function getSafe(Twig_Node $filterArgs); + + public function setArguments($arguments); + + public function getArguments(); +} diff --git a/inc/Twig/Gettext/Extractor.php b/inc/Twig/Gettext/Extractor.php new file mode 100644 index 0000000..e7fa1af --- /dev/null +++ b/inc/Twig/Gettext/Extractor.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Twig\Gettext; + +use Symfony\Component\Filesystem\Filesystem; + +/** + * Extracts translations from twig templates. + * + * @author Саша Стаменковић + */ +class Extractor +{ + /** + * @var \Twig_Environment + */ + protected $environment; + + /** + * Template cached file names. + * + * @var string[] + */ + protected $templates; + + /** + * Gettext parameters. + * + * @var string[] + */ + protected $parameters; + + public function __construct(\Twig_Environment $environment) + { + $this->environment = $environment; + $this->reset(); + } + + protected function reset() + { + $this->templates = array(); + $this->parameters = array(); + } + + public function addTemplate($path) + { + $this->environment->loadTemplate($path); + $this->templates[] = $this->environment->getCacheFilename($path); + } + + public function addGettextParameter($parameter) + { + $this->parameters[] = $parameter; + } + + public function setGettextParameters(array $parameters) + { + $this->parameters = $parameters; + } + + public function extract() + { + $command = 'xgettext'; + $command .= ' '.join(' ', $this->parameters); + $command .= ' '.join(' ', $this->templates); + + $error = 0; + $output = system($command, $error); + if (0 !== $error) { + throw new \RuntimeException(sprintf( + 'Gettext command "%s" failed with error code %s and output: %s', + $command, + $error, + $output + )); + } + + $this->reset(); + } + + public function __destruct() + { + $filesystem = new Filesystem(); + $filesystem->remove($this->environment->getCache()); + } +} diff --git a/inc/Twig/Gettext/Loader/Filesystem.php b/inc/Twig/Gettext/Loader/Filesystem.php new file mode 100644 index 0000000..b011b03 --- /dev/null +++ b/inc/Twig/Gettext/Loader/Filesystem.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Twig\Gettext\Loader; + +/** + * Loads template from the filesystem. + * + * @author Саша Стаменковић + */ +class Filesystem extends \Twig_Loader_Filesystem +{ + /** + * Hacked find template to allow loading templates by absolute path. + * + * @param string $name template name or absolute path + */ + protected function findTemplate($name) + { + // normalize name + $name = preg_replace('#/{2,}#', '/', strtr($name, '\\', '/')); + + if (isset($this->cache[$name])) { + return $this->cache[$name]; + } + + $this->validateName($name); + + $namespace = '__main__'; + if (isset($name[0]) && '@' == $name[0]) { + if (false === $pos = strpos($name, '/')) { + throw new \InvalidArgumentException(sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name)); + } + + $namespace = substr($name, 1, $pos - 1); + + $name = substr($name, $pos + 1); + } + + if (!isset($this->paths[$namespace])) { + throw new \Twig_Error_Loader(sprintf('There are no registered paths for namespace "%s".', $namespace)); + } + + if (is_file($name)) { + return $this->cache[$name] = $name; + } + + return __DIR__.'/../Test/Fixtures/twig/empty.twig'; + } +} diff --git a/inc/Twig/Gettext/Routing/Generator/UrlGenerator.php b/inc/Twig/Gettext/Routing/Generator/UrlGenerator.php new file mode 100644 index 0000000..9e3431b --- /dev/null +++ b/inc/Twig/Gettext/Routing/Generator/UrlGenerator.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Twig\Gettext\Routing\Generator; + +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Routing\RequestContext; + +/** + * Dummy url generator. + * + * @author Саша Стаменковић + */ +class UrlGenerator implements UrlGeneratorInterface +{ + protected $context; + + public function generate($name, $parameters = array(), $absolute = false) + { + } + + public function getContext() + { + return $this->context; + } + + public function setContext(RequestContext $context) + { + $this->context = $context; + } +} diff --git a/inc/Twig/Gettext/Test/ExtractorTest.php b/inc/Twig/Gettext/Test/ExtractorTest.php new file mode 100644 index 0000000..d467835 --- /dev/null +++ b/inc/Twig/Gettext/Test/ExtractorTest.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Twig\Gettext\Test; + +use Twig\Gettext\Extractor; +use Twig\Gettext\Loader\Filesystem; +use Symfony\Component\Translation\Loader\PoFileLoader; + +/** + * @author Саша Стаменковић + */ +class ExtractorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Twig_Environment + */ + protected $twig; + + /** + * @var PoFileLoader + */ + protected $loader; + + protected function setUp() + { + $this->twig = new \Twig_Environment(new Filesystem('/'), array( + 'cache' => '/tmp/cache/'.uniqid(), + 'auto_reload' => true + )); + $this->twig->addExtension(new \Twig_Extensions_Extension_I18n()); + + $this->loader = new PoFileLoader(); + } + + /** + * @dataProvider testExtractDataProvider + */ + public function testExtract(array $templates, array $parameters, array $messages) + { + $extractor = new Extractor($this->twig); + + foreach ($templates as $template) { + $extractor->addTemplate($template); + } + foreach ($parameters as $parameter) { + $extractor->addGettextParameter($parameter); + } + + $extractor->extract(); + + $catalog = $this->loader->load($this->getPotFile(), null); + + foreach ($messages as $message) { + $this->assertTrue( + $catalog->has($message), + sprintf('Message "%s" not found in catalog.', $message) + ); + } + } + + public function testExtractDataProvider() + { + return array( + array( + array( + __DIR__.'/Fixtures/twig/singular.twig', + __DIR__.'/Fixtures/twig/plural.twig', + ), + $this->getGettextParameters(), + array( + 'Hello %name%!', + 'Hello World!', + 'Hey %name%, I have one apple.', + 'Hey %name%, I have %count% apples.', + ), + ), + ); + } + + public function testExtractNoTranslations() + { + $extractor = new Extractor($this->twig); + + $extractor->addTemplate(__DIR__.'/Fixtures/twig/empty.twig'); + $extractor->setGettextParameters($this->getGettextParameters()); + + $extractor->extract(); + + $catalog = $this->loader->load($this->getPotFile(), null); + + $this->assertEmpty($catalog->all('messages')); + } + + private function getPotFile() + { + return __DIR__.'/Fixtures/messages.pot'; + } + + private function getGettextParameters() + { + return array( + '--force-po', + '-o', + $this->getPotFile(), + ); + } + + protected function tearDown() + { + if (file_exists($this->getPotFile())) { + unlink($this->getPotFile()); + } + } +} diff --git a/inc/Twig/Gettext/Test/Fixtures/twig/empty.twig b/inc/Twig/Gettext/Test/Fixtures/twig/empty.twig new file mode 100644 index 0000000..05f0d26 --- /dev/null +++ b/inc/Twig/Gettext/Test/Fixtures/twig/empty.twig @@ -0,0 +1 @@ +Nothing to translate here. diff --git a/inc/Twig/Gettext/Test/Fixtures/twig/plural.twig b/inc/Twig/Gettext/Test/Fixtures/twig/plural.twig new file mode 100644 index 0000000..f9754ff --- /dev/null +++ b/inc/Twig/Gettext/Test/Fixtures/twig/plural.twig @@ -0,0 +1,5 @@ +{% trans %} + Hey {{ name }}, I have one apple. +{% plural apple_count %} + Hey {{ name }}, I have {{ count }} apples. +{% endtrans %} diff --git a/inc/Twig/Gettext/Test/Fixtures/twig/singular.twig b/inc/Twig/Gettext/Test/Fixtures/twig/singular.twig new file mode 100644 index 0000000..d757cf9 --- /dev/null +++ b/inc/Twig/Gettext/Test/Fixtures/twig/singular.twig @@ -0,0 +1,9 @@ +{% trans "Hello World!" %} + +{% trans %} + Hello World! +{% endtrans %} + +{% trans %} + Hello {{ name }}! +{% endtrans %} diff --git a/inc/Twig/Lexer.php b/inc/Twig/Lexer.php new file mode 100644 index 0000000..000b038 --- /dev/null +++ b/inc/Twig/Lexer.php @@ -0,0 +1,408 @@ + + */ +class Twig_Lexer implements Twig_LexerInterface +{ + protected $tokens; + protected $code; + protected $cursor; + protected $lineno; + protected $end; + protected $state; + protected $states; + protected $brackets; + protected $env; + protected $filename; + protected $options; + protected $regexes; + protected $position; + protected $positions; + protected $currentVarBlockLine; + + const STATE_DATA = 0; + const STATE_BLOCK = 1; + const STATE_VAR = 2; + const STATE_STRING = 3; + const STATE_INTERPOLATION = 4; + + const REGEX_NAME = '/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A'; + const REGEX_NUMBER = '/[0-9]+(?:\.[0-9]+)?/A'; + const REGEX_STRING = '/"([^#"\\\\]*(?:\\\\.[^#"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As'; + const REGEX_DQ_STRING_DELIM = '/"/A'; + const REGEX_DQ_STRING_PART = '/[^#"\\\\]*(?:(?:\\\\.|#(?!\{))[^#"\\\\]*)*/As'; + const PUNCTUATION = '()[]{}?:.,|'; + + public function __construct(Twig_Environment $env, array $options = array()) + { + $this->env = $env; + + $this->options = array_merge(array( + 'tag_comment' => array('{#', '#}'), + 'tag_block' => array('{%', '%}'), + 'tag_variable' => array('{{', '}}'), + 'whitespace_trim' => '-', + 'interpolation' => array('#{', '}'), + ), $options); + + $this->regexes = array( + 'lex_var' => '/\s*'.preg_quote($this->options['whitespace_trim'].$this->options['tag_variable'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_variable'][1], '/').'/A', + 'lex_block' => '/\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')\n?/A', + 'lex_raw_data' => '/('.preg_quote($this->options['tag_block'][0].$this->options['whitespace_trim'], '/').'|'.preg_quote($this->options['tag_block'][0], '/').')\s*(?:end%s)\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')/s', + 'operator' => $this->getOperatorRegex(), + 'lex_comment' => '/(?:'.preg_quote($this->options['whitespace_trim'], '/').preg_quote($this->options['tag_comment'][1], '/').'\s*|'.preg_quote($this->options['tag_comment'][1], '/').')\n?/s', + 'lex_block_raw' => '/\s*(raw|verbatim)\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')/As', + 'lex_block_line' => '/\s*line\s+(\d+)\s*'.preg_quote($this->options['tag_block'][1], '/').'/As', + 'lex_tokens_start' => '/('.preg_quote($this->options['tag_variable'][0], '/').'|'.preg_quote($this->options['tag_block'][0], '/').'|'.preg_quote($this->options['tag_comment'][0], '/').')('.preg_quote($this->options['whitespace_trim'], '/').')?/s', + 'interpolation_start' => '/'.preg_quote($this->options['interpolation'][0], '/').'\s*/A', + 'interpolation_end' => '/\s*'.preg_quote($this->options['interpolation'][1], '/').'/A', + ); + } + + /** + * Tokenizes a source code. + * + * @param string $code The source code + * @param string $filename A unique identifier for the source code + * + * @return Twig_TokenStream A token stream instance + */ + public function tokenize($code, $filename = null) + { + if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) { + $mbEncoding = mb_internal_encoding(); + mb_internal_encoding('ASCII'); + } + + $this->code = str_replace(array("\r\n", "\r"), "\n", $code); + $this->filename = $filename; + $this->cursor = 0; + $this->lineno = 1; + $this->end = strlen($this->code); + $this->tokens = array(); + $this->state = self::STATE_DATA; + $this->states = array(); + $this->brackets = array(); + $this->position = -1; + + // find all token starts in one go + preg_match_all($this->regexes['lex_tokens_start'], $this->code, $matches, PREG_OFFSET_CAPTURE); + $this->positions = $matches; + + while ($this->cursor < $this->end) { + // dispatch to the lexing functions depending + // on the current state + switch ($this->state) { + case self::STATE_DATA: + $this->lexData(); + break; + + case self::STATE_BLOCK: + $this->lexBlock(); + break; + + case self::STATE_VAR: + $this->lexVar(); + break; + + case self::STATE_STRING: + $this->lexString(); + break; + + case self::STATE_INTERPOLATION: + $this->lexInterpolation(); + break; + } + } + + $this->pushToken(Twig_Token::EOF_TYPE); + + if (!empty($this->brackets)) { + list($expect, $lineno) = array_pop($this->brackets); + throw new Twig_Error_Syntax(sprintf('Unclosed "%s"', $expect), $lineno, $this->filename); + } + + if (isset($mbEncoding)) { + mb_internal_encoding($mbEncoding); + } + + return new Twig_TokenStream($this->tokens, $this->filename); + } + + protected function lexData() + { + // if no matches are left we return the rest of the template as simple text token + if ($this->position == count($this->positions[0]) - 1) { + $this->pushToken(Twig_Token::TEXT_TYPE, substr($this->code, $this->cursor)); + $this->cursor = $this->end; + + return; + } + + // Find the first token after the current cursor + $position = $this->positions[0][++$this->position]; + while ($position[1] < $this->cursor) { + if ($this->position == count($this->positions[0]) - 1) { + return; + } + $position = $this->positions[0][++$this->position]; + } + + // push the template text first + $text = $textContent = substr($this->code, $this->cursor, $position[1] - $this->cursor); + if (isset($this->positions[2][$this->position][0])) { + $text = rtrim($text); + } + $this->pushToken(Twig_Token::TEXT_TYPE, $text); + $this->moveCursor($textContent.$position[0]); + + switch ($this->positions[1][$this->position][0]) { + case $this->options['tag_comment'][0]: + $this->lexComment(); + break; + + case $this->options['tag_block'][0]: + // raw data? + if (preg_match($this->regexes['lex_block_raw'], $this->code, $match, null, $this->cursor)) { + $this->moveCursor($match[0]); + $this->lexRawData($match[1]); + // {% line \d+ %} + } elseif (preg_match($this->regexes['lex_block_line'], $this->code, $match, null, $this->cursor)) { + $this->moveCursor($match[0]); + $this->lineno = (int) $match[1]; + } else { + $this->pushToken(Twig_Token::BLOCK_START_TYPE); + $this->pushState(self::STATE_BLOCK); + $this->currentVarBlockLine = $this->lineno; + } + break; + + case $this->options['tag_variable'][0]: + $this->pushToken(Twig_Token::VAR_START_TYPE); + $this->pushState(self::STATE_VAR); + $this->currentVarBlockLine = $this->lineno; + break; + } + } + + protected function lexBlock() + { + if (empty($this->brackets) && preg_match($this->regexes['lex_block'], $this->code, $match, null, $this->cursor)) { + $this->pushToken(Twig_Token::BLOCK_END_TYPE); + $this->moveCursor($match[0]); + $this->popState(); + } else { + $this->lexExpression(); + } + } + + protected function lexVar() + { + if (empty($this->brackets) && preg_match($this->regexes['lex_var'], $this->code, $match, null, $this->cursor)) { + $this->pushToken(Twig_Token::VAR_END_TYPE); + $this->moveCursor($match[0]); + $this->popState(); + } else { + $this->lexExpression(); + } + } + + protected function lexExpression() + { + // whitespace + if (preg_match('/\s+/A', $this->code, $match, null, $this->cursor)) { + $this->moveCursor($match[0]); + + if ($this->cursor >= $this->end) { + throw new Twig_Error_Syntax(sprintf('Unclosed "%s"', $this->state === self::STATE_BLOCK ? 'block' : 'variable'), $this->currentVarBlockLine, $this->filename); + } + } + + // operators + if (preg_match($this->regexes['operator'], $this->code, $match, null, $this->cursor)) { + $this->pushToken(Twig_Token::OPERATOR_TYPE, $match[0]); + $this->moveCursor($match[0]); + } + // names + elseif (preg_match(self::REGEX_NAME, $this->code, $match, null, $this->cursor)) { + $this->pushToken(Twig_Token::NAME_TYPE, $match[0]); + $this->moveCursor($match[0]); + } + // numbers + elseif (preg_match(self::REGEX_NUMBER, $this->code, $match, null, $this->cursor)) { + $number = (float) $match[0]; // floats + if (ctype_digit($match[0]) && $number <= PHP_INT_MAX) { + $number = (int) $match[0]; // integers lower than the maximum + } + $this->pushToken(Twig_Token::NUMBER_TYPE, $number); + $this->moveCursor($match[0]); + } + // punctuation + elseif (false !== strpos(self::PUNCTUATION, $this->code[$this->cursor])) { + // opening bracket + if (false !== strpos('([{', $this->code[$this->cursor])) { + $this->brackets[] = array($this->code[$this->cursor], $this->lineno); + } + // closing bracket + elseif (false !== strpos(')]}', $this->code[$this->cursor])) { + if (empty($this->brackets)) { + throw new Twig_Error_Syntax(sprintf('Unexpected "%s"', $this->code[$this->cursor]), $this->lineno, $this->filename); + } + + list($expect, $lineno) = array_pop($this->brackets); + if ($this->code[$this->cursor] != strtr($expect, '([{', ')]}')) { + throw new Twig_Error_Syntax(sprintf('Unclosed "%s"', $expect), $lineno, $this->filename); + } + } + + $this->pushToken(Twig_Token::PUNCTUATION_TYPE, $this->code[$this->cursor]); + ++$this->cursor; + } + // strings + elseif (preg_match(self::REGEX_STRING, $this->code, $match, null, $this->cursor)) { + $this->pushToken(Twig_Token::STRING_TYPE, stripcslashes(substr($match[0], 1, -1))); + $this->moveCursor($match[0]); + } + // opening double quoted string + elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, null, $this->cursor)) { + $this->brackets[] = array('"', $this->lineno); + $this->pushState(self::STATE_STRING); + $this->moveCursor($match[0]); + } + // unlexable + else { + throw new Twig_Error_Syntax(sprintf('Unexpected character "%s"', $this->code[$this->cursor]), $this->lineno, $this->filename); + } + } + + protected function lexRawData($tag) + { + if (!preg_match(str_replace('%s', $tag, $this->regexes['lex_raw_data']), $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) { + throw new Twig_Error_Syntax(sprintf('Unexpected end of file: Unclosed "%s" block', $tag), $this->lineno, $this->filename); + } + + $text = substr($this->code, $this->cursor, $match[0][1] - $this->cursor); + $this->moveCursor($text.$match[0][0]); + + if (false !== strpos($match[1][0], $this->options['whitespace_trim'])) { + $text = rtrim($text); + } + + $this->pushToken(Twig_Token::TEXT_TYPE, $text); + } + + protected function lexComment() + { + if (!preg_match($this->regexes['lex_comment'], $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) { + throw new Twig_Error_Syntax('Unclosed comment', $this->lineno, $this->filename); + } + + $this->moveCursor(substr($this->code, $this->cursor, $match[0][1] - $this->cursor).$match[0][0]); + } + + protected function lexString() + { + if (preg_match($this->regexes['interpolation_start'], $this->code, $match, null, $this->cursor)) { + $this->brackets[] = array($this->options['interpolation'][0], $this->lineno); + $this->pushToken(Twig_Token::INTERPOLATION_START_TYPE); + $this->moveCursor($match[0]); + $this->pushState(self::STATE_INTERPOLATION); + + } elseif (preg_match(self::REGEX_DQ_STRING_PART, $this->code, $match, null, $this->cursor) && strlen($match[0]) > 0) { + $this->pushToken(Twig_Token::STRING_TYPE, stripcslashes($match[0])); + $this->moveCursor($match[0]); + + } elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, null, $this->cursor)) { + + list($expect, $lineno) = array_pop($this->brackets); + if ($this->code[$this->cursor] != '"') { + throw new Twig_Error_Syntax(sprintf('Unclosed "%s"', $expect), $lineno, $this->filename); + } + + $this->popState(); + ++$this->cursor; + } + } + + protected function lexInterpolation() + { + $bracket = end($this->brackets); + if ($this->options['interpolation'][0] === $bracket[0] && preg_match($this->regexes['interpolation_end'], $this->code, $match, null, $this->cursor)) { + array_pop($this->brackets); + $this->pushToken(Twig_Token::INTERPOLATION_END_TYPE); + $this->moveCursor($match[0]); + $this->popState(); + } else { + $this->lexExpression(); + } + } + + protected function pushToken($type, $value = '') + { + // do not push empty text tokens + if (Twig_Token::TEXT_TYPE === $type && '' === $value) { + return; + } + + $this->tokens[] = new Twig_Token($type, $value, $this->lineno); + } + + protected function moveCursor($text) + { + $this->cursor += strlen($text); + $this->lineno += substr_count($text, "\n"); + } + + protected function getOperatorRegex() + { + $operators = array_merge( + array('='), + array_keys($this->env->getUnaryOperators()), + array_keys($this->env->getBinaryOperators()) + ); + + $operators = array_combine($operators, array_map('strlen', $operators)); + arsort($operators); + + $regex = array(); + foreach ($operators as $operator => $length) { + // an operator that ends with a character must be followed by + // a whitespace or a parenthesis + if (ctype_alpha($operator[$length - 1])) { + $regex[] = preg_quote($operator, '/').'(?=[\s()])'; + } else { + $regex[] = preg_quote($operator, '/'); + } + } + + return '/'.implode('|', $regex).'/A'; + } + + protected function pushState($state) + { + $this->states[] = $this->state; + $this->state = $state; + } + + protected function popState() + { + if (0 === count($this->states)) { + throw new Exception('Cannot pop state without a previous state'); + } + + $this->state = array_pop($this->states); + } +} diff --git a/inc/Twig/LexerInterface.php b/inc/Twig/LexerInterface.php new file mode 100644 index 0000000..4b83f81 --- /dev/null +++ b/inc/Twig/LexerInterface.php @@ -0,0 +1,29 @@ + + * @deprecated since 1.12 (to be removed in 2.0) + */ +interface Twig_LexerInterface +{ + /** + * Tokenizes a source code. + * + * @param string $code The source code + * @param string $filename A unique identifier for the source code + * + * @return Twig_TokenStream A token stream instance + */ + public function tokenize($code, $filename = null); +} diff --git a/inc/Twig/Loader/Array.php b/inc/Twig/Loader/Array.php new file mode 100644 index 0000000..89087ae --- /dev/null +++ b/inc/Twig/Loader/Array.php @@ -0,0 +1,98 @@ + + */ +class Twig_Loader_Array implements Twig_LoaderInterface, Twig_ExistsLoaderInterface +{ + protected $templates; + + /** + * Constructor. + * + * @param array $templates An array of templates (keys are the names, and values are the source code) + * + * @see Twig_Loader + */ + public function __construct(array $templates) + { + $this->templates = array(); + foreach ($templates as $name => $template) { + $this->templates[$name] = $template; + } + } + + /** + * Adds or overrides a template. + * + * @param string $name The template name + * @param string $template The template source + */ + public function setTemplate($name, $template) + { + $this->templates[(string) $name] = $template; + } + + /** + * {@inheritdoc} + */ + public function getSource($name) + { + $name = (string) $name; + if (!isset($this->templates[$name])) { + throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name)); + } + + return $this->templates[$name]; + } + + /** + * {@inheritdoc} + */ + public function exists($name) + { + return isset($this->templates[(string) $name]); + } + + /** + * {@inheritdoc} + */ + public function getCacheKey($name) + { + $name = (string) $name; + if (!isset($this->templates[$name])) { + throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name)); + } + + return $this->templates[$name]; + } + + /** + * {@inheritdoc} + */ + public function isFresh($name, $time) + { + $name = (string) $name; + if (!isset($this->templates[$name])) { + throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name)); + } + + return true; + } +} diff --git a/inc/Twig/Loader/Chain.php b/inc/Twig/Loader/Chain.php new file mode 100644 index 0000000..1f1cf06 --- /dev/null +++ b/inc/Twig/Loader/Chain.php @@ -0,0 +1,139 @@ + + */ +class Twig_Loader_Chain implements Twig_LoaderInterface, Twig_ExistsLoaderInterface +{ + private $hasSourceCache = array(); + protected $loaders; + + /** + * Constructor. + * + * @param Twig_LoaderInterface[] $loaders An array of loader instances + */ + public function __construct(array $loaders = array()) + { + $this->loaders = array(); + foreach ($loaders as $loader) { + $this->addLoader($loader); + } + } + + /** + * Adds a loader instance. + * + * @param Twig_LoaderInterface $loader A Loader instance + */ + public function addLoader(Twig_LoaderInterface $loader) + { + $this->loaders[] = $loader; + $this->hasSourceCache = array(); + } + + /** + * {@inheritdoc} + */ + public function getSource($name) + { + $exceptions = array(); + foreach ($this->loaders as $loader) { + if ($loader instanceof Twig_ExistsLoaderInterface && !$loader->exists($name)) { + continue; + } + + try { + return $loader->getSource($name); + } catch (Twig_Error_Loader $e) { + $exceptions[] = $e->getMessage(); + } + } + + throw new Twig_Error_Loader(sprintf('Template "%s" is not defined (%s).', $name, implode(', ', $exceptions))); + } + + /** + * {@inheritdoc} + */ + public function exists($name) + { + $name = (string) $name; + + if (isset($this->hasSourceCache[$name])) { + return $this->hasSourceCache[$name]; + } + + foreach ($this->loaders as $loader) { + if ($loader instanceof Twig_ExistsLoaderInterface) { + if ($loader->exists($name)) { + return $this->hasSourceCache[$name] = true; + } + + continue; + } + + try { + $loader->getSource($name); + + return $this->hasSourceCache[$name] = true; + } catch (Twig_Error_Loader $e) { + } + } + + return $this->hasSourceCache[$name] = false; + } + + /** + * {@inheritdoc} + */ + public function getCacheKey($name) + { + $exceptions = array(); + foreach ($this->loaders as $loader) { + if ($loader instanceof Twig_ExistsLoaderInterface && !$loader->exists($name)) { + continue; + } + + try { + return $loader->getCacheKey($name); + } catch (Twig_Error_Loader $e) { + $exceptions[] = get_class($loader).': '.$e->getMessage(); + } + } + + throw new Twig_Error_Loader(sprintf('Template "%s" is not defined (%s).', $name, implode(' ', $exceptions))); + } + + /** + * {@inheritdoc} + */ + public function isFresh($name, $time) + { + $exceptions = array(); + foreach ($this->loaders as $loader) { + if ($loader instanceof Twig_ExistsLoaderInterface && !$loader->exists($name)) { + continue; + } + + try { + return $loader->isFresh($name, $time); + } catch (Twig_Error_Loader $e) { + $exceptions[] = get_class($loader).': '.$e->getMessage(); + } + } + + throw new Twig_Error_Loader(sprintf('Template "%s" is not defined (%s).', $name, implode(' ', $exceptions))); + } +} diff --git a/inc/Twig/Loader/Filesystem.php b/inc/Twig/Loader/Filesystem.php new file mode 100644 index 0000000..f9211cb --- /dev/null +++ b/inc/Twig/Loader/Filesystem.php @@ -0,0 +1,223 @@ + + */ +class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderInterface +{ + protected $paths; + protected $cache; + + /** + * Constructor. + * + * @param string|array $paths A path or an array of paths where to look for templates + */ + public function __construct($paths = array()) + { + if ($paths) { + $this->setPaths($paths); + } + } + + /** + * Returns the paths to the templates. + * + * @param string $namespace A path namespace + * + * @return array The array of paths where to look for templates + */ + public function getPaths($namespace = '__main__') + { + return isset($this->paths[$namespace]) ? $this->paths[$namespace] : array(); + } + + /** + * Returns the path namespaces. + * + * The "__main__" namespace is always defined. + * + * @return array The array of defined namespaces + */ + public function getNamespaces() + { + return array_keys($this->paths); + } + + /** + * Sets the paths where templates are stored. + * + * @param string|array $paths A path or an array of paths where to look for templates + * @param string $namespace A path namespace + */ + public function setPaths($paths, $namespace = '__main__') + { + if (!is_array($paths)) { + $paths = array($paths); + } + + $this->paths[$namespace] = array(); + foreach ($paths as $path) { + $this->addPath($path, $namespace); + } + } + + /** + * Adds a path where templates are stored. + * + * @param string $path A path where to look for templates + * @param string $namespace A path name + * + * @throws Twig_Error_Loader + */ + public function addPath($path, $namespace = '__main__') + { + // invalidate the cache + $this->cache = array(); + + if (!is_dir($path)) { + throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path)); + } + + $this->paths[$namespace][] = rtrim($path, '/\\'); + } + + /** + * Prepends a path where templates are stored. + * + * @param string $path A path where to look for templates + * @param string $namespace A path name + * + * @throws Twig_Error_Loader + */ + public function prependPath($path, $namespace = '__main__') + { + // invalidate the cache + $this->cache = array(); + + if (!is_dir($path)) { + throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path)); + } + + $path = rtrim($path, '/\\'); + + if (!isset($this->paths[$namespace])) { + $this->paths[$namespace][] = $path; + } else { + array_unshift($this->paths[$namespace], $path); + } + } + + /** + * {@inheritdoc} + */ + public function getSource($name) + { + return file_get_contents($this->findTemplate($name)); + } + + /** + * {@inheritdoc} + */ + public function getCacheKey($name) + { + return $this->findTemplate($name); + } + + /** + * {@inheritdoc} + */ + public function exists($name) + { + $name = (string) $name; + if (isset($this->cache[$name])) { + return true; + } + + try { + $this->findTemplate($name); + + return true; + } catch (Twig_Error_Loader $exception) { + return false; + } + } + + /** + * {@inheritdoc} + */ + public function isFresh($name, $time) + { + return filemtime($this->findTemplate($name)) <= $time; + } + + protected function findTemplate($name) + { + $name = (string) $name; + + // normalize name + $name = preg_replace('#/{2,}#', '/', strtr($name, '\\', '/')); + + if (isset($this->cache[$name])) { + return $this->cache[$name]; + } + + $this->validateName($name); + + $namespace = '__main__'; + if (isset($name[0]) && '@' == $name[0]) { + if (false === $pos = strpos($name, '/')) { + throw new Twig_Error_Loader(sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name)); + } + + $namespace = substr($name, 1, $pos - 1); + + $name = substr($name, $pos + 1); + } + + if (!isset($this->paths[$namespace])) { + throw new Twig_Error_Loader(sprintf('There are no registered paths for namespace "%s".', $namespace)); + } + + foreach ($this->paths[$namespace] as $path) { + if (is_file($path.'/'.$name)) { + return $this->cache[$name] = $path.'/'.$name; + } + } + + throw new Twig_Error_Loader(sprintf('Unable to find template "%s" (looked into: %s).', $name, implode(', ', $this->paths[$namespace]))); + } + + protected function validateName($name) + { + if (false !== strpos($name, "\0")) { + throw new Twig_Error_Loader('A template name cannot contain NUL bytes.'); + } + + $name = ltrim($name, '/'); + $parts = explode('/', $name); + $level = 0; + foreach ($parts as $part) { + if ('..' === $part) { + --$level; + } elseif ('.' !== $part) { + ++$level; + } + + if ($level < 0) { + throw new Twig_Error_Loader(sprintf('Looks like you try to load a template outside configured directories (%s).', $name)); + } + } + } +} diff --git a/inc/Twig/Loader/String.php b/inc/Twig/Loader/String.php new file mode 100644 index 0000000..8ad9856 --- /dev/null +++ b/inc/Twig/Loader/String.php @@ -0,0 +1,59 @@ + + */ +class Twig_Loader_String implements Twig_LoaderInterface, Twig_ExistsLoaderInterface +{ + /** + * {@inheritdoc} + */ + public function getSource($name) + { + return $name; + } + + /** + * {@inheritdoc} + */ + public function exists($name) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function getCacheKey($name) + { + return $name; + } + + /** + * {@inheritdoc} + */ + public function isFresh($name, $time) + { + return true; + } +} diff --git a/inc/Twig/LoaderInterface.php b/inc/Twig/LoaderInterface.php new file mode 100644 index 0000000..927786d --- /dev/null +++ b/inc/Twig/LoaderInterface.php @@ -0,0 +1,52 @@ + + */ +interface Twig_LoaderInterface +{ + /** + * Gets the source code of a template, given its name. + * + * @param string $name The name of the template to load + * + * @return string The template source code + * + * @throws Twig_Error_Loader When $name is not found + */ + public function getSource($name); + + /** + * Gets the cache key to use for the cache for a given template name. + * + * @param string $name The name of the template to load + * + * @return string The cache key + * + * @throws Twig_Error_Loader When $name is not found + */ + public function getCacheKey($name); + + /** + * Returns true if the template is still fresh. + * + * @param string $name The template name + * @param timestamp $time The last modification time of the cached template + * + * @return Boolean true if the template is fresh, false otherwise + * + * @throws Twig_Error_Loader When $name is not found + */ + public function isFresh($name, $time); +} diff --git a/inc/Twig/Markup.php b/inc/Twig/Markup.php new file mode 100644 index 0000000..69871fc --- /dev/null +++ b/inc/Twig/Markup.php @@ -0,0 +1,37 @@ + + */ +class Twig_Markup implements Countable +{ + protected $content; + protected $charset; + + public function __construct($content, $charset) + { + $this->content = (string) $content; + $this->charset = $charset; + } + + public function __toString() + { + return $this->content; + } + + public function count() + { + return function_exists('mb_get_info') ? mb_strlen($this->content, $this->charset) : strlen($this->content); + } +} diff --git a/inc/Twig/Node.php b/inc/Twig/Node.php new file mode 100644 index 0000000..931b463 --- /dev/null +++ b/inc/Twig/Node.php @@ -0,0 +1,226 @@ + + */ +class Twig_Node implements Twig_NodeInterface +{ + protected $nodes; + protected $attributes; + protected $lineno; + protected $tag; + + /** + * Constructor. + * + * The nodes are automatically made available as properties ($this->node). + * The attributes are automatically made available as array items ($this['name']). + * + * @param array $nodes An array of named nodes + * @param array $attributes An array of attributes (should not be nodes) + * @param integer $lineno The line number + * @param string $tag The tag name associated with the Node + */ + public function __construct(array $nodes = array(), array $attributes = array(), $lineno = 0, $tag = null) + { + $this->nodes = $nodes; + $this->attributes = $attributes; + $this->lineno = $lineno; + $this->tag = $tag; + } + + public function __toString() + { + $attributes = array(); + foreach ($this->attributes as $name => $value) { + $attributes[] = sprintf('%s: %s', $name, str_replace("\n", '', var_export($value, true))); + } + + $repr = array(get_class($this).'('.implode(', ', $attributes)); + + if (count($this->nodes)) { + foreach ($this->nodes as $name => $node) { + $len = strlen($name) + 4; + $noderepr = array(); + foreach (explode("\n", (string) $node) as $line) { + $noderepr[] = str_repeat(' ', $len).$line; + } + + $repr[] = sprintf(' %s: %s', $name, ltrim(implode("\n", $noderepr))); + } + + $repr[] = ')'; + } else { + $repr[0] .= ')'; + } + + return implode("\n", $repr); + } + + public function toXml($asDom = false) + { + $dom = new DOMDocument('1.0', 'UTF-8'); + $dom->formatOutput = true; + $dom->appendChild($xml = $dom->createElement('twig')); + + $xml->appendChild($node = $dom->createElement('node')); + $node->setAttribute('class', get_class($this)); + + foreach ($this->attributes as $name => $value) { + $node->appendChild($attribute = $dom->createElement('attribute')); + $attribute->setAttribute('name', $name); + $attribute->appendChild($dom->createTextNode($value)); + } + + foreach ($this->nodes as $name => $n) { + if (null === $n) { + continue; + } + + $child = $n->toXml(true)->getElementsByTagName('node')->item(0); + $child = $dom->importNode($child, true); + $child->setAttribute('name', $name); + + $node->appendChild($child); + } + + return $asDom ? $dom : $dom->saveXml(); + } + + public function compile(Twig_Compiler $compiler) + { + foreach ($this->nodes as $node) { + $node->compile($compiler); + } + } + + public function getLine() + { + return $this->lineno; + } + + public function getNodeTag() + { + return $this->tag; + } + + /** + * Returns true if the attribute is defined. + * + * @param string The attribute name + * + * @return Boolean true if the attribute is defined, false otherwise + */ + public function hasAttribute($name) + { + return array_key_exists($name, $this->attributes); + } + + /** + * Gets an attribute. + * + * @param string The attribute name + * + * @return mixed The attribute value + */ + public function getAttribute($name) + { + if (!array_key_exists($name, $this->attributes)) { + throw new LogicException(sprintf('Attribute "%s" does not exist for Node "%s".', $name, get_class($this))); + } + + return $this->attributes[$name]; + } + + /** + * Sets an attribute. + * + * @param string The attribute name + * @param mixed The attribute value + */ + public function setAttribute($name, $value) + { + $this->attributes[$name] = $value; + } + + /** + * Removes an attribute. + * + * @param string The attribute name + */ + public function removeAttribute($name) + { + unset($this->attributes[$name]); + } + + /** + * Returns true if the node with the given identifier exists. + * + * @param string The node name + * + * @return Boolean true if the node with the given name exists, false otherwise + */ + public function hasNode($name) + { + return array_key_exists($name, $this->nodes); + } + + /** + * Gets a node by name. + * + * @param string The node name + * + * @return Twig_Node A Twig_Node instance + */ + public function getNode($name) + { + if (!array_key_exists($name, $this->nodes)) { + throw new LogicException(sprintf('Node "%s" does not exist for Node "%s".', $name, get_class($this))); + } + + return $this->nodes[$name]; + } + + /** + * Sets a node. + * + * @param string The node name + * @param Twig_Node A Twig_Node instance + */ + public function setNode($name, $node = null) + { + $this->nodes[$name] = $node; + } + + /** + * Removes a node by name. + * + * @param string The node name + */ + public function removeNode($name) + { + unset($this->nodes[$name]); + } + + public function count() + { + return count($this->nodes); + } + + public function getIterator() + { + return new ArrayIterator($this->nodes); + } +} diff --git a/inc/Twig/Node/AutoEscape.php b/inc/Twig/Node/AutoEscape.php new file mode 100644 index 0000000..8f190e0 --- /dev/null +++ b/inc/Twig/Node/AutoEscape.php @@ -0,0 +1,39 @@ + + */ +class Twig_Node_AutoEscape extends Twig_Node +{ + public function __construct($value, Twig_NodeInterface $body, $lineno, $tag = 'autoescape') + { + parent::__construct(array('body' => $body), array('value' => $value), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler->subcompile($this->getNode('body')); + } +} diff --git a/inc/Twig/Node/Block.php b/inc/Twig/Node/Block.php new file mode 100644 index 0000000..50eb67e --- /dev/null +++ b/inc/Twig/Node/Block.php @@ -0,0 +1,44 @@ + + */ +class Twig_Node_Block extends Twig_Node +{ + public function __construct($name, Twig_NodeInterface $body, $lineno, $tag = null) + { + parent::__construct(array('body' => $body), array('name' => $name), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->addDebugInfo($this) + ->write(sprintf("public function block_%s(\$context, array \$blocks = array())\n", $this->getAttribute('name')), "{\n") + ->indent() + ; + + $compiler + ->subcompile($this->getNode('body')) + ->outdent() + ->write("}\n\n") + ; + } +} diff --git a/inc/Twig/Node/BlockReference.php b/inc/Twig/Node/BlockReference.php new file mode 100644 index 0000000..013e369 --- /dev/null +++ b/inc/Twig/Node/BlockReference.php @@ -0,0 +1,37 @@ + + */ +class Twig_Node_BlockReference extends Twig_Node implements Twig_NodeOutputInterface +{ + public function __construct($name, $lineno, $tag = null) + { + parent::__construct(array(), array('name' => $name), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->addDebugInfo($this) + ->write(sprintf("\$this->displayBlock('%s', \$context, \$blocks);\n", $this->getAttribute('name'))) + ; + } +} diff --git a/inc/Twig/Node/Body.php b/inc/Twig/Node/Body.php new file mode 100644 index 0000000..3ffb134 --- /dev/null +++ b/inc/Twig/Node/Body.php @@ -0,0 +1,19 @@ + + */ +class Twig_Node_Body extends Twig_Node +{ +} diff --git a/inc/Twig/Node/Do.php b/inc/Twig/Node/Do.php new file mode 100644 index 0000000..c528066 --- /dev/null +++ b/inc/Twig/Node/Do.php @@ -0,0 +1,38 @@ + + */ +class Twig_Node_Do extends Twig_Node +{ + public function __construct(Twig_Node_Expression $expr, $lineno, $tag = null) + { + parent::__construct(array('expr' => $expr), array(), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->addDebugInfo($this) + ->write('') + ->subcompile($this->getNode('expr')) + ->raw(";\n") + ; + } +} diff --git a/inc/Twig/Node/Embed.php b/inc/Twig/Node/Embed.php new file mode 100644 index 0000000..4c9456d --- /dev/null +++ b/inc/Twig/Node/Embed.php @@ -0,0 +1,38 @@ + + */ +class Twig_Node_Embed extends Twig_Node_Include +{ + // we don't inject the module to avoid node visitors to traverse it twice (as it will be already visited in the main module) + public function __construct($filename, $index, Twig_Node_Expression $variables = null, $only = false, $ignoreMissing = false, $lineno, $tag = null) + { + parent::__construct(new Twig_Node_Expression_Constant('not_used', $lineno), $variables, $only, $ignoreMissing, $lineno, $tag); + + $this->setAttribute('filename', $filename); + $this->setAttribute('index', $index); + } + + protected function addGetTemplate(Twig_Compiler $compiler) + { + $compiler + ->write("\$this->env->loadTemplate(") + ->string($this->getAttribute('filename')) + ->raw(', ') + ->string($this->getAttribute('index')) + ->raw(")") + ; + } +} diff --git a/inc/Twig/Node/Expression.php b/inc/Twig/Node/Expression.php new file mode 100644 index 0000000..a7382e7 --- /dev/null +++ b/inc/Twig/Node/Expression.php @@ -0,0 +1,20 @@ + + */ +abstract class Twig_Node_Expression extends Twig_Node +{ +} diff --git a/inc/Twig/Node/Expression/Array.php b/inc/Twig/Node/Expression/Array.php new file mode 100644 index 0000000..1da785f --- /dev/null +++ b/inc/Twig/Node/Expression/Array.php @@ -0,0 +1,86 @@ +index = -1; + foreach ($this->getKeyValuePairs() as $pair) { + if ($pair['key'] instanceof Twig_Node_Expression_Constant && ctype_digit((string) $pair['key']->getAttribute('value')) && $pair['key']->getAttribute('value') > $this->index) { + $this->index = $pair['key']->getAttribute('value'); + } + } + } + + public function getKeyValuePairs() + { + $pairs = array(); + + foreach (array_chunk($this->nodes, 2) as $pair) { + $pairs[] = array( + 'key' => $pair[0], + 'value' => $pair[1], + ); + } + + return $pairs; + } + + public function hasElement(Twig_Node_Expression $key) + { + foreach ($this->getKeyValuePairs() as $pair) { + // we compare the string representation of the keys + // to avoid comparing the line numbers which are not relevant here. + if ((string) $key == (string) $pair['key']) { + return true; + } + } + + return false; + } + + public function addElement(Twig_Node_Expression $value, Twig_Node_Expression $key = null) + { + if (null === $key) { + $key = new Twig_Node_Expression_Constant(++$this->index, $value->getLine()); + } + + array_push($this->nodes, $key, $value); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler->raw('array('); + $first = true; + foreach ($this->getKeyValuePairs() as $pair) { + if (!$first) { + $compiler->raw(', '); + } + $first = false; + + $compiler + ->subcompile($pair['key']) + ->raw(' => ') + ->subcompile($pair['value']) + ; + } + $compiler->raw(')'); + } +} diff --git a/inc/Twig/Node/Expression/AssignName.php b/inc/Twig/Node/Expression/AssignName.php new file mode 100644 index 0000000..2ddea78 --- /dev/null +++ b/inc/Twig/Node/Expression/AssignName.php @@ -0,0 +1,28 @@ +raw('$context[') + ->string($this->getAttribute('name')) + ->raw(']') + ; + } +} diff --git a/inc/Twig/Node/Expression/Binary.php b/inc/Twig/Node/Expression/Binary.php new file mode 100644 index 0000000..9dd5de2 --- /dev/null +++ b/inc/Twig/Node/Expression/Binary.php @@ -0,0 +1,40 @@ + $left, 'right' => $right), array(), $lineno); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->raw('(') + ->subcompile($this->getNode('left')) + ->raw(' ') + ; + $this->operator($compiler); + $compiler + ->raw(' ') + ->subcompile($this->getNode('right')) + ->raw(')') + ; + } + + abstract public function operator(Twig_Compiler $compiler); +} diff --git a/inc/Twig/Node/Expression/Binary/Add.php b/inc/Twig/Node/Expression/Binary/Add.php new file mode 100644 index 0000000..0ef8e11 --- /dev/null +++ b/inc/Twig/Node/Expression/Binary/Add.php @@ -0,0 +1,18 @@ +raw('+'); + } +} diff --git a/inc/Twig/Node/Expression/Binary/And.php b/inc/Twig/Node/Expression/Binary/And.php new file mode 100644 index 0000000..d5752eb --- /dev/null +++ b/inc/Twig/Node/Expression/Binary/And.php @@ -0,0 +1,18 @@ +raw('&&'); + } +} diff --git a/inc/Twig/Node/Expression/Binary/BitwiseAnd.php b/inc/Twig/Node/Expression/Binary/BitwiseAnd.php new file mode 100644 index 0000000..9a46d84 --- /dev/null +++ b/inc/Twig/Node/Expression/Binary/BitwiseAnd.php @@ -0,0 +1,18 @@ +raw('&'); + } +} diff --git a/inc/Twig/Node/Expression/Binary/BitwiseOr.php b/inc/Twig/Node/Expression/Binary/BitwiseOr.php new file mode 100644 index 0000000..058a20b --- /dev/null +++ b/inc/Twig/Node/Expression/Binary/BitwiseOr.php @@ -0,0 +1,18 @@ +raw('|'); + } +} diff --git a/inc/Twig/Node/Expression/Binary/BitwiseXor.php b/inc/Twig/Node/Expression/Binary/BitwiseXor.php new file mode 100644 index 0000000..f4da73d --- /dev/null +++ b/inc/Twig/Node/Expression/Binary/BitwiseXor.php @@ -0,0 +1,18 @@ +raw('^'); + } +} diff --git a/inc/Twig/Node/Expression/Binary/Concat.php b/inc/Twig/Node/Expression/Binary/Concat.php new file mode 100644 index 0000000..f9a6462 --- /dev/null +++ b/inc/Twig/Node/Expression/Binary/Concat.php @@ -0,0 +1,18 @@ +raw('.'); + } +} diff --git a/inc/Twig/Node/Expression/Binary/Div.php b/inc/Twig/Node/Expression/Binary/Div.php new file mode 100644 index 0000000..e0797a6 --- /dev/null +++ b/inc/Twig/Node/Expression/Binary/Div.php @@ -0,0 +1,18 @@ +raw('/'); + } +} diff --git a/inc/Twig/Node/Expression/Binary/Equal.php b/inc/Twig/Node/Expression/Binary/Equal.php new file mode 100644 index 0000000..7b1236d --- /dev/null +++ b/inc/Twig/Node/Expression/Binary/Equal.php @@ -0,0 +1,17 @@ +raw('=='); + } +} diff --git a/inc/Twig/Node/Expression/Binary/FloorDiv.php b/inc/Twig/Node/Expression/Binary/FloorDiv.php new file mode 100644 index 0000000..7fbd055 --- /dev/null +++ b/inc/Twig/Node/Expression/Binary/FloorDiv.php @@ -0,0 +1,29 @@ +raw('intval(floor('); + parent::compile($compiler); + $compiler->raw('))'); + } + + public function operator(Twig_Compiler $compiler) + { + return $compiler->raw('/'); + } +} diff --git a/inc/Twig/Node/Expression/Binary/Greater.php b/inc/Twig/Node/Expression/Binary/Greater.php new file mode 100644 index 0000000..a110bd9 --- /dev/null +++ b/inc/Twig/Node/Expression/Binary/Greater.php @@ -0,0 +1,17 @@ +raw('>'); + } +} diff --git a/inc/Twig/Node/Expression/Binary/GreaterEqual.php b/inc/Twig/Node/Expression/Binary/GreaterEqual.php new file mode 100644 index 0000000..3754fed --- /dev/null +++ b/inc/Twig/Node/Expression/Binary/GreaterEqual.php @@ -0,0 +1,17 @@ +raw('>='); + } +} diff --git a/inc/Twig/Node/Expression/Binary/In.php b/inc/Twig/Node/Expression/Binary/In.php new file mode 100644 index 0000000..788f937 --- /dev/null +++ b/inc/Twig/Node/Expression/Binary/In.php @@ -0,0 +1,33 @@ +raw('twig_in_filter(') + ->subcompile($this->getNode('left')) + ->raw(', ') + ->subcompile($this->getNode('right')) + ->raw(')') + ; + } + + public function operator(Twig_Compiler $compiler) + { + return $compiler->raw('in'); + } +} diff --git a/inc/Twig/Node/Expression/Binary/Less.php b/inc/Twig/Node/Expression/Binary/Less.php new file mode 100644 index 0000000..45fd300 --- /dev/null +++ b/inc/Twig/Node/Expression/Binary/Less.php @@ -0,0 +1,17 @@ +raw('<'); + } +} diff --git a/inc/Twig/Node/Expression/Binary/LessEqual.php b/inc/Twig/Node/Expression/Binary/LessEqual.php new file mode 100644 index 0000000..e38e257 --- /dev/null +++ b/inc/Twig/Node/Expression/Binary/LessEqual.php @@ -0,0 +1,17 @@ +raw('<='); + } +} diff --git a/inc/Twig/Node/Expression/Binary/Mod.php b/inc/Twig/Node/Expression/Binary/Mod.php new file mode 100644 index 0000000..9924114 --- /dev/null +++ b/inc/Twig/Node/Expression/Binary/Mod.php @@ -0,0 +1,18 @@ +raw('%'); + } +} diff --git a/inc/Twig/Node/Expression/Binary/Mul.php b/inc/Twig/Node/Expression/Binary/Mul.php new file mode 100644 index 0000000..c91529c --- /dev/null +++ b/inc/Twig/Node/Expression/Binary/Mul.php @@ -0,0 +1,18 @@ +raw('*'); + } +} diff --git a/inc/Twig/Node/Expression/Binary/NotEqual.php b/inc/Twig/Node/Expression/Binary/NotEqual.php new file mode 100644 index 0000000..26867ba --- /dev/null +++ b/inc/Twig/Node/Expression/Binary/NotEqual.php @@ -0,0 +1,17 @@ +raw('!='); + } +} diff --git a/inc/Twig/Node/Expression/Binary/NotIn.php b/inc/Twig/Node/Expression/Binary/NotIn.php new file mode 100644 index 0000000..f347b7b --- /dev/null +++ b/inc/Twig/Node/Expression/Binary/NotIn.php @@ -0,0 +1,33 @@ +raw('!twig_in_filter(') + ->subcompile($this->getNode('left')) + ->raw(', ') + ->subcompile($this->getNode('right')) + ->raw(')') + ; + } + + public function operator(Twig_Compiler $compiler) + { + return $compiler->raw('not in'); + } +} diff --git a/inc/Twig/Node/Expression/Binary/Or.php b/inc/Twig/Node/Expression/Binary/Or.php new file mode 100644 index 0000000..adba49c --- /dev/null +++ b/inc/Twig/Node/Expression/Binary/Or.php @@ -0,0 +1,18 @@ +raw('||'); + } +} diff --git a/inc/Twig/Node/Expression/Binary/Power.php b/inc/Twig/Node/Expression/Binary/Power.php new file mode 100644 index 0000000..b2c5904 --- /dev/null +++ b/inc/Twig/Node/Expression/Binary/Power.php @@ -0,0 +1,33 @@ +raw('pow(') + ->subcompile($this->getNode('left')) + ->raw(', ') + ->subcompile($this->getNode('right')) + ->raw(')') + ; + } + + public function operator(Twig_Compiler $compiler) + { + return $compiler->raw('**'); + } +} diff --git a/inc/Twig/Node/Expression/Binary/Range.php b/inc/Twig/Node/Expression/Binary/Range.php new file mode 100644 index 0000000..bea4f2a --- /dev/null +++ b/inc/Twig/Node/Expression/Binary/Range.php @@ -0,0 +1,33 @@ +raw('range(') + ->subcompile($this->getNode('left')) + ->raw(', ') + ->subcompile($this->getNode('right')) + ->raw(')') + ; + } + + public function operator(Twig_Compiler $compiler) + { + return $compiler->raw('..'); + } +} diff --git a/inc/Twig/Node/Expression/Binary/Sub.php b/inc/Twig/Node/Expression/Binary/Sub.php new file mode 100644 index 0000000..d446399 --- /dev/null +++ b/inc/Twig/Node/Expression/Binary/Sub.php @@ -0,0 +1,18 @@ +raw('-'); + } +} diff --git a/inc/Twig/Node/Expression/BlockReference.php b/inc/Twig/Node/Expression/BlockReference.php new file mode 100644 index 0000000..647196e --- /dev/null +++ b/inc/Twig/Node/Expression/BlockReference.php @@ -0,0 +1,51 @@ + + */ +class Twig_Node_Expression_BlockReference extends Twig_Node_Expression +{ + public function __construct(Twig_NodeInterface $name, $asString = false, $lineno, $tag = null) + { + parent::__construct(array('name' => $name), array('as_string' => $asString, 'output' => false), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + if ($this->getAttribute('as_string')) { + $compiler->raw('(string) '); + } + + if ($this->getAttribute('output')) { + $compiler + ->addDebugInfo($this) + ->write("\$this->displayBlock(") + ->subcompile($this->getNode('name')) + ->raw(", \$context, \$blocks);\n") + ; + } else { + $compiler + ->raw("\$this->renderBlock(") + ->subcompile($this->getNode('name')) + ->raw(", \$context, \$blocks)") + ; + } + } +} diff --git a/inc/Twig/Node/Expression/Call.php b/inc/Twig/Node/Expression/Call.php new file mode 100644 index 0000000..87b62de --- /dev/null +++ b/inc/Twig/Node/Expression/Call.php @@ -0,0 +1,178 @@ +getAttribute('callable'); + + $closingParenthesis = false; + if ($callable) { + if (is_string($callable)) { + $compiler->raw($callable); + } elseif (is_array($callable) && $callable[0] instanceof Twig_ExtensionInterface) { + $compiler->raw(sprintf('$this->env->getExtension(\'%s\')->%s', $callable[0]->getName(), $callable[1])); + } else { + $type = ucfirst($this->getAttribute('type')); + $compiler->raw(sprintf('call_user_func_array($this->env->get%s(\'%s\')->getCallable(), array', $type, $this->getAttribute('name'))); + $closingParenthesis = true; + } + } else { + $compiler->raw($this->getAttribute('thing')->compile()); + } + + $this->compileArguments($compiler); + + if ($closingParenthesis) { + $compiler->raw(')'); + } + } + + protected function compileArguments(Twig_Compiler $compiler) + { + $compiler->raw('('); + + $first = true; + + if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) { + $compiler->raw('$this->env'); + $first = false; + } + + if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) { + if (!$first) { + $compiler->raw(', '); + } + $compiler->raw('$context'); + $first = false; + } + + if ($this->hasAttribute('arguments')) { + foreach ($this->getAttribute('arguments') as $argument) { + if (!$first) { + $compiler->raw(', '); + } + $compiler->string($argument); + $first = false; + } + } + + if ($this->hasNode('node')) { + if (!$first) { + $compiler->raw(', '); + } + $compiler->subcompile($this->getNode('node')); + $first = false; + } + + if ($this->hasNode('arguments') && null !== $this->getNode('arguments')) { + $callable = $this->hasAttribute('callable') ? $this->getAttribute('callable') : null; + + $arguments = $this->getArguments($callable, $this->getNode('arguments')); + + foreach ($arguments as $node) { + if (!$first) { + $compiler->raw(', '); + } + $compiler->subcompile($node); + $first = false; + } + } + + $compiler->raw(')'); + } + + protected function getArguments($callable, $arguments) + { + $parameters = array(); + $named = false; + foreach ($arguments as $name => $node) { + if (!is_int($name)) { + $named = true; + $name = $this->normalizeName($name); + } elseif ($named) { + throw new Twig_Error_Syntax(sprintf('Positional arguments cannot be used after named arguments for %s "%s".', $this->getAttribute('type'), $this->getAttribute('name'))); + } + + $parameters[$name] = $node; + } + + if (!$named) { + return $parameters; + } + + if (!$callable) { + throw new LogicException(sprintf('Named arguments are not supported for %s "%s".', $this->getAttribute('type'), $this->getAttribute('name'))); + } + + // manage named arguments + if (is_array($callable)) { + $r = new ReflectionMethod($callable[0], $callable[1]); + } elseif (is_object($callable) && !$callable instanceof Closure) { + $r = new ReflectionObject($callable); + $r = $r->getMethod('__invoke'); + } else { + $r = new ReflectionFunction($callable); + } + + $definition = $r->getParameters(); + if ($this->hasNode('node')) { + array_shift($definition); + } + if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) { + array_shift($definition); + } + if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) { + array_shift($definition); + } + if ($this->hasAttribute('arguments') && null !== $this->getAttribute('arguments')) { + foreach ($this->getAttribute('arguments') as $argument) { + array_shift($definition); + } + } + + $arguments = array(); + $pos = 0; + foreach ($definition as $param) { + $name = $this->normalizeName($param->name); + + if (array_key_exists($name, $parameters)) { + if (array_key_exists($pos, $parameters)) { + throw new Twig_Error_Syntax(sprintf('Arguments "%s" is defined twice for %s "%s".', $name, $this->getAttribute('type'), $this->getAttribute('name'))); + } + + $arguments[] = $parameters[$name]; + unset($parameters[$name]); + } elseif (array_key_exists($pos, $parameters)) { + $arguments[] = $parameters[$pos]; + unset($parameters[$pos]); + ++$pos; + } elseif ($param->isDefaultValueAvailable()) { + $arguments[] = new Twig_Node_Expression_Constant($param->getDefaultValue(), -1); + } elseif ($param->isOptional()) { + break; + } else { + throw new Twig_Error_Syntax(sprintf('Value for argument "%s" is required for %s "%s".', $name, $this->getAttribute('type'), $this->getAttribute('name'))); + } + } + + foreach (array_keys($parameters) as $name) { + throw new Twig_Error_Syntax(sprintf('Unknown argument "%s" for %s "%s".', $name, $this->getAttribute('type'), $this->getAttribute('name'))); + } + + return $arguments; + } + + protected function normalizeName($name) + { + return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), $name)); + } +} diff --git a/inc/Twig/Node/Expression/Conditional.php b/inc/Twig/Node/Expression/Conditional.php new file mode 100644 index 0000000..edcb1e2 --- /dev/null +++ b/inc/Twig/Node/Expression/Conditional.php @@ -0,0 +1,31 @@ + $expr1, 'expr2' => $expr2, 'expr3' => $expr3), array(), $lineno); + } + + public function compile(Twig_Compiler $compiler) + { + $compiler + ->raw('((') + ->subcompile($this->getNode('expr1')) + ->raw(') ? (') + ->subcompile($this->getNode('expr2')) + ->raw(') : (') + ->subcompile($this->getNode('expr3')) + ->raw('))') + ; + } +} diff --git a/inc/Twig/Node/Expression/Constant.php b/inc/Twig/Node/Expression/Constant.php new file mode 100644 index 0000000..a91dc69 --- /dev/null +++ b/inc/Twig/Node/Expression/Constant.php @@ -0,0 +1,23 @@ + $value), $lineno); + } + + public function compile(Twig_Compiler $compiler) + { + $compiler->repr($this->getAttribute('value')); + } +} diff --git a/inc/Twig/Node/Expression/ExtensionReference.php b/inc/Twig/Node/Expression/ExtensionReference.php new file mode 100644 index 0000000..00ac670 --- /dev/null +++ b/inc/Twig/Node/Expression/ExtensionReference.php @@ -0,0 +1,33 @@ + + */ +class Twig_Node_Expression_ExtensionReference extends Twig_Node_Expression +{ + public function __construct($name, $lineno, $tag = null) + { + parent::__construct(array(), array('name' => $name), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler->raw(sprintf("\$this->env->getExtension('%s')", $this->getAttribute('name'))); + } +} diff --git a/inc/Twig/Node/Expression/Filter.php b/inc/Twig/Node/Expression/Filter.php new file mode 100644 index 0000000..207b062 --- /dev/null +++ b/inc/Twig/Node/Expression/Filter.php @@ -0,0 +1,36 @@ + $node, 'filter' => $filterName, 'arguments' => $arguments), array(), $lineno, $tag); + } + + public function compile(Twig_Compiler $compiler) + { + $name = $this->getNode('filter')->getAttribute('value'); + $filter = $compiler->getEnvironment()->getFilter($name); + + $this->setAttribute('name', $name); + $this->setAttribute('type', 'filter'); + $this->setAttribute('thing', $filter); + $this->setAttribute('needs_environment', $filter->needsEnvironment()); + $this->setAttribute('needs_context', $filter->needsContext()); + $this->setAttribute('arguments', $filter->getArguments()); + if ($filter instanceof Twig_FilterCallableInterface || $filter instanceof Twig_SimpleFilter) { + $this->setAttribute('callable', $filter->getCallable()); + } + + $this->compileCallable($compiler); + } +} diff --git a/inc/Twig/Node/Expression/Filter/Default.php b/inc/Twig/Node/Expression/Filter/Default.php new file mode 100644 index 0000000..1827c88 --- /dev/null +++ b/inc/Twig/Node/Expression/Filter/Default.php @@ -0,0 +1,43 @@ + + * {{ var.foo|default('foo item on var is not defined') }} + * + * + * @author Fabien Potencier + */ +class Twig_Node_Expression_Filter_Default extends Twig_Node_Expression_Filter +{ + public function __construct(Twig_NodeInterface $node, Twig_Node_Expression_Constant $filterName, Twig_NodeInterface $arguments, $lineno, $tag = null) + { + $default = new Twig_Node_Expression_Filter($node, new Twig_Node_Expression_Constant('default', $node->getLine()), $arguments, $node->getLine()); + + if ('default' === $filterName->getAttribute('value') && ($node instanceof Twig_Node_Expression_Name || $node instanceof Twig_Node_Expression_GetAttr)) { + $test = new Twig_Node_Expression_Test_Defined(clone $node, 'defined', new Twig_Node(), $node->getLine()); + $false = count($arguments) ? $arguments->getNode(0) : new Twig_Node_Expression_Constant('', $node->getLine()); + + $node = new Twig_Node_Expression_Conditional($test, $default, $false, $node->getLine()); + } else { + $node = $default; + } + + parent::__construct($node, $filterName, $arguments, $lineno, $tag); + } + + public function compile(Twig_Compiler $compiler) + { + $compiler->subcompile($this->getNode('node')); + } +} diff --git a/inc/Twig/Node/Expression/Function.php b/inc/Twig/Node/Expression/Function.php new file mode 100644 index 0000000..3e1f6b5 --- /dev/null +++ b/inc/Twig/Node/Expression/Function.php @@ -0,0 +1,35 @@ + $arguments), array('name' => $name), $lineno); + } + + public function compile(Twig_Compiler $compiler) + { + $name = $this->getAttribute('name'); + $function = $compiler->getEnvironment()->getFunction($name); + + $this->setAttribute('name', $name); + $this->setAttribute('type', 'function'); + $this->setAttribute('thing', $function); + $this->setAttribute('needs_environment', $function->needsEnvironment()); + $this->setAttribute('needs_context', $function->needsContext()); + $this->setAttribute('arguments', $function->getArguments()); + if ($function instanceof Twig_FunctionCallableInterface || $function instanceof Twig_SimpleFunction) { + $this->setAttribute('callable', $function->getCallable()); + } + + $this->compileCallable($compiler); + } +} diff --git a/inc/Twig/Node/Expression/GetAttr.php b/inc/Twig/Node/Expression/GetAttr.php new file mode 100644 index 0000000..81a9b13 --- /dev/null +++ b/inc/Twig/Node/Expression/GetAttr.php @@ -0,0 +1,53 @@ + $node, 'attribute' => $attribute, 'arguments' => $arguments), array('type' => $type, 'is_defined_test' => false, 'ignore_strict_check' => false, 'disable_c_ext' => false), $lineno); + } + + public function compile(Twig_Compiler $compiler) + { + if (function_exists('twig_template_get_attributes') && !$this->getAttribute('disable_c_ext')) { + $compiler->raw('twig_template_get_attributes($this, '); + } else { + $compiler->raw('$this->getAttribute('); + } + + if ($this->getAttribute('ignore_strict_check')) { + $this->getNode('node')->setAttribute('ignore_strict_check', true); + } + + $compiler->subcompile($this->getNode('node')); + + $compiler->raw(', ')->subcompile($this->getNode('attribute')); + + if (count($this->getNode('arguments')) || Twig_TemplateInterface::ANY_CALL !== $this->getAttribute('type') || $this->getAttribute('is_defined_test') || $this->getAttribute('ignore_strict_check')) { + $compiler->raw(', ')->subcompile($this->getNode('arguments')); + + if (Twig_TemplateInterface::ANY_CALL !== $this->getAttribute('type') || $this->getAttribute('is_defined_test') || $this->getAttribute('ignore_strict_check')) { + $compiler->raw(', ')->repr($this->getAttribute('type')); + } + + if ($this->getAttribute('is_defined_test') || $this->getAttribute('ignore_strict_check')) { + $compiler->raw(', '.($this->getAttribute('is_defined_test') ? 'true' : 'false')); + } + + if ($this->getAttribute('ignore_strict_check')) { + $compiler->raw(', '.($this->getAttribute('ignore_strict_check') ? 'true' : 'false')); + } + } + + $compiler->raw(')'); + } +} diff --git a/inc/Twig/Node/Expression/MethodCall.php b/inc/Twig/Node/Expression/MethodCall.php new file mode 100644 index 0000000..620b02b --- /dev/null +++ b/inc/Twig/Node/Expression/MethodCall.php @@ -0,0 +1,41 @@ + $node, 'arguments' => $arguments), array('method' => $method, 'safe' => false), $lineno); + + if ($node instanceof Twig_Node_Expression_Name) { + $node->setAttribute('always_defined', true); + } + } + + public function compile(Twig_Compiler $compiler) + { + $compiler + ->subcompile($this->getNode('node')) + ->raw('->') + ->raw($this->getAttribute('method')) + ->raw('(') + ; + $first = true; + foreach ($this->getNode('arguments')->getKeyValuePairs() as $pair) { + if (!$first) { + $compiler->raw(', '); + } + $first = false; + + $compiler->subcompile($pair['value']); + } + $compiler->raw(')'); + } +} diff --git a/inc/Twig/Node/Expression/Name.php b/inc/Twig/Node/Expression/Name.php new file mode 100644 index 0000000..3b8fae0 --- /dev/null +++ b/inc/Twig/Node/Expression/Name.php @@ -0,0 +1,88 @@ + '$this', + '_context' => '$context', + '_charset' => '$this->env->getCharset()', + ); + + public function __construct($name, $lineno) + { + parent::__construct(array(), array('name' => $name, 'is_defined_test' => false, 'ignore_strict_check' => false, 'always_defined' => false), $lineno); + } + + public function compile(Twig_Compiler $compiler) + { + $name = $this->getAttribute('name'); + + if ($this->getAttribute('is_defined_test')) { + if ($this->isSpecial()) { + $compiler->repr(true); + } else { + $compiler->raw('array_key_exists(')->repr($name)->raw(', $context)'); + } + } elseif ($this->isSpecial()) { + $compiler->raw($this->specialVars[$name]); + } elseif ($this->getAttribute('always_defined')) { + $compiler + ->raw('$context[') + ->string($name) + ->raw(']') + ; + } else { + // remove the non-PHP 5.4 version when PHP 5.3 support is dropped + // as the non-optimized version is just a workaround for slow ternary operator + // when the context has a lot of variables + if (version_compare(phpversion(), '5.4.0RC1', '>=')) { + // PHP 5.4 ternary operator performance was optimized + $compiler + ->raw('(isset($context[') + ->string($name) + ->raw(']) ? $context[') + ->string($name) + ->raw('] : ') + ; + + if ($this->getAttribute('ignore_strict_check') || !$compiler->getEnvironment()->isStrictVariables()) { + $compiler->raw('null)'); + } else { + $compiler->raw('$this->getContext($context, ')->string($name)->raw('))'); + } + } else { + $compiler + ->raw('$this->getContext($context, ') + ->string($name) + ; + + if ($this->getAttribute('ignore_strict_check')) { + $compiler->raw(', true'); + } + + $compiler + ->raw(')') + ; + } + } + } + + public function isSpecial() + { + return isset($this->specialVars[$this->getAttribute('name')]); + } + + public function isSimple() + { + return !$this->isSpecial() && !$this->getAttribute('is_defined_test'); + } +} diff --git a/inc/Twig/Node/Expression/Parent.php b/inc/Twig/Node/Expression/Parent.php new file mode 100644 index 0000000..dcf618c --- /dev/null +++ b/inc/Twig/Node/Expression/Parent.php @@ -0,0 +1,47 @@ + + */ +class Twig_Node_Expression_Parent extends Twig_Node_Expression +{ + public function __construct($name, $lineno, $tag = null) + { + parent::__construct(array(), array('output' => false, 'name' => $name), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + if ($this->getAttribute('output')) { + $compiler + ->addDebugInfo($this) + ->write("\$this->displayParentBlock(") + ->string($this->getAttribute('name')) + ->raw(", \$context, \$blocks);\n") + ; + } else { + $compiler + ->raw("\$this->renderParentBlock(") + ->string($this->getAttribute('name')) + ->raw(", \$context, \$blocks)") + ; + } + } +} diff --git a/inc/Twig/Node/Expression/TempName.php b/inc/Twig/Node/Expression/TempName.php new file mode 100644 index 0000000..e6b058e --- /dev/null +++ b/inc/Twig/Node/Expression/TempName.php @@ -0,0 +1,26 @@ + $name), $lineno); + } + + public function compile(Twig_Compiler $compiler) + { + $compiler + ->raw('$_') + ->raw($this->getAttribute('name')) + ->raw('_') + ; + } +} diff --git a/inc/Twig/Node/Expression/Test.php b/inc/Twig/Node/Expression/Test.php new file mode 100644 index 0000000..639f501 --- /dev/null +++ b/inc/Twig/Node/Expression/Test.php @@ -0,0 +1,32 @@ + $node, 'arguments' => $arguments), array('name' => $name), $lineno); + } + + public function compile(Twig_Compiler $compiler) + { + $name = $this->getAttribute('name'); + $test = $compiler->getEnvironment()->getTest($name); + + $this->setAttribute('name', $name); + $this->setAttribute('type', 'test'); + $this->setAttribute('thing', $test); + if ($test instanceof Twig_TestCallableInterface || $test instanceof Twig_SimpleTest) { + $this->setAttribute('callable', $test->getCallable()); + } + + $this->compileCallable($compiler); + } +} diff --git a/inc/Twig/Node/Expression/Test/Constant.php b/inc/Twig/Node/Expression/Test/Constant.php new file mode 100644 index 0000000..de55f5f --- /dev/null +++ b/inc/Twig/Node/Expression/Test/Constant.php @@ -0,0 +1,46 @@ + + * {% if post.status is constant('Post::PUBLISHED') %} + * the status attribute is exactly the same as Post::PUBLISHED + * {% endif %} + * + * + * @author Fabien Potencier + */ +class Twig_Node_Expression_Test_Constant extends Twig_Node_Expression_Test +{ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->raw('(') + ->subcompile($this->getNode('node')) + ->raw(' === constant(') + ; + + if ($this->getNode('arguments')->hasNode(1)) { + $compiler + ->raw('get_class(') + ->subcompile($this->getNode('arguments')->getNode(1)) + ->raw(')."::".') + ; + } + + $compiler + ->subcompile($this->getNode('arguments')->getNode(0)) + ->raw('))') + ; + } +} diff --git a/inc/Twig/Node/Expression/Test/Defined.php b/inc/Twig/Node/Expression/Test/Defined.php new file mode 100644 index 0000000..247b2e2 --- /dev/null +++ b/inc/Twig/Node/Expression/Test/Defined.php @@ -0,0 +1,54 @@ + + * {# defined works with variable names and variable attributes #} + * {% if foo is defined %} + * {# ... #} + * {% endif %} + * + * + * @author Fabien Potencier + */ +class Twig_Node_Expression_Test_Defined extends Twig_Node_Expression_Test +{ + public function __construct(Twig_NodeInterface $node, $name, Twig_NodeInterface $arguments = null, $lineno) + { + parent::__construct($node, $name, $arguments, $lineno); + + if ($node instanceof Twig_Node_Expression_Name) { + $node->setAttribute('is_defined_test', true); + } elseif ($node instanceof Twig_Node_Expression_GetAttr) { + $node->setAttribute('is_defined_test', true); + + $this->changeIgnoreStrictCheck($node); + } else { + throw new Twig_Error_Syntax('The "defined" test only works with simple variables', $this->getLine()); + } + } + + protected function changeIgnoreStrictCheck(Twig_Node_Expression_GetAttr $node) + { + $node->setAttribute('ignore_strict_check', true); + + if ($node->getNode('node') instanceof Twig_Node_Expression_GetAttr) { + $this->changeIgnoreStrictCheck($node->getNode('node')); + } + } + + public function compile(Twig_Compiler $compiler) + { + $compiler->subcompile($this->getNode('node')); + } +} diff --git a/inc/Twig/Node/Expression/Test/Divisibleby.php b/inc/Twig/Node/Expression/Test/Divisibleby.php new file mode 100644 index 0000000..0aceb53 --- /dev/null +++ b/inc/Twig/Node/Expression/Test/Divisibleby.php @@ -0,0 +1,33 @@ + + * {% if loop.index is divisibleby(3) %} + * + * + * @author Fabien Potencier + */ +class Twig_Node_Expression_Test_Divisibleby extends Twig_Node_Expression_Test +{ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->raw('(0 == ') + ->subcompile($this->getNode('node')) + ->raw(' % ') + ->subcompile($this->getNode('arguments')->getNode(0)) + ->raw(')') + ; + } +} diff --git a/inc/Twig/Node/Expression/Test/Even.php b/inc/Twig/Node/Expression/Test/Even.php new file mode 100644 index 0000000..d7853e8 --- /dev/null +++ b/inc/Twig/Node/Expression/Test/Even.php @@ -0,0 +1,32 @@ + + * {{ var is even }} + * + * + * @author Fabien Potencier + */ +class Twig_Node_Expression_Test_Even extends Twig_Node_Expression_Test +{ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->raw('(') + ->subcompile($this->getNode('node')) + ->raw(' % 2 == 0') + ->raw(')') + ; + } +} diff --git a/inc/Twig/Node/Expression/Test/Null.php b/inc/Twig/Node/Expression/Test/Null.php new file mode 100644 index 0000000..1c83825 --- /dev/null +++ b/inc/Twig/Node/Expression/Test/Null.php @@ -0,0 +1,31 @@ + + * {{ var is none }} + * + * + * @author Fabien Potencier + */ +class Twig_Node_Expression_Test_Null extends Twig_Node_Expression_Test +{ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->raw('(null === ') + ->subcompile($this->getNode('node')) + ->raw(')') + ; + } +} diff --git a/inc/Twig/Node/Expression/Test/Odd.php b/inc/Twig/Node/Expression/Test/Odd.php new file mode 100644 index 0000000..421c19e --- /dev/null +++ b/inc/Twig/Node/Expression/Test/Odd.php @@ -0,0 +1,32 @@ + + * {{ var is odd }} + * + * + * @author Fabien Potencier + */ +class Twig_Node_Expression_Test_Odd extends Twig_Node_Expression_Test +{ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->raw('(') + ->subcompile($this->getNode('node')) + ->raw(' % 2 == 1') + ->raw(')') + ; + } +} diff --git a/inc/Twig/Node/Expression/Test/Sameas.php b/inc/Twig/Node/Expression/Test/Sameas.php new file mode 100644 index 0000000..b48905e --- /dev/null +++ b/inc/Twig/Node/Expression/Test/Sameas.php @@ -0,0 +1,29 @@ + + */ +class Twig_Node_Expression_Test_Sameas extends Twig_Node_Expression_Test +{ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->raw('(') + ->subcompile($this->getNode('node')) + ->raw(' === ') + ->subcompile($this->getNode('arguments')->getNode(0)) + ->raw(')') + ; + } +} diff --git a/inc/Twig/Node/Expression/Unary.php b/inc/Twig/Node/Expression/Unary.php new file mode 100644 index 0000000..c514388 --- /dev/null +++ b/inc/Twig/Node/Expression/Unary.php @@ -0,0 +1,30 @@ + $node), array(), $lineno); + } + + public function compile(Twig_Compiler $compiler) + { + $compiler->raw('('); + $this->operator($compiler); + $compiler + ->subcompile($this->getNode('node')) + ->raw(')') + ; + } + + abstract public function operator(Twig_Compiler $compiler); +} diff --git a/inc/Twig/Node/Expression/Unary/Neg.php b/inc/Twig/Node/Expression/Unary/Neg.php new file mode 100644 index 0000000..2a3937e --- /dev/null +++ b/inc/Twig/Node/Expression/Unary/Neg.php @@ -0,0 +1,18 @@ +raw('-'); + } +} diff --git a/inc/Twig/Node/Expression/Unary/Not.php b/inc/Twig/Node/Expression/Unary/Not.php new file mode 100644 index 0000000..f94073c --- /dev/null +++ b/inc/Twig/Node/Expression/Unary/Not.php @@ -0,0 +1,18 @@ +raw('!'); + } +} diff --git a/inc/Twig/Node/Expression/Unary/Pos.php b/inc/Twig/Node/Expression/Unary/Pos.php new file mode 100644 index 0000000..04edb52 --- /dev/null +++ b/inc/Twig/Node/Expression/Unary/Pos.php @@ -0,0 +1,18 @@ +raw('+'); + } +} diff --git a/inc/Twig/Node/Flush.php b/inc/Twig/Node/Flush.php new file mode 100644 index 0000000..0467ddc --- /dev/null +++ b/inc/Twig/Node/Flush.php @@ -0,0 +1,36 @@ + + */ +class Twig_Node_Flush extends Twig_Node +{ + public function __construct($lineno, $tag) + { + parent::__construct(array(), array(), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->addDebugInfo($this) + ->write("flush();\n") + ; + } +} diff --git a/inc/Twig/Node/For.php b/inc/Twig/Node/For.php new file mode 100644 index 0000000..d1ff371 --- /dev/null +++ b/inc/Twig/Node/For.php @@ -0,0 +1,112 @@ + + */ +class Twig_Node_For extends Twig_Node +{ + protected $loop; + + public function __construct(Twig_Node_Expression_AssignName $keyTarget, Twig_Node_Expression_AssignName $valueTarget, Twig_Node_Expression $seq, Twig_Node_Expression $ifexpr = null, Twig_NodeInterface $body, Twig_NodeInterface $else = null, $lineno, $tag = null) + { + $body = new Twig_Node(array($body, $this->loop = new Twig_Node_ForLoop($lineno, $tag))); + + if (null !== $ifexpr) { + $body = new Twig_Node_If(new Twig_Node(array($ifexpr, $body)), null, $lineno, $tag); + } + + parent::__construct(array('key_target' => $keyTarget, 'value_target' => $valueTarget, 'seq' => $seq, 'body' => $body, 'else' => $else), array('with_loop' => true, 'ifexpr' => null !== $ifexpr), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->addDebugInfo($this) + // the (array) cast bypasses a PHP 5.2.6 bug + ->write("\$context['_parent'] = (array) \$context;\n") + ->write("\$context['_seq'] = twig_ensure_traversable(") + ->subcompile($this->getNode('seq')) + ->raw(");\n") + ; + + if (null !== $this->getNode('else')) { + $compiler->write("\$context['_iterated'] = false;\n"); + } + + if ($this->getAttribute('with_loop')) { + $compiler + ->write("\$context['loop'] = array(\n") + ->write(" 'parent' => \$context['_parent'],\n") + ->write(" 'index0' => 0,\n") + ->write(" 'index' => 1,\n") + ->write(" 'first' => true,\n") + ->write(");\n") + ; + + if (!$this->getAttribute('ifexpr')) { + $compiler + ->write("if (is_array(\$context['_seq']) || (is_object(\$context['_seq']) && \$context['_seq'] instanceof Countable)) {\n") + ->indent() + ->write("\$length = count(\$context['_seq']);\n") + ->write("\$context['loop']['revindex0'] = \$length - 1;\n") + ->write("\$context['loop']['revindex'] = \$length;\n") + ->write("\$context['loop']['length'] = \$length;\n") + ->write("\$context['loop']['last'] = 1 === \$length;\n") + ->outdent() + ->write("}\n") + ; + } + } + + $this->loop->setAttribute('else', null !== $this->getNode('else')); + $this->loop->setAttribute('with_loop', $this->getAttribute('with_loop')); + $this->loop->setAttribute('ifexpr', $this->getAttribute('ifexpr')); + + $compiler + ->write("foreach (\$context['_seq'] as ") + ->subcompile($this->getNode('key_target')) + ->raw(" => ") + ->subcompile($this->getNode('value_target')) + ->raw(") {\n") + ->indent() + ->subcompile($this->getNode('body')) + ->outdent() + ->write("}\n") + ; + + if (null !== $this->getNode('else')) { + $compiler + ->write("if (!\$context['_iterated']) {\n") + ->indent() + ->subcompile($this->getNode('else')) + ->outdent() + ->write("}\n") + ; + } + + $compiler->write("\$_parent = \$context['_parent'];\n"); + + // remove some "private" loop variables (needed for nested loops) + $compiler->write('unset($context[\'_seq\'], $context[\'_iterated\'], $context[\''.$this->getNode('key_target')->getAttribute('name').'\'], $context[\''.$this->getNode('value_target')->getAttribute('name').'\'], $context[\'_parent\'], $context[\'loop\']);'."\n"); + + // keep the values set in the inner context for variables defined in the outer context + $compiler->write("\$context = array_intersect_key(\$context, \$_parent) + \$_parent;\n"); + } +} diff --git a/inc/Twig/Node/ForLoop.php b/inc/Twig/Node/ForLoop.php new file mode 100644 index 0000000..b884158 --- /dev/null +++ b/inc/Twig/Node/ForLoop.php @@ -0,0 +1,55 @@ + + */ +class Twig_Node_ForLoop extends Twig_Node +{ + public function __construct($lineno, $tag = null) + { + parent::__construct(array(), array('with_loop' => false, 'ifexpr' => false, 'else' => false), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + if ($this->getAttribute('else')) { + $compiler->write("\$context['_iterated'] = true;\n"); + } + + if ($this->getAttribute('with_loop')) { + $compiler + ->write("++\$context['loop']['index0'];\n") + ->write("++\$context['loop']['index'];\n") + ->write("\$context['loop']['first'] = false;\n") + ; + + if (!$this->getAttribute('ifexpr')) { + $compiler + ->write("if (isset(\$context['loop']['length'])) {\n") + ->indent() + ->write("--\$context['loop']['revindex0'];\n") + ->write("--\$context['loop']['revindex'];\n") + ->write("\$context['loop']['last'] = 0 === \$context['loop']['revindex0'];\n") + ->outdent() + ->write("}\n") + ; + } + } + } +} diff --git a/inc/Twig/Node/If.php b/inc/Twig/Node/If.php new file mode 100644 index 0000000..4296a8d --- /dev/null +++ b/inc/Twig/Node/If.php @@ -0,0 +1,66 @@ + + */ +class Twig_Node_If extends Twig_Node +{ + public function __construct(Twig_NodeInterface $tests, Twig_NodeInterface $else = null, $lineno, $tag = null) + { + parent::__construct(array('tests' => $tests, 'else' => $else), array(), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler->addDebugInfo($this); + for ($i = 0; $i < count($this->getNode('tests')); $i += 2) { + if ($i > 0) { + $compiler + ->outdent() + ->write("} elseif (") + ; + } else { + $compiler + ->write('if (') + ; + } + + $compiler + ->subcompile($this->getNode('tests')->getNode($i)) + ->raw(") {\n") + ->indent() + ->subcompile($this->getNode('tests')->getNode($i + 1)) + ; + } + + if ($this->hasNode('else') && null !== $this->getNode('else')) { + $compiler + ->outdent() + ->write("} else {\n") + ->indent() + ->subcompile($this->getNode('else')) + ; + } + + $compiler + ->outdent() + ->write("}\n"); + } +} diff --git a/inc/Twig/Node/Import.php b/inc/Twig/Node/Import.php new file mode 100644 index 0000000..99efc09 --- /dev/null +++ b/inc/Twig/Node/Import.php @@ -0,0 +1,50 @@ + + */ +class Twig_Node_Import extends Twig_Node +{ + public function __construct(Twig_Node_Expression $expr, Twig_Node_Expression $var, $lineno, $tag = null) + { + parent::__construct(array('expr' => $expr, 'var' => $var), array(), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->addDebugInfo($this) + ->write('') + ->subcompile($this->getNode('var')) + ->raw(' = ') + ; + + if ($this->getNode('expr') instanceof Twig_Node_Expression_Name && '_self' === $this->getNode('expr')->getAttribute('name')) { + $compiler->raw("\$this"); + } else { + $compiler + ->raw('$this->env->loadTemplate(') + ->subcompile($this->getNode('expr')) + ->raw(")") + ; + } + + $compiler->raw(";\n"); + } +} diff --git a/inc/Twig/Node/Include.php b/inc/Twig/Node/Include.php new file mode 100644 index 0000000..ed4a375 --- /dev/null +++ b/inc/Twig/Node/Include.php @@ -0,0 +1,99 @@ + + */ +class Twig_Node_Include extends Twig_Node implements Twig_NodeOutputInterface +{ + public function __construct(Twig_Node_Expression $expr, Twig_Node_Expression $variables = null, $only = false, $ignoreMissing = false, $lineno, $tag = null) + { + parent::__construct(array('expr' => $expr, 'variables' => $variables), array('only' => (Boolean) $only, 'ignore_missing' => (Boolean) $ignoreMissing), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler->addDebugInfo($this); + + if ($this->getAttribute('ignore_missing')) { + $compiler + ->write("try {\n") + ->indent() + ; + } + + $this->addGetTemplate($compiler); + + $compiler->raw('->display('); + + $this->addTemplateArguments($compiler); + + $compiler->raw(");\n"); + + if ($this->getAttribute('ignore_missing')) { + $compiler + ->outdent() + ->write("} catch (Twig_Error_Loader \$e) {\n") + ->indent() + ->write("// ignore missing template\n") + ->outdent() + ->write("}\n\n") + ; + } + } + + protected function addGetTemplate(Twig_Compiler $compiler) + { + if ($this->getNode('expr') instanceof Twig_Node_Expression_Constant) { + $compiler + ->write("\$this->env->loadTemplate(") + ->subcompile($this->getNode('expr')) + ->raw(")") + ; + } else { + $compiler + ->write("\$template = \$this->env->resolveTemplate(") + ->subcompile($this->getNode('expr')) + ->raw(");\n") + ->write('$template') + ; + } + } + + protected function addTemplateArguments(Twig_Compiler $compiler) + { + if (false === $this->getAttribute('only')) { + if (null === $this->getNode('variables')) { + $compiler->raw('$context'); + } else { + $compiler + ->raw('array_merge($context, ') + ->subcompile($this->getNode('variables')) + ->raw(')') + ; + } + } else { + if (null === $this->getNode('variables')) { + $compiler->raw('array()'); + } else { + $compiler->subcompile($this->getNode('variables')); + } + } + } +} diff --git a/inc/Twig/Node/Macro.php b/inc/Twig/Node/Macro.php new file mode 100644 index 0000000..8991061 --- /dev/null +++ b/inc/Twig/Node/Macro.php @@ -0,0 +1,96 @@ + + */ +class Twig_Node_Macro extends Twig_Node +{ + public function __construct($name, Twig_NodeInterface $body, Twig_NodeInterface $arguments, $lineno, $tag = null) + { + parent::__construct(array('body' => $body, 'arguments' => $arguments), array('name' => $name), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->addDebugInfo($this) + ->write(sprintf("public function get%s(", $this->getAttribute('name'))) + ; + + $count = count($this->getNode('arguments')); + $pos = 0; + foreach ($this->getNode('arguments') as $name => $default) { + $compiler + ->raw('$_'.$name.' = ') + ->subcompile($default) + ; + + if (++$pos < $count) { + $compiler->raw(', '); + } + } + + $compiler + ->raw(")\n") + ->write("{\n") + ->indent() + ; + + if (!count($this->getNode('arguments'))) { + $compiler->write("\$context = \$this->env->getGlobals();\n\n"); + } else { + $compiler + ->write("\$context = \$this->env->mergeGlobals(array(\n") + ->indent() + ; + + foreach ($this->getNode('arguments') as $name => $default) { + $compiler + ->write('') + ->string($name) + ->raw(' => $_'.$name) + ->raw(",\n") + ; + } + + $compiler + ->outdent() + ->write("));\n\n") + ; + } + + $compiler + ->write("\$blocks = array();\n\n") + ->write("ob_start();\n") + ->write("try {\n") + ->indent() + ->subcompile($this->getNode('body')) + ->outdent() + ->write("} catch (Exception \$e) {\n") + ->indent() + ->write("ob_end_clean();\n\n") + ->write("throw \$e;\n") + ->outdent() + ->write("}\n\n") + ->write("return ('' === \$tmp = ob_get_clean()) ? '' : new Twig_Markup(\$tmp, \$this->env->getCharset());\n") + ->outdent() + ->write("}\n\n") + ; + } +} diff --git a/inc/Twig/Node/Module.php b/inc/Twig/Node/Module.php new file mode 100644 index 0000000..585048b --- /dev/null +++ b/inc/Twig/Node/Module.php @@ -0,0 +1,371 @@ + + */ +class Twig_Node_Module extends Twig_Node +{ + public function __construct(Twig_NodeInterface $body, Twig_Node_Expression $parent = null, Twig_NodeInterface $blocks, Twig_NodeInterface $macros, Twig_NodeInterface $traits, $embeddedTemplates, $filename) + { + // embedded templates are set as attributes so that they are only visited once by the visitors + parent::__construct(array('parent' => $parent, 'body' => $body, 'blocks' => $blocks, 'macros' => $macros, 'traits' => $traits), array('filename' => $filename, 'index' => null, 'embedded_templates' => $embeddedTemplates), 1); + } + + public function setIndex($index) + { + $this->setAttribute('index', $index); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $this->compileTemplate($compiler); + + foreach ($this->getAttribute('embedded_templates') as $template) { + $compiler->subcompile($template); + } + } + + protected function compileTemplate(Twig_Compiler $compiler) + { + if (!$this->getAttribute('index')) { + $compiler->write('compileClassHeader($compiler); + + if (count($this->getNode('blocks')) || count($this->getNode('traits')) || null === $this->getNode('parent') || $this->getNode('parent') instanceof Twig_Node_Expression_Constant) { + $this->compileConstructor($compiler); + } + + $this->compileGetParent($compiler); + + $this->compileDisplayHeader($compiler); + + $this->compileDisplayBody($compiler); + + $this->compileDisplayFooter($compiler); + + $compiler->subcompile($this->getNode('blocks')); + + $this->compileMacros($compiler); + + $this->compileGetTemplateName($compiler); + + $this->compileIsTraitable($compiler); + + $this->compileDebugInfo($compiler); + + $this->compileClassFooter($compiler); + } + + protected function compileGetParent(Twig_Compiler $compiler) + { + if (null === $this->getNode('parent')) { + return; + } + + $compiler + ->write("protected function doGetParent(array \$context)\n", "{\n") + ->indent() + ->write("return ") + ; + + if ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) { + $compiler->subcompile($this->getNode('parent')); + } else { + $compiler + ->raw("\$this->env->resolveTemplate(") + ->subcompile($this->getNode('parent')) + ->raw(")") + ; + } + + $compiler + ->raw(";\n") + ->outdent() + ->write("}\n\n") + ; + } + + protected function compileDisplayBody(Twig_Compiler $compiler) + { + $compiler->subcompile($this->getNode('body')); + + if (null !== $this->getNode('parent')) { + if ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) { + $compiler->write("\$this->parent"); + } else { + $compiler->write("\$this->getParent(\$context)"); + } + $compiler->raw("->display(\$context, array_merge(\$this->blocks, \$blocks));\n"); + } + } + + protected function compileClassHeader(Twig_Compiler $compiler) + { + $compiler + ->write("\n\n") + // if the filename contains */, add a blank to avoid a PHP parse error + ->write("/* ".str_replace('*/', '* /', $this->getAttribute('filename'))." */\n") + ->write('class '.$compiler->getEnvironment()->getTemplateClass($this->getAttribute('filename'), $this->getAttribute('index'))) + ->raw(sprintf(" extends %s\n", $compiler->getEnvironment()->getBaseTemplateClass())) + ->write("{\n") + ->indent() + ; + } + + protected function compileConstructor(Twig_Compiler $compiler) + { + $compiler + ->write("public function __construct(Twig_Environment \$env)\n", "{\n") + ->indent() + ->write("parent::__construct(\$env);\n\n") + ; + + // parent + if (null === $this->getNode('parent')) { + $compiler->write("\$this->parent = false;\n\n"); + } elseif ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) { + $compiler + ->write("\$this->parent = \$this->env->loadTemplate(") + ->subcompile($this->getNode('parent')) + ->raw(");\n\n") + ; + } + + $countTraits = count($this->getNode('traits')); + if ($countTraits) { + // traits + foreach ($this->getNode('traits') as $i => $trait) { + $this->compileLoadTemplate($compiler, $trait->getNode('template'), sprintf('$_trait_%s', $i)); + + $compiler + ->addDebugInfo($trait->getNode('template')) + ->write(sprintf("if (!\$_trait_%s->isTraitable()) {\n", $i)) + ->indent() + ->write("throw new Twig_Error_Runtime('Template \"'.") + ->subcompile($trait->getNode('template')) + ->raw(".'\" cannot be used as a trait.');\n") + ->outdent() + ->write("}\n") + ->write(sprintf("\$_trait_%s_blocks = \$_trait_%s->getBlocks();\n\n", $i, $i)) + ; + + foreach ($trait->getNode('targets') as $key => $value) { + $compiler + ->write(sprintf("\$_trait_%s_blocks[", $i)) + ->subcompile($value) + ->raw(sprintf("] = \$_trait_%s_blocks[", $i)) + ->string($key) + ->raw(sprintf("]; unset(\$_trait_%s_blocks[", $i)) + ->string($key) + ->raw("]);\n\n") + ; + } + } + + if ($countTraits > 1) { + $compiler + ->write("\$this->traits = array_merge(\n") + ->indent() + ; + + for ($i = 0; $i < $countTraits; $i++) { + $compiler + ->write(sprintf("\$_trait_%s_blocks".($i == $countTraits - 1 ? '' : ',')."\n", $i)) + ; + } + + $compiler + ->outdent() + ->write(");\n\n") + ; + } else { + $compiler + ->write("\$this->traits = \$_trait_0_blocks;\n\n") + ; + } + + $compiler + ->write("\$this->blocks = array_merge(\n") + ->indent() + ->write("\$this->traits,\n") + ->write("array(\n") + ; + } else { + $compiler + ->write("\$this->blocks = array(\n") + ; + } + + // blocks + $compiler + ->indent() + ; + + foreach ($this->getNode('blocks') as $name => $node) { + $compiler + ->write(sprintf("'%s' => array(\$this, 'block_%s'),\n", $name, $name)) + ; + } + + if ($countTraits) { + $compiler + ->outdent() + ->write(")\n") + ; + } + + $compiler + ->outdent() + ->write(");\n") + ->outdent() + ->write("}\n\n"); + ; + } + + protected function compileDisplayHeader(Twig_Compiler $compiler) + { + $compiler + ->write("protected function doDisplay(array \$context, array \$blocks = array())\n", "{\n") + ->indent() + ; + } + + protected function compileDisplayFooter(Twig_Compiler $compiler) + { + $compiler + ->outdent() + ->write("}\n\n") + ; + } + + protected function compileClassFooter(Twig_Compiler $compiler) + { + $compiler + ->outdent() + ->write("}\n") + ; + } + + protected function compileMacros(Twig_Compiler $compiler) + { + $compiler->subcompile($this->getNode('macros')); + } + + protected function compileGetTemplateName(Twig_Compiler $compiler) + { + $compiler + ->write("public function getTemplateName()\n", "{\n") + ->indent() + ->write('return ') + ->repr($this->getAttribute('filename')) + ->raw(";\n") + ->outdent() + ->write("}\n\n") + ; + } + + protected function compileIsTraitable(Twig_Compiler $compiler) + { + // A template can be used as a trait if: + // * it has no parent + // * it has no macros + // * it has no body + // + // Put another way, a template can be used as a trait if it + // only contains blocks and use statements. + $traitable = null === $this->getNode('parent') && 0 === count($this->getNode('macros')); + if ($traitable) { + if ($this->getNode('body') instanceof Twig_Node_Body) { + $nodes = $this->getNode('body')->getNode(0); + } else { + $nodes = $this->getNode('body'); + } + + if (!count($nodes)) { + $nodes = new Twig_Node(array($nodes)); + } + + foreach ($nodes as $node) { + if (!count($node)) { + continue; + } + + if ($node instanceof Twig_Node_Text && ctype_space($node->getAttribute('data'))) { + continue; + } + + if ($node instanceof Twig_Node_BlockReference) { + continue; + } + + $traitable = false; + break; + } + } + + if ($traitable) { + return; + } + + $compiler + ->write("public function isTraitable()\n", "{\n") + ->indent() + ->write(sprintf("return %s;\n", $traitable ? 'true' : 'false')) + ->outdent() + ->write("}\n\n") + ; + } + + protected function compileDebugInfo(Twig_Compiler $compiler) + { + $compiler + ->write("public function getDebugInfo()\n", "{\n") + ->indent() + ->write(sprintf("return %s;\n", str_replace("\n", '', var_export(array_reverse($compiler->getDebugInfo(), true), true)))) + ->outdent() + ->write("}\n") + ; + } + + protected function compileLoadTemplate(Twig_Compiler $compiler, $node, $var) + { + if ($node instanceof Twig_Node_Expression_Constant) { + $compiler + ->write(sprintf("%s = \$this->env->loadTemplate(", $var)) + ->subcompile($node) + ->raw(");\n") + ; + } else { + $compiler + ->write(sprintf("%s = ", $var)) + ->subcompile($node) + ->raw(";\n") + ->write(sprintf("if (!%s", $var)) + ->raw(" instanceof Twig_Template) {\n") + ->indent() + ->write(sprintf("%s = \$this->env->loadTemplate(%s);\n", $var, $var)) + ->outdent() + ->write("}\n") + ; + } + } +} diff --git a/inc/Twig/Node/Print.php b/inc/Twig/Node/Print.php new file mode 100644 index 0000000..b0c41d1 --- /dev/null +++ b/inc/Twig/Node/Print.php @@ -0,0 +1,39 @@ + + */ +class Twig_Node_Print extends Twig_Node implements Twig_NodeOutputInterface +{ + public function __construct(Twig_Node_Expression $expr, $lineno, $tag = null) + { + parent::__construct(array('expr' => $expr), array(), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->addDebugInfo($this) + ->write('echo ') + ->subcompile($this->getNode('expr')) + ->raw(";\n") + ; + } +} diff --git a/inc/Twig/Node/Sandbox.php b/inc/Twig/Node/Sandbox.php new file mode 100644 index 0000000..8cf3ed4 --- /dev/null +++ b/inc/Twig/Node/Sandbox.php @@ -0,0 +1,47 @@ + + */ +class Twig_Node_Sandbox extends Twig_Node +{ + public function __construct(Twig_NodeInterface $body, $lineno, $tag = null) + { + parent::__construct(array('body' => $body), array(), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->addDebugInfo($this) + ->write("\$sandbox = \$this->env->getExtension('sandbox');\n") + ->write("if (!\$alreadySandboxed = \$sandbox->isSandboxed()) {\n") + ->indent() + ->write("\$sandbox->enableSandbox();\n") + ->outdent() + ->write("}\n") + ->subcompile($this->getNode('body')) + ->write("if (!\$alreadySandboxed) {\n") + ->indent() + ->write("\$sandbox->disableSandbox();\n") + ->outdent() + ->write("}\n") + ; + } +} diff --git a/inc/Twig/Node/SandboxedModule.php b/inc/Twig/Node/SandboxedModule.php new file mode 100644 index 0000000..be1f5da --- /dev/null +++ b/inc/Twig/Node/SandboxedModule.php @@ -0,0 +1,60 @@ + + */ +class Twig_Node_SandboxedModule extends Twig_Node_Module +{ + protected $usedFilters; + protected $usedTags; + protected $usedFunctions; + + public function __construct(Twig_Node_Module $node, array $usedFilters, array $usedTags, array $usedFunctions) + { + parent::__construct($node->getNode('body'), $node->getNode('parent'), $node->getNode('blocks'), $node->getNode('macros'), $node->getNode('traits'), $node->getAttribute('embedded_templates'), $node->getAttribute('filename'), $node->getLine(), $node->getNodeTag()); + + $this->setAttribute('index', $node->getAttribute('index')); + + $this->usedFilters = $usedFilters; + $this->usedTags = $usedTags; + $this->usedFunctions = $usedFunctions; + } + + protected function compileDisplayBody(Twig_Compiler $compiler) + { + $compiler->write("\$this->checkSecurity();\n"); + + parent::compileDisplayBody($compiler); + } + + protected function compileDisplayFooter(Twig_Compiler $compiler) + { + parent::compileDisplayFooter($compiler); + + $compiler + ->write("protected function checkSecurity()\n", "{\n") + ->indent() + ->write("\$this->env->getExtension('sandbox')->checkSecurity(\n") + ->indent() + ->write(!$this->usedTags ? "array(),\n" : "array('".implode('\', \'', $this->usedTags)."'),\n") + ->write(!$this->usedFilters ? "array(),\n" : "array('".implode('\', \'', $this->usedFilters)."'),\n") + ->write(!$this->usedFunctions ? "array()\n" : "array('".implode('\', \'', $this->usedFunctions)."')\n") + ->outdent() + ->write(");\n") + ->outdent() + ->write("}\n\n") + ; + } +} diff --git a/inc/Twig/Node/SandboxedPrint.php b/inc/Twig/Node/SandboxedPrint.php new file mode 100644 index 0000000..73dfaa9 --- /dev/null +++ b/inc/Twig/Node/SandboxedPrint.php @@ -0,0 +1,59 @@ + + */ +class Twig_Node_SandboxedPrint extends Twig_Node_Print +{ + public function __construct(Twig_Node_Expression $expr, $lineno, $tag = null) + { + parent::__construct($expr, $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->addDebugInfo($this) + ->write('echo $this->env->getExtension(\'sandbox\')->ensureToStringAllowed(') + ->subcompile($this->getNode('expr')) + ->raw(");\n") + ; + } + + /** + * Removes node filters. + * + * This is mostly needed when another visitor adds filters (like the escaper one). + * + * @param Twig_Node $node A Node + */ + protected function removeNodeFilter($node) + { + if ($node instanceof Twig_Node_Expression_Filter) { + return $this->removeNodeFilter($node->getNode('node')); + } + + return $node; + } +} diff --git a/inc/Twig/Node/Set.php b/inc/Twig/Node/Set.php new file mode 100644 index 0000000..4c9c16c --- /dev/null +++ b/inc/Twig/Node/Set.php @@ -0,0 +1,101 @@ + + */ +class Twig_Node_Set extends Twig_Node +{ + public function __construct($capture, Twig_NodeInterface $names, Twig_NodeInterface $values, $lineno, $tag = null) + { + parent::__construct(array('names' => $names, 'values' => $values), array('capture' => $capture, 'safe' => false), $lineno, $tag); + + /* + * Optimizes the node when capture is used for a large block of text. + * + * {% set foo %}foo{% endset %} is compiled to $context['foo'] = new Twig_Markup("foo"); + */ + if ($this->getAttribute('capture')) { + $this->setAttribute('safe', true); + + $values = $this->getNode('values'); + if ($values instanceof Twig_Node_Text) { + $this->setNode('values', new Twig_Node_Expression_Constant($values->getAttribute('data'), $values->getLine())); + $this->setAttribute('capture', false); + } + } + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler->addDebugInfo($this); + + if (count($this->getNode('names')) > 1) { + $compiler->write('list('); + foreach ($this->getNode('names') as $idx => $node) { + if ($idx) { + $compiler->raw(', '); + } + + $compiler->subcompile($node); + } + $compiler->raw(')'); + } else { + if ($this->getAttribute('capture')) { + $compiler + ->write("ob_start();\n") + ->subcompile($this->getNode('values')) + ; + } + + $compiler->subcompile($this->getNode('names'), false); + + if ($this->getAttribute('capture')) { + $compiler->raw(" = ('' === \$tmp = ob_get_clean()) ? '' : new Twig_Markup(\$tmp, \$this->env->getCharset())"); + } + } + + if (!$this->getAttribute('capture')) { + $compiler->raw(' = '); + + if (count($this->getNode('names')) > 1) { + $compiler->write('array('); + foreach ($this->getNode('values') as $idx => $value) { + if ($idx) { + $compiler->raw(', '); + } + + $compiler->subcompile($value); + } + $compiler->raw(')'); + } else { + if ($this->getAttribute('safe')) { + $compiler + ->raw("('' === \$tmp = ") + ->subcompile($this->getNode('values')) + ->raw(") ? '' : new Twig_Markup(\$tmp, \$this->env->getCharset())") + ; + } else { + $compiler->subcompile($this->getNode('values')); + } + } + } + + $compiler->raw(";\n"); + } +} diff --git a/inc/Twig/Node/SetTemp.php b/inc/Twig/Node/SetTemp.php new file mode 100644 index 0000000..3bdd1cb --- /dev/null +++ b/inc/Twig/Node/SetTemp.php @@ -0,0 +1,35 @@ + $name), $lineno); + } + + public function compile(Twig_Compiler $compiler) + { + $name = $this->getAttribute('name'); + $compiler + ->addDebugInfo($this) + ->write('if (isset($context[') + ->string($name) + ->raw('])) { $_') + ->raw($name) + ->raw('_ = $context[') + ->repr($name) + ->raw(']; } else { $_') + ->raw($name) + ->raw("_ = null; }\n") + ; + } +} diff --git a/inc/Twig/Node/Spaceless.php b/inc/Twig/Node/Spaceless.php new file mode 100644 index 0000000..7555fa0 --- /dev/null +++ b/inc/Twig/Node/Spaceless.php @@ -0,0 +1,40 @@ + + */ +class Twig_Node_Spaceless extends Twig_Node +{ + public function __construct(Twig_NodeInterface $body, $lineno, $tag = 'spaceless') + { + parent::__construct(array('body' => $body), array(), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->addDebugInfo($this) + ->write("ob_start();\n") + ->subcompile($this->getNode('body')) + ->write("echo trim(preg_replace('/>\s+<', ob_get_clean()));\n") + ; + } +} diff --git a/inc/Twig/Node/Text.php b/inc/Twig/Node/Text.php new file mode 100644 index 0000000..21bdcea --- /dev/null +++ b/inc/Twig/Node/Text.php @@ -0,0 +1,39 @@ + + */ +class Twig_Node_Text extends Twig_Node implements Twig_NodeOutputInterface +{ + public function __construct($data, $lineno) + { + parent::__construct(array(), array('data' => $data), $lineno); + } + + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler) + { + $compiler + ->addDebugInfo($this) + ->write('echo ') + ->string($this->getAttribute('data')) + ->raw(";\n") + ; + } +} diff --git a/inc/Twig/NodeInterface.php b/inc/Twig/NodeInterface.php new file mode 100644 index 0000000..f0ef725 --- /dev/null +++ b/inc/Twig/NodeInterface.php @@ -0,0 +1,30 @@ + + * @deprecated since 1.12 (to be removed in 2.0) + */ +interface Twig_NodeInterface extends Countable, IteratorAggregate +{ + /** + * Compiles the node to PHP. + * + * @param Twig_Compiler A Twig_Compiler instance + */ + public function compile(Twig_Compiler $compiler); + + public function getLine(); + + public function getNodeTag(); +} diff --git a/inc/Twig/NodeOutputInterface.php b/inc/Twig/NodeOutputInterface.php new file mode 100644 index 0000000..22172c0 --- /dev/null +++ b/inc/Twig/NodeOutputInterface.php @@ -0,0 +1,19 @@ + + */ +interface Twig_NodeOutputInterface +{ +} diff --git a/inc/Twig/NodeTraverser.php b/inc/Twig/NodeTraverser.php new file mode 100644 index 0000000..28cba1a --- /dev/null +++ b/inc/Twig/NodeTraverser.php @@ -0,0 +1,88 @@ + + */ +class Twig_NodeTraverser +{ + protected $env; + protected $visitors; + + /** + * Constructor. + * + * @param Twig_Environment $env A Twig_Environment instance + * @param array $visitors An array of Twig_NodeVisitorInterface instances + */ + public function __construct(Twig_Environment $env, array $visitors = array()) + { + $this->env = $env; + $this->visitors = array(); + foreach ($visitors as $visitor) { + $this->addVisitor($visitor); + } + } + + /** + * Adds a visitor. + * + * @param Twig_NodeVisitorInterface $visitor A Twig_NodeVisitorInterface instance + */ + public function addVisitor(Twig_NodeVisitorInterface $visitor) + { + if (!isset($this->visitors[$visitor->getPriority()])) { + $this->visitors[$visitor->getPriority()] = array(); + } + + $this->visitors[$visitor->getPriority()][] = $visitor; + } + + /** + * Traverses a node and calls the registered visitors. + * + * @param Twig_NodeInterface $node A Twig_NodeInterface instance + */ + public function traverse(Twig_NodeInterface $node) + { + ksort($this->visitors); + foreach ($this->visitors as $visitors) { + foreach ($visitors as $visitor) { + $node = $this->traverseForVisitor($visitor, $node); + } + } + + return $node; + } + + protected function traverseForVisitor(Twig_NodeVisitorInterface $visitor, Twig_NodeInterface $node = null) + { + if (null === $node) { + return null; + } + + $node = $visitor->enterNode($node, $this->env); + + foreach ($node as $k => $n) { + if (false !== $n = $this->traverseForVisitor($visitor, $n)) { + $node->setNode($k, $n); + } else { + $node->removeNode($k); + } + } + + return $visitor->leaveNode($node, $this->env); + } +} diff --git a/inc/Twig/NodeVisitor/Escaper.php b/inc/Twig/NodeVisitor/Escaper.php new file mode 100644 index 0000000..cc4b3d7 --- /dev/null +++ b/inc/Twig/NodeVisitor/Escaper.php @@ -0,0 +1,167 @@ + + */ +class Twig_NodeVisitor_Escaper implements Twig_NodeVisitorInterface +{ + protected $statusStack = array(); + protected $blocks = array(); + protected $safeAnalysis; + protected $traverser; + protected $defaultStrategy = false; + protected $safeVars = array(); + + public function __construct() + { + $this->safeAnalysis = new Twig_NodeVisitor_SafeAnalysis(); + } + + /** + * Called before child nodes are visited. + * + * @param Twig_NodeInterface $node The node to visit + * @param Twig_Environment $env The Twig environment instance + * + * @return Twig_NodeInterface The modified node + */ + public function enterNode(Twig_NodeInterface $node, Twig_Environment $env) + { + if ($node instanceof Twig_Node_Module) { + if ($env->hasExtension('escaper') && $defaultStrategy = $env->getExtension('escaper')->getDefaultStrategy($node->getAttribute('filename'))) { + $this->defaultStrategy = $defaultStrategy; + } + $this->safeVars = array(); + } elseif ($node instanceof Twig_Node_AutoEscape) { + $this->statusStack[] = $node->getAttribute('value'); + } elseif ($node instanceof Twig_Node_Block) { + $this->statusStack[] = isset($this->blocks[$node->getAttribute('name')]) ? $this->blocks[$node->getAttribute('name')] : $this->needEscaping($env); + } elseif ($node instanceof Twig_Node_Import) { + $this->safeVars[] = $node->getNode('var')->getAttribute('name'); + } + + return $node; + } + + /** + * Called after child nodes are visited. + * + * @param Twig_NodeInterface $node The node to visit + * @param Twig_Environment $env The Twig environment instance + * + * @return Twig_NodeInterface The modified node + */ + public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env) + { + if ($node instanceof Twig_Node_Module) { + $this->defaultStrategy = false; + $this->safeVars = array(); + } elseif ($node instanceof Twig_Node_Expression_Filter) { + return $this->preEscapeFilterNode($node, $env); + } elseif ($node instanceof Twig_Node_Print) { + return $this->escapePrintNode($node, $env, $this->needEscaping($env)); + } + + if ($node instanceof Twig_Node_AutoEscape || $node instanceof Twig_Node_Block) { + array_pop($this->statusStack); + } elseif ($node instanceof Twig_Node_BlockReference) { + $this->blocks[$node->getAttribute('name')] = $this->needEscaping($env); + } + + return $node; + } + + protected function escapePrintNode(Twig_Node_Print $node, Twig_Environment $env, $type) + { + if (false === $type) { + return $node; + } + + $expression = $node->getNode('expr'); + + if ($this->isSafeFor($type, $expression, $env)) { + return $node; + } + + $class = get_class($node); + + return new $class( + $this->getEscaperFilter($type, $expression), + $node->getLine() + ); + } + + protected function preEscapeFilterNode(Twig_Node_Expression_Filter $filter, Twig_Environment $env) + { + $name = $filter->getNode('filter')->getAttribute('value'); + + $type = $env->getFilter($name)->getPreEscape(); + if (null === $type) { + return $filter; + } + + $node = $filter->getNode('node'); + if ($this->isSafeFor($type, $node, $env)) { + return $filter; + } + + $filter->setNode('node', $this->getEscaperFilter($type, $node)); + + return $filter; + } + + protected function isSafeFor($type, Twig_NodeInterface $expression, $env) + { + $safe = $this->safeAnalysis->getSafe($expression); + + if (null === $safe) { + if (null === $this->traverser) { + $this->traverser = new Twig_NodeTraverser($env, array($this->safeAnalysis)); + } + + $this->safeAnalysis->setSafeVars($this->safeVars); + + $this->traverser->traverse($expression); + $safe = $this->safeAnalysis->getSafe($expression); + } + + return in_array($type, $safe) || in_array('all', $safe); + } + + protected function needEscaping(Twig_Environment $env) + { + if (count($this->statusStack)) { + return $this->statusStack[count($this->statusStack) - 1]; + } + + return $this->defaultStrategy ? $this->defaultStrategy : false; + } + + protected function getEscaperFilter($type, Twig_NodeInterface $node) + { + $line = $node->getLine(); + $name = new Twig_Node_Expression_Constant('escape', $line); + $args = new Twig_Node(array(new Twig_Node_Expression_Constant((string) $type, $line), new Twig_Node_Expression_Constant(null, $line), new Twig_Node_Expression_Constant(true, $line))); + + return new Twig_Node_Expression_Filter($node, $name, $args, $line); + } + + /** + * {@inheritdoc} + */ + public function getPriority() + { + return 0; + } +} diff --git a/inc/Twig/NodeVisitor/Optimizer.php b/inc/Twig/NodeVisitor/Optimizer.php new file mode 100644 index 0000000..a254def --- /dev/null +++ b/inc/Twig/NodeVisitor/Optimizer.php @@ -0,0 +1,246 @@ + + */ +class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface +{ + const OPTIMIZE_ALL = -1; + const OPTIMIZE_NONE = 0; + const OPTIMIZE_FOR = 2; + const OPTIMIZE_RAW_FILTER = 4; + const OPTIMIZE_VAR_ACCESS = 8; + + protected $loops = array(); + protected $optimizers; + protected $prependedNodes = array(); + protected $inABody = false; + + /** + * Constructor. + * + * @param integer $optimizers The optimizer mode + */ + public function __construct($optimizers = -1) + { + if (!is_int($optimizers) || $optimizers > 2) { + throw new InvalidArgumentException(sprintf('Optimizer mode "%s" is not valid.', $optimizers)); + } + + $this->optimizers = $optimizers; + } + + /** + * {@inheritdoc} + */ + public function enterNode(Twig_NodeInterface $node, Twig_Environment $env) + { + if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR & $this->optimizers)) { + $this->enterOptimizeFor($node, $env); + } + + if (!version_compare(phpversion(), '5.4.0RC1', '>=') && self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('sandbox')) { + if ($this->inABody) { + if (!$node instanceof Twig_Node_Expression) { + if (get_class($node) !== 'Twig_Node') { + array_unshift($this->prependedNodes, array()); + } + } else { + $node = $this->optimizeVariables($node, $env); + } + } elseif ($node instanceof Twig_Node_Body) { + $this->inABody = true; + } + } + + return $node; + } + + /** + * {@inheritdoc} + */ + public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env) + { + $expression = $node instanceof Twig_Node_Expression; + + if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR & $this->optimizers)) { + $this->leaveOptimizeFor($node, $env); + } + + if (self::OPTIMIZE_RAW_FILTER === (self::OPTIMIZE_RAW_FILTER & $this->optimizers)) { + $node = $this->optimizeRawFilter($node, $env); + } + + $node = $this->optimizePrintNode($node, $env); + + if (self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('sandbox')) { + if ($node instanceof Twig_Node_Body) { + $this->inABody = false; + } elseif ($this->inABody) { + if (!$expression && get_class($node) !== 'Twig_Node' && $prependedNodes = array_shift($this->prependedNodes)) { + $nodes = array(); + foreach (array_unique($prependedNodes) as $name) { + $nodes[] = new Twig_Node_SetTemp($name, $node->getLine()); + } + + $nodes[] = $node; + $node = new Twig_Node($nodes); + } + } + } + + return $node; + } + + protected function optimizeVariables($node, $env) + { + if ('Twig_Node_Expression_Name' === get_class($node) && $node->isSimple()) { + $this->prependedNodes[0][] = $node->getAttribute('name'); + + return new Twig_Node_Expression_TempName($node->getAttribute('name'), $node->getLine()); + } + + return $node; + } + + /** + * Optimizes print nodes. + * + * It replaces: + * + * * "echo $this->render(Parent)Block()" with "$this->display(Parent)Block()" + * + * @param Twig_NodeInterface $node A Node + * @param Twig_Environment $env The current Twig environment + */ + protected function optimizePrintNode($node, $env) + { + if (!$node instanceof Twig_Node_Print) { + return $node; + } + + if ( + $node->getNode('expr') instanceof Twig_Node_Expression_BlockReference || + $node->getNode('expr') instanceof Twig_Node_Expression_Parent + ) { + $node->getNode('expr')->setAttribute('output', true); + + return $node->getNode('expr'); + } + + return $node; + } + + /** + * Removes "raw" filters. + * + * @param Twig_NodeInterface $node A Node + * @param Twig_Environment $env The current Twig environment + */ + protected function optimizeRawFilter($node, $env) + { + if ($node instanceof Twig_Node_Expression_Filter && 'raw' == $node->getNode('filter')->getAttribute('value')) { + return $node->getNode('node'); + } + + return $node; + } + + /** + * Optimizes "for" tag by removing the "loop" variable creation whenever possible. + * + * @param Twig_NodeInterface $node A Node + * @param Twig_Environment $env The current Twig environment + */ + protected function enterOptimizeFor($node, $env) + { + if ($node instanceof Twig_Node_For) { + // disable the loop variable by default + $node->setAttribute('with_loop', false); + array_unshift($this->loops, $node); + } elseif (!$this->loops) { + // we are outside a loop + return; + } + + // when do we need to add the loop variable back? + + // the loop variable is referenced for the current loop + elseif ($node instanceof Twig_Node_Expression_Name && 'loop' === $node->getAttribute('name')) { + $this->addLoopToCurrent(); + } + + // block reference + elseif ($node instanceof Twig_Node_BlockReference || $node instanceof Twig_Node_Expression_BlockReference) { + $this->addLoopToCurrent(); + } + + // include without the only attribute + elseif ($node instanceof Twig_Node_Include && !$node->getAttribute('only')) { + $this->addLoopToAll(); + } + + // the loop variable is referenced via an attribute + elseif ($node instanceof Twig_Node_Expression_GetAttr + && (!$node->getNode('attribute') instanceof Twig_Node_Expression_Constant + || 'parent' === $node->getNode('attribute')->getAttribute('value') + ) + && (true === $this->loops[0]->getAttribute('with_loop') + || ($node->getNode('node') instanceof Twig_Node_Expression_Name + && 'loop' === $node->getNode('node')->getAttribute('name') + ) + ) + ) { + $this->addLoopToAll(); + } + } + + /** + * Optimizes "for" tag by removing the "loop" variable creation whenever possible. + * + * @param Twig_NodeInterface $node A Node + * @param Twig_Environment $env The current Twig environment + */ + protected function leaveOptimizeFor($node, $env) + { + if ($node instanceof Twig_Node_For) { + array_shift($this->loops); + } + } + + protected function addLoopToCurrent() + { + $this->loops[0]->setAttribute('with_loop', true); + } + + protected function addLoopToAll() + { + foreach ($this->loops as $loop) { + $loop->setAttribute('with_loop', true); + } + } + + /** + * {@inheritdoc} + */ + public function getPriority() + { + return 255; + } +} diff --git a/inc/Twig/NodeVisitor/SafeAnalysis.php b/inc/Twig/NodeVisitor/SafeAnalysis.php new file mode 100644 index 0000000..c4bbd81 --- /dev/null +++ b/inc/Twig/NodeVisitor/SafeAnalysis.php @@ -0,0 +1,131 @@ +safeVars = $safeVars; + } + + public function getSafe(Twig_NodeInterface $node) + { + $hash = spl_object_hash($node); + if (isset($this->data[$hash])) { + foreach ($this->data[$hash] as $bucket) { + if ($bucket['key'] === $node) { + return $bucket['value']; + } + } + } + } + + protected function setSafe(Twig_NodeInterface $node, array $safe) + { + $hash = spl_object_hash($node); + if (isset($this->data[$hash])) { + foreach ($this->data[$hash] as &$bucket) { + if ($bucket['key'] === $node) { + $bucket['value'] = $safe; + + return; + } + } + } + $this->data[$hash][] = array( + 'key' => $node, + 'value' => $safe, + ); + } + + public function enterNode(Twig_NodeInterface $node, Twig_Environment $env) + { + return $node; + } + + public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env) + { + if ($node instanceof Twig_Node_Expression_Constant) { + // constants are marked safe for all + $this->setSafe($node, array('all')); + } elseif ($node instanceof Twig_Node_Expression_BlockReference) { + // blocks are safe by definition + $this->setSafe($node, array('all')); + } elseif ($node instanceof Twig_Node_Expression_Parent) { + // parent block is safe by definition + $this->setSafe($node, array('all')); + } elseif ($node instanceof Twig_Node_Expression_Conditional) { + // intersect safeness of both operands + $safe = $this->intersectSafe($this->getSafe($node->getNode('expr2')), $this->getSafe($node->getNode('expr3'))); + $this->setSafe($node, $safe); + } elseif ($node instanceof Twig_Node_Expression_Filter) { + // filter expression is safe when the filter is safe + $name = $node->getNode('filter')->getAttribute('value'); + $args = $node->getNode('arguments'); + if (false !== $filter = $env->getFilter($name)) { + $safe = $filter->getSafe($args); + if (null === $safe) { + $safe = $this->intersectSafe($this->getSafe($node->getNode('node')), $filter->getPreservesSafety()); + } + $this->setSafe($node, $safe); + } else { + $this->setSafe($node, array()); + } + } elseif ($node instanceof Twig_Node_Expression_Function) { + // function expression is safe when the function is safe + $name = $node->getAttribute('name'); + $args = $node->getNode('arguments'); + $function = $env->getFunction($name); + if (false !== $function) { + $this->setSafe($node, $function->getSafe($args)); + } else { + $this->setSafe($node, array()); + } + } elseif ($node instanceof Twig_Node_Expression_MethodCall) { + if ($node->getAttribute('safe')) { + $this->setSafe($node, array('all')); + } else { + $this->setSafe($node, array()); + } + } elseif ($node instanceof Twig_Node_Expression_GetAttr && $node->getNode('node') instanceof Twig_Node_Expression_Name) { + $name = $node->getNode('node')->getAttribute('name'); + // attributes on template instances are safe + if ('_self' == $name || in_array($name, $this->safeVars)) { + $this->setSafe($node, array('all')); + } else { + $this->setSafe($node, array()); + } + } else { + $this->setSafe($node, array()); + } + + return $node; + } + + protected function intersectSafe(array $a = null, array $b = null) + { + if (null === $a || null === $b) { + return array(); + } + + if (in_array('all', $a)) { + return $b; + } + + if (in_array('all', $b)) { + return $a; + } + + return array_intersect($a, $b); + } + + /** + * {@inheritdoc} + */ + public function getPriority() + { + return 0; + } +} diff --git a/inc/Twig/NodeVisitor/Sandbox.php b/inc/Twig/NodeVisitor/Sandbox.php new file mode 100644 index 0000000..fb27045 --- /dev/null +++ b/inc/Twig/NodeVisitor/Sandbox.php @@ -0,0 +1,92 @@ + + */ +class Twig_NodeVisitor_Sandbox implements Twig_NodeVisitorInterface +{ + protected $inAModule = false; + protected $tags; + protected $filters; + protected $functions; + + /** + * Called before child nodes are visited. + * + * @param Twig_NodeInterface $node The node to visit + * @param Twig_Environment $env The Twig environment instance + * + * @return Twig_NodeInterface The modified node + */ + public function enterNode(Twig_NodeInterface $node, Twig_Environment $env) + { + if ($node instanceof Twig_Node_Module) { + $this->inAModule = true; + $this->tags = array(); + $this->filters = array(); + $this->functions = array(); + + return $node; + } elseif ($this->inAModule) { + // look for tags + if ($node->getNodeTag()) { + $this->tags[] = $node->getNodeTag(); + } + + // look for filters + if ($node instanceof Twig_Node_Expression_Filter) { + $this->filters[] = $node->getNode('filter')->getAttribute('value'); + } + + // look for functions + if ($node instanceof Twig_Node_Expression_Function) { + $this->functions[] = $node->getAttribute('name'); + } + + // wrap print to check __toString() calls + if ($node instanceof Twig_Node_Print) { + return new Twig_Node_SandboxedPrint($node->getNode('expr'), $node->getLine(), $node->getNodeTag()); + } + } + + return $node; + } + + /** + * Called after child nodes are visited. + * + * @param Twig_NodeInterface $node The node to visit + * @param Twig_Environment $env The Twig environment instance + * + * @return Twig_NodeInterface The modified node + */ + public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env) + { + if ($node instanceof Twig_Node_Module) { + $this->inAModule = false; + + return new Twig_Node_SandboxedModule($node, array_unique($this->filters), array_unique($this->tags), array_unique($this->functions)); + } + + return $node; + } + + /** + * {@inheritdoc} + */ + public function getPriority() + { + return 0; + } +} diff --git a/inc/Twig/NodeVisitorInterface.php b/inc/Twig/NodeVisitorInterface.php new file mode 100644 index 0000000..f33c13f --- /dev/null +++ b/inc/Twig/NodeVisitorInterface.php @@ -0,0 +1,47 @@ + + */ +interface Twig_NodeVisitorInterface +{ + /** + * Called before child nodes are visited. + * + * @param Twig_NodeInterface $node The node to visit + * @param Twig_Environment $env The Twig environment instance + * + * @return Twig_NodeInterface The modified node + */ + public function enterNode(Twig_NodeInterface $node, Twig_Environment $env); + + /** + * Called after child nodes are visited. + * + * @param Twig_NodeInterface $node The node to visit + * @param Twig_Environment $env The Twig environment instance + * + * @return Twig_NodeInterface|false The modified node or false if the node must be removed + */ + public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env); + + /** + * Returns the priority for this visitor. + * + * Priority should be between -10 and 10 (0 is the default). + * + * @return integer The priority level + */ + public function getPriority(); +} diff --git a/inc/Twig/Parser.php b/inc/Twig/Parser.php new file mode 100644 index 0000000..958e46b --- /dev/null +++ b/inc/Twig/Parser.php @@ -0,0 +1,394 @@ + + */ +class Twig_Parser implements Twig_ParserInterface +{ + protected $stack = array(); + protected $stream; + protected $parent; + protected $handlers; + protected $visitors; + protected $expressionParser; + protected $blocks; + protected $blockStack; + protected $macros; + protected $env; + protected $reservedMacroNames; + protected $importedSymbols; + protected $traits; + protected $embeddedTemplates = array(); + + /** + * Constructor. + * + * @param Twig_Environment $env A Twig_Environment instance + */ + public function __construct(Twig_Environment $env) + { + $this->env = $env; + } + + public function getEnvironment() + { + return $this->env; + } + + public function getVarName() + { + return sprintf('__internal_%s', hash('sha1', uniqid(mt_rand(), true), false)); + } + + public function getFilename() + { + return $this->stream->getFilename(); + } + + /** + * Converts a token stream to a node tree. + * + * @param Twig_TokenStream $stream A token stream instance + * + * @return Twig_Node_Module A node tree + */ + public function parse(Twig_TokenStream $stream, $test = null, $dropNeedle = false) + { + // push all variables into the stack to keep the current state of the parser + $vars = get_object_vars($this); + unset($vars['stack'], $vars['env'], $vars['handlers'], $vars['visitors'], $vars['expressionParser']); + $this->stack[] = $vars; + + // tag handlers + if (null === $this->handlers) { + $this->handlers = $this->env->getTokenParsers(); + $this->handlers->setParser($this); + } + + // node visitors + if (null === $this->visitors) { + $this->visitors = $this->env->getNodeVisitors(); + } + + if (null === $this->expressionParser) { + $this->expressionParser = new Twig_ExpressionParser($this, $this->env->getUnaryOperators(), $this->env->getBinaryOperators()); + } + + $this->stream = $stream; + $this->parent = null; + $this->blocks = array(); + $this->macros = array(); + $this->traits = array(); + $this->blockStack = array(); + $this->importedSymbols = array(array()); + $this->embeddedTemplates = array(); + + try { + $body = $this->subparse($test, $dropNeedle); + + if (null !== $this->parent) { + if (null === $body = $this->filterBodyNodes($body)) { + $body = new Twig_Node(); + } + } + } catch (Twig_Error_Syntax $e) { + if (!$e->getTemplateFile()) { + $e->setTemplateFile($this->getFilename()); + } + + if (!$e->getTemplateLine()) { + $e->setTemplateLine($this->stream->getCurrent()->getLine()); + } + + throw $e; + } + + $node = new Twig_Node_Module(new Twig_Node_Body(array($body)), $this->parent, new Twig_Node($this->blocks), new Twig_Node($this->macros), new Twig_Node($this->traits), $this->embeddedTemplates, $this->getFilename()); + + $traverser = new Twig_NodeTraverser($this->env, $this->visitors); + + $node = $traverser->traverse($node); + + // restore previous stack so previous parse() call can resume working + foreach (array_pop($this->stack) as $key => $val) { + $this->$key = $val; + } + + return $node; + } + + public function subparse($test, $dropNeedle = false) + { + $lineno = $this->getCurrentToken()->getLine(); + $rv = array(); + while (!$this->stream->isEOF()) { + switch ($this->getCurrentToken()->getType()) { + case Twig_Token::TEXT_TYPE: + $token = $this->stream->next(); + $rv[] = new Twig_Node_Text($token->getValue(), $token->getLine()); + break; + + case Twig_Token::VAR_START_TYPE: + $token = $this->stream->next(); + $expr = $this->expressionParser->parseExpression(); + $this->stream->expect(Twig_Token::VAR_END_TYPE); + $rv[] = new Twig_Node_Print($expr, $token->getLine()); + break; + + case Twig_Token::BLOCK_START_TYPE: + $this->stream->next(); + $token = $this->getCurrentToken(); + + if ($token->getType() !== Twig_Token::NAME_TYPE) { + throw new Twig_Error_Syntax('A block must start with a tag name', $token->getLine(), $this->getFilename()); + } + + if (null !== $test && call_user_func($test, $token)) { + if ($dropNeedle) { + $this->stream->next(); + } + + if (1 === count($rv)) { + return $rv[0]; + } + + return new Twig_Node($rv, array(), $lineno); + } + + $subparser = $this->handlers->getTokenParser($token->getValue()); + if (null === $subparser) { + if (null !== $test) { + $error = sprintf('Unexpected tag name "%s"', $token->getValue()); + if (is_array($test) && isset($test[0]) && $test[0] instanceof Twig_TokenParserInterface) { + $error .= sprintf(' (expecting closing tag for the "%s" tag defined near line %s)', $test[0]->getTag(), $lineno); + } + + throw new Twig_Error_Syntax($error, $token->getLine(), $this->getFilename()); + } + + $message = sprintf('Unknown tag name "%s"', $token->getValue()); + if ($alternatives = $this->env->computeAlternatives($token->getValue(), array_keys($this->env->getTags()))) { + $message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives)); + } + + throw new Twig_Error_Syntax($message, $token->getLine(), $this->getFilename()); + } + + $this->stream->next(); + + $node = $subparser->parse($token); + if (null !== $node) { + $rv[] = $node; + } + break; + + default: + throw new Twig_Error_Syntax('Lexer or parser ended up in unsupported state.', 0, $this->getFilename()); + } + } + + if (1 === count($rv)) { + return $rv[0]; + } + + return new Twig_Node($rv, array(), $lineno); + } + + public function addHandler($name, $class) + { + $this->handlers[$name] = $class; + } + + public function addNodeVisitor(Twig_NodeVisitorInterface $visitor) + { + $this->visitors[] = $visitor; + } + + public function getBlockStack() + { + return $this->blockStack; + } + + public function peekBlockStack() + { + return $this->blockStack[count($this->blockStack) - 1]; + } + + public function popBlockStack() + { + array_pop($this->blockStack); + } + + public function pushBlockStack($name) + { + $this->blockStack[] = $name; + } + + public function hasBlock($name) + { + return isset($this->blocks[$name]); + } + + public function getBlock($name) + { + return $this->blocks[$name]; + } + + public function setBlock($name, $value) + { + $this->blocks[$name] = new Twig_Node_Body(array($value), array(), $value->getLine()); + } + + public function hasMacro($name) + { + return isset($this->macros[$name]); + } + + public function setMacro($name, Twig_Node_Macro $node) + { + if (null === $this->reservedMacroNames) { + $this->reservedMacroNames = array(); + $r = new ReflectionClass($this->env->getBaseTemplateClass()); + foreach ($r->getMethods() as $method) { + $this->reservedMacroNames[] = $method->getName(); + } + } + + if (in_array($name, $this->reservedMacroNames)) { + throw new Twig_Error_Syntax(sprintf('"%s" cannot be used as a macro name as it is a reserved keyword', $name), $node->getLine(), $this->getFilename()); + } + + $this->macros[$name] = $node; + } + + public function addTrait($trait) + { + $this->traits[] = $trait; + } + + public function hasTraits() + { + return count($this->traits) > 0; + } + + public function embedTemplate(Twig_Node_Module $template) + { + $template->setIndex(mt_rand()); + + $this->embeddedTemplates[] = $template; + } + + public function addImportedSymbol($type, $alias, $name = null, Twig_Node_Expression $node = null) + { + $this->importedSymbols[0][$type][$alias] = array('name' => $name, 'node' => $node); + } + + public function getImportedSymbol($type, $alias) + { + foreach ($this->importedSymbols as $functions) { + if (isset($functions[$type][$alias])) { + return $functions[$type][$alias]; + } + } + } + + public function isMainScope() + { + return 1 === count($this->importedSymbols); + } + + public function pushLocalScope() + { + array_unshift($this->importedSymbols, array()); + } + + public function popLocalScope() + { + array_shift($this->importedSymbols); + } + + /** + * Gets the expression parser. + * + * @return Twig_ExpressionParser The expression parser + */ + public function getExpressionParser() + { + return $this->expressionParser; + } + + public function getParent() + { + return $this->parent; + } + + public function setParent($parent) + { + $this->parent = $parent; + } + + /** + * Gets the token stream. + * + * @return Twig_TokenStream The token stream + */ + public function getStream() + { + return $this->stream; + } + + /** + * Gets the current token. + * + * @return Twig_Token The current token + */ + public function getCurrentToken() + { + return $this->stream->getCurrent(); + } + + protected function filterBodyNodes(Twig_NodeInterface $node) + { + // check that the body does not contain non-empty output nodes + if ( + ($node instanceof Twig_Node_Text && !ctype_space($node->getAttribute('data'))) + || + (!$node instanceof Twig_Node_Text && !$node instanceof Twig_Node_BlockReference && $node instanceof Twig_NodeOutputInterface) + ) { + if (false !== strpos((string) $node, chr(0xEF).chr(0xBB).chr(0xBF))) { + throw new Twig_Error_Syntax('A template that extends another one cannot have a body but a byte order mark (BOM) has been detected; it must be removed.', $node->getLine(), $this->getFilename()); + } + + throw new Twig_Error_Syntax('A template that extends another one cannot have a body.', $node->getLine(), $this->getFilename()); + } + + // bypass "set" nodes as they "capture" the output + if ($node instanceof Twig_Node_Set) { + return $node; + } + + if ($node instanceof Twig_NodeOutputInterface) { + return; + } + + foreach ($node as $k => $n) { + if (null !== $n && null === $n = $this->filterBodyNodes($n)) { + $node->removeNode($k); + } + } + + return $node; + } +} diff --git a/inc/Twig/ParserInterface.php b/inc/Twig/ParserInterface.php new file mode 100644 index 0000000..f0d7900 --- /dev/null +++ b/inc/Twig/ParserInterface.php @@ -0,0 +1,28 @@ + + * @deprecated since 1.12 (to be removed in 2.0) + */ +interface Twig_ParserInterface +{ + /** + * Converts a token stream to a node tree. + * + * @param Twig_TokenStream $stream A token stream instance + * + * @return Twig_Node_Module A node tree + */ + public function parse(Twig_TokenStream $stream); +} diff --git a/inc/Twig/Sandbox/SecurityError.php b/inc/Twig/Sandbox/SecurityError.php new file mode 100644 index 0000000..015bfae --- /dev/null +++ b/inc/Twig/Sandbox/SecurityError.php @@ -0,0 +1,19 @@ + + */ +class Twig_Sandbox_SecurityError extends Twig_Error +{ +} diff --git a/inc/Twig/Sandbox/SecurityPolicy.php b/inc/Twig/Sandbox/SecurityPolicy.php new file mode 100644 index 0000000..66ee233 --- /dev/null +++ b/inc/Twig/Sandbox/SecurityPolicy.php @@ -0,0 +1,119 @@ + + */ +class Twig_Sandbox_SecurityPolicy implements Twig_Sandbox_SecurityPolicyInterface +{ + protected $allowedTags; + protected $allowedFilters; + protected $allowedMethods; + protected $allowedProperties; + protected $allowedFunctions; + + public function __construct(array $allowedTags = array(), array $allowedFilters = array(), array $allowedMethods = array(), array $allowedProperties = array(), array $allowedFunctions = array()) + { + $this->allowedTags = $allowedTags; + $this->allowedFilters = $allowedFilters; + $this->setAllowedMethods($allowedMethods); + $this->allowedProperties = $allowedProperties; + $this->allowedFunctions = $allowedFunctions; + } + + public function setAllowedTags(array $tags) + { + $this->allowedTags = $tags; + } + + public function setAllowedFilters(array $filters) + { + $this->allowedFilters = $filters; + } + + public function setAllowedMethods(array $methods) + { + $this->allowedMethods = array(); + foreach ($methods as $class => $m) { + $this->allowedMethods[$class] = array_map('strtolower', is_array($m) ? $m : array($m)); + } + } + + public function setAllowedProperties(array $properties) + { + $this->allowedProperties = $properties; + } + + public function setAllowedFunctions(array $functions) + { + $this->allowedFunctions = $functions; + } + + public function checkSecurity($tags, $filters, $functions) + { + foreach ($tags as $tag) { + if (!in_array($tag, $this->allowedTags)) { + throw new Twig_Sandbox_SecurityError(sprintf('Tag "%s" is not allowed.', $tag)); + } + } + + foreach ($filters as $filter) { + if (!in_array($filter, $this->allowedFilters)) { + throw new Twig_Sandbox_SecurityError(sprintf('Filter "%s" is not allowed.', $filter)); + } + } + + foreach ($functions as $function) { + if (!in_array($function, $this->allowedFunctions)) { + throw new Twig_Sandbox_SecurityError(sprintf('Function "%s" is not allowed.', $function)); + } + } + } + + public function checkMethodAllowed($obj, $method) + { + if ($obj instanceof Twig_TemplateInterface || $obj instanceof Twig_Markup) { + return true; + } + + $allowed = false; + $method = strtolower($method); + foreach ($this->allowedMethods as $class => $methods) { + if ($obj instanceof $class) { + $allowed = in_array($method, $methods); + + break; + } + } + + if (!$allowed) { + throw new Twig_Sandbox_SecurityError(sprintf('Calling "%s" method on a "%s" object is not allowed.', $method, get_class($obj))); + } + } + + public function checkPropertyAllowed($obj, $property) + { + $allowed = false; + foreach ($this->allowedProperties as $class => $properties) { + if ($obj instanceof $class) { + $allowed = in_array($property, is_array($properties) ? $properties : array($properties)); + + break; + } + } + + if (!$allowed) { + throw new Twig_Sandbox_SecurityError(sprintf('Calling "%s" property on a "%s" object is not allowed.', $property, get_class($obj))); + } + } +} diff --git a/inc/Twig/Sandbox/SecurityPolicyInterface.php b/inc/Twig/Sandbox/SecurityPolicyInterface.php new file mode 100644 index 0000000..6ab48e3 --- /dev/null +++ b/inc/Twig/Sandbox/SecurityPolicyInterface.php @@ -0,0 +1,24 @@ + + */ +interface Twig_Sandbox_SecurityPolicyInterface +{ + public function checkSecurity($tags, $filters, $functions); + + public function checkMethodAllowed($obj, $method); + + public function checkPropertyAllowed($obj, $method); +} diff --git a/inc/Twig/SimpleFilter.php b/inc/Twig/SimpleFilter.php new file mode 100644 index 0000000..d35c563 --- /dev/null +++ b/inc/Twig/SimpleFilter.php @@ -0,0 +1,94 @@ + + */ +class Twig_SimpleFilter +{ + protected $name; + protected $callable; + protected $options; + protected $arguments = array(); + + public function __construct($name, $callable, array $options = array()) + { + $this->name = $name; + $this->callable = $callable; + $this->options = array_merge(array( + 'needs_environment' => false, + 'needs_context' => false, + 'is_safe' => null, + 'is_safe_callback' => null, + 'pre_escape' => null, + 'preserves_safety' => null, + 'node_class' => 'Twig_Node_Expression_Filter', + ), $options); + } + + public function getName() + { + return $this->name; + } + + public function getCallable() + { + return $this->callable; + } + + public function getNodeClass() + { + return $this->options['node_class']; + } + + public function setArguments($arguments) + { + $this->arguments = $arguments; + } + + public function getArguments() + { + return $this->arguments; + } + + public function needsEnvironment() + { + return $this->options['needs_environment']; + } + + public function needsContext() + { + return $this->options['needs_context']; + } + + public function getSafe(Twig_Node $filterArgs) + { + if (null !== $this->options['is_safe']) { + return $this->options['is_safe']; + } + + if (null !== $this->options['is_safe_callback']) { + return call_user_func($this->options['is_safe_callback'], $filterArgs); + } + } + + public function getPreservesSafety() + { + return $this->options['preserves_safety']; + } + + public function getPreEscape() + { + return $this->options['pre_escape']; + } +} diff --git a/inc/Twig/SimpleFunction.php b/inc/Twig/SimpleFunction.php new file mode 100644 index 0000000..8ef6aca --- /dev/null +++ b/inc/Twig/SimpleFunction.php @@ -0,0 +1,84 @@ + + */ +class Twig_SimpleFunction +{ + protected $name; + protected $callable; + protected $options; + protected $arguments = array(); + + public function __construct($name, $callable, array $options = array()) + { + $this->name = $name; + $this->callable = $callable; + $this->options = array_merge(array( + 'needs_environment' => false, + 'needs_context' => false, + 'is_safe' => null, + 'is_safe_callback' => null, + 'node_class' => 'Twig_Node_Expression_Function', + ), $options); + } + + public function getName() + { + return $this->name; + } + + public function getCallable() + { + return $this->callable; + } + + public function getNodeClass() + { + return $this->options['node_class']; + } + + public function setArguments($arguments) + { + $this->arguments = $arguments; + } + + public function getArguments() + { + return $this->arguments; + } + + public function needsEnvironment() + { + return $this->options['needs_environment']; + } + + public function needsContext() + { + return $this->options['needs_context']; + } + + public function getSafe(Twig_Node $functionArgs) + { + if (null !== $this->options['is_safe']) { + return $this->options['is_safe']; + } + + if (null !== $this->options['is_safe_callback']) { + return call_user_func($this->options['is_safe_callback'], $functionArgs); + } + + return array(); + } +} diff --git a/inc/Twig/SimpleTest.php b/inc/Twig/SimpleTest.php new file mode 100644 index 0000000..225459c --- /dev/null +++ b/inc/Twig/SimpleTest.php @@ -0,0 +1,46 @@ + + */ +class Twig_SimpleTest +{ + protected $name; + protected $callable; + protected $options; + + public function __construct($name, $callable, array $options = array()) + { + $this->name = $name; + $this->callable = $callable; + $this->options = array_merge(array( + 'node_class' => 'Twig_Node_Expression_Test', + ), $options); + } + + public function getName() + { + return $this->name; + } + + public function getCallable() + { + return $this->callable; + } + + public function getNodeClass() + { + return $this->options['node_class']; + } +} diff --git a/inc/Twig/Template.php b/inc/Twig/Template.php new file mode 100644 index 0000000..a001ca0 --- /dev/null +++ b/inc/Twig/Template.php @@ -0,0 +1,455 @@ + + */ +abstract class Twig_Template implements Twig_TemplateInterface +{ + protected static $cache = array(); + + protected $parent; + protected $parents; + protected $env; + protected $blocks; + protected $traits; + + /** + * Constructor. + * + * @param Twig_Environment $env A Twig_Environment instance + */ + public function __construct(Twig_Environment $env) + { + $this->env = $env; + $this->blocks = array(); + $this->traits = array(); + } + + /** + * Returns the template name. + * + * @return string The template name + */ + abstract public function getTemplateName(); + + /** + * {@inheritdoc} + */ + public function getEnvironment() + { + return $this->env; + } + + /** + * Returns the parent template. + * + * This method is for internal use only and should never be called + * directly. + * + * @return Twig_TemplateInterface|false The parent template or false if there is no parent + */ + public function getParent(array $context) + { + if (null !== $this->parent) { + return $this->parent; + } + + $parent = $this->doGetParent($context); + if (false === $parent) { + return false; + } elseif ($parent instanceof Twig_Template) { + $name = $parent->getTemplateName(); + $this->parents[$name] = $parent; + $parent = $name; + } elseif (!isset($this->parents[$parent])) { + $this->parents[$parent] = $this->env->loadTemplate($parent); + } + + return $this->parents[$parent]; + } + + protected function doGetParent(array $context) + { + return false; + } + + public function isTraitable() + { + return true; + } + + /** + * Displays a parent block. + * + * This method is for internal use only and should never be called + * directly. + * + * @param string $name The block name to display from the parent + * @param array $context The context + * @param array $blocks The current set of blocks + */ + public function displayParentBlock($name, array $context, array $blocks = array()) + { + $name = (string) $name; + + if (isset($this->traits[$name])) { + $this->traits[$name][0]->displayBlock($name, $context, $blocks); + } elseif (false !== $parent = $this->getParent($context)) { + $parent->displayBlock($name, $context, $blocks); + } else { + throw new Twig_Error_Runtime(sprintf('The template has no parent and no traits defining the "%s" block', $name), -1, $this->getTemplateName()); + } + } + + /** + * Displays a block. + * + * This method is for internal use only and should never be called + * directly. + * + * @param string $name The block name to display + * @param array $context The context + * @param array $blocks The current set of blocks + */ + public function displayBlock($name, array $context, array $blocks = array()) + { + $name = (string) $name; + + if (isset($blocks[$name])) { + $b = $blocks; + unset($b[$name]); + call_user_func($blocks[$name], $context, $b); + } elseif (isset($this->blocks[$name])) { + call_user_func($this->blocks[$name], $context, $blocks); + } elseif (false !== $parent = $this->getParent($context)) { + $parent->displayBlock($name, $context, array_merge($this->blocks, $blocks)); + } + } + + /** + * Renders a parent block. + * + * This method is for internal use only and should never be called + * directly. + * + * @param string $name The block name to render from the parent + * @param array $context The context + * @param array $blocks The current set of blocks + * + * @return string The rendered block + */ + public function renderParentBlock($name, array $context, array $blocks = array()) + { + ob_start(); + $this->displayParentBlock($name, $context, $blocks); + + return ob_get_clean(); + } + + /** + * Renders a block. + * + * This method is for internal use only and should never be called + * directly. + * + * @param string $name The block name to render + * @param array $context The context + * @param array $blocks The current set of blocks + * + * @return string The rendered block + */ + public function renderBlock($name, array $context, array $blocks = array()) + { + ob_start(); + $this->displayBlock($name, $context, $blocks); + + return ob_get_clean(); + } + + /** + * Returns whether a block exists or not. + * + * This method is for internal use only and should never be called + * directly. + * + * This method does only return blocks defined in the current template + * or defined in "used" traits. + * + * It does not return blocks from parent templates as the parent + * template name can be dynamic, which is only known based on the + * current context. + * + * @param string $name The block name + * + * @return Boolean true if the block exists, false otherwise + */ + public function hasBlock($name) + { + return isset($this->blocks[(string) $name]); + } + + /** + * Returns all block names. + * + * This method is for internal use only and should never be called + * directly. + * + * @return array An array of block names + * + * @see hasBlock + */ + public function getBlockNames() + { + return array_keys($this->blocks); + } + + /** + * Returns all blocks. + * + * This method is for internal use only and should never be called + * directly. + * + * @return array An array of blocks + * + * @see hasBlock + */ + public function getBlocks() + { + return $this->blocks; + } + + /** + * {@inheritdoc} + */ + public function display(array $context, array $blocks = array()) + { + $this->displayWithErrorHandling($this->env->mergeGlobals($context), $blocks); + } + + /** + * {@inheritdoc} + */ + public function render(array $context) + { + $level = ob_get_level(); + ob_start(); + try { + $this->display($context); + } catch (Exception $e) { + while (ob_get_level() > $level) { + ob_end_clean(); + } + + throw $e; + } + + return ob_get_clean(); + } + + protected function displayWithErrorHandling(array $context, array $blocks = array()) + { + try { + $this->doDisplay($context, $blocks); + } catch (Twig_Error $e) { + if (!$e->getTemplateFile()) { + $e->setTemplateFile($this->getTemplateName()); + } + + // this is mostly useful for Twig_Error_Loader exceptions + // see Twig_Error_Loader + if (false === $e->getTemplateLine()) { + $e->setTemplateLine(-1); + $e->guess(); + } + + throw $e; + } catch (Exception $e) { + throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, null, $e); + } + } + + /** + * Auto-generated method to display the template with the given context. + * + * @param array $context An array of parameters to pass to the template + * @param array $blocks An array of blocks to pass to the template + */ + abstract protected function doDisplay(array $context, array $blocks = array()); + + /** + * Returns a variable from the context. + * + * This method is for internal use only and should never be called + * directly. + * + * This method should not be overridden in a sub-class as this is an + * implementation detail that has been introduced to optimize variable + * access for versions of PHP before 5.4. This is not a way to override + * the way to get a variable value. + * + * @param array $context The context + * @param string $item The variable to return from the context + * @param Boolean $ignoreStrictCheck Whether to ignore the strict variable check or not + * + * @return The content of the context variable + * + * @throws Twig_Error_Runtime if the variable does not exist and Twig is running in strict mode + */ + final protected function getContext($context, $item, $ignoreStrictCheck = false) + { + if (!array_key_exists($item, $context)) { + if ($ignoreStrictCheck || !$this->env->isStrictVariables()) { + return null; + } + + throw new Twig_Error_Runtime(sprintf('Variable "%s" does not exist', $item), -1, $this->getTemplateName()); + } + + return $context[$item]; + } + + /** + * Returns the attribute value for a given array/object. + * + * @param mixed $object The object or array from where to get the item + * @param mixed $item The item to get from the array or object + * @param array $arguments An array of arguments to pass if the item is an object method + * @param string $type The type of attribute (@see Twig_TemplateInterface) + * @param Boolean $isDefinedTest Whether this is only a defined check + * @param Boolean $ignoreStrictCheck Whether to ignore the strict attribute check or not + * + * @return mixed The attribute value, or a Boolean when $isDefinedTest is true, or null when the attribute is not set and $ignoreStrictCheck is true + * + * @throws Twig_Error_Runtime if the attribute does not exist and Twig is running in strict mode and $isDefinedTest is false + */ + protected function getAttribute($object, $item, array $arguments = array(), $type = Twig_TemplateInterface::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false) + { + // array + if (Twig_TemplateInterface::METHOD_CALL !== $type) { + $arrayItem = is_bool($item) || is_float($item) ? (int) $item : $item; + + if ((is_array($object) && array_key_exists($arrayItem, $object)) + || ($object instanceof ArrayAccess && isset($object[$arrayItem])) + ) { + if ($isDefinedTest) { + return true; + } + + return $object[$arrayItem]; + } + + if (Twig_TemplateInterface::ARRAY_CALL === $type || !is_object($object)) { + if ($isDefinedTest) { + return false; + } + + if ($ignoreStrictCheck || !$this->env->isStrictVariables()) { + return null; + } + + if (is_object($object)) { + throw new Twig_Error_Runtime(sprintf('Key "%s" in object (with ArrayAccess) of type "%s" does not exist', $arrayItem, get_class($object)), -1, $this->getTemplateName()); + } elseif (is_array($object)) { + throw new Twig_Error_Runtime(sprintf('Key "%s" for array with keys "%s" does not exist', $arrayItem, implode(', ', array_keys($object))), -1, $this->getTemplateName()); + } elseif (Twig_TemplateInterface::ARRAY_CALL === $type) { + throw new Twig_Error_Runtime(sprintf('Impossible to access a key ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this->getTemplateName()); + } else { + throw new Twig_Error_Runtime(sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this->getTemplateName()); + } + } + } + + if (!is_object($object)) { + if ($isDefinedTest) { + return false; + } + + if ($ignoreStrictCheck || !$this->env->isStrictVariables()) { + return null; + } + + throw new Twig_Error_Runtime(sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this->getTemplateName()); + } + + $class = get_class($object); + + // object property + if (Twig_TemplateInterface::METHOD_CALL !== $type) { + if (isset($object->$item) || array_key_exists((string) $item, $object)) { + if ($isDefinedTest) { + return true; + } + + if ($this->env->hasExtension('sandbox')) { + $this->env->getExtension('sandbox')->checkPropertyAllowed($object, $item); + } + + return $object->$item; + } + } + + // object method + if (!isset(self::$cache[$class]['methods'])) { + self::$cache[$class]['methods'] = array_change_key_case(array_flip(get_class_methods($object))); + } + + $lcItem = strtolower($item); + if (isset(self::$cache[$class]['methods'][$lcItem])) { + $method = (string) $item; + } elseif (isset(self::$cache[$class]['methods']['get'.$lcItem])) { + $method = 'get'.$item; + } elseif (isset(self::$cache[$class]['methods']['is'.$lcItem])) { + $method = 'is'.$item; + } elseif (isset(self::$cache[$class]['methods']['__call'])) { + $method = (string) $item; + } else { + if ($isDefinedTest) { + return false; + } + + if ($ignoreStrictCheck || !$this->env->isStrictVariables()) { + return null; + } + + throw new Twig_Error_Runtime(sprintf('Method "%s" for object "%s" does not exist', $item, get_class($object)), -1, $this->getTemplateName()); + } + + if ($isDefinedTest) { + return true; + } + + if ($this->env->hasExtension('sandbox')) { + $this->env->getExtension('sandbox')->checkMethodAllowed($object, $method); + } + + $ret = call_user_func_array(array($object, $method), $arguments); + + // useful when calling a template method from a template + // this is not supported but unfortunately heavily used in the Symfony profiler + if ($object instanceof Twig_TemplateInterface) { + return $ret === '' ? '' : new Twig_Markup($ret, $this->env->getCharset()); + } + + return $ret; + } + + /** + * This method is only useful when testing Twig. Do not use it. + */ + public static function clearCache() + { + self::$cache = array(); + } +} diff --git a/inc/Twig/TemplateInterface.php b/inc/Twig/TemplateInterface.php new file mode 100644 index 0000000..879f503 --- /dev/null +++ b/inc/Twig/TemplateInterface.php @@ -0,0 +1,47 @@ + + * @deprecated since 1.12 (to be removed in 2.0) + */ +interface Twig_TemplateInterface +{ + const ANY_CALL = 'any'; + const ARRAY_CALL = 'array'; + const METHOD_CALL = 'method'; + + /** + * Renders the template with the given context and returns it as string. + * + * @param array $context An array of parameters to pass to the template + * + * @return string The rendered template + */ + public function render(array $context); + + /** + * Displays the template with the given context. + * + * @param array $context An array of parameters to pass to the template + * @param array $blocks An array of blocks to pass to the template + */ + public function display(array $context, array $blocks = array()); + + /** + * Returns the bound environment for this template. + * + * @return Twig_Environment The current environment + */ + public function getEnvironment(); +} diff --git a/inc/Twig/Test.php b/inc/Twig/Test.php new file mode 100644 index 0000000..3baff88 --- /dev/null +++ b/inc/Twig/Test.php @@ -0,0 +1,34 @@ + + * @deprecated since 1.12 (to be removed in 2.0) + */ +abstract class Twig_Test implements Twig_TestInterface, Twig_TestCallableInterface +{ + protected $options; + protected $arguments = array(); + + public function __construct(array $options = array()) + { + $this->options = array_merge(array( + 'callable' => null, + ), $options); + } + + public function getCallable() + { + return $this->options['callable']; + } +} diff --git a/inc/Twig/Test/Function.php b/inc/Twig/Test/Function.php new file mode 100644 index 0000000..4be6b9b --- /dev/null +++ b/inc/Twig/Test/Function.php @@ -0,0 +1,35 @@ + + * @deprecated since 1.12 (to be removed in 2.0) + */ +class Twig_Test_Function extends Twig_Test +{ + protected $function; + + public function __construct($function, array $options = array()) + { + $options['callable'] = $function; + + parent::__construct($options); + + $this->function = $function; + } + + public function compile() + { + return $this->function; + } +} diff --git a/inc/Twig/Test/IntegrationTestCase.php b/inc/Twig/Test/IntegrationTestCase.php new file mode 100644 index 0000000..724f094 --- /dev/null +++ b/inc/Twig/Test/IntegrationTestCase.php @@ -0,0 +1,154 @@ + + * @author Karma Dordrak + */ +abstract class Twig_Test_IntegrationTestCase extends PHPUnit_Framework_TestCase +{ + abstract protected function getExtensions(); + abstract protected function getFixturesDir(); + + /** + * @dataProvider getTests + */ + public function testIntegration($file, $message, $condition, $templates, $exception, $outputs) + { + $this->doIntegrationTest($file, $message, $condition, $templates, $exception, $outputs); + } + + public function getTests() + { + $fixturesDir = realpath($this->getFixturesDir()); + $tests = array(); + + foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($fixturesDir), RecursiveIteratorIterator::LEAVES_ONLY) as $file) { + if (!preg_match('/\.test$/', $file)) { + continue; + } + + $test = file_get_contents($file->getRealpath()); + + if (preg_match('/ + --TEST--\s*(.*?)\s*(?:--CONDITION--\s*(.*))?\s*((?:--TEMPLATE(?:\(.*?\))?--(?:.*?))+)\s*(?:--DATA--\s*(.*))?\s*--EXCEPTION--\s*(.*)/sx', $test, $match)) { + $message = $match[1]; + $condition = $match[2]; + $templates = $this->parseTemplates($match[3]); + $exception = $match[5]; + $outputs = array(array(null, $match[4], null, '')); + } elseif (preg_match('/--TEST--\s*(.*?)\s*(?:--CONDITION--\s*(.*))?\s*((?:--TEMPLATE(?:\(.*?\))?--(?:.*?))+)--DATA--.*?--EXPECT--.*/s', $test, $match)) { + $message = $match[1]; + $condition = $match[2]; + $templates = $this->parseTemplates($match[3]); + $exception = false; + preg_match_all('/--DATA--(.*?)(?:--CONFIG--(.*?))?--EXPECT--(.*?)(?=\-\-DATA\-\-|$)/s', $test, $outputs, PREG_SET_ORDER); + } else { + throw new InvalidArgumentException(sprintf('Test "%s" is not valid.', str_replace($fixturesDir.'/', '', $file))); + } + + $tests[] = array(str_replace($fixturesDir.'/', '', $file), $message, $condition, $templates, $exception, $outputs); + } + + return $tests; + } + + protected function doIntegrationTest($file, $message, $condition, $templates, $exception, $outputs) + { + if ($condition) { + eval('$ret = '.$condition.';'); + if (!$ret) { + $this->markTestSkipped($condition); + } + } + + $loader = new Twig_Loader_Array($templates); + + foreach ($outputs as $match) { + $config = array_merge(array( + 'cache' => false, + 'strict_variables' => true, + ), $match[2] ? eval($match[2].';') : array()); + $twig = new Twig_Environment($loader, $config); + $twig->addGlobal('global', 'global'); + foreach ($this->getExtensions() as $extension) { + $twig->addExtension($extension); + } + + try { + $template = $twig->loadTemplate('index.twig'); + } catch (Exception $e) { + if (false !== $exception) { + $this->assertEquals(trim($exception), trim(sprintf('%s: %s', get_class($e), $e->getMessage()))); + + return; + } + + if ($e instanceof Twig_Error_Syntax) { + $e->setTemplateFile($file); + + throw $e; + } + + throw new Twig_Error(sprintf('%s: %s', get_class($e), $e->getMessage()), -1, $file, $e); + } + + try { + $output = trim($template->render(eval($match[1].';')), "\n "); + } catch (Exception $e) { + if (false !== $exception) { + $this->assertEquals(trim($exception), trim(sprintf('%s: %s', get_class($e), $e->getMessage()))); + + return; + } + + if ($e instanceof Twig_Error_Syntax) { + $e->setTemplateFile($file); + } else { + $e = new Twig_Error(sprintf('%s: %s', get_class($e), $e->getMessage()), -1, $file, $e); + } + + $output = trim(sprintf('%s: %s', get_class($e), $e->getMessage())); + } + + if (false !== $exception) { + list($class, ) = explode(':', $exception); + $this->assertThat(NULL, new PHPUnit_Framework_Constraint_Exception($class)); + } + + $expected = trim($match[3], "\n "); + + if ($expected != $output) { + echo 'Compiled template that failed:'; + + foreach (array_keys($templates) as $name) { + echo "Template: $name\n"; + $source = $loader->getSource($name); + echo $twig->compile($twig->parse($twig->tokenize($source, $name))); + } + } + $this->assertEquals($expected, $output, $message.' (in '.$file.')'); + } + } + + protected static function parseTemplates($test) + { + $templates = array(); + preg_match_all('/--TEMPLATE(?:\((.*?)\))?--(.*?)(?=\-\-TEMPLATE|$)/s', $test, $matches, PREG_SET_ORDER); + foreach ($matches as $match) { + $templates[($match[1] ? $match[1] : 'index.twig')] = $match[2]; + } + + return $templates; + } +} diff --git a/inc/Twig/Test/Method.php b/inc/Twig/Test/Method.php new file mode 100644 index 0000000..17c6c04 --- /dev/null +++ b/inc/Twig/Test/Method.php @@ -0,0 +1,37 @@ + + * @deprecated since 1.12 (to be removed in 2.0) + */ +class Twig_Test_Method extends Twig_Test +{ + protected $extension; + protected $method; + + public function __construct(Twig_ExtensionInterface $extension, $method, array $options = array()) + { + $options['callable'] = array($extension, $method); + + parent::__construct($options); + + $this->extension = $extension; + $this->method = $method; + } + + public function compile() + { + return sprintf('$this->env->getExtension(\'%s\')->%s', $this->extension->getName(), $this->method); + } +} diff --git a/inc/Twig/Test/Node.php b/inc/Twig/Test/Node.php new file mode 100644 index 0000000..c832a57 --- /dev/null +++ b/inc/Twig/Test/Node.php @@ -0,0 +1,37 @@ + + * @deprecated since 1.12 (to be removed in 2.0) + */ +class Twig_Test_Node extends Twig_Test +{ + protected $class; + + public function __construct($class, array $options = array()) + { + parent::__construct($options); + + $this->class = $class; + } + + public function getClass() + { + return $this->class; + } + + public function compile() + { + } +} diff --git a/inc/Twig/Test/NodeTestCase.php b/inc/Twig/Test/NodeTestCase.php new file mode 100644 index 0000000..b15c85f --- /dev/null +++ b/inc/Twig/Test/NodeTestCase.php @@ -0,0 +1,58 @@ +assertNodeCompilation($source, $node, $environment); + } + + public function assertNodeCompilation($source, Twig_Node $node, Twig_Environment $environment = null) + { + $compiler = $this->getCompiler($environment); + $compiler->compile($node); + + $this->assertEquals($source, trim($compiler->getSource())); + } + + protected function getCompiler(Twig_Environment $environment = null) + { + return new Twig_Compiler(null === $environment ? $this->getEnvironment() : $environment); + } + + protected function getEnvironment() + { + return new Twig_Environment(); + } + + protected function getVariableGetter($name) + { + if (version_compare(phpversion(), '5.4.0RC1', '>=')) { + return sprintf('(isset($context["%s"]) ? $context["%s"] : null)', $name, $name); + } + + return sprintf('$this->getContext($context, "%s")', $name); + } + + protected function getAttributeGetter() + { + if (function_exists('twig_template_get_attributes')) { + return 'twig_template_get_attributes($this, '; + } + + return '$this->getAttribute('; + } +} diff --git a/inc/Twig/TestCallableInterface.php b/inc/Twig/TestCallableInterface.php new file mode 100644 index 0000000..0db4368 --- /dev/null +++ b/inc/Twig/TestCallableInterface.php @@ -0,0 +1,21 @@ + + * @deprecated since 1.12 (to be removed in 2.0) + */ +interface Twig_TestCallableInterface +{ + public function getCallable(); +} diff --git a/inc/Twig/TestInterface.php b/inc/Twig/TestInterface.php new file mode 100644 index 0000000..30d8a2c --- /dev/null +++ b/inc/Twig/TestInterface.php @@ -0,0 +1,26 @@ + + * @deprecated since 1.12 (to be removed in 2.0) + */ +interface Twig_TestInterface +{ + /** + * Compiles a test. + * + * @return string The PHP code for the test + */ + public function compile(); +} diff --git a/inc/Twig/Token.php b/inc/Twig/Token.php new file mode 100644 index 0000000..bbca90d --- /dev/null +++ b/inc/Twig/Token.php @@ -0,0 +1,218 @@ + + */ +class Twig_Token +{ + protected $value; + protected $type; + protected $lineno; + + const EOF_TYPE = -1; + const TEXT_TYPE = 0; + const BLOCK_START_TYPE = 1; + const VAR_START_TYPE = 2; + const BLOCK_END_TYPE = 3; + const VAR_END_TYPE = 4; + const NAME_TYPE = 5; + const NUMBER_TYPE = 6; + const STRING_TYPE = 7; + const OPERATOR_TYPE = 8; + const PUNCTUATION_TYPE = 9; + const INTERPOLATION_START_TYPE = 10; + const INTERPOLATION_END_TYPE = 11; + + /** + * Constructor. + * + * @param integer $type The type of the token + * @param string $value The token value + * @param integer $lineno The line position in the source + */ + public function __construct($type, $value, $lineno) + { + $this->type = $type; + $this->value = $value; + $this->lineno = $lineno; + } + + /** + * Returns a string representation of the token. + * + * @return string A string representation of the token + */ + public function __toString() + { + return sprintf('%s(%s)', self::typeToString($this->type, true, $this->lineno), $this->value); + } + + /** + * Tests the current token for a type and/or a value. + * + * Parameters may be: + * * just type + * * type and value (or array of possible values) + * * just value (or array of possible values) (NAME_TYPE is used as type) + * + * @param array|integer $type The type to test + * @param array|string|null $values The token value + * + * @return Boolean + */ + public function test($type, $values = null) + { + if (null === $values && !is_int($type)) { + $values = $type; + $type = self::NAME_TYPE; + } + + return ($this->type === $type) && ( + null === $values || + (is_array($values) && in_array($this->value, $values)) || + $this->value == $values + ); + } + + /** + * Gets the line. + * + * @return integer The source line + */ + public function getLine() + { + return $this->lineno; + } + + /** + * Gets the token type. + * + * @return integer The token type + */ + public function getType() + { + return $this->type; + } + + /** + * Gets the token value. + * + * @return string The token value + */ + public function getValue() + { + return $this->value; + } + + /** + * Returns the constant representation (internal) of a given type. + * + * @param integer $type The type as an integer + * @param Boolean $short Whether to return a short representation or not + * @param integer $line The code line + * + * @return string The string representation + */ + public static function typeToString($type, $short = false, $line = -1) + { + switch ($type) { + case self::EOF_TYPE: + $name = 'EOF_TYPE'; + break; + case self::TEXT_TYPE: + $name = 'TEXT_TYPE'; + break; + case self::BLOCK_START_TYPE: + $name = 'BLOCK_START_TYPE'; + break; + case self::VAR_START_TYPE: + $name = 'VAR_START_TYPE'; + break; + case self::BLOCK_END_TYPE: + $name = 'BLOCK_END_TYPE'; + break; + case self::VAR_END_TYPE: + $name = 'VAR_END_TYPE'; + break; + case self::NAME_TYPE: + $name = 'NAME_TYPE'; + break; + case self::NUMBER_TYPE: + $name = 'NUMBER_TYPE'; + break; + case self::STRING_TYPE: + $name = 'STRING_TYPE'; + break; + case self::OPERATOR_TYPE: + $name = 'OPERATOR_TYPE'; + break; + case self::PUNCTUATION_TYPE: + $name = 'PUNCTUATION_TYPE'; + break; + case self::INTERPOLATION_START_TYPE: + $name = 'INTERPOLATION_START_TYPE'; + break; + case self::INTERPOLATION_END_TYPE: + $name = 'INTERPOLATION_END_TYPE'; + break; + default: + throw new LogicException(sprintf('Token of type "%s" does not exist.', $type)); + } + + return $short ? $name : 'Twig_Token::'.$name; + } + + /** + * Returns the english representation of a given type. + * + * @param integer $type The type as an integer + * @param integer $line The code line + * + * @return string The string representation + */ + public static function typeToEnglish($type, $line = -1) + { + switch ($type) { + case self::EOF_TYPE: + return 'end of template'; + case self::TEXT_TYPE: + return 'text'; + case self::BLOCK_START_TYPE: + return 'begin of statement block'; + case self::VAR_START_TYPE: + return 'begin of print statement'; + case self::BLOCK_END_TYPE: + return 'end of statement block'; + case self::VAR_END_TYPE: + return 'end of print statement'; + case self::NAME_TYPE: + return 'name'; + case self::NUMBER_TYPE: + return 'number'; + case self::STRING_TYPE: + return 'string'; + case self::OPERATOR_TYPE: + return 'operator'; + case self::PUNCTUATION_TYPE: + return 'punctuation'; + case self::INTERPOLATION_START_TYPE: + return 'begin of string interpolation'; + case self::INTERPOLATION_END_TYPE: + return 'end of string interpolation'; + default: + throw new LogicException(sprintf('Token of type "%s" does not exist.', $type)); + } + } +} diff --git a/inc/Twig/TokenParser.php b/inc/Twig/TokenParser.php new file mode 100644 index 0000000..decebd5 --- /dev/null +++ b/inc/Twig/TokenParser.php @@ -0,0 +1,33 @@ + + */ +abstract class Twig_TokenParser implements Twig_TokenParserInterface +{ + /** + * @var Twig_Parser + */ + protected $parser; + + /** + * Sets the parser associated with this token parser + * + * @param $parser A Twig_Parser instance + */ + public function setParser(Twig_Parser $parser) + { + $this->parser = $parser; + } +} diff --git a/inc/Twig/TokenParser/AutoEscape.php b/inc/Twig/TokenParser/AutoEscape.php new file mode 100644 index 0000000..2756028 --- /dev/null +++ b/inc/Twig/TokenParser/AutoEscape.php @@ -0,0 +1,89 @@ + + * {% autoescape true %} + * Everything will be automatically escaped in this block + * {% endautoescape %} + * + * {% autoescape false %} + * Everything will be outputed as is in this block + * {% endautoescape %} + * + * {% autoescape true js %} + * Everything will be automatically escaped in this block + * using the js escaping strategy + * {% endautoescape %} + * + */ +class Twig_TokenParser_AutoEscape extends Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(Twig_Token $token) + { + $lineno = $token->getLine(); + $stream = $this->parser->getStream(); + + if ($stream->test(Twig_Token::BLOCK_END_TYPE)) { + $value = 'html'; + } else { + $expr = $this->parser->getExpressionParser()->parseExpression(); + if (!$expr instanceof Twig_Node_Expression_Constant) { + throw new Twig_Error_Syntax('An escaping strategy must be a string or a Boolean.', $stream->getCurrent()->getLine(), $stream->getFilename()); + } + $value = $expr->getAttribute('value'); + + $compat = true === $value || false === $value; + + if (true === $value) { + $value = 'html'; + } + + if ($compat && $stream->test(Twig_Token::NAME_TYPE)) { + if (false === $value) { + throw new Twig_Error_Syntax('Unexpected escaping strategy as you set autoescaping to false.', $stream->getCurrent()->getLine(), $stream->getFilename()); + } + + $value = $stream->next()->getValue(); + } + } + + $stream->expect(Twig_Token::BLOCK_END_TYPE); + $body = $this->parser->subparse(array($this, 'decideBlockEnd'), true); + $stream->expect(Twig_Token::BLOCK_END_TYPE); + + return new Twig_Node_AutoEscape($value, $body, $lineno, $this->getTag()); + } + + public function decideBlockEnd(Twig_Token $token) + { + return $token->test('endautoescape'); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'autoescape'; + } +} diff --git a/inc/Twig/TokenParser/Block.php b/inc/Twig/TokenParser/Block.php new file mode 100644 index 0000000..a2e017f --- /dev/null +++ b/inc/Twig/TokenParser/Block.php @@ -0,0 +1,83 @@ + + * {% block head %} + * + * {% block title %}{% endblock %} - My Webpage + * {% endblock %} + * + */ +class Twig_TokenParser_Block extends Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(Twig_Token $token) + { + $lineno = $token->getLine(); + $stream = $this->parser->getStream(); + $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue(); + if ($this->parser->hasBlock($name)) { + throw new Twig_Error_Syntax(sprintf("The block '$name' has already been defined line %d", $this->parser->getBlock($name)->getLine()), $stream->getCurrent()->getLine(), $stream->getFilename()); + } + $this->parser->setBlock($name, $block = new Twig_Node_Block($name, new Twig_Node(array()), $lineno)); + $this->parser->pushLocalScope(); + $this->parser->pushBlockStack($name); + + if ($stream->test(Twig_Token::BLOCK_END_TYPE)) { + $stream->next(); + + $body = $this->parser->subparse(array($this, 'decideBlockEnd'), true); + if ($stream->test(Twig_Token::NAME_TYPE)) { + $value = $stream->next()->getValue(); + + if ($value != $name) { + throw new Twig_Error_Syntax(sprintf("Expected endblock for block '$name' (but %s given)", $value), $stream->getCurrent()->getLine(), $stream->getFilename()); + } + } + } else { + $body = new Twig_Node(array( + new Twig_Node_Print($this->parser->getExpressionParser()->parseExpression(), $lineno), + )); + } + $stream->expect(Twig_Token::BLOCK_END_TYPE); + + $block->setNode('body', $body); + $this->parser->popBlockStack(); + $this->parser->popLocalScope(); + + return new Twig_Node_BlockReference($name, $lineno, $this->getTag()); + } + + public function decideBlockEnd(Twig_Token $token) + { + return $token->test('endblock'); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'block'; + } +} diff --git a/inc/Twig/TokenParser/Do.php b/inc/Twig/TokenParser/Do.php new file mode 100644 index 0000000..f50939d --- /dev/null +++ b/inc/Twig/TokenParser/Do.php @@ -0,0 +1,42 @@ +parser->getExpressionParser()->parseExpression(); + + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + + return new Twig_Node_Do($expr, $token->getLine(), $this->getTag()); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'do'; + } +} diff --git a/inc/Twig/TokenParser/Embed.php b/inc/Twig/TokenParser/Embed.php new file mode 100644 index 0000000..69cb5f3 --- /dev/null +++ b/inc/Twig/TokenParser/Embed.php @@ -0,0 +1,66 @@ +parser->getStream(); + + $parent = $this->parser->getExpressionParser()->parseExpression(); + + list($variables, $only, $ignoreMissing) = $this->parseArguments(); + + // inject a fake parent to make the parent() function work + $stream->injectTokens(array( + new Twig_Token(Twig_Token::BLOCK_START_TYPE, '', $token->getLine()), + new Twig_Token(Twig_Token::NAME_TYPE, 'extends', $token->getLine()), + new Twig_Token(Twig_Token::STRING_TYPE, '__parent__', $token->getLine()), + new Twig_Token(Twig_Token::BLOCK_END_TYPE, '', $token->getLine()), + )); + + $module = $this->parser->parse($stream, array($this, 'decideBlockEnd'), true); + + // override the parent with the correct one + $module->setNode('parent', $parent); + + $this->parser->embedTemplate($module); + + $stream->expect(Twig_Token::BLOCK_END_TYPE); + + return new Twig_Node_Embed($module->getAttribute('filename'), $module->getAttribute('index'), $variables, $only, $ignoreMissing, $token->getLine(), $this->getTag()); + } + + public function decideBlockEnd(Twig_Token $token) + { + return $token->test('endembed'); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'embed'; + } +} diff --git a/inc/Twig/TokenParser/Extends.php b/inc/Twig/TokenParser/Extends.php new file mode 100644 index 0000000..f5ecee2 --- /dev/null +++ b/inc/Twig/TokenParser/Extends.php @@ -0,0 +1,52 @@ + + * {% extends "base.html" %} + * + */ +class Twig_TokenParser_Extends extends Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(Twig_Token $token) + { + if (!$this->parser->isMainScope()) { + throw new Twig_Error_Syntax('Cannot extend from a block', $token->getLine(), $this->parser->getFilename()); + } + + if (null !== $this->parser->getParent()) { + throw new Twig_Error_Syntax('Multiple extends tags are forbidden', $token->getLine(), $this->parser->getFilename()); + } + $this->parser->setParent($this->parser->getExpressionParser()->parseExpression()); + + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'extends'; + } +} diff --git a/inc/Twig/TokenParser/Filter.php b/inc/Twig/TokenParser/Filter.php new file mode 100644 index 0000000..2b97475 --- /dev/null +++ b/inc/Twig/TokenParser/Filter.php @@ -0,0 +1,61 @@ + + * {% filter upper %} + * This text becomes uppercase + * {% endfilter %} + * + */ +class Twig_TokenParser_Filter extends Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(Twig_Token $token) + { + $name = $this->parser->getVarName(); + $ref = new Twig_Node_Expression_BlockReference(new Twig_Node_Expression_Constant($name, $token->getLine()), true, $token->getLine(), $this->getTag()); + + $filter = $this->parser->getExpressionParser()->parseFilterExpressionRaw($ref, $this->getTag()); + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + + $body = $this->parser->subparse(array($this, 'decideBlockEnd'), true); + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + + $block = new Twig_Node_Block($name, $body, $token->getLine()); + $this->parser->setBlock($name, $block); + + return new Twig_Node_Print($filter, $token->getLine(), $this->getTag()); + } + + public function decideBlockEnd(Twig_Token $token) + { + return $token->test('endfilter'); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'filter'; + } +} diff --git a/inc/Twig/TokenParser/Flush.php b/inc/Twig/TokenParser/Flush.php new file mode 100644 index 0000000..4e15e78 --- /dev/null +++ b/inc/Twig/TokenParser/Flush.php @@ -0,0 +1,42 @@ +parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + + return new Twig_Node_Flush($token->getLine(), $this->getTag()); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'flush'; + } +} diff --git a/inc/Twig/TokenParser/For.php b/inc/Twig/TokenParser/For.php new file mode 100644 index 0000000..98a6d07 --- /dev/null +++ b/inc/Twig/TokenParser/For.php @@ -0,0 +1,136 @@ + + *
    + * {% for user in users %} + *
  • {{ user.username|e }}
  • + * {% endfor %} + *
+ * + */ +class Twig_TokenParser_For extends Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(Twig_Token $token) + { + $lineno = $token->getLine(); + $stream = $this->parser->getStream(); + $targets = $this->parser->getExpressionParser()->parseAssignmentExpression(); + $stream->expect(Twig_Token::OPERATOR_TYPE, 'in'); + $seq = $this->parser->getExpressionParser()->parseExpression(); + + $ifexpr = null; + if ($stream->test(Twig_Token::NAME_TYPE, 'if')) { + $stream->next(); + $ifexpr = $this->parser->getExpressionParser()->parseExpression(); + } + + $stream->expect(Twig_Token::BLOCK_END_TYPE); + $body = $this->parser->subparse(array($this, 'decideForFork')); + if ($stream->next()->getValue() == 'else') { + $stream->expect(Twig_Token::BLOCK_END_TYPE); + $else = $this->parser->subparse(array($this, 'decideForEnd'), true); + } else { + $else = null; + } + $stream->expect(Twig_Token::BLOCK_END_TYPE); + + if (count($targets) > 1) { + $keyTarget = $targets->getNode(0); + $keyTarget = new Twig_Node_Expression_AssignName($keyTarget->getAttribute('name'), $keyTarget->getLine()); + $valueTarget = $targets->getNode(1); + $valueTarget = new Twig_Node_Expression_AssignName($valueTarget->getAttribute('name'), $valueTarget->getLine()); + } else { + $keyTarget = new Twig_Node_Expression_AssignName('_key', $lineno); + $valueTarget = $targets->getNode(0); + $valueTarget = new Twig_Node_Expression_AssignName($valueTarget->getAttribute('name'), $valueTarget->getLine()); + } + + if ($ifexpr) { + $this->checkLoopUsageCondition($stream, $ifexpr); + $this->checkLoopUsageBody($stream, $body); + } + + return new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, $lineno, $this->getTag()); + } + + public function decideForFork(Twig_Token $token) + { + return $token->test(array('else', 'endfor')); + } + + public function decideForEnd(Twig_Token $token) + { + return $token->test('endfor'); + } + + // the loop variable cannot be used in the condition + protected function checkLoopUsageCondition(Twig_TokenStream $stream, Twig_NodeInterface $node) + { + if ($node instanceof Twig_Node_Expression_GetAttr && $node->getNode('node') instanceof Twig_Node_Expression_Name && 'loop' == $node->getNode('node')->getAttribute('name')) { + throw new Twig_Error_Syntax('The "loop" variable cannot be used in a looping condition', $node->getLine(), $stream->getFilename()); + } + + foreach ($node as $n) { + if (!$n) { + continue; + } + + $this->checkLoopUsageCondition($stream, $n); + } + } + + // check usage of non-defined loop-items + // it does not catch all problems (for instance when a for is included into another or when the variable is used in an include) + protected function checkLoopUsageBody(Twig_TokenStream $stream, Twig_NodeInterface $node) + { + if ($node instanceof Twig_Node_Expression_GetAttr && $node->getNode('node') instanceof Twig_Node_Expression_Name && 'loop' == $node->getNode('node')->getAttribute('name')) { + $attribute = $node->getNode('attribute'); + if ($attribute instanceof Twig_Node_Expression_Constant && in_array($attribute->getAttribute('value'), array('length', 'revindex0', 'revindex', 'last'))) { + throw new Twig_Error_Syntax(sprintf('The "loop.%s" variable is not defined when looping with a condition', $attribute->getAttribute('value')), $node->getLine(), $stream->getFilename()); + } + } + + // should check for parent.loop.XXX usage + if ($node instanceof Twig_Node_For) { + return; + } + + foreach ($node as $n) { + if (!$n) { + continue; + } + + $this->checkLoopUsageBody($stream, $n); + } + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'for'; + } +} diff --git a/inc/Twig/TokenParser/From.php b/inc/Twig/TokenParser/From.php new file mode 100644 index 0000000..a54054d --- /dev/null +++ b/inc/Twig/TokenParser/From.php @@ -0,0 +1,74 @@ + + * {% from 'forms.html' import forms %} + * + */ +class Twig_TokenParser_From extends Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(Twig_Token $token) + { + $macro = $this->parser->getExpressionParser()->parseExpression(); + $stream = $this->parser->getStream(); + $stream->expect('import'); + + $targets = array(); + do { + $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue(); + + $alias = $name; + if ($stream->test('as')) { + $stream->next(); + + $alias = $stream->expect(Twig_Token::NAME_TYPE)->getValue(); + } + + $targets[$name] = $alias; + + if (!$stream->test(Twig_Token::PUNCTUATION_TYPE, ',')) { + break; + } + + $stream->next(); + } while (true); + + $stream->expect(Twig_Token::BLOCK_END_TYPE); + + $node = new Twig_Node_Import($macro, new Twig_Node_Expression_AssignName($this->parser->getVarName(), $token->getLine()), $token->getLine(), $this->getTag()); + + foreach ($targets as $name => $alias) { + $this->parser->addImportedSymbol('function', $alias, 'get'.$name, $node->getNode('var')); + } + + return $node; + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'from'; + } +} diff --git a/inc/Twig/TokenParser/If.php b/inc/Twig/TokenParser/If.php new file mode 100644 index 0000000..3d7d1f5 --- /dev/null +++ b/inc/Twig/TokenParser/If.php @@ -0,0 +1,94 @@ + + * {% if users %} + *
    + * {% for user in users %} + *
  • {{ user.username|e }}
  • + * {% endfor %} + *
+ * {% endif %} + * + */ +class Twig_TokenParser_If extends Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(Twig_Token $token) + { + $lineno = $token->getLine(); + $expr = $this->parser->getExpressionParser()->parseExpression(); + $stream = $this->parser->getStream(); + $stream->expect(Twig_Token::BLOCK_END_TYPE); + $body = $this->parser->subparse(array($this, 'decideIfFork')); + $tests = array($expr, $body); + $else = null; + + $end = false; + while (!$end) { + switch ($stream->next()->getValue()) { + case 'else': + $stream->expect(Twig_Token::BLOCK_END_TYPE); + $else = $this->parser->subparse(array($this, 'decideIfEnd')); + break; + + case 'elseif': + $expr = $this->parser->getExpressionParser()->parseExpression(); + $stream->expect(Twig_Token::BLOCK_END_TYPE); + $body = $this->parser->subparse(array($this, 'decideIfFork')); + $tests[] = $expr; + $tests[] = $body; + break; + + case 'endif': + $end = true; + break; + + default: + throw new Twig_Error_Syntax(sprintf('Unexpected end of template. Twig was looking for the following tags "else", "elseif", or "endif" to close the "if" block started at line %d)', $lineno), $stream->getCurrent()->getLine(), $stream->getFilename()); + } + } + + $stream->expect(Twig_Token::BLOCK_END_TYPE); + + return new Twig_Node_If(new Twig_Node($tests), $else, $lineno, $this->getTag()); + } + + public function decideIfFork(Twig_Token $token) + { + return $token->test(array('elseif', 'else', 'endif')); + } + + public function decideIfEnd(Twig_Token $token) + { + return $token->test(array('endif')); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'if'; + } +} diff --git a/inc/Twig/TokenParser/Import.php b/inc/Twig/TokenParser/Import.php new file mode 100644 index 0000000..e7050c7 --- /dev/null +++ b/inc/Twig/TokenParser/Import.php @@ -0,0 +1,49 @@ + + * {% import 'forms.html' as forms %} + * + */ +class Twig_TokenParser_Import extends Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(Twig_Token $token) + { + $macro = $this->parser->getExpressionParser()->parseExpression(); + $this->parser->getStream()->expect('as'); + $var = new Twig_Node_Expression_AssignName($this->parser->getStream()->expect(Twig_Token::NAME_TYPE)->getValue(), $token->getLine()); + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + + $this->parser->addImportedSymbol('template', $var->getAttribute('name')); + + return new Twig_Node_Import($macro, $var, $token->getLine(), $this->getTag()); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'import'; + } +} diff --git a/inc/Twig/TokenParser/Include.php b/inc/Twig/TokenParser/Include.php new file mode 100644 index 0000000..4a31786 --- /dev/null +++ b/inc/Twig/TokenParser/Include.php @@ -0,0 +1,80 @@ + + * {% include 'header.html' %} + * Body + * {% include 'footer.html' %} + * + */ +class Twig_TokenParser_Include extends Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(Twig_Token $token) + { + $expr = $this->parser->getExpressionParser()->parseExpression(); + + list($variables, $only, $ignoreMissing) = $this->parseArguments(); + + return new Twig_Node_Include($expr, $variables, $only, $ignoreMissing, $token->getLine(), $this->getTag()); + } + + protected function parseArguments() + { + $stream = $this->parser->getStream(); + + $ignoreMissing = false; + if ($stream->test(Twig_Token::NAME_TYPE, 'ignore')) { + $stream->next(); + $stream->expect(Twig_Token::NAME_TYPE, 'missing'); + + $ignoreMissing = true; + } + + $variables = null; + if ($stream->test(Twig_Token::NAME_TYPE, 'with')) { + $stream->next(); + + $variables = $this->parser->getExpressionParser()->parseExpression(); + } + + $only = false; + if ($stream->test(Twig_Token::NAME_TYPE, 'only')) { + $stream->next(); + + $only = true; + } + + $stream->expect(Twig_Token::BLOCK_END_TYPE); + + return array($variables, $only, $ignoreMissing); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'include'; + } +} diff --git a/inc/Twig/TokenParser/Macro.php b/inc/Twig/TokenParser/Macro.php new file mode 100644 index 0000000..82b4fa6 --- /dev/null +++ b/inc/Twig/TokenParser/Macro.php @@ -0,0 +1,68 @@ + + * {% macro input(name, value, type, size) %} + * + * {% endmacro %} + * + */ +class Twig_TokenParser_Macro extends Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(Twig_Token $token) + { + $lineno = $token->getLine(); + $stream = $this->parser->getStream(); + $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue(); + + $arguments = $this->parser->getExpressionParser()->parseArguments(true, true); + + $stream->expect(Twig_Token::BLOCK_END_TYPE); + $this->parser->pushLocalScope(); + $body = $this->parser->subparse(array($this, 'decideBlockEnd'), true); + if ($stream->test(Twig_Token::NAME_TYPE)) { + $value = $stream->next()->getValue(); + + if ($value != $name) { + throw new Twig_Error_Syntax(sprintf("Expected endmacro for macro '$name' (but %s given)", $value), $stream->getCurrent()->getLine(), $stream->getFilename()); + } + } + $this->parser->popLocalScope(); + $stream->expect(Twig_Token::BLOCK_END_TYPE); + + $this->parser->setMacro($name, new Twig_Node_Macro($name, new Twig_Node_Body(array($body)), $arguments, $lineno, $this->getTag())); + } + + public function decideBlockEnd(Twig_Token $token) + { + return $token->test('endmacro'); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'macro'; + } +} diff --git a/inc/Twig/TokenParser/Sandbox.php b/inc/Twig/TokenParser/Sandbox.php new file mode 100644 index 0000000..9457325 --- /dev/null +++ b/inc/Twig/TokenParser/Sandbox.php @@ -0,0 +1,68 @@ + + * {% sandbox %} + * {% include 'user.html' %} + * {% endsandbox %} + * + * + * @see http://www.twig-project.org/doc/api.html#sandbox-extension for details + */ +class Twig_TokenParser_Sandbox extends Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(Twig_Token $token) + { + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + $body = $this->parser->subparse(array($this, 'decideBlockEnd'), true); + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + + // in a sandbox tag, only include tags are allowed + if (!$body instanceof Twig_Node_Include) { + foreach ($body as $node) { + if ($node instanceof Twig_Node_Text && ctype_space($node->getAttribute('data'))) { + continue; + } + + if (!$node instanceof Twig_Node_Include) { + throw new Twig_Error_Syntax('Only "include" tags are allowed within a "sandbox" section', $node->getLine(), $this->parser->getFilename()); + } + } + } + + return new Twig_Node_Sandbox($body, $token->getLine(), $this->getTag()); + } + + public function decideBlockEnd(Twig_Token $token) + { + return $token->test('endsandbox'); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'sandbox'; + } +} diff --git a/inc/Twig/TokenParser/Set.php b/inc/Twig/TokenParser/Set.php new file mode 100644 index 0000000..70e0b41 --- /dev/null +++ b/inc/Twig/TokenParser/Set.php @@ -0,0 +1,84 @@ + + * {% set foo = 'foo' %} + * + * {% set foo = [1, 2] %} + * + * {% set foo = {'foo': 'bar'} %} + * + * {% set foo = 'foo' ~ 'bar' %} + * + * {% set foo, bar = 'foo', 'bar' %} + * + * {% set foo %}Some content{% endset %} + * + */ +class Twig_TokenParser_Set extends Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(Twig_Token $token) + { + $lineno = $token->getLine(); + $stream = $this->parser->getStream(); + $names = $this->parser->getExpressionParser()->parseAssignmentExpression(); + + $capture = false; + if ($stream->test(Twig_Token::OPERATOR_TYPE, '=')) { + $stream->next(); + $values = $this->parser->getExpressionParser()->parseMultitargetExpression(); + + $stream->expect(Twig_Token::BLOCK_END_TYPE); + + if (count($names) !== count($values)) { + throw new Twig_Error_Syntax("When using set, you must have the same number of variables and assignments.", $stream->getCurrent()->getLine(), $stream->getFilename()); + } + } else { + $capture = true; + + if (count($names) > 1) { + throw new Twig_Error_Syntax("When using set with a block, you cannot have a multi-target.", $stream->getCurrent()->getLine(), $stream->getFilename()); + } + + $stream->expect(Twig_Token::BLOCK_END_TYPE); + + $values = $this->parser->subparse(array($this, 'decideBlockEnd'), true); + $stream->expect(Twig_Token::BLOCK_END_TYPE); + } + + return new Twig_Node_Set($capture, $names, $values, $lineno, $this->getTag()); + } + + public function decideBlockEnd(Twig_Token $token) + { + return $token->test('endset'); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'set'; + } +} diff --git a/inc/Twig/TokenParser/Spaceless.php b/inc/Twig/TokenParser/Spaceless.php new file mode 100644 index 0000000..1e3fa8f --- /dev/null +++ b/inc/Twig/TokenParser/Spaceless.php @@ -0,0 +1,59 @@ + + * {% spaceless %} + *
+ * foo + *
+ * {% endspaceless %} + * + * {# output will be
foo
#} + * + */ +class Twig_TokenParser_Spaceless extends Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(Twig_Token $token) + { + $lineno = $token->getLine(); + + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + $body = $this->parser->subparse(array($this, 'decideSpacelessEnd'), true); + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + + return new Twig_Node_Spaceless($body, $lineno, $this->getTag()); + } + + public function decideSpacelessEnd(Twig_Token $token) + { + return $token->test('endspaceless'); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'spaceless'; + } +} diff --git a/inc/Twig/TokenParser/Use.php b/inc/Twig/TokenParser/Use.php new file mode 100644 index 0000000..bc0e09e --- /dev/null +++ b/inc/Twig/TokenParser/Use.php @@ -0,0 +1,82 @@ + + * {% extends "base.html" %} + * + * {% use "blocks.html" %} + * + * {% block title %}{% endblock %} + * {% block content %}{% endblock %} + * + * + * @see http://www.twig-project.org/doc/templates.html#horizontal-reuse for details. + */ +class Twig_TokenParser_Use extends Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(Twig_Token $token) + { + $template = $this->parser->getExpressionParser()->parseExpression(); + $stream = $this->parser->getStream(); + + if (!$template instanceof Twig_Node_Expression_Constant) { + throw new Twig_Error_Syntax('The template references in a "use" statement must be a string.', $stream->getCurrent()->getLine(), $stream->getFilename()); + } + + $targets = array(); + if ($stream->test('with')) { + $stream->next(); + + do { + $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue(); + + $alias = $name; + if ($stream->test('as')) { + $stream->next(); + + $alias = $stream->expect(Twig_Token::NAME_TYPE)->getValue(); + } + + $targets[$name] = new Twig_Node_Expression_Constant($alias, -1); + + if (!$stream->test(Twig_Token::PUNCTUATION_TYPE, ',')) { + break; + } + + $stream->next(); + } while (true); + } + + $stream->expect(Twig_Token::BLOCK_END_TYPE); + + $this->parser->addTrait(new Twig_Node(array('template' => $template, 'targets' => new Twig_Node($targets)))); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'use'; + } +} diff --git a/inc/Twig/TokenParserBroker.php b/inc/Twig/TokenParserBroker.php new file mode 100644 index 0000000..ec3fba6 --- /dev/null +++ b/inc/Twig/TokenParserBroker.php @@ -0,0 +1,136 @@ + + * @deprecated since 1.12 (to be removed in 2.0) + */ +class Twig_TokenParserBroker implements Twig_TokenParserBrokerInterface +{ + protected $parser; + protected $parsers = array(); + protected $brokers = array(); + + /** + * Constructor. + * + * @param array|Traversable $parsers A Traversable of Twig_TokenParserInterface instances + * @param array|Traversable $brokers A Traversable of Twig_TokenParserBrokerInterface instances + */ + public function __construct($parsers = array(), $brokers = array()) + { + foreach ($parsers as $parser) { + if (!$parser instanceof Twig_TokenParserInterface) { + throw new LogicException('$parsers must a an array of Twig_TokenParserInterface'); + } + $this->parsers[$parser->getTag()] = $parser; + } + foreach ($brokers as $broker) { + if (!$broker instanceof Twig_TokenParserBrokerInterface) { + throw new LogicException('$brokers must a an array of Twig_TokenParserBrokerInterface'); + } + $this->brokers[] = $broker; + } + } + + /** + * Adds a TokenParser. + * + * @param Twig_TokenParserInterface $parser A Twig_TokenParserInterface instance + */ + public function addTokenParser(Twig_TokenParserInterface $parser) + { + $this->parsers[$parser->getTag()] = $parser; + } + + /** + * Removes a TokenParser. + * + * @param Twig_TokenParserInterface $parser A Twig_TokenParserInterface instance + */ + public function removeTokenParser(Twig_TokenParserInterface $parser) + { + $name = $parser->getTag(); + if (isset($this->parsers[$name]) && $parser === $this->parsers[$name]) { + unset($this->parsers[$name]); + } + } + + /** + * Adds a TokenParserBroker. + * + * @param Twig_TokenParserBroker $broker A Twig_TokenParserBroker instance + */ + public function addTokenParserBroker(Twig_TokenParserBroker $broker) + { + $this->brokers[] = $broker; + } + + /** + * Removes a TokenParserBroker. + * + * @param Twig_TokenParserBroker $broker A Twig_TokenParserBroker instance + */ + public function removeTokenParserBroker(Twig_TokenParserBroker $broker) + { + if (false !== $pos = array_search($broker, $this->brokers)) { + unset($this->brokers[$pos]); + } + } + + /** + * Gets a suitable TokenParser for a tag. + * + * First looks in parsers, then in brokers. + * + * @param string $tag A tag name + * + * @return null|Twig_TokenParserInterface A Twig_TokenParserInterface or null if no suitable TokenParser was found + */ + public function getTokenParser($tag) + { + if (isset($this->parsers[$tag])) { + return $this->parsers[$tag]; + } + $broker = end($this->brokers); + while (false !== $broker) { + $parser = $broker->getTokenParser($tag); + if (null !== $parser) { + return $parser; + } + $broker = prev($this->brokers); + } + } + + public function getParsers() + { + return $this->parsers; + } + + public function getParser() + { + return $this->parser; + } + + public function setParser(Twig_ParserInterface $parser) + { + $this->parser = $parser; + foreach ($this->parsers as $tokenParser) { + $tokenParser->setParser($parser); + } + foreach ($this->brokers as $broker) { + $broker->setParser($parser); + } + } +} diff --git a/inc/Twig/TokenParserBrokerInterface.php b/inc/Twig/TokenParserBrokerInterface.php new file mode 100644 index 0000000..3f006e3 --- /dev/null +++ b/inc/Twig/TokenParserBrokerInterface.php @@ -0,0 +1,45 @@ + + * @deprecated since 1.12 (to be removed in 2.0) + */ +interface Twig_TokenParserBrokerInterface +{ + /** + * Gets a TokenParser suitable for a tag. + * + * @param string $tag A tag name + * + * @return null|Twig_TokenParserInterface A Twig_TokenParserInterface or null if no suitable TokenParser was found + */ + public function getTokenParser($tag); + + /** + * Calls Twig_TokenParserInterface::setParser on all parsers the implementation knows of. + * + * @param Twig_ParserInterface $parser A Twig_ParserInterface interface + */ + public function setParser(Twig_ParserInterface $parser); + + /** + * Gets the Twig_ParserInterface. + * + * @return null|Twig_ParserInterface A Twig_ParserInterface instance or null + */ + public function getParser(); +} diff --git a/inc/Twig/TokenParserInterface.php b/inc/Twig/TokenParserInterface.php new file mode 100644 index 0000000..bbde771 --- /dev/null +++ b/inc/Twig/TokenParserInterface.php @@ -0,0 +1,41 @@ + + */ +interface Twig_TokenParserInterface +{ + /** + * Sets the parser associated with this token parser + * + * @param $parser A Twig_Parser instance + */ + public function setParser(Twig_Parser $parser); + + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(Twig_Token $token); + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag(); +} diff --git a/inc/Twig/TokenStream.php b/inc/Twig/TokenStream.php new file mode 100644 index 0000000..a78189f --- /dev/null +++ b/inc/Twig/TokenStream.php @@ -0,0 +1,144 @@ + + */ +class Twig_TokenStream +{ + protected $tokens; + protected $current; + protected $filename; + + /** + * Constructor. + * + * @param array $tokens An array of tokens + * @param string $filename The name of the filename which tokens are associated with + */ + public function __construct(array $tokens, $filename = null) + { + $this->tokens = $tokens; + $this->current = 0; + $this->filename = $filename; + } + + /** + * Returns a string representation of the token stream. + * + * @return string + */ + public function __toString() + { + return implode("\n", $this->tokens); + } + + public function injectTokens(array $tokens) + { + $this->tokens = array_merge(array_slice($this->tokens, 0, $this->current), $tokens, array_slice($this->tokens, $this->current)); + } + + /** + * Sets the pointer to the next token and returns the old one. + * + * @return Twig_Token + */ + public function next() + { + if (!isset($this->tokens[++$this->current])) { + throw new Twig_Error_Syntax('Unexpected end of template', $this->tokens[$this->current - 1]->getLine(), $this->filename); + } + + return $this->tokens[$this->current - 1]; + } + + /** + * Tests a token and returns it or throws a syntax error. + * + * @return Twig_Token + */ + public function expect($type, $value = null, $message = null) + { + $token = $this->tokens[$this->current]; + if (!$token->test($type, $value)) { + $line = $token->getLine(); + throw new Twig_Error_Syntax(sprintf('%sUnexpected token "%s" of value "%s" ("%s" expected%s)', + $message ? $message.'. ' : '', + Twig_Token::typeToEnglish($token->getType(), $line), $token->getValue(), + Twig_Token::typeToEnglish($type, $line), $value ? sprintf(' with value "%s"', $value) : ''), + $line, + $this->filename + ); + } + $this->next(); + + return $token; + } + + /** + * Looks at the next token. + * + * @param integer $number + * + * @return Twig_Token + */ + public function look($number = 1) + { + if (!isset($this->tokens[$this->current + $number])) { + throw new Twig_Error_Syntax('Unexpected end of template', $this->tokens[$this->current + $number - 1]->getLine(), $this->filename); + } + + return $this->tokens[$this->current + $number]; + } + + /** + * Tests the current token + * + * @return bool + */ + public function test($primary, $secondary = null) + { + return $this->tokens[$this->current]->test($primary, $secondary); + } + + /** + * Checks if end of stream was reached + * + * @return bool + */ + public function isEOF() + { + return $this->tokens[$this->current]->getType() === Twig_Token::EOF_TYPE; + } + + /** + * Gets the current token + * + * @return Twig_Token + */ + public function getCurrent() + { + return $this->tokens[$this->current]; + } + + /** + * Gets the filename associated with this stream + * + * @return string + */ + public function getFilename() + { + return $this->filename; + } +} diff --git a/inc/config.php b/inc/config.php index 6e3f80b..7470f0c 100644 --- a/inc/config.php +++ b/inc/config.php @@ -9,65 +9,69 @@ */ define ('POCHE_VERSION', '0.3'); - -if (!is_dir('db/')) { - @mkdir('db/',0705); -} - define ('MODE_DEMO', FALSE); -define ('ABS_PATH', 'assets/'); -define ('CONVERT_LINKS_FOOTNOTES', TRUE); -define ('REVERT_FORCED_PARAGRAPH_ELEMENTS',FALSE); -define ('DOWNLOAD_PICTURES', TRUE); +define ('CONVERT_LINKS_FOOTNOTES', FALSE); +define ('REVERT_FORCED_PARAGRAPH_ELEMENTS', FALSE); +define ('DOWNLOAD_PICTURES', FALSE); define ('SALT', '464v54gLLw928uz4zUBqkRJeiPY68zCX'); +define ('ABS_PATH', 'assets/'); +define ('TPL', './tpl'); +define ('LOCALE', './locale'); +define ('CACHE', './cache'); define ('LANG', 'fr_FR.UTF8'); -putenv("LC_ALL=".LANG); -setlocale(LC_ALL, LANG); -bindtextdomain(LANG, "./locale"); -textdomain(LANG); - $storage_type = 'sqlite'; # sqlite or file -include 'functions.php'; +# /!\ Be careful if you change the lines below /!\ + +require_once 'pocheCore.php'; require_once 'Readability.php'; require_once 'Encoding.php'; -require_once 'rain.tpl.class.php'; -require_once 'MyTool.class.php'; +require_once 'pocheTool.class.php'; require_once 'Session.class.php'; +require_once 'Twig/Autoloader.php'; require_once 'store/store.class.php'; -require_once 'store/sqlite.class.php'; -require_once 'store/file.class.php'; -require_once 'class.messages.php'; +require_once 'store/' . $storage_type . '.class.php'; + +if (DOWNLOAD_PICTURES) { + require_once 'pochePicture.php'; +} + +# i18n +putenv('LC_ALL=' . LANG); +setlocale(LC_ALL, LANG); +bindtextdomain(LANG, LOCALE); +textdomain(LANG); + +# template engine +Twig_Autoloader::register(); +$loader = new Twig_Loader_Filesystem(TPL); +$twig = new Twig_Environment($loader, array( + 'cache' => CACHE, +)); +$twig->addExtension(new Twig_Extensions_Extension_I18n()); Session::init(); +$store = new $storage_type(); -$store = new $storage_type(); -# initialisation de RainTPL -raintpl::$tpl_dir = './tpl/'; -raintpl::$cache_dir = './cache/'; -raintpl::$base_url = get_poche_url(); -raintpl::configure('path_replace', false); -raintpl::configure('debug', false); -$tpl = new raintpl(); - +# installation if(!$store->isInstalled()) { - logm('poche still not installed'); - $tpl->draw('install'); + pocheTool::logm('poche still not installed'); + echo $twig->render('install.twig', array( + 'token' => Session::getToken(), + )); if (isset($_GET['install'])) { if (($_POST['password'] == $_POST['password_repeat']) && $_POST['password'] != "" && $_POST['login'] != "") { + # let's rock, install poche baby ! $store->install($_POST['login'], encode_string($_POST['password'] . $_POST['login'])); Session::logout(); - MyTool::redirect(); + pocheTool::redirect(); } } exit(); } $_SESSION['login'] = (isset ($_SESSION['login'])) ? $_SESSION['login'] : $store->getLogin(); -$_SESSION['pass'] = (isset ($_SESSION['pass'])) ? $_SESSION['pass'] : $store->getPassword(); - -$msg = new Messages(); -$tpl->assign('msg', $msg); \ No newline at end of file +$_SESSION['pass'] = (isset ($_SESSION['pass'])) ? $_SESSION['pass'] : $store->getPassword(); \ No newline at end of file diff --git a/inc/functions.php b/inc/functions.php deleted file mode 100644 index ee26fba..0000000 --- a/inc/functions.php +++ /dev/null @@ -1,400 +0,0 @@ - - * @copyright 2013 - * @license http://www.wtfpl.net/ see COPYING file - */ - -/** - * Permet de générer l'URL de poche pour le bookmarklet - */ -function get_poche_url() -{ - $protocol = "http"; - if(isset($_SERVER['HTTPS'])) { - if($_SERVER['HTTPS'] != "off" && $_SERVER['HTTPS'] != "") { - $protocol = "https"; - } - } - - return $protocol . "://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; -} - -function encode_string($string) -{ - return sha1($string . SALT); -} - -// function define to retrieve url content -function get_external_file($url) -{ - $timeout = 15; - // spoofing FireFox 18.0 - $useragent="Mozilla/5.0 (Windows NT 5.1; rv:18.0) Gecko/20100101 Firefox/18.0"; - - if (in_array ('curl', get_loaded_extensions())) { - // Fetch feed from URL - $curl = curl_init(); - curl_setopt($curl, CURLOPT_URL, $url); - curl_setopt($curl, CURLOPT_TIMEOUT, $timeout); - curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - curl_setopt($curl, CURLOPT_HEADER, false); - - // FOR SSL do not verified certificate - curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); - curl_setopt($curl, CURLOPT_AUTOREFERER, TRUE ); - - // FeedBurner requires a proper USER-AGENT... - curl_setopt($curl, CURL_HTTP_VERSION_1_1, true); - curl_setopt($curl, CURLOPT_ENCODING, "gzip, deflate"); - curl_setopt($curl, CURLOPT_USERAGENT, $useragent); - - $data = curl_exec($curl); - - $httpcode = curl_getinfo($curl, CURLINFO_HTTP_CODE); - - $httpcodeOK = isset($httpcode) and ($httpcode == 200 or $httpcode == 301); - - curl_close($curl); - } else { - - // create http context and add timeout and user-agent - $context = stream_context_create(array( - 'http'=>array('timeout' => $timeout, - 'header'=> "User-Agent: ".$useragent, /*spoot Mozilla Firefox*/ - 'follow_location' => true), - // FOR SSL do not verified certificate - 'ssl' => array('verify_peer' => false, - 'allow_self_signed' => true) - ) - ); - - // only download page lesser than 4MB - $data = @file_get_contents($url, false, $context, -1, 4000000); // We download at most 4 MB from source. - - if(isset($http_response_header) and isset($http_response_header[0])) { - $httpcodeOK = isset($http_response_header) and isset($http_response_header[0]) and ((strpos($http_response_header[0], '200 OK') !== FALSE) or (strpos($http_response_header[0], '301 Moved Permanently') !== FALSE)); - } - } - - // if response is not empty and response is OK - if (isset($data) and isset($httpcodeOK) and $httpcodeOK ) { - - // take charset of page and get it - preg_match('##Usi', $data, $meta); - - // if meta tag is found - if (!empty($meta[0])) { - // retrieve encoding in $enc - preg_match('#charset="?(.*)"#si', $meta[0], $enc); - - // if charset is found set it otherwise, set it to utf-8 - $html_charset = (!empty($enc[1])) ? strtolower($enc[1]) : 'utf-8'; - - } else { - $html_charset = 'utf-8'; - $enc[1] = ''; - } - - // replace charset of url to charset of page - $data = str_replace('charset='.$enc[1], 'charset='.$html_charset, $data); - - return $data; - } - else { - return FALSE; - } -} - -/** - * Préparation de l'URL avec récupération du contenu avant insertion en base - */ -function prepare_url($url) -{ - $parametres = array(); - $url = html_entity_decode(trim($url)); - - // We remove the annoying parameters added by FeedBurner and GoogleFeedProxy (?utm_source=...) - // from shaarli, by sebsauvage - $i=strpos($url,'&utm_source='); if ($i!==false) $url=substr($url,0,$i); - $i=strpos($url,'?utm_source='); if ($i!==false) $url=substr($url,0,$i); - $i=strpos($url,'#xtor=RSS-'); if ($i!==false) $url=substr($url,0,$i); - - $title = $url; - $html = Encoding::toUTF8(get_external_file($url,15)); - // If get_external_file if not able to retrieve HTTPS content try the same URL with HTTP protocol - if (!preg_match('!^https?://!i', $url) && (!isset($html) || strlen($html) <= 0)) { - $url = 'http://' . $url; - $html = Encoding::toUTF8(get_external_file($url,15)); - } - - if (function_exists('tidy_parse_string')) { - $tidy = tidy_parse_string($html, array(), 'UTF8'); - $tidy->cleanRepair(); - $html = $tidy->value; - } - - if (isset($html) and strlen($html) > 0) - { - $r = new Readability($html, $url); - - $r->convertLinksToFootnotes = CONVERT_LINKS_FOOTNOTES; - $r->revertForcedParagraphElements = REVERT_FORCED_PARAGRAPH_ELEMENTS; - - if($r->init()) - { - $content = $r->articleContent->innerHTML; - $parametres['title'] = $r->articleTitle->innerHTML; - $parametres['content'] = $content; - return $parametres; - } - } - - return FALSE; -} - -/** - * On modifie les URLS des images dans le corps de l'article - */ -function filtre_picture($content, $url, $id) -{ - $matches = array(); - preg_match_all('#<\s*(img)[^>]+src="([^"]*)"[^>]*>#Si', $content, $matches, PREG_SET_ORDER); - foreach($matches as $i => $link) - { - $link[1] = trim($link[1]); - if (!preg_match('#^(([a-z]+://)|(\#))#', $link[1]) ) - { - $absolute_path = get_absolute_link($link[2],$url); - $filename = basename(parse_url($absolute_path, PHP_URL_PATH)); - $directory = create_assets_directory($id); - $fullpath = $directory . '/' . $filename; - download_pictures($absolute_path, $fullpath); - $content = str_replace($matches[$i][2], $fullpath, $content); - } - - } - - return $content; -} - -/** - * Retourne le lien absolu - */ -function get_absolute_link($relative_link, $url) -{ - /* return if already absolute URL */ - if (parse_url($relative_link, PHP_URL_SCHEME) != '') return $relative_link; - - /* queries and anchors */ - if ($relative_link[0]=='#' || $relative_link[0]=='?') return $url . $relative_link; - - /* parse base URL and convert to local variables: - $scheme, $host, $path */ - extract(parse_url($url)); - - /* remove non-directory element from path */ - $path = preg_replace('#/[^/]*$#', '', $path); - - /* destroy path if relative url points to root */ - if ($relative_link[0] == '/') $path = ''; - - /* dirty absolute URL */ - $abs = $host . $path . '/' . $relative_link; - - /* replace '//' or '/./' or '/foo/../' with '/' */ - $re = array('#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#'); - for($n=1; $n>0; $abs=preg_replace($re, '/', $abs, -1, $n)) {} - - /* absolute URL is ready! */ - return $scheme.'://'.$abs; -} - -/** - * Téléchargement des images - */ - -function download_pictures($absolute_path, $fullpath) -{ - $rawdata = get_external_file($absolute_path); - - if(file_exists($fullpath)) { - unlink($fullpath); - } - $fp = fopen($fullpath, 'x'); - fwrite($fp, $rawdata); - fclose($fp); -} - -/** - * Crée un répertoire de médias pour l'article - */ -function create_assets_directory($id) -{ - $assets_path = ABS_PATH; - if(!is_dir($assets_path)) { - mkdir($assets_path, 0705); - } - - $article_directory = $assets_path . $id; - if(!is_dir($article_directory)) { - mkdir($article_directory, 0705); - } - - return $article_directory; -} - -/** - * Suppression du répertoire d'images - */ -function remove_directory($directory) -{ - if(is_dir($directory)) { - $files = array_diff(scandir($directory), array('.','..')); - foreach ($files as $file) { - (is_dir("$directory/$file")) ? remove_directory("$directory/$file") : unlink("$directory/$file"); - } - return rmdir($directory); - } -} - -function display_view($view, $id = 0, $full_head = 'yes') -{ - global $tpl, $store, $msg; - - switch ($view) - { - case 'export': - $entries = $store->retrieveAll(); - $tpl->assign('export', myTool::renderJson($entries)); - $tpl->draw('export'); - logm('export view'); - break; - case 'config': - $tpl->assign('load_all_js', 0); - $tpl->draw('head'); - $tpl->draw('home'); - $tpl->draw('config'); - $tpl->draw('js'); - $tpl->draw('footer'); - logm('config view'); - break; - case 'view': - $entry = $store->retrieveOneById($id); - - if ($entry != NULL) { - $tpl->assign('id', $entry['id']); - $tpl->assign('url', $entry['url']); - $tpl->assign('title', $entry['title']); - $content = $entry['content']; - if (function_exists('tidy_parse_string')) { - $tidy = tidy_parse_string($content, array('indent'=>true, 'show-body-only' => true), 'UTF8'); - $tidy->cleanRepair(); - $content = $tidy->value; - } - $tpl->assign('content', $content); - $tpl->assign('is_fav', $entry['is_fav']); - $tpl->assign('is_read', $entry['is_read']); - $tpl->assign('load_all_js', 0); - $tpl->draw('view'); - } - else { - logm('error in view call : entry is NULL'); - } - - logm('view link #' . $id); - break; - default: # home view - $entries = $store->getEntriesByView($view); - - $tpl->assign('entries', $entries); - - if ($full_head == 'yes') { - $tpl->assign('load_all_js', 1); - $tpl->draw('head'); - $tpl->draw('home'); - } - - $tpl->draw('entries'); - - if ($full_head == 'yes') { - $tpl->draw('js'); - $tpl->draw('footer'); - } - break; - } -} - -/** - * Appel d'une action (mark as fav, archive, delete) - */ -function action_to_do($action, $url, $id = 0) -{ - global $store, $msg; - - switch ($action) - { - case 'add': - if ($url == '') - continue; - - $url = base64_decode($url); - error_log(print_r($url, TRUE)); - if (MyTool::isUrl($url)) { - if($parametres_url = prepare_url($url)) { - if ($store->add($url, $parametres_url['title'], $parametres_url['content'])) { - $last_id = $store->getLastId(); - if (DOWNLOAD_PICTURES) { - $content = filtre_picture($parametres_url['content'], $url, $last_id); - } - $msg->add('s', _('the link has been added successfully')); - } - else { - $msg->add('e', _('error during insertion : the link wasn\'t added')); - } - } - else { - $msg->add('e', _('error during url preparation : the link wasn\'t added')); - logm('error during url preparation'); - } - } - else { - $msg->add('e', _('error during url preparation : the link is not valid')); - logm($url . ' is not a valid url'); - } - - logm('add link ' . $url); - break; - case 'delete': - if ($store->deleteById($id)) { - remove_directory(ABS_PATH . $id); - $msg->add('s', _('the link has been deleted successfully')); - logm('delete link #' . $id); - } - else { - $msg->add('e', _('the link wasn\'t deleted')); - logm('error : can\'t delete link #' . $id); - } - break; - case 'toggle_fav' : - $store->favoriteById($id); - logm('mark as favorite link #' . $id); - break; - case 'toggle_archive' : - $store->archiveById($id); - logm('archive link #' . $id); - break; - default: - break; - } -} - -function logm($message) -{ - $t = strval(date('Y/m/d_H:i:s')).' - '.$_SERVER["REMOTE_ADDR"].' - '.strval($message)."\n"; - file_put_contents('./log.txt',$t,FILE_APPEND); -} diff --git a/inc/poche/pocheCore.php b/inc/poche/pocheCore.php new file mode 100644 index 0000000..52c603a --- /dev/null +++ b/inc/poche/pocheCore.php @@ -0,0 +1,257 @@ + + * @copyright 2013 + * @license http://www.wtfpl.net/ see COPYING file + */ + +function encode_string($string) +{ + return sha1($string . SALT); +} + +function get_external_file($url) +{ + $timeout = 15; + $useragent = "Mozilla/5.0 (Windows NT 5.1; rv:18.0) Gecko/20100101 Firefox/18.0"; + + if (in_array ('curl', get_loaded_extensions())) { + # Fetch feed from URL + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $url); + curl_setopt($curl, CURLOPT_TIMEOUT, $timeout); + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl, CURLOPT_HEADER, false); + + # for ssl, do not verified certificate + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); + curl_setopt($curl, CURLOPT_AUTOREFERER, TRUE ); + + # FeedBurner requires a proper USER-AGENT... + curl_setopt($curl, CURL_HTTP_VERSION_1_1, true); + curl_setopt($curl, CURLOPT_ENCODING, "gzip, deflate"); + curl_setopt($curl, CURLOPT_USERAGENT, $useragent); + + $data = curl_exec($curl); + $httpcode = curl_getinfo($curl, CURLINFO_HTTP_CODE); + $httpcodeOK = isset($httpcode) and ($httpcode == 200 or $httpcode == 301); + curl_close($curl); + } else { + # create http context and add timeout and user-agent + $context = stream_context_create( + array( + 'http' => array( + 'timeout' => $timeout, + 'header' => "User-Agent: " . $useragent, + 'follow_location' => true + ), + 'ssl' => array( + 'verify_peer' => false, + 'allow_self_signed' => true + ) + ) + ); + + # only download page lesser than 4MB + $data = @file_get_contents($url, false, $context, -1, 4000000); + + if (isset($http_response_header) and isset($http_response_header[0])) { + $httpcodeOK = isset($http_response_header) and isset($http_response_header[0]) and ((strpos($http_response_header[0], '200 OK') !== FALSE) or (strpos($http_response_header[0], '301 Moved Permanently') !== FALSE)); + } + } + + # if response is not empty and response is OK + if (isset($data) and isset($httpcodeOK) and $httpcodeOK) { + + # take charset of page and get it + preg_match('##Usi', $data, $meta); + + # if meta tag is found + if (!empty($meta[0])) { + preg_match('#charset="?(.*)"#si', $meta[0], $encoding); + # if charset is found set it otherwise, set it to utf-8 + $html_charset = (!empty($encoding[1])) ? strtolower($encoding[1]) : 'utf-8'; + } else { + $html_charset = 'utf-8'; + $encoding[1] = ''; + } + + # replace charset of url to charset of page + $data = str_replace('charset=' . $encoding[1], 'charset=' . $html_charset, $data); + + return $data; + } + else { + return FALSE; + } +} + +function fetch_url_content($url) +{ + $url = base64_decode($url); + if (pocheTool::isUrl($url)) { + $url = pocheTool::cleanURL($url); + $html = Encoding::toUTF8(get_external_file($url)); + + # if get_external_file if not able to retrieve HTTPS content, try the same URL with HTTP protocol + if (!preg_match('!^https?://!i', $url) && (!isset($html) || strlen($html) <= 0)) { + $url = 'http://' . $url; + $html = Encoding::toUTF8(get_external_file($url)); + } + + if (function_exists('tidy_parse_string')) { + $tidy = tidy_parse_string($html, array(), 'UTF8'); + $tidy->cleanRepair(); + $html = $tidy->value; + } + + $parameters = array(); + if (isset($html) and strlen($html) > 0) + { + $readability = new Readability($html, $url); + $readability->convertLinksToFootnotes = CONVERT_LINKS_FOOTNOTES; + $readability->revertForcedParagraphElements = REVERT_FORCED_PARAGRAPH_ELEMENTS; + + if($readability->init()) + { + $content = $readability->articleContent->innerHTML; + $parameters['title'] = $readability->articleTitle->innerHTML; + $parameters['content'] = $content; + + return $parameters; + } + } + } + else { + #$msg->add('e', _('error during url preparation : the link is not valid')); + pocheTool::logm($url . ' is not a valid url'); + } + + return FALSE; +} + +function display_view($view, $id = 0, $full_head = 'yes') +{ + global $tpl, $store, $msg; + + switch ($view) + { + case 'export': + $entries = $store->retrieveAll(); + $tpl->assign('export', pocheTool::renderJson($entries)); + $tpl->draw('export'); + pocheTool::logm('export view'); + break; + case 'config': + $tpl->assign('load_all_js', 0); + $tpl->draw('head'); + $tpl->draw('home'); + $tpl->draw('config'); + $tpl->draw('js'); + $tpl->draw('footer'); + pocheTool::logm('config view'); + break; + case 'view': + $entry = $store->retrieveOneById($id); + + if ($entry != NULL) { + $tpl->assign('id', $entry['id']); + $tpl->assign('url', $entry['url']); + $tpl->assign('title', $entry['title']); + $content = $entry['content']; + if (function_exists('tidy_parse_string')) { + $tidy = tidy_parse_string($content, array('indent'=>true, 'show-body-only' => true), 'UTF8'); + $tidy->cleanRepair(); + $content = $tidy->value; + } + $tpl->assign('content', $content); + $tpl->assign('is_fav', $entry['is_fav']); + $tpl->assign('is_read', $entry['is_read']); + $tpl->assign('load_all_js', 0); + $tpl->draw('view'); + } + else { + pocheTool::logm('error in view call : entry is NULL'); + } + + pocheTool::logm('view link #' . $id); + break; + default: # home view + $entries = $store->getEntriesByView($view); + + $tpl->assign('entries', $entries); + + if ($full_head == 'yes') { + $tpl->assign('load_all_js', 1); + $tpl->draw('head'); + $tpl->draw('home'); + } + + $tpl->draw('entries'); + + if ($full_head == 'yes') { + $tpl->draw('js'); + $tpl->draw('footer'); + } + break; + } +} + +/** + * Appel d'une action (mark as fav, archive, delete) + */ +function action_to_do($action, $url, $id = 0) +{ + global $store, $msg; + + switch ($action) + { + case 'add': + if($parametres_url = fetch_url_content($url)) { + if ($store->add($url, $parametres_url['title'], $parametres_url['content'])) { + pocheTool::logm('add link ' . $url); + $last_id = $store->getLastId(); + if (DOWNLOAD_PICTURES) { + $content = filtre_picture($parametres_url['content'], $url, $last_id); + } + #$msg->add('s', _('the link has been added successfully')); + } + else { + #$msg->add('e', _('error during insertion : the link wasn\'t added')); + pocheTool::logm('error during insertion : the link wasn\'t added'); + } + } + else { + #$msg->add('e', _('error during url preparation : the link wasn\'t added')); + pocheTool::logm('error during content fetch'); + } + break; + case 'delete': + if ($store->deleteById($id)) { + if (DOWNLOAD_PICTURES) { + remove_directory(ABS_PATH . $id); + } + #$msg->add('s', _('the link has been deleted successfully')); + pocheTool::logm('delete link #' . $id); + } + else { + #$msg->add('e', _('the link wasn\'t deleted')); + pocheTool::logm('error : can\'t delete link #' . $id); + } + break; + case 'toggle_fav' : + $store->favoriteById($id); + pocheTool::logm('mark as favorite link #' . $id); + break; + case 'toggle_archive' : + $store->archiveById($id); + pocheTool::logm('archive link #' . $id); + break; + default: + break; + } +} diff --git a/inc/poche/pochePictures.php b/inc/poche/pochePictures.php new file mode 100644 index 0000000..bfc8065 --- /dev/null +++ b/inc/poche/pochePictures.php @@ -0,0 +1,114 @@ + + * @copyright 2013 + * @license http://www.wtfpl.net/ see COPYING file + */ + +/** + * On modifie les URLS des images dans le corps de l'article + */ +function filtre_picture($content, $url, $id) +{ + $matches = array(); + preg_match_all('#<\s*(img)[^>]+src="([^"]*)"[^>]*>#Si', $content, $matches, PREG_SET_ORDER); + foreach($matches as $i => $link) + { + $link[1] = trim($link[1]); + if (!preg_match('#^(([a-z]+://)|(\#))#', $link[1]) ) + { + $absolute_path = get_absolute_link($link[2],$url); + $filename = basename(parse_url($absolute_path, PHP_URL_PATH)); + $directory = create_assets_directory($id); + $fullpath = $directory . '/' . $filename; + download_pictures($absolute_path, $fullpath); + $content = str_replace($matches[$i][2], $fullpath, $content); + } + + } + + return $content; +} + +/** + * Retourne le lien absolu + */ +function get_absolute_link($relative_link, $url) +{ + /* return if already absolute URL */ + if (parse_url($relative_link, PHP_URL_SCHEME) != '') return $relative_link; + + /* queries and anchors */ + if ($relative_link[0]=='#' || $relative_link[0]=='?') return $url . $relative_link; + + /* parse base URL and convert to local variables: + $scheme, $host, $path */ + extract(parse_url($url)); + + /* remove non-directory element from path */ + $path = preg_replace('#/[^/]*$#', '', $path); + + /* destroy path if relative url points to root */ + if ($relative_link[0] == '/') $path = ''; + + /* dirty absolute URL */ + $abs = $host . $path . '/' . $relative_link; + + /* replace '//' or '/./' or '/foo/../' with '/' */ + $re = array('#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#'); + for($n=1; $n>0; $abs=preg_replace($re, '/', $abs, -1, $n)) {} + + /* absolute URL is ready! */ + return $scheme.'://'.$abs; +} + +/** + * Téléchargement des images + */ + +function download_pictures($absolute_path, $fullpath) +{ + $rawdata = get_external_file($absolute_path); + + if(file_exists($fullpath)) { + unlink($fullpath); + } + $fp = fopen($fullpath, 'x'); + fwrite($fp, $rawdata); + fclose($fp); +} + +/** + * Crée un répertoire de médias pour l'article + */ +function create_assets_directory($id) +{ + $assets_path = ABS_PATH; + if(!is_dir($assets_path)) { + mkdir($assets_path, 0705); + } + + $article_directory = $assets_path . $id; + if(!is_dir($article_directory)) { + mkdir($article_directory, 0705); + } + + return $article_directory; +} + +/** + * Suppression du répertoire d'images + */ +function remove_directory($directory) +{ + if(is_dir($directory)) { + $files = array_diff(scandir($directory), array('.','..')); + foreach ($files as $file) { + (is_dir("$directory/$file")) ? remove_directory("$directory/$file") : unlink("$directory/$file"); + } + return rmdir($directory); + } +} diff --git a/inc/pocheTool.class.php b/inc/poche/pocheTool.class.php similarity index 64% rename from inc/pocheTool.class.php rename to inc/poche/pocheTool.class.php index 9aab76b..cade115 100644 --- a/inc/pocheTool.class.php +++ b/inc/poche/pocheTool.class.php @@ -38,8 +38,7 @@ class pocheTools public static function isUrl($url) { - // http://neo22s.com/check-if-url-exists-and-is-online-php/ - $pattern='|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i'; + $pattern = '|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i'; return preg_match($pattern, $url); } @@ -65,6 +64,48 @@ class pocheTools . $_SERVER["SERVER_NAME"] . $serverport . $scriptname; } + public static function redirect($url = '') + { + if ($url === '') { + $url = (empty($_SERVER['HTTP_REFERER'])?'?':$_SERVER['HTTP_REFERER']); + if (isset($_POST['returnurl'])) { + $url = $_POST['returnurl']; + } + } + + # prevent loop + if (empty($url) || parse_url($url, PHP_URL_QUERY) === $_SERVER['QUERY_STRING']) { + $url = pocheTool::getUrl(); + } + + if (substr($url, 0, 1) !== '?') { + $ref = pocheTool::getUrl(); + if (substr($url, 0, strlen($ref)) !== $ref) { + $url = $ref; + } + } + header('Location: '.$url); + exit(); + } + + public static function cleanURL($url) + { + + $url = html_entity_decode(trim($url)); + + $stuff = strpos($url,'&utm_source='); + if ($stuff !== FALSE) + $url = substr($url, 0, $stuff); + $stuff = strpos($url,'?utm_source='); + if ($stuff !== FALSE) + $url = substr($url, 0, $stuff); + $stuff = strpos($url,'#xtor=RSS-'); + if ($stuff !== FALSE) + $url = substr($url, 0, $stuff); + + return $url; + } + public static function renderJson($data) { header('Cache-Control: no-cache, must-revalidate'); @@ -75,27 +116,9 @@ class pocheTools exit(); } - public static function redirect($rurl = '') + public static function logm($message) { - if ($rurl === '') { - $rurl = (empty($_SERVER['HTTP_REFERER'])?'?':$_SERVER['HTTP_REFERER']); - if (isset($_POST['returnurl'])) { - $rurl = $_POST['returnurl']; - } - } - - // prevent loop - if (empty($rurl) || parse_url($rurl, PHP_URL_QUERY) === $_SERVER['QUERY_STRING']) { - $rurl = pocheTool::getUrl(); - } - - if (substr($rurl, 0, 1) !== '?') { - $ref = pocheTool::getUrl(); - if (substr($rurl, 0, strlen($ref)) !== $ref) { - $rurl = $ref; - } - } - header('Location: '.$rurl); - exit(); + $t = strval(date('Y/m/d_H:i:s')).' - '.$_SERVER["REMOTE_ADDR"].' - '.strval($message)."\n"; + file_put_contents('./log.txt',$t,FILE_APPEND); } } \ No newline at end of file diff --git a/inc/rain.tpl.class.php b/inc/rain.tpl.class.php deleted file mode 100644 index 6522c79..0000000 --- a/inc/rain.tpl.class.php +++ /dev/null @@ -1,1043 +0,0 @@ -), stylesheet (), script ( - + + - {if="$load_all_js == '1'"} - - + - {/if} \ No newline at end of file + + {/if} \ No newline at end of file diff --git a/tpl/layout.twig b/tpl/layout.twig new file mode 100644 index 0000000..c5f52bb --- /dev/null +++ b/tpl/layout.twig @@ -0,0 +1,58 @@ + + + + + + + + + + + {% block title %}{% endblock %} - poche + + + + + + + + + + + + + +
+

logo pochepoche

+
+
+ {% block menu %}{% endblock %} + {% block precontent %}{% endblock %} + {% block content %}{% endblock %} + {% block js %}{% endblock %} +
+ + + + \ No newline at end of file diff --git a/tpl/login.twig b/tpl/login.twig new file mode 100644 index 0000000..390718b --- /dev/null +++ b/tpl/login.twig @@ -0,0 +1,31 @@ +{% extends "layout.twig" %} +{% block title %}Login{% endblock %} +{% block content %} +
+
+

{% trans "login to your poche" %}

+ {% if demo == 1 %}

{% trans "you are in demo mode, some features may be disabled." %}

{% endif %} +
+ + +
+ +
+ + +
+
+ +
+ + (Do not check on public computers) +
+
+
+ +
+
+ + +
+{% endblock %} \ No newline at end of file From 45161a64026cec35fcf07659508143a6f55ddf57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Fri, 2 Aug 2013 22:41:21 +0200 Subject: [PATCH 03/49] delete some files --- tpl/install.html | 30 ------------------------------ tpl/login.html | 33 --------------------------------- tpl/messages.html | 1 - 3 files changed, 64 deletions(-) delete mode 100644 tpl/install.html delete mode 100644 tpl/login.html delete mode 100644 tpl/messages.html diff --git a/tpl/install.html b/tpl/install.html deleted file mode 100644 index d11a781..0000000 --- a/tpl/install.html +++ /dev/null @@ -1,30 +0,0 @@ -{include="head"} - -
-

logo pochepoche

-
-
-
-
-

install your poche

-
- - -
-
- - -
-
- - -
-
- -
-
- - -
- -{include="footer"} diff --git a/tpl/login.html b/tpl/login.html deleted file mode 100644 index 6db742c..0000000 --- a/tpl/login.html +++ /dev/null @@ -1,33 +0,0 @@ -{include="head"} - -
-

logo pochepoche

-
-
-
-
-

login to your poche

-
- - -
-
- - -
-
- -
- - (Do not check on public computers) -
-
-
- -
-
- - -
- -{include="footer"} diff --git a/tpl/messages.html b/tpl/messages.html deleted file mode 100644 index 87af259..0000000 --- a/tpl/messages.html +++ /dev/null @@ -1 +0,0 @@ -
display(); ?>
\ No newline at end of file From 8069e235fd2971675ee5fc05026ffa9bce5cbbb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Fri, 2 Aug 2013 22:43:56 +0200 Subject: [PATCH 04/49] move Twig in 3rdparty --- inc/{ => 3rdparty}/Twig/Autoloader.php | 0 inc/{ => 3rdparty}/Twig/Compiler.php | 0 inc/{ => 3rdparty}/Twig/CompilerInterface.php | 0 inc/{ => 3rdparty}/Twig/Environment.php | 0 inc/{ => 3rdparty}/Twig/Error.php | 0 inc/{ => 3rdparty}/Twig/Error/Loader.php | 0 inc/{ => 3rdparty}/Twig/Error/Runtime.php | 0 inc/{ => 3rdparty}/Twig/Error/Syntax.php | 0 inc/{ => 3rdparty}/Twig/ExistsLoaderInterface.php | 0 inc/{ => 3rdparty}/Twig/ExpressionParser.php | 0 inc/{ => 3rdparty}/Twig/Extension.php | 0 inc/{ => 3rdparty}/Twig/Extension/Core.php | 0 inc/{ => 3rdparty}/Twig/Extension/Debug.php | 0 inc/{ => 3rdparty}/Twig/Extension/Escaper.php | 0 inc/{ => 3rdparty}/Twig/Extension/Optimizer.php | 0 inc/{ => 3rdparty}/Twig/Extension/Sandbox.php | 0 inc/{ => 3rdparty}/Twig/Extension/Staging.php | 0 inc/{ => 3rdparty}/Twig/Extension/StringLoader.php | 0 inc/{ => 3rdparty}/Twig/ExtensionInterface.php | 0 inc/{ => 3rdparty}/Twig/Extensions/Autoloader.php | 0 .../Twig/Extensions/Extension/Debug.php | 0 .../Twig/Extensions/Extension/I18n.php | 0 .../Twig/Extensions/Extension/Intl.php | 0 .../Twig/Extensions/Extension/Text.php | 0 .../Twig/Extensions/Gettext/Extractor.php | 0 .../Twig/Extensions/Gettext/Loader/Filesystem.php | 0 .../Gettext/Routing/Generator/UrlGenerator.php | 0 .../Twig/Extensions/Gettext/Test/ExtractorTest.php | 0 .../Gettext/Test/Fixtures/twig/empty.twig | 0 .../Gettext/Test/Fixtures/twig/plural.twig | 0 .../Gettext/Test/Fixtures/twig/singular.twig | 0 inc/{ => 3rdparty}/Twig/Extensions/Grammar.php | 0 .../Twig/Extensions/Grammar/Arguments.php | 0 .../Twig/Extensions/Grammar/Array.php | 0 .../Twig/Extensions/Grammar/Body.php | 0 .../Twig/Extensions/Grammar/Boolean.php | 0 .../Twig/Extensions/Grammar/Constant.php | 0 .../Twig/Extensions/Grammar/Expression.php | 0 .../Twig/Extensions/Grammar/Hash.php | 0 .../Twig/Extensions/Grammar/Number.php | 0 .../Twig/Extensions/Grammar/Optional.php | 0 .../Twig/Extensions/Grammar/Switch.php | 0 inc/{ => 3rdparty}/Twig/Extensions/Grammar/Tag.php | 0 .../Twig/Extensions/GrammarInterface.php | 0 inc/{ => 3rdparty}/Twig/Extensions/Node/Debug.php | 0 inc/{ => 3rdparty}/Twig/Extensions/Node/Trans.php | 0 .../Twig/Extensions/SimpleTokenParser.php | 0 .../Twig/Extensions/TokenParser/Debug.php | 0 .../Twig/Extensions/TokenParser/Trans.php | 0 inc/{ => 3rdparty}/Twig/Filter.php | 0 inc/{ => 3rdparty}/Twig/Filter/Function.php | 0 inc/{ => 3rdparty}/Twig/Filter/Method.php | 0 inc/{ => 3rdparty}/Twig/Filter/Node.php | 0 .../Twig/FilterCallableInterface.php | 0 inc/{ => 3rdparty}/Twig/FilterInterface.php | 0 inc/{ => 3rdparty}/Twig/Function.php | 0 inc/{ => 3rdparty}/Twig/Function/Function.php | 0 inc/{ => 3rdparty}/Twig/Function/Method.php | 0 inc/{ => 3rdparty}/Twig/Function/Node.php | 0 .../Twig/FunctionCallableInterface.php | 0 inc/{ => 3rdparty}/Twig/FunctionInterface.php | 0 inc/{ => 3rdparty}/Twig/Gettext/Extractor.php | 0 .../Twig/Gettext/Loader/Filesystem.php | 0 .../Gettext/Routing/Generator/UrlGenerator.php | 0 .../Twig/Gettext/Test/ExtractorTest.php | 0 .../Twig/Gettext/Test/Fixtures/twig/empty.twig | 0 .../Twig/Gettext/Test/Fixtures/twig/plural.twig | 0 .../Twig/Gettext/Test/Fixtures/twig/singular.twig | 0 inc/{ => 3rdparty}/Twig/Lexer.php | 0 inc/{ => 3rdparty}/Twig/LexerInterface.php | 0 inc/{ => 3rdparty}/Twig/Loader/Array.php | 0 inc/{ => 3rdparty}/Twig/Loader/Chain.php | 0 inc/{ => 3rdparty}/Twig/Loader/Filesystem.php | 0 inc/{ => 3rdparty}/Twig/Loader/String.php | 0 inc/{ => 3rdparty}/Twig/LoaderInterface.php | 0 inc/{ => 3rdparty}/Twig/Markup.php | 0 inc/{ => 3rdparty}/Twig/Node.php | 0 inc/{ => 3rdparty}/Twig/Node/AutoEscape.php | 0 inc/{ => 3rdparty}/Twig/Node/Block.php | 0 inc/{ => 3rdparty}/Twig/Node/BlockReference.php | 0 inc/{ => 3rdparty}/Twig/Node/Body.php | 0 inc/{ => 3rdparty}/Twig/Node/Do.php | 0 inc/{ => 3rdparty}/Twig/Node/Embed.php | 0 inc/{ => 3rdparty}/Twig/Node/Expression.php | 0 inc/{ => 3rdparty}/Twig/Node/Expression/Array.php | 0 .../Twig/Node/Expression/AssignName.php | 0 inc/{ => 3rdparty}/Twig/Node/Expression/Binary.php | 0 .../Twig/Node/Expression/Binary/Add.php | 0 .../Twig/Node/Expression/Binary/And.php | 0 .../Twig/Node/Expression/Binary/BitwiseAnd.php | 0 .../Twig/Node/Expression/Binary/BitwiseOr.php | 0 .../Twig/Node/Expression/Binary/BitwiseXor.php | 0 .../Twig/Node/Expression/Binary/Concat.php | 0 .../Twig/Node/Expression/Binary/Div.php | 0 .../Twig/Node/Expression/Binary/Equal.php | 0 .../Twig/Node/Expression/Binary/FloorDiv.php | 0 .../Twig/Node/Expression/Binary/Greater.php | 0 .../Twig/Node/Expression/Binary/GreaterEqual.php | 0 .../Twig/Node/Expression/Binary/In.php | 0 .../Twig/Node/Expression/Binary/Less.php | 0 .../Twig/Node/Expression/Binary/LessEqual.php | 0 .../Twig/Node/Expression/Binary/Mod.php | 0 .../Twig/Node/Expression/Binary/Mul.php | 0 .../Twig/Node/Expression/Binary/NotEqual.php | 0 .../Twig/Node/Expression/Binary/NotIn.php | 0 .../Twig/Node/Expression/Binary/Or.php | 0 .../Twig/Node/Expression/Binary/Power.php | 0 .../Twig/Node/Expression/Binary/Range.php | 0 .../Twig/Node/Expression/Binary/Sub.php | 0 .../Twig/Node/Expression/BlockReference.php | 0 inc/{ => 3rdparty}/Twig/Node/Expression/Call.php | 0 .../Twig/Node/Expression/Conditional.php | 0 .../Twig/Node/Expression/Constant.php | 0 .../Twig/Node/Expression/ExtensionReference.php | 0 inc/{ => 3rdparty}/Twig/Node/Expression/Filter.php | 0 .../Twig/Node/Expression/Filter/Default.php | 0 .../Twig/Node/Expression/Function.php | 0 .../Twig/Node/Expression/GetAttr.php | 0 .../Twig/Node/Expression/MethodCall.php | 0 inc/{ => 3rdparty}/Twig/Node/Expression/Name.php | 0 inc/{ => 3rdparty}/Twig/Node/Expression/Parent.php | 0 .../Twig/Node/Expression/TempName.php | 0 inc/{ => 3rdparty}/Twig/Node/Expression/Test.php | 0 .../Twig/Node/Expression/Test/Constant.php | 0 .../Twig/Node/Expression/Test/Defined.php | 0 .../Twig/Node/Expression/Test/Divisibleby.php | 0 .../Twig/Node/Expression/Test/Even.php | 0 .../Twig/Node/Expression/Test/Null.php | 0 .../Twig/Node/Expression/Test/Odd.php | 0 .../Twig/Node/Expression/Test/Sameas.php | 0 inc/{ => 3rdparty}/Twig/Node/Expression/Unary.php | 0 .../Twig/Node/Expression/Unary/Neg.php | 0 .../Twig/Node/Expression/Unary/Not.php | 0 .../Twig/Node/Expression/Unary/Pos.php | 0 inc/{ => 3rdparty}/Twig/Node/Flush.php | 0 inc/{ => 3rdparty}/Twig/Node/For.php | 0 inc/{ => 3rdparty}/Twig/Node/ForLoop.php | 0 inc/{ => 3rdparty}/Twig/Node/If.php | 0 inc/{ => 3rdparty}/Twig/Node/Import.php | 0 inc/{ => 3rdparty}/Twig/Node/Include.php | 0 inc/{ => 3rdparty}/Twig/Node/Macro.php | 0 inc/{ => 3rdparty}/Twig/Node/Module.php | 0 inc/{ => 3rdparty}/Twig/Node/Print.php | 0 inc/{ => 3rdparty}/Twig/Node/Sandbox.php | 0 inc/{ => 3rdparty}/Twig/Node/SandboxedModule.php | 0 inc/{ => 3rdparty}/Twig/Node/SandboxedPrint.php | 0 inc/{ => 3rdparty}/Twig/Node/Set.php | 0 inc/{ => 3rdparty}/Twig/Node/SetTemp.php | 0 inc/{ => 3rdparty}/Twig/Node/Spaceless.php | 0 inc/{ => 3rdparty}/Twig/Node/Text.php | 0 inc/{ => 3rdparty}/Twig/NodeInterface.php | 0 inc/{ => 3rdparty}/Twig/NodeOutputInterface.php | 0 inc/{ => 3rdparty}/Twig/NodeTraverser.php | 0 inc/{ => 3rdparty}/Twig/NodeVisitor/Escaper.php | 0 inc/{ => 3rdparty}/Twig/NodeVisitor/Optimizer.php | 0 .../Twig/NodeVisitor/SafeAnalysis.php | 0 inc/{ => 3rdparty}/Twig/NodeVisitor/Sandbox.php | 0 inc/{ => 3rdparty}/Twig/NodeVisitorInterface.php | 0 inc/{ => 3rdparty}/Twig/Parser.php | 0 inc/{ => 3rdparty}/Twig/ParserInterface.php | 0 inc/{ => 3rdparty}/Twig/Sandbox/SecurityError.php | 0 inc/{ => 3rdparty}/Twig/Sandbox/SecurityPolicy.php | 0 .../Twig/Sandbox/SecurityPolicyInterface.php | 0 inc/{ => 3rdparty}/Twig/SimpleFilter.php | 0 inc/{ => 3rdparty}/Twig/SimpleFunction.php | 0 inc/{ => 3rdparty}/Twig/SimpleTest.php | 0 inc/{ => 3rdparty}/Twig/Template.php | 0 inc/{ => 3rdparty}/Twig/TemplateInterface.php | 0 inc/{ => 3rdparty}/Twig/Test.php | 0 inc/{ => 3rdparty}/Twig/Test/Function.php | 0 .../Twig/Test/IntegrationTestCase.php | 0 inc/{ => 3rdparty}/Twig/Test/Method.php | 0 inc/{ => 3rdparty}/Twig/Test/Node.php | 0 inc/{ => 3rdparty}/Twig/Test/NodeTestCase.php | 0 inc/{ => 3rdparty}/Twig/TestCallableInterface.php | 0 inc/{ => 3rdparty}/Twig/TestInterface.php | 0 inc/{ => 3rdparty}/Twig/Token.php | 0 inc/{ => 3rdparty}/Twig/TokenParser.php | 0 inc/{ => 3rdparty}/Twig/TokenParser/AutoEscape.php | 0 inc/{ => 3rdparty}/Twig/TokenParser/Block.php | 0 inc/{ => 3rdparty}/Twig/TokenParser/Do.php | 0 inc/{ => 3rdparty}/Twig/TokenParser/Embed.php | 0 inc/{ => 3rdparty}/Twig/TokenParser/Extends.php | 0 inc/{ => 3rdparty}/Twig/TokenParser/Filter.php | 0 inc/{ => 3rdparty}/Twig/TokenParser/Flush.php | 0 inc/{ => 3rdparty}/Twig/TokenParser/For.php | 0 inc/{ => 3rdparty}/Twig/TokenParser/From.php | 0 inc/{ => 3rdparty}/Twig/TokenParser/If.php | 0 inc/{ => 3rdparty}/Twig/TokenParser/Import.php | 0 inc/{ => 3rdparty}/Twig/TokenParser/Include.php | 0 inc/{ => 3rdparty}/Twig/TokenParser/Macro.php | 0 inc/{ => 3rdparty}/Twig/TokenParser/Sandbox.php | 0 inc/{ => 3rdparty}/Twig/TokenParser/Set.php | 0 inc/{ => 3rdparty}/Twig/TokenParser/Spaceless.php | 0 inc/{ => 3rdparty}/Twig/TokenParser/Use.php | 0 inc/{ => 3rdparty}/Twig/TokenParserBroker.php | 0 .../Twig/TokenParserBrokerInterface.php | 0 inc/{ => 3rdparty}/Twig/TokenParserInterface.php | 0 inc/{ => 3rdparty}/Twig/TokenStream.php | 0 inc/config.php | 14 +++++++------- 200 files changed, 7 insertions(+), 7 deletions(-) rename inc/{ => 3rdparty}/Twig/Autoloader.php (100%) rename inc/{ => 3rdparty}/Twig/Compiler.php (100%) rename inc/{ => 3rdparty}/Twig/CompilerInterface.php (100%) rename inc/{ => 3rdparty}/Twig/Environment.php (100%) rename inc/{ => 3rdparty}/Twig/Error.php (100%) rename inc/{ => 3rdparty}/Twig/Error/Loader.php (100%) rename inc/{ => 3rdparty}/Twig/Error/Runtime.php (100%) rename inc/{ => 3rdparty}/Twig/Error/Syntax.php (100%) rename inc/{ => 3rdparty}/Twig/ExistsLoaderInterface.php (100%) rename inc/{ => 3rdparty}/Twig/ExpressionParser.php (100%) rename inc/{ => 3rdparty}/Twig/Extension.php (100%) rename inc/{ => 3rdparty}/Twig/Extension/Core.php (100%) rename inc/{ => 3rdparty}/Twig/Extension/Debug.php (100%) rename inc/{ => 3rdparty}/Twig/Extension/Escaper.php (100%) rename inc/{ => 3rdparty}/Twig/Extension/Optimizer.php (100%) rename inc/{ => 3rdparty}/Twig/Extension/Sandbox.php (100%) rename inc/{ => 3rdparty}/Twig/Extension/Staging.php (100%) rename inc/{ => 3rdparty}/Twig/Extension/StringLoader.php (100%) rename inc/{ => 3rdparty}/Twig/ExtensionInterface.php (100%) rename inc/{ => 3rdparty}/Twig/Extensions/Autoloader.php (100%) rename inc/{ => 3rdparty}/Twig/Extensions/Extension/Debug.php (100%) rename inc/{ => 3rdparty}/Twig/Extensions/Extension/I18n.php (100%) rename inc/{ => 3rdparty}/Twig/Extensions/Extension/Intl.php (100%) rename inc/{ => 3rdparty}/Twig/Extensions/Extension/Text.php (100%) rename inc/{ => 3rdparty}/Twig/Extensions/Gettext/Extractor.php (100%) rename inc/{ => 3rdparty}/Twig/Extensions/Gettext/Loader/Filesystem.php (100%) rename inc/{ => 3rdparty}/Twig/Extensions/Gettext/Routing/Generator/UrlGenerator.php (100%) rename inc/{ => 3rdparty}/Twig/Extensions/Gettext/Test/ExtractorTest.php (100%) rename inc/{ => 3rdparty}/Twig/Extensions/Gettext/Test/Fixtures/twig/empty.twig (100%) rename inc/{ => 3rdparty}/Twig/Extensions/Gettext/Test/Fixtures/twig/plural.twig (100%) rename inc/{ => 3rdparty}/Twig/Extensions/Gettext/Test/Fixtures/twig/singular.twig (100%) rename inc/{ => 3rdparty}/Twig/Extensions/Grammar.php (100%) rename inc/{ => 3rdparty}/Twig/Extensions/Grammar/Arguments.php (100%) rename inc/{ => 3rdparty}/Twig/Extensions/Grammar/Array.php (100%) rename inc/{ => 3rdparty}/Twig/Extensions/Grammar/Body.php (100%) rename inc/{ => 3rdparty}/Twig/Extensions/Grammar/Boolean.php (100%) rename inc/{ => 3rdparty}/Twig/Extensions/Grammar/Constant.php (100%) rename inc/{ => 3rdparty}/Twig/Extensions/Grammar/Expression.php (100%) rename inc/{ => 3rdparty}/Twig/Extensions/Grammar/Hash.php (100%) rename inc/{ => 3rdparty}/Twig/Extensions/Grammar/Number.php (100%) rename inc/{ => 3rdparty}/Twig/Extensions/Grammar/Optional.php (100%) rename inc/{ => 3rdparty}/Twig/Extensions/Grammar/Switch.php (100%) rename inc/{ => 3rdparty}/Twig/Extensions/Grammar/Tag.php (100%) rename inc/{ => 3rdparty}/Twig/Extensions/GrammarInterface.php (100%) rename inc/{ => 3rdparty}/Twig/Extensions/Node/Debug.php (100%) rename inc/{ => 3rdparty}/Twig/Extensions/Node/Trans.php (100%) rename inc/{ => 3rdparty}/Twig/Extensions/SimpleTokenParser.php (100%) rename inc/{ => 3rdparty}/Twig/Extensions/TokenParser/Debug.php (100%) rename inc/{ => 3rdparty}/Twig/Extensions/TokenParser/Trans.php (100%) rename inc/{ => 3rdparty}/Twig/Filter.php (100%) rename inc/{ => 3rdparty}/Twig/Filter/Function.php (100%) rename inc/{ => 3rdparty}/Twig/Filter/Method.php (100%) rename inc/{ => 3rdparty}/Twig/Filter/Node.php (100%) rename inc/{ => 3rdparty}/Twig/FilterCallableInterface.php (100%) rename inc/{ => 3rdparty}/Twig/FilterInterface.php (100%) rename inc/{ => 3rdparty}/Twig/Function.php (100%) rename inc/{ => 3rdparty}/Twig/Function/Function.php (100%) rename inc/{ => 3rdparty}/Twig/Function/Method.php (100%) rename inc/{ => 3rdparty}/Twig/Function/Node.php (100%) rename inc/{ => 3rdparty}/Twig/FunctionCallableInterface.php (100%) rename inc/{ => 3rdparty}/Twig/FunctionInterface.php (100%) rename inc/{ => 3rdparty}/Twig/Gettext/Extractor.php (100%) rename inc/{ => 3rdparty}/Twig/Gettext/Loader/Filesystem.php (100%) rename inc/{ => 3rdparty}/Twig/Gettext/Routing/Generator/UrlGenerator.php (100%) rename inc/{ => 3rdparty}/Twig/Gettext/Test/ExtractorTest.php (100%) rename inc/{ => 3rdparty}/Twig/Gettext/Test/Fixtures/twig/empty.twig (100%) rename inc/{ => 3rdparty}/Twig/Gettext/Test/Fixtures/twig/plural.twig (100%) rename inc/{ => 3rdparty}/Twig/Gettext/Test/Fixtures/twig/singular.twig (100%) rename inc/{ => 3rdparty}/Twig/Lexer.php (100%) rename inc/{ => 3rdparty}/Twig/LexerInterface.php (100%) rename inc/{ => 3rdparty}/Twig/Loader/Array.php (100%) rename inc/{ => 3rdparty}/Twig/Loader/Chain.php (100%) rename inc/{ => 3rdparty}/Twig/Loader/Filesystem.php (100%) rename inc/{ => 3rdparty}/Twig/Loader/String.php (100%) rename inc/{ => 3rdparty}/Twig/LoaderInterface.php (100%) rename inc/{ => 3rdparty}/Twig/Markup.php (100%) rename inc/{ => 3rdparty}/Twig/Node.php (100%) rename inc/{ => 3rdparty}/Twig/Node/AutoEscape.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Block.php (100%) rename inc/{ => 3rdparty}/Twig/Node/BlockReference.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Body.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Do.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Embed.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Array.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/AssignName.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Binary.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Binary/Add.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Binary/And.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Binary/BitwiseAnd.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Binary/BitwiseOr.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Binary/BitwiseXor.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Binary/Concat.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Binary/Div.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Binary/Equal.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Binary/FloorDiv.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Binary/Greater.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Binary/GreaterEqual.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Binary/In.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Binary/Less.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Binary/LessEqual.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Binary/Mod.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Binary/Mul.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Binary/NotEqual.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Binary/NotIn.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Binary/Or.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Binary/Power.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Binary/Range.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Binary/Sub.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/BlockReference.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Call.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Conditional.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Constant.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/ExtensionReference.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Filter.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Filter/Default.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Function.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/GetAttr.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/MethodCall.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Name.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Parent.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/TempName.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Test.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Test/Constant.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Test/Defined.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Test/Divisibleby.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Test/Even.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Test/Null.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Test/Odd.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Test/Sameas.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Unary.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Unary/Neg.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Unary/Not.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Expression/Unary/Pos.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Flush.php (100%) rename inc/{ => 3rdparty}/Twig/Node/For.php (100%) rename inc/{ => 3rdparty}/Twig/Node/ForLoop.php (100%) rename inc/{ => 3rdparty}/Twig/Node/If.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Import.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Include.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Macro.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Module.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Print.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Sandbox.php (100%) rename inc/{ => 3rdparty}/Twig/Node/SandboxedModule.php (100%) rename inc/{ => 3rdparty}/Twig/Node/SandboxedPrint.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Set.php (100%) rename inc/{ => 3rdparty}/Twig/Node/SetTemp.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Spaceless.php (100%) rename inc/{ => 3rdparty}/Twig/Node/Text.php (100%) rename inc/{ => 3rdparty}/Twig/NodeInterface.php (100%) rename inc/{ => 3rdparty}/Twig/NodeOutputInterface.php (100%) rename inc/{ => 3rdparty}/Twig/NodeTraverser.php (100%) rename inc/{ => 3rdparty}/Twig/NodeVisitor/Escaper.php (100%) rename inc/{ => 3rdparty}/Twig/NodeVisitor/Optimizer.php (100%) rename inc/{ => 3rdparty}/Twig/NodeVisitor/SafeAnalysis.php (100%) rename inc/{ => 3rdparty}/Twig/NodeVisitor/Sandbox.php (100%) rename inc/{ => 3rdparty}/Twig/NodeVisitorInterface.php (100%) rename inc/{ => 3rdparty}/Twig/Parser.php (100%) rename inc/{ => 3rdparty}/Twig/ParserInterface.php (100%) rename inc/{ => 3rdparty}/Twig/Sandbox/SecurityError.php (100%) rename inc/{ => 3rdparty}/Twig/Sandbox/SecurityPolicy.php (100%) rename inc/{ => 3rdparty}/Twig/Sandbox/SecurityPolicyInterface.php (100%) rename inc/{ => 3rdparty}/Twig/SimpleFilter.php (100%) rename inc/{ => 3rdparty}/Twig/SimpleFunction.php (100%) rename inc/{ => 3rdparty}/Twig/SimpleTest.php (100%) rename inc/{ => 3rdparty}/Twig/Template.php (100%) rename inc/{ => 3rdparty}/Twig/TemplateInterface.php (100%) rename inc/{ => 3rdparty}/Twig/Test.php (100%) rename inc/{ => 3rdparty}/Twig/Test/Function.php (100%) rename inc/{ => 3rdparty}/Twig/Test/IntegrationTestCase.php (100%) rename inc/{ => 3rdparty}/Twig/Test/Method.php (100%) rename inc/{ => 3rdparty}/Twig/Test/Node.php (100%) rename inc/{ => 3rdparty}/Twig/Test/NodeTestCase.php (100%) rename inc/{ => 3rdparty}/Twig/TestCallableInterface.php (100%) rename inc/{ => 3rdparty}/Twig/TestInterface.php (100%) rename inc/{ => 3rdparty}/Twig/Token.php (100%) rename inc/{ => 3rdparty}/Twig/TokenParser.php (100%) rename inc/{ => 3rdparty}/Twig/TokenParser/AutoEscape.php (100%) rename inc/{ => 3rdparty}/Twig/TokenParser/Block.php (100%) rename inc/{ => 3rdparty}/Twig/TokenParser/Do.php (100%) rename inc/{ => 3rdparty}/Twig/TokenParser/Embed.php (100%) rename inc/{ => 3rdparty}/Twig/TokenParser/Extends.php (100%) rename inc/{ => 3rdparty}/Twig/TokenParser/Filter.php (100%) rename inc/{ => 3rdparty}/Twig/TokenParser/Flush.php (100%) rename inc/{ => 3rdparty}/Twig/TokenParser/For.php (100%) rename inc/{ => 3rdparty}/Twig/TokenParser/From.php (100%) rename inc/{ => 3rdparty}/Twig/TokenParser/If.php (100%) rename inc/{ => 3rdparty}/Twig/TokenParser/Import.php (100%) rename inc/{ => 3rdparty}/Twig/TokenParser/Include.php (100%) rename inc/{ => 3rdparty}/Twig/TokenParser/Macro.php (100%) rename inc/{ => 3rdparty}/Twig/TokenParser/Sandbox.php (100%) rename inc/{ => 3rdparty}/Twig/TokenParser/Set.php (100%) rename inc/{ => 3rdparty}/Twig/TokenParser/Spaceless.php (100%) rename inc/{ => 3rdparty}/Twig/TokenParser/Use.php (100%) rename inc/{ => 3rdparty}/Twig/TokenParserBroker.php (100%) rename inc/{ => 3rdparty}/Twig/TokenParserBrokerInterface.php (100%) rename inc/{ => 3rdparty}/Twig/TokenParserInterface.php (100%) rename inc/{ => 3rdparty}/Twig/TokenStream.php (100%) diff --git a/inc/Twig/Autoloader.php b/inc/3rdparty/Twig/Autoloader.php similarity index 100% rename from inc/Twig/Autoloader.php rename to inc/3rdparty/Twig/Autoloader.php diff --git a/inc/Twig/Compiler.php b/inc/3rdparty/Twig/Compiler.php similarity index 100% rename from inc/Twig/Compiler.php rename to inc/3rdparty/Twig/Compiler.php diff --git a/inc/Twig/CompilerInterface.php b/inc/3rdparty/Twig/CompilerInterface.php similarity index 100% rename from inc/Twig/CompilerInterface.php rename to inc/3rdparty/Twig/CompilerInterface.php diff --git a/inc/Twig/Environment.php b/inc/3rdparty/Twig/Environment.php similarity index 100% rename from inc/Twig/Environment.php rename to inc/3rdparty/Twig/Environment.php diff --git a/inc/Twig/Error.php b/inc/3rdparty/Twig/Error.php similarity index 100% rename from inc/Twig/Error.php rename to inc/3rdparty/Twig/Error.php diff --git a/inc/Twig/Error/Loader.php b/inc/3rdparty/Twig/Error/Loader.php similarity index 100% rename from inc/Twig/Error/Loader.php rename to inc/3rdparty/Twig/Error/Loader.php diff --git a/inc/Twig/Error/Runtime.php b/inc/3rdparty/Twig/Error/Runtime.php similarity index 100% rename from inc/Twig/Error/Runtime.php rename to inc/3rdparty/Twig/Error/Runtime.php diff --git a/inc/Twig/Error/Syntax.php b/inc/3rdparty/Twig/Error/Syntax.php similarity index 100% rename from inc/Twig/Error/Syntax.php rename to inc/3rdparty/Twig/Error/Syntax.php diff --git a/inc/Twig/ExistsLoaderInterface.php b/inc/3rdparty/Twig/ExistsLoaderInterface.php similarity index 100% rename from inc/Twig/ExistsLoaderInterface.php rename to inc/3rdparty/Twig/ExistsLoaderInterface.php diff --git a/inc/Twig/ExpressionParser.php b/inc/3rdparty/Twig/ExpressionParser.php similarity index 100% rename from inc/Twig/ExpressionParser.php rename to inc/3rdparty/Twig/ExpressionParser.php diff --git a/inc/Twig/Extension.php b/inc/3rdparty/Twig/Extension.php similarity index 100% rename from inc/Twig/Extension.php rename to inc/3rdparty/Twig/Extension.php diff --git a/inc/Twig/Extension/Core.php b/inc/3rdparty/Twig/Extension/Core.php similarity index 100% rename from inc/Twig/Extension/Core.php rename to inc/3rdparty/Twig/Extension/Core.php diff --git a/inc/Twig/Extension/Debug.php b/inc/3rdparty/Twig/Extension/Debug.php similarity index 100% rename from inc/Twig/Extension/Debug.php rename to inc/3rdparty/Twig/Extension/Debug.php diff --git a/inc/Twig/Extension/Escaper.php b/inc/3rdparty/Twig/Extension/Escaper.php similarity index 100% rename from inc/Twig/Extension/Escaper.php rename to inc/3rdparty/Twig/Extension/Escaper.php diff --git a/inc/Twig/Extension/Optimizer.php b/inc/3rdparty/Twig/Extension/Optimizer.php similarity index 100% rename from inc/Twig/Extension/Optimizer.php rename to inc/3rdparty/Twig/Extension/Optimizer.php diff --git a/inc/Twig/Extension/Sandbox.php b/inc/3rdparty/Twig/Extension/Sandbox.php similarity index 100% rename from inc/Twig/Extension/Sandbox.php rename to inc/3rdparty/Twig/Extension/Sandbox.php diff --git a/inc/Twig/Extension/Staging.php b/inc/3rdparty/Twig/Extension/Staging.php similarity index 100% rename from inc/Twig/Extension/Staging.php rename to inc/3rdparty/Twig/Extension/Staging.php diff --git a/inc/Twig/Extension/StringLoader.php b/inc/3rdparty/Twig/Extension/StringLoader.php similarity index 100% rename from inc/Twig/Extension/StringLoader.php rename to inc/3rdparty/Twig/Extension/StringLoader.php diff --git a/inc/Twig/ExtensionInterface.php b/inc/3rdparty/Twig/ExtensionInterface.php similarity index 100% rename from inc/Twig/ExtensionInterface.php rename to inc/3rdparty/Twig/ExtensionInterface.php diff --git a/inc/Twig/Extensions/Autoloader.php b/inc/3rdparty/Twig/Extensions/Autoloader.php similarity index 100% rename from inc/Twig/Extensions/Autoloader.php rename to inc/3rdparty/Twig/Extensions/Autoloader.php diff --git a/inc/Twig/Extensions/Extension/Debug.php b/inc/3rdparty/Twig/Extensions/Extension/Debug.php similarity index 100% rename from inc/Twig/Extensions/Extension/Debug.php rename to inc/3rdparty/Twig/Extensions/Extension/Debug.php diff --git a/inc/Twig/Extensions/Extension/I18n.php b/inc/3rdparty/Twig/Extensions/Extension/I18n.php similarity index 100% rename from inc/Twig/Extensions/Extension/I18n.php rename to inc/3rdparty/Twig/Extensions/Extension/I18n.php diff --git a/inc/Twig/Extensions/Extension/Intl.php b/inc/3rdparty/Twig/Extensions/Extension/Intl.php similarity index 100% rename from inc/Twig/Extensions/Extension/Intl.php rename to inc/3rdparty/Twig/Extensions/Extension/Intl.php diff --git a/inc/Twig/Extensions/Extension/Text.php b/inc/3rdparty/Twig/Extensions/Extension/Text.php similarity index 100% rename from inc/Twig/Extensions/Extension/Text.php rename to inc/3rdparty/Twig/Extensions/Extension/Text.php diff --git a/inc/Twig/Extensions/Gettext/Extractor.php b/inc/3rdparty/Twig/Extensions/Gettext/Extractor.php similarity index 100% rename from inc/Twig/Extensions/Gettext/Extractor.php rename to inc/3rdparty/Twig/Extensions/Gettext/Extractor.php diff --git a/inc/Twig/Extensions/Gettext/Loader/Filesystem.php b/inc/3rdparty/Twig/Extensions/Gettext/Loader/Filesystem.php similarity index 100% rename from inc/Twig/Extensions/Gettext/Loader/Filesystem.php rename to inc/3rdparty/Twig/Extensions/Gettext/Loader/Filesystem.php diff --git a/inc/Twig/Extensions/Gettext/Routing/Generator/UrlGenerator.php b/inc/3rdparty/Twig/Extensions/Gettext/Routing/Generator/UrlGenerator.php similarity index 100% rename from inc/Twig/Extensions/Gettext/Routing/Generator/UrlGenerator.php rename to inc/3rdparty/Twig/Extensions/Gettext/Routing/Generator/UrlGenerator.php diff --git a/inc/Twig/Extensions/Gettext/Test/ExtractorTest.php b/inc/3rdparty/Twig/Extensions/Gettext/Test/ExtractorTest.php similarity index 100% rename from inc/Twig/Extensions/Gettext/Test/ExtractorTest.php rename to inc/3rdparty/Twig/Extensions/Gettext/Test/ExtractorTest.php diff --git a/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/empty.twig b/inc/3rdparty/Twig/Extensions/Gettext/Test/Fixtures/twig/empty.twig similarity index 100% rename from inc/Twig/Extensions/Gettext/Test/Fixtures/twig/empty.twig rename to inc/3rdparty/Twig/Extensions/Gettext/Test/Fixtures/twig/empty.twig diff --git a/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/plural.twig b/inc/3rdparty/Twig/Extensions/Gettext/Test/Fixtures/twig/plural.twig similarity index 100% rename from inc/Twig/Extensions/Gettext/Test/Fixtures/twig/plural.twig rename to inc/3rdparty/Twig/Extensions/Gettext/Test/Fixtures/twig/plural.twig diff --git a/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/singular.twig b/inc/3rdparty/Twig/Extensions/Gettext/Test/Fixtures/twig/singular.twig similarity index 100% rename from inc/Twig/Extensions/Gettext/Test/Fixtures/twig/singular.twig rename to inc/3rdparty/Twig/Extensions/Gettext/Test/Fixtures/twig/singular.twig diff --git a/inc/Twig/Extensions/Grammar.php b/inc/3rdparty/Twig/Extensions/Grammar.php similarity index 100% rename from inc/Twig/Extensions/Grammar.php rename to inc/3rdparty/Twig/Extensions/Grammar.php diff --git a/inc/Twig/Extensions/Grammar/Arguments.php b/inc/3rdparty/Twig/Extensions/Grammar/Arguments.php similarity index 100% rename from inc/Twig/Extensions/Grammar/Arguments.php rename to inc/3rdparty/Twig/Extensions/Grammar/Arguments.php diff --git a/inc/Twig/Extensions/Grammar/Array.php b/inc/3rdparty/Twig/Extensions/Grammar/Array.php similarity index 100% rename from inc/Twig/Extensions/Grammar/Array.php rename to inc/3rdparty/Twig/Extensions/Grammar/Array.php diff --git a/inc/Twig/Extensions/Grammar/Body.php b/inc/3rdparty/Twig/Extensions/Grammar/Body.php similarity index 100% rename from inc/Twig/Extensions/Grammar/Body.php rename to inc/3rdparty/Twig/Extensions/Grammar/Body.php diff --git a/inc/Twig/Extensions/Grammar/Boolean.php b/inc/3rdparty/Twig/Extensions/Grammar/Boolean.php similarity index 100% rename from inc/Twig/Extensions/Grammar/Boolean.php rename to inc/3rdparty/Twig/Extensions/Grammar/Boolean.php diff --git a/inc/Twig/Extensions/Grammar/Constant.php b/inc/3rdparty/Twig/Extensions/Grammar/Constant.php similarity index 100% rename from inc/Twig/Extensions/Grammar/Constant.php rename to inc/3rdparty/Twig/Extensions/Grammar/Constant.php diff --git a/inc/Twig/Extensions/Grammar/Expression.php b/inc/3rdparty/Twig/Extensions/Grammar/Expression.php similarity index 100% rename from inc/Twig/Extensions/Grammar/Expression.php rename to inc/3rdparty/Twig/Extensions/Grammar/Expression.php diff --git a/inc/Twig/Extensions/Grammar/Hash.php b/inc/3rdparty/Twig/Extensions/Grammar/Hash.php similarity index 100% rename from inc/Twig/Extensions/Grammar/Hash.php rename to inc/3rdparty/Twig/Extensions/Grammar/Hash.php diff --git a/inc/Twig/Extensions/Grammar/Number.php b/inc/3rdparty/Twig/Extensions/Grammar/Number.php similarity index 100% rename from inc/Twig/Extensions/Grammar/Number.php rename to inc/3rdparty/Twig/Extensions/Grammar/Number.php diff --git a/inc/Twig/Extensions/Grammar/Optional.php b/inc/3rdparty/Twig/Extensions/Grammar/Optional.php similarity index 100% rename from inc/Twig/Extensions/Grammar/Optional.php rename to inc/3rdparty/Twig/Extensions/Grammar/Optional.php diff --git a/inc/Twig/Extensions/Grammar/Switch.php b/inc/3rdparty/Twig/Extensions/Grammar/Switch.php similarity index 100% rename from inc/Twig/Extensions/Grammar/Switch.php rename to inc/3rdparty/Twig/Extensions/Grammar/Switch.php diff --git a/inc/Twig/Extensions/Grammar/Tag.php b/inc/3rdparty/Twig/Extensions/Grammar/Tag.php similarity index 100% rename from inc/Twig/Extensions/Grammar/Tag.php rename to inc/3rdparty/Twig/Extensions/Grammar/Tag.php diff --git a/inc/Twig/Extensions/GrammarInterface.php b/inc/3rdparty/Twig/Extensions/GrammarInterface.php similarity index 100% rename from inc/Twig/Extensions/GrammarInterface.php rename to inc/3rdparty/Twig/Extensions/GrammarInterface.php diff --git a/inc/Twig/Extensions/Node/Debug.php b/inc/3rdparty/Twig/Extensions/Node/Debug.php similarity index 100% rename from inc/Twig/Extensions/Node/Debug.php rename to inc/3rdparty/Twig/Extensions/Node/Debug.php diff --git a/inc/Twig/Extensions/Node/Trans.php b/inc/3rdparty/Twig/Extensions/Node/Trans.php similarity index 100% rename from inc/Twig/Extensions/Node/Trans.php rename to inc/3rdparty/Twig/Extensions/Node/Trans.php diff --git a/inc/Twig/Extensions/SimpleTokenParser.php b/inc/3rdparty/Twig/Extensions/SimpleTokenParser.php similarity index 100% rename from inc/Twig/Extensions/SimpleTokenParser.php rename to inc/3rdparty/Twig/Extensions/SimpleTokenParser.php diff --git a/inc/Twig/Extensions/TokenParser/Debug.php b/inc/3rdparty/Twig/Extensions/TokenParser/Debug.php similarity index 100% rename from inc/Twig/Extensions/TokenParser/Debug.php rename to inc/3rdparty/Twig/Extensions/TokenParser/Debug.php diff --git a/inc/Twig/Extensions/TokenParser/Trans.php b/inc/3rdparty/Twig/Extensions/TokenParser/Trans.php similarity index 100% rename from inc/Twig/Extensions/TokenParser/Trans.php rename to inc/3rdparty/Twig/Extensions/TokenParser/Trans.php diff --git a/inc/Twig/Filter.php b/inc/3rdparty/Twig/Filter.php similarity index 100% rename from inc/Twig/Filter.php rename to inc/3rdparty/Twig/Filter.php diff --git a/inc/Twig/Filter/Function.php b/inc/3rdparty/Twig/Filter/Function.php similarity index 100% rename from inc/Twig/Filter/Function.php rename to inc/3rdparty/Twig/Filter/Function.php diff --git a/inc/Twig/Filter/Method.php b/inc/3rdparty/Twig/Filter/Method.php similarity index 100% rename from inc/Twig/Filter/Method.php rename to inc/3rdparty/Twig/Filter/Method.php diff --git a/inc/Twig/Filter/Node.php b/inc/3rdparty/Twig/Filter/Node.php similarity index 100% rename from inc/Twig/Filter/Node.php rename to inc/3rdparty/Twig/Filter/Node.php diff --git a/inc/Twig/FilterCallableInterface.php b/inc/3rdparty/Twig/FilterCallableInterface.php similarity index 100% rename from inc/Twig/FilterCallableInterface.php rename to inc/3rdparty/Twig/FilterCallableInterface.php diff --git a/inc/Twig/FilterInterface.php b/inc/3rdparty/Twig/FilterInterface.php similarity index 100% rename from inc/Twig/FilterInterface.php rename to inc/3rdparty/Twig/FilterInterface.php diff --git a/inc/Twig/Function.php b/inc/3rdparty/Twig/Function.php similarity index 100% rename from inc/Twig/Function.php rename to inc/3rdparty/Twig/Function.php diff --git a/inc/Twig/Function/Function.php b/inc/3rdparty/Twig/Function/Function.php similarity index 100% rename from inc/Twig/Function/Function.php rename to inc/3rdparty/Twig/Function/Function.php diff --git a/inc/Twig/Function/Method.php b/inc/3rdparty/Twig/Function/Method.php similarity index 100% rename from inc/Twig/Function/Method.php rename to inc/3rdparty/Twig/Function/Method.php diff --git a/inc/Twig/Function/Node.php b/inc/3rdparty/Twig/Function/Node.php similarity index 100% rename from inc/Twig/Function/Node.php rename to inc/3rdparty/Twig/Function/Node.php diff --git a/inc/Twig/FunctionCallableInterface.php b/inc/3rdparty/Twig/FunctionCallableInterface.php similarity index 100% rename from inc/Twig/FunctionCallableInterface.php rename to inc/3rdparty/Twig/FunctionCallableInterface.php diff --git a/inc/Twig/FunctionInterface.php b/inc/3rdparty/Twig/FunctionInterface.php similarity index 100% rename from inc/Twig/FunctionInterface.php rename to inc/3rdparty/Twig/FunctionInterface.php diff --git a/inc/Twig/Gettext/Extractor.php b/inc/3rdparty/Twig/Gettext/Extractor.php similarity index 100% rename from inc/Twig/Gettext/Extractor.php rename to inc/3rdparty/Twig/Gettext/Extractor.php diff --git a/inc/Twig/Gettext/Loader/Filesystem.php b/inc/3rdparty/Twig/Gettext/Loader/Filesystem.php similarity index 100% rename from inc/Twig/Gettext/Loader/Filesystem.php rename to inc/3rdparty/Twig/Gettext/Loader/Filesystem.php diff --git a/inc/Twig/Gettext/Routing/Generator/UrlGenerator.php b/inc/3rdparty/Twig/Gettext/Routing/Generator/UrlGenerator.php similarity index 100% rename from inc/Twig/Gettext/Routing/Generator/UrlGenerator.php rename to inc/3rdparty/Twig/Gettext/Routing/Generator/UrlGenerator.php diff --git a/inc/Twig/Gettext/Test/ExtractorTest.php b/inc/3rdparty/Twig/Gettext/Test/ExtractorTest.php similarity index 100% rename from inc/Twig/Gettext/Test/ExtractorTest.php rename to inc/3rdparty/Twig/Gettext/Test/ExtractorTest.php diff --git a/inc/Twig/Gettext/Test/Fixtures/twig/empty.twig b/inc/3rdparty/Twig/Gettext/Test/Fixtures/twig/empty.twig similarity index 100% rename from inc/Twig/Gettext/Test/Fixtures/twig/empty.twig rename to inc/3rdparty/Twig/Gettext/Test/Fixtures/twig/empty.twig diff --git a/inc/Twig/Gettext/Test/Fixtures/twig/plural.twig b/inc/3rdparty/Twig/Gettext/Test/Fixtures/twig/plural.twig similarity index 100% rename from inc/Twig/Gettext/Test/Fixtures/twig/plural.twig rename to inc/3rdparty/Twig/Gettext/Test/Fixtures/twig/plural.twig diff --git a/inc/Twig/Gettext/Test/Fixtures/twig/singular.twig b/inc/3rdparty/Twig/Gettext/Test/Fixtures/twig/singular.twig similarity index 100% rename from inc/Twig/Gettext/Test/Fixtures/twig/singular.twig rename to inc/3rdparty/Twig/Gettext/Test/Fixtures/twig/singular.twig diff --git a/inc/Twig/Lexer.php b/inc/3rdparty/Twig/Lexer.php similarity index 100% rename from inc/Twig/Lexer.php rename to inc/3rdparty/Twig/Lexer.php diff --git a/inc/Twig/LexerInterface.php b/inc/3rdparty/Twig/LexerInterface.php similarity index 100% rename from inc/Twig/LexerInterface.php rename to inc/3rdparty/Twig/LexerInterface.php diff --git a/inc/Twig/Loader/Array.php b/inc/3rdparty/Twig/Loader/Array.php similarity index 100% rename from inc/Twig/Loader/Array.php rename to inc/3rdparty/Twig/Loader/Array.php diff --git a/inc/Twig/Loader/Chain.php b/inc/3rdparty/Twig/Loader/Chain.php similarity index 100% rename from inc/Twig/Loader/Chain.php rename to inc/3rdparty/Twig/Loader/Chain.php diff --git a/inc/Twig/Loader/Filesystem.php b/inc/3rdparty/Twig/Loader/Filesystem.php similarity index 100% rename from inc/Twig/Loader/Filesystem.php rename to inc/3rdparty/Twig/Loader/Filesystem.php diff --git a/inc/Twig/Loader/String.php b/inc/3rdparty/Twig/Loader/String.php similarity index 100% rename from inc/Twig/Loader/String.php rename to inc/3rdparty/Twig/Loader/String.php diff --git a/inc/Twig/LoaderInterface.php b/inc/3rdparty/Twig/LoaderInterface.php similarity index 100% rename from inc/Twig/LoaderInterface.php rename to inc/3rdparty/Twig/LoaderInterface.php diff --git a/inc/Twig/Markup.php b/inc/3rdparty/Twig/Markup.php similarity index 100% rename from inc/Twig/Markup.php rename to inc/3rdparty/Twig/Markup.php diff --git a/inc/Twig/Node.php b/inc/3rdparty/Twig/Node.php similarity index 100% rename from inc/Twig/Node.php rename to inc/3rdparty/Twig/Node.php diff --git a/inc/Twig/Node/AutoEscape.php b/inc/3rdparty/Twig/Node/AutoEscape.php similarity index 100% rename from inc/Twig/Node/AutoEscape.php rename to inc/3rdparty/Twig/Node/AutoEscape.php diff --git a/inc/Twig/Node/Block.php b/inc/3rdparty/Twig/Node/Block.php similarity index 100% rename from inc/Twig/Node/Block.php rename to inc/3rdparty/Twig/Node/Block.php diff --git a/inc/Twig/Node/BlockReference.php b/inc/3rdparty/Twig/Node/BlockReference.php similarity index 100% rename from inc/Twig/Node/BlockReference.php rename to inc/3rdparty/Twig/Node/BlockReference.php diff --git a/inc/Twig/Node/Body.php b/inc/3rdparty/Twig/Node/Body.php similarity index 100% rename from inc/Twig/Node/Body.php rename to inc/3rdparty/Twig/Node/Body.php diff --git a/inc/Twig/Node/Do.php b/inc/3rdparty/Twig/Node/Do.php similarity index 100% rename from inc/Twig/Node/Do.php rename to inc/3rdparty/Twig/Node/Do.php diff --git a/inc/Twig/Node/Embed.php b/inc/3rdparty/Twig/Node/Embed.php similarity index 100% rename from inc/Twig/Node/Embed.php rename to inc/3rdparty/Twig/Node/Embed.php diff --git a/inc/Twig/Node/Expression.php b/inc/3rdparty/Twig/Node/Expression.php similarity index 100% rename from inc/Twig/Node/Expression.php rename to inc/3rdparty/Twig/Node/Expression.php diff --git a/inc/Twig/Node/Expression/Array.php b/inc/3rdparty/Twig/Node/Expression/Array.php similarity index 100% rename from inc/Twig/Node/Expression/Array.php rename to inc/3rdparty/Twig/Node/Expression/Array.php diff --git a/inc/Twig/Node/Expression/AssignName.php b/inc/3rdparty/Twig/Node/Expression/AssignName.php similarity index 100% rename from inc/Twig/Node/Expression/AssignName.php rename to inc/3rdparty/Twig/Node/Expression/AssignName.php diff --git a/inc/Twig/Node/Expression/Binary.php b/inc/3rdparty/Twig/Node/Expression/Binary.php similarity index 100% rename from inc/Twig/Node/Expression/Binary.php rename to inc/3rdparty/Twig/Node/Expression/Binary.php diff --git a/inc/Twig/Node/Expression/Binary/Add.php b/inc/3rdparty/Twig/Node/Expression/Binary/Add.php similarity index 100% rename from inc/Twig/Node/Expression/Binary/Add.php rename to inc/3rdparty/Twig/Node/Expression/Binary/Add.php diff --git a/inc/Twig/Node/Expression/Binary/And.php b/inc/3rdparty/Twig/Node/Expression/Binary/And.php similarity index 100% rename from inc/Twig/Node/Expression/Binary/And.php rename to inc/3rdparty/Twig/Node/Expression/Binary/And.php diff --git a/inc/Twig/Node/Expression/Binary/BitwiseAnd.php b/inc/3rdparty/Twig/Node/Expression/Binary/BitwiseAnd.php similarity index 100% rename from inc/Twig/Node/Expression/Binary/BitwiseAnd.php rename to inc/3rdparty/Twig/Node/Expression/Binary/BitwiseAnd.php diff --git a/inc/Twig/Node/Expression/Binary/BitwiseOr.php b/inc/3rdparty/Twig/Node/Expression/Binary/BitwiseOr.php similarity index 100% rename from inc/Twig/Node/Expression/Binary/BitwiseOr.php rename to inc/3rdparty/Twig/Node/Expression/Binary/BitwiseOr.php diff --git a/inc/Twig/Node/Expression/Binary/BitwiseXor.php b/inc/3rdparty/Twig/Node/Expression/Binary/BitwiseXor.php similarity index 100% rename from inc/Twig/Node/Expression/Binary/BitwiseXor.php rename to inc/3rdparty/Twig/Node/Expression/Binary/BitwiseXor.php diff --git a/inc/Twig/Node/Expression/Binary/Concat.php b/inc/3rdparty/Twig/Node/Expression/Binary/Concat.php similarity index 100% rename from inc/Twig/Node/Expression/Binary/Concat.php rename to inc/3rdparty/Twig/Node/Expression/Binary/Concat.php diff --git a/inc/Twig/Node/Expression/Binary/Div.php b/inc/3rdparty/Twig/Node/Expression/Binary/Div.php similarity index 100% rename from inc/Twig/Node/Expression/Binary/Div.php rename to inc/3rdparty/Twig/Node/Expression/Binary/Div.php diff --git a/inc/Twig/Node/Expression/Binary/Equal.php b/inc/3rdparty/Twig/Node/Expression/Binary/Equal.php similarity index 100% rename from inc/Twig/Node/Expression/Binary/Equal.php rename to inc/3rdparty/Twig/Node/Expression/Binary/Equal.php diff --git a/inc/Twig/Node/Expression/Binary/FloorDiv.php b/inc/3rdparty/Twig/Node/Expression/Binary/FloorDiv.php similarity index 100% rename from inc/Twig/Node/Expression/Binary/FloorDiv.php rename to inc/3rdparty/Twig/Node/Expression/Binary/FloorDiv.php diff --git a/inc/Twig/Node/Expression/Binary/Greater.php b/inc/3rdparty/Twig/Node/Expression/Binary/Greater.php similarity index 100% rename from inc/Twig/Node/Expression/Binary/Greater.php rename to inc/3rdparty/Twig/Node/Expression/Binary/Greater.php diff --git a/inc/Twig/Node/Expression/Binary/GreaterEqual.php b/inc/3rdparty/Twig/Node/Expression/Binary/GreaterEqual.php similarity index 100% rename from inc/Twig/Node/Expression/Binary/GreaterEqual.php rename to inc/3rdparty/Twig/Node/Expression/Binary/GreaterEqual.php diff --git a/inc/Twig/Node/Expression/Binary/In.php b/inc/3rdparty/Twig/Node/Expression/Binary/In.php similarity index 100% rename from inc/Twig/Node/Expression/Binary/In.php rename to inc/3rdparty/Twig/Node/Expression/Binary/In.php diff --git a/inc/Twig/Node/Expression/Binary/Less.php b/inc/3rdparty/Twig/Node/Expression/Binary/Less.php similarity index 100% rename from inc/Twig/Node/Expression/Binary/Less.php rename to inc/3rdparty/Twig/Node/Expression/Binary/Less.php diff --git a/inc/Twig/Node/Expression/Binary/LessEqual.php b/inc/3rdparty/Twig/Node/Expression/Binary/LessEqual.php similarity index 100% rename from inc/Twig/Node/Expression/Binary/LessEqual.php rename to inc/3rdparty/Twig/Node/Expression/Binary/LessEqual.php diff --git a/inc/Twig/Node/Expression/Binary/Mod.php b/inc/3rdparty/Twig/Node/Expression/Binary/Mod.php similarity index 100% rename from inc/Twig/Node/Expression/Binary/Mod.php rename to inc/3rdparty/Twig/Node/Expression/Binary/Mod.php diff --git a/inc/Twig/Node/Expression/Binary/Mul.php b/inc/3rdparty/Twig/Node/Expression/Binary/Mul.php similarity index 100% rename from inc/Twig/Node/Expression/Binary/Mul.php rename to inc/3rdparty/Twig/Node/Expression/Binary/Mul.php diff --git a/inc/Twig/Node/Expression/Binary/NotEqual.php b/inc/3rdparty/Twig/Node/Expression/Binary/NotEqual.php similarity index 100% rename from inc/Twig/Node/Expression/Binary/NotEqual.php rename to inc/3rdparty/Twig/Node/Expression/Binary/NotEqual.php diff --git a/inc/Twig/Node/Expression/Binary/NotIn.php b/inc/3rdparty/Twig/Node/Expression/Binary/NotIn.php similarity index 100% rename from inc/Twig/Node/Expression/Binary/NotIn.php rename to inc/3rdparty/Twig/Node/Expression/Binary/NotIn.php diff --git a/inc/Twig/Node/Expression/Binary/Or.php b/inc/3rdparty/Twig/Node/Expression/Binary/Or.php similarity index 100% rename from inc/Twig/Node/Expression/Binary/Or.php rename to inc/3rdparty/Twig/Node/Expression/Binary/Or.php diff --git a/inc/Twig/Node/Expression/Binary/Power.php b/inc/3rdparty/Twig/Node/Expression/Binary/Power.php similarity index 100% rename from inc/Twig/Node/Expression/Binary/Power.php rename to inc/3rdparty/Twig/Node/Expression/Binary/Power.php diff --git a/inc/Twig/Node/Expression/Binary/Range.php b/inc/3rdparty/Twig/Node/Expression/Binary/Range.php similarity index 100% rename from inc/Twig/Node/Expression/Binary/Range.php rename to inc/3rdparty/Twig/Node/Expression/Binary/Range.php diff --git a/inc/Twig/Node/Expression/Binary/Sub.php b/inc/3rdparty/Twig/Node/Expression/Binary/Sub.php similarity index 100% rename from inc/Twig/Node/Expression/Binary/Sub.php rename to inc/3rdparty/Twig/Node/Expression/Binary/Sub.php diff --git a/inc/Twig/Node/Expression/BlockReference.php b/inc/3rdparty/Twig/Node/Expression/BlockReference.php similarity index 100% rename from inc/Twig/Node/Expression/BlockReference.php rename to inc/3rdparty/Twig/Node/Expression/BlockReference.php diff --git a/inc/Twig/Node/Expression/Call.php b/inc/3rdparty/Twig/Node/Expression/Call.php similarity index 100% rename from inc/Twig/Node/Expression/Call.php rename to inc/3rdparty/Twig/Node/Expression/Call.php diff --git a/inc/Twig/Node/Expression/Conditional.php b/inc/3rdparty/Twig/Node/Expression/Conditional.php similarity index 100% rename from inc/Twig/Node/Expression/Conditional.php rename to inc/3rdparty/Twig/Node/Expression/Conditional.php diff --git a/inc/Twig/Node/Expression/Constant.php b/inc/3rdparty/Twig/Node/Expression/Constant.php similarity index 100% rename from inc/Twig/Node/Expression/Constant.php rename to inc/3rdparty/Twig/Node/Expression/Constant.php diff --git a/inc/Twig/Node/Expression/ExtensionReference.php b/inc/3rdparty/Twig/Node/Expression/ExtensionReference.php similarity index 100% rename from inc/Twig/Node/Expression/ExtensionReference.php rename to inc/3rdparty/Twig/Node/Expression/ExtensionReference.php diff --git a/inc/Twig/Node/Expression/Filter.php b/inc/3rdparty/Twig/Node/Expression/Filter.php similarity index 100% rename from inc/Twig/Node/Expression/Filter.php rename to inc/3rdparty/Twig/Node/Expression/Filter.php diff --git a/inc/Twig/Node/Expression/Filter/Default.php b/inc/3rdparty/Twig/Node/Expression/Filter/Default.php similarity index 100% rename from inc/Twig/Node/Expression/Filter/Default.php rename to inc/3rdparty/Twig/Node/Expression/Filter/Default.php diff --git a/inc/Twig/Node/Expression/Function.php b/inc/3rdparty/Twig/Node/Expression/Function.php similarity index 100% rename from inc/Twig/Node/Expression/Function.php rename to inc/3rdparty/Twig/Node/Expression/Function.php diff --git a/inc/Twig/Node/Expression/GetAttr.php b/inc/3rdparty/Twig/Node/Expression/GetAttr.php similarity index 100% rename from inc/Twig/Node/Expression/GetAttr.php rename to inc/3rdparty/Twig/Node/Expression/GetAttr.php diff --git a/inc/Twig/Node/Expression/MethodCall.php b/inc/3rdparty/Twig/Node/Expression/MethodCall.php similarity index 100% rename from inc/Twig/Node/Expression/MethodCall.php rename to inc/3rdparty/Twig/Node/Expression/MethodCall.php diff --git a/inc/Twig/Node/Expression/Name.php b/inc/3rdparty/Twig/Node/Expression/Name.php similarity index 100% rename from inc/Twig/Node/Expression/Name.php rename to inc/3rdparty/Twig/Node/Expression/Name.php diff --git a/inc/Twig/Node/Expression/Parent.php b/inc/3rdparty/Twig/Node/Expression/Parent.php similarity index 100% rename from inc/Twig/Node/Expression/Parent.php rename to inc/3rdparty/Twig/Node/Expression/Parent.php diff --git a/inc/Twig/Node/Expression/TempName.php b/inc/3rdparty/Twig/Node/Expression/TempName.php similarity index 100% rename from inc/Twig/Node/Expression/TempName.php rename to inc/3rdparty/Twig/Node/Expression/TempName.php diff --git a/inc/Twig/Node/Expression/Test.php b/inc/3rdparty/Twig/Node/Expression/Test.php similarity index 100% rename from inc/Twig/Node/Expression/Test.php rename to inc/3rdparty/Twig/Node/Expression/Test.php diff --git a/inc/Twig/Node/Expression/Test/Constant.php b/inc/3rdparty/Twig/Node/Expression/Test/Constant.php similarity index 100% rename from inc/Twig/Node/Expression/Test/Constant.php rename to inc/3rdparty/Twig/Node/Expression/Test/Constant.php diff --git a/inc/Twig/Node/Expression/Test/Defined.php b/inc/3rdparty/Twig/Node/Expression/Test/Defined.php similarity index 100% rename from inc/Twig/Node/Expression/Test/Defined.php rename to inc/3rdparty/Twig/Node/Expression/Test/Defined.php diff --git a/inc/Twig/Node/Expression/Test/Divisibleby.php b/inc/3rdparty/Twig/Node/Expression/Test/Divisibleby.php similarity index 100% rename from inc/Twig/Node/Expression/Test/Divisibleby.php rename to inc/3rdparty/Twig/Node/Expression/Test/Divisibleby.php diff --git a/inc/Twig/Node/Expression/Test/Even.php b/inc/3rdparty/Twig/Node/Expression/Test/Even.php similarity index 100% rename from inc/Twig/Node/Expression/Test/Even.php rename to inc/3rdparty/Twig/Node/Expression/Test/Even.php diff --git a/inc/Twig/Node/Expression/Test/Null.php b/inc/3rdparty/Twig/Node/Expression/Test/Null.php similarity index 100% rename from inc/Twig/Node/Expression/Test/Null.php rename to inc/3rdparty/Twig/Node/Expression/Test/Null.php diff --git a/inc/Twig/Node/Expression/Test/Odd.php b/inc/3rdparty/Twig/Node/Expression/Test/Odd.php similarity index 100% rename from inc/Twig/Node/Expression/Test/Odd.php rename to inc/3rdparty/Twig/Node/Expression/Test/Odd.php diff --git a/inc/Twig/Node/Expression/Test/Sameas.php b/inc/3rdparty/Twig/Node/Expression/Test/Sameas.php similarity index 100% rename from inc/Twig/Node/Expression/Test/Sameas.php rename to inc/3rdparty/Twig/Node/Expression/Test/Sameas.php diff --git a/inc/Twig/Node/Expression/Unary.php b/inc/3rdparty/Twig/Node/Expression/Unary.php similarity index 100% rename from inc/Twig/Node/Expression/Unary.php rename to inc/3rdparty/Twig/Node/Expression/Unary.php diff --git a/inc/Twig/Node/Expression/Unary/Neg.php b/inc/3rdparty/Twig/Node/Expression/Unary/Neg.php similarity index 100% rename from inc/Twig/Node/Expression/Unary/Neg.php rename to inc/3rdparty/Twig/Node/Expression/Unary/Neg.php diff --git a/inc/Twig/Node/Expression/Unary/Not.php b/inc/3rdparty/Twig/Node/Expression/Unary/Not.php similarity index 100% rename from inc/Twig/Node/Expression/Unary/Not.php rename to inc/3rdparty/Twig/Node/Expression/Unary/Not.php diff --git a/inc/Twig/Node/Expression/Unary/Pos.php b/inc/3rdparty/Twig/Node/Expression/Unary/Pos.php similarity index 100% rename from inc/Twig/Node/Expression/Unary/Pos.php rename to inc/3rdparty/Twig/Node/Expression/Unary/Pos.php diff --git a/inc/Twig/Node/Flush.php b/inc/3rdparty/Twig/Node/Flush.php similarity index 100% rename from inc/Twig/Node/Flush.php rename to inc/3rdparty/Twig/Node/Flush.php diff --git a/inc/Twig/Node/For.php b/inc/3rdparty/Twig/Node/For.php similarity index 100% rename from inc/Twig/Node/For.php rename to inc/3rdparty/Twig/Node/For.php diff --git a/inc/Twig/Node/ForLoop.php b/inc/3rdparty/Twig/Node/ForLoop.php similarity index 100% rename from inc/Twig/Node/ForLoop.php rename to inc/3rdparty/Twig/Node/ForLoop.php diff --git a/inc/Twig/Node/If.php b/inc/3rdparty/Twig/Node/If.php similarity index 100% rename from inc/Twig/Node/If.php rename to inc/3rdparty/Twig/Node/If.php diff --git a/inc/Twig/Node/Import.php b/inc/3rdparty/Twig/Node/Import.php similarity index 100% rename from inc/Twig/Node/Import.php rename to inc/3rdparty/Twig/Node/Import.php diff --git a/inc/Twig/Node/Include.php b/inc/3rdparty/Twig/Node/Include.php similarity index 100% rename from inc/Twig/Node/Include.php rename to inc/3rdparty/Twig/Node/Include.php diff --git a/inc/Twig/Node/Macro.php b/inc/3rdparty/Twig/Node/Macro.php similarity index 100% rename from inc/Twig/Node/Macro.php rename to inc/3rdparty/Twig/Node/Macro.php diff --git a/inc/Twig/Node/Module.php b/inc/3rdparty/Twig/Node/Module.php similarity index 100% rename from inc/Twig/Node/Module.php rename to inc/3rdparty/Twig/Node/Module.php diff --git a/inc/Twig/Node/Print.php b/inc/3rdparty/Twig/Node/Print.php similarity index 100% rename from inc/Twig/Node/Print.php rename to inc/3rdparty/Twig/Node/Print.php diff --git a/inc/Twig/Node/Sandbox.php b/inc/3rdparty/Twig/Node/Sandbox.php similarity index 100% rename from inc/Twig/Node/Sandbox.php rename to inc/3rdparty/Twig/Node/Sandbox.php diff --git a/inc/Twig/Node/SandboxedModule.php b/inc/3rdparty/Twig/Node/SandboxedModule.php similarity index 100% rename from inc/Twig/Node/SandboxedModule.php rename to inc/3rdparty/Twig/Node/SandboxedModule.php diff --git a/inc/Twig/Node/SandboxedPrint.php b/inc/3rdparty/Twig/Node/SandboxedPrint.php similarity index 100% rename from inc/Twig/Node/SandboxedPrint.php rename to inc/3rdparty/Twig/Node/SandboxedPrint.php diff --git a/inc/Twig/Node/Set.php b/inc/3rdparty/Twig/Node/Set.php similarity index 100% rename from inc/Twig/Node/Set.php rename to inc/3rdparty/Twig/Node/Set.php diff --git a/inc/Twig/Node/SetTemp.php b/inc/3rdparty/Twig/Node/SetTemp.php similarity index 100% rename from inc/Twig/Node/SetTemp.php rename to inc/3rdparty/Twig/Node/SetTemp.php diff --git a/inc/Twig/Node/Spaceless.php b/inc/3rdparty/Twig/Node/Spaceless.php similarity index 100% rename from inc/Twig/Node/Spaceless.php rename to inc/3rdparty/Twig/Node/Spaceless.php diff --git a/inc/Twig/Node/Text.php b/inc/3rdparty/Twig/Node/Text.php similarity index 100% rename from inc/Twig/Node/Text.php rename to inc/3rdparty/Twig/Node/Text.php diff --git a/inc/Twig/NodeInterface.php b/inc/3rdparty/Twig/NodeInterface.php similarity index 100% rename from inc/Twig/NodeInterface.php rename to inc/3rdparty/Twig/NodeInterface.php diff --git a/inc/Twig/NodeOutputInterface.php b/inc/3rdparty/Twig/NodeOutputInterface.php similarity index 100% rename from inc/Twig/NodeOutputInterface.php rename to inc/3rdparty/Twig/NodeOutputInterface.php diff --git a/inc/Twig/NodeTraverser.php b/inc/3rdparty/Twig/NodeTraverser.php similarity index 100% rename from inc/Twig/NodeTraverser.php rename to inc/3rdparty/Twig/NodeTraverser.php diff --git a/inc/Twig/NodeVisitor/Escaper.php b/inc/3rdparty/Twig/NodeVisitor/Escaper.php similarity index 100% rename from inc/Twig/NodeVisitor/Escaper.php rename to inc/3rdparty/Twig/NodeVisitor/Escaper.php diff --git a/inc/Twig/NodeVisitor/Optimizer.php b/inc/3rdparty/Twig/NodeVisitor/Optimizer.php similarity index 100% rename from inc/Twig/NodeVisitor/Optimizer.php rename to inc/3rdparty/Twig/NodeVisitor/Optimizer.php diff --git a/inc/Twig/NodeVisitor/SafeAnalysis.php b/inc/3rdparty/Twig/NodeVisitor/SafeAnalysis.php similarity index 100% rename from inc/Twig/NodeVisitor/SafeAnalysis.php rename to inc/3rdparty/Twig/NodeVisitor/SafeAnalysis.php diff --git a/inc/Twig/NodeVisitor/Sandbox.php b/inc/3rdparty/Twig/NodeVisitor/Sandbox.php similarity index 100% rename from inc/Twig/NodeVisitor/Sandbox.php rename to inc/3rdparty/Twig/NodeVisitor/Sandbox.php diff --git a/inc/Twig/NodeVisitorInterface.php b/inc/3rdparty/Twig/NodeVisitorInterface.php similarity index 100% rename from inc/Twig/NodeVisitorInterface.php rename to inc/3rdparty/Twig/NodeVisitorInterface.php diff --git a/inc/Twig/Parser.php b/inc/3rdparty/Twig/Parser.php similarity index 100% rename from inc/Twig/Parser.php rename to inc/3rdparty/Twig/Parser.php diff --git a/inc/Twig/ParserInterface.php b/inc/3rdparty/Twig/ParserInterface.php similarity index 100% rename from inc/Twig/ParserInterface.php rename to inc/3rdparty/Twig/ParserInterface.php diff --git a/inc/Twig/Sandbox/SecurityError.php b/inc/3rdparty/Twig/Sandbox/SecurityError.php similarity index 100% rename from inc/Twig/Sandbox/SecurityError.php rename to inc/3rdparty/Twig/Sandbox/SecurityError.php diff --git a/inc/Twig/Sandbox/SecurityPolicy.php b/inc/3rdparty/Twig/Sandbox/SecurityPolicy.php similarity index 100% rename from inc/Twig/Sandbox/SecurityPolicy.php rename to inc/3rdparty/Twig/Sandbox/SecurityPolicy.php diff --git a/inc/Twig/Sandbox/SecurityPolicyInterface.php b/inc/3rdparty/Twig/Sandbox/SecurityPolicyInterface.php similarity index 100% rename from inc/Twig/Sandbox/SecurityPolicyInterface.php rename to inc/3rdparty/Twig/Sandbox/SecurityPolicyInterface.php diff --git a/inc/Twig/SimpleFilter.php b/inc/3rdparty/Twig/SimpleFilter.php similarity index 100% rename from inc/Twig/SimpleFilter.php rename to inc/3rdparty/Twig/SimpleFilter.php diff --git a/inc/Twig/SimpleFunction.php b/inc/3rdparty/Twig/SimpleFunction.php similarity index 100% rename from inc/Twig/SimpleFunction.php rename to inc/3rdparty/Twig/SimpleFunction.php diff --git a/inc/Twig/SimpleTest.php b/inc/3rdparty/Twig/SimpleTest.php similarity index 100% rename from inc/Twig/SimpleTest.php rename to inc/3rdparty/Twig/SimpleTest.php diff --git a/inc/Twig/Template.php b/inc/3rdparty/Twig/Template.php similarity index 100% rename from inc/Twig/Template.php rename to inc/3rdparty/Twig/Template.php diff --git a/inc/Twig/TemplateInterface.php b/inc/3rdparty/Twig/TemplateInterface.php similarity index 100% rename from inc/Twig/TemplateInterface.php rename to inc/3rdparty/Twig/TemplateInterface.php diff --git a/inc/Twig/Test.php b/inc/3rdparty/Twig/Test.php similarity index 100% rename from inc/Twig/Test.php rename to inc/3rdparty/Twig/Test.php diff --git a/inc/Twig/Test/Function.php b/inc/3rdparty/Twig/Test/Function.php similarity index 100% rename from inc/Twig/Test/Function.php rename to inc/3rdparty/Twig/Test/Function.php diff --git a/inc/Twig/Test/IntegrationTestCase.php b/inc/3rdparty/Twig/Test/IntegrationTestCase.php similarity index 100% rename from inc/Twig/Test/IntegrationTestCase.php rename to inc/3rdparty/Twig/Test/IntegrationTestCase.php diff --git a/inc/Twig/Test/Method.php b/inc/3rdparty/Twig/Test/Method.php similarity index 100% rename from inc/Twig/Test/Method.php rename to inc/3rdparty/Twig/Test/Method.php diff --git a/inc/Twig/Test/Node.php b/inc/3rdparty/Twig/Test/Node.php similarity index 100% rename from inc/Twig/Test/Node.php rename to inc/3rdparty/Twig/Test/Node.php diff --git a/inc/Twig/Test/NodeTestCase.php b/inc/3rdparty/Twig/Test/NodeTestCase.php similarity index 100% rename from inc/Twig/Test/NodeTestCase.php rename to inc/3rdparty/Twig/Test/NodeTestCase.php diff --git a/inc/Twig/TestCallableInterface.php b/inc/3rdparty/Twig/TestCallableInterface.php similarity index 100% rename from inc/Twig/TestCallableInterface.php rename to inc/3rdparty/Twig/TestCallableInterface.php diff --git a/inc/Twig/TestInterface.php b/inc/3rdparty/Twig/TestInterface.php similarity index 100% rename from inc/Twig/TestInterface.php rename to inc/3rdparty/Twig/TestInterface.php diff --git a/inc/Twig/Token.php b/inc/3rdparty/Twig/Token.php similarity index 100% rename from inc/Twig/Token.php rename to inc/3rdparty/Twig/Token.php diff --git a/inc/Twig/TokenParser.php b/inc/3rdparty/Twig/TokenParser.php similarity index 100% rename from inc/Twig/TokenParser.php rename to inc/3rdparty/Twig/TokenParser.php diff --git a/inc/Twig/TokenParser/AutoEscape.php b/inc/3rdparty/Twig/TokenParser/AutoEscape.php similarity index 100% rename from inc/Twig/TokenParser/AutoEscape.php rename to inc/3rdparty/Twig/TokenParser/AutoEscape.php diff --git a/inc/Twig/TokenParser/Block.php b/inc/3rdparty/Twig/TokenParser/Block.php similarity index 100% rename from inc/Twig/TokenParser/Block.php rename to inc/3rdparty/Twig/TokenParser/Block.php diff --git a/inc/Twig/TokenParser/Do.php b/inc/3rdparty/Twig/TokenParser/Do.php similarity index 100% rename from inc/Twig/TokenParser/Do.php rename to inc/3rdparty/Twig/TokenParser/Do.php diff --git a/inc/Twig/TokenParser/Embed.php b/inc/3rdparty/Twig/TokenParser/Embed.php similarity index 100% rename from inc/Twig/TokenParser/Embed.php rename to inc/3rdparty/Twig/TokenParser/Embed.php diff --git a/inc/Twig/TokenParser/Extends.php b/inc/3rdparty/Twig/TokenParser/Extends.php similarity index 100% rename from inc/Twig/TokenParser/Extends.php rename to inc/3rdparty/Twig/TokenParser/Extends.php diff --git a/inc/Twig/TokenParser/Filter.php b/inc/3rdparty/Twig/TokenParser/Filter.php similarity index 100% rename from inc/Twig/TokenParser/Filter.php rename to inc/3rdparty/Twig/TokenParser/Filter.php diff --git a/inc/Twig/TokenParser/Flush.php b/inc/3rdparty/Twig/TokenParser/Flush.php similarity index 100% rename from inc/Twig/TokenParser/Flush.php rename to inc/3rdparty/Twig/TokenParser/Flush.php diff --git a/inc/Twig/TokenParser/For.php b/inc/3rdparty/Twig/TokenParser/For.php similarity index 100% rename from inc/Twig/TokenParser/For.php rename to inc/3rdparty/Twig/TokenParser/For.php diff --git a/inc/Twig/TokenParser/From.php b/inc/3rdparty/Twig/TokenParser/From.php similarity index 100% rename from inc/Twig/TokenParser/From.php rename to inc/3rdparty/Twig/TokenParser/From.php diff --git a/inc/Twig/TokenParser/If.php b/inc/3rdparty/Twig/TokenParser/If.php similarity index 100% rename from inc/Twig/TokenParser/If.php rename to inc/3rdparty/Twig/TokenParser/If.php diff --git a/inc/Twig/TokenParser/Import.php b/inc/3rdparty/Twig/TokenParser/Import.php similarity index 100% rename from inc/Twig/TokenParser/Import.php rename to inc/3rdparty/Twig/TokenParser/Import.php diff --git a/inc/Twig/TokenParser/Include.php b/inc/3rdparty/Twig/TokenParser/Include.php similarity index 100% rename from inc/Twig/TokenParser/Include.php rename to inc/3rdparty/Twig/TokenParser/Include.php diff --git a/inc/Twig/TokenParser/Macro.php b/inc/3rdparty/Twig/TokenParser/Macro.php similarity index 100% rename from inc/Twig/TokenParser/Macro.php rename to inc/3rdparty/Twig/TokenParser/Macro.php diff --git a/inc/Twig/TokenParser/Sandbox.php b/inc/3rdparty/Twig/TokenParser/Sandbox.php similarity index 100% rename from inc/Twig/TokenParser/Sandbox.php rename to inc/3rdparty/Twig/TokenParser/Sandbox.php diff --git a/inc/Twig/TokenParser/Set.php b/inc/3rdparty/Twig/TokenParser/Set.php similarity index 100% rename from inc/Twig/TokenParser/Set.php rename to inc/3rdparty/Twig/TokenParser/Set.php diff --git a/inc/Twig/TokenParser/Spaceless.php b/inc/3rdparty/Twig/TokenParser/Spaceless.php similarity index 100% rename from inc/Twig/TokenParser/Spaceless.php rename to inc/3rdparty/Twig/TokenParser/Spaceless.php diff --git a/inc/Twig/TokenParser/Use.php b/inc/3rdparty/Twig/TokenParser/Use.php similarity index 100% rename from inc/Twig/TokenParser/Use.php rename to inc/3rdparty/Twig/TokenParser/Use.php diff --git a/inc/Twig/TokenParserBroker.php b/inc/3rdparty/Twig/TokenParserBroker.php similarity index 100% rename from inc/Twig/TokenParserBroker.php rename to inc/3rdparty/Twig/TokenParserBroker.php diff --git a/inc/Twig/TokenParserBrokerInterface.php b/inc/3rdparty/Twig/TokenParserBrokerInterface.php similarity index 100% rename from inc/Twig/TokenParserBrokerInterface.php rename to inc/3rdparty/Twig/TokenParserBrokerInterface.php diff --git a/inc/Twig/TokenParserInterface.php b/inc/3rdparty/Twig/TokenParserInterface.php similarity index 100% rename from inc/Twig/TokenParserInterface.php rename to inc/3rdparty/Twig/TokenParserInterface.php diff --git a/inc/Twig/TokenStream.php b/inc/3rdparty/Twig/TokenStream.php similarity index 100% rename from inc/Twig/TokenStream.php rename to inc/3rdparty/Twig/TokenStream.php diff --git a/inc/config.php b/inc/config.php index 7470f0c..65b2b9c 100644 --- a/inc/config.php +++ b/inc/config.php @@ -24,17 +24,17 @@ $storage_type = 'sqlite'; # sqlite or file # /!\ Be careful if you change the lines below /!\ -require_once 'pocheCore.php'; -require_once 'Readability.php'; -require_once 'Encoding.php'; -require_once 'pocheTool.class.php'; -require_once 'Session.class.php'; -require_once 'Twig/Autoloader.php'; +require_once 'poche/pocheTool.class.php'; +require_once 'poche/pocheCore.php'; +require_once '3rdparty/Readability.php'; +require_once '3rdparty/Encoding.php'; +require_once '3rdparty/Session.class.php'; +require_once '3rdparty/Twig/Autoloader.php'; require_once 'store/store.class.php'; require_once 'store/' . $storage_type . '.class.php'; if (DOWNLOAD_PICTURES) { - require_once 'pochePicture.php'; + require_once 'poche/pochePicture.php'; } # i18n From 5ffe5cf541d0d1c7524537b034d0cde3da18f6e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Fri, 2 Aug 2013 23:00:57 +0200 Subject: [PATCH 05/49] rename pocheTool -> pocheTools --- inc/config.php | 3 ++- inc/poche/pocheCore.php | 18 ++++++++++-------- inc/poche/pochePictures.php | 12 ++++-------- ...ocheTool.class.php => pocheTools.class.php} | 6 ++++-- 4 files changed, 20 insertions(+), 19 deletions(-) rename inc/poche/{pocheTool.class.php => pocheTools.class.php} (94%) diff --git a/inc/config.php b/inc/config.php index 65b2b9c..58abb53 100644 --- a/inc/config.php +++ b/inc/config.php @@ -10,6 +10,7 @@ define ('POCHE_VERSION', '0.3'); define ('MODE_DEMO', FALSE); +define ('DEBUG_POCHE', TRUE); define ('CONVERT_LINKS_FOOTNOTES', FALSE); define ('REVERT_FORCED_PARAGRAPH_ELEMENTS', FALSE); define ('DOWNLOAD_PICTURES', FALSE); @@ -24,7 +25,7 @@ $storage_type = 'sqlite'; # sqlite or file # /!\ Be careful if you change the lines below /!\ -require_once 'poche/pocheTool.class.php'; +require_once 'poche/pocheTools.class.php'; require_once 'poche/pocheCore.php'; require_once '3rdparty/Readability.php'; require_once '3rdparty/Encoding.php'; diff --git a/inc/poche/pocheCore.php b/inc/poche/pocheCore.php index 52c603a..34c15d8 100644 --- a/inc/poche/pocheCore.php +++ b/inc/poche/pocheCore.php @@ -136,10 +136,16 @@ function fetch_url_content($url) function display_view($view, $id = 0, $full_head = 'yes') { - global $tpl, $store, $msg; + global $tpl, $store; switch ($view) { + case 'install': + pocheTool::logm('install mode'); + break; + case 'import'; + pocheTool::logm('import mode'); + break; case 'export': $entries = $store->retrieveAll(); $tpl->assign('export', pocheTool::renderJson($entries)); @@ -157,8 +163,8 @@ function display_view($view, $id = 0, $full_head = 'yes') break; case 'view': $entry = $store->retrieveOneById($id); - if ($entry != NULL) { + pocheTool::logm('view link #' . $id); $tpl->assign('id', $entry['id']); $tpl->assign('url', $entry['url']); $tpl->assign('title', $entry['title']); @@ -177,12 +183,9 @@ function display_view($view, $id = 0, $full_head = 'yes') else { pocheTool::logm('error in view call : entry is NULL'); } - - pocheTool::logm('view link #' . $id); break; default: # home view $entries = $store->getEntriesByView($view); - $tpl->assign('entries', $entries); if ($full_head == 'yes') { @@ -192,7 +195,6 @@ function display_view($view, $id = 0, $full_head = 'yes') } $tpl->draw('entries'); - if ($full_head == 'yes') { $tpl->draw('js'); $tpl->draw('footer'); @@ -202,11 +204,11 @@ function display_view($view, $id = 0, $full_head = 'yes') } /** - * Appel d'une action (mark as fav, archive, delete) + * Call action (mark as fav, archive, delete, etc.) */ function action_to_do($action, $url, $id = 0) { - global $store, $msg; + global $store; switch ($action) { diff --git a/inc/poche/pochePictures.php b/inc/poche/pochePictures.php index bfc8065..0d73a14 100644 --- a/inc/poche/pochePictures.php +++ b/inc/poche/pochePictures.php @@ -15,11 +15,9 @@ function filtre_picture($content, $url, $id) { $matches = array(); preg_match_all('#<\s*(img)[^>]+src="([^"]*)"[^>]*>#Si', $content, $matches, PREG_SET_ORDER); - foreach($matches as $i => $link) - { + foreach($matches as $i => $link) { $link[1] = trim($link[1]); - if (!preg_match('#^(([a-z]+://)|(\#))#', $link[1]) ) - { + if (!preg_match('#^(([a-z]+://)|(\#))#', $link[1])) { $absolute_path = get_absolute_link($link[2],$url); $filename = basename(parse_url($absolute_path, PHP_URL_PATH)); $directory = create_assets_directory($id); @@ -36,8 +34,7 @@ function filtre_picture($content, $url, $id) /** * Retourne le lien absolu */ -function get_absolute_link($relative_link, $url) -{ +function get_absolute_link($relative_link, $url) { /* return if already absolute URL */ if (parse_url($relative_link, PHP_URL_SCHEME) != '') return $relative_link; @@ -68,7 +65,6 @@ function get_absolute_link($relative_link, $url) /** * Téléchargement des images */ - function download_pictures($absolute_path, $fullpath) { $rawdata = get_external_file($absolute_path); @@ -111,4 +107,4 @@ function remove_directory($directory) } return rmdir($directory); } -} +} \ No newline at end of file diff --git a/inc/poche/pocheTool.class.php b/inc/poche/pocheTools.class.php similarity index 94% rename from inc/poche/pocheTool.class.php rename to inc/poche/pocheTools.class.php index cade115..8907c18 100644 --- a/inc/poche/pocheTool.class.php +++ b/inc/poche/pocheTools.class.php @@ -118,7 +118,9 @@ class pocheTools public static function logm($message) { - $t = strval(date('Y/m/d_H:i:s')).' - '.$_SERVER["REMOTE_ADDR"].' - '.strval($message)."\n"; - file_put_contents('./log.txt',$t,FILE_APPEND); + if (DEBUG_POCHE) { + $t = strval(date('Y/m/d_H:i:s')) . ' - ' . $_SERVER["REMOTE_ADDR"] . ' - ' . strval($message) . "\n"; + file_put_contents('./log.txt', $t, FILE_APPEND); + } } } \ No newline at end of file From 161395d7098ec2bd86671d15d5b54f39148e2d5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Fri, 2 Aug 2013 23:04:24 +0200 Subject: [PATCH 06/49] mv pochetool pochetools --- inc/config.php | 4 +- inc/poche/pocheCore.php | 34 ++++----- inc/poche/pocheTool.class.php | 126 +++++++++++++++++++++++++++++++++ inc/poche/pocheTools.class.php | 4 +- index.php | 18 ++--- 5 files changed, 156 insertions(+), 30 deletions(-) create mode 100644 inc/poche/pocheTool.class.php diff --git a/inc/config.php b/inc/config.php index 58abb53..c4898cc 100644 --- a/inc/config.php +++ b/inc/config.php @@ -58,7 +58,7 @@ $store = new $storage_type(); # installation if(!$store->isInstalled()) { - pocheTool::logm('poche still not installed'); + pocheTools::logm('poche still not installed'); echo $twig->render('install.twig', array( 'token' => Session::getToken(), )); @@ -68,7 +68,7 @@ if(!$store->isInstalled()) # let's rock, install poche baby ! $store->install($_POST['login'], encode_string($_POST['password'] . $_POST['login'])); Session::logout(); - pocheTool::redirect(); + pocheTools::redirect(); } } exit(); diff --git a/inc/poche/pocheCore.php b/inc/poche/pocheCore.php index 34c15d8..e68696a 100644 --- a/inc/poche/pocheCore.php +++ b/inc/poche/pocheCore.php @@ -93,8 +93,8 @@ function get_external_file($url) function fetch_url_content($url) { $url = base64_decode($url); - if (pocheTool::isUrl($url)) { - $url = pocheTool::cleanURL($url); + if (pocheTools::isUrl($url)) { + $url = pocheTools::cleanURL($url); $html = Encoding::toUTF8(get_external_file($url)); # if get_external_file if not able to retrieve HTTPS content, try the same URL with HTTP protocol @@ -128,7 +128,7 @@ function fetch_url_content($url) } else { #$msg->add('e', _('error during url preparation : the link is not valid')); - pocheTool::logm($url . ' is not a valid url'); + pocheTools::logm($url . ' is not a valid url'); } return FALSE; @@ -141,16 +141,16 @@ function display_view($view, $id = 0, $full_head = 'yes') switch ($view) { case 'install': - pocheTool::logm('install mode'); + pocheTools::logm('install mode'); break; case 'import'; - pocheTool::logm('import mode'); + pocheTools::logm('import mode'); break; case 'export': $entries = $store->retrieveAll(); - $tpl->assign('export', pocheTool::renderJson($entries)); + $tpl->assign('export', pocheTools::renderJson($entries)); $tpl->draw('export'); - pocheTool::logm('export view'); + pocheTools::logm('export view'); break; case 'config': $tpl->assign('load_all_js', 0); @@ -159,12 +159,12 @@ function display_view($view, $id = 0, $full_head = 'yes') $tpl->draw('config'); $tpl->draw('js'); $tpl->draw('footer'); - pocheTool::logm('config view'); + pocheTools::logm('config view'); break; case 'view': $entry = $store->retrieveOneById($id); if ($entry != NULL) { - pocheTool::logm('view link #' . $id); + pocheTools::logm('view link #' . $id); $tpl->assign('id', $entry['id']); $tpl->assign('url', $entry['url']); $tpl->assign('title', $entry['title']); @@ -181,7 +181,7 @@ function display_view($view, $id = 0, $full_head = 'yes') $tpl->draw('view'); } else { - pocheTool::logm('error in view call : entry is NULL'); + pocheTools::logm('error in view call : entry is NULL'); } break; default: # home view @@ -215,7 +215,7 @@ function action_to_do($action, $url, $id = 0) case 'add': if($parametres_url = fetch_url_content($url)) { if ($store->add($url, $parametres_url['title'], $parametres_url['content'])) { - pocheTool::logm('add link ' . $url); + pocheTools::logm('add link ' . $url); $last_id = $store->getLastId(); if (DOWNLOAD_PICTURES) { $content = filtre_picture($parametres_url['content'], $url, $last_id); @@ -224,12 +224,12 @@ function action_to_do($action, $url, $id = 0) } else { #$msg->add('e', _('error during insertion : the link wasn\'t added')); - pocheTool::logm('error during insertion : the link wasn\'t added'); + pocheTools::logm('error during insertion : the link wasn\'t added'); } } else { #$msg->add('e', _('error during url preparation : the link wasn\'t added')); - pocheTool::logm('error during content fetch'); + pocheTools::logm('error during content fetch'); } break; case 'delete': @@ -238,20 +238,20 @@ function action_to_do($action, $url, $id = 0) remove_directory(ABS_PATH . $id); } #$msg->add('s', _('the link has been deleted successfully')); - pocheTool::logm('delete link #' . $id); + pocheTools::logm('delete link #' . $id); } else { #$msg->add('e', _('the link wasn\'t deleted')); - pocheTool::logm('error : can\'t delete link #' . $id); + pocheTools::logm('error : can\'t delete link #' . $id); } break; case 'toggle_fav' : $store->favoriteById($id); - pocheTool::logm('mark as favorite link #' . $id); + pocheTools::logm('mark as favorite link #' . $id); break; case 'toggle_archive' : $store->archiveById($id); - pocheTool::logm('archive link #' . $id); + pocheTools::logm('archive link #' . $id); break; default: break; diff --git a/inc/poche/pocheTool.class.php b/inc/poche/pocheTool.class.php new file mode 100644 index 0000000..8907c18 --- /dev/null +++ b/inc/poche/pocheTool.class.php @@ -0,0 +1,126 @@ + + * @copyright 2013 + * @license http://www.wtfpl.net/ see COPYING file + */ + +class pocheTools +{ + public static function initPhp() + { + define('START_TIME', microtime(true)); + + if (phpversion() < 5) { + die(_('Oops, it seems you don\'t have PHP 5.')); + } + + error_reporting(E_ALL); + + function stripslashesDeep($value) { + return is_array($value) + ? array_map('stripslashesDeep', $value) + : stripslashes($value); + } + + if (get_magic_quotes_gpc()) { + $_POST = array_map('stripslashesDeep', $_POST); + $_GET = array_map('stripslashesDeep', $_GET); + $_COOKIE = array_map('stripslashesDeep', $_COOKIE); + } + + ob_start(); + register_shutdown_function('ob_end_flush'); + } + + public static function isUrl($url) + { + $pattern = '|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i'; + + return preg_match($pattern, $url); + } + + public static function getUrl() + { + $https = (!empty($_SERVER['HTTPS']) + && (strtolower($_SERVER['HTTPS']) == 'on')) + || (isset($_SERVER["SERVER_PORT"]) + && $_SERVER["SERVER_PORT"] == '443'); // HTTPS detection. + $serverport = (!isset($_SERVER["SERVER_PORT"]) + || $_SERVER["SERVER_PORT"] == '80' + || ($https && $_SERVER["SERVER_PORT"] == '443') + ? '' : ':' . $_SERVER["SERVER_PORT"]); + + $scriptname = str_replace('/index.php', '/', $_SERVER["SCRIPT_NAME"]); + + if (!isset($_SERVER["SERVER_NAME"])) { + return $scriptname; + } + + return 'http' . ($https ? 's' : '') . '://' + . $_SERVER["SERVER_NAME"] . $serverport . $scriptname; + } + + public static function redirect($url = '') + { + if ($url === '') { + $url = (empty($_SERVER['HTTP_REFERER'])?'?':$_SERVER['HTTP_REFERER']); + if (isset($_POST['returnurl'])) { + $url = $_POST['returnurl']; + } + } + + # prevent loop + if (empty($url) || parse_url($url, PHP_URL_QUERY) === $_SERVER['QUERY_STRING']) { + $url = pocheTool::getUrl(); + } + + if (substr($url, 0, 1) !== '?') { + $ref = pocheTool::getUrl(); + if (substr($url, 0, strlen($ref)) !== $ref) { + $url = $ref; + } + } + header('Location: '.$url); + exit(); + } + + public static function cleanURL($url) + { + + $url = html_entity_decode(trim($url)); + + $stuff = strpos($url,'&utm_source='); + if ($stuff !== FALSE) + $url = substr($url, 0, $stuff); + $stuff = strpos($url,'?utm_source='); + if ($stuff !== FALSE) + $url = substr($url, 0, $stuff); + $stuff = strpos($url,'#xtor=RSS-'); + if ($stuff !== FALSE) + $url = substr($url, 0, $stuff); + + return $url; + } + + public static function renderJson($data) + { + header('Cache-Control: no-cache, must-revalidate'); + header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); + header('Content-type: application/json; charset=UTF-8'); + + echo json_encode($data); + exit(); + } + + public static function logm($message) + { + if (DEBUG_POCHE) { + $t = strval(date('Y/m/d_H:i:s')) . ' - ' . $_SERVER["REMOTE_ADDR"] . ' - ' . strval($message) . "\n"; + file_put_contents('./log.txt', $t, FILE_APPEND); + } + } +} \ No newline at end of file diff --git a/inc/poche/pocheTools.class.php b/inc/poche/pocheTools.class.php index 8907c18..08c9dc8 100644 --- a/inc/poche/pocheTools.class.php +++ b/inc/poche/pocheTools.class.php @@ -75,11 +75,11 @@ class pocheTools # prevent loop if (empty($url) || parse_url($url, PHP_URL_QUERY) === $_SERVER['QUERY_STRING']) { - $url = pocheTool::getUrl(); + $url = pocheTools::getUrl(); } if (substr($url, 0, 1) !== '?') { - $ref = pocheTool::getUrl(); + $ref = pocheTools::getUrl(); if (substr($url, 0, strlen($ref)) !== $ref) { $url = $ref; } diff --git a/index.php b/index.php index 78daaaf..4962639 100644 --- a/index.php +++ b/index.php @@ -10,7 +10,7 @@ include dirname(__FILE__).'/inc/config.php'; -pocheTool::initPhp(); +pocheTools::initPhp(); # XSRF protection with token if (!empty($_POST)) { @@ -26,7 +26,7 @@ if (isset($_GET['login'])) { // Login if (!empty($_POST['login']) && !empty($_POST['password'])) { if (Session::login($_SESSION['login'], $_SESSION['pass'], $_POST['login'], encode_string($_POST['password'] . $_POST['login']))) { - pocheTool::logm('login successful'); + pocheTools::logm('login successful'); if (!empty($_POST['longlastingsession'])) { $_SESSION['longlastingsession'] = 31536000; $_SESSION['expires_on'] = time() + $_SESSION['longlastingsession']; @@ -36,23 +36,23 @@ if (isset($_GET['login'])) { } session_regenerate_id(true); - pocheTool::redirect($referer); + pocheTools::redirect($referer); } - pocheTool::logm('login failed'); + pocheTools::logm('login failed'); die(_("Login failed !")); } else { - pocheTool::logm('login failed'); + pocheTools::logm('login failed'); } } elseif (isset($_GET['logout'])) { - pocheTool::logm('logout'); + pocheTools::logm('logout'); Session::logout(); - pocheTool::redirect(); + pocheTools::redirect(); } elseif (isset($_GET['config'])) { if (isset($_POST['password']) && isset($_POST['password_repeat'])) { if ($_POST['password'] == $_POST['password_repeat'] && $_POST['password'] != "") { - pocheTool::logm('password updated'); + pocheTools::logm('password updated'); if (!MODE_DEMO) { $store->updatePassword(encode_string($_POST['password'] . $_SESSION['login'])); #your password has been updated @@ -78,7 +78,7 @@ $tpl_vars = array( 'isLogged' => Session::isLogged(), 'referer' => $referer, 'view' => $view, - 'poche_url' => pocheTool::getUrl(), + 'poche_url' => pocheTools::getUrl(), 'demo' => MODE_DEMO, 'title' => _('poche, a read it later open source system'), ); From afe60d614b5c3c7700db93e51a991b0180887726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Sat, 3 Aug 2013 08:05:02 +0200 Subject: [PATCH 07/49] remove file --- inc/poche/pocheTool.class.php | 126 ---------------------------------- 1 file changed, 126 deletions(-) delete mode 100644 inc/poche/pocheTool.class.php diff --git a/inc/poche/pocheTool.class.php b/inc/poche/pocheTool.class.php deleted file mode 100644 index 8907c18..0000000 --- a/inc/poche/pocheTool.class.php +++ /dev/null @@ -1,126 +0,0 @@ - - * @copyright 2013 - * @license http://www.wtfpl.net/ see COPYING file - */ - -class pocheTools -{ - public static function initPhp() - { - define('START_TIME', microtime(true)); - - if (phpversion() < 5) { - die(_('Oops, it seems you don\'t have PHP 5.')); - } - - error_reporting(E_ALL); - - function stripslashesDeep($value) { - return is_array($value) - ? array_map('stripslashesDeep', $value) - : stripslashes($value); - } - - if (get_magic_quotes_gpc()) { - $_POST = array_map('stripslashesDeep', $_POST); - $_GET = array_map('stripslashesDeep', $_GET); - $_COOKIE = array_map('stripslashesDeep', $_COOKIE); - } - - ob_start(); - register_shutdown_function('ob_end_flush'); - } - - public static function isUrl($url) - { - $pattern = '|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i'; - - return preg_match($pattern, $url); - } - - public static function getUrl() - { - $https = (!empty($_SERVER['HTTPS']) - && (strtolower($_SERVER['HTTPS']) == 'on')) - || (isset($_SERVER["SERVER_PORT"]) - && $_SERVER["SERVER_PORT"] == '443'); // HTTPS detection. - $serverport = (!isset($_SERVER["SERVER_PORT"]) - || $_SERVER["SERVER_PORT"] == '80' - || ($https && $_SERVER["SERVER_PORT"] == '443') - ? '' : ':' . $_SERVER["SERVER_PORT"]); - - $scriptname = str_replace('/index.php', '/', $_SERVER["SCRIPT_NAME"]); - - if (!isset($_SERVER["SERVER_NAME"])) { - return $scriptname; - } - - return 'http' . ($https ? 's' : '') . '://' - . $_SERVER["SERVER_NAME"] . $serverport . $scriptname; - } - - public static function redirect($url = '') - { - if ($url === '') { - $url = (empty($_SERVER['HTTP_REFERER'])?'?':$_SERVER['HTTP_REFERER']); - if (isset($_POST['returnurl'])) { - $url = $_POST['returnurl']; - } - } - - # prevent loop - if (empty($url) || parse_url($url, PHP_URL_QUERY) === $_SERVER['QUERY_STRING']) { - $url = pocheTool::getUrl(); - } - - if (substr($url, 0, 1) !== '?') { - $ref = pocheTool::getUrl(); - if (substr($url, 0, strlen($ref)) !== $ref) { - $url = $ref; - } - } - header('Location: '.$url); - exit(); - } - - public static function cleanURL($url) - { - - $url = html_entity_decode(trim($url)); - - $stuff = strpos($url,'&utm_source='); - if ($stuff !== FALSE) - $url = substr($url, 0, $stuff); - $stuff = strpos($url,'?utm_source='); - if ($stuff !== FALSE) - $url = substr($url, 0, $stuff); - $stuff = strpos($url,'#xtor=RSS-'); - if ($stuff !== FALSE) - $url = substr($url, 0, $stuff); - - return $url; - } - - public static function renderJson($data) - { - header('Cache-Control: no-cache, must-revalidate'); - header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); - header('Content-type: application/json; charset=UTF-8'); - - echo json_encode($data); - exit(); - } - - public static function logm($message) - { - if (DEBUG_POCHE) { - $t = strval(date('Y/m/d_H:i:s')) . ' - ' . $_SERVER["REMOTE_ADDR"] . ' - ' . strval($message) . "\n"; - file_put_contents('./log.txt', $t, FILE_APPEND); - } - } -} \ No newline at end of file From c67e13e04baab64bcc63fd0dca46125513250c44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Sat, 3 Aug 2013 08:24:42 +0200 Subject: [PATCH 08/49] new tpl files --- tpl/_bookmarklet.twig | 20 ++++++++++++++++++++ tpl/_footer.twig | 3 +++ tpl/_head.twig | 10 ++++++++++ tpl/_top.twig | 3 +++ tpl/{home.html => home.twig} | 0 5 files changed, 36 insertions(+) create mode 100644 tpl/_bookmarklet.twig create mode 100644 tpl/_footer.twig create mode 100644 tpl/_head.twig create mode 100644 tpl/_top.twig rename tpl/{home.html => home.twig} (100%) diff --git a/tpl/_bookmarklet.twig b/tpl/_bookmarklet.twig new file mode 100644 index 0000000..0878e07 --- /dev/null +++ b/tpl/_bookmarklet.twig @@ -0,0 +1,20 @@ + \ No newline at end of file diff --git a/tpl/_footer.twig b/tpl/_footer.twig new file mode 100644 index 0000000..59b58fa --- /dev/null +++ b/tpl/_footer.twig @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/tpl/_head.twig b/tpl/_head.twig new file mode 100644 index 0000000..2d640cb --- /dev/null +++ b/tpl/_head.twig @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/tpl/_top.twig b/tpl/_top.twig new file mode 100644 index 0000000..8e3ea7e --- /dev/null +++ b/tpl/_top.twig @@ -0,0 +1,3 @@ +
+

logo pochepoche

+
\ No newline at end of file diff --git a/tpl/home.html b/tpl/home.twig similarity index 100% rename from tpl/home.html rename to tpl/home.twig From 8cbb2a88024969f7efd90f8053f3b0805fa2f8fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Sat, 3 Aug 2013 08:25:11 +0200 Subject: [PATCH 09/49] twig implementation --- css/style.css | 19 +++--------------- inc/config.php | 8 +++++--- inc/poche/pocheCore.php | 30 +++++++++++++++++----------- index.php | 23 +++++++++++---------- tpl/home.twig | 44 ++++++++++++++++++++++++++++++----------- tpl/layout.twig | 42 +++++---------------------------------- tpl/login.twig | 10 ++++++++++ 7 files changed, 86 insertions(+), 90 deletions(-) diff --git a/css/style.css b/css/style.css index 28e18b9..69e3749 100644 --- a/css/style.css +++ b/css/style.css @@ -205,19 +205,6 @@ body.article { } } -/*** ***/ -/*** MESSAGES ***/ - -.messages { width: 100%; -moz-border-radius: 4px; border-radius: 4px; display: block; padding: 10px 0; margin: 10px auto 10px; clear: both; } -.messages a.closeMessage { margin: -14px -8px 0 0; display:none; width: 16px; height: 16px; float: right; background: url(../img/messages/close.png) no-repeat; } -/*.messages:hover a.closeMessage { visibility:visible; }*/ -.messages p { margin: 3px 0 3px 10px !important; padding: 0 10px 0 23px !important; font-size: 14px; line-height: 16px; } -.messages.error { border: 1px solid #C42608; color: #c00 !important; background: #FFF0EF; } -.messages.error p { background: url(../img/messages/cross.png ) no-repeat 0px 50%; color:#c00 !important; } -.messages.success {background: #E0FBCC; border: 1px solid #6DC70C; } -.messages.success p { background: url(../img/messages/tick.png) no-repeat 0px 50%; color: #2B6301 !important; } -.messages.warning { background: #FFFCD3; border: 1px solid #EBCD41; color: #000; } -.messages.warning p { background: url(../img/messages/warning.png ) no-repeat 0px 50%; color: #5F4E01; } -.messages.information, .messages.info { background: #DFEBFB; border: 1px solid #82AEE7; } -.messages.information p, .messages.info p { background: url(../img/messages/help.png ) no-repeat 0px 50%; color: #064393; } -.messages.information a { text-decoration: underline; } +.messages { + +} \ No newline at end of file diff --git a/inc/config.php b/inc/config.php index c4898cc..b78147a 100644 --- a/inc/config.php +++ b/inc/config.php @@ -10,7 +10,7 @@ define ('POCHE_VERSION', '0.3'); define ('MODE_DEMO', FALSE); -define ('DEBUG_POCHE', TRUE); +define ('DEBUG_POCHE', FALSE); define ('CONVERT_LINKS_FOOTNOTES', FALSE); define ('REVERT_FORCED_PARAGRAPH_ELEMENTS', FALSE); define ('DOWNLOAD_PICTURES', FALSE); @@ -21,7 +21,7 @@ define ('LOCALE', './locale'); define ('CACHE', './cache'); define ('LANG', 'fr_FR.UTF8'); -$storage_type = 'sqlite'; # sqlite or file +$storage_type = 'sqlite'; # sqlite, file # /!\ Be careful if you change the lines below /!\ @@ -75,4 +75,6 @@ if(!$store->isInstalled()) } $_SESSION['login'] = (isset ($_SESSION['login'])) ? $_SESSION['login'] : $store->getLogin(); -$_SESSION['pass'] = (isset ($_SESSION['pass'])) ? $_SESSION['pass'] : $store->getPassword(); \ No newline at end of file +$_SESSION['pass'] = (isset ($_SESSION['pass'])) ? $_SESSION['pass'] : $store->getPassword(); + +pocheTools::initPhp(); \ No newline at end of file diff --git a/inc/poche/pocheCore.php b/inc/poche/pocheCore.php index e68696a..3e32c4a 100644 --- a/inc/poche/pocheCore.php +++ b/inc/poche/pocheCore.php @@ -134,10 +134,12 @@ function fetch_url_content($url) return FALSE; } -function display_view($view, $id = 0, $full_head = 'yes') +function display_view($view, $id = 0) { global $tpl, $store; + $tpl_vars = array(); + switch ($view) { case 'install': @@ -186,21 +188,25 @@ function display_view($view, $id = 0, $full_head = 'yes') break; default: # home view $entries = $store->getEntriesByView($view); - $tpl->assign('entries', $entries); + $tpl_vars = array( + 'entries' => $entries, + ); - if ($full_head == 'yes') { - $tpl->assign('load_all_js', 1); - $tpl->draw('head'); - $tpl->draw('home'); - } + // if ($full_head == 'yes') { + // $tpl->assign('load_all_js', 1); + // $tpl->draw('head'); + // $tpl->draw('home'); + // } - $tpl->draw('entries'); - if ($full_head == 'yes') { - $tpl->draw('js'); - $tpl->draw('footer'); - } + // $tpl->draw('entries'); + // if ($full_head == 'yes') { + // $tpl->draw('js'); + // $tpl->draw('footer'); + // } break; } + + return $tpl_vars; } /** diff --git a/index.php b/index.php index 4962639..f0a8aef 100644 --- a/index.php +++ b/index.php @@ -10,12 +10,12 @@ include dirname(__FILE__).'/inc/config.php'; -pocheTools::initPhp(); +$errors = array(); # XSRF protection with token if (!empty($_POST)) { if (!Session::isToken($_POST['token'])) { - die(_('Wrong token.')); + die(_('Wrong token')); } unset($_SESSION['tokens']); } @@ -23,10 +23,11 @@ if (!empty($_POST)) { $referer = empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER']; if (isset($_GET['login'])) { - // Login if (!empty($_POST['login']) && !empty($_POST['password'])) { if (Session::login($_SESSION['login'], $_SESSION['pass'], $_POST['login'], encode_string($_POST['password'] . $_POST['login']))) { pocheTools::logm('login successful'); + $errors[]['value'] = _('login successful'); + if (!empty($_POST['longlastingsession'])) { $_SESSION['longlastingsession'] = 31536000; $_SESSION['expires_on'] = time() + $_SESSION['longlastingsession']; @@ -35,11 +36,10 @@ if (isset($_GET['login'])) { session_set_cookie_params(0); // when browser closes } session_regenerate_id(true); - pocheTools::redirect($referer); } pocheTools::logm('login failed'); - die(_("Login failed !")); + $errors[]['value'] = _('Login failed !'); } else { pocheTools::logm('login failed'); } @@ -67,7 +67,7 @@ elseif (isset($_GET['config'])) { } # Traitement des paramètres et déclenchement des actions -$view = (isset ($_REQUEST['view'])) ? htmlentities($_REQUEST['view']) : 'index'; +$view = (isset ($_REQUEST['view'])) ? htmlentities($_REQUEST['view']) : 'home'; $full_head = (isset ($_REQUEST['full_head'])) ? htmlentities($_REQUEST['full_head']) : 'yes'; $action = (isset ($_REQUEST['action'])) ? htmlentities($_REQUEST['action']) : ''; $_SESSION['sort'] = (isset ($_REQUEST['sort'])) ? htmlentities($_REQUEST['sort']) : 'id'; @@ -75,20 +75,23 @@ $id = (isset ($_REQUEST['id'])) ? htmlspecialchars($_REQUEST['id $url = (isset ($_GET['url'])) ? $_GET['url'] : ''; $tpl_vars = array( - 'isLogged' => Session::isLogged(), 'referer' => $referer, 'view' => $view, 'poche_url' => pocheTools::getUrl(), 'demo' => MODE_DEMO, 'title' => _('poche, a read it later open source system'), + 'token' => Session::getToken(), + 'errors' => $errors, ); +$tpl_file = 'home.twig'; + if (Session::isLogged()) { action_to_do($action, $url, $id); - display_view($view, $id, $full_head); + $tpl_vars = array_merge($tpl_vars, display_view($view, $id)); } else { - $template = $twig->loadTemplate('login.twig'); + $tpl_file = 'login.twig'; } -echo $template->render($tpl_vars); \ No newline at end of file +echo $twig->render($tpl_file, $tpl_vars); \ No newline at end of file diff --git a/tpl/home.twig b/tpl/home.twig index 8b602a2..7b5b88a 100644 --- a/tpl/home.twig +++ b/tpl/home.twig @@ -1,21 +1,41 @@ - -
-

logo pochepoche

-
-
+{% extends "layout.twig" %} + +{% block title %}Home{% endblock %} {% block menu %} {% endblock %} {% block precontent %}
    -
  • by date
  • -
  • by title
  • +
  • by date
  • +
  • by title
{% endblock %} - {include="messages"} \ No newline at end of file +{% block content %} +
+ {% for entry in entries %} +
+ +

+ {{ entry.title|e }} +

+
+
    +
  • +
  • +
  • +
  • +
  • +
+
+
{{ entry.url|e }}
+
+
+ {% endfor %} +
+{% endblock %} \ No newline at end of file diff --git a/tpl/layout.twig b/tpl/layout.twig index c5f52bb..9dc83ef 100644 --- a/tpl/layout.twig +++ b/tpl/layout.twig @@ -9,50 +9,18 @@ {% block title %}{% endblock %} - poche - - - - - - - - - - - + {% include '_head.twig' %} + {% include '_bookmarklet.twig' %} -
-

logo pochepoche

-
+ {% include '_top.twig' %}
{% block menu %}{% endblock %} {% block precontent %}{% endblock %} + {% block messages %}{% endblock %} {% block content %}{% endblock %} {% block js %}{% endblock %}
- - + {% include '_footer.twig' %} \ No newline at end of file diff --git a/tpl/login.twig b/tpl/login.twig index 390718b..c95a5f0 100644 --- a/tpl/login.twig +++ b/tpl/login.twig @@ -1,5 +1,15 @@ {% extends "layout.twig" %} + {% block title %}Login{% endblock %} +{% block messages %} +
+
    + {% for error in errors %} +
  • {{ error.value|e }}
  • + {% endfor %} +
+
+{% endblock %} {% block content %}
From 2b840e0cfb63a453bea67a98541f3df9c273c5f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Sat, 3 Aug 2013 08:57:35 +0200 Subject: [PATCH 10/49] twig implementation --- inc/poche/pocheCore.php | 46 +++++++++++++++++++------------- index.php | 6 ++--- tpl/{config.html => config.twig} | 16 ++++++++++- tpl/footer.html | 7 ----- tpl/head.html | 42 ----------------------------- tpl/home.twig | 45 ++++++++++++++++++++++--------- tpl/install.twig | 2 +- tpl/js.html | 22 --------------- tpl/login.twig | 2 +- 9 files changed, 80 insertions(+), 108 deletions(-) rename tpl/{config.html => config.twig} (72%) delete mode 100644 tpl/footer.html delete mode 100644 tpl/head.html delete mode 100644 tpl/js.html diff --git a/inc/poche/pocheCore.php b/inc/poche/pocheCore.php index 3e32c4a..9cbcc07 100644 --- a/inc/poche/pocheCore.php +++ b/inc/poche/pocheCore.php @@ -134,9 +134,35 @@ function fetch_url_content($url) return FALSE; } +function get_tpl_file($view) +{ + $tpl_file = 'home.twig'; + switch ($view) + { + case 'install': + $tpl_file = 'install.twig'; + break; + case 'import'; + $tpl_file = 'import.twig'; + break; + case 'export': + $tpl_file = 'export.twig'; + break; + case 'config': + $tpl_file = 'config.twig'; + break; + case 'view': + $tpl_file = 'view.twig'; + break; + default: + break; + } + return $tpl_file; +} + function display_view($view, $id = 0) { - global $tpl, $store; + global $store; $tpl_vars = array(); @@ -155,12 +181,6 @@ function display_view($view, $id = 0) pocheTools::logm('export view'); break; case 'config': - $tpl->assign('load_all_js', 0); - $tpl->draw('head'); - $tpl->draw('home'); - $tpl->draw('config'); - $tpl->draw('js'); - $tpl->draw('footer'); pocheTools::logm('config view'); break; case 'view': @@ -191,18 +211,6 @@ function display_view($view, $id = 0) $tpl_vars = array( 'entries' => $entries, ); - - // if ($full_head == 'yes') { - // $tpl->assign('load_all_js', 1); - // $tpl->draw('head'); - // $tpl->draw('home'); - // } - - // $tpl->draw('entries'); - // if ($full_head == 'yes') { - // $tpl->draw('js'); - // $tpl->draw('footer'); - // } break; } diff --git a/index.php b/index.php index f0a8aef..81bd017 100644 --- a/index.php +++ b/index.php @@ -15,7 +15,8 @@ $errors = array(); # XSRF protection with token if (!empty($_POST)) { if (!Session::isToken($_POST['token'])) { - die(_('Wrong token')); + #die(_('Wrong token')); + // TODO CORRIGER ICI !!! } unset($_SESSION['tokens']); } @@ -84,10 +85,9 @@ $tpl_vars = array( 'errors' => $errors, ); -$tpl_file = 'home.twig'; - if (Session::isLogged()) { action_to_do($action, $url, $id); + $tpl_file = get_tpl_file($view); $tpl_vars = array_merge($tpl_vars, display_view($view, $id)); } else { diff --git a/tpl/config.html b/tpl/config.twig similarity index 72% rename from tpl/config.html rename to tpl/config.twig index 2c7df4f..10c481d 100644 --- a/tpl/config.html +++ b/tpl/config.twig @@ -1,3 +1,16 @@ +{% extends "layout.twig" %} + +{% block title %}{% trans "config" %}{% endblock %} +{% block menu %} + +{% endblock %} +{% block content %}

Bookmarklet

Thanks to the bookmarklet, you will be able to easily add a link to your poche. If you don't know how use a bookmarklet, have a look here.

@@ -24,4 +37,5 @@

Export

Click here to export your poche datas.

-
\ No newline at end of file +
+{% endblock %} \ No newline at end of file diff --git a/tpl/footer.html b/tpl/footer.html deleted file mode 100644 index b8bd755..0000000 --- a/tpl/footer.html +++ /dev/null @@ -1,7 +0,0 @@ -
- - - - \ No newline at end of file diff --git a/tpl/head.html b/tpl/head.html deleted file mode 100644 index dfb1278..0000000 --- a/tpl/head.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - {$title} - - - - - - - - - - - - \ No newline at end of file diff --git a/tpl/home.twig b/tpl/home.twig index 7b5b88a..c79d427 100644 --- a/tpl/home.twig +++ b/tpl/home.twig @@ -1,19 +1,18 @@ {% extends "layout.twig" %} - -{% block title %}Home{% endblock %} +{% block title %}{% trans "home" %}{% endblock %} {% block menu %} {% endblock %} {% block precontent %}
    -
  • by date
  • -
  • by title
  • +
  • {% trans "by date" %}
  • +
  • {% trans "by title" %}
{% endblock %} {% block content %} @@ -27,9 +26,9 @@
  • -
  • -
  • -
  • +
  • +
  • +
@@ -38,4 +37,26 @@
{% endfor %} +{% endblock %} + +{% block js %} + + + + {% endblock %} \ No newline at end of file diff --git a/tpl/install.twig b/tpl/install.twig index 4342df2..8bcede0 100644 --- a/tpl/install.twig +++ b/tpl/install.twig @@ -1,5 +1,5 @@ {% extends "layout.twig" %} -{% block title %}Installation{% endblock %} +{% block title %}{% trans "installation" %}{% endblock %} {% block content %}
diff --git a/tpl/js.html b/tpl/js.html deleted file mode 100644 index 3a51af6..0000000 --- a/tpl/js.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - {if="$load_all_js == '1'"} - - - {/if} \ No newline at end of file diff --git a/tpl/login.twig b/tpl/login.twig index c95a5f0..d108053 100644 --- a/tpl/login.twig +++ b/tpl/login.twig @@ -1,6 +1,6 @@ {% extends "layout.twig" %} -{% block title %}Login{% endblock %} +{% block title %}{% trans "login to your poche" %}{% endblock %} {% block messages %}
    From 4f5b44bd3bd490309eb2ba7b44df4769816ba729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Sat, 3 Aug 2013 19:26:54 +0200 Subject: [PATCH 11/49] twig implementation --- inc/3rdparty/Twig/Extensions/Autoloader.php | 45 - .../Twig/Extensions/Extension/Debug.php | 34 - .../Twig/Extensions/Extension/I18n.php | 44 - .../Twig/Extensions/Extension/Intl.php | 66 - .../Twig/Extensions/Extension/Text.php | 109 - inc/3rdparty/Twig/Extensions/Grammar.php | 30 - .../Twig/Extensions/Grammar/Arguments.php | 22 - .../Twig/Extensions/Grammar/Array.php | 22 - inc/3rdparty/Twig/Extensions/Grammar/Body.php | 39 - .../Twig/Extensions/Grammar/Boolean.php | 24 - .../Twig/Extensions/Grammar/Constant.php | 37 - .../Twig/Extensions/Grammar/Expression.php | 22 - inc/3rdparty/Twig/Extensions/Grammar/Hash.php | 22 - .../Twig/Extensions/Grammar/Number.php | 24 - .../Twig/Extensions/Grammar/Optional.php | 69 - .../Twig/Extensions/Grammar/Switch.php | 24 - inc/3rdparty/Twig/Extensions/Grammar/Tag.php | 56 - .../Twig/Extensions/GrammarInterface.php | 18 - inc/3rdparty/Twig/Extensions/Node/Debug.php | 69 - inc/3rdparty/Twig/Extensions/Node/Trans.php | 133 - .../Twig/Extensions/SimpleTokenParser.php | 132 - .../Twig/Extensions/TokenParser/Debug.php | 42 - .../Twig/Extensions/TokenParser/Trans.php | 80 - inc/3rdparty/Twig/Gettext/Extractor.php | 95 - .../Twig/Gettext/Loader/Filesystem.php | 58 - .../Routing/Generator/UrlGenerator.php | 39 - .../Twig/Gettext/Test/ExtractorTest.php | 123 - .../Gettext/Test/Fixtures/twig/empty.twig | 1 - .../Gettext/Test/Fixtures/twig/plural.twig | 5 - .../Gettext/Test/Fixtures/twig/singular.twig | 9 - inc/config.php | 5 +- index.php | 52 +- tpl/_head.twig | 16 +- tpl/_top.twig | 2 +- tpl/config.twig | 28 +- {css => tpl/css}/knacss.css | 0 {css => tpl/css}/style-dark.css | 0 {css => tpl/css}/style-light.css | 0 {css => tpl/css}/style.css | 0 tpl/entries.html | 21 - tpl/home.twig | 21 +- .../apple-touch-icon-144x144-precomposed.png | Bin .../apple-touch-icon-72x72-precomposed.png | Bin {img => tpl/img}/apple-touch-icon.png | Bin {img => tpl/img}/dark/checkmark-off.png | Bin {img => tpl/img}/dark/checkmark-on.png | Bin {img => tpl/img}/dark/down.png | Bin {img => tpl/img}/dark/logo.png | Bin {img => tpl/img}/dark/remove.png | Bin {img => tpl/img}/dark/star-off.png | Bin {img => tpl/img}/dark/star-on.png | Bin {img => tpl/img}/dark/up.png | Bin {img => tpl/img}/down.png | Bin {img => tpl/img}/favicon.ico | Bin {img => tpl/img}/light/checkmark-off.png | Bin {img => tpl/img}/light/checkmark-on.png | Bin {img => tpl/img}/light/remove.png | Bin {img => tpl/img}/light/star-off.png | Bin {img => tpl/img}/light/star-on.png | Bin {img => tpl/img}/logo.png | Bin {img => tpl/img}/messages/close.png | Bin {img => tpl/img}/messages/cross.png | Bin {img => tpl/img}/messages/help.png | Bin {img => tpl/img}/messages/tick.png | Bin {img => tpl/img}/messages/warning.png | Bin {img => tpl/img}/up.png | Bin {js => tpl/js}/jquery-1.9.1.min.js | 0 {js => tpl/js}/jquery.masonry.min.js | 0 {js => tpl/js}/poche.js | 0 tpl/login.twig | 8 +- vendor/autoload.php | 7 + vendor/bin/twig-gettext-extractor | 1 + vendor/composer/ClassLoader.php | 246 ++ vendor/composer/autoload_classmap.php | 13 + vendor/composer/autoload_files.php | 10 + vendor/composer/autoload_namespaces.php | 22 + vendor/composer/autoload_real.php | 47 + vendor/composer/installed.json | 747 ++++++ .../Component/EventDispatcher/.gitignore | 4 + .../Component/EventDispatcher/CHANGELOG.md | 16 + .../ContainerAwareEventDispatcher.php | 202 ++ .../TraceableEventDispatcherInterface.php | 32 + .../Component/EventDispatcher/Event.php | 121 + .../EventDispatcher/EventDispatcher.php | 185 ++ .../EventDispatcherInterface.php | 96 + .../EventSubscriberInterface.php | 50 + .../EventDispatcher/GenericEvent.php | 186 ++ .../ImmutableEventDispatcher.php | 92 + .../Symfony/Component/EventDispatcher/LICENSE | 19 + .../Component/EventDispatcher/README.md | 25 + .../ContainerAwareEventDispatcherTest.php | 257 ++ .../Tests/EventDispatcherTest.php | 320 +++ .../EventDispatcher/Tests/EventTest.php | 84 + .../Tests/GenericEventTest.php | 140 ++ .../Tests/ImmutableEventDispatcherTest.php | 106 + .../Component/EventDispatcher/composer.json | 38 + .../EventDispatcher/phpunit.xml.dist | 30 + .../Symfony/Component/Filesystem/.gitignore | 4 + .../Symfony/Component/Filesystem/CHANGELOG.md | 18 + .../Exception/ExceptionInterface.php | 24 + .../Filesystem/Exception/IOException.php | 24 + .../Component/Filesystem/Filesystem.php | 471 ++++ .../Symfony/Component/Filesystem/LICENSE | 19 + .../Symfony/Component/Filesystem/README.md | 45 + .../Filesystem/Tests/FilesystemTest.php | 982 ++++++++ .../Component/Filesystem/composer.json | 31 + .../Component/Filesystem/phpunit.xml.dist | 28 + .../form/Symfony/Component/Form/.gitignore | 4 + .../Component/Form/AbstractExtension.php | 195 ++ .../Component/Form/AbstractRendererEngine.php | 206 ++ .../Symfony/Component/Form/AbstractType.php | 56 + .../Component/Form/AbstractTypeExtension.php | 48 + .../form/Symfony/Component/Form/Button.php | 436 ++++ .../Symfony/Component/Form/ButtonBuilder.php | 864 +++++++ .../Component/Form/ButtonTypeInterface.php | 21 + .../form/Symfony/Component/Form/CHANGELOG.md | 238 ++ .../Component/Form/CallbackTransformer.php | 70 + .../Component/Form/ClickableInterface.php | 27 + .../Component/Form/DataMapperInterface.php | 38 + .../Form/DataTransformerInterface.php | 77 + .../Form/Exception/AlreadyBoundException.php | 22 + .../Exception/AlreadySubmittedException.php | 22 + .../Form/Exception/BadMethodCallException.php | 21 + .../Form/Exception/ErrorMappingException.php | 16 + .../Form/Exception/ExceptionInterface.php | 21 + .../Exception/InvalidArgumentException.php | 21 + .../InvalidConfigurationException.php | 16 + .../Form/Exception/LogicException.php | 21 + .../Form/Exception/OutOfBoundsException.php | 21 + .../Form/Exception/RuntimeException.php | 21 + .../Form/Exception/StringCastException.php | 16 + .../TransformationFailedException.php | 21 + .../Exception/UnexpectedTypeException.php | 20 + .../Extension/Core/ChoiceList/ChoiceList.php | 510 ++++ .../Core/ChoiceList/ChoiceListInterface.php | 149 ++ .../Core/ChoiceList/LazyChoiceList.php | 149 ++ .../Core/ChoiceList/ObjectChoiceList.php | 184 ++ .../Core/ChoiceList/SimpleChoiceList.php | 164 ++ .../Form/Extension/Core/CoreExtension.php | 59 + .../Core/DataMapper/PropertyPathMapper.php | 92 + .../ArrayToPartsTransformer.php | 86 + .../BaseDateTimeTransformer.php | 52 + .../BooleanToStringTransformer.php | 85 + .../ChoiceToBooleanArrayTransformer.php | 118 + .../ChoiceToValueTransformer.php | 62 + .../ChoicesToBooleanArrayTransformer.php | 117 + .../ChoicesToValuesTransformer.php | 83 + .../DataTransformer/DataTransformerChain.php | 86 + .../DateTimeToArrayTransformer.php | 184 ++ .../DateTimeToLocalizedStringTransformer.php | 169 ++ .../DateTimeToRfc3339Transformer.php | 82 + .../DateTimeToStringTransformer.php | 231 ++ .../DateTimeToTimestampTransformer.php | 89 + .../IntegerToLocalizedStringTransformer.php | 53 + .../MoneyToLocalizedStringTransformer.php | 90 + .../NumberToLocalizedStringTransformer.php | 184 ++ .../PercentToLocalizedStringTransformer.php | 149 ++ .../ValueToDuplicatesTransformer.php | 91 + .../FixCheckboxInputListener.php | 62 + .../EventListener/FixRadioInputListener.php | 66 + .../EventListener/FixUrlProtocolListener.php | 56 + .../EventListener/MergeCollectionListener.php | 137 + .../Core/EventListener/ResizeFormListener.php | 173 ++ .../Core/EventListener/TrimListener.php | 55 + .../Form/Extension/Core/Type/BaseType.php | 121 + .../Form/Extension/Core/Type/BirthdayType.php | 44 + .../Form/Extension/Core/Type/ButtonType.php | 38 + .../Form/Extension/Core/Type/CheckboxType.php | 67 + .../Form/Extension/Core/Type/ChoiceType.php | 274 ++ .../Extension/Core/Type/CollectionType.php | 103 + .../Form/Extension/Core/Type/CountryType.php | 45 + .../Form/Extension/Core/Type/CurrencyType.php | 45 + .../Form/Extension/Core/Type/DateTimeType.php | 281 +++ .../Form/Extension/Core/Type/DateType.php | 309 +++ .../Form/Extension/Core/Type/EmailType.php | 33 + .../Form/Extension/Core/Type/FileType.php | 61 + .../Form/Extension/Core/Type/FormType.php | 214 ++ .../Form/Extension/Core/Type/HiddenType.php | 40 + .../Form/Extension/Core/Type/IntegerType.php | 68 + .../Form/Extension/Core/Type/LanguageType.php | 45 + .../Form/Extension/Core/Type/LocaleType.php | 46 + .../Form/Extension/Core/Type/MoneyType.php | 111 + .../Form/Extension/Core/Type/NumberType.php | 66 + .../Form/Extension/Core/Type/PasswordType.php | 57 + .../Form/Extension/Core/Type/PercentType.php | 55 + .../Form/Extension/Core/Type/RadioType.php | 33 + .../Form/Extension/Core/Type/RepeatedType.php | 67 + .../Form/Extension/Core/Type/ResetType.php | 39 + .../Form/Extension/Core/Type/SearchType.php | 33 + .../Form/Extension/Core/Type/SubmitType.php | 46 + .../Form/Extension/Core/Type/TextType.php | 36 + .../Form/Extension/Core/Type/TextareaType.php | 43 + .../Form/Extension/Core/Type/TimeType.php | 225 ++ .../Form/Extension/Core/Type/TimezoneType.php | 86 + .../Form/Extension/Core/Type/UrlType.php | 54 + .../Form/Extension/Core/View/ChoiceView.php | 55 + .../Form/Extension/Csrf/CsrfExtension.php | 64 + .../CsrfProvider/CsrfProviderInterface.php | 49 + .../Csrf/CsrfProvider/DefaultCsrfProvider.php | 78 + .../Csrf/CsrfProvider/SessionCsrfProvider.php | 57 + .../EventListener/CsrfValidationListener.php | 115 + .../Csrf/Type/FormTypeCsrfExtension.php | 129 + .../DependencyInjectionExtension.php | 101 + .../EventListener/BindRequestListener.php | 91 + .../HttpFoundationExtension.php | 29 + .../HttpFoundationRequestHandler.php | 79 + .../Type/FormTypeHttpFoundationExtension.php | 56 + .../Templating/TemplatingExtension.php | 33 + .../Templating/TemplatingRendererEngine.php | 125 + .../Extension/Validator/Constraints/Form.php | 33 + .../Validator/Constraints/FormValidator.php | 236 ++ .../EventListener/ValidationListener.php | 68 + .../Validator/Type/BaseValidatorExtension.php | 56 + .../Type/FormTypeValidatorExtension.php | 84 + .../Type/RepeatedTypeValidatorExtension.php | 45 + .../Type/SubmitTypeValidatorExtension.php | 28 + .../Extension/Validator/Util/ServerParams.php | 64 + .../Validator/ValidatorExtension.php | 57 + .../Validator/ValidatorTypeGuesser.php | 286 +++ .../Validator/ViolationMapper/MappingRule.php | 106 + .../ViolationMapper/RelativePath.php | 45 + .../ViolationMapper/ViolationMapper.php | 299 +++ .../ViolationMapperInterface.php | 33 + .../ViolationMapper/ViolationPath.php | 250 ++ .../ViolationMapper/ViolationPathIterator.php | 30 + .../form/Symfony/Component/Form/Form.php | 1046 ++++++++ .../Symfony/Component/Form/FormBuilder.php | 275 ++ .../Component/Form/FormBuilderInterface.php | 87 + .../Component/Form/FormConfigBuilder.php | 919 +++++++ .../Form/FormConfigBuilderInterface.php | 287 +++ .../Component/Form/FormConfigInterface.php | 243 ++ .../form/Symfony/Component/Form/FormError.php | 105 + .../form/Symfony/Component/Form/FormEvent.php | 65 + .../Symfony/Component/Form/FormEvents.php | 49 + .../Component/Form/FormExtensionInterface.php | 63 + .../Symfony/Component/Form/FormFactory.php | 156 ++ .../Component/Form/FormFactoryBuilder.php | 162 ++ .../Form/FormFactoryBuilderInterface.php | 108 + .../Component/Form/FormFactoryInterface.php | 109 + .../Symfony/Component/Form/FormInterface.php | 288 +++ .../Symfony/Component/Form/FormRegistry.php | 180 ++ .../Component/Form/FormRegistryInterface.php | 57 + .../Symfony/Component/Form/FormRenderer.php | 304 +++ .../Form/FormRendererEngineInterface.php | 150 ++ .../Component/Form/FormRendererInterface.php | 103 + .../Form/FormTypeExtensionInterface.php | 75 + .../Component/Form/FormTypeGuesserChain.php | 104 + .../Form/FormTypeGuesserInterface.php | 64 + .../Component/Form/FormTypeInterface.php | 95 + .../form/Symfony/Component/Form/FormView.php | 159 ++ .../form/Symfony/Component/Form/Forms.php | 185 ++ .../Symfony/Component/Form/Guess/Guess.php | 113 + .../Component/Form/Guess/TypeGuess.php | 70 + .../Component/Form/Guess/ValueGuess.php | 50 + .../form/Symfony/Component/Form/LICENSE | 19 + .../Component/Form/NativeRequestHandler.php | 194 ++ .../Component/Form/PreloadedExtension.php | 97 + .../form/Symfony/Component/Form/README.md | 26 + .../Form/RequestHandlerInterface.php | 28 + .../Component/Form/ResolvedFormType.php | 284 +++ .../Form/ResolvedFormTypeFactory.php | 26 + .../Form/ResolvedFormTypeFactoryInterface.php | 38 + .../Form/ResolvedFormTypeInterface.php | 106 + .../Form/Resources/config/validation.xml | 13 + .../Resources/translations/validators.ar.xlf | 19 + .../Resources/translations/validators.bg.xlf | 19 + .../Resources/translations/validators.ca.xlf | 19 + .../Resources/translations/validators.cs.xlf | 19 + .../Resources/translations/validators.da.xlf | 19 + .../Resources/translations/validators.de.xlf | 19 + .../Resources/translations/validators.el.xlf | 19 + .../Resources/translations/validators.en.xlf | 19 + .../Resources/translations/validators.es.xlf | 19 + .../Resources/translations/validators.et.xlf | 19 + .../Resources/translations/validators.eu.xlf | 19 + .../Resources/translations/validators.fa.xlf | 19 + .../Resources/translations/validators.fi.xlf | 19 + .../Resources/translations/validators.fr.xlf | 19 + .../Resources/translations/validators.gl.xlf | 19 + .../Resources/translations/validators.he.xlf | 19 + .../Resources/translations/validators.hr.xlf | 19 + .../Resources/translations/validators.hu.xlf | 19 + .../Resources/translations/validators.hy.xlf | 19 + .../Resources/translations/validators.id.xlf | 19 + .../Resources/translations/validators.it.xlf | 19 + .../Resources/translations/validators.ja.xlf | 19 + .../Resources/translations/validators.lb.xlf | 19 + .../Resources/translations/validators.lt.xlf | 19 + .../Resources/translations/validators.lv.xlf | 19 + .../Resources/translations/validators.mn.xlf | 19 + .../Resources/translations/validators.nb.xlf | 19 + .../Resources/translations/validators.nl.xlf | 19 + .../Resources/translations/validators.pl.xlf | 19 + .../Resources/translations/validators.pt.xlf | 19 + .../translations/validators.pt_BR.xlf | 19 + .../Resources/translations/validators.ro.xlf | 19 + .../Resources/translations/validators.ru.xlf | 19 + .../Resources/translations/validators.sk.xlf | 19 + .../Resources/translations/validators.sl.xlf | 19 + .../translations/validators.sr_Cyrl.xlf | 19 + .../translations/validators.sr_Latn.xlf | 19 + .../Resources/translations/validators.sv.xlf | 19 + .../Resources/translations/validators.ua.xlf | 19 + .../translations/validators.zh_CN.xlf | 19 + .../Component/Form/ReversedTransformer.php | 55 + .../Symfony/Component/Form/SubmitButton.php | 52 + .../Component/Form/SubmitButtonBuilder.php | 30 + .../Form/SubmitButtonTypeInterface.php | 21 + .../Form/Test/DeprecationErrorHandler.php | 42 + .../Form/Test/FormBuilderInterface.php | 16 + .../Form/Test/FormIntegrationTestCase.php | 41 + .../Component/Form/Test/FormInterface.php | 16 + .../Form/Test/FormPerformanceTestCase.php | 70 + .../Component/Form/Test/TypeTestCase.php | 41 + .../Form/Tests/AbstractDivLayoutTest.php | 732 ++++++ .../Form/Tests/AbstractExtensionTest.php | 43 + .../Component/Form/Tests/AbstractFormTest.php | 147 ++ .../Form/Tests/AbstractLayoutTest.php | 1876 ++++++++++++++ .../Form/Tests/AbstractRequestHandlerTest.php | 280 +++ .../Form/Tests/AbstractTableLayoutTest.php | 509 ++++ .../Tests/CompoundFormPerformanceTest.php | 48 + .../Component/Form/Tests/CompoundFormTest.php | 759 ++++++ .../Core/ChoiceList/ChoiceListTest.php | 200 ++ .../Core/ChoiceList/LazyChoiceListTest.php | 116 + .../Core/ChoiceList/ObjectChoiceListTest.php | 212 ++ .../Core/ChoiceList/SimpleChoiceListTest.php | 188 ++ .../DataMapper/PropertyPathMapperTest.php | 319 +++ .../ArrayToPartsTransformerTest.php | 149 ++ .../BooleanToStringTransformerTest.php | 60 + .../ChoiceToValueTransformerTest.php | 76 + .../ChoicesToValuesTransformerTest.php | 76 + .../DataTransformerChainTest.php | 53 + .../Core/DataTransformer/DateTimeTestCase.php | 20 + .../DateTimeToArrayTransformerTest.php | 512 ++++ ...teTimeToLocalizedStringTransformerTest.php | 275 ++ .../DateTimeToRfc3339TransformerTest.php | 132 + .../DateTimeToStringTransformerTest.php | 181 ++ .../DateTimeToTimestampTransformerTest.php | 104 + ...ntegerToLocalizedStringTransformerTest.php | 115 + .../MoneyToLocalizedStringTransformerTest.php | 74 + ...NumberToLocalizedStringTransformerTest.php | 393 +++ ...ercentToLocalizedStringTransformerTest.php | 114 + .../ValueToDuplicatesTransformerTest.php | 120 + .../FixRadioInputListenerTest.php | 106 + .../FixUrlProtocolListenerTest.php | 61 + .../Core/EventListener/Fixtures/randomhash | Bin 0 -> 35 bytes ...MergeCollectionListenerArrayObjectTest.php | 27 + .../MergeCollectionListenerArrayTest.php | 27 + ...ollectionListenerCustomArrayObjectTest.php | 28 + .../MergeCollectionListenerTest.php | 259 ++ .../EventListener/ResizeFormListenerTest.php | 255 ++ .../Core/EventListener/TrimListenerTest.php | 79 + .../Extension/Core/Type/BaseTypeTest.php | 135 + .../Extension/Core/Type/ButtonTypeTest.php | 28 + .../Extension/Core/Type/CheckboxTypeTest.php | 162 ++ .../Core/Type/ChoiceTypePerformanceTest.php | 38 + .../Extension/Core/Type/ChoiceTypeTest.php | 949 +++++++ .../Core/Type/CollectionTypeTest.php | 200 ++ .../Extension/Core/Type/CountryTypeTest.php | 52 + .../Extension/Core/Type/CurrencyTypeTest.php | 37 + .../Extension/Core/Type/DateTimeTypeTest.php | 477 ++++ .../Extension/Core/Type/DateTypeTest.php | 781 ++++++ .../Extension/Core/Type/FileTypeTest.php | 83 + .../Extension/Core/Type/FormTypeTest.php | 578 +++++ .../Extension/Core/Type/IntegerTypeTest.php | 34 + .../Extension/Core/Type/LanguageTypeTest.php | 47 + .../Extension/Core/Type/LocaleTypeTest.php | 36 + .../Extension/Core/Type/MoneyTypeTest.php | 59 + .../Extension/Core/Type/NumberTypeTest.php | 63 + .../Extension/Core/Type/PasswordTypeTest.php | 51 + .../Extension/Core/Type/RepeatedTypeTest.php | 149 ++ .../Extension/Core/Type/SubmitTypeTest.php | 54 + .../Extension/Core/Type/TimeTypeTest.php | 649 +++++ .../Extension/Core/Type/TimezoneTypeTest.php | 30 + .../Extension/Core/Type/TypeTestCase.php | 21 + .../Tests/Extension/Core/Type/UrlTypeTest.php | 61 + .../CsrfProvider/DefaultCsrfProviderTest.php | 81 + .../CsrfProvider/SessionCsrfProviderTest.php | 75 + .../CsrfValidationListenerTest.php | 78 + .../Csrf/Type/FormTypeCsrfExtensionTest.php | 301 +++ .../EventListener/BindRequestListenerTest.php | 286 +++ .../HttpFoundationRequestHandlerTest.php | 54 + .../Constraints/FormValidatorTest.php | 748 ++++++ .../EventListener/ValidationListenerTest.php | 145 ++ .../Type/FormTypeValidatorExtensionTest.php | 85 + .../Extension/Validator/Type/TypeTestCase.php | 49 + .../Validator/Util/ServerParamsTest.php | 46 + .../ViolationMapper/ViolationMapperTest.php | 1481 +++++++++++ .../ViolationMapper/ViolationPathTest.php | 245 ++ .../Tests/Fixtures/AlternatingRowType.php | 27 + .../Component/Form/Tests/Fixtures/Author.php | 71 + .../Form/Tests/Fixtures/AuthorType.php | 30 + .../Form/Tests/Fixtures/CustomArrayObject.php | 70 + .../Tests/Fixtures/FixedDataTransformer.php | 45 + .../Tests/Fixtures/FixedFilterListener.php | 66 + .../Form/Tests/Fixtures/FooSubType.php | 32 + .../Fixtures/FooSubTypeWithParentInstance.php | 32 + .../Component/Form/Tests/Fixtures/FooType.php | 32 + .../Tests/Fixtures/FooTypeBarExtension.php | 35 + .../Tests/Fixtures/FooTypeBazExtension.php | 28 + .../Form/Tests/Fixtures/TestExtension.php | 72 + .../Symfony/Component/Form/Tests/Fixtures/foo | 0 .../Component/Form/Tests/FormBuilderTest.php | 232 ++ .../Component/Form/Tests/FormConfigTest.php | 148 ++ .../Form/Tests/FormFactoryBuilderTest.php | 59 + .../Component/Form/Tests/FormFactoryTest.php | 506 ++++ .../Form/Tests/FormIntegrationTestCase.php | 21 + .../Form/Tests/FormPerformanceTestCase.php | 21 + .../Component/Form/Tests/FormRegistryTest.php | 243 ++ .../Component/Form/Tests/FormRendererTest.php | 27 + .../Component/Form/Tests/Guess/GuessTest.php | 36 + .../Form/Tests/NativeRequestHandlerTest.php | 219 ++ .../Form/Tests/ResolvedFormTypeTest.php | 280 +++ .../Component/Form/Tests/SimpleFormTest.php | 1045 ++++++++ .../Symfony/Component/Form/Util/FormUtil.php | 42 + .../Form/Util/InheritDataAwareIterator.php | 35 + .../Form/Util/VirtualFormAwareIterator.php | 50 + .../form/Symfony/Component/Form/composer.json | 43 + .../Symfony/Component/Form/phpunit.xml.dist | 29 + .../icu/Symfony/Component/Icu/.gitignore | 3 + .../Component/Icu/IcuCurrencyBundle.php | 28 + .../icu/Symfony/Component/Icu/IcuData.php | 66 + .../Component/Icu/IcuLanguageBundle.php | 28 + .../Symfony/Component/Icu/IcuLocaleBundle.php | 28 + .../Symfony/Component/Icu/IcuRegionBundle.php | 28 + .../symfony/icu/Symfony/Component/Icu/LICENSE | 19 + .../icu/Symfony/Component/Icu/README.md | 18 + .../Component/Icu/Resources/data/curr/en.php | 1791 +++++++++++++ .../Component/Icu/Resources/data/lang/en.php | 750 ++++++ .../Icu/Resources/data/locales/en.php | 305 +++ .../Icu/Resources/data/region/en.php | 273 ++ .../Component/Icu/Resources/data/version.txt | 1 + .../Icu/Tests/IcuIntegrationTest.php | 55 + .../icu/Symfony/Component/Icu/composer.json | 26 + .../Symfony/Component/Icu/phpunit.xml.dist | 29 + .../intl/Symfony/Component/Intl/.gitignore | 3 + .../Symfony/Component/Intl/CONTRIBUTING.md | 91 + .../Component/Intl/Collator/Collator.php | 295 +++ .../DateFormat/AmPmTransformer.php | 46 + .../DateFormat/DayOfWeekTransformer.php | 59 + .../DateFormat/DayOfYearTransformer.php | 46 + .../DateFormat/DayTransformer.php | 46 + .../DateFormat/FullTransformer.php | 356 +++ .../DateFormat/Hour1200Transformer.php | 62 + .../DateFormat/Hour1201Transformer.php | 62 + .../DateFormat/Hour2400Transformer.php | 61 + .../DateFormat/Hour2401Transformer.php | 64 + .../DateFormat/HourTransformer.php | 30 + .../DateFormat/MinuteTransformer.php | 48 + .../DateFormat/MonthTransformer.php | 143 ++ .../DateFormat/QuarterTransformer.php | 64 + .../DateFormat/SecondTransformer.php | 48 + .../DateFormat/TimeZoneTransformer.php | 99 + .../DateFormatter/DateFormat/Transformer.php | 64 + .../DateFormat/YearTransformer.php | 50 + .../Intl/DateFormatter/IntlDateFormatter.php | 631 +++++ .../Intl/Exception/BadMethodCallException.php | 21 + .../Intl/Exception/ExceptionInterface.php | 21 + .../Exception/InvalidArgumentException.php | 21 + .../MethodArgumentNotImplementedException.php | 32 + ...odArgumentValueNotImplementedException.php | 41 + .../MethodNotImplementedException.php | 28 + .../Exception/NotImplementedException.php | 32 + .../Intl/Exception/OutOfBoundsException.php | 21 + .../Intl/Exception/RuntimeException.php | 21 + .../Component/Intl/Globals/IntlGlobals.php | 137 + .../intl/Symfony/Component/Intl/Intl.php | 211 ++ .../intl/Symfony/Component/Intl/LICENSE | 19 + .../Symfony/Component/Intl/Locale/Locale.php | 317 +++ .../Intl/NumberFormatter/NumberFormatter.php | 891 +++++++ .../intl/Symfony/Component/Intl/README.md | 25 + .../Intl/ResourceBundle/AbstractBundle.php | 71 + .../Compiler/BundleCompiler.php | 71 + .../Compiler/BundleCompilerInterface.php | 29 + .../Intl/ResourceBundle/CurrencyBundle.php | 94 + .../CurrencyBundleInterface.php | 74 + .../Intl/ResourceBundle/LanguageBundle.php | 115 + .../LanguageBundleInterface.php | 64 + .../Intl/ResourceBundle/LocaleBundle.php | 52 + .../ResourceBundle/LocaleBundleInterface.php | 41 + .../Reader/AbstractBundleReader.php | 42 + .../Reader/BinaryBundleReader.php | 51 + .../Reader/BufferedBundleReader.php | 62 + .../Reader/BundleReaderInterface.php | 40 + .../ResourceBundle/Reader/PhpBundleReader.php | 61 + .../Reader/StructuredBundleReader.php | 113 + .../StructuredBundleReaderInterface.php | 50 + .../Intl/ResourceBundle/RegionBundle.php | 52 + .../ResourceBundle/RegionBundleInterface.php | 41 + .../ResourceBundleInterface.php | 27 + .../Transformer/BundleTransformer.php | 96 + .../Transformer/CompilationContext.php | 97 + .../CompilationContextInterface.php | 56 + .../Rule/CurrencyBundleTransformationRule.php | 94 + .../Rule/LanguageBundleTransformationRule.php | 71 + .../Rule/LocaleBundleTransformationRule.php | 251 ++ .../Rule/RegionBundleTransformationRule.php | 70 + .../Rule/TransformationRuleInterface.php | 70 + .../Transformer/StubbingContext.php | 80 + .../Transformer/StubbingContextInterface.php | 46 + .../Util/ArrayAccessibleResourceBundle.php | 79 + .../Util/RecursiveArrayAccess.php | 33 + .../Intl/ResourceBundle/Util/RingBuffer.php | 88 + .../Writer/BundleWriterInterface.php | 29 + .../ResourceBundle/Writer/PhpBundleWriter.php | 50 + .../Writer/TextBundleWriter.php | 202 ++ .../Component/Intl/Resources/bin/autoload.php | 18 + .../Component/Intl/Resources/bin/common.php | 69 + .../Resources/bin/copy-stubs-to-component.php | 63 + .../Intl/Resources/bin/create-stubs.php | 112 + .../Intl/Resources/bin/icu-version.php | 18 + .../Component/Intl/Resources/bin/icu.ini | 9 + .../Intl/Resources/bin/test-compat.php | 56 + .../Resources/bin/update-icu-component.php | 212 ++ .../Resources/bin/util/test-compat-helper.php | 23 + .../Intl/Resources/stubs/Collator.php | 21 + .../Resources/stubs/IntlDateFormatter.php | 21 + .../Component/Intl/Resources/stubs/Locale.php | 21 + .../Intl/Resources/stubs/NumberFormatter.php | 21 + .../Intl/Resources/stubs/functions.php | 80 + .../Tests/Collator/AbstractCollatorTest.php | 62 + .../Intl/Tests/Collator/CollatorTest.php | 109 + .../Collator/Verification/CollatorTest.php | 37 + .../AbstractIntlDateFormatterTest.php | 932 +++++++ .../DateFormatter/IntlDateFormatterTest.php | 220 ++ .../Verification/IntlDateFormatterTest.php | 64 + .../Tests/Globals/AbstractIntlGlobalsTest.php | 41 + .../Intl/Tests/Globals/IntlGlobalsTest.php | 22 + .../Globals/Verification/IntlGlobalsTest.php | 36 + .../Intl/Tests/Locale/AbstractLocaleTest.php | 29 + .../Intl/Tests/Locale/LocaleTest.php | 159 ++ .../Tests/Locale/Verification/LocaleTest.php | 38 + .../AbstractNumberFormatterTest.php | 707 ++++++ .../NumberFormatter/NumberFormatterTest.php | 239 ++ .../Verification/NumberFormatterTest.php | 54 + .../ResourceBundle/AbstractBundleTest.php | 55 + .../ResourceBundle/CurrencyBundleTest.php | 98 + .../ResourceBundle/LanguageBundleTest.php | 197 ++ .../Tests/ResourceBundle/LocaleBundleTest.php | 64 + .../Reader/AbstractBundleReaderTest.php | 64 + .../Reader/BinaryBundleReaderTest.php | 58 + .../Reader/Fixtures/NotAFile/en.php/.gitkeep | 0 .../ResourceBundle/Reader/Fixtures/en.php | 14 + .../ResourceBundle/Reader/Fixtures/en.res | Bin 0 -> 84 bytes .../ResourceBundle/Reader/Fixtures/en.txt | 3 + .../Reader/PhpBundleReaderTest.php | 63 + .../Reader/StructuredBundleReaderTest.php | 223 ++ .../Tests/ResourceBundle/RegionBundleTest.php | 63 + .../ResourceBundle/Util/RingBufferTest.php | 101 + .../ResourceBundle/Writer/Fixtures/en.php | 23 + .../ResourceBundle/Writer/Fixtures/en.res | Bin 0 -> 316 bytes .../ResourceBundle/Writer/Fixtures/en.txt | 23 + .../Writer/PhpBundleWriterTest.php | 62 + .../Writer/TextBundleWriterTest.php | 67 + .../Intl/Tests/Util/IcuVersionTest.php | 111 + .../Component/Intl/Tests/Util/VersionTest.php | 87 + .../Component/Intl/Util/IcuVersion.php | 105 + .../Component/Intl/Util/IntlTestHelper.php | 128 + .../Symfony/Component/Intl/Util/SvnCommit.php | 66 + .../Component/Intl/Util/SvnRepository.php | 141 ++ .../Symfony/Component/Intl/Util/Version.php | 96 + .../intl/Symfony/Component/Intl/composer.json | 48 + .../Symfony/Component/Intl/phpunit.xml.dist | 29 + .../Component/OptionsResolver/.gitignore | 4 + .../Exception/ExceptionInterface.php | 21 + .../Exception/InvalidOptionsException.php | 21 + .../Exception/MissingOptionsException.php | 21 + .../Exception/OptionDefinitionException.php | 21 + .../Symfony/Component/OptionsResolver/LICENSE | 19 + .../Component/OptionsResolver/Options.php | 513 ++++ .../OptionsResolver/OptionsResolver.php | 346 +++ .../OptionsResolverInterface.php | 210 ++ .../Component/OptionsResolver/README.md | 107 + .../Tests/OptionsResolverTest.php | 681 +++++ .../OptionsResolver/Tests/OptionsTest.php | 529 ++++ .../Component/OptionsResolver/composer.json | 31 + .../OptionsResolver/phpunit.xml.dist | 29 + .../Component/PropertyAccess/.gitattributes | 2 + .../Component/PropertyAccess/.gitignore | 4 + .../Component/PropertyAccess/CHANGELOG.md | 14 + .../Exception/ExceptionInterface.php | 21 + .../InvalidPropertyPathException.php | 21 + .../Exception/NoSuchPropertyException.php | 21 + .../Exception/OutOfBoundsException.php | 21 + .../Exception/RuntimeException.php | 21 + .../Exception/UnexpectedTypeException.php | 25 + .../Symfony/Component/PropertyAccess/LICENSE | 19 + .../PropertyAccess/PropertyAccess.php | 60 + .../PropertyAccess/PropertyAccessor.php | 442 ++++ .../PropertyAccessorBuilder.php | 67 + .../PropertyAccessorInterface.php | 81 + .../Component/PropertyAccess/PropertyPath.php | 225 ++ .../PropertyAccess/PropertyPathBuilder.php | 306 +++ .../PropertyAccess/PropertyPathInterface.php | 86 + .../PropertyAccess/PropertyPathIterator.php | 55 + .../PropertyPathIteratorInterface.php | 34 + .../Component/PropertyAccess/README.md | 14 + .../Component/PropertyAccess/StringUtil.php | 195 ++ .../Component/PropertyAccess/composer.json | 31 + .../Symfony/Component/Routing/.gitignore | 4 + .../Component/Routing/Annotation/Route.php | 156 ++ .../Symfony/Component/Routing/CHANGELOG.md | 162 ++ .../Component/Routing/CompiledRoute.php | 134 + .../Routing/Exception/ExceptionInterface.php | 23 + .../Exception/InvalidParameterException.php | 23 + .../Exception/MethodNotAllowedException.php | 46 + .../MissingMandatoryParametersException.php | 24 + .../Exception/ResourceNotFoundException.php | 25 + .../Exception/RouteNotFoundException.php | 23 + .../ConfigurableRequirementsInterface.php | 55 + .../Generator/Dumper/GeneratorDumper.php | 45 + .../Dumper/GeneratorDumperInterface.php | 41 + .../Generator/Dumper/PhpGeneratorDumper.php | 123 + .../Routing/Generator/UrlGenerator.php | 322 +++ .../Generator/UrlGeneratorInterface.php | 87 + .../routing/Symfony/Component/Routing/LICENSE | 19 + .../Routing/Loader/AnnotationClassLoader.php | 246 ++ .../Loader/AnnotationDirectoryLoader.php | 77 + .../Routing/Loader/AnnotationFileLoader.php | 122 + .../Routing/Loader/ClosureLoader.php | 52 + .../Routing/Loader/PhpFileLoader.php | 62 + .../Routing/Loader/XmlFileLoader.php | 238 ++ .../Routing/Loader/YamlFileLoader.php | 212 ++ .../Loader/schema/routing/routing-1.0.xsd | 64 + .../Routing/Matcher/ApacheUrlMatcher.php | 94 + .../Matcher/Dumper/ApacheMatcherDumper.php | 252 ++ .../Matcher/Dumper/DumperCollection.php | 159 ++ .../Matcher/Dumper/DumperPrefixCollection.php | 108 + .../Routing/Matcher/Dumper/DumperRoute.php | 64 + .../Routing/Matcher/Dumper/MatcherDumper.php | 45 + .../Matcher/Dumper/MatcherDumperInterface.php | 37 + .../Matcher/Dumper/PhpMatcherDumper.php | 378 +++ .../Matcher/RedirectableUrlMatcher.php | 61 + .../RedirectableUrlMatcherInterface.php | 35 + .../Matcher/RequestMatcherInterface.php | 39 + .../Routing/Matcher/TraceableUrlMatcher.php | 121 + .../Component/Routing/Matcher/UrlMatcher.php | 208 ++ .../Routing/Matcher/UrlMatcherInterface.php | 43 + .../Symfony/Component/Routing/README.md | 34 + .../Component/Routing/RequestContext.php | 315 +++ .../Routing/RequestContextAwareInterface.php | 36 + .../Symfony/Component/Routing/Route.php | 594 +++++ .../Component/Routing/RouteCollection.php | 271 ++ .../Component/Routing/RouteCompiler.php | 233 ++ .../Routing/RouteCompilerInterface.php | 32 + .../Symfony/Component/Routing/Router.php | 289 +++ .../Component/Routing/RouterInterface.php | 32 + .../Routing/Tests/Annotation/RouteTest.php | 49 + .../Routing/Tests/CompiledRouteTest.php | 26 + .../AnnotatedClasses/AbstractClass.php | 16 + .../Fixtures/AnnotatedClasses/BarClass.php | 19 + .../Fixtures/AnnotatedClasses/FooClass.php | 16 + .../Tests/Fixtures/CustomXmlFileLoader.php | 26 + .../Tests/Fixtures/RedirectableUrlMatcher.php | 30 + .../Routing/Tests/Fixtures/annotated.php | 0 .../Tests/Fixtures/dumper/url_matcher1.apache | 163 ++ .../Tests/Fixtures/dumper/url_matcher1.php | 310 +++ .../Tests/Fixtures/dumper/url_matcher2.apache | 7 + .../Tests/Fixtures/dumper/url_matcher2.php | 340 +++ .../Tests/Fixtures/dumper/url_matcher3.php | 43 + .../Routing/Tests/Fixtures/empty.yml | 0 .../Component/Routing/Tests/Fixtures/foo.xml | 0 .../Component/Routing/Tests/Fixtures/foo1.xml | 0 .../Routing/Tests/Fixtures/incomplete.yml | 2 + .../Routing/Tests/Fixtures/missing_id.xml | 8 + .../Routing/Tests/Fixtures/missing_path.xml | 8 + .../Tests/Fixtures/namespaceprefix.xml | 13 + .../Fixtures/nonesense_resource_plus_path.yml | 3 + .../nonesense_type_without_resource.yml | 3 + .../Routing/Tests/Fixtures/nonvalid.xml | 11 + .../Routing/Tests/Fixtures/nonvalid.yml | 1 + .../Routing/Tests/Fixtures/nonvalid2.yml | 1 + .../Routing/Tests/Fixtures/nonvalidkeys.yml | 3 + .../Routing/Tests/Fixtures/nonvalidnode.xml | 8 + .../Routing/Tests/Fixtures/nonvalidroute.xml | 13 + .../Tests/Fixtures/special_route_name.yml | 2 + .../Routing/Tests/Fixtures/validpattern.php | 23 + .../Routing/Tests/Fixtures/validpattern.xml | 21 + .../Routing/Tests/Fixtures/validpattern.yml | 17 + .../Routing/Tests/Fixtures/validresource.xml | 12 + .../Routing/Tests/Fixtures/validresource.yml | 7 + .../Routing/Tests/Fixtures/withdoctype.xml | 3 + .../Dumper/PhpGeneratorDumperTest.php | 117 + .../Tests/Generator/UrlGeneratorTest.php | 635 +++++ .../Loader/AbstractAnnotationLoaderTest.php | 38 + .../Loader/AnnotationClassLoaderTest.php | 119 + .../Loader/AnnotationDirectoryLoaderTest.php | 53 + .../Tests/Loader/AnnotationFileLoaderTest.php | 47 + .../Tests/Loader/ClosureLoaderTest.php | 55 + .../Tests/Loader/PhpFileLoaderTest.php | 55 + .../Tests/Loader/XmlFileLoaderTest.php | 127 + .../Tests/Loader/YamlFileLoaderTest.php | 113 + .../Tests/Matcher/ApacheUrlMatcherTest.php | 137 + .../Dumper/ApacheMatcherDumperTest.php | 196 ++ .../Matcher/Dumper/DumperCollectionTest.php | 33 + .../Dumper/DumperPrefixCollectionTest.php | 123 + .../Matcher/Dumper/PhpMatcherDumperTest.php | 261 ++ .../Matcher/RedirectableUrlMatcherTest.php | 58 + .../Tests/Matcher/TraceableUrlMatcherTest.php | 66 + .../Routing/Tests/Matcher/UrlMatcherTest.php | 383 +++ .../Routing/Tests/RouteCollectionTest.php | 255 ++ .../Routing/Tests/RouteCompilerTest.php | 253 ++ .../Component/Routing/Tests/RouteTest.php | 192 ++ .../Component/Routing/Tests/RouterTest.php | 138 + .../Symfony/Component/Routing/composer.json | 42 + .../Component/Routing/phpunit.xml.dist | 29 + .../Symfony/Component/Translation/.gitignore | 4 + .../Component/Translation/CHANGELOG.md | 27 + .../Catalogue/AbstractOperation.php | 146 ++ .../Translation/Catalogue/DiffOperation.php | 49 + .../Translation/Catalogue/MergeOperation.php | 45 + .../Catalogue/OperationInterface.php | 63 + .../Translation/Dumper/CsvFileDumper.php | 63 + .../Translation/Dumper/DumperInterface.php | 31 + .../Translation/Dumper/FileDumper.php | 65 + .../Translation/Dumper/IcuResFileDumper.php | 135 + .../Translation/Dumper/IniFileDumper.php | 45 + .../Translation/Dumper/MoFileDumper.php | 82 + .../Translation/Dumper/PhpFileDumper.php | 40 + .../Translation/Dumper/PoFileDumper.php | 55 + .../Translation/Dumper/QtFileDumper.php | 50 + .../Translation/Dumper/XliffFileDumper.php | 66 + .../Translation/Dumper/YamlFileDumper.php | 39 + .../Exception/ExceptionInterface.php | 23 + .../Exception/InvalidResourceException.php | 23 + .../Exception/NotFoundResourceException.php | 23 + .../Translation/Extractor/ChainExtractor.php | 60 + .../Extractor/ExtractorInterface.php | 38 + .../Translation/IdentityTranslator.php | 74 + .../Component/Translation/Interval.php | 107 + .../Symfony/Component/Translation/LICENSE | 19 + .../Translation/Loader/ArrayLoader.php | 70 + .../Translation/Loader/CsvFileLoader.php | 92 + .../Translation/Loader/IcuDatFileLoader.php | 54 + .../Translation/Loader/IcuResFileLoader.php | 84 + .../Translation/Loader/IniFileLoader.php | 45 + .../Translation/Loader/LoaderInterface.php | 41 + .../Translation/Loader/MoFileLoader.php | 179 ++ .../Translation/Loader/PhpFileLoader.php | 49 + .../Translation/Loader/PoFileLoader.php | 178 ++ .../Translation/Loader/QtFileLoader.php | 95 + .../Translation/Loader/XliffFileLoader.php | 163 ++ .../Translation/Loader/YamlFileLoader.php | 71 + .../dic/xliff-core/xliff-core-1.2-strict.xsd | 2223 +++++++++++++++++ .../Loader/schema/dic/xliff-core/xml.xsd | 309 +++ .../Translation/MessageCatalogue.php | 295 +++ .../Translation/MessageCatalogueInterface.php | 172 ++ .../Component/Translation/MessageSelector.php | 82 + .../Translation/MetadataAwareInterface.php | 54 + .../Translation/PluralizationRules.php | 219 ++ .../Symfony/Component/Translation/README.md | 35 + .../Tests/Catalogue/AbstractOperationTest.php | 74 + .../Tests/Catalogue/DiffOperationTest.php | 60 + .../Tests/Catalogue/MergeOperationTest.php | 60 + .../Tests/Dumper/CsvFileDumperTest.php | 33 + .../Tests/Dumper/IcuResFileDumperTest.php | 37 + .../Tests/Dumper/IniFileDumperTest.php | 32 + .../Tests/Dumper/MoFileDumperTest.php | 31 + .../Tests/Dumper/PhpFileDumperTest.php | 32 + .../Tests/Dumper/PoFileDumperTest.php | 31 + .../Tests/Dumper/QtFileDumperTest.php | 32 + .../Tests/Dumper/XliffFileDumperTest.php | 32 + .../Tests/Dumper/YamlFileDumperTest.php | 39 + .../Tests/IdentityTranslatorTest.php | 61 + .../Translation/Tests/IntervalTest.php | 48 + .../Tests/Loader/CsvFileLoaderTest.php | 67 + .../Tests/Loader/IcuDatFileLoaderTest.php | 72 + .../Tests/Loader/IcuResFileLoaderTest.php | 59 + .../Tests/Loader/IniFileLoaderTest.php | 57 + .../Tests/Loader/LocalizedTestCase.php | 22 + .../Tests/Loader/MoFileLoaderTest.php | 67 + .../Tests/Loader/PhpFileLoaderTest.php | 56 + .../Tests/Loader/PoFileLoaderTest.php | 79 + .../Tests/Loader/QtFileLoaderTest.php | 66 + .../Tests/Loader/XliffFileLoaderTest.php | 113 + .../Tests/Loader/YamlFileLoaderTest.php | 81 + .../Tests/MessageCatalogueTest.php | 212 ++ .../Translation/Tests/MessageSelectorTest.php | 80 + .../Tests/PluralizationRulesTest.php | 124 + .../Translation/Tests/TranslatorTest.php | 306 +++ .../Tests/fixtures/empty-translation.po | 3 + .../Translation/Tests/fixtures/empty.csv | 0 .../Translation/Tests/fixtures/empty.ini | 0 .../Translation/Tests/fixtures/empty.mo | 0 .../Translation/Tests/fixtures/empty.po | 0 .../Translation/Tests/fixtures/empty.yml | 0 .../Translation/Tests/fixtures/encoding.xlf | 15 + .../Tests/fixtures/invalid-xml-resources.xlf | 23 + .../Translation/Tests/fixtures/non-valid.xlf | 11 + .../Translation/Tests/fixtures/non-valid.yml | 1 + .../Translation/Tests/fixtures/plurals.mo | Bin 0 -> 74 bytes .../Translation/Tests/fixtures/plurals.po | 5 + .../Translation/Tests/fixtures/resname.xlf | 19 + .../resourcebundle/corrupted/resources.dat | 1 + .../Tests/fixtures/resourcebundle/dat/en.res | Bin 0 -> 120 bytes .../Tests/fixtures/resourcebundle/dat/en.txt | 3 + .../Tests/fixtures/resourcebundle/dat/fr.res | Bin 0 -> 124 bytes .../Tests/fixtures/resourcebundle/dat/fr.txt | 3 + .../resourcebundle/dat/packagelist.txt | 2 + .../fixtures/resourcebundle/dat/resources.dat | Bin 0 -> 352 bytes .../Tests/fixtures/resourcebundle/res/en.res | Bin 0 -> 84 bytes .../Tests/fixtures/resources-clean.xlf | 15 + .../Translation/Tests/fixtures/resources.csv | 4 + .../Translation/Tests/fixtures/resources.ini | 1 + .../Translation/Tests/fixtures/resources.mo | Bin 0 -> 52 bytes .../Translation/Tests/fixtures/resources.php | 5 + .../Translation/Tests/fixtures/resources.po | 2 + .../Translation/Tests/fixtures/resources.ts | 10 + .../Translation/Tests/fixtures/resources.xlf | 23 + .../Translation/Tests/fixtures/resources.yml | 1 + .../Translation/Tests/fixtures/valid.csv | 4 + .../Tests/fixtures/withdoctype.xlf | 12 + .../Component/Translation/Translator.php | 282 +++ .../Translation/TranslatorInterface.php | 69 + .../Translation/Writer/TranslationWriter.php | 73 + .../Component/Translation/composer.json | 39 + .../Component/Translation/phpunit.xml.dist | 29 + .../Symfony/Bridge/Twig/.gitignore | 4 + .../Symfony/Bridge/Twig/CHANGELOG.md | 29 + .../Bridge/Twig/Extension/CodeExtension.php | 232 ++ .../Bridge/Twig/Extension/FormExtension.php | 136 + .../Twig/Extension/HttpKernelExtension.php | 88 + .../Twig/Extension/RoutingExtension.php | 100 + .../Twig/Extension/SecurityExtension.php | 63 + .../Twig/Extension/TranslationExtension.php | 118 + .../Bridge/Twig/Extension/YamlExtension.php | 67 + .../Symfony/Bridge/Twig/Form/TwigRenderer.php | 41 + .../Bridge/Twig/Form/TwigRendererEngine.php | 183 ++ .../Twig/Form/TwigRendererEngineInterface.php | 27 + .../Twig/Form/TwigRendererInterface.php | 27 + .../twig-bridge/Symfony/Bridge/Twig/LICENSE | 19 + .../Bridge/Twig/Node/FormEnctypeNode.php | 31 + .../Bridge/Twig/Node/FormThemeNode.php | 40 + .../Bridge/Twig/Node/RenderBlockNode.php | 42 + .../Twig/Node/SearchAndRenderBlockNode.php | 106 + .../Twig/Node/TransDefaultDomainNode.php | 33 + .../Symfony/Bridge/Twig/Node/TransNode.php | 119 + .../Symfony/Bridge/Twig/NodeVisitor/Scope.php | 135 + .../TranslationDefaultDomainNodeVisitor.php | 106 + .../NodeVisitor/TranslationNodeVisitor.php | 137 + .../twig-bridge/Symfony/Bridge/Twig/README.md | 15 + .../views/Form/form_div_layout.html.twig | 390 +++ .../views/Form/form_table_layout.html.twig | 52 + .../Tests/Extension/CodeExtensionTest.php | 69 + .../Fixtures/StubFilesystemLoader.php | 30 + .../Extension/Fixtures/StubTranslator.php | 35 + .../Extension/FormExtensionDivLayoutTest.php | 209 ++ .../FormExtensionTableLayoutTest.php | 131 + .../Extension/HttpKernelExtensionTest.php | 68 + .../Tests/Extension/RoutingExtensionTest.php | 60 + .../Extension/TranslationExtensionTest.php | 151 ++ .../Tests/Extension/child_label.html.twig | 3 + .../Tests/Extension/custom_widgets.html.twig | 16 + .../Tests/Extension/parent_label.html.twig | 3 + .../Twig/Tests/Extension/theme.html.twig | 6 + .../Tests/Extension/theme_extends.html.twig | 8 + .../Twig/Tests/Extension/theme_use.html.twig | 8 + .../Bridge/Twig/Tests/Node/FormThemeTest.php | 85 + .../Node/SearchAndRenderBlockNodeTest.php | 282 +++ .../Twig/Tests/NodeVisitor/ScopeTest.php | 25 + ...ranslationDefaultDomainNodeVisitorTest.php | 83 + .../TranslationNodeVisitorTest.php | 61 + .../Tests/NodeVisitor/TwigNodeProvider.php | 77 + .../Symfony/Bridge/Twig/Tests/TestCase.php | 22 + .../TokenParser/FormThemeTokenParserTest.php | 108 + .../Tests/Translation/TwigExtractorTest.php | 81 + .../Twig/TokenParser/FormThemeTokenParser.php | 61 + .../TokenParser/TransChoiceTokenParser.php | 89 + .../TransDefaultDomainTokenParser.php | 48 + .../Twig/TokenParser/TransTokenParser.php | 89 + .../Bridge/Twig/Translation/TwigExtractor.php | 86 + .../Symfony/Bridge/Twig/TwigEngine.php | 126 + .../Symfony/Bridge/Twig/composer.json | 50 + .../Symfony/Bridge/Twig/phpunit.xml.dist | 30 + vendor/twig/extensions | 1 + vendor/twig/twig/.editorconfig | 18 + vendor/twig/twig/.gitignore | 2 + vendor/twig/twig/.travis.yml | 15 + vendor/twig/twig/AUTHORS | 9 + vendor/twig/twig/CHANGELOG | 637 +++++ vendor/twig/twig/LICENSE | 31 + vendor/twig/twig/README.markdown | 17 + vendor/twig/twig/composer.json | 31 + vendor/twig/twig/doc/advanced.rst | 829 ++++++ vendor/twig/twig/doc/advanced_legacy.rst | 887 +++++++ vendor/twig/twig/doc/api.rst | 529 ++++ vendor/twig/twig/doc/coding_standards.rst | 101 + vendor/twig/twig/doc/deprecated.rst | 98 + vendor/twig/twig/doc/filters/abs.rst | 18 + vendor/twig/twig/doc/filters/batch.rst | 45 + vendor/twig/twig/doc/filters/capitalize.rst | 11 + .../twig/doc/filters/convert_encoding.rst | 28 + vendor/twig/twig/doc/filters/date.rst | 88 + vendor/twig/twig/doc/filters/date_modify.rst | 23 + vendor/twig/twig/doc/filters/default.rst | 33 + vendor/twig/twig/doc/filters/escape.rst | 93 + vendor/twig/twig/doc/filters/first.rst | 25 + vendor/twig/twig/doc/filters/format.rst | 16 + vendor/twig/twig/doc/filters/index.rst | 36 + vendor/twig/twig/doc/filters/join.rst | 23 + vendor/twig/twig/doc/filters/json_encode.rst | 21 + vendor/twig/twig/doc/filters/keys.rst | 11 + vendor/twig/twig/doc/filters/last.rst | 25 + vendor/twig/twig/doc/filters/length.rst | 12 + vendor/twig/twig/doc/filters/lower.rst | 10 + vendor/twig/twig/doc/filters/merge.rst | 41 + vendor/twig/twig/doc/filters/nl2br.rst | 22 + .../twig/twig/doc/filters/number_format.rst | 45 + vendor/twig/twig/doc/filters/raw.rst | 12 + vendor/twig/twig/doc/filters/replace.rst | 19 + vendor/twig/twig/doc/filters/reverse.rst | 47 + vendor/twig/twig/doc/filters/slice.rst | 70 + vendor/twig/twig/doc/filters/sort.rst | 17 + vendor/twig/twig/doc/filters/split.rst | 53 + vendor/twig/twig/doc/filters/striptags.rst | 15 + vendor/twig/twig/doc/filters/title.rst | 11 + vendor/twig/twig/doc/filters/trim.rst | 29 + vendor/twig/twig/doc/filters/upper.rst | 10 + vendor/twig/twig/doc/filters/url_encode.rst | 28 + vendor/twig/twig/doc/functions/attribute.rst | 18 + vendor/twig/twig/doc/functions/block.rst | 15 + vendor/twig/twig/doc/functions/constant.rst | 18 + vendor/twig/twig/doc/functions/cycle.rst | 25 + vendor/twig/twig/doc/functions/date.rst | 52 + vendor/twig/twig/doc/functions/dump.rst | 69 + vendor/twig/twig/doc/functions/include.rst | 80 + vendor/twig/twig/doc/functions/index.rst | 17 + vendor/twig/twig/doc/functions/parent.rst | 20 + vendor/twig/twig/doc/functions/random.rst | 29 + vendor/twig/twig/doc/functions/range.rst | 45 + .../doc/functions/template_from_string.rst | 32 + vendor/twig/twig/doc/index.rst | 18 + vendor/twig/twig/doc/internals.rst | 140 ++ vendor/twig/twig/doc/intro.rst | 164 ++ vendor/twig/twig/doc/recipes.rst | 475 ++++ vendor/twig/twig/doc/tags/autoescape.rst | 71 + vendor/twig/twig/doc/tags/block.rst | 11 + vendor/twig/twig/doc/tags/do.rst | 12 + vendor/twig/twig/doc/tags/embed.rst | 178 ++ vendor/twig/twig/doc/tags/extends.rst | 268 ++ vendor/twig/twig/doc/tags/filter.rst | 21 + vendor/twig/twig/doc/tags/flush.rst | 17 + vendor/twig/twig/doc/tags/for.rst | 172 ++ vendor/twig/twig/doc/tags/from.rst | 8 + vendor/twig/twig/doc/tags/if.rst | 43 + vendor/twig/twig/doc/tags/import.rst | 57 + vendor/twig/twig/doc/tags/include.rst | 86 + vendor/twig/twig/doc/tags/index.rst | 24 + vendor/twig/twig/doc/tags/macro.rst | 83 + vendor/twig/twig/doc/tags/sandbox.rst | 30 + vendor/twig/twig/doc/tags/set.rst | 78 + vendor/twig/twig/doc/tags/spaceless.rst | 37 + vendor/twig/twig/doc/tags/use.rst | 123 + vendor/twig/twig/doc/tags/verbatim.rst | 24 + vendor/twig/twig/doc/templates.rst | 851 +++++++ vendor/twig/twig/doc/tests/constant.rst | 22 + vendor/twig/twig/doc/tests/defined.rst | 30 + vendor/twig/twig/doc/tests/divisibleby.rst | 10 + vendor/twig/twig/doc/tests/empty.rst | 11 + vendor/twig/twig/doc/tests/even.rst | 10 + vendor/twig/twig/doc/tests/index.rst | 15 + vendor/twig/twig/doc/tests/iterable.rst | 19 + vendor/twig/twig/doc/tests/null.rst | 12 + vendor/twig/twig/doc/tests/odd.rst | 10 + vendor/twig/twig/doc/tests/sameas.rst | 11 + vendor/twig/twig/ext/twig/.gitignore | 30 + vendor/twig/twig/ext/twig/LICENSE | 22 + vendor/twig/twig/ext/twig/config.m4 | 8 + vendor/twig/twig/ext/twig/config.w32 | 8 + vendor/twig/twig/ext/twig/php_twig.h | 31 + vendor/twig/twig/ext/twig/twig.c | 1076 ++++++++ .../twig/twig/lib}/Twig/Autoloader.php | 0 .../twig/twig/lib}/Twig/Compiler.php | 0 .../twig/twig/lib}/Twig/CompilerInterface.php | 0 .../twig/twig/lib}/Twig/Environment.php | 4 +- .../twig/twig/lib}/Twig/Error.php | 6 +- .../twig/twig/lib}/Twig/Error/Loader.php | 0 .../twig/twig/lib}/Twig/Error/Runtime.php | 0 .../twig/twig/lib}/Twig/Error/Syntax.php | 0 .../twig/lib}/Twig/ExistsLoaderInterface.php | 0 .../twig/twig/lib}/Twig/ExpressionParser.php | 0 .../twig/twig/lib}/Twig/Extension.php | 0 .../twig/twig/lib}/Twig/Extension/Core.php | 0 .../twig/twig/lib}/Twig/Extension/Debug.php | 0 .../twig/twig/lib}/Twig/Extension/Escaper.php | 0 .../twig/lib}/Twig/Extension/Optimizer.php | 0 .../twig/twig/lib}/Twig/Extension/Sandbox.php | 0 .../twig/twig/lib}/Twig/Extension/Staging.php | 0 .../twig/lib}/Twig/Extension/StringLoader.php | 0 .../twig/lib}/Twig/ExtensionInterface.php | 0 .../twig/twig/lib}/Twig/Filter.php | 0 .../twig/twig/lib}/Twig/Filter/Function.php | 0 .../twig/twig/lib}/Twig/Filter/Method.php | 0 .../twig/twig/lib}/Twig/Filter/Node.php | 0 .../lib}/Twig/FilterCallableInterface.php | 0 .../twig/twig/lib}/Twig/FilterInterface.php | 0 .../twig/twig/lib}/Twig/Function.php | 0 .../twig/twig/lib}/Twig/Function/Function.php | 0 .../twig/twig/lib}/Twig/Function/Method.php | 0 .../twig/twig/lib}/Twig/Function/Node.php | 0 .../lib}/Twig/FunctionCallableInterface.php | 0 .../twig/twig/lib}/Twig/FunctionInterface.php | 0 .../twig/twig/lib}/Twig/Lexer.php | 0 .../twig/twig/lib}/Twig/LexerInterface.php | 0 .../twig/twig/lib}/Twig/Loader/Array.php | 0 .../twig/twig/lib}/Twig/Loader/Chain.php | 0 .../twig/twig/lib}/Twig/Loader/Filesystem.php | 15 +- .../twig/twig/lib}/Twig/Loader/String.php | 0 .../twig/twig/lib}/Twig/LoaderInterface.php | 0 .../twig/twig/lib}/Twig/Markup.php | 0 .../twig/twig/lib}/Twig/Node.php | 0 .../twig/twig/lib}/Twig/Node/AutoEscape.php | 0 .../twig/twig/lib}/Twig/Node/Block.php | 0 .../twig/lib}/Twig/Node/BlockReference.php | 0 .../twig/twig/lib}/Twig/Node/Body.php | 0 .../twig/twig/lib}/Twig/Node/Do.php | 0 .../twig/twig/lib}/Twig/Node/Embed.php | 0 .../twig/twig/lib}/Twig/Node/Expression.php | 0 .../twig/lib}/Twig/Node/Expression/Array.php | 0 .../lib}/Twig/Node/Expression/AssignName.php | 0 .../twig/lib}/Twig/Node/Expression/Binary.php | 0 .../lib}/Twig/Node/Expression/Binary/Add.php | 0 .../lib}/Twig/Node/Expression/Binary/And.php | 0 .../Node/Expression/Binary/BitwiseAnd.php | 0 .../Twig/Node/Expression/Binary/BitwiseOr.php | 0 .../Node/Expression/Binary/BitwiseXor.php | 0 .../Twig/Node/Expression/Binary/Concat.php | 0 .../lib}/Twig/Node/Expression/Binary/Div.php | 0 .../Twig/Node/Expression/Binary/Equal.php | 0 .../Twig/Node/Expression/Binary/FloorDiv.php | 0 .../Twig/Node/Expression/Binary/Greater.php | 0 .../Node/Expression/Binary/GreaterEqual.php | 0 .../lib}/Twig/Node/Expression/Binary/In.php | 0 .../lib}/Twig/Node/Expression/Binary/Less.php | 0 .../Twig/Node/Expression/Binary/LessEqual.php | 0 .../lib}/Twig/Node/Expression/Binary/Mod.php | 0 .../lib}/Twig/Node/Expression/Binary/Mul.php | 0 .../Twig/Node/Expression/Binary/NotEqual.php | 0 .../Twig/Node/Expression/Binary/NotIn.php | 0 .../lib}/Twig/Node/Expression/Binary/Or.php | 0 .../Twig/Node/Expression/Binary/Power.php | 0 .../Twig/Node/Expression/Binary/Range.php | 0 .../lib}/Twig/Node/Expression/Binary/Sub.php | 0 .../Twig/Node/Expression/BlockReference.php | 0 .../twig/lib}/Twig/Node/Expression/Call.php | 6 +- .../lib}/Twig/Node/Expression/Conditional.php | 0 .../lib}/Twig/Node/Expression/Constant.php | 0 .../Node/Expression/ExtensionReference.php | 0 .../twig/lib}/Twig/Node/Expression/Filter.php | 0 .../Twig/Node/Expression/Filter/Default.php | 0 .../lib}/Twig/Node/Expression/Function.php | 0 .../lib}/Twig/Node/Expression/GetAttr.php | 0 .../lib}/Twig/Node/Expression/MethodCall.php | 0 .../twig/lib}/Twig/Node/Expression/Name.php | 0 .../twig/lib}/Twig/Node/Expression/Parent.php | 0 .../lib}/Twig/Node/Expression/TempName.php | 0 .../twig/lib}/Twig/Node/Expression/Test.php | 0 .../Twig/Node/Expression/Test/Constant.php | 0 .../Twig/Node/Expression/Test/Defined.php | 0 .../Twig/Node/Expression/Test/Divisibleby.php | 0 .../lib}/Twig/Node/Expression/Test/Even.php | 0 .../lib}/Twig/Node/Expression/Test/Null.php | 0 .../lib}/Twig/Node/Expression/Test/Odd.php | 0 .../lib}/Twig/Node/Expression/Test/Sameas.php | 0 .../twig/lib}/Twig/Node/Expression/Unary.php | 0 .../lib}/Twig/Node/Expression/Unary/Neg.php | 0 .../lib}/Twig/Node/Expression/Unary/Not.php | 0 .../lib}/Twig/Node/Expression/Unary/Pos.php | 0 .../twig/twig/lib}/Twig/Node/Flush.php | 0 .../twig/twig/lib}/Twig/Node/For.php | 0 .../twig/twig/lib}/Twig/Node/ForLoop.php | 0 .../twig/twig/lib}/Twig/Node/If.php | 0 .../twig/twig/lib}/Twig/Node/Import.php | 0 .../twig/twig/lib}/Twig/Node/Include.php | 0 .../twig/twig/lib}/Twig/Node/Macro.php | 0 .../twig/twig/lib}/Twig/Node/Module.php | 0 .../twig/twig/lib}/Twig/Node/Print.php | 0 .../twig/twig/lib}/Twig/Node/Sandbox.php | 0 .../twig/lib}/Twig/Node/SandboxedModule.php | 0 .../twig/lib}/Twig/Node/SandboxedPrint.php | 0 .../twig/twig/lib}/Twig/Node/Set.php | 0 .../twig/twig/lib}/Twig/Node/SetTemp.php | 0 .../twig/twig/lib}/Twig/Node/Spaceless.php | 0 .../twig/twig/lib}/Twig/Node/Text.php | 0 .../twig/twig/lib}/Twig/NodeInterface.php | 0 .../twig/lib}/Twig/NodeOutputInterface.php | 0 .../twig/twig/lib}/Twig/NodeTraverser.php | 0 .../twig/lib}/Twig/NodeVisitor/Escaper.php | 0 .../twig/lib}/Twig/NodeVisitor/Optimizer.php | 0 .../lib}/Twig/NodeVisitor/SafeAnalysis.php | 0 .../twig/lib}/Twig/NodeVisitor/Sandbox.php | 0 .../twig/lib}/Twig/NodeVisitorInterface.php | 0 .../twig/twig/lib}/Twig/Parser.php | 0 .../twig/twig/lib}/Twig/ParserInterface.php | 0 .../twig/lib}/Twig/Sandbox/SecurityError.php | 0 .../twig/lib}/Twig/Sandbox/SecurityPolicy.php | 0 .../Twig/Sandbox/SecurityPolicyInterface.php | 0 .../twig/twig/lib}/Twig/SimpleFilter.php | 0 .../twig/twig/lib}/Twig/SimpleFunction.php | 0 .../twig/twig/lib}/Twig/SimpleTest.php | 0 .../twig/twig/lib}/Twig/Template.php | 0 .../twig/twig/lib}/Twig/TemplateInterface.php | 0 .../twig/twig/lib}/Twig/Test.php | 0 .../twig/twig/lib}/Twig/Test/Function.php | 0 .../lib}/Twig/Test/IntegrationTestCase.php | 0 .../twig/twig/lib}/Twig/Test/Method.php | 0 .../twig/twig/lib}/Twig/Test/Node.php | 0 .../twig/twig/lib}/Twig/Test/NodeTestCase.php | 0 .../twig/lib}/Twig/TestCallableInterface.php | 0 .../twig/twig/lib}/Twig/TestInterface.php | 0 .../twig/twig/lib}/Twig/Token.php | 0 .../twig/twig/lib}/Twig/TokenParser.php | 0 .../twig/lib}/Twig/TokenParser/AutoEscape.php | 0 .../twig/twig/lib}/Twig/TokenParser/Block.php | 0 .../twig/twig/lib}/Twig/TokenParser/Do.php | 0 .../twig/twig/lib}/Twig/TokenParser/Embed.php | 0 .../twig/lib}/Twig/TokenParser/Extends.php | 0 .../twig/lib}/Twig/TokenParser/Filter.php | 0 .../twig/twig/lib}/Twig/TokenParser/Flush.php | 0 .../twig/twig/lib}/Twig/TokenParser/For.php | 0 .../twig/twig/lib}/Twig/TokenParser/From.php | 0 .../twig/twig/lib}/Twig/TokenParser/If.php | 0 .../twig/lib}/Twig/TokenParser/Import.php | 0 .../twig/lib}/Twig/TokenParser/Include.php | 0 .../twig/twig/lib}/Twig/TokenParser/Macro.php | 0 .../twig/lib}/Twig/TokenParser/Sandbox.php | 0 .../twig/twig/lib}/Twig/TokenParser/Set.php | 0 .../twig/lib}/Twig/TokenParser/Spaceless.php | 0 .../twig/twig/lib}/Twig/TokenParser/Use.php | 0 .../twig/twig/lib}/Twig/TokenParserBroker.php | 0 .../lib}/Twig/TokenParserBrokerInterface.php | 0 .../twig/lib}/Twig/TokenParserInterface.php | 0 .../twig/twig/lib}/Twig/TokenStream.php | 0 vendor/twig/twig/phpunit.xml.dist | 25 + .../twig/test/Twig/Tests/AutoloaderTest.php | 21 + .../twig/test/Twig/Tests/CompilerTest.php | 33 + .../twig/test/Twig/Tests/EnvironmentTest.php | 288 +++ .../twig/twig/test/Twig/Tests/ErrorTest.php | 159 ++ .../test/Twig/Tests/ExpressionParserTest.php | 332 +++ .../test/Twig/Tests/Extension/CoreTest.php | 117 + .../test/Twig/Tests/Extension/SandboxTest.php | 212 ++ .../twig/test/Twig/Tests/FileCachingTest.php | 70 + .../test/Twig/Tests/Fixtures/errors/base.html | 1 + .../Twig/Tests/Fixtures/errors/index.html | 7 + .../Fixtures/exceptions/unclosed_tag.test | 20 + .../Tests/Fixtures/expressions/array.test | 61 + .../Fixtures/expressions/array_call.test | 14 + .../Tests/Fixtures/expressions/binary.test | 46 + .../Tests/Fixtures/expressions/bitwise.test | 14 + .../Fixtures/expressions/comparison.test | 14 + .../Tests/Fixtures/expressions/dotdot.test | 20 + .../Tests/Fixtures/expressions/grouping.test | 8 + .../Tests/Fixtures/expressions/literals.test | 22 + .../Fixtures/expressions/magic_call.test | 27 + .../Fixtures/expressions/method_call.test | 28 + .../Tests/Fixtures/expressions/postfix.test | 22 + .../Tests/Fixtures/expressions/strings.test | 10 + .../expressions/ternary_operator.test | 18 + .../expressions/ternary_operator_noelse.test | 10 + .../expressions/ternary_operator_nothen.test | 10 + .../Tests/Fixtures/expressions/unary.test | 12 + .../expressions/unary_precedence.test | 14 + .../test/Twig/Tests/Fixtures/filters/abs.test | 30 + .../Twig/Tests/Fixtures/filters/batch.test | 31 + .../Tests/Fixtures/filters/batch_float.php | 31 + .../filters/batch_with_empty_fill.test | 37 + .../Fixtures/filters/batch_with_fill.test | 37 + .../Fixtures/filters/convert_encoding.test | 10 + .../Twig/Tests/Fixtures/filters/date.test | 76 + .../Fixtures/filters/date_default_format.test | 14 + .../filters/date_default_format_interval.test | 16 + .../Tests/Fixtures/filters/date_interval.test | 19 + .../Tests/Fixtures/filters/date_modify.test | 14 + .../Fixtures/filters/date_namedargs.test | 13 + .../Twig/Tests/Fixtures/filters/default.test | 150 ++ .../Fixtures/filters/dynamic_filter.test | 10 + .../Twig/Tests/Fixtures/filters/escape.test | 8 + .../filters/escape_non_supported_charset.test | 8 + .../Twig/Tests/Fixtures/filters/first.test | 14 + .../Tests/Fixtures/filters/force_escape.test | 18 + .../Twig/Tests/Fixtures/filters/format.test | 8 + .../Twig/Tests/Fixtures/filters/join.test | 12 + .../Tests/Fixtures/filters/json_encode.test | 12 + .../Twig/Tests/Fixtures/filters/last.test | 14 + .../Twig/Tests/Fixtures/filters/length.test | 14 + .../Tests/Fixtures/filters/length_utf8.test | 12 + .../Twig/Tests/Fixtures/filters/merge.test | 16 + .../Twig/Tests/Fixtures/filters/nl2br.test | 14 + .../Tests/Fixtures/filters/number_format.test | 18 + .../filters/number_format_default.test | 21 + .../Twig/Tests/Fixtures/filters/replace.test | 8 + .../Twig/Tests/Fixtures/filters/reverse.test | 18 + .../Twig/Tests/Fixtures/filters/slice.test | 42 + .../Twig/Tests/Fixtures/filters/sort.test | 10 + .../Tests/Fixtures/filters/special_chars.test | 8 + .../Twig/Tests/Fixtures/filters/split.test | 18 + .../Twig/Tests/Fixtures/filters/trim.test | 12 + .../Tests/Fixtures/filters/urlencode.test | 12 + .../Tests/Fixtures/functions/attribute.test | 12 + .../Twig/Tests/Fixtures/functions/block.test | 12 + .../Tests/Fixtures/functions/constant.test | 10 + .../Twig/Tests/Fixtures/functions/cycle.test | 16 + .../Twig/Tests/Fixtures/functions/date.test | 27 + .../Fixtures/functions/date_namedargs.test | 11 + .../Twig/Tests/Fixtures/functions/dump.test | 16 + .../Tests/Fixtures/functions/dump_array.test | 19 + .../Fixtures/functions/dynamic_function.test | 10 + .../functions/include/assignment.test | 13 + .../functions/include/autoescaping.test | 10 + .../Fixtures/functions/include/basic.test | 17 + .../functions/include/expression.test | 17 + .../functions/include/ignore_missing.test | 10 + .../Fixtures/functions/include/missing.test | 8 + .../functions/include/missing_nested.test | 16 + .../Fixtures/functions/include/sandbox.test | 10 + .../functions/include/template_instance.test | 10 + .../functions/include/templates_as_array.test | 12 + .../functions/include/with_context.test | 16 + .../functions/include/with_variables.test | 12 + .../Twig/Tests/Fixtures/functions/range.test | 8 + .../Fixtures/functions/special_chars.test | 8 + .../functions/template_from_string.test | 11 + .../Tests/Fixtures/macros/default_values.test | 16 + .../Tests/Fixtures/macros/nested_calls.test | 18 + .../Fixtures/macros/reserved_variables.test | 14 + .../Twig/Tests/Fixtures/macros/simple.test | 22 + .../Tests/Fixtures/macros/with_filters.test | 14 + .../Fixtures/regression/empty_token.test | 8 + .../regression/simple_xml_element.test | 17 + .../regression/strings_like_numbers.test | 8 + .../Tests/Fixtures/tags/autoescape/basic.test | 26 + .../Fixtures/tags/autoescape/blocks.test | 12 + .../tags/autoescape/double_escaping.test | 10 + .../Fixtures/tags/autoescape/functions.test | 83 + .../Fixtures/tags/autoescape/literal.test | 45 + .../Fixtures/tags/autoescape/nested.test | 26 + .../Fixtures/tags/autoescape/objects.test | 26 + .../Tests/Fixtures/tags/autoescape/raw.test | 10 + .../Fixtures/tags/autoescape/strategy.test | 17 + .../Tests/Fixtures/tags/autoescape/type.test | 69 + .../tags/autoescape/with_filters.test | 131 + .../autoescape/with_filters_arguments.test | 23 + .../autoescape/with_pre_escape_filters.test | 68 + .../with_preserves_safety_filters.test | 50 + .../Twig/Tests/Fixtures/tags/block/basic.test | 11 + .../tags/block/block_unique_name.test | 11 + .../Fixtures/tags/block/special_chars.test | 10 + .../Twig/Tests/Fixtures/tags/embed/basic.test | 35 + .../Tests/Fixtures/tags/embed/error_line.test | 16 + .../Tests/Fixtures/tags/embed/multiple.test | 50 + .../Tests/Fixtures/tags/embed/nested.test | 42 + .../Fixtures/tags/embed/with_extends.test | 57 + .../Tests/Fixtures/tags/filter/basic.test | 10 + .../Fixtures/tags/filter/json_encode.test | 8 + .../Tests/Fixtures/tags/filter/multiple.test | 10 + .../Tests/Fixtures/tags/filter/nested.test | 16 + .../Fixtures/tags/filter/with_for_tag.test | 13 + .../Fixtures/tags/filter/with_if_tag.test | 29 + .../Tests/Fixtures/tags/for/condition.test | 14 + .../Twig/Tests/Fixtures/tags/for/context.test | 18 + .../Twig/Tests/Fixtures/tags/for/else.test | 23 + .../Fixtures/tags/for/inner_variables.test | 17 + .../Twig/Tests/Fixtures/tags/for/keys.test | 11 + .../Fixtures/tags/for/keys_and_values.test | 11 + .../Tests/Fixtures/tags/for/loop_context.test | 19 + .../Fixtures/tags/for/loop_context_local.test | 10 + .../Fixtures/tags/for/loop_not_defined.test | 10 + .../tags/for/loop_not_defined_cond.test | 9 + .../Tests/Fixtures/tags/for/nested_else.test | 17 + .../Twig/Tests/Fixtures/tags/for/objects.test | 43 + .../Fixtures/tags/for/objects_countable.test | 47 + .../Tests/Fixtures/tags/for/recursive.test | 18 + .../Twig/Tests/Fixtures/tags/for/values.test | 11 + .../test/Twig/Tests/Fixtures/tags/from.test | 14 + .../Twig/Tests/Fixtures/tags/if/basic.test | 22 + .../Tests/Fixtures/tags/if/expression.test | 22 + .../Tests/Fixtures/tags/include/basic.test | 16 + .../Fixtures/tags/include/expression.test | 16 + .../Fixtures/tags/include/ignore_missing.test | 10 + .../Tests/Fixtures/tags/include/missing.test | 8 + .../Fixtures/tags/include/missing_nested.test | 16 + .../Tests/Fixtures/tags/include/only.test | 16 + .../tags/include/template_instance.test | 10 + .../tags/include/templates_as_array.test | 12 + .../Fixtures/tags/include/with_variables.test | 12 + .../Fixtures/tags/inheritance/basic.test | 14 + .../tags/inheritance/conditional.test | 14 + .../Fixtures/tags/inheritance/dynamic.test | 14 + .../Fixtures/tags/inheritance/empty.test | 10 + .../tags/inheritance/extends_as_array.test | 12 + .../Fixtures/tags/inheritance/multiple.test | 12 + .../tags/inheritance/nested_blocks.test | 22 + .../nested_blocks_parent_only.test | 15 + .../tags/inheritance/nested_inheritance.test | 16 + .../Fixtures/tags/inheritance/parent.test | 12 + .../tags/inheritance/parent_change.test | 16 + .../tags/inheritance/parent_in_a_block.test | 8 + .../tags/inheritance/parent_isolation.test | 20 + .../tags/inheritance/parent_nested.test | 28 + .../inheritance/parent_without_extends.test | 8 + .../parent_without_extends_but_traits.test | 14 + .../tags/inheritance/template_instance.test | 14 + .../Tests/Fixtures/tags/inheritance/use.test | 44 + .../Twig/Tests/Fixtures/tags/macro/basic.test | 17 + .../Fixtures/tags/macro/endmacro_name.test | 16 + .../Tests/Fixtures/tags/macro/external.test | 17 + .../Twig/Tests/Fixtures/tags/macro/from.test | 18 + .../Tests/Fixtures/tags/macro/global.test | 14 + .../Fixtures/tags/macro/self_import.test | 17 + .../Fixtures/tags/macro/special_chars.test | 14 + .../Twig/Tests/Fixtures/tags/raw/basic.test | 10 + .../tags/raw/mixed_usage_with_raw.test | 10 + .../Fixtures/tags/raw/whitespace_control.test | 56 + .../Fixtures/tags/sandbox/not_valid1.test | 11 + .../Fixtures/tags/sandbox/not_valid2.test | 14 + .../Tests/Fixtures/tags/sandbox/simple.test | 22 + .../Twig/Tests/Fixtures/tags/set/basic.test | 20 + .../Fixtures/tags/set/capture-empty.test | 9 + .../Twig/Tests/Fixtures/tags/set/capture.test | 10 + .../Tests/Fixtures/tags/set/expression.test | 12 + .../Tests/Fixtures/tags/spaceless/simple.test | 12 + .../Tests/Fixtures/tags/special_chars.test | 8 + .../Twig/Tests/Fixtures/tags/trim_block.test | 74 + .../Twig/Tests/Fixtures/tags/use/aliases.test | 12 + .../Twig/Tests/Fixtures/tags/use/basic.test | 12 + .../Twig/Tests/Fixtures/tags/use/deep.test | 22 + .../Tests/Fixtures/tags/use/deep_empty.test | 10 + .../Tests/Fixtures/tags/use/multiple.test | 21 + .../Fixtures/tags/use/multiple_aliases.test | 23 + .../Tests/Fixtures/tags/verbatim/basic.test | 10 + .../tags/verbatim/mixed_usage_with_raw.test | 10 + .../tags/verbatim/whitespace_control.test | 56 + .../test/Twig/Tests/Fixtures/tests/array.test | 24 + .../Twig/Tests/Fixtures/tests/constant.test | 14 + .../Twig/Tests/Fixtures/tests/defined.test | 108 + .../test/Twig/Tests/Fixtures/tests/empty.test | 45 + .../test/Twig/Tests/Fixtures/tests/even.test | 14 + .../test/Twig/Tests/Fixtures/tests/in.test | 48 + .../Tests/Fixtures/tests/in_with_objects.test | 19 + .../Twig/Tests/Fixtures/tests/iterable.test | 19 + .../test/Twig/Tests/Fixtures/tests/odd.test | 10 + .../twig/test/Twig/Tests/IntegrationTest.php | 217 ++ .../twig/twig/test/Twig/Tests/LexerTest.php | 301 +++ .../twig/test/Twig/Tests/Loader/ArrayTest.php | 97 + .../twig/test/Twig/Tests/Loader/ChainTest.php | 79 + .../test/Twig/Tests/Loader/FilesystemTest.php | 97 + .../Tests/Loader/Fixtures/named/index.html | 1 + .../Loader/Fixtures/named_bis/index.html | 1 + .../Loader/Fixtures/named_final/index.html | 1 + .../Loader/Fixtures/named_ter/index.html | 1 + .../Tests/Loader/Fixtures/normal/index.html | 1 + .../Loader/Fixtures/normal_bis/index.html | 1 + .../Loader/Fixtures/normal_final/index.html | 1 + .../Loader/Fixtures/normal_ter/index.html | 1 + .../test/Twig/Tests/NativeExtensionTest.php | 29 + .../test/Twig/Tests/Node/AutoEscapeTest.php | 44 + .../Twig/Tests/Node/BlockReferenceTest.php | 43 + .../twig/test/Twig/Tests/Node/BlockTest.php | 51 + .../twig/twig/test/Twig/Tests/Node/DoTest.php | 44 + .../Twig/Tests/Node/Expression/ArrayTest.php | 49 + .../Tests/Node/Expression/AssignNameTest.php | 41 + .../Tests/Node/Expression/Binary/AddTest.php | 47 + .../Tests/Node/Expression/Binary/AndTest.php | 47 + .../Node/Expression/Binary/ConcatTest.php | 47 + .../Tests/Node/Expression/Binary/DivTest.php | 47 + .../Node/Expression/Binary/FloorDivTest.php | 47 + .../Tests/Node/Expression/Binary/ModTest.php | 47 + .../Tests/Node/Expression/Binary/MulTest.php | 47 + .../Tests/Node/Expression/Binary/OrTest.php | 47 + .../Tests/Node/Expression/Binary/SubTest.php | 47 + .../Twig/Tests/Node/Expression/CallTest.php | 67 + .../Tests/Node/Expression/ConditionalTest.php | 50 + .../Tests/Node/Expression/ConstantTest.php | 42 + .../Twig/Tests/Node/Expression/FilterTest.php | 133 + .../Tests/Node/Expression/FunctionTest.php | 99 + .../Tests/Node/Expression/GetAttrTest.php | 62 + .../Twig/Tests/Node/Expression/NameTest.php | 49 + .../Node/Expression/PHP53/FilterInclude.php | 6 + .../Node/Expression/PHP53/FunctionInclude.php | 6 + .../Node/Expression/PHP53/TestInclude.php | 6 + .../Twig/Tests/Node/Expression/ParentTest.php | 40 + .../Twig/Tests/Node/Expression/TestTest.php | 68 + .../Tests/Node/Expression/Unary/NegTest.php | 44 + .../Tests/Node/Expression/Unary/NotTest.php | 44 + .../Tests/Node/Expression/Unary/PosTest.php | 44 + .../twig/test/Twig/Tests/Node/ForTest.php | 203 ++ .../twig/twig/test/Twig/Tests/Node/IfTest.php | 100 + .../twig/test/Twig/Tests/Node/ImportTest.php | 52 + .../twig/test/Twig/Tests/Node/IncludeTest.php | 96 + .../twig/test/Twig/Tests/Node/MacroTest.php | 73 + .../twig/test/Twig/Tests/Node/ModuleTest.php | 196 ++ .../twig/test/Twig/Tests/Node/PrintTest.php | 41 + .../twig/test/Twig/Tests/Node/SandboxTest.php | 56 + .../Twig/Tests/Node/SandboxedModuleTest.php | 173 ++ .../Twig/Tests/Node/SandboxedPrintTest.php | 45 + .../twig/test/Twig/Tests/Node/SetTest.php | 81 + .../test/Twig/Tests/Node/SpacelessTest.php | 49 + .../twig/test/Twig/Tests/Node/TextTest.php | 40 + .../Twig/Tests/NodeVisitor/OptimizerTest.php | 114 + .../twig/twig/test/Twig/Tests/ParserTest.php | 180 ++ .../twig/test/Twig/Tests/TemplateTest.php | 626 +++++ .../twig/test/Twig/Tests/TokenStreamTest.php | 70 + .../twig/test/Twig/Tests/escapingTest.php | 320 +++ vendor/twig/twig/test/bootstrap.php | 13 + .../twig-gettext-extractor/.gitignore | 3 + .../twig-gettext-extractor/.travis.yml | 10 + .../umpirsky/twig-gettext-extractor/LICENSE | 19 + .../umpirsky/twig-gettext-extractor/README.md | 49 + .../Twig}/Gettext/Extractor.php | 0 .../Twig}/Gettext/Loader/Filesystem.php | 0 .../Routing/Generator/UrlGenerator.php | 0 .../Twig}/Gettext/Test/ExtractorTest.php | 0 .../Gettext/Test/Fixtures/twig/empty.twig | 0 .../Gettext/Test/Fixtures/twig/plural.twig | 0 .../Gettext/Test/Fixtures/twig/singular.twig | 0 .../twig-gettext-extractor/composer.json | 30 + .../twig-gettext-extractor/phpunit.xml.dist | 14 + .../twig-gettext-extractor | 58 + 1418 files changed, 108207 insertions(+), 1586 deletions(-) delete mode 100644 inc/3rdparty/Twig/Extensions/Autoloader.php delete mode 100644 inc/3rdparty/Twig/Extensions/Extension/Debug.php delete mode 100644 inc/3rdparty/Twig/Extensions/Extension/I18n.php delete mode 100644 inc/3rdparty/Twig/Extensions/Extension/Intl.php delete mode 100644 inc/3rdparty/Twig/Extensions/Extension/Text.php delete mode 100644 inc/3rdparty/Twig/Extensions/Grammar.php delete mode 100644 inc/3rdparty/Twig/Extensions/Grammar/Arguments.php delete mode 100644 inc/3rdparty/Twig/Extensions/Grammar/Array.php delete mode 100644 inc/3rdparty/Twig/Extensions/Grammar/Body.php delete mode 100644 inc/3rdparty/Twig/Extensions/Grammar/Boolean.php delete mode 100644 inc/3rdparty/Twig/Extensions/Grammar/Constant.php delete mode 100644 inc/3rdparty/Twig/Extensions/Grammar/Expression.php delete mode 100644 inc/3rdparty/Twig/Extensions/Grammar/Hash.php delete mode 100644 inc/3rdparty/Twig/Extensions/Grammar/Number.php delete mode 100644 inc/3rdparty/Twig/Extensions/Grammar/Optional.php delete mode 100644 inc/3rdparty/Twig/Extensions/Grammar/Switch.php delete mode 100644 inc/3rdparty/Twig/Extensions/Grammar/Tag.php delete mode 100644 inc/3rdparty/Twig/Extensions/GrammarInterface.php delete mode 100644 inc/3rdparty/Twig/Extensions/Node/Debug.php delete mode 100644 inc/3rdparty/Twig/Extensions/Node/Trans.php delete mode 100644 inc/3rdparty/Twig/Extensions/SimpleTokenParser.php delete mode 100644 inc/3rdparty/Twig/Extensions/TokenParser/Debug.php delete mode 100644 inc/3rdparty/Twig/Extensions/TokenParser/Trans.php delete mode 100644 inc/3rdparty/Twig/Gettext/Extractor.php delete mode 100644 inc/3rdparty/Twig/Gettext/Loader/Filesystem.php delete mode 100644 inc/3rdparty/Twig/Gettext/Routing/Generator/UrlGenerator.php delete mode 100644 inc/3rdparty/Twig/Gettext/Test/ExtractorTest.php delete mode 100644 inc/3rdparty/Twig/Gettext/Test/Fixtures/twig/empty.twig delete mode 100644 inc/3rdparty/Twig/Gettext/Test/Fixtures/twig/plural.twig delete mode 100644 inc/3rdparty/Twig/Gettext/Test/Fixtures/twig/singular.twig rename {css => tpl/css}/knacss.css (100%) rename {css => tpl/css}/style-dark.css (100%) rename {css => tpl/css}/style-light.css (100%) rename {css => tpl/css}/style.css (100%) delete mode 100644 tpl/entries.html rename {img => tpl/img}/apple-touch-icon-144x144-precomposed.png (100%) rename {img => tpl/img}/apple-touch-icon-72x72-precomposed.png (100%) rename {img => tpl/img}/apple-touch-icon.png (100%) rename {img => tpl/img}/dark/checkmark-off.png (100%) rename {img => tpl/img}/dark/checkmark-on.png (100%) rename {img => tpl/img}/dark/down.png (100%) rename {img => tpl/img}/dark/logo.png (100%) rename {img => tpl/img}/dark/remove.png (100%) rename {img => tpl/img}/dark/star-off.png (100%) rename {img => tpl/img}/dark/star-on.png (100%) rename {img => tpl/img}/dark/up.png (100%) rename {img => tpl/img}/down.png (100%) rename {img => tpl/img}/favicon.ico (100%) rename {img => tpl/img}/light/checkmark-off.png (100%) rename {img => tpl/img}/light/checkmark-on.png (100%) rename {img => tpl/img}/light/remove.png (100%) rename {img => tpl/img}/light/star-off.png (100%) rename {img => tpl/img}/light/star-on.png (100%) rename {img => tpl/img}/logo.png (100%) rename {img => tpl/img}/messages/close.png (100%) rename {img => tpl/img}/messages/cross.png (100%) rename {img => tpl/img}/messages/help.png (100%) rename {img => tpl/img}/messages/tick.png (100%) rename {img => tpl/img}/messages/warning.png (100%) rename {img => tpl/img}/up.png (100%) rename {js => tpl/js}/jquery-1.9.1.min.js (100%) rename {js => tpl/js}/jquery.masonry.min.js (100%) rename {js => tpl/js}/poche.js (100%) create mode 100644 vendor/autoload.php create mode 120000 vendor/bin/twig-gettext-extractor create mode 100644 vendor/composer/ClassLoader.php create mode 100644 vendor/composer/autoload_classmap.php create mode 100644 vendor/composer/autoload_files.php create mode 100644 vendor/composer/autoload_namespaces.php create mode 100644 vendor/composer/autoload_real.php create mode 100644 vendor/composer/installed.json create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/.gitignore create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/CHANGELOG.md create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Event.php create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcher.php create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcherInterface.php create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventSubscriberInterface.php create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/GenericEvent.php create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/LICENSE create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/README.md create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventTest.php create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/GenericEventTest.php create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.php create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/composer.json create mode 100644 vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/phpunit.xml.dist create mode 100644 vendor/symfony/filesystem/Symfony/Component/Filesystem/.gitignore create mode 100644 vendor/symfony/filesystem/Symfony/Component/Filesystem/CHANGELOG.md create mode 100644 vendor/symfony/filesystem/Symfony/Component/Filesystem/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/filesystem/Symfony/Component/Filesystem/Exception/IOException.php create mode 100644 vendor/symfony/filesystem/Symfony/Component/Filesystem/Filesystem.php create mode 100644 vendor/symfony/filesystem/Symfony/Component/Filesystem/LICENSE create mode 100644 vendor/symfony/filesystem/Symfony/Component/Filesystem/README.md create mode 100644 vendor/symfony/filesystem/Symfony/Component/Filesystem/Tests/FilesystemTest.php create mode 100644 vendor/symfony/filesystem/Symfony/Component/Filesystem/composer.json create mode 100644 vendor/symfony/filesystem/Symfony/Component/Filesystem/phpunit.xml.dist create mode 100644 vendor/symfony/form/Symfony/Component/Form/.gitignore create mode 100644 vendor/symfony/form/Symfony/Component/Form/AbstractExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/AbstractRendererEngine.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/AbstractType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/AbstractTypeExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Button.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/ButtonBuilder.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/ButtonTypeInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/CHANGELOG.md create mode 100644 vendor/symfony/form/Symfony/Component/Form/CallbackTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/ClickableInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/DataMapperInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/DataTransformerInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Exception/AlreadyBoundException.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Exception/AlreadySubmittedException.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Exception/BadMethodCallException.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Exception/ErrorMappingException.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Exception/InvalidArgumentException.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Exception/InvalidConfigurationException.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Exception/LogicException.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Exception/OutOfBoundsException.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Exception/RuntimeException.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Exception/StringCastException.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Exception/TransformationFailedException.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Exception/UnexpectedTypeException.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/ChoiceList.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/ChoiceListInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/LazyChoiceList.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/ObjectChoiceList.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/ChoiceList/SimpleChoiceList.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/CoreExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ArrayToPartsTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/BaseDateTimeTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/BooleanToStringTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ChoiceToBooleanArrayTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ChoiceToValueTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ChoicesToBooleanArrayTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ChoicesToValuesTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DataTransformerChain.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToArrayTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToRfc3339Transformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToStringTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToTimestampTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/PercentToLocalizedStringTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/DataTransformer/ValueToDuplicatesTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/FixCheckboxInputListener.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/FixRadioInputListener.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/FixUrlProtocolListener.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/MergeCollectionListener.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/ResizeFormListener.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/EventListener/TrimListener.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/BaseType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/BirthdayType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/ButtonType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/CollectionType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/CountryType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/DateType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/EmailType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/FileType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/FormType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/HiddenType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/IntegerType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/LanguageType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/LocaleType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/MoneyType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/NumberType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/PasswordType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/PercentType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/RadioType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/RepeatedType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/ResetType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/SearchType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/SubmitType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/TextType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/TextareaType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/TimeType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/UrlType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Core/View/ChoiceView.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/CsrfProvider/CsrfProviderInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/CsrfProvider/SessionCsrfProvider.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/DependencyInjection/DependencyInjectionExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/HttpFoundation/EventListener/BindRequestListener.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/HttpFoundation/HttpFoundationRequestHandler.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/HttpFoundation/Type/FormTypeHttpFoundationExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Templating/TemplatingExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Templating/TemplatingRendererEngine.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Constraints/Form.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/BaseValidatorExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/RepeatedTypeValidatorExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Type/SubmitTypeValidatorExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/Util/ServerParams.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ValidatorExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/MappingRule.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/RelativePath.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapperInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPath.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPathIterator.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Form.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormBuilder.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormBuilderInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormConfigBuilder.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormConfigBuilderInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormConfigInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormError.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormEvent.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormEvents.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormExtensionInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormFactory.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormFactoryBuilder.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormFactoryBuilderInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormFactoryInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormRegistry.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormRegistryInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormRenderer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormRendererEngineInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormRendererInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormTypeExtensionInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormTypeGuesserChain.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormTypeGuesserInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormTypeInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/FormView.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Forms.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Guess/Guess.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Guess/TypeGuess.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Guess/ValueGuess.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/LICENSE create mode 100644 vendor/symfony/form/Symfony/Component/Form/NativeRequestHandler.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/PreloadedExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/README.md create mode 100644 vendor/symfony/form/Symfony/Component/Form/RequestHandlerInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/ResolvedFormType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/ResolvedFormTypeFactory.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/ResolvedFormTypeFactoryInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/ResolvedFormTypeInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/config/validation.xml create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ar.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.bg.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ca.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.cs.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.da.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.de.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.el.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.en.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.es.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.et.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.eu.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.fa.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.fi.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.fr.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.gl.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.he.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.hr.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.hu.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.hy.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.id.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.it.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ja.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.lb.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.lt.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.lv.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.mn.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.nb.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.nl.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.pl.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.pt.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.pt_BR.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ro.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ru.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sk.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sl.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sr_Cyrl.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sr_Latn.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.sv.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.ua.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/Resources/translations/validators.zh_CN.xlf create mode 100644 vendor/symfony/form/Symfony/Component/Form/ReversedTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/SubmitButton.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/SubmitButtonBuilder.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/SubmitButtonTypeInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Test/DeprecationErrorHandler.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Test/FormBuilderInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Test/FormIntegrationTestCase.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Test/FormInterface.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Test/FormPerformanceTestCase.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Test/TypeTestCase.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/AbstractExtensionTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/AbstractFormTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/AbstractLayoutTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/AbstractRequestHandlerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/CompoundFormPerformanceTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/CompoundFormTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/ChoiceListTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/LazyChoiceListTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/ObjectChoiceListTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/SimpleChoiceListTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ArrayToPartsTransformerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BooleanToStringTransformerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoiceToValueTransformerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoicesToValuesTransformerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DataTransformerChainTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeTestCase.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToArrayTransformerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToRfc3339TransformerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToStringTransformerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToTimestampTransformerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/PercentToLocalizedStringTransformerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ValueToDuplicatesTransformerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/FixRadioInputListenerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/FixUrlProtocolListenerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/Fixtures/randomhash create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayObjectTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerCustomArrayObjectTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/ResizeFormListenerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/EventListener/TrimListenerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/BaseTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/ButtonTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypePerformanceTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/CountryTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/CurrencyTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/LanguageTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/LocaleTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/MoneyTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/PasswordTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/SubmitTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/TimezoneTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/TypeTestCase.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Core/Type/UrlTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Csrf/CsrfProvider/DefaultCsrfProviderTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Csrf/CsrfProvider/SessionCsrfProviderTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/CsrfValidationListenerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/HttpFoundation/EventListener/BindRequestListenerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/HttpFoundation/HttpFoundationRequestHandlerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/EventListener/ValidationListenerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/Type/TypeTestCase.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/Util/ServerParamsTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationPathTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/AlternatingRowType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/Author.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/AuthorType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/CustomArrayObject.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FixedDataTransformer.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FixedFilterListener.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooSubType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooSubTypeWithParentInstance.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooType.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooTypeBarExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/FooTypeBazExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/TestExtension.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Fixtures/foo create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/FormBuilderTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/FormConfigTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/FormFactoryBuilderTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/FormFactoryTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/FormIntegrationTestCase.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/FormPerformanceTestCase.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/FormRegistryTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/FormRendererTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/Guess/GuessTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/NativeRequestHandlerTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/ResolvedFormTypeTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Tests/SimpleFormTest.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Util/FormUtil.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Util/InheritDataAwareIterator.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/Util/VirtualFormAwareIterator.php create mode 100644 vendor/symfony/form/Symfony/Component/Form/composer.json create mode 100644 vendor/symfony/form/Symfony/Component/Form/phpunit.xml.dist create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/.gitignore create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/IcuCurrencyBundle.php create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/IcuData.php create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/IcuLanguageBundle.php create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/IcuLocaleBundle.php create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/IcuRegionBundle.php create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/LICENSE create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/README.md create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/Resources/data/curr/en.php create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/Resources/data/lang/en.php create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/Resources/data/locales/en.php create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/Resources/data/region/en.php create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/Resources/data/version.txt create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/Tests/IcuIntegrationTest.php create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/composer.json create mode 100644 vendor/symfony/icu/Symfony/Component/Icu/phpunit.xml.dist create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/.gitignore create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/CONTRIBUTING.md create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Collator/Collator.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/AmPmTransformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/DayOfWeekTransformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/DayOfYearTransformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/DayTransformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/FullTransformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Hour1200Transformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Hour1201Transformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Hour2400Transformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Hour2401Transformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/HourTransformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/MinuteTransformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/MonthTransformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/QuarterTransformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/SecondTransformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/TimeZoneTransformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/Transformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/DateFormat/YearTransformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/DateFormatter/IntlDateFormatter.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Exception/BadMethodCallException.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Exception/InvalidArgumentException.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Exception/MethodArgumentNotImplementedException.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Exception/MethodArgumentValueNotImplementedException.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Exception/MethodNotImplementedException.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Exception/NotImplementedException.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Exception/OutOfBoundsException.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Exception/RuntimeException.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Globals/IntlGlobals.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Intl.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/LICENSE create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Locale/Locale.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/README.md create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/AbstractBundle.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Compiler/BundleCompiler.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Compiler/BundleCompilerInterface.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/CurrencyBundle.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/CurrencyBundleInterface.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/LanguageBundle.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/LanguageBundleInterface.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/LocaleBundle.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/LocaleBundleInterface.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/AbstractBundleReader.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/BinaryBundleReader.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/BufferedBundleReader.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/BundleReaderInterface.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/PhpBundleReader.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/StructuredBundleReader.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Reader/StructuredBundleReaderInterface.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/RegionBundle.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/RegionBundleInterface.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/ResourceBundleInterface.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/BundleTransformer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/CompilationContext.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/CompilationContextInterface.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/CurrencyBundleTransformationRule.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/LanguageBundleTransformationRule.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/LocaleBundleTransformationRule.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/RegionBundleTransformationRule.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/TransformationRuleInterface.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/StubbingContext.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Transformer/StubbingContextInterface.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Util/ArrayAccessibleResourceBundle.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Util/RecursiveArrayAccess.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Util/RingBuffer.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Writer/BundleWriterInterface.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Writer/PhpBundleWriter.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/ResourceBundle/Writer/TextBundleWriter.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/autoload.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/common.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/copy-stubs-to-component.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/create-stubs.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/icu-version.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/icu.ini create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/test-compat.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/update-icu-component.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Resources/bin/util/test-compat-helper.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Resources/stubs/Collator.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Resources/stubs/IntlDateFormatter.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Resources/stubs/Locale.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Resources/stubs/NumberFormatter.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Resources/stubs/functions.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/Collator/AbstractCollatorTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/Collator/CollatorTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/Collator/Verification/CollatorTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/DateFormatter/AbstractIntlDateFormatterTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/DateFormatter/IntlDateFormatterTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/DateFormatter/Verification/IntlDateFormatterTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/Globals/AbstractIntlGlobalsTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/Globals/IntlGlobalsTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/Globals/Verification/IntlGlobalsTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/Locale/AbstractLocaleTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/Locale/LocaleTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/Locale/Verification/LocaleTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/NumberFormatter/AbstractNumberFormatterTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/NumberFormatter/NumberFormatterTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/NumberFormatter/Verification/NumberFormatterTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/AbstractBundleTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/CurrencyBundleTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/LanguageBundleTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/LocaleBundleTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/AbstractBundleReaderTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/BinaryBundleReaderTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/NotAFile/en.php/.gitkeep create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/en.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/en.res create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/en.txt create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/PhpBundleReaderTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Reader/StructuredBundleReaderTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/RegionBundleTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Util/RingBufferTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Writer/Fixtures/en.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Writer/Fixtures/en.res create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Writer/Fixtures/en.txt create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Writer/PhpBundleWriterTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/ResourceBundle/Writer/TextBundleWriterTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/Util/IcuVersionTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Tests/Util/VersionTest.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Util/IcuVersion.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Util/IntlTestHelper.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Util/SvnCommit.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Util/SvnRepository.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/Util/Version.php create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/composer.json create mode 100644 vendor/symfony/intl/Symfony/Component/Intl/phpunit.xml.dist create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/.gitignore create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/InvalidOptionsException.php create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/MissingOptionsException.php create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/OptionDefinitionException.php create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/LICENSE create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Options.php create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/OptionsResolver.php create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/OptionsResolverInterface.php create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/README.md create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Tests/OptionsTest.php create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/composer.json create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/phpunit.xml.dist create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/.gitattributes create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/.gitignore create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/CHANGELOG.md create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/InvalidPropertyPathException.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/NoSuchPropertyException.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/OutOfBoundsException.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/RuntimeException.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/UnexpectedTypeException.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/LICENSE create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccess.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccessor.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccessorBuilder.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccessorInterface.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPath.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathBuilder.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathInterface.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathIterator.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathIteratorInterface.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/README.md create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/StringUtil.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/composer.json create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/.gitignore create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Annotation/Route.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/CHANGELOG.md create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/CompiledRoute.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Exception/InvalidParameterException.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Exception/MethodNotAllowedException.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Exception/MissingMandatoryParametersException.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Exception/ResourceNotFoundException.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Exception/RouteNotFoundException.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Generator/ConfigurableRequirementsInterface.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Generator/Dumper/GeneratorDumper.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Generator/Dumper/GeneratorDumperInterface.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Generator/UrlGenerator.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Generator/UrlGeneratorInterface.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/LICENSE create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Loader/AnnotationClassLoader.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Loader/AnnotationDirectoryLoader.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Loader/AnnotationFileLoader.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Loader/ClosureLoader.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Loader/PhpFileLoader.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Loader/XmlFileLoader.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Loader/YamlFileLoader.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Matcher/ApacheUrlMatcher.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/ApacheMatcherDumper.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/DumperCollection.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/DumperPrefixCollection.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/DumperRoute.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/MatcherDumper.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/MatcherDumperInterface.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Matcher/RedirectableUrlMatcher.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Matcher/RedirectableUrlMatcherInterface.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Matcher/RequestMatcherInterface.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Matcher/TraceableUrlMatcher.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Matcher/UrlMatcher.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Matcher/UrlMatcherInterface.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/README.md create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/RequestContext.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/RequestContextAwareInterface.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Route.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/RouteCollection.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/RouteCompiler.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/RouteCompilerInterface.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Router.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/RouterInterface.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Annotation/RouteTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/CompiledRouteTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/AbstractClass.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/BarClass.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/FooClass.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/CustomXmlFileLoader.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/RedirectableUrlMatcher.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/annotated.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.apache create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.apache create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher3.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/empty.yml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/foo.xml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/foo1.xml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/incomplete.yml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/missing_id.xml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/missing_path.xml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/namespaceprefix.xml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonesense_resource_plus_path.yml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonesense_type_without_resource.yml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalid.xml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalid.yml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalid2.yml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalidkeys.yml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalidnode.xml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalidroute.xml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/special_route_name.yml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validpattern.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validpattern.xml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validpattern.yml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validresource.xml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validresource.yml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/withdoctype.xml create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/AbstractAnnotationLoaderTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/AnnotationDirectoryLoaderTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/AnnotationFileLoaderTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/ClosureLoaderTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/ApacheUrlMatcherTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/Dumper/ApacheMatcherDumperTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/Dumper/DumperCollectionTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/Dumper/DumperPrefixCollectionTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/RedirectableUrlMatcherTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/TraceableUrlMatcherTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/RouteCollectionTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/RouteCompilerTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/RouteTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/Tests/RouterTest.php create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/composer.json create mode 100644 vendor/symfony/routing/Symfony/Component/Routing/phpunit.xml.dist create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/.gitignore create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/CHANGELOG.md create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Catalogue/AbstractOperation.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Catalogue/DiffOperation.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Catalogue/MergeOperation.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Catalogue/OperationInterface.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Dumper/CsvFileDumper.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Dumper/DumperInterface.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Dumper/FileDumper.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Dumper/IcuResFileDumper.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Dumper/IniFileDumper.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Dumper/MoFileDumper.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Dumper/PhpFileDumper.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Dumper/PoFileDumper.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Dumper/QtFileDumper.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Dumper/XliffFileDumper.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Dumper/YamlFileDumper.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Exception/InvalidResourceException.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Exception/NotFoundResourceException.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Extractor/ChainExtractor.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Extractor/ExtractorInterface.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/IdentityTranslator.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Interval.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/LICENSE create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Loader/ArrayLoader.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Loader/CsvFileLoader.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Loader/IcuDatFileLoader.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Loader/IcuResFileLoader.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Loader/IniFileLoader.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Loader/LoaderInterface.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Loader/MoFileLoader.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Loader/PhpFileLoader.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Loader/PoFileLoader.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Loader/QtFileLoader.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Loader/XliffFileLoader.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Loader/YamlFileLoader.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Loader/schema/dic/xliff-core/xliff-core-1.2-strict.xsd create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Loader/schema/dic/xliff-core/xml.xsd create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/MessageCatalogue.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/MessageCatalogueInterface.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/MessageSelector.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/MetadataAwareInterface.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/PluralizationRules.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/README.md create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Catalogue/AbstractOperationTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Catalogue/DiffOperationTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Catalogue/MergeOperationTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/CsvFileDumperTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/IcuResFileDumperTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/IniFileDumperTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/MoFileDumperTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/PhpFileDumperTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/PoFileDumperTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/QtFileDumperTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/XliffFileDumperTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Dumper/YamlFileDumperTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/IdentityTranslatorTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/IntervalTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/CsvFileLoaderTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/IcuDatFileLoaderTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/IcuResFileLoaderTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/IniFileLoaderTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/LocalizedTestCase.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/MoFileLoaderTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/PhpFileLoaderTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/PoFileLoaderTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/QtFileLoaderTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/XliffFileLoaderTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/Loader/YamlFileLoaderTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/MessageCatalogueTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/MessageSelectorTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/PluralizationRulesTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/TranslatorTest.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty-translation.po create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty.csv create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty.ini create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty.mo create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty.po create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/empty.yml create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/encoding.xlf create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/invalid-xml-resources.xlf create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/non-valid.xlf create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/non-valid.yml create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/plurals.mo create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/plurals.po create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resname.xlf create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/corrupted/resources.dat create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/en.res create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/en.txt create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/fr.res create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/fr.txt create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/packagelist.txt create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/dat/resources.dat create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resourcebundle/res/en.res create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources-clean.xlf create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.csv create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.ini create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.mo create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.po create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.ts create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.xlf create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/resources.yml create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/valid.csv create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Tests/fixtures/withdoctype.xlf create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Translator.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/TranslatorInterface.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/Writer/TranslationWriter.php create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/composer.json create mode 100644 vendor/symfony/translation/Symfony/Component/Translation/phpunit.xml.dist create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/.gitignore create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/CHANGELOG.md create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/CodeExtension.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/FormExtension.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/HttpKernelExtension.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/RoutingExtension.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/SecurityExtension.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/TranslationExtension.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/YamlExtension.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Form/TwigRenderer.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Form/TwigRendererEngine.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Form/TwigRendererEngineInterface.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Form/TwigRendererInterface.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/LICENSE create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Node/FormEnctypeNode.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Node/FormThemeNode.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Node/RenderBlockNode.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Node/SearchAndRenderBlockNode.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Node/TransDefaultDomainNode.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Node/TransNode.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/NodeVisitor/Scope.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/NodeVisitor/TranslationDefaultDomainNodeVisitor.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/NodeVisitor/TranslationNodeVisitor.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/README.md create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/CodeExtensionTest.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/Fixtures/StubFilesystemLoader.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/Fixtures/StubTranslator.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/RoutingExtensionTest.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/child_label.html.twig create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/custom_widgets.html.twig create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/parent_label.html.twig create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/theme.html.twig create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/theme_extends.html.twig create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Extension/theme_use.html.twig create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Node/FormThemeTest.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Node/SearchAndRenderBlockNodeTest.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/NodeVisitor/ScopeTest.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationDefaultDomainNodeVisitorTest.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationNodeVisitorTest.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/NodeVisitor/TwigNodeProvider.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/TestCase.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/TokenParser/FormThemeTokenParser.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/TokenParser/TransChoiceTokenParser.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/TokenParser/TransDefaultDomainTokenParser.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/TokenParser/TransTokenParser.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Translation/TwigExtractor.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/TwigEngine.php create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/composer.json create mode 100644 vendor/symfony/twig-bridge/Symfony/Bridge/Twig/phpunit.xml.dist create mode 160000 vendor/twig/extensions create mode 100644 vendor/twig/twig/.editorconfig create mode 100644 vendor/twig/twig/.gitignore create mode 100644 vendor/twig/twig/.travis.yml create mode 100644 vendor/twig/twig/AUTHORS create mode 100644 vendor/twig/twig/CHANGELOG create mode 100644 vendor/twig/twig/LICENSE create mode 100644 vendor/twig/twig/README.markdown create mode 100644 vendor/twig/twig/composer.json create mode 100644 vendor/twig/twig/doc/advanced.rst create mode 100644 vendor/twig/twig/doc/advanced_legacy.rst create mode 100644 vendor/twig/twig/doc/api.rst create mode 100644 vendor/twig/twig/doc/coding_standards.rst create mode 100644 vendor/twig/twig/doc/deprecated.rst create mode 100644 vendor/twig/twig/doc/filters/abs.rst create mode 100644 vendor/twig/twig/doc/filters/batch.rst create mode 100644 vendor/twig/twig/doc/filters/capitalize.rst create mode 100644 vendor/twig/twig/doc/filters/convert_encoding.rst create mode 100644 vendor/twig/twig/doc/filters/date.rst create mode 100644 vendor/twig/twig/doc/filters/date_modify.rst create mode 100644 vendor/twig/twig/doc/filters/default.rst create mode 100644 vendor/twig/twig/doc/filters/escape.rst create mode 100644 vendor/twig/twig/doc/filters/first.rst create mode 100644 vendor/twig/twig/doc/filters/format.rst create mode 100644 vendor/twig/twig/doc/filters/index.rst create mode 100644 vendor/twig/twig/doc/filters/join.rst create mode 100644 vendor/twig/twig/doc/filters/json_encode.rst create mode 100644 vendor/twig/twig/doc/filters/keys.rst create mode 100644 vendor/twig/twig/doc/filters/last.rst create mode 100644 vendor/twig/twig/doc/filters/length.rst create mode 100644 vendor/twig/twig/doc/filters/lower.rst create mode 100644 vendor/twig/twig/doc/filters/merge.rst create mode 100644 vendor/twig/twig/doc/filters/nl2br.rst create mode 100644 vendor/twig/twig/doc/filters/number_format.rst create mode 100644 vendor/twig/twig/doc/filters/raw.rst create mode 100644 vendor/twig/twig/doc/filters/replace.rst create mode 100644 vendor/twig/twig/doc/filters/reverse.rst create mode 100644 vendor/twig/twig/doc/filters/slice.rst create mode 100644 vendor/twig/twig/doc/filters/sort.rst create mode 100644 vendor/twig/twig/doc/filters/split.rst create mode 100644 vendor/twig/twig/doc/filters/striptags.rst create mode 100644 vendor/twig/twig/doc/filters/title.rst create mode 100644 vendor/twig/twig/doc/filters/trim.rst create mode 100644 vendor/twig/twig/doc/filters/upper.rst create mode 100644 vendor/twig/twig/doc/filters/url_encode.rst create mode 100644 vendor/twig/twig/doc/functions/attribute.rst create mode 100644 vendor/twig/twig/doc/functions/block.rst create mode 100644 vendor/twig/twig/doc/functions/constant.rst create mode 100644 vendor/twig/twig/doc/functions/cycle.rst create mode 100644 vendor/twig/twig/doc/functions/date.rst create mode 100644 vendor/twig/twig/doc/functions/dump.rst create mode 100644 vendor/twig/twig/doc/functions/include.rst create mode 100644 vendor/twig/twig/doc/functions/index.rst create mode 100644 vendor/twig/twig/doc/functions/parent.rst create mode 100644 vendor/twig/twig/doc/functions/random.rst create mode 100644 vendor/twig/twig/doc/functions/range.rst create mode 100644 vendor/twig/twig/doc/functions/template_from_string.rst create mode 100644 vendor/twig/twig/doc/index.rst create mode 100644 vendor/twig/twig/doc/internals.rst create mode 100644 vendor/twig/twig/doc/intro.rst create mode 100644 vendor/twig/twig/doc/recipes.rst create mode 100644 vendor/twig/twig/doc/tags/autoescape.rst create mode 100644 vendor/twig/twig/doc/tags/block.rst create mode 100644 vendor/twig/twig/doc/tags/do.rst create mode 100644 vendor/twig/twig/doc/tags/embed.rst create mode 100644 vendor/twig/twig/doc/tags/extends.rst create mode 100644 vendor/twig/twig/doc/tags/filter.rst create mode 100644 vendor/twig/twig/doc/tags/flush.rst create mode 100644 vendor/twig/twig/doc/tags/for.rst create mode 100644 vendor/twig/twig/doc/tags/from.rst create mode 100644 vendor/twig/twig/doc/tags/if.rst create mode 100644 vendor/twig/twig/doc/tags/import.rst create mode 100644 vendor/twig/twig/doc/tags/include.rst create mode 100644 vendor/twig/twig/doc/tags/index.rst create mode 100644 vendor/twig/twig/doc/tags/macro.rst create mode 100644 vendor/twig/twig/doc/tags/sandbox.rst create mode 100644 vendor/twig/twig/doc/tags/set.rst create mode 100644 vendor/twig/twig/doc/tags/spaceless.rst create mode 100644 vendor/twig/twig/doc/tags/use.rst create mode 100644 vendor/twig/twig/doc/tags/verbatim.rst create mode 100644 vendor/twig/twig/doc/templates.rst create mode 100644 vendor/twig/twig/doc/tests/constant.rst create mode 100644 vendor/twig/twig/doc/tests/defined.rst create mode 100644 vendor/twig/twig/doc/tests/divisibleby.rst create mode 100644 vendor/twig/twig/doc/tests/empty.rst create mode 100644 vendor/twig/twig/doc/tests/even.rst create mode 100644 vendor/twig/twig/doc/tests/index.rst create mode 100644 vendor/twig/twig/doc/tests/iterable.rst create mode 100644 vendor/twig/twig/doc/tests/null.rst create mode 100644 vendor/twig/twig/doc/tests/odd.rst create mode 100644 vendor/twig/twig/doc/tests/sameas.rst create mode 100644 vendor/twig/twig/ext/twig/.gitignore create mode 100644 vendor/twig/twig/ext/twig/LICENSE create mode 100644 vendor/twig/twig/ext/twig/config.m4 create mode 100644 vendor/twig/twig/ext/twig/config.w32 create mode 100644 vendor/twig/twig/ext/twig/php_twig.h create mode 100644 vendor/twig/twig/ext/twig/twig.c rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Autoloader.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Compiler.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/CompilerInterface.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Environment.php (99%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Error.php (94%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Error/Loader.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Error/Runtime.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Error/Syntax.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/ExistsLoaderInterface.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/ExpressionParser.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Extension.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Extension/Core.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Extension/Debug.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Extension/Escaper.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Extension/Optimizer.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Extension/Sandbox.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Extension/Staging.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Extension/StringLoader.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/ExtensionInterface.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Filter.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Filter/Function.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Filter/Method.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Filter/Node.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/FilterCallableInterface.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/FilterInterface.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Function.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Function/Function.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Function/Method.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Function/Node.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/FunctionCallableInterface.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/FunctionInterface.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Lexer.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/LexerInterface.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Loader/Array.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Loader/Chain.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Loader/Filesystem.php (92%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Loader/String.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/LoaderInterface.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Markup.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/AutoEscape.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Block.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/BlockReference.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Body.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Do.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Embed.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Array.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/AssignName.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Binary.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Binary/Add.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Binary/And.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Binary/BitwiseAnd.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Binary/BitwiseOr.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Binary/BitwiseXor.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Binary/Concat.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Binary/Div.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Binary/Equal.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Binary/FloorDiv.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Binary/Greater.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Binary/GreaterEqual.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Binary/In.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Binary/Less.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Binary/LessEqual.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Binary/Mod.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Binary/Mul.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Binary/NotEqual.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Binary/NotIn.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Binary/Or.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Binary/Power.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Binary/Range.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Binary/Sub.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/BlockReference.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Call.php (95%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Conditional.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Constant.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/ExtensionReference.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Filter.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Filter/Default.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Function.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/GetAttr.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/MethodCall.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Name.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Parent.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/TempName.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Test.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Test/Constant.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Test/Defined.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Test/Divisibleby.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Test/Even.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Test/Null.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Test/Odd.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Test/Sameas.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Unary.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Unary/Neg.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Unary/Not.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Expression/Unary/Pos.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Flush.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/For.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/ForLoop.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/If.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Import.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Include.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Macro.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Module.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Print.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Sandbox.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/SandboxedModule.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/SandboxedPrint.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Set.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/SetTemp.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Spaceless.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Node/Text.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/NodeInterface.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/NodeOutputInterface.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/NodeTraverser.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/NodeVisitor/Escaper.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/NodeVisitor/Optimizer.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/NodeVisitor/SafeAnalysis.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/NodeVisitor/Sandbox.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/NodeVisitorInterface.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Parser.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/ParserInterface.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Sandbox/SecurityError.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Sandbox/SecurityPolicy.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Sandbox/SecurityPolicyInterface.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/SimpleFilter.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/SimpleFunction.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/SimpleTest.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Template.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/TemplateInterface.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Test.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Test/Function.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Test/IntegrationTestCase.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Test/Method.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Test/Node.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Test/NodeTestCase.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/TestCallableInterface.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/TestInterface.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/Token.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/TokenParser.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/TokenParser/AutoEscape.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/TokenParser/Block.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/TokenParser/Do.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/TokenParser/Embed.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/TokenParser/Extends.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/TokenParser/Filter.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/TokenParser/Flush.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/TokenParser/For.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/TokenParser/From.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/TokenParser/If.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/TokenParser/Import.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/TokenParser/Include.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/TokenParser/Macro.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/TokenParser/Sandbox.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/TokenParser/Set.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/TokenParser/Spaceless.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/TokenParser/Use.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/TokenParserBroker.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/TokenParserBrokerInterface.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/TokenParserInterface.php (100%) rename {inc/3rdparty => vendor/twig/twig/lib}/Twig/TokenStream.php (100%) create mode 100644 vendor/twig/twig/phpunit.xml.dist create mode 100644 vendor/twig/twig/test/Twig/Tests/AutoloaderTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/CompilerTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/EnvironmentTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/ErrorTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/ExpressionParserTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Extension/CoreTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Extension/SandboxTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/FileCachingTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/errors/base.html create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/errors/index.html create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/unclosed_tag.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/array.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/array_call.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/binary.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/bitwise.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/comparison.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/dotdot.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/grouping.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/literals.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/magic_call.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/method_call.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/postfix.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/strings.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/ternary_operator.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/ternary_operator_noelse.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/ternary_operator_nothen.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/unary.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/unary_precedence.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/abs.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch_float.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch_with_empty_fill.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch_with_fill.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/convert_encoding.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_default_format.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_default_format_interval.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_interval.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_modify.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_namedargs.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/default.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/dynamic_filter.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/escape.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/escape_non_supported_charset.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/first.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/force_escape.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/format.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/join.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/json_encode.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/last.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/length.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/length_utf8.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/merge.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/nl2br.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/number_format.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/number_format_default.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/replace.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/reverse.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/slice.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/sort.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/special_chars.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/split.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/trim.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/filters/urlencode.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/functions/attribute.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/functions/block.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/functions/constant.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/functions/cycle.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/functions/date.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/functions/date_namedargs.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/functions/dump.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/functions/dump_array.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/functions/dynamic_function.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/assignment.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/autoescaping.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/basic.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/expression.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/ignore_missing.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/missing.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/missing_nested.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/sandbox.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/template_instance.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/templates_as_array.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/with_context.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/with_variables.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/functions/range.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/functions/special_chars.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/functions/template_from_string.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/macros/default_values.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/macros/nested_calls.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/macros/reserved_variables.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/macros/simple.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/macros/with_filters.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/regression/empty_token.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/regression/simple_xml_element.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/regression/strings_like_numbers.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/basic.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/blocks.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/double_escaping.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/functions.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/literal.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/nested.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/objects.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/raw.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/strategy.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/type.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_filters.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_filters_arguments.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_pre_escape_filters.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_preserves_safety_filters.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/basic.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/block_unique_name.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/special_chars.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/basic.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/error_line.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/multiple.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/nested.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/with_extends.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/basic.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/json_encode.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/multiple.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/nested.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/with_for_tag.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/with_if_tag.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/condition.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/context.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/else.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/inner_variables.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/keys.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/keys_and_values.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_context.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_context_local.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_not_defined.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_not_defined_cond.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/nested_else.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/objects.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/objects_countable.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/recursive.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/values.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/from.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/if/basic.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/if/expression.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/basic.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/expression.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/ignore_missing.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/missing.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/missing_nested.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/only.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/template_instance.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/templates_as_array.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/with_variables.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/basic.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/conditional.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/dynamic.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/empty.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/extends_as_array.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/multiple.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/nested_blocks.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/nested_blocks_parent_only.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/nested_inheritance.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_change.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_in_a_block.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_isolation.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_nested.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends_but_traits.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/template_instance.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/use.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/basic.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/endmacro_name.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/external.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/global.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/self_import.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/special_chars.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/raw/basic.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/raw/mixed_usage_with_raw.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/raw/whitespace_control.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/not_valid1.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/not_valid2.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/simple.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/basic.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/capture-empty.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/capture.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/expression.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/spaceless/simple.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/special_chars.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/trim_block.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/aliases.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/basic.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/deep.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/deep_empty.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/multiple.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/multiple_aliases.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/basic.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/mixed_usage_with_raw.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/whitespace_control.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tests/array.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tests/constant.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tests/empty.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tests/even.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tests/in.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tests/in_with_objects.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tests/iterable.test create mode 100644 vendor/twig/twig/test/Twig/Tests/Fixtures/tests/odd.test create mode 100644 vendor/twig/twig/test/Twig/Tests/IntegrationTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/LexerTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Loader/ArrayTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Loader/ChainTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Loader/FilesystemTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named/index.html create mode 100644 vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named_bis/index.html create mode 100644 vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named_final/index.html create mode 100644 vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named_ter/index.html create mode 100644 vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal/index.html create mode 100644 vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal_bis/index.html create mode 100644 vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal_final/index.html create mode 100644 vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal_ter/index.html create mode 100644 vendor/twig/twig/test/Twig/Tests/NativeExtensionTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/AutoEscapeTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/BlockReferenceTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/BlockTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/DoTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/Expression/ArrayTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/Expression/AssignNameTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/AddTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/AndTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/ConcatTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/DivTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/FloorDivTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/ModTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/MulTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/OrTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/SubTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/Expression/CallTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/Expression/ConditionalTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/Expression/ConstantTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/Expression/FilterTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/Expression/FunctionTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/Expression/GetAttrTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/Expression/NameTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/Expression/PHP53/FilterInclude.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/Expression/PHP53/FunctionInclude.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/Expression/PHP53/TestInclude.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/Expression/ParentTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/Expression/TestTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/Expression/Unary/NegTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/Expression/Unary/NotTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/Expression/Unary/PosTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/ForTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/IfTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/ImportTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/IncludeTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/MacroTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/ModuleTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/PrintTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/SandboxTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/SandboxedModuleTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/SandboxedPrintTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/SetTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/SpacelessTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/Node/TextTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/NodeVisitor/OptimizerTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/ParserTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/TemplateTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/TokenStreamTest.php create mode 100644 vendor/twig/twig/test/Twig/Tests/escapingTest.php create mode 100644 vendor/twig/twig/test/bootstrap.php create mode 100644 vendor/umpirsky/twig-gettext-extractor/.gitignore create mode 100644 vendor/umpirsky/twig-gettext-extractor/.travis.yml create mode 100644 vendor/umpirsky/twig-gettext-extractor/LICENSE create mode 100644 vendor/umpirsky/twig-gettext-extractor/README.md rename {inc/3rdparty/Twig/Extensions => vendor/umpirsky/twig-gettext-extractor/Twig}/Gettext/Extractor.php (100%) rename {inc/3rdparty/Twig/Extensions => vendor/umpirsky/twig-gettext-extractor/Twig}/Gettext/Loader/Filesystem.php (100%) rename {inc/3rdparty/Twig/Extensions => vendor/umpirsky/twig-gettext-extractor/Twig}/Gettext/Routing/Generator/UrlGenerator.php (100%) rename {inc/3rdparty/Twig/Extensions => vendor/umpirsky/twig-gettext-extractor/Twig}/Gettext/Test/ExtractorTest.php (100%) rename {inc/3rdparty/Twig/Extensions => vendor/umpirsky/twig-gettext-extractor/Twig}/Gettext/Test/Fixtures/twig/empty.twig (100%) rename {inc/3rdparty/Twig/Extensions => vendor/umpirsky/twig-gettext-extractor/Twig}/Gettext/Test/Fixtures/twig/plural.twig (100%) rename {inc/3rdparty/Twig/Extensions => vendor/umpirsky/twig-gettext-extractor/Twig}/Gettext/Test/Fixtures/twig/singular.twig (100%) create mode 100644 vendor/umpirsky/twig-gettext-extractor/composer.json create mode 100644 vendor/umpirsky/twig-gettext-extractor/phpunit.xml.dist create mode 100755 vendor/umpirsky/twig-gettext-extractor/twig-gettext-extractor diff --git a/inc/3rdparty/Twig/Extensions/Autoloader.php b/inc/3rdparty/Twig/Extensions/Autoloader.php deleted file mode 100644 index f23cced..0000000 --- a/inc/3rdparty/Twig/Extensions/Autoloader.php +++ /dev/null @@ -1,45 +0,0 @@ - - */ -class Twig_Extensions_Autoloader -{ - /** - * Registers Twig_Extensions_Autoloader as an SPL autoloader. - */ - static public function register() - { - spl_autoload_register(array(new self, 'autoload')); - } - - /** - * Handles autoloading of classes. - * - * @param string $class A class name. - * - * @return boolean Returns true if the class has been loaded - */ - static public function autoload($class) - { - if (0 !== strpos($class, 'Twig_Extensions')) { - return; - } - - if (file_exists($file = dirname(__FILE__).'/../../'.str_replace('_', '/', $class).'.php')) { - require $file; - } - } -} diff --git a/inc/3rdparty/Twig/Extensions/Extension/Debug.php b/inc/3rdparty/Twig/Extensions/Extension/Debug.php deleted file mode 100644 index 8974ce2..0000000 --- a/inc/3rdparty/Twig/Extensions/Extension/Debug.php +++ /dev/null @@ -1,34 +0,0 @@ - new Twig_Filter_Function('gettext'), - ); - } - - /** - * Returns the name of the extension. - * - * @return string The extension name - */ - public function getName() - { - return 'i18n'; - } -} diff --git a/inc/3rdparty/Twig/Extensions/Extension/Intl.php b/inc/3rdparty/Twig/Extensions/Extension/Intl.php deleted file mode 100644 index 40f7fc2..0000000 --- a/inc/3rdparty/Twig/Extensions/Extension/Intl.php +++ /dev/null @@ -1,66 +0,0 @@ - new Twig_Filter_Function('twig_localized_date_filter', array('needs_environment' => true)), - ); - } - - /** - * Returns the name of the extension. - * - * @return string The extension name - */ - public function getName() - { - return 'intl'; - } -} - -function twig_localized_date_filter(Twig_Environment $env, $date, $dateFormat = 'medium', $timeFormat = 'medium', $locale = null, $timezone = null, $format = null) -{ - $date = twig_date_converter($env, $date, $timezone); - - $formatValues = array( - 'none' => IntlDateFormatter::NONE, - 'short' => IntlDateFormatter::SHORT, - 'medium' => IntlDateFormatter::MEDIUM, - 'long' => IntlDateFormatter::LONG, - 'full' => IntlDateFormatter::FULL, - ); - - $formatter = IntlDateFormatter::create( - $locale !== null ? $locale : Locale::getDefault(), - $formatValues[$dateFormat], - $formatValues[$timeFormat], - $date->getTimezone()->getName(), - IntlDateFormatter::GREGORIAN, - $format - ); - - return $formatter->format($date->getTimestamp()); -} diff --git a/inc/3rdparty/Twig/Extensions/Extension/Text.php b/inc/3rdparty/Twig/Extensions/Extension/Text.php deleted file mode 100644 index 0a3dc35..0000000 --- a/inc/3rdparty/Twig/Extensions/Extension/Text.php +++ /dev/null @@ -1,109 +0,0 @@ - - * @package Twig - * @subpackage Twig-extensions - */ -class Twig_Extensions_Extension_Text extends Twig_Extension -{ - /** - * Returns a list of filters. - * - * @return array - */ - public function getFilters() - { - $filters = array( - 'truncate' => new Twig_Filter_Function('twig_truncate_filter', array('needs_environment' => true)), - 'wordwrap' => new Twig_Filter_Function('twig_wordwrap_filter', array('needs_environment' => true)), - ); - - if (version_compare(Twig_Environment::VERSION, '1.5.0-DEV', '<')) { - $filters['nl2br'] = new Twig_Filter_Function('twig_nl2br_filter', array('pre_escape' => 'html', 'is_safe' => array('html'))); - } - - return $filters; - } - - /** - * Name of this extension - * - * @return string - */ - public function getName() - { - return 'Text'; - } -} - -function twig_nl2br_filter($value, $sep = '
    ') -{ - return str_replace("\n", $sep."\n", $value); -} - -if (function_exists('mb_get_info')) { - function twig_truncate_filter(Twig_Environment $env, $value, $length = 30, $preserve = false, $separator = '...') - { - if (mb_strlen($value, $env->getCharset()) > $length) { - if ($preserve) { - if (false !== ($breakpoint = mb_strpos($value, ' ', $length, $env->getCharset()))) { - $length = $breakpoint; - } - } - - return rtrim(mb_substr($value, 0, $length, $env->getCharset())) . $separator; - } - - return $value; - } - - function twig_wordwrap_filter(Twig_Environment $env, $value, $length = 80, $separator = "\n", $preserve = false) - { - $sentences = array(); - - $previous = mb_regex_encoding(); - mb_regex_encoding($env->getCharset()); - - $pieces = mb_split($separator, $value); - mb_regex_encoding($previous); - - foreach ($pieces as $piece) { - while(!$preserve && mb_strlen($piece, $env->getCharset()) > $length) { - $sentences[] = mb_substr($piece, 0, $length, $env->getCharset()); - $piece = mb_substr($piece, $length, 2048, $env->getCharset()); - } - - $sentences[] = $piece; - } - - return implode($separator, $sentences); - } -} else { - function twig_truncate_filter(Twig_Environment $env, $value, $length = 30, $preserve = false, $separator = '...') - { - if (strlen($value) > $length) { - if ($preserve) { - if (false !== ($breakpoint = strpos($value, ' ', $length))) { - $length = $breakpoint; - } - } - - return rtrim(substr($value, 0, $length)) . $separator; - } - - return $value; - } - - function twig_wordwrap_filter(Twig_Environment $env, $value, $length = 80, $separator = "\n", $preserve = false) - { - return wordwrap($value, $length, $separator, !$preserve); - } -} \ No newline at end of file diff --git a/inc/3rdparty/Twig/Extensions/Grammar.php b/inc/3rdparty/Twig/Extensions/Grammar.php deleted file mode 100644 index 4d031b1..0000000 --- a/inc/3rdparty/Twig/Extensions/Grammar.php +++ /dev/null @@ -1,30 +0,0 @@ -name = $name; - } - - public function setParser(Twig_ParserInterface $parser) - { - $this->parser = $parser; - } - - public function getName() - { - return $this->name; - } -} diff --git a/inc/3rdparty/Twig/Extensions/Grammar/Arguments.php b/inc/3rdparty/Twig/Extensions/Grammar/Arguments.php deleted file mode 100644 index 158c05a..0000000 --- a/inc/3rdparty/Twig/Extensions/Grammar/Arguments.php +++ /dev/null @@ -1,22 +0,0 @@ -', $this->name); - } - - public function parse(Twig_Token $token) - { - return $this->parser->getExpressionParser()->parseArguments(); - } -} diff --git a/inc/3rdparty/Twig/Extensions/Grammar/Array.php b/inc/3rdparty/Twig/Extensions/Grammar/Array.php deleted file mode 100644 index 34aece0..0000000 --- a/inc/3rdparty/Twig/Extensions/Grammar/Array.php +++ /dev/null @@ -1,22 +0,0 @@ -', $this->name); - } - - public function parse(Twig_Token $token) - { - return $this->parser->getExpressionParser()->parseArrayExpression(); - } -} diff --git a/inc/3rdparty/Twig/Extensions/Grammar/Body.php b/inc/3rdparty/Twig/Extensions/Grammar/Body.php deleted file mode 100644 index 540cfc7..0000000 --- a/inc/3rdparty/Twig/Extensions/Grammar/Body.php +++ /dev/null @@ -1,39 +0,0 @@ -end = null === $end ? 'end'.$name : $end; - } - - public function __toString() - { - return sprintf('<%s:body>', $this->name); - } - - public function parse(Twig_Token $token) - { - $stream = $this->parser->getStream(); - $stream->expect(Twig_Token::BLOCK_END_TYPE); - - return $this->parser->subparse(array($this, 'decideBlockEnd'), true); - } - - public function decideBlockEnd(Twig_Token $token) - { - return $token->test($this->end); - } -} diff --git a/inc/3rdparty/Twig/Extensions/Grammar/Boolean.php b/inc/3rdparty/Twig/Extensions/Grammar/Boolean.php deleted file mode 100644 index c004809..0000000 --- a/inc/3rdparty/Twig/Extensions/Grammar/Boolean.php +++ /dev/null @@ -1,24 +0,0 @@ -', $this->name); - } - - public function parse(Twig_Token $token) - { - $this->parser->getStream()->expect(Twig_Token::NAME_TYPE, array('true', 'false')); - - return new Twig_Node_Expression_Constant('true' === $token->getValue() ? true : false, $token->getLine()); - } -} diff --git a/inc/3rdparty/Twig/Extensions/Grammar/Constant.php b/inc/3rdparty/Twig/Extensions/Grammar/Constant.php deleted file mode 100644 index 9df6045..0000000 --- a/inc/3rdparty/Twig/Extensions/Grammar/Constant.php +++ /dev/null @@ -1,37 +0,0 @@ -name = $name; - $this->type = null === $type ? Twig_Token::NAME_TYPE : $type; - } - - public function __toString() - { - return $this->name; - } - - public function parse(Twig_Token $token) - { - $this->parser->getStream()->expect($this->type, $this->name); - - return $this->name; - } - - public function getType() - { - return $this->type; - } -} diff --git a/inc/3rdparty/Twig/Extensions/Grammar/Expression.php b/inc/3rdparty/Twig/Extensions/Grammar/Expression.php deleted file mode 100644 index 4c33df0..0000000 --- a/inc/3rdparty/Twig/Extensions/Grammar/Expression.php +++ /dev/null @@ -1,22 +0,0 @@ -', $this->name); - } - - public function parse(Twig_Token $token) - { - return $this->parser->getExpressionParser()->parseExpression(); - } -} diff --git a/inc/3rdparty/Twig/Extensions/Grammar/Hash.php b/inc/3rdparty/Twig/Extensions/Grammar/Hash.php deleted file mode 100644 index 98b07d2..0000000 --- a/inc/3rdparty/Twig/Extensions/Grammar/Hash.php +++ /dev/null @@ -1,22 +0,0 @@ -', $this->name); - } - - public function parse(Twig_Token $token) - { - return $this->parser->getExpressionParser()->parseHashExpression(); - } -} diff --git a/inc/3rdparty/Twig/Extensions/Grammar/Number.php b/inc/3rdparty/Twig/Extensions/Grammar/Number.php deleted file mode 100644 index f0857d2..0000000 --- a/inc/3rdparty/Twig/Extensions/Grammar/Number.php +++ /dev/null @@ -1,24 +0,0 @@ -', $this->name); - } - - public function parse(Twig_Token $token) - { - $this->parser->getStream()->expect(Twig_Token::NUMBER_TYPE); - - return new Twig_Node_Expression_Constant($token->getValue(), $token->getLine()); - } -} diff --git a/inc/3rdparty/Twig/Extensions/Grammar/Optional.php b/inc/3rdparty/Twig/Extensions/Grammar/Optional.php deleted file mode 100644 index da42748..0000000 --- a/inc/3rdparty/Twig/Extensions/Grammar/Optional.php +++ /dev/null @@ -1,69 +0,0 @@ -grammar = array(); - foreach (func_get_args() as $grammar) { - $this->addGrammar($grammar); - } - } - - public function __toString() - { - $repr = array(); - foreach ($this->grammar as $grammar) { - $repr[] = (string) $grammar; - } - - return sprintf('[%s]', implode(' ', $repr)); - } - - public function addGrammar(Twig_Extensions_GrammarInterface $grammar) - { - $this->grammar[] = $grammar; - } - - public function parse(Twig_Token $token) - { - // test if we have the optional element before consuming it - if ($this->grammar[0] instanceof Twig_Extensions_Grammar_Constant) { - if (!$this->parser->getStream()->test($this->grammar[0]->getType(), $this->grammar[0]->getName())) { - return array(); - } - } elseif ($this->grammar[0] instanceof Twig_Extensions_Grammar_Name) { - if (!$this->parser->getStream()->test(Twig_Token::NAME_TYPE)) { - return array(); - } - } elseif ($this->parser->getStream()->test(Twig_Token::BLOCK_END_TYPE)) { - // if this is not a Constant or a Name, it must be the last element of the tag - - return array(); - } - - $elements = array(); - foreach ($this->grammar as $grammar) { - $grammar->setParser($this->parser); - - $element = $grammar->parse($token); - if (is_array($element)) { - $elements = array_merge($elements, $element); - } else { - $elements[$grammar->getName()] = $element; - } - } - - return $elements; - } -} diff --git a/inc/3rdparty/Twig/Extensions/Grammar/Switch.php b/inc/3rdparty/Twig/Extensions/Grammar/Switch.php deleted file mode 100644 index 4245f2c..0000000 --- a/inc/3rdparty/Twig/Extensions/Grammar/Switch.php +++ /dev/null @@ -1,24 +0,0 @@ -', $this->name); - } - - public function parse(Twig_Token $token) - { - $this->parser->getStream()->expect(Twig_Token::NAME_TYPE, $this->name); - - return new Twig_Node_Expression_Constant(true, $token->getLine()); - } -} diff --git a/inc/3rdparty/Twig/Extensions/Grammar/Tag.php b/inc/3rdparty/Twig/Extensions/Grammar/Tag.php deleted file mode 100644 index 727f261..0000000 --- a/inc/3rdparty/Twig/Extensions/Grammar/Tag.php +++ /dev/null @@ -1,56 +0,0 @@ -grammar = array(); - foreach (func_get_args() as $grammar) { - $this->addGrammar($grammar); - } - } - - public function __toString() - { - $repr = array(); - foreach ($this->grammar as $grammar) { - $repr[] = (string) $grammar; - } - - return implode(' ', $repr); - } - - public function addGrammar(Twig_Extensions_GrammarInterface $grammar) - { - $this->grammar[] = $grammar; - } - - public function parse(Twig_Token $token) - { - $elements = array(); - foreach ($this->grammar as $grammar) { - $grammar->setParser($this->parser); - - $element = $grammar->parse($token); - if (is_array($element)) { - $elements = array_merge($elements, $element); - } else { - $elements[$grammar->getName()] = $element; - } - } - - $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); - - return $elements; - } -} diff --git a/inc/3rdparty/Twig/Extensions/GrammarInterface.php b/inc/3rdparty/Twig/Extensions/GrammarInterface.php deleted file mode 100644 index 22713bf..0000000 --- a/inc/3rdparty/Twig/Extensions/GrammarInterface.php +++ /dev/null @@ -1,18 +0,0 @@ - - * @version SVN: $Id$ - */ -class Twig_Extensions_Node_Debug extends Twig_Node -{ - public function __construct(Twig_Node_Expression $expr = null, $lineno, $tag = null) - { - parent::__construct(array('expr' => $expr), array(), $lineno, $tag); - } - - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler A Twig_Compiler instance - */ - public function compile(Twig_Compiler $compiler) - { - $compiler->addDebugInfo($this); - - $compiler - ->write("if (\$this->env->isDebug()) {\n") - ->indent() - ; - - if (null === $this->getNode('expr')) { - // remove embedded templates (macros) from the context - $compiler - ->write("\$vars = array();\n") - ->write("foreach (\$context as \$key => \$value) {\n") - ->indent() - ->write("if (!\$value instanceof Twig_Template) {\n") - ->indent() - ->write("\$vars[\$key] = \$value;\n") - ->outdent() - ->write("}\n") - ->outdent() - ->write("}\n") - ->write("var_dump(\$vars);\n") - ; - } else { - $compiler - ->write("var_dump(") - ->subcompile($this->getNode('expr')) - ->raw(");\n") - ; - } - - $compiler - ->outdent() - ->write("}\n") - ; - } -} diff --git a/inc/3rdparty/Twig/Extensions/Node/Trans.php b/inc/3rdparty/Twig/Extensions/Node/Trans.php deleted file mode 100644 index d12564a..0000000 --- a/inc/3rdparty/Twig/Extensions/Node/Trans.php +++ /dev/null @@ -1,133 +0,0 @@ - - */ -class Twig_Extensions_Node_Trans extends Twig_Node -{ - public function __construct(Twig_NodeInterface $body, Twig_NodeInterface $plural = null, Twig_Node_Expression $count = null, $lineno, $tag = null) - { - parent::__construct(array('count' => $count, 'body' => $body, 'plural' => $plural), array(), $lineno, $tag); - } - - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler A Twig_Compiler instance - */ - public function compile(Twig_Compiler $compiler) - { - $compiler->addDebugInfo($this); - - list($msg, $vars) = $this->compileString($this->getNode('body')); - - if (null !== $this->getNode('plural')) { - list($msg1, $vars1) = $this->compileString($this->getNode('plural')); - - $vars = array_merge($vars, $vars1); - } - - $function = null === $this->getNode('plural') ? 'gettext' : 'ngettext'; - - if ($vars) { - $compiler - ->write('echo strtr('.$function.'(') - ->subcompile($msg) - ; - - if (null !== $this->getNode('plural')) { - $compiler - ->raw(', ') - ->subcompile($msg1) - ->raw(', abs(') - ->subcompile($this->getNode('count')) - ->raw(')') - ; - } - - $compiler->raw('), array('); - - foreach ($vars as $var) { - if ('count' === $var->getAttribute('name')) { - $compiler - ->string('%count%') - ->raw(' => abs(') - ->subcompile($this->getNode('count')) - ->raw('), ') - ; - } else { - $compiler - ->string('%'.$var->getAttribute('name').'%') - ->raw(' => ') - ->subcompile($var) - ->raw(', ') - ; - } - } - - $compiler->raw("));\n"); - } else { - $compiler - ->write('echo '.$function.'(') - ->subcompile($msg) - ; - - if (null !== $this->getNode('plural')) { - $compiler - ->raw(', ') - ->subcompile($msg1) - ->raw(', abs(') - ->subcompile($this->getNode('count')) - ->raw(')') - ; - } - - $compiler->raw(");\n"); - } - } - - protected function compileString(Twig_NodeInterface $body) - { - if ($body instanceof Twig_Node_Expression_Name || $body instanceof Twig_Node_Expression_Constant || $body instanceof Twig_Node_Expression_TempName) { - return array($body, array()); - } - - $vars = array(); - if (count($body)) { - $msg = ''; - - foreach ($body as $node) { - if (get_class($node) === 'Twig_Node' && $node->getNode(0) instanceof Twig_Node_SetTemp) { - $node = $node->getNode(1); - } - - if ($node instanceof Twig_Node_Print) { - $n = $node->getNode('expr'); - while ($n instanceof Twig_Node_Expression_Filter) { - $n = $n->getNode('node'); - } - $msg .= sprintf('%%%s%%', $n->getAttribute('name')); - $vars[] = new Twig_Node_Expression_Name($n->getAttribute('name'), $n->getLine()); - } else { - $msg .= $node->getAttribute('data'); - } - } - } else { - $msg = $body->getAttribute('data'); - } - - return array(new Twig_Node(array(new Twig_Node_Expression_Constant(trim($msg), $body->getLine()))), $vars); - } -} diff --git a/inc/3rdparty/Twig/Extensions/SimpleTokenParser.php b/inc/3rdparty/Twig/Extensions/SimpleTokenParser.php deleted file mode 100644 index 4954648..0000000 --- a/inc/3rdparty/Twig/Extensions/SimpleTokenParser.php +++ /dev/null @@ -1,132 +0,0 @@ -getGrammar(); - if (!is_object($grammar)) { - $grammar = self::parseGrammar($grammar); - } - - $grammar->setParser($this->parser); - $values = $grammar->parse($token); - - return $this->getNode($values, $token->getLine()); - } - - /** - * Gets the grammar as an object or as a string. - * - * @return string|Twig_Extensions_Grammar A Twig_Extensions_Grammar instance or a string - */ - abstract protected function getGrammar(); - - /** - * Gets the nodes based on the parsed values. - * - * @param array $values An array of values - * @param integer $line The parser line - */ - abstract protected function getNode(array $values, $line); - - protected function getAttribute($node, $attribute, $arguments = array(), $type = Twig_Node_Expression_GetAttr::TYPE_ANY, $line = -1) - { - return new Twig_Node_Expression_GetAttr( - $node instanceof Twig_NodeInterface ? $node : new Twig_Node_Expression_Name($node, $line), - $attribute instanceof Twig_NodeInterface ? $attribute : new Twig_Node_Expression_Constant($attribute, $line), - $arguments instanceof Twig_NodeInterface ? $arguments : new Twig_Node($arguments), - $type, - $line - ); - } - - protected function call($node, $attribute, $arguments = array(), $line = -1) - { - return $this->getAttribute($node, $attribute, $arguments, Twig_Node_Expression_GetAttr::TYPE_METHOD, $line); - } - - protected function markAsSafe(Twig_NodeInterface $node, $line = -1) - { - return new Twig_Node_Expression_Filter( - $node, - new Twig_Node_Expression_Constant('raw', $line), - new Twig_Node(), - $line - ); - } - - protected function output(Twig_NodeInterface $node, $line = -1) - { - return new Twig_Node_Print($node, $line); - } - - protected function getNodeValues(array $values) - { - $nodes = array(); - foreach ($values as $value) { - if ($value instanceof Twig_NodeInterface) { - $nodes[] = $value; - } - } - - return $nodes; - } - - static public function parseGrammar($str, $main = true) - { - static $cursor; - - if (true === $main) { - $cursor = 0; - $grammar = new Twig_Extensions_Grammar_Tag(); - } else { - $grammar = new Twig_Extensions_Grammar_Optional(); - } - - while ($cursor < strlen($str)) { - if (preg_match('/\s+/A', $str, $match, null, $cursor)) { - $cursor += strlen($match[0]); - } elseif (preg_match('/<(\w+)(?:\:(\w+))?>/A', $str, $match, null, $cursor)) { - $class = sprintf('Twig_Extensions_Grammar_%s', ucfirst(isset($match[2]) ? $match[2] : 'Expression')); - if (!class_exists($class)) { - throw new Twig_Error_Runtime(sprintf('Unable to understand "%s" in grammar (%s class does not exist)', $match[0], $class)); - } - $grammar->addGrammar(new $class($match[1])); - $cursor += strlen($match[0]); - } elseif (preg_match('/\w+/A', $str, $match, null, $cursor)) { - $grammar->addGrammar(new Twig_Extensions_Grammar_Constant($match[0])); - $cursor += strlen($match[0]); - } elseif (preg_match('/,/A', $str, $match, null, $cursor)) { - $grammar->addGrammar(new Twig_Extensions_Grammar_Constant($match[0], Twig_Token::PUNCTUATION_TYPE)); - $cursor += strlen($match[0]); - } elseif (preg_match('/\[/A', $str, $match, null, $cursor)) { - $cursor += strlen($match[0]); - $grammar->addGrammar(self::parseGrammar($str, false)); - } elseif (true !== $main && preg_match('/\]/A', $str, $match, null, $cursor)) { - $cursor += strlen($match[0]); - - return $grammar; - } else { - throw new Twig_Error_Runtime(sprintf('Unable to parse grammar "%s" near "...%s..."', $str, substr($str, $cursor, 10))); - } - } - - return $grammar; - } -} diff --git a/inc/3rdparty/Twig/Extensions/TokenParser/Debug.php b/inc/3rdparty/Twig/Extensions/TokenParser/Debug.php deleted file mode 100644 index 4a7dfcc..0000000 --- a/inc/3rdparty/Twig/Extensions/TokenParser/Debug.php +++ /dev/null @@ -1,42 +0,0 @@ -getLine(); - - $expr = null; - if (!$this->parser->getStream()->test(Twig_Token::BLOCK_END_TYPE)) { - $expr = $this->parser->getExpressionParser()->parseExpression(); - } - $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); - - return new Twig_Extensions_Node_Debug($expr, $lineno, $this->getTag()); - } - - /** - * Gets the tag name associated with this token parser. - * - * @param string The tag name - */ - public function getTag() - { - return 'debug'; - } -} diff --git a/inc/3rdparty/Twig/Extensions/TokenParser/Trans.php b/inc/3rdparty/Twig/Extensions/TokenParser/Trans.php deleted file mode 100644 index 5e2dc46..0000000 --- a/inc/3rdparty/Twig/Extensions/TokenParser/Trans.php +++ /dev/null @@ -1,80 +0,0 @@ -getLine(); - $stream = $this->parser->getStream(); - $count = null; - $plural = null; - - if (!$stream->test(Twig_Token::BLOCK_END_TYPE)) { - $body = $this->parser->getExpressionParser()->parseExpression(); - } else { - $stream->expect(Twig_Token::BLOCK_END_TYPE); - $body = $this->parser->subparse(array($this, 'decideForFork')); - if ('plural' === $stream->next()->getValue()) { - $count = $this->parser->getExpressionParser()->parseExpression(); - $stream->expect(Twig_Token::BLOCK_END_TYPE); - $plural = $this->parser->subparse(array($this, 'decideForEnd'), true); - } - } - - $stream->expect(Twig_Token::BLOCK_END_TYPE); - - $this->checkTransString($body, $lineno); - - return new Twig_Extensions_Node_Trans($body, $plural, $count, $lineno, $this->getTag()); - } - - public function decideForFork(Twig_Token $token) - { - return $token->test(array('plural', 'endtrans')); - } - - public function decideForEnd(Twig_Token $token) - { - return $token->test('endtrans'); - } - - /** - * Gets the tag name associated with this token parser. - * - * @param string The tag name - */ - public function getTag() - { - return 'trans'; - } - - protected function checkTransString(Twig_NodeInterface $body, $lineno) - { - foreach ($body as $i => $node) { - if ( - $node instanceof Twig_Node_Text - || - ($node instanceof Twig_Node_Print && $node->getNode('expr') instanceof Twig_Node_Expression_Name) - ) { - continue; - } - - throw new Twig_Error_Syntax(sprintf('The text to be translated with "trans" can only contain references to simple variables'), $lineno); - } - } -} diff --git a/inc/3rdparty/Twig/Gettext/Extractor.php b/inc/3rdparty/Twig/Gettext/Extractor.php deleted file mode 100644 index e7fa1af..0000000 --- a/inc/3rdparty/Twig/Gettext/Extractor.php +++ /dev/null @@ -1,95 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Twig\Gettext; - -use Symfony\Component\Filesystem\Filesystem; - -/** - * Extracts translations from twig templates. - * - * @author Саша Стаменковић - */ -class Extractor -{ - /** - * @var \Twig_Environment - */ - protected $environment; - - /** - * Template cached file names. - * - * @var string[] - */ - protected $templates; - - /** - * Gettext parameters. - * - * @var string[] - */ - protected $parameters; - - public function __construct(\Twig_Environment $environment) - { - $this->environment = $environment; - $this->reset(); - } - - protected function reset() - { - $this->templates = array(); - $this->parameters = array(); - } - - public function addTemplate($path) - { - $this->environment->loadTemplate($path); - $this->templates[] = $this->environment->getCacheFilename($path); - } - - public function addGettextParameter($parameter) - { - $this->parameters[] = $parameter; - } - - public function setGettextParameters(array $parameters) - { - $this->parameters = $parameters; - } - - public function extract() - { - $command = 'xgettext'; - $command .= ' '.join(' ', $this->parameters); - $command .= ' '.join(' ', $this->templates); - - $error = 0; - $output = system($command, $error); - if (0 !== $error) { - throw new \RuntimeException(sprintf( - 'Gettext command "%s" failed with error code %s and output: %s', - $command, - $error, - $output - )); - } - - $this->reset(); - } - - public function __destruct() - { - $filesystem = new Filesystem(); - $filesystem->remove($this->environment->getCache()); - } -} diff --git a/inc/3rdparty/Twig/Gettext/Loader/Filesystem.php b/inc/3rdparty/Twig/Gettext/Loader/Filesystem.php deleted file mode 100644 index b011b03..0000000 --- a/inc/3rdparty/Twig/Gettext/Loader/Filesystem.php +++ /dev/null @@ -1,58 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Twig\Gettext\Loader; - -/** - * Loads template from the filesystem. - * - * @author Саша Стаменковић - */ -class Filesystem extends \Twig_Loader_Filesystem -{ - /** - * Hacked find template to allow loading templates by absolute path. - * - * @param string $name template name or absolute path - */ - protected function findTemplate($name) - { - // normalize name - $name = preg_replace('#/{2,}#', '/', strtr($name, '\\', '/')); - - if (isset($this->cache[$name])) { - return $this->cache[$name]; - } - - $this->validateName($name); - - $namespace = '__main__'; - if (isset($name[0]) && '@' == $name[0]) { - if (false === $pos = strpos($name, '/')) { - throw new \InvalidArgumentException(sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name)); - } - - $namespace = substr($name, 1, $pos - 1); - - $name = substr($name, $pos + 1); - } - - if (!isset($this->paths[$namespace])) { - throw new \Twig_Error_Loader(sprintf('There are no registered paths for namespace "%s".', $namespace)); - } - - if (is_file($name)) { - return $this->cache[$name] = $name; - } - - return __DIR__.'/../Test/Fixtures/twig/empty.twig'; - } -} diff --git a/inc/3rdparty/Twig/Gettext/Routing/Generator/UrlGenerator.php b/inc/3rdparty/Twig/Gettext/Routing/Generator/UrlGenerator.php deleted file mode 100644 index 9e3431b..0000000 --- a/inc/3rdparty/Twig/Gettext/Routing/Generator/UrlGenerator.php +++ /dev/null @@ -1,39 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Twig\Gettext\Routing\Generator; - -use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Symfony\Component\Routing\RequestContext; - -/** - * Dummy url generator. - * - * @author Саша Стаменковић - */ -class UrlGenerator implements UrlGeneratorInterface -{ - protected $context; - - public function generate($name, $parameters = array(), $absolute = false) - { - } - - public function getContext() - { - return $this->context; - } - - public function setContext(RequestContext $context) - { - $this->context = $context; - } -} diff --git a/inc/3rdparty/Twig/Gettext/Test/ExtractorTest.php b/inc/3rdparty/Twig/Gettext/Test/ExtractorTest.php deleted file mode 100644 index d467835..0000000 --- a/inc/3rdparty/Twig/Gettext/Test/ExtractorTest.php +++ /dev/null @@ -1,123 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Twig\Gettext\Test; - -use Twig\Gettext\Extractor; -use Twig\Gettext\Loader\Filesystem; -use Symfony\Component\Translation\Loader\PoFileLoader; - -/** - * @author Саша Стаменковић - */ -class ExtractorTest extends \PHPUnit_Framework_TestCase -{ - /** - * @var \Twig_Environment - */ - protected $twig; - - /** - * @var PoFileLoader - */ - protected $loader; - - protected function setUp() - { - $this->twig = new \Twig_Environment(new Filesystem('/'), array( - 'cache' => '/tmp/cache/'.uniqid(), - 'auto_reload' => true - )); - $this->twig->addExtension(new \Twig_Extensions_Extension_I18n()); - - $this->loader = new PoFileLoader(); - } - - /** - * @dataProvider testExtractDataProvider - */ - public function testExtract(array $templates, array $parameters, array $messages) - { - $extractor = new Extractor($this->twig); - - foreach ($templates as $template) { - $extractor->addTemplate($template); - } - foreach ($parameters as $parameter) { - $extractor->addGettextParameter($parameter); - } - - $extractor->extract(); - - $catalog = $this->loader->load($this->getPotFile(), null); - - foreach ($messages as $message) { - $this->assertTrue( - $catalog->has($message), - sprintf('Message "%s" not found in catalog.', $message) - ); - } - } - - public function testExtractDataProvider() - { - return array( - array( - array( - __DIR__.'/Fixtures/twig/singular.twig', - __DIR__.'/Fixtures/twig/plural.twig', - ), - $this->getGettextParameters(), - array( - 'Hello %name%!', - 'Hello World!', - 'Hey %name%, I have one apple.', - 'Hey %name%, I have %count% apples.', - ), - ), - ); - } - - public function testExtractNoTranslations() - { - $extractor = new Extractor($this->twig); - - $extractor->addTemplate(__DIR__.'/Fixtures/twig/empty.twig'); - $extractor->setGettextParameters($this->getGettextParameters()); - - $extractor->extract(); - - $catalog = $this->loader->load($this->getPotFile(), null); - - $this->assertEmpty($catalog->all('messages')); - } - - private function getPotFile() - { - return __DIR__.'/Fixtures/messages.pot'; - } - - private function getGettextParameters() - { - return array( - '--force-po', - '-o', - $this->getPotFile(), - ); - } - - protected function tearDown() - { - if (file_exists($this->getPotFile())) { - unlink($this->getPotFile()); - } - } -} diff --git a/inc/3rdparty/Twig/Gettext/Test/Fixtures/twig/empty.twig b/inc/3rdparty/Twig/Gettext/Test/Fixtures/twig/empty.twig deleted file mode 100644 index 05f0d26..0000000 --- a/inc/3rdparty/Twig/Gettext/Test/Fixtures/twig/empty.twig +++ /dev/null @@ -1 +0,0 @@ -Nothing to translate here. diff --git a/inc/3rdparty/Twig/Gettext/Test/Fixtures/twig/plural.twig b/inc/3rdparty/Twig/Gettext/Test/Fixtures/twig/plural.twig deleted file mode 100644 index f9754ff..0000000 --- a/inc/3rdparty/Twig/Gettext/Test/Fixtures/twig/plural.twig +++ /dev/null @@ -1,5 +0,0 @@ -{% trans %} - Hey {{ name }}, I have one apple. -{% plural apple_count %} - Hey {{ name }}, I have {{ count }} apples. -{% endtrans %} diff --git a/inc/3rdparty/Twig/Gettext/Test/Fixtures/twig/singular.twig b/inc/3rdparty/Twig/Gettext/Test/Fixtures/twig/singular.twig deleted file mode 100644 index d757cf9..0000000 --- a/inc/3rdparty/Twig/Gettext/Test/Fixtures/twig/singular.twig +++ /dev/null @@ -1,9 +0,0 @@ -{% trans "Hello World!" %} - -{% trans %} - Hello World! -{% endtrans %} - -{% trans %} - Hello {{ name }}! -{% endtrans %} diff --git a/inc/config.php b/inc/config.php index b78147a..495dbb8 100644 --- a/inc/config.php +++ b/inc/config.php @@ -24,15 +24,14 @@ define ('LANG', 'fr_FR.UTF8'); $storage_type = 'sqlite'; # sqlite, file # /!\ Be careful if you change the lines below /!\ - require_once 'poche/pocheTools.class.php'; require_once 'poche/pocheCore.php'; require_once '3rdparty/Readability.php'; require_once '3rdparty/Encoding.php'; require_once '3rdparty/Session.class.php'; -require_once '3rdparty/Twig/Autoloader.php'; require_once 'store/store.class.php'; require_once 'store/' . $storage_type . '.class.php'; +require_once './vendor/autoload.php'; if (DOWNLOAD_PICTURES) { require_once 'poche/pochePicture.php'; @@ -45,7 +44,7 @@ bindtextdomain(LANG, LOCALE); textdomain(LANG); # template engine -Twig_Autoloader::register(); +// Twig_Autoloader::register(); $loader = new Twig_Loader_Filesystem(TPL); $twig = new Twig_Environment($loader, array( 'cache' => CACHE, diff --git a/index.php b/index.php index 81bd017..dc06442 100644 --- a/index.php +++ b/index.php @@ -10,70 +10,74 @@ include dirname(__FILE__).'/inc/config.php'; -$errors = array(); +$notices = array(); # XSRF protection with token -if (!empty($_POST)) { - if (!Session::isToken($_POST['token'])) { - #die(_('Wrong token')); - // TODO CORRIGER ICI !!! - } - unset($_SESSION['tokens']); -} +// if (!empty($_POST)) { +// if (!Session::isToken($_POST['token'])) { +// die(_('Wrong token')); +// // TODO remettre le test +// } +// unset($_SESSION['tokens']); +// } $referer = empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER']; if (isset($_GET['login'])) { + # hello you if (!empty($_POST['login']) && !empty($_POST['password'])) { if (Session::login($_SESSION['login'], $_SESSION['pass'], $_POST['login'], encode_string($_POST['password'] . $_POST['login']))) { pocheTools::logm('login successful'); - $errors[]['value'] = _('login successful'); + $pocheTools[]['value'] = _('login successful'); if (!empty($_POST['longlastingsession'])) { $_SESSION['longlastingsession'] = 31536000; $_SESSION['expires_on'] = time() + $_SESSION['longlastingsession']; session_set_cookie_params($_SESSION['longlastingsession']); } else { - session_set_cookie_params(0); // when browser closes + session_set_cookie_params(0); } session_regenerate_id(true); pocheTools::redirect($referer); } pocheTools::logm('login failed'); - $errors[]['value'] = _('Login failed !'); + $notices[]['value'] = _('Login failed !'); + pocheTools::redirect(); } else { pocheTools::logm('login failed'); + pocheTools::redirect(); } } elseif (isset($_GET['logout'])) { + # see you soon ! pocheTools::logm('logout'); Session::logout(); pocheTools::redirect(); } elseif (isset($_GET['config'])) { + # Update password if (isset($_POST['password']) && isset($_POST['password_repeat'])) { if ($_POST['password'] == $_POST['password_repeat'] && $_POST['password'] != "") { - pocheTools::logm('password updated'); if (!MODE_DEMO) { + pocheTools::logm('password updated'); $store->updatePassword(encode_string($_POST['password'] . $_SESSION['login'])); - #your password has been updated + Session::logout(); + pocheTools::redirect(); } else { - #in demo mode, you can\'t update password + pocheTools::logm('in demo mode, you can\'t do this'); } } - #else - #your password can\'t be empty and you have to repeat it in the second field } } -# Traitement des paramètres et déclenchement des actions -$view = (isset ($_REQUEST['view'])) ? htmlentities($_REQUEST['view']) : 'home'; -$full_head = (isset ($_REQUEST['full_head'])) ? htmlentities($_REQUEST['full_head']) : 'yes'; -$action = (isset ($_REQUEST['action'])) ? htmlentities($_REQUEST['action']) : ''; -$_SESSION['sort'] = (isset ($_REQUEST['sort'])) ? htmlentities($_REQUEST['sort']) : 'id'; -$id = (isset ($_REQUEST['id'])) ? htmlspecialchars($_REQUEST['id']) : ''; -$url = (isset ($_GET['url'])) ? $_GET['url'] : ''; +# Aaaaaaand action ! +$view = (isset ($_REQUEST['view'])) ? htmlentities($_REQUEST['view']) : 'home'; +$full_head = (isset ($_REQUEST['full_head'])) ? htmlentities($_REQUEST['full_head']) : 'yes'; +$action = (isset ($_REQUEST['action'])) ? htmlentities($_REQUEST['action']) : ''; +$_SESSION['sort'] = (isset ($_REQUEST['sort'])) ? htmlentities($_REQUEST['sort']) : 'id'; +$id = (isset ($_REQUEST['id'])) ? htmlspecialchars($_REQUEST['id']) : ''; +$url = (isset ($_GET['url'])) ? $_GET['url'] : ''; $tpl_vars = array( 'referer' => $referer, @@ -82,7 +86,7 @@ $tpl_vars = array( 'demo' => MODE_DEMO, 'title' => _('poche, a read it later open source system'), 'token' => Session::getToken(), - 'errors' => $errors, + 'notices' => $notices, ); if (Session::isLogged()) { diff --git a/tpl/_head.twig b/tpl/_head.twig index 2d640cb..ad96e9d 100644 --- a/tpl/_head.twig +++ b/tpl/_head.twig @@ -1,10 +1,10 @@ - - - - - - + + + + + + - + - \ No newline at end of file + \ No newline at end of file diff --git a/tpl/_top.twig b/tpl/_top.twig index 8e3ea7e..daee44f 100644 --- a/tpl/_top.twig +++ b/tpl/_top.twig @@ -1,3 +1,3 @@
    -

    logo pochepoche

    +

    logo pochepoche

    \ No newline at end of file diff --git a/tpl/config.twig b/tpl/config.twig index 10c481d..be2c045 100644 --- a/tpl/config.twig +++ b/tpl/config.twig @@ -12,30 +12,30 @@ {% endblock %} {% block content %}
    -

    Bookmarklet

    -

    Thanks to the bookmarklet, you will be able to easily add a link to your poche. If you don't know how use a bookmarklet, have a look here.

    -

    Drag & drop this link to your bookmarks bar and have fun with poche.

    -

    poche it !

    +

    {% trans "Bookmarklet" %}

    +

    {% trans "Thanks to the bookmarklet, you will be able to easily add a link to your poche." %} {% trans "Have a look to this documentation:" %} http://inthepoche.com/?pages/Documentation.

    +

    {% trans "Drag & drop this link to your bookmarks bar and have fun with poche." %}

    +

    {% trans "poche it!" %}

    -

    Password

    +

    {% trans "Change your password" %}

    - - + +
    - - + +
    - +
    - - + + -

    Export

    -

    Click here to export your poche datas.

    +

    {% trans "Export your poche datas" %}

    +

    {% trans "Click here" %} {% trans "to export your poche datas." %}

    {% endblock %} \ No newline at end of file diff --git a/css/knacss.css b/tpl/css/knacss.css similarity index 100% rename from css/knacss.css rename to tpl/css/knacss.css diff --git a/css/style-dark.css b/tpl/css/style-dark.css similarity index 100% rename from css/style-dark.css rename to tpl/css/style-dark.css diff --git a/css/style-light.css b/tpl/css/style-light.css similarity index 100% rename from css/style-light.css rename to tpl/css/style-light.css diff --git a/css/style.css b/tpl/css/style.css similarity index 100% rename from css/style.css rename to tpl/css/style.css diff --git a/tpl/entries.html b/tpl/entries.html deleted file mode 100644 index 83e58c7..0000000 --- a/tpl/entries.html +++ /dev/null @@ -1,21 +0,0 @@ -
    - {loop="entries"} -
    - -

    - {$value.title} -

    -
    -
      -
    • -
    • -
    • -
    • -
    • -
    -
    -
    {$value.url}
    -
    -
    - {/loop} -
    \ No newline at end of file diff --git a/tpl/home.twig b/tpl/home.twig index c79d427..5752b2c 100644 --- a/tpl/home.twig +++ b/tpl/home.twig @@ -11,10 +11,19 @@ {% endblock %} {% block precontent %}
      -
    • {% trans "by date" %}
    • -
    • {% trans "by title" %}
    • +
    • {% trans "by date" %}
    • +
    • {% trans "by title" %}
    {% endblock %} +{% block notices %} +
    +
      + {% for notice in notices %} +
    • {{ notice.value|e }}
    • + {% endfor %} +
    +
    +{% endblock %} {% block content %}
    {% for entry in entries %} @@ -28,7 +37,7 @@
  • -
  • +
@@ -40,9 +49,9 @@ {% endblock %} {% block js %} - - - + + + + +{% endblock %} \ No newline at end of file From eb1af592194e225bf887e4893e697f0ab8dd9a26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Sun, 4 Aug 2013 20:58:31 +0200 Subject: [PATCH 20/49] refactoring --- inc/config.php | 79 ---------- inc/poche/Poche.class.php | 176 +++++++++++++++++++++ inc/poche/Tools.class.php | 208 +++++++++++++++++++++++++ inc/poche/Url.class.php | 94 ++++++++++++ inc/poche/config.inc.php | 40 +++++ inc/poche/pocheCore.php | 269 --------------------------------- inc/poche/pochePictures.php | 2 +- inc/poche/pocheTools.class.php | 126 --------------- inc/store/sqlite.class.php | 2 +- index.php | 45 +++--- tpl/config.twig | 2 +- 11 files changed, 544 insertions(+), 499 deletions(-) delete mode 100644 inc/config.php create mode 100644 inc/poche/Poche.class.php create mode 100644 inc/poche/Tools.class.php create mode 100644 inc/poche/Url.class.php create mode 100644 inc/poche/config.inc.php delete mode 100644 inc/poche/pocheCore.php delete mode 100644 inc/poche/pocheTools.class.php diff --git a/inc/config.php b/inc/config.php deleted file mode 100644 index 495dbb8..0000000 --- a/inc/config.php +++ /dev/null @@ -1,79 +0,0 @@ - - * @copyright 2013 - * @license http://www.wtfpl.net/ see COPYING file - */ - -define ('POCHE_VERSION', '0.3'); -define ('MODE_DEMO', FALSE); -define ('DEBUG_POCHE', FALSE); -define ('CONVERT_LINKS_FOOTNOTES', FALSE); -define ('REVERT_FORCED_PARAGRAPH_ELEMENTS', FALSE); -define ('DOWNLOAD_PICTURES', FALSE); -define ('SALT', '464v54gLLw928uz4zUBqkRJeiPY68zCX'); -define ('ABS_PATH', 'assets/'); -define ('TPL', './tpl'); -define ('LOCALE', './locale'); -define ('CACHE', './cache'); -define ('LANG', 'fr_FR.UTF8'); - -$storage_type = 'sqlite'; # sqlite, file - -# /!\ Be careful if you change the lines below /!\ -require_once 'poche/pocheTools.class.php'; -require_once 'poche/pocheCore.php'; -require_once '3rdparty/Readability.php'; -require_once '3rdparty/Encoding.php'; -require_once '3rdparty/Session.class.php'; -require_once 'store/store.class.php'; -require_once 'store/' . $storage_type . '.class.php'; -require_once './vendor/autoload.php'; - -if (DOWNLOAD_PICTURES) { - require_once 'poche/pochePicture.php'; -} - -# i18n -putenv('LC_ALL=' . LANG); -setlocale(LC_ALL, LANG); -bindtextdomain(LANG, LOCALE); -textdomain(LANG); - -# template engine -// Twig_Autoloader::register(); -$loader = new Twig_Loader_Filesystem(TPL); -$twig = new Twig_Environment($loader, array( - 'cache' => CACHE, -)); -$twig->addExtension(new Twig_Extensions_Extension_I18n()); - -Session::init(); -$store = new $storage_type(); - -# installation -if(!$store->isInstalled()) -{ - pocheTools::logm('poche still not installed'); - echo $twig->render('install.twig', array( - 'token' => Session::getToken(), - )); - if (isset($_GET['install'])) { - if (($_POST['password'] == $_POST['password_repeat']) - && $_POST['password'] != "" && $_POST['login'] != "") { - # let's rock, install poche baby ! - $store->install($_POST['login'], encode_string($_POST['password'] . $_POST['login'])); - Session::logout(); - pocheTools::redirect(); - } - } - exit(); -} - -$_SESSION['login'] = (isset ($_SESSION['login'])) ? $_SESSION['login'] : $store->getLogin(); -$_SESSION['pass'] = (isset ($_SESSION['pass'])) ? $_SESSION['pass'] : $store->getPassword(); - -pocheTools::initPhp(); \ No newline at end of file diff --git a/inc/poche/Poche.class.php b/inc/poche/Poche.class.php new file mode 100644 index 0000000..973ae3e --- /dev/null +++ b/inc/poche/Poche.class.php @@ -0,0 +1,176 @@ + + * @copyright 2013 + * @license http://www.wtfpl.net/ see COPYING file + */ + +class Poche +{ + public $store; + public $tpl; + + function __construct($storage_type) + { + $this->store = new $storage_type(); + $this->init(); + + # installation + if(!$this->store->isInstalled()) + { + $this->install(); + } + + $this->saveUser(); + } + + private function init() + { + # l10n + putenv('LC_ALL=' . LANG); + setlocale(LC_ALL, LANG); + bindtextdomain(LANG, LOCALE); + textdomain(LANG); + + # template engine + $loader = new Twig_Loader_Filesystem(TPL); + $this->tpl = new Twig_Environment($loader, array( + 'cache' => CACHE, + )); + $this->tpl->addExtension(new Twig_Extensions_Extension_I18n()); + + Tools::initPhp(); + Session::init(); + } + + private function install() + { + Tools::logm('poche still not installed'); + echo $this->tpl->render('install.twig', array( + 'token' => Session::getToken(), + )); + if (isset($_GET['install'])) { + if (($_POST['password'] == $_POST['password_repeat']) + && $_POST['password'] != "" && $_POST['login'] != "") { + # let's rock, install poche baby ! + $this->store->install($_POST['login'], Tools::encodeString($_POST['password'] . $_POST['login'])); + Session::logout(); + Tools::redirect(); + } + } + exit(); + } + + private function saveUser() + { + $_SESSION['login'] = (isset ($_SESSION['login'])) ? $_SESSION['login'] : $this->store->getLogin(); + $_SESSION['pass'] = (isset ($_SESSION['pass'])) ? $_SESSION['pass'] : $this->store->getPassword(); + } + + /** + * Call action (mark as fav, archive, delete, etc.) + */ + public function action($action, Url $url, $id) + { + switch ($action) + { + case 'add': + if($parametres_url = $url->fetchContent()) { + if ($this->store->add($url->getUrl(), $parametres_url['title'], $parametres_url['content'])) { + Tools::logm('add link ' . $url->getUrl()); + $last_id = $this->store->getLastId(); + if (DOWNLOAD_PICTURES) { + $content = filtre_picture($parametres_url['content'], $url->getUrl(), $last_id); + } + #$msg->add('s', _('the link has been added successfully')); + } + else { + #$msg->add('e', _('error during insertion : the link wasn\'t added')); + Tools::logm('error during insertion : the link wasn\'t added'); + } + } + else { + #$msg->add('e', _('error during url preparation : the link wasn\'t added')); + Tools::logm('error during content fetch'); + } + break; + case 'delete': + if ($this->store->deleteById($id)) { + if (DOWNLOAD_PICTURES) { + remove_directory(ABS_PATH . $id); + } + #$msg->add('s', _('the link has been deleted successfully')); + Tools::logm('delete link #' . $id); + } + else { + #$msg->add('e', _('the link wasn\'t deleted')); + Tools::logm('error : can\'t delete link #' . $id); + } + break; + case 'toggle_fav' : + $this->store->favoriteById($id); + Tools::logm('mark as favorite link #' . $id); + break; + case 'toggle_archive' : + $this->store->archiveById($id); + Tools::logm('archive link #' . $id); + break; + default: + break; + } + } + + function displayView($view, $id = 0) + { + $tpl_vars = array(); + + switch ($view) + { + case 'install': + Tools::logm('install mode'); + break; + case 'import'; + Tools::logm('import mode'); + break; + case 'export': + $entries = $this->store->retrieveAll(); + // $tpl->assign('export', Tools::renderJson($entries)); + // $tpl->draw('export'); + Tools::logm('export view'); + break; + case 'config': + Tools::logm('config view'); + break; + case 'view': + $entry = $this->store->retrieveOneById($id); + if ($entry != NULL) { + Tools::logm('view link #' . $id); + $content = $entry['content']; + if (function_exists('tidy_parse_string')) { + $tidy = tidy_parse_string($content, array('indent'=>true, 'show-body-only' => true), 'UTF8'); + $tidy->cleanRepair(); + $content = $tidy->value; + } + $tpl_vars = array( + 'entry' => $entry, + 'content' => $content, + ); + } + else { + Tools::logm('error in view call : entry is NULL'); + } + break; + default: # home view + $entries = $this->store->getEntriesByView($view); + $tpl_vars = array( + 'entries' => $entries, + ); + break; + } + + return $tpl_vars; + } +} \ No newline at end of file diff --git a/inc/poche/Tools.class.php b/inc/poche/Tools.class.php new file mode 100644 index 0000000..c277035 --- /dev/null +++ b/inc/poche/Tools.class.php @@ -0,0 +1,208 @@ + + * @copyright 2013 + * @license http://www.wtfpl.net/ see COPYING file + */ + +class Tools +{ + public static function initPhp() + { + define('START_TIME', microtime(true)); + + if (phpversion() < 5) { + die(_('Oops, it seems you don\'t have PHP 5.')); + } + + error_reporting(E_ALL); + + function stripslashesDeep($value) { + return is_array($value) + ? array_map('stripslashesDeep', $value) + : stripslashes($value); + } + + if (get_magic_quotes_gpc()) { + $_POST = array_map('stripslashesDeep', $_POST); + $_GET = array_map('stripslashesDeep', $_GET); + $_COOKIE = array_map('stripslashesDeep', $_COOKIE); + } + + ob_start(); + register_shutdown_function('ob_end_flush'); + } + + public static function getPocheUrl() + { + $https = (!empty($_SERVER['HTTPS']) + && (strtolower($_SERVER['HTTPS']) == 'on')) + || (isset($_SERVER["SERVER_PORT"]) + && $_SERVER["SERVER_PORT"] == '443'); // HTTPS detection. + $serverport = (!isset($_SERVER["SERVER_PORT"]) + || $_SERVER["SERVER_PORT"] == '80' + || ($https && $_SERVER["SERVER_PORT"] == '443') + ? '' : ':' . $_SERVER["SERVER_PORT"]); + + $scriptname = str_replace('/index.php', '/', $_SERVER["SCRIPT_NAME"]); + + if (!isset($_SERVER["SERVER_NAME"])) { + return $scriptname; + } + + return 'http' . ($https ? 's' : '') . '://' + . $_SERVER["SERVER_NAME"] . $serverport . $scriptname; + } + + public static function redirect($url = '') + { + if ($url === '') { + $url = (empty($_SERVER['HTTP_REFERER'])?'?':$_SERVER['HTTP_REFERER']); + if (isset($_POST['returnurl'])) { + $url = $_POST['returnurl']; + } + } + + # prevent loop + if (empty($url) || parse_url($url, PHP_URL_QUERY) === $_SERVER['QUERY_STRING']) { + $url = Tools::getPocheUrl(); + } + + if (substr($url, 0, 1) !== '?') { + $ref = Tools::getPocheUrl(); + if (substr($url, 0, strlen($ref)) !== $ref) { + $url = $ref; + } + } + header('Location: '.$url); + exit(); + } + + public static function getTplFile($view) + { + $tpl_file = 'home.twig'; + switch ($view) + { + case 'install': + $tpl_file = 'install.twig'; + break; + case 'import'; + $tpl_file = 'import.twig'; + break; + case 'export': + $tpl_file = 'export.twig'; + break; + case 'config': + $tpl_file = 'config.twig'; + break; + case 'view': + $tpl_file = 'view.twig'; + break; + default: + break; + } + return $tpl_file; + } + + public static function getFile($url) + { + $timeout = 15; + $useragent = "Mozilla/5.0 (Windows NT 5.1; rv:18.0) Gecko/20100101 Firefox/18.0"; + + if (in_array ('curl', get_loaded_extensions())) { + # Fetch feed from URL + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $url); + curl_setopt($curl, CURLOPT_TIMEOUT, $timeout); + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl, CURLOPT_HEADER, false); + + # for ssl, do not verified certificate + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); + curl_setopt($curl, CURLOPT_AUTOREFERER, TRUE ); + + # FeedBurner requires a proper USER-AGENT... + curl_setopt($curl, CURL_HTTP_VERSION_1_1, true); + curl_setopt($curl, CURLOPT_ENCODING, "gzip, deflate"); + curl_setopt($curl, CURLOPT_USERAGENT, $useragent); + + $data = curl_exec($curl); + $httpcode = curl_getinfo($curl, CURLINFO_HTTP_CODE); + $httpcodeOK = isset($httpcode) and ($httpcode == 200 or $httpcode == 301); + curl_close($curl); + } else { + # create http context and add timeout and user-agent + $context = stream_context_create( + array( + 'http' => array( + 'timeout' => $timeout, + 'header' => "User-Agent: " . $useragent, + 'follow_location' => true + ), + 'ssl' => array( + 'verify_peer' => false, + 'allow_self_signed' => true + ) + ) + ); + + # only download page lesser than 4MB + $data = @file_get_contents($url, false, $context, -1, 4000000); + + if (isset($http_response_header) and isset($http_response_header[0])) { + $httpcodeOK = isset($http_response_header) and isset($http_response_header[0]) and ((strpos($http_response_header[0], '200 OK') !== FALSE) or (strpos($http_response_header[0], '301 Moved Permanently') !== FALSE)); + } + } + + # if response is not empty and response is OK + if (isset($data) and isset($httpcodeOK) and $httpcodeOK) { + + # take charset of page and get it + preg_match('##Usi', $data, $meta); + + # if meta tag is found + if (!empty($meta[0])) { + preg_match('#charset="?(.*)"#si', $meta[0], $encoding); + # if charset is found set it otherwise, set it to utf-8 + $html_charset = (!empty($encoding[1])) ? strtolower($encoding[1]) : 'utf-8'; + } else { + $html_charset = 'utf-8'; + $encoding[1] = ''; + } + + # replace charset of url to charset of page + $data = str_replace('charset=' . $encoding[1], 'charset=' . $html_charset, $data); + + return $data; + } + else { + return FALSE; + } + } + + public static function renderJson($data) + { + header('Cache-Control: no-cache, must-revalidate'); + header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); + header('Content-type: application/json; charset=UTF-8'); + echo json_encode($data); + exit(); + } + + public static function logm($message) + { + if (DEBUG_POCHE) { + $t = strval(date('Y/m/d_H:i:s')) . ' - ' . $_SERVER["REMOTE_ADDR"] . ' - ' . strval($message) . "\n"; + file_put_contents('./log.txt', $t, FILE_APPEND); + } + } + + public static function encodeString($string) + { + return sha1($string . SALT); + } +} \ No newline at end of file diff --git a/inc/poche/Url.class.php b/inc/poche/Url.class.php new file mode 100644 index 0000000..f4a8f99 --- /dev/null +++ b/inc/poche/Url.class.php @@ -0,0 +1,94 @@ + + * @copyright 2013 + * @license http://www.wtfpl.net/ see COPYING file + */ + +class Url +{ + public $url; + + function __construct($url) + { + $this->url = base64_decode($url); + } + + public function getUrl() { + return $this->url; + } + + public function setUrl($url) { + $this->url = $url; + } + + public function isCorrect() + { + $pattern = '|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i'; + + return preg_match($pattern, $this->url); + } + + public function clean() + { + $url = html_entity_decode(trim($this->url)); + + $stuff = strpos($url,'&utm_source='); + if ($stuff !== FALSE) + $url = substr($url, 0, $stuff); + $stuff = strpos($url,'?utm_source='); + if ($stuff !== FALSE) + $url = substr($url, 0, $stuff); + $stuff = strpos($url,'#xtor=RSS-'); + if ($stuff !== FALSE) + $url = substr($url, 0, $stuff); + + $this->url = $url; + } + + public function fetchContent() + { + if ($this->isCorrect()) { + $this->clean(); + $html = Encoding::toUTF8(Tools::getFile($this->getUrl())); + + # if Tools::getFile() if not able to retrieve HTTPS content, try the same URL with HTTP protocol + if (!preg_match('!^https?://!i', $this->getUrl()) && (!isset($html) || strlen($html) <= 0)) { + $this->setUrl('http://' . $this->getUrl()); + $html = Encoding::toUTF8(Tools::getFile($this->getUrl())); + } + + if (function_exists('tidy_parse_string')) { + $tidy = tidy_parse_string($html, array(), 'UTF8'); + $tidy->cleanRepair(); + $html = $tidy->value; + } + + $parameters = array(); + if (isset($html) and strlen($html) > 0) + { + $readability = new Readability($html, $this->getUrl()); + $readability->convertLinksToFootnotes = CONVERT_LINKS_FOOTNOTES; + $readability->revertForcedParagraphElements = REVERT_FORCED_PARAGRAPH_ELEMENTS; + + if($readability->init()) + { + $content = $readability->articleContent->innerHTML; + $parameters['title'] = $readability->articleTitle->innerHTML; + $parameters['content'] = $content; + + return $parameters; + } + } + } + else { + #$msg->add('e', _('error during url preparation : the link is not valid')); + Tools::logm($this->getUrl() . ' is not a valid url'); + } + + return FALSE; + } +} \ No newline at end of file diff --git a/inc/poche/config.inc.php b/inc/poche/config.inc.php new file mode 100644 index 0000000..81297e0 --- /dev/null +++ b/inc/poche/config.inc.php @@ -0,0 +1,40 @@ + + * @copyright 2013 + * @license http://www.wtfpl.net/ see COPYING file + */ + +define ('POCHE_VERSION', '0.4'); +define ('MODE_DEMO', FALSE); +define ('DEBUG_POCHE', FALSE); +define ('CONVERT_LINKS_FOOTNOTES', FALSE); +define ('REVERT_FORCED_PARAGRAPH_ELEMENTS', FALSE); +define ('DOWNLOAD_PICTURES', FALSE); +define ('SALT', '464v54gLLw928uz4zUBqkRJeiPY68zCX'); +define ('ABS_PATH', 'assets/'); +define ('TPL', './tpl'); +define ('LOCALE', './locale'); +define ('CACHE', './cache'); +define ('LANG', 'fr_FR.UTF8'); +$storage_type = 'sqlite'; # sqlite, file + +# /!\ Be careful if you change the lines below /!\ +require_once './inc/poche/Tools.class.php'; +require_once './inc/poche/Url.class.php'; +require_once './inc/poche/Poche.class.php'; +require_once './inc/3rdparty/Readability.php'; +require_once './inc/3rdparty/Encoding.php'; +require_once './inc/3rdparty/Session.class.php'; +require_once './inc/store/store.class.php'; +require_once './inc/store/' . $storage_type . '.class.php'; +require_once './vendor/autoload.php'; + +if (DOWNLOAD_PICTURES) { + require_once './inc/poche/pochePicture.php'; +} + +$poche = new Poche($storage_type); \ No newline at end of file diff --git a/inc/poche/pocheCore.php b/inc/poche/pocheCore.php deleted file mode 100644 index 74b063e..0000000 --- a/inc/poche/pocheCore.php +++ /dev/null @@ -1,269 +0,0 @@ - - * @copyright 2013 - * @license http://www.wtfpl.net/ see COPYING file - */ - -function encode_string($string) -{ - return sha1($string . SALT); -} - -function get_external_file($url) -{ - $timeout = 15; - $useragent = "Mozilla/5.0 (Windows NT 5.1; rv:18.0) Gecko/20100101 Firefox/18.0"; - - if (in_array ('curl', get_loaded_extensions())) { - # Fetch feed from URL - $curl = curl_init(); - curl_setopt($curl, CURLOPT_URL, $url); - curl_setopt($curl, CURLOPT_TIMEOUT, $timeout); - curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - curl_setopt($curl, CURLOPT_HEADER, false); - - # for ssl, do not verified certificate - curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); - curl_setopt($curl, CURLOPT_AUTOREFERER, TRUE ); - - # FeedBurner requires a proper USER-AGENT... - curl_setopt($curl, CURL_HTTP_VERSION_1_1, true); - curl_setopt($curl, CURLOPT_ENCODING, "gzip, deflate"); - curl_setopt($curl, CURLOPT_USERAGENT, $useragent); - - $data = curl_exec($curl); - $httpcode = curl_getinfo($curl, CURLINFO_HTTP_CODE); - $httpcodeOK = isset($httpcode) and ($httpcode == 200 or $httpcode == 301); - curl_close($curl); - } else { - # create http context and add timeout and user-agent - $context = stream_context_create( - array( - 'http' => array( - 'timeout' => $timeout, - 'header' => "User-Agent: " . $useragent, - 'follow_location' => true - ), - 'ssl' => array( - 'verify_peer' => false, - 'allow_self_signed' => true - ) - ) - ); - - # only download page lesser than 4MB - $data = @file_get_contents($url, false, $context, -1, 4000000); - - if (isset($http_response_header) and isset($http_response_header[0])) { - $httpcodeOK = isset($http_response_header) and isset($http_response_header[0]) and ((strpos($http_response_header[0], '200 OK') !== FALSE) or (strpos($http_response_header[0], '301 Moved Permanently') !== FALSE)); - } - } - - # if response is not empty and response is OK - if (isset($data) and isset($httpcodeOK) and $httpcodeOK) { - - # take charset of page and get it - preg_match('##Usi', $data, $meta); - - # if meta tag is found - if (!empty($meta[0])) { - preg_match('#charset="?(.*)"#si', $meta[0], $encoding); - # if charset is found set it otherwise, set it to utf-8 - $html_charset = (!empty($encoding[1])) ? strtolower($encoding[1]) : 'utf-8'; - } else { - $html_charset = 'utf-8'; - $encoding[1] = ''; - } - - # replace charset of url to charset of page - $data = str_replace('charset=' . $encoding[1], 'charset=' . $html_charset, $data); - - return $data; - } - else { - return FALSE; - } -} - -function fetch_url_content($url) -{ - $url = base64_decode($url); - if (pocheTools::isUrl($url)) { - $url = pocheTools::cleanURL($url); - $html = Encoding::toUTF8(get_external_file($url)); - - # if get_external_file if not able to retrieve HTTPS content, try the same URL with HTTP protocol - if (!preg_match('!^https?://!i', $url) && (!isset($html) || strlen($html) <= 0)) { - $url = 'http://' . $url; - $html = Encoding::toUTF8(get_external_file($url)); - } - - if (function_exists('tidy_parse_string')) { - $tidy = tidy_parse_string($html, array(), 'UTF8'); - $tidy->cleanRepair(); - $html = $tidy->value; - } - - $parameters = array(); - if (isset($html) and strlen($html) > 0) - { - $readability = new Readability($html, $url); - $readability->convertLinksToFootnotes = CONVERT_LINKS_FOOTNOTES; - $readability->revertForcedParagraphElements = REVERT_FORCED_PARAGRAPH_ELEMENTS; - - if($readability->init()) - { - $content = $readability->articleContent->innerHTML; - $parameters['title'] = $readability->articleTitle->innerHTML; - $parameters['content'] = $content; - - return $parameters; - } - } - } - else { - #$msg->add('e', _('error during url preparation : the link is not valid')); - pocheTools::logm($url . ' is not a valid url'); - } - - return FALSE; -} - -function get_tpl_file($view) -{ - $tpl_file = 'home.twig'; - switch ($view) - { - case 'install': - $tpl_file = 'install.twig'; - break; - case 'import'; - $tpl_file = 'import.twig'; - break; - case 'export': - $tpl_file = 'export.twig'; - break; - case 'config': - $tpl_file = 'config.twig'; - break; - case 'view': - $tpl_file = 'view.twig'; - break; - default: - break; - } - return $tpl_file; -} - -function display_view($view, $id = 0) -{ - global $store; - - $tpl_vars = array(); - - switch ($view) - { - case 'install': - pocheTools::logm('install mode'); - break; - case 'import'; - pocheTools::logm('import mode'); - break; - case 'export': - $entries = $store->retrieveAll(); - $tpl->assign('export', pocheTools::renderJson($entries)); - $tpl->draw('export'); - pocheTools::logm('export view'); - break; - case 'config': - pocheTools::logm('config view'); - break; - case 'view': - $entry = $store->retrieveOneById($id); - if ($entry != NULL) { - pocheTools::logm('view link #' . $id); - $content = $entry['content']; - if (function_exists('tidy_parse_string')) { - $tidy = tidy_parse_string($content, array('indent'=>true, 'show-body-only' => true), 'UTF8'); - $tidy->cleanRepair(); - $content = $tidy->value; - } - $tpl_vars = array( - 'entry' => $entry, - 'content' => $content, - ); - } - else { - pocheTools::logm('error in view call : entry is NULL'); - } - break; - default: # home view - $entries = $store->getEntriesByView($view); - $tpl_vars = array( - 'entries' => $entries, - ); - break; - } - - return $tpl_vars; -} - -/** - * Call action (mark as fav, archive, delete, etc.) - */ -function action_to_do($action, $url, $id = 0) -{ - global $store; - - switch ($action) - { - case 'add': - if($parametres_url = fetch_url_content($url)) { - if ($store->add($url, $parametres_url['title'], $parametres_url['content'])) { - pocheTools::logm('add link ' . $url); - $last_id = $store->getLastId(); - if (DOWNLOAD_PICTURES) { - $content = filtre_picture($parametres_url['content'], $url, $last_id); - } - #$msg->add('s', _('the link has been added successfully')); - } - else { - #$msg->add('e', _('error during insertion : the link wasn\'t added')); - pocheTools::logm('error during insertion : the link wasn\'t added'); - } - } - else { - #$msg->add('e', _('error during url preparation : the link wasn\'t added')); - pocheTools::logm('error during content fetch'); - } - break; - case 'delete': - if ($store->deleteById($id)) { - if (DOWNLOAD_PICTURES) { - remove_directory(ABS_PATH . $id); - } - #$msg->add('s', _('the link has been deleted successfully')); - pocheTools::logm('delete link #' . $id); - } - else { - #$msg->add('e', _('the link wasn\'t deleted')); - pocheTools::logm('error : can\'t delete link #' . $id); - } - break; - case 'toggle_fav' : - $store->favoriteById($id); - pocheTools::logm('mark as favorite link #' . $id); - break; - case 'toggle_archive' : - $store->archiveById($id); - pocheTools::logm('archive link #' . $id); - break; - default: - break; - } -} diff --git a/inc/poche/pochePictures.php b/inc/poche/pochePictures.php index 0d73a14..4e4a0b0 100644 --- a/inc/poche/pochePictures.php +++ b/inc/poche/pochePictures.php @@ -67,7 +67,7 @@ function get_absolute_link($relative_link, $url) { */ function download_pictures($absolute_path, $fullpath) { - $rawdata = get_external_file($absolute_path); + $rawdata = Tools::getFile($absolute_path); if(file_exists($fullpath)) { unlink($fullpath); diff --git a/inc/poche/pocheTools.class.php b/inc/poche/pocheTools.class.php deleted file mode 100644 index 08c9dc8..0000000 --- a/inc/poche/pocheTools.class.php +++ /dev/null @@ -1,126 +0,0 @@ - - * @copyright 2013 - * @license http://www.wtfpl.net/ see COPYING file - */ - -class pocheTools -{ - public static function initPhp() - { - define('START_TIME', microtime(true)); - - if (phpversion() < 5) { - die(_('Oops, it seems you don\'t have PHP 5.')); - } - - error_reporting(E_ALL); - - function stripslashesDeep($value) { - return is_array($value) - ? array_map('stripslashesDeep', $value) - : stripslashes($value); - } - - if (get_magic_quotes_gpc()) { - $_POST = array_map('stripslashesDeep', $_POST); - $_GET = array_map('stripslashesDeep', $_GET); - $_COOKIE = array_map('stripslashesDeep', $_COOKIE); - } - - ob_start(); - register_shutdown_function('ob_end_flush'); - } - - public static function isUrl($url) - { - $pattern = '|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i'; - - return preg_match($pattern, $url); - } - - public static function getUrl() - { - $https = (!empty($_SERVER['HTTPS']) - && (strtolower($_SERVER['HTTPS']) == 'on')) - || (isset($_SERVER["SERVER_PORT"]) - && $_SERVER["SERVER_PORT"] == '443'); // HTTPS detection. - $serverport = (!isset($_SERVER["SERVER_PORT"]) - || $_SERVER["SERVER_PORT"] == '80' - || ($https && $_SERVER["SERVER_PORT"] == '443') - ? '' : ':' . $_SERVER["SERVER_PORT"]); - - $scriptname = str_replace('/index.php', '/', $_SERVER["SCRIPT_NAME"]); - - if (!isset($_SERVER["SERVER_NAME"])) { - return $scriptname; - } - - return 'http' . ($https ? 's' : '') . '://' - . $_SERVER["SERVER_NAME"] . $serverport . $scriptname; - } - - public static function redirect($url = '') - { - if ($url === '') { - $url = (empty($_SERVER['HTTP_REFERER'])?'?':$_SERVER['HTTP_REFERER']); - if (isset($_POST['returnurl'])) { - $url = $_POST['returnurl']; - } - } - - # prevent loop - if (empty($url) || parse_url($url, PHP_URL_QUERY) === $_SERVER['QUERY_STRING']) { - $url = pocheTools::getUrl(); - } - - if (substr($url, 0, 1) !== '?') { - $ref = pocheTools::getUrl(); - if (substr($url, 0, strlen($ref)) !== $ref) { - $url = $ref; - } - } - header('Location: '.$url); - exit(); - } - - public static function cleanURL($url) - { - - $url = html_entity_decode(trim($url)); - - $stuff = strpos($url,'&utm_source='); - if ($stuff !== FALSE) - $url = substr($url, 0, $stuff); - $stuff = strpos($url,'?utm_source='); - if ($stuff !== FALSE) - $url = substr($url, 0, $stuff); - $stuff = strpos($url,'#xtor=RSS-'); - if ($stuff !== FALSE) - $url = substr($url, 0, $stuff); - - return $url; - } - - public static function renderJson($data) - { - header('Cache-Control: no-cache, must-revalidate'); - header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); - header('Content-type: application/json; charset=UTF-8'); - - echo json_encode($data); - exit(); - } - - public static function logm($message) - { - if (DEBUG_POCHE) { - $t = strval(date('Y/m/d_H:i:s')) . ' - ' . $_SERVER["REMOTE_ADDR"] . ' - ' . strval($message) . "\n"; - file_put_contents('./log.txt', $t, FILE_APPEND); - } - } -} \ No newline at end of file diff --git a/inc/store/sqlite.class.php b/inc/store/sqlite.class.php index 2108160..a15bc09 100644 --- a/inc/store/sqlite.class.php +++ b/inc/store/sqlite.class.php @@ -90,7 +90,7 @@ class Sqlite extends Store { } catch (Exception $e) { - logm('execute query error : '.$e->getMessage()); + Tools::logm('execute query error : '.$e->getMessage()); } } diff --git a/index.php b/index.php index dc06442..4077969 100644 --- a/index.php +++ b/index.php @@ -8,7 +8,7 @@ * @license http://www.wtfpl.net/ see COPYING file */ -include dirname(__FILE__).'/inc/config.php'; +include dirname(__FILE__).'/inc/poche/config.inc.php'; $notices = array(); @@ -26,9 +26,9 @@ $referer = empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER']; if (isset($_GET['login'])) { # hello you if (!empty($_POST['login']) && !empty($_POST['password'])) { - if (Session::login($_SESSION['login'], $_SESSION['pass'], $_POST['login'], encode_string($_POST['password'] . $_POST['login']))) { - pocheTools::logm('login successful'); - $pocheTools[]['value'] = _('login successful'); + if (Session::login($_SESSION['login'], $_SESSION['pass'], $_POST['login'], Tools::encodeString($_POST['password'] . $_POST['login']))) { + Tools::logm('login successful'); + $notices['value'] = _('login successful'); if (!empty($_POST['longlastingsession'])) { $_SESSION['longlastingsession'] = 31536000; @@ -38,34 +38,34 @@ if (isset($_GET['login'])) { session_set_cookie_params(0); } session_regenerate_id(true); - pocheTools::redirect($referer); + Tools::redirect($referer); } - pocheTools::logm('login failed'); - $notices[]['value'] = _('Login failed !'); - pocheTools::redirect(); + Tools::logm('login failed'); + $notices['value'] = _('Login failed !'); + Tools::redirect(); } else { - pocheTools::logm('login failed'); - pocheTools::redirect(); + Tools::logm('login failed'); + Tools::redirect(); } } elseif (isset($_GET['logout'])) { # see you soon ! - pocheTools::logm('logout'); + Tools::logm('logout'); Session::logout(); - pocheTools::redirect(); + Tools::redirect(); } elseif (isset($_GET['config'])) { # Update password if (isset($_POST['password']) && isset($_POST['password_repeat'])) { if ($_POST['password'] == $_POST['password_repeat'] && $_POST['password'] != "") { if (!MODE_DEMO) { - pocheTools::logm('password updated'); - $store->updatePassword(encode_string($_POST['password'] . $_SESSION['login'])); + Tools::logm('password updated'); + $poche->store->updatePassword(Tools::encodeString($_POST['password'] . $_SESSION['login'])); Session::logout(); - pocheTools::redirect(); + Tools::redirect(); } else { - pocheTools::logm('in demo mode, you can\'t do this'); + Tools::logm('in demo mode, you can\'t do this'); } } } @@ -77,12 +77,13 @@ $full_head = (isset ($_REQUEST['full_head'])) ? htmlentities($_REQUEST['full_hea $action = (isset ($_REQUEST['action'])) ? htmlentities($_REQUEST['action']) : ''; $_SESSION['sort'] = (isset ($_REQUEST['sort'])) ? htmlentities($_REQUEST['sort']) : 'id'; $id = (isset ($_REQUEST['id'])) ? htmlspecialchars($_REQUEST['id']) : ''; -$url = (isset ($_GET['url'])) ? $_GET['url'] : ''; + +$url = new Url((isset ($_GET['url'])) ? $_GET['url'] : ''); $tpl_vars = array( 'referer' => $referer, 'view' => $view, - 'poche_url' => pocheTools::getUrl(), + 'poche_url' => Tools::getPocheUrl(), 'demo' => MODE_DEMO, 'title' => _('poche, a read it later open source system'), 'token' => Session::getToken(), @@ -90,12 +91,12 @@ $tpl_vars = array( ); if (Session::isLogged()) { - action_to_do($action, $url, $id); - $tpl_file = get_tpl_file($view); - $tpl_vars = array_merge($tpl_vars, display_view($view, $id)); + $poche->action($action, $url, $id); + $tpl_file = Tools::getTplFile($view); + $tpl_vars = array_merge($tpl_vars, $poche->displayView($view, $id)); } else { $tpl_file = 'login.twig'; } -echo $twig->render($tpl_file, $tpl_vars); \ No newline at end of file +echo $poche->tpl->render($tpl_file, $tpl_vars); \ No newline at end of file diff --git a/tpl/config.twig b/tpl/config.twig index be2c045..4e7bb9d 100644 --- a/tpl/config.twig +++ b/tpl/config.twig @@ -15,7 +15,7 @@

{% trans "Bookmarklet" %}

{% trans "Thanks to the bookmarklet, you will be able to easily add a link to your poche." %} {% trans "Have a look to this documentation:" %} http://inthepoche.com/?pages/Documentation.

{% trans "Drag & drop this link to your bookmarks bar and have fun with poche." %}

-

{% trans "poche it!" %}

+

{% trans "poche it!" %}

{% trans "Change your password" %}

From c765c3679fee3ed9e4bad9954a808116187a7e83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Sun, 4 Aug 2013 21:42:46 +0200 Subject: [PATCH 21/49] import in poche and not in an external file --- import.php | 73 --------------------- inc/poche/Poche.class.php | 108 ++++++++++++++++++++++++++++++- inc/poche/config.inc.php | 1 + index.php | 49 ++------------ tpl/config.twig | 11 +++- tpl/{export.html => export.twig} | 0 tpl/home.twig | 9 --- tpl/layout.twig | 1 - tpl/login.twig | 9 --- 9 files changed, 125 insertions(+), 136 deletions(-) delete mode 100644 import.php rename tpl/{export.html => export.twig} (100%) diff --git a/import.php b/import.php deleted file mode 100644 index 45fe833..0000000 --- a/import.php +++ /dev/null @@ -1,73 +0,0 @@ - - * @copyright 2013 - * @license http://www.wtfpl.net/ see COPYING file - */ - -set_time_limit(0); - -include dirname(__FILE__).'/inc/config.php'; -include dirname(__FILE__).'/inc/simple_html_dom.php'; - -if (!isset($_GET['start'])) { - echo _('Please execute the import script locally, it can take a very long time.') . '

' . _('Please choose between Pocket & Readabilty :') . '
' . _('Bye bye Pocket, let\'s go !') . '
' . _('Bye bye Readability, let\'s go !') . ''; -} -else { - if ($_GET['start'] == 'pocket') { - $html = new simple_html_dom(); - $html->load_file('ril_export.html'); - - $read = 0; - $errors = array(); - foreach($html->find('ul') as $ul) - { - foreach($ul->find('li') as $li) - { - $a = $li->find('a'); - $url = $a[0]->href; - - action_to_do('add', $url); - if ($read == '1') { - $last_id = $db->getHandle()->lastInsertId(); - $sql_update = "UPDATE entries SET is_read=~is_read WHERE id=?"; - $params_update = array($last_id); - $query_update = $db->getHandle()->prepare($sql_update); - $query_update->execute($params_update); - } - } - # Pocket génère un fichier HTML avec deux
    - # Le premier concerne les éléments non lus - # Le second concerne les éléments archivés - $read = 1; - } - - echo _('Import from Pocket completed.') . '' . _('Welcome to poche !') .''; - logm('import from pocket completed'); - } - else if ($_GET['start'] == 'readability') { - $str_data = file_get_contents("readability"); - $data = json_decode($str_data,true); - - foreach ($data as $key => $value) { - $url = ''; - foreach ($value as $key2 => $value2) { - if ($key2 == 'article__url') { - $url = $value2; - } - } - if ($url != '') - action_to_do('add', $url); - } - - echo _('Import from Readability completed.') . '' . _('Welcome to poche !') . ''; - logm('import from Readability completed'); - } - else { - echo _('Error with the import.') . '' . _('Back to poche'). ''; - logm('error with the import'); - } -} \ No newline at end of file diff --git a/inc/poche/Poche.class.php b/inc/poche/Poche.class.php index 973ae3e..9e407d4 100644 --- a/inc/poche/Poche.class.php +++ b/inc/poche/Poche.class.php @@ -73,7 +73,7 @@ class Poche /** * Call action (mark as fav, archive, delete, etc.) */ - public function action($action, Url $url, $id) + public function action($action, Url $url, $id = 0) { switch ($action) { @@ -118,6 +118,8 @@ class Poche $this->store->archiveById($id); Tools::logm('archive link #' . $id); break; + case 'import': + break; default: break; } @@ -173,4 +175,108 @@ class Poche return $tpl_vars; } + + public function updatePassword() + { + if (isset($_POST['password']) && isset($_POST['password_repeat'])) { + if ($_POST['password'] == $_POST['password_repeat'] && $_POST['password'] != "") { + if (!MODE_DEMO) { + Tools::logm('password updated'); + $this->store->updatePassword(Tools::encodeString($_POST['password'] . $_SESSION['login'])); + Session::logout(); + Tools::redirect(); + } + else { + Tools::logm('in demo mode, you can\'t do this'); + } + } + } + } + + public function login($referer) + { + if (!empty($_POST['login']) && !empty($_POST['password'])) { + if (Session::login($_SESSION['login'], $_SESSION['pass'], $_POST['login'], Tools::encodeString($_POST['password'] . $_POST['login']))) { + Tools::logm('login successful'); + + if (!empty($_POST['longlastingsession'])) { + $_SESSION['longlastingsession'] = 31536000; + $_SESSION['expires_on'] = time() + $_SESSION['longlastingsession']; + session_set_cookie_params($_SESSION['longlastingsession']); + } else { + session_set_cookie_params(0); + } + session_regenerate_id(true); + Tools::redirect($referer); + } + Tools::logm('login failed'); + Tools::redirect(); + } else { + Tools::logm('login failed'); + Tools::redirect(); + } + } + + public function logout() + { + Tools::logm('logout'); + Session::logout(); + Tools::redirect(); + } + + public function import($from) + { + if ($from == 'pocket') { + $html = new simple_html_dom(); + $html->load_file('./ril_export.html'); + + $read = 0; + $errors = array(); + foreach($html->find('ul') as $ul) + { + foreach($ul->find('li') as $li) + { + $a = $li->find('a'); + $url = new Url($a[0]->href); + $this->action('add', $url); + if ($read == '1') { + $last_id = $this->store->lastInsertId(); + $sql_update = "UPDATE entries SET is_read=~is_read WHERE id=?"; + $params_update = array($last_id); + $query_update = $this->store->prepare($sql_update); + $query_update->execute($params_update); + } + } + # Pocket génère un fichier HTML avec deux
      + # Le premier concerne les éléments non lus + # Le second concerne les éléments archivés + $read = 1; + } + logm('import from pocket completed'); + Tools::redirect(); + } + else if ($from == 'readability') { + # TODO finaliser tout ça ici + $str_data = file_get_contents("readability"); + $data = json_decode($str_data,true); + + foreach ($data as $key => $value) { + $url = ''; + foreach ($value as $key2 => $value2) { + if ($key2 == 'article__url') { + $url = new Url($value2); + } + } + if ($url != '') + action_to_do('add', $url); + } + logm('import from Readability completed'); + Tools::redirect(); + } + } + + public function export() + { + + } } \ No newline at end of file diff --git a/inc/poche/config.inc.php b/inc/poche/config.inc.php index 81297e0..98a9ee6 100644 --- a/inc/poche/config.inc.php +++ b/inc/poche/config.inc.php @@ -32,6 +32,7 @@ require_once './inc/3rdparty/Session.class.php'; require_once './inc/store/store.class.php'; require_once './inc/store/' . $storage_type . '.class.php'; require_once './vendor/autoload.php'; +require_once './inc/3rdparty/simple_html_dom.php'; if (DOWNLOAD_PICTURES) { require_once './inc/poche/pochePicture.php'; diff --git a/index.php b/index.php index 4077969..1554c0a 100644 --- a/index.php +++ b/index.php @@ -10,8 +10,6 @@ include dirname(__FILE__).'/inc/poche/config.inc.php'; -$notices = array(); - # XSRF protection with token // if (!empty($_POST)) { // if (!Session::isToken($_POST['token'])) { @@ -25,50 +23,18 @@ $referer = empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER']; if (isset($_GET['login'])) { # hello you - if (!empty($_POST['login']) && !empty($_POST['password'])) { - if (Session::login($_SESSION['login'], $_SESSION['pass'], $_POST['login'], Tools::encodeString($_POST['password'] . $_POST['login']))) { - Tools::logm('login successful'); - $notices['value'] = _('login successful'); - - if (!empty($_POST['longlastingsession'])) { - $_SESSION['longlastingsession'] = 31536000; - $_SESSION['expires_on'] = time() + $_SESSION['longlastingsession']; - session_set_cookie_params($_SESSION['longlastingsession']); - } else { - session_set_cookie_params(0); - } - session_regenerate_id(true); - Tools::redirect($referer); - } - Tools::logm('login failed'); - $notices['value'] = _('Login failed !'); - Tools::redirect(); - } else { - Tools::logm('login failed'); - Tools::redirect(); - } + $poche->login($referer); } elseif (isset($_GET['logout'])) { # see you soon ! - Tools::logm('logout'); - Session::logout(); - Tools::redirect(); + $poche->logout(); } -elseif (isset($_GET['config'])) { +elseif (isset($_GET['config'])) { # Update password - if (isset($_POST['password']) && isset($_POST['password_repeat'])) { - if ($_POST['password'] == $_POST['password_repeat'] && $_POST['password'] != "") { - if (!MODE_DEMO) { - Tools::logm('password updated'); - $poche->store->updatePassword(Tools::encodeString($_POST['password'] . $_SESSION['login'])); - Session::logout(); - Tools::redirect(); - } - else { - Tools::logm('in demo mode, you can\'t do this'); - } - } - } + $poche->updatePassword(); +} +elseif (isset($_GET['import'])) { + $poche->import($_GET['from']); } # Aaaaaaand action ! @@ -87,7 +53,6 @@ $tpl_vars = array( 'demo' => MODE_DEMO, 'title' => _('poche, a read it later open source system'), 'token' => Session::getToken(), - 'notices' => $notices, ); if (Session::isLogged()) { diff --git a/tpl/config.twig b/tpl/config.twig index 4e7bb9d..9a51e56 100644 --- a/tpl/config.twig +++ b/tpl/config.twig @@ -13,7 +13,7 @@ {% block content %}

      {% trans "Bookmarklet" %}

      -

      {% trans "Thanks to the bookmarklet, you will be able to easily add a link to your poche." %} {% trans "Have a look to this documentation:" %} http://inthepoche.com/?pages/Documentation.

      +

      {% trans "Thanks to the bookmarklet, you will be able to easily add a link to your poche." %} {% trans "Have a look to this documentation:" %} inthepoche.com.

      {% trans "Drag & drop this link to your bookmarks bar and have fun with poche." %}

      {% trans "poche it!" %}

      @@ -35,6 +35,15 @@ + +

      {% trans "Import" %}

      +

      {% trans "Please execute the import script locally, it can take a very long time." %}

      +

      {% trans "More infos in the official doc:" %} inthepoche.com

      +

      +

      {% trans "Export your poche datas" %}

      {% trans "Click here" %} {% trans "to export your poche datas." %}

      diff --git a/tpl/export.html b/tpl/export.twig similarity index 100% rename from tpl/export.html rename to tpl/export.twig diff --git a/tpl/home.twig b/tpl/home.twig index 5752b2c..49ef905 100644 --- a/tpl/home.twig +++ b/tpl/home.twig @@ -15,15 +15,6 @@
    • {% trans "by title" %}
    {% endblock %} -{% block notices %} -
    -
      - {% for notice in notices %} -
    • {{ notice.value|e }}
    • - {% endfor %} -
    -
    -{% endblock %} {% block content %}
    {% for entry in entries %} diff --git a/tpl/layout.twig b/tpl/layout.twig index 9dc83ef..cbe965f 100644 --- a/tpl/layout.twig +++ b/tpl/layout.twig @@ -17,7 +17,6 @@
    {% block menu %}{% endblock %} {% block precontent %}{% endblock %} - {% block messages %}{% endblock %} {% block content %}{% endblock %} {% block js %}{% endblock %}
    diff --git a/tpl/login.twig b/tpl/login.twig index fcb9b4d..70c2190 100644 --- a/tpl/login.twig +++ b/tpl/login.twig @@ -1,15 +1,6 @@ {% extends "layout.twig" %} {% block title %}{% trans "login to your poche" %}{% endblock %} -{% block notices %} -
    -
      - {% for notice in notices %} -
    • {{ notice.value|e }}
    • - {% endfor %} -
    -
    -{% endblock %} {% block content %}
    From 63c35580c7d60e2278ee6fe9ba2d4440ff0308d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Sun, 4 Aug 2013 22:35:08 +0200 Subject: [PATCH 22/49] twig implementation --- README.md | 33 +++++----- inc/poche/Poche.class.php | 123 ++++++++++++++++++++------------------ inc/poche/Tools.class.php | 5 ++ index.php | 34 +++++------ tpl/_footer.twig | 2 +- tpl/config.twig | 6 +- tpl/export.twig | 2 +- tpl/js/poche.js | 2 +- tpl/login.twig | 4 +- 9 files changed, 111 insertions(+), 100 deletions(-) diff --git a/README.md b/README.md index b44e7d3..09f48b1 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # poche -Abandon Pocket, Instapaper and other Readability service : adopt poche. It is the same, but it is open source. +Abandon Pocket, Instapaper and other Readability service : adopt poche. It is the same, but it is open source. Moreover, you can migrate from Pocket & Readability. ![poche](http://inthepoche.com/img/logo.png) @@ -11,23 +11,23 @@ To get news from poche, [follow us on twitter](http://twitter.com/getpoche) or [ [![flattr](http://api.flattr.com/button/flattr-badge-large.png)](http://flattr.com/thing/1265480/poche-a-read-it-later-open-source-system) -## Usage -You can easily add a "poched" page with the bookmarklet. - -poche save the entire content of a poched links : text and pictures are stored on your server. - -You can : -* read a page in a comfortable reading view -* archive a link -* put a link in favorite -* delete a link - ## Requirements & installation You have to install [sqlite for php](http://www.php.net/manual/en/book.sqlite.php) on your server. +[PHP cURL](http://www.php.net/manual/en/book.curl.php) & [tidy_parse_string](http://www.php.net/manual/en/tidy.parsestring.php) are recommended. + Get the [latest version](https://github.com/inthepoche/poche) of poche on github. Unzip it and upload it on your server. poche must have write access on assets, cache and db directories. -That's all, **poche works** ! +Install composer in your project : +```bash +curl -s http://getcomposer.org/installer | php +``` +Install via composer : +```bash +php composer.phar install +``` + +That's all, you can use poche ! ## Security You **have** to protect your db/poche.sqlite file. Modify the virtual host of your website to add this condition : @@ -46,12 +46,11 @@ location ~ /(db) { } ``` -## Import from Pocket - -If you want to import your Pocket datas, [export them here](https://getpocket.com/export). Put the HTML file in your poche directory, execute import.php file locally by following instructions. Be careful, the script can take a very long time. +## Usage +See the documentation on our website : [inthepoche.com](http://inthepoche.com). ## License Copyright © 2010-2013 Nicolas Lœuillet This work is free. You can redistribute it and/or modify it under the terms of the Do What The Fuck You Want To Public License, Version 2, -as published by Sam Hocevar. See the COPYING file for more details. +as published by Sam Hocevar. See the COPYING file for more details. \ No newline at end of file diff --git a/inc/poche/Poche.class.php b/inc/poche/Poche.class.php index 9e407d4..5c3eda8 100644 --- a/inc/poche/Poche.class.php +++ b/inc/poche/Poche.class.php @@ -118,8 +118,6 @@ class Poche $this->store->archiveById($id); Tools::logm('archive link #' . $id); break; - case 'import': - break; default: break; } @@ -131,18 +129,6 @@ class Poche switch ($view) { - case 'install': - Tools::logm('install mode'); - break; - case 'import'; - Tools::logm('import mode'); - break; - case 'export': - $entries = $this->store->retrieveAll(); - // $tpl->assign('export', Tools::renderJson($entries)); - // $tpl->draw('export'); - Tools::logm('export view'); - break; case 'config': Tools::logm('config view'); break; @@ -224,59 +210,80 @@ class Poche Tools::redirect(); } + private function importFromInstapaper() + { + Tools::logm('import from instapaper completed'); + Tools::redirect(); + } + + private function importFromPocket() + { + $html = new simple_html_dom(); + $html->load_file('./ril_export.html'); + + $read = 0; + $errors = array(); + foreach($html->find('ul') as $ul) + { + foreach($ul->find('li') as $li) + { + $a = $li->find('a'); + $url = new Url(base64_encode($a[0]->href)); + $this->action('add', $url); + if ($read == '1') { + $last_id = $this->store->getLastId(); + $this->store->archiveById($last_id); + } + } + # Pocket génère un fichier HTML avec deux
      + # Le premier concerne les éléments non lus + # Le second concerne les éléments archivés + $read = 1; + } + Tools::logm('import from pocket completed'); + Tools::redirect(); + } + + private function importFromReadability() + { + # TODO finaliser tout ça ici + # noms des variables + gestion des articles lus + $str_data = file_get_contents("./readability"); + $data = json_decode($str_data,true); + + foreach ($data as $key => $value) { + $url = ''; + foreach ($value as $key2 => $value2) { + if ($key2 == 'article__url') { + $url = new Url(base64_encode($value2)); + } + } + if ($url->isCorrect()) + $this->action('add', $url); + } + Tools::logm('import from Readability completed'); + Tools::redirect(); + } + public function import($from) { if ($from == 'pocket') { - $html = new simple_html_dom(); - $html->load_file('./ril_export.html'); - - $read = 0; - $errors = array(); - foreach($html->find('ul') as $ul) - { - foreach($ul->find('li') as $li) - { - $a = $li->find('a'); - $url = new Url($a[0]->href); - $this->action('add', $url); - if ($read == '1') { - $last_id = $this->store->lastInsertId(); - $sql_update = "UPDATE entries SET is_read=~is_read WHERE id=?"; - $params_update = array($last_id); - $query_update = $this->store->prepare($sql_update); - $query_update->execute($params_update); - } - } - # Pocket génère un fichier HTML avec deux
        - # Le premier concerne les éléments non lus - # Le second concerne les éléments archivés - $read = 1; - } - logm('import from pocket completed'); - Tools::redirect(); + $this->importFromPocket(); } else if ($from == 'readability') { - # TODO finaliser tout ça ici - $str_data = file_get_contents("readability"); - $data = json_decode($str_data,true); - - foreach ($data as $key => $value) { - $url = ''; - foreach ($value as $key2 => $value2) { - if ($key2 == 'article__url') { - $url = new Url($value2); - } - } - if ($url != '') - action_to_do('add', $url); - } - logm('import from Readability completed'); - Tools::redirect(); + $this->importFromReadability(); + } + else if ($from == 'instapaper') { + $this->importFromInstapaper(); } } public function export() { - + $entries = $this->store->retrieveAll(); + echo $this->tpl->render('export.twig', array( + 'export' => Tools::renderJson($entries), + )); + Tools::logm('export view'); } } \ No newline at end of file diff --git a/inc/poche/Tools.class.php b/inc/poche/Tools.class.php index c277035..1ff4ba5 100644 --- a/inc/poche/Tools.class.php +++ b/inc/poche/Tools.class.php @@ -205,4 +205,9 @@ class Tools { return sha1($string . SALT); } + + public static function checkVar($var) + { + return ((isset ($_REQUEST["$var"])) ? htmlentities($_REQUEST["$var"]) : ''); + } } \ No newline at end of file diff --git a/index.php b/index.php index 1554c0a..381b8cc 100644 --- a/index.php +++ b/index.php @@ -10,16 +10,21 @@ include dirname(__FILE__).'/inc/poche/config.inc.php'; -# XSRF protection with token -// if (!empty($_POST)) { -// if (!Session::isToken($_POST['token'])) { -// die(_('Wrong token')); -// // TODO remettre le test -// } -// unset($_SESSION['tokens']); -// } +#XSRF protection with token +if (!empty($_POST)) { + if (!Session::isToken($_POST['token'])) { + die(_('Wrong token')); + // TODO remettre le test + } + unset($_SESSION['tokens']); +} $referer = empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER']; +$view = Tools::checkVar('view'); +$action = Tools::checkVar('action'); +$id = Tools::checkVar('id'); +$_SESSION['sort'] = Tools::checkVar('sort'); +$url = new Url((isset ($_GET['url'])) ? $_GET['url'] : ''); if (isset($_GET['login'])) { # hello you @@ -36,15 +41,9 @@ elseif (isset($_GET['config'])) { elseif (isset($_GET['import'])) { $poche->import($_GET['from']); } - -# Aaaaaaand action ! -$view = (isset ($_REQUEST['view'])) ? htmlentities($_REQUEST['view']) : 'home'; -$full_head = (isset ($_REQUEST['full_head'])) ? htmlentities($_REQUEST['full_head']) : 'yes'; -$action = (isset ($_REQUEST['action'])) ? htmlentities($_REQUEST['action']) : ''; -$_SESSION['sort'] = (isset ($_REQUEST['sort'])) ? htmlentities($_REQUEST['sort']) : 'id'; -$id = (isset ($_REQUEST['id'])) ? htmlspecialchars($_REQUEST['id']) : ''; - -$url = new Url((isset ($_GET['url'])) ? $_GET['url'] : ''); +elseif (isset($_GET['export'])) { + $poche->export(); +} $tpl_vars = array( 'referer' => $referer, @@ -64,4 +63,5 @@ else { $tpl_file = 'login.twig'; } +# Aaaaaaand action ! echo $poche->tpl->render($tpl_file, $tpl_vars); \ No newline at end of file diff --git a/tpl/_footer.twig b/tpl/_footer.twig index 59b58fa..b1d7b8d 100644 --- a/tpl/_footer.twig +++ b/tpl/_footer.twig @@ -1,3 +1,3 @@
        -

        powered by poche

        +

        {% trans "powered by" %} poche

        \ No newline at end of file diff --git a/tpl/config.twig b/tpl/config.twig index 9a51e56..c18806b 100644 --- a/tpl/config.twig +++ b/tpl/config.twig @@ -40,11 +40,11 @@

        {% trans "Please execute the import script locally, it can take a very long time." %}

        {% trans "More infos in the official doc:" %} inthepoche.com

        {% trans "Export your poche datas" %}

        -

        {% trans "Click here" %} {% trans "to export your poche datas." %}

        +

        {% trans "Click here" %} {% trans "to export your poche datas." %}

    {% endblock %} \ No newline at end of file diff --git a/tpl/export.twig b/tpl/export.twig index d22d05f..4adb954 100644 --- a/tpl/export.twig +++ b/tpl/export.twig @@ -1 +1 @@ -export {$export} \ No newline at end of file +{{ export }} \ No newline at end of file diff --git a/tpl/js/poche.js b/tpl/js/poche.js index 97d9911..b4eac11 100644 --- a/tpl/js/poche.js +++ b/tpl/js/poche.js @@ -23,7 +23,7 @@ function toggle_archive(element, id, view_article) { } function sort_links(view, sort) { - $.get('index.php', { view: view, sort: sort, full_head: 'no' }, function(data) { + $.get('index.php', { view: view, sort: sort }, function(data) { $('#content').html(data); }); } diff --git a/tpl/login.twig b/tpl/login.twig index 70c2190..c302879 100644 --- a/tpl/login.twig +++ b/tpl/login.twig @@ -16,9 +16,9 @@
    - +
    - + {% trans "(Do not check on public computers)" %}
    From 2a1791a4b1c319fc5bbc286d0bc94827fe1feec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Sun, 4 Aug 2013 22:51:12 +0200 Subject: [PATCH 23/49] view of an article --- index.php | 14 +++++++------- tpl/css/style.css | 5 +++++ tpl/img/logo.png | Bin 911 -> 454 bytes tpl/img/messages/close.png | Bin 662 -> 0 bytes tpl/img/messages/cross.png | Bin 655 -> 0 bytes tpl/img/messages/help.png | Bin 786 -> 0 bytes tpl/img/messages/tick.png | Bin 537 -> 0 bytes tpl/img/messages/warning.png | Bin 666 -> 0 bytes tpl/view.twig | 4 ++-- 9 files changed, 14 insertions(+), 9 deletions(-) delete mode 100644 tpl/img/messages/close.png delete mode 100644 tpl/img/messages/cross.png delete mode 100644 tpl/img/messages/help.png delete mode 100644 tpl/img/messages/tick.png delete mode 100644 tpl/img/messages/warning.png diff --git a/index.php b/index.php index 381b8cc..94c72a2 100644 --- a/index.php +++ b/index.php @@ -11,13 +11,13 @@ include dirname(__FILE__).'/inc/poche/config.inc.php'; #XSRF protection with token -if (!empty($_POST)) { - if (!Session::isToken($_POST['token'])) { - die(_('Wrong token')); - // TODO remettre le test - } - unset($_SESSION['tokens']); -} +// if (!empty($_POST)) { +// if (!Session::isToken($_POST['token'])) { +// die(_('Wrong token')); +// // TODO remettre le test +// } +// unset($_SESSION['tokens']); +// } $referer = empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER']; $view = Tools::checkVar('view'); diff --git a/tpl/css/style.css b/tpl/css/style.css index d856a35..6b9f6ac 100644 --- a/tpl/css/style.css +++ b/tpl/css/style.css @@ -8,6 +8,10 @@ header { text-align: center; } +header h1 { + font-size: 1.3em; +} + .bouton { border-radius: 2px; } @@ -16,6 +20,7 @@ header { padding: 0; list-style-type: none; text-align: center; + font-size: 0.9em; } #main ul#links li { diff --git a/tpl/img/logo.png b/tpl/img/logo.png index f917857fb164763de174630ae68852136d6350b2..549b84665cdbab6f20a4661b52d55e933776662c 100644 GIT binary patch delta 417 zcmeBYKgO)n8Q|y6%O%Cdz`(%k>ERLtq#Zz*gBeK9-QM^HNErwCgt-3y|9_&NLA}16 z_%5IjV@Z%-FoVOh8)+a;lDE4HLkFv@2av;A;1OBOz`!jG!i)^F=12eq*-JcqUD@w) zu!v}i%(!a#7pRBF)5S3);_%zB(?ZP(JU$)=9xeXyKR$YS*lOK=hUDr*|DGM%YKoB! zZEdR)XLh=Uxjwnwz<#GbS8eV`Znb8+h2lC-lLh8#6iGCCf3EI16!K{@m)PVN+>h0} zRk|k3{qNtilG))Uw~nYmC&!Xxksb9#jP;K1zKdTGKFu2^E_uL{$0~htw1QgDz{f%v?*E$fsWaF+0uUOf3HuRN0$+`48=5D|N z57rl}mhHMORGIE>zI^%Z-QUkI{NFwAgUu{Hd8wmnj+JQ_rWnW6KGV3Y&@;7$Av}+7 zTS@4wH3bSqa~Azv7Q8IH^w)`Nd|nI0*87HJY?GEM`oOT^8{2{lm%1JS1B=1a)z4*} HQ$iB}hz+8< delta 878 zcmX@c+|TaU8Q|y6%O%Cdz`(%k>ERLtq#Hn(gBeJEc`)%OkdiEMjVKAuPb(=;EJ|f? zOvz75Rq)JBOiv9;O-!jQJeg_(RAe0B6XH5i$DscI|Nr9ueryH`36=!;1p~P#fI)Tb z%p?W|rmLPVjv*Dd-rRa!v_^r4;eo@Bl`CrhFSmU?Z{EyRD>z$Ht=j**$(g=h?67L% z`eTf;7Z7N_n1G1VD_u30rFL8&4x1b@bhG}^&tNl;{lEBUk$NrWI|a7Cwkz(cVrXaM zNUW7*D6G(HSXOR`Y+xwb_=LgD zv7xZWf#u|k8x4P@4eGP&-ppU{oavjffbqB6S>JiJg$q0wpE3m49C#=y!Vn?$U97a-hKc6G#0&=Zocd8$KcGi z|F~D*^X2Xr?frj(lcA9-LW^+%*9EfS+_ToVo&C<-xtddvq0~J^oAE^T zp@l*Xd$hkzWmxqjMTKF({GM~{%z_8J*juhGfBft_^YY$}EC>FrvR;&|C&Z9(&vdRh zyTN--{{96r3%K8RDQ~wv#5?N?L;bq^)BV4dY%Pwx(B7F+7^^?yyu03rQP!1ZVBYA2sy{*lvGXNldS3E^&LLA5UHuj-~Nttpxt674l>XMLTD-rXyCR>x0sgzcK0=e^_0 zlm(Ku>*n-%DLHPA%Bi$ny1o0}{r`WD+_BxXJ4?KJuGl*>?W6`>{Ucv{45T*inq6$Y zdT&?8{@JEsE>Dg3FE`&_?!aCGLtEJYhzlk#JPrCSy)5dDA}|jyc)I$ztaD0e0swQ7 Be2f49 diff --git a/tpl/img/messages/close.png b/tpl/img/messages/close.png deleted file mode 100644 index 731aa018f240a80ed89ed48e386f8a364089e99e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 662 zcmV;H0%`q;P)z@;j(q!3lK=n!AY({UO#lFTB>(_`g8%^e{{R4h=>PzA zFaQARU;qF*m;eA5Z<1fdMgRZ;3`s;mRCwBA{Qv(y0~)Xa;xHh#0%Ap|*nJ>A2E?m? z_z1FMfB<4dGJqe5n}Im-^XJbzXU?2qxO3+YSVC1*mBGZsgn@^L=L?WKABby#_#;RT zAb=P^1^_W15bpru|MTb1|F5g73o;c2gTz2`P_P54hXEjfU-TN zkO7lFe*E}fOG^tZsiULw|J%23{~tVf@Lxqm<-eGi*#Fa~PlK(jtgHm90jUM)0qF$@ zAdmrP_U+pTw@p}B`2V_f>%bar-@g6-^5x55_Q8V(|I5qE!_|TGfb;?c5XgYHvuDpn z@h1ld$Nw!`w!r-Q|J=EA|M~g(QPj+uH47y579fC_U@?IVczJmlgoK3Pd@e372B6DO z)Bpn;OalZEh8GkR6#ielcoA&#-o1POZ`!m8%zpIf(SI{DGYl^P1Q13b>g((O|MKO_ z|HFq5{}&V#{LjwL{(se~RsX+#|NcKLEDR$M0RjkXG=gG6R8$ns2YTthfq?;>4^2?u zXaoo#l!R1ORfRVZVM|B=0fds2|NZ;-f8M-#Sd%iyRUkQ#8swx55I|Tn6EFuEoyT++I zn$b9r%cFfhHe2K68PkBu*@^<$y+7xQ$wJ~;c5aBx$R=xq*41Wo zhwQus_VOgm0hughj}MhOvs#{>Vg09Y8WxjWUJY5YW zJ?&8eG!59Cz=|E%Ns@013KLWOLV)CObIIj_5{>{#k%TEAMs_GbdDV`x-iYsGH z#=Z{USAQA>NY(}X7=3{K8#$XgYMs^AIOw1Qr{*Wn)N-{9ma}x2(<~`9Go1=*>YR!KZvrBS zCd!u}@M0og%Ev@_;Z?Kk>Wwv=%h_57zmt2<_1msz_niYE=YRNPpd%02TK9oK1z z>ooPno}v^sikz_|1XHFx_L%~;ljh7i(jiay5F0x*+(9aXXFCl?AdQj5XlQ65%sEv+ ztfe?|YcjPN*@yYtE~ImQh{l|#A6Z8iu>pf43Rj52CzU_dMQm|S2xR62YjQOn+z8WH zaK=!}ggOZi{4pB7SQ=xC0n|vXP_Bkx_a)FeNd}w8U97BNbSWxa^QW-li9BZ#M1!_xE*?wzt^GcoeoL*JGLSe_+l-JT2#2tz!z&^ z_s5anq&^nBklIMwRvcoP3%qs%%Ea?1c{_*V*Xj&~uLu-2Dp1fUN4<0zMo$EH>*U83 zm_9;Vt%-bE{_J_!If!1y=c+`QVZ>0_BPy z+%^pgnv`f8H)Z%0&Tp8&u*MCIC4igNW5MeWM_DHpDNi)Zxz|9XboOnitwFq$ETN=X zj-tkCJnz**Y4k#6_Ty^B=hWo~L!47r`HoP=x&3T1)JLr2t2+#fHHs{AQG2a)rMyf zFQK~pm1x3+7!nu%-M`k}``c>^00{o_1pjWJUTfl8mg=3qGEl8H@}^@w`VUx0_$uy4 z2FhRqKX}xI*?Tv1DJd8z#F#0c%*~rM30HE1@2o5m~}ZyoWhqv>ql{V z1ZGE0lgcoK^lx+eqc*rAX1Ky;Xx3U%u#zG!m-;eD1Qsn@kf3|F9qz~|95=&g3(7!X zB}JAT>RU;a%vaNOGnJ%e1=K6eAh43c(QN8RQ6~GP%O}Jju$~Ld*%`mO1peOSYYtbpBV}~vsBnU!_?2tr-P=|^T zED%wc9ezHgW@NMb!^uT_|SvCpFLJylbx zY%bpaTGI8IYXMN$9w<3j9VkA~NYOKEQXsj?6a9_hcwfU$acAhJhB)zb_w@MVUEy@S zX&I>K-R!bhu3?(6bHWIg$HEl7{9g>>&l_qdd+UYb(1~BCo9LptNq&8>!yoJ3Ui(i5 zRJ|XnYBklL!{@$-7=3mJ>P@1c=7Oc79e-V7yf+%lD2!I;Y&nXBZ>=B!5?CB>LvEx6 znI%n)qqi$#X#wKB(U7XP2P=+4{b@j#r%9-K(8UqtSDk>0UKzf*HM9yqMZ1D!$2MdZ zR=`U>0zhOH1XqN?nY@AQqB7)Fp4{v&dKXvb43hZKvnN8;Po;+jY*}~*Z|W9Q0W%{D z^T}Cc<|r(Su=1K=P5>Z4 zg`et&Va}tdzBS-G-ZcO)zCWpJvGQwrHZ`@wpM420ac@bI5~KkTFfGEM3sPWO8co4^fI6lPnA)Y{ef%@{+SnoUk0+dW+*{8WvF8}}l07*qoM6N<$g7cXs A&j0`b diff --git a/tpl/view.twig b/tpl/view.twig index d2ec207..8ef5cd9 100644 --- a/tpl/view.twig +++ b/tpl/view.twig @@ -2,7 +2,7 @@ {% block title %}{% trans "home" %}{% endblock %} {% block content %} -
    +
    @@ -21,7 +21,7 @@
    - {{ content|striptags }} + {{ content | raw }}
    From 3208d538a750866221fa231d4230082eef90ca69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Mon, 5 Aug 2013 08:54:42 +0200 Subject: [PATCH 24/49] mysql support --- inc/poche/config.inc.php | 2 +- inc/store/mysql.class.php | 202 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 inc/store/mysql.class.php diff --git a/inc/poche/config.inc.php b/inc/poche/config.inc.php index 98a9ee6..67d0c88 100644 --- a/inc/poche/config.inc.php +++ b/inc/poche/config.inc.php @@ -20,7 +20,7 @@ define ('TPL', './tpl'); define ('LOCALE', './locale'); define ('CACHE', './cache'); define ('LANG', 'fr_FR.UTF8'); -$storage_type = 'sqlite'; # sqlite, file +$storage_type = 'sqlite'; # sqlite, mysql, (file, not yet) # /!\ Be careful if you change the lines below /!\ require_once './inc/poche/Tools.class.php'; diff --git a/inc/store/mysql.class.php b/inc/store/mysql.class.php new file mode 100644 index 0000000..78254a5 --- /dev/null +++ b/inc/store/mysql.class.php @@ -0,0 +1,202 @@ + + * @copyright 2013 + * @license http://www.wtfpl.net/ see COPYING file + */ + +class Mysql extends Store { + + public static $db_path = 'mysql:host=localhost;dbname=poche'; + public static $user = 'root'; + public static $password = 'root'; + var $handle; + + function __construct() { + parent::__construct(); + + $this->handle = new PDO(self::$db_path, self::$user, self::$password); + $this->handle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } + + private function getHandle() { + return $this->handle; + } + + public function isInstalled() { + // $sql = "SELECT name FROM sqlite_sequence WHERE name=?"; + // $query = $this->executeQuery($sql, array('config')); + // $hasConfig = $query->fetchAll(); + + // if (count($hasConfig) == 0) + // return FALSE; + + // if (!$this->getLogin() || !$this->getPassword()) + // return FALSE; + + return TRUE; + } + + public function install($login, $password) { + $this->getHandle()->exec('CREATE TABLE IF NOT EXISTS "config" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE , "name" VARCHAR UNIQUE, "value" BLOB)'); + + $this->handle->exec('CREATE TABLE IF NOT EXISTS "entries" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE , "title" VARCHAR, "url" VARCHAR UNIQUE , "is_read" INTEGER DEFAULT 0, "is_fav" INTEGER DEFAULT 0, "content" BLOB)'); + + if (!$this->getLogin()) { + $sql_login = 'INSERT INTO config ( name, value ) VALUES (?, ?)'; + $params_login = array('login', $login); + $query = $this->executeQuery($sql_login, $params_login); + } + + if (!$this->getPassword()) { + $sql_pass = 'INSERT INTO config ( name, value ) VALUES (?, ?)'; + $params_pass = array('password', $password); + $query = $this->executeQuery($sql_pass, $params_pass); + } + + return TRUE; + } + + public function getLogin() { + $sql = "SELECT value FROM config WHERE name=?"; + $query = $this->executeQuery($sql, array('login')); + $login = $query->fetchAll(); + + return isset($login[0]['value']) ? $login[0]['value'] : FALSE; + } + + public function getPassword() { + $sql = "SELECT value FROM config WHERE name=?"; + $query = $this->executeQuery($sql, array('password')); + $pass = $query->fetchAll(); + + return isset($pass[0]['value']) ? $pass[0]['value'] : FALSE; + } + + public function updatePassword($password) + { + $sql_update = "UPDATE config SET value=? WHERE name='password'"; + $params_update = array($password); + $query = $this->executeQuery($sql_update, $params_update); + } + + private function executeQuery($sql, $params) { + try + { + $query = $this->getHandle()->prepare($sql); + $query->execute($params); + return $query; + } + catch (Exception $e) + { + Tools::logm('execute query error : '.$e->getMessage()); + } + } + + public function retrieveAll() { + $sql = "SELECT * FROM entries ORDER BY id"; + $query = $this->executeQuery($sql, array()); + $entries = $query->fetchAll(); + + return $entries; + } + + public function retrieveOneById($id) { + parent::__construct(); + + $entry = NULL; + $sql = "SELECT * FROM entries WHERE id=?"; + $params = array(intval($id)); + $query = $this->executeQuery($sql, $params); + $entry = $query->fetchAll(); + + return $entry[0]; + } + + public function getEntriesByView($view) { + parent::__construct(); + + switch ($_SESSION['sort']) + { + case 'ia': + $order = 'ORDER BY id'; + break; + case 'id': + $order = 'ORDER BY id DESC'; + break; + case 'ta': + $order = 'ORDER BY lower(title)'; + break; + case 'td': + $order = 'ORDER BY lower(title) DESC'; + break; + default: + $order = 'ORDER BY id'; + break; + } + + switch ($view) + { + case 'archive': + $sql = "SELECT * FROM entries WHERE is_read=? " . $order; + $params = array(1); + break; + case 'fav' : + $sql = "SELECT * FROM entries WHERE is_fav=? " . $order; + $params = array(1); + break; + default: + $sql = "SELECT * FROM entries WHERE is_read=? " . $order; + $params = array(0); + break; + } + + $query = $this->executeQuery($sql, $params); + $entries = $query->fetchAll(); + + return $entries; + } + + public function add($url, $title, $content) { + parent::__construct(); + $sql_action = 'INSERT INTO entries ( url, title, content ) VALUES (?, ?, ?)'; + $params_action = array($url, $title, $content); + $query = $this->executeQuery($sql_action, $params_action); + return $query; + } + + public function deleteById($id) { + parent::__construct(); + $sql_action = "DELETE FROM entries WHERE id=?"; + $params_action = array($id); + $query = $this->executeQuery($sql_action, $params_action); + return $query; + } + + public function favoriteById($id) { + parent::__construct(); + $sql_action = "UPDATE entries SET is_fav = IF (is_fav, 0, 1)"; + $query = $this->executeQuery($sql_action, array()); + } + + public function archiveById($id) { + parent::__construct(); + $sql_action = "UPDATE entries SET is_read = IF (is_read, 0, 1)"; + $query = $this->executeQuery($sql_action, array()); + } + + public function getLastId() { + parent::__construct(); + return $this->getHandle()->lastInsertId(); + } + + public function updateContentById($id) { + parent::__construct(); + $sql_update = "UPDATE entries SET content=? WHERE id=?"; + $params_update = array($content, $id); + $query = $this->executeQuery($sql_update, $params_update); + } +} From a62788c61ef80e6b0f1cf0b6304b2dfd2223aa38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Mon, 5 Aug 2013 09:43:33 +0200 Subject: [PATCH 25/49] #100: welcome to you, instapaper users --- inc/poche/Poche.class.php | 22 ++++++++++++++++++++++ inc/poche/config.inc.php | 5 +++-- index.php | 1 - tpl/config.twig | 1 + tpl/login.twig | 6 +++--- tpl/view.twig | 1 + 6 files changed, 30 insertions(+), 6 deletions(-) diff --git a/inc/poche/Poche.class.php b/inc/poche/Poche.class.php index 5c3eda8..0d37e3c 100644 --- a/inc/poche/Poche.class.php +++ b/inc/poche/Poche.class.php @@ -212,6 +212,28 @@ class Poche private function importFromInstapaper() { + $html = new simple_html_dom(); + $html->load_file('./instapaper-export.html'); + + $read = 0; + $errors = array(); + foreach($html->find('ol') as $ul) + { + foreach($ul->find('li') as $li) + { + $a = $li->find('a'); + $url = new Url(base64_encode($a[0]->href)); + $this->action('add', $url); + if ($read == '1') { + $last_id = $this->store->getLastId(); + $this->store->archiveById($last_id); + } + } + # Instapaper génère un fichier HTML avec deux
      + # Le premier concerne les éléments non lus + # Le second concerne les éléments archivés + $read = 1; + } Tools::logm('import from instapaper completed'); Tools::redirect(); } diff --git a/inc/poche/config.inc.php b/inc/poche/config.inc.php index 67d0c88..a16098d 100644 --- a/inc/poche/config.inc.php +++ b/inc/poche/config.inc.php @@ -8,12 +8,13 @@ * @license http://www.wtfpl.net/ see COPYING file */ -define ('POCHE_VERSION', '0.4'); -define ('MODE_DEMO', FALSE); +define ('POCHE_VERSION', '1.0-alpha'); +define ('MODE_DEMO', TRUE); define ('DEBUG_POCHE', FALSE); define ('CONVERT_LINKS_FOOTNOTES', FALSE); define ('REVERT_FORCED_PARAGRAPH_ELEMENTS', FALSE); define ('DOWNLOAD_PICTURES', FALSE); +define ('SHARE_TWITTER', TRUE); define ('SALT', '464v54gLLw928uz4zUBqkRJeiPY68zCX'); define ('ABS_PATH', 'assets/'); define ('TPL', './tpl'); diff --git a/index.php b/index.php index 94c72a2..294620d 100644 --- a/index.php +++ b/index.php @@ -49,7 +49,6 @@ $tpl_vars = array( 'referer' => $referer, 'view' => $view, 'poche_url' => Tools::getPocheUrl(), - 'demo' => MODE_DEMO, 'title' => _('poche, a read it later open source system'), 'token' => Session::getToken(), ); diff --git a/tpl/config.twig b/tpl/config.twig index c18806b..dc49ee3 100644 --- a/tpl/config.twig +++ b/tpl/config.twig @@ -42,6 +42,7 @@

      {% trans "Export your poche datas" %}

      diff --git a/tpl/login.twig b/tpl/login.twig index c302879..b24674e 100644 --- a/tpl/login.twig +++ b/tpl/login.twig @@ -5,15 +5,15 @@

      {% trans "login to your poche" %}

      - {% if demo == 1 %}

      {% trans "you are in demo mode, some features may be disabled." %}

      {% endif %} + {% if constant('MODE_DEMO') == 1 %}

      {% trans "you are in demo mode, some features may be disabled." %}

      {% endif %}
      - +
      - +
      diff --git a/tpl/view.twig b/tpl/view.twig index 8ef5cd9..bf9a9af 100644 --- a/tpl/view.twig +++ b/tpl/view.twig @@ -8,6 +8,7 @@
        + {% if constant('SHARE_TWITTER') == 1 %}
      • {% endif %}
      • {% trans "dark" %}
      • From a12832488d7da2fc7caffae3b5a6b06a02912c67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Mon, 5 Aug 2013 10:03:59 +0200 Subject: [PATCH 26/49] update poche.sqlite --- db/poche.sqlite | Bin 294912 -> 294912 bytes inc/poche/config.inc.php | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/db/poche.sqlite b/db/poche.sqlite index 2aee61f4b087d427b0e0f6ac0ad0c790b433d2dd..45add0d76750821603c167ba00de75bb7bd4abe3 100755 GIT binary patch delta 985 zcmb7DK~EDw6rSB8?N(8Q1k!LBmje@Y+mw2Ri9?w(RVJBJH#q zj~WO!55%j-#tV0@%5N}UNVu68E)e2ury9NKWZzEqz3+YBd-G+GZ z78a%myLk|QFmeedzm4_r6bRQVpz*bS&A;%YdWBc{e7p61Z>rTF4iCp-O6#SEN;Wnf zx#^-ixsE5Bcln7WPk;t2cM~>-NG4+r~911NAYmQmQa8mAN z&#TQC#?H=8f>5uFJHdqMRE@bB+QiKGD?M$IZAh$H-6WEa>CnZXA1wp&tzav9gV7?*d2Knra)8)jcDT3T!NV-IR*SwVoPfFAppq`(f)7L zfhuSMz>oQ{WLi;`!a-|V892zswBWCc2-9Tjt$HgE?j&6B3+a5P4tAWN=XdO!pUc=Q Ob#N$1__YE)0Dc0Wtv=8I literal 294912 zcmeFaTZ~=Vedfn@%XYI8`6fF`U<|xfvQ5(Fsp6%tB%3{KHd{@JH}N7_V@!lor*>5x z^3*v;=Tc86AUFyqI>^AaEvH1rUQ4xj-71A${C4_(R20F&e?PYDp{DNjK#G4hbh zOn%?@Uu*AEm!?{>9AMx@bdy!*?7j9{-~WDDYv23&jsC2zHb#@}?yNe0?7PQadg<8T zt*T?kj{OV%KhFR8&k{d;F8{*6FD?B1X+QppW1Sy;>GRLd9sAse|MJ*J|I3H}^4ULs z_Md#(#pUyYKtZ4&P!K2x6a)$a1%ZMehoBH>&Evt?OTZaIHFBEzkP1LA_jk zw5KJn3^k z&tE_H?Eln5k3ajr;|Kjx{uBfX0tJDBKtZ4&P!K2x6a)$a1%ZMhE9q75)9o zzpTH1>6bpwN?&^Re;nh#@~0qB5GV)~1PTHLfr3Cmpde5XCDaFMja3?|mU(y3-%_c)|S2!_j;?tyj2c^h5GX=*cHYyX|g&HmSEq zD;ty5@u1ru&g#`Kti0UK->-MK`}J^TJX)&aX1U-1kl`-rHNRXT6wBZ>#Q%0kOj(n18RHbo#^TtUKf5Z`MK}?ojm4fy3-x3cc&};;l^lnQcuUD;k3U~cOHIc zUQc#A+uiAC$iE{-)Ks}*)|t+Bp+dm=OX7e@)rC&|YA_mZnmDH`(B6iE)!v_Wt6^PD zPwYLP0eA%otLgl7rI~+cKH{EkwNZ1euc|9iVsn_TJY5Y>ba(P?lWuP`ob5f||7mwT zt_OS1t3f@jHU|CPwEEF^|D=Mr)9OZdKB-|OW&>^NYJ2a`cuqX+JM$C$0W;})`1N`` z=Z5`Dm_m-~Vb?=-cSik5HGv_j9cJ&Z7@{5wm^f?O_5;$zENOexAFfYgIl$&Q)w7C6 z^5twYn#W`Jo?^9M;4>$O6db;ZF z)V*pxWU-TeC93cJ>72XwelV>jUARK{uLGGLI}IFXNJ6!qp$HHlPxHt`Awf8p_pAEJ zc+mgO915Hkq^xAfqtp5W&fRJZ$Xe@Wcf!aQpaheKRVMt~G6HkZ-8?{)nh*JF+yzqT zH=4{~peOu~K(a1at{QC*`{T*p^Jx%J>)zRWKIw1td)-+dCIvEaJ5!o!L0Z^p553bL z4RjOZZ|zQ*y@p&9<)(Xo$%KNQ!LJ&%xmUpu3JloCf!udNTDh zpkQCu`Ptju;W|GceEl>bU0bb<=Dh*8F}a8H>8zQ#KG*<XU^tjbQ}OhwE%4U_=d5AYA2Qwfc7N{&`m8(P z=e_4zE5OB?(6tk-$&zX&8H@n0+M0{Y2ls2l_kKs7D`?Mu7(Vz*YK5v*B`hd&mdfc@K7i4g1qt z6VosCaC2VIps9F({}3NTs(tDp6LIo2V?ULbjA!4Tg=01I}n+uM>Z6b}ZyO~8EviyVv%$bgSw03ZYanrveZ>bXgP z<{6_eYIKLFFXoU&Wl)sP-t+O+$V6+nRq9C67|+~5Pw~q-+R4nRxq`Hx11S;MSNtgi)4BrrfQ zLyZ8;=5T8wbx20sFcXa#r+%{cyf>FVlTH~#MA~vY!;tuZc>MQzl@E{!i|^O~3wi5TJ0%(2B&P!PoEaf}0sspi){zTP%#Ll&ne>`P2W+pJg` zZz911DC6Gd8Ct=bL;Gv6BNhy^1#g+eF;koVjnNRr42urBpu!xiw?#ipJ1ekbG_4_3Lw>1L znePP>Tpu(Zl6{jgQiMG)^%|F}yEbM1l6LX;aD;6{AHX47lP((5&*FIt%FMhuIDt1f z0Y28fE#G@0Cq-!O$YkJK_3Nhy?O#^gCoF1{K|NWv!Iwv{NSy4&1yLLZScgd`GiA6v zWCYgO2$G2vP+7fR7ZlA*WAv4I`l47k^x=3kh1;a>ZIna_`3`8U?p&r<@UpV?Ab4gK zjPF!z(P-ghwH6phUh6^TL3iNvOXbF76JBVo=?Fz>p$#j}&~UbFm1d+F$jC4m(8iy! zR+HM*+C&w>Bll8^mLgf}5?l0PG*sEgz8|`=Rt8+4{kQXVg|Q%=Y>E93>#II@-+{+Qa5ca%u$h{R zgrQ|n-mbJ4Z~?fyZ~l|3JL&Zy_!OSSr}w8jPJRT(nrKutX9awrMA^#Qkk87 z`UR4pE+dYBn}#QL4Ug=a=n;l*d+!I~tn!Hj)ADN#H*FN;t?q0KLW1S2y2o45NfVS28DV|lP~@5wv3 zP%U4ow=Z9Xm1c_LPFFYkaLXXErlZeYG@q3hYgnniF_Jkq(XtlB`6pr{T;R#4tB0ap z*T#Dtd8sAF%a>Mp${~rBj-6rk;Xg0l@q;L(pkeP%JW`mbyb61p(}t%T|TA$y}OW zvha$*ryZgKGJ8nq=(fTCk$Oo}LlQgQQ5o;|K{tzT{+wu6zTFFo5l3#VJgpg)<+$FkDF(lR4!sLn3IzRZpIS{W;8 z5~BS%T*AlVkZHHaRITjRd^waxMOoOS%uS{ap2DK;#%@n)OdXK7;(xl@>565;$-qlZ z3}AXQU@`)PJ@Z}!_lZ@%Q4i~<$!lQ<+Xw^EEOJ7Ml{5OSq`ybvQGWUW>tc_-}sXL9jrn9-=033nP7JDt)gggA4@V7k34OUD2||;vTi_X>6W@hKS(r!YAe&IZ93jAh z%dy`#K!1Z@ci{ZO40Ar;kUe8X;vzDiy+_ypTPwT<1q$%QbT(5Ju56KIwfb- zHs@0N%1l*q*12Lb30*R%MNN@y!*H5xVl~o_#AY(51V1uV8W!(BE-W*Wqq^RI%IAI_ za1kNw29%eJvN$g# z$n~@wjWN~yQLn$#7rDsRFfa3<0stag$IG&xqF!2thlaDUYJ_Ws0e7yo^rT1@9G>h5 znNItxY1JDfVxx`v_ zk^si)l4&~suNj3A-;s+H@!&ppijVUpo=2=BISh;j4=IDHOjbt;Na80k8wL;S5()$w zAp>Z8=9C0Qo=TPwb9xu+CQEJsK!)&Z%smZ5q6^6iNaNdhumA;lbH#KDm~2SkY?Lf( z1!*}DmU8l}N_!fDt!|2!9j|c}L?_1CdoG?>wqk%_N?S6Deh9-yCQz!E6qICHG}XvX zOQ4*PAPjD|$Mowd*rE1zJjv5|x7Z4v0s>)5T?=raBB+B^LOa^o5r?|aVN(T*ZWHY# z_fa9v%E~**Xf+C6OivC?=}h2la&_itd*C_oBXqflmD~A4g~}1ta?D2hAsk!Gf-OVj zDP)K2>YxcGHKx%KshE^+;)XErl-{aq8blsAtaPHgHXh$Bhz3VVtZEWd4~x+%GYKEQ`TcLGJ83Hm5GBnt5X zQ}gdRfz2;uPjEjYs#ieI)ia7(c1J^ZB2}nI*UtPYXu~yYkz=W_g!XoMZZ~2_VgY_3 z$W$hG;!HPepb!{N3Uj6TaAV9h6K5F@pwsFd>9WJ4!FfdGF_f}{+!?Hw&3Wd)a9L80U_u-- zgk$X1o7JRcn{S$Hj`%`!g3@3SjvdoA zCog`${2M(h;!;Bl<%APOc3RGTnH zA7Gt>75m$NmQy&~>26^))+R_!P@(aNZtQ*BSreVCso`^7FFn)BvCA*E1StQ&R%!}| za1HNqtGm^$y4!1ggvhoqT&`q(mEf=%%?3fjWIvG+Ad6#fkq?no`oq4t1dBoA0qwP%48ql(bC(#0{Aux-8Cc|6U972))G{^3dkVH+&##89vNw;1_fS`?v!B;dP_y< zcI$hs8MUmONOrW+4e4U|LID!`?|t04cSoZ~hHp1rWwKVwv^!ugWh1kBF_|M=(#Lqp zNfl z`R;suN+i*sORR^BhQ!K8p7!>B0QWQK297gu1q=J5^=&E;M8c0n&@d-l4 z`fz|-S1&0O(O=qu8Ni|TTE(VL^hvSML9l6_^5Cs7D22r&vK&O@kjSOkWC|a;pb0J& z?QL~8U}IPXf}&MW2Ix|pBy@8$Z}63~15GzYMU+aeNqI;$Yh-yBAl#jJxy#a-T}s7( zBZbwNo5NLKm{QZGuYHLHxA&&=dtXYfZL626a7A-kL`0TTuK!gHj>KLw6%w-3v5ZNW z0V`9~xMJoD=1SKSn6sL4#P+h=azku6&2YL0VH!@IoVQ@ly66>$0&7T9R>R!p*sZZ! z51@BM;g~AoQc29#Tuf;*2IH!zi1XD%y4mAta>o{->*#Nl)TJWq6{CH?+KdvS%B(|= zhL6NBI6Sg=xH^gv!du1VbPE-H5yc?&N->*I63TmJKklZ4Rxa<9G&}e6lx*FyyAyu( zGIO(!=~VD?^7e-(M)NgP-Xuw9YHO0xq_9S!S&1*Au29CAw&`ZL)&88-ghitxj%d444K3R(iJD+=TrJO&FL!FhUBbQC5(NFD#*9{r ztZo8)yGzi-^{NcXUNBNv8j)8{?e+*^g7Q)5m$K#q4L1UkO&4-QycQ%U4`e3=Gn2Aj z`c)Z1`UZe~izgE}QO$O<1w(57$}Vc^9%ABl5pfr4jgUY+JicpU>GA*Cm=DR zglWnMk!hQdI`sr|r6-hdq@<(Lqu?9o5__{v#+OH#m%_(AaI^Fq1kjTru2Bj)>z$pE z7?2RgvEC@HYw{%#I_LZCv)GhGR#`)N zwT;eaS$LzuG$*9gVboYaVIs4D2wJr|w61Nj6|Y!ofb^b2wEHd=Lt zu@;0Wr|k?9_$XdYJ%K6dhF*h~5D|e_KqX&D=24Zv<$L#U-&v_bslR{y)^%doYt_9g z*Y8#LZ$G#X^ozO|jk-Z*8AjKH;BYF_1D>XEDGy@#kngjYvcW`r0~d`~hH8cQi$@wn0=G;*Qu2!9b%r);9YpOVW?pW5zAGtq0Nu1yBbm2w3rZEa`x<)Ct@tI3s*+>18Vatnjrys(9T3yom_rRG-{K|Y$mv2ofJ{Z@1nhU zK}RU{L7XIR7XY4}60d*eF3RDa>l_{B3V| z+()QUpchCSnPGKHB=Qw4Hp$KR;r8C6>IU9r8`@ghq$lJU>uxJ49Cy0uAX*ECL^(O) zB(VE(f2=N;MWi<)u*cnmVccZ08w!=%_9e_6OrWrGnt?7rgaHMpmeFubC>%DP-(>(T8JsQW0;iw3%0j@kG4qoZbkJ+T;9|N<#J!5 zO~g2I1;w8ZTqXOMAP4!e@SMuz`5^#OUt2k&>_jh`nY;lCS-P~NxGzO5;gQRSluzU8 z4$`_o(A6U!WM`-CHR3knF_9(=HFN5OAnJ*&=;OWjvx_2kBYHn_2dup~>(`Aw9dD8b zn^CP$X5}^xo{LU6ifZ1EjDz%{ke27AM}?%zACxCX9zx+Bn+~j4J0fVCyN{7^##Mub z#9yD|a55JZMHjZ1Imsw@$Zg??$6BJwNl-- zB}3IKp|nMfo6PA#_7J^tbeD#z0OXw3Pf%~)N?`sR+!PSFrm>GMd|GV=KHRlk*?X%Y$D%8=8Teq%jZsMIh`Uy zm-=uoL|TykZ!ukt*t=*nk0BkL(sRr86UFZmbI(OQBbH@@&$NSfn0iOgu;x;r$mY@vWR(DrrU{x_!WYW=={|9>u}ZDZviA zGUR9Gu)_)B#>ouK-%B%>e#M)$Q==^wW*i$lqF6ZEB?;lSVAwH4X(dsgZMYJ*|i#)&nV@!y|Px+i<<5Smf)oJ`{kwbfpUO&MtxwLWPsHeE+N6A-4(C$I01WTi|W}DsD z$8^R^qmO7()0gVOrgEHI)ST334iF(c3bf9Gmlb6TCL4%yUofJaE^(o!;%@Yswh4Hn zHB&c|d4!h`4plXnu0yVPkW{1_wIr~6&-aPrvYDr)$-@(rNu0nA%@?Z8A$)iPi-_6g znN^-5b|lCb*^PqPAe0Uv^4<@j*ezj)s>y0JRkN);nPb#4QIHZXt-;li*h2$jszKA8 z2`nzBZxDCi9qR;j-O4}G8h#YN&3CzqtW0x_VDcp^)!sk!E<;38cZ9D6SN39Z~+X!(1F8N$Lun-FLDTN9ZDYRM3 z-K?nIXj7Eb&fHsXUKa5t?3nW&TX~^FhF1=O*^kw(rO%vwhAl{-Nq4%)uYJXKG~pRS zsy0j5x_xb2B=YCwHCfx9)j!z_+ulXC+z)p@@?8Qcg{+Icv6sl1gYH zl3+BixXoEfS{O-}x+g*IGQX!Nlt;pqOkYx^m|c*7i=0n}^7BHVly8&DY?K*1264$DvKP40qJf&?c;DJon!YX*}O zylao66#^7z-@KBNyLp$`{uVVz9p*fo2<4@?XF3wDJdwyD_MBL=?dCRVBs?iQRxfck z=W0%%AUmI`3iJh19%#(^9lK;s_{b+COwbOuCUxZQYFuW=Wr$!eDov$I7@m2C!6_KOn6VG5wxoeXq1@h#e$wfjx z)sgjG5*Xvb)MMprfKr}D$!*!{Tr>0cd+Rw%KgUZ%r{%>$h4e z``X|;Tt>I2ygr|X4ix10Io8MeFt)515M7`+OLZXrBaUAqgi#9V2EXNzva{@FxYipa zI@aa^S!KpnIXSvR7i^G?wps5=57h9z@p*)3j)Ips@RM|xB zb5<<^o62trL2#Fpdt2b@WRZ~ju)eSkqmnkW*0WmIQ)tcbFog_~y}Z zmO8#^P$Fy$x0jR(?9Ck^wD7R{#rXC>GC6UG(@>tPRl59ooLi+w5?JgPl4}g6x!fjp zwH`p;6mQsQ$QnjQ*9y}0Yy<{C*G9wC7MeXx5R+J$$XZvUc8&B`Wi3^Oh=IRU-A`2s zwXREVN%t{8ShSQ2kO2o@QdJfvK^WxWT2)hO_G?{n;;PLPhR1mXQc1UGb$Ubd)s8>h zAb1Wto@={!A#hwYpGQp{!kgC0yWwTG*%nwc`gPqI0QYhy>R;y_0j*58yfJ0PB`I>3 z7u=25RLZA}`4mi1a4;=e-0H~=1A8WjFs(Ym^$2@n92fGI5$l6vYIp!f6c0}xMx|G} zO%+(z;8=ES5rP{+m5YK+iBxM9{WVqDwD5{0MqBgnoAJ?{M8eR0A0@=t>NQ#Plo-RA zQH?=d1r6O5f;|;C=~6eZsB9xp)Mn2Q9RVU5FJGhEhoG4h{xXXI6-|fue%Ga&WnU!! zVWCM1mK4)LA$pgUm2|)kPova=%=YygYig#vO-R0v*Y$Kh6syt&$YWJ)@gyY)@;*aa zOc%v12I^0GoJ-xHv(^U0J>o+rHX>FH>5iafP|X8 zZJbS|XWmp*2;5?pat5ljwYlGy-dxI;r569NvSXn zCsK*C2>nKQ_NimS$}n+lRA_{~d>eD&R#zwK==;{Wb6r2x0r)s=(UOh#()Hz{V7C4hO*B4LKs{l>Kie;`0) zC4G}x8B_AcIT3NcoG!V7jD&L+f@85(S}Am7v{8xTEd7G}I6NZW*BZw;M8`?!SwBr( zG2HB*2YcT~4d8S@$duM%*lde@N|j@;aEY3Z7ztC_*{T z0G4tlVi08}QJ+b9^HUdRRXRrPvdK@BdZi23J6EB&mY;T=7*OSI@x>%MR!z1Ney*lY zTCDjP^Ag4PSP8Z!EjPKa=sg>@NSMOmJXxM!eE|a;3_(rbhatL9|#?1eS`dk)N8sauDFLMP(R54rEHp0E;A6jKs}4 zbi__Xn%u&Y_c!-kvsrHTg~@kj?2u-;a<*-@Inn{i^^i4TX*rrD47i@$Uk>Y|_r&5B za*93US9NUgllC#WnUK%qhv@-^F}Y4Lev>Y+nsf#cKJd!I9|6p*ek|4ASpAP98TCqzJp!1 zlz8;!qQ%dhT7X(jfn02i-?@d2lzx(Wz)VpCk*woeBx7oiSL?**cCcd2FxXXNpodY3*&H_@C8QKk(L$Zp*kbht>$ezoKSTh<3Xu&~*V`%xG zKl<)}nj%x0X`#P$H(t5KJuCf&axci$-bolU%@nof{3b(U<0Drl-xfZas>>20Fg;kH zyS|Nkr~}WS278!gLc%a<`<1#HqV0VCGo|LFJ)Kgqvt>WKhqACfSR z91sdSM+DOPSDSr!3Z!)Or_9wSAICN7xp-$tXue;u6z&cVBJ^84E&|EnAX1u`=(_+?J;f>3 zB-;6rq#)CIHt8ZXi-}{-g5b>3PjCjz!vMWV5bNa%iM1?19PokJWB%HPn6y<2xh`l} zSp<*ijC!!S&81F9MN>fPWDrDcq}&F6?}xNPhHe%S^|>sh^j>2ml_qN%$Mhscb7~k$ zwS9}Mnvv5oMP%pGy(9z!QEzsPHW*|@5CS7PkgM6WowbKFS`DVqP`-NuIhusif|!Nd z44An+Hi!`}fE9qVLqVZ*IgeS~jZA9Fe4vNBd0nfg3DH#)78;Q;P7PNXc3=h#T0z^S zKc{oxb2Tu?a){32Rw%MeU7|;nK1Cn#>w7eYDLIE5D!uhjk(x**!ilpwn$nr!D5(=! z5!o|02uU-A!>u_=Mx96xk7zn4$EPPueHu_x9y8O!!B&mZmh!3;ld#Zbl6E#!!RhMo z41r4OuTd%vlBK`H3kbPQUUgI{6#p%V9RQ#$L4Zk$9*xkZ`eZmcC_eCX8XCvezXXSa z9gd9SLR2$CAr`h+h)vnz189f?#t5o~CnpU>EQjeYF=CwK%zLRvU2YjgAOW=V;o$jBja6Hjp`E20VyHASe8g`eB?4Acj{b z!FT5JFhqHHTDntwAc=q~(p==TIYL==(sZI`Y~$3kH;@_@&Qk5sh^Fbd81RuGIme2C z#Zf1G&-p_KZ36c+j10VZQxXb`Gp+ zduD!t+ggO&rkP?9nm6bX@3s7H_v@Bpw#52w6+T_Pd-wWRuDo~s+TH5v2iM*gitbgE zI;y_@;L6?m0`Lz`+tJJxPlH{-CA6F;s(qC{QT8K?FbpBj4980Ti!J4W2DH6Db|xOt zVPQKE{qXqLtN`WZnZF~^Q1$wqw38juN1q8C49RdB5dlXHDv#Q%D(~JSqo5O`QP+xC zZFwmvQSmvnqI(bSR`+h-s;<9x?biM4_xJwz&=t$>&r`8QUftbmx4!cCt~3?hqt^L{ zeE9bd4WkF$xOT64pY28Wj+hAPd4|W0elB}%4z}+LnC=j@@?iZq$=FgGCb>^qF6Twl z$BC9Z060VPrI<_I-O>WC0iHYKHd?3jn+#@JSL_IQPkbol5f)$a>u``XyZjbq8KOYr zsEuFMdYJ4dp2-f~I+RD0uvBjxA+iNf(%#ru2j1~9G_=4ieqD{HyA+o=yHopC2~Vym zvXgRb9xmtC;*j!D*hHqmY1$U~*tCT&n&uGO$7IoXjH0pDt>6K2C!>SG?l^>?QDw~G zv^-GOIkpdr$J2IPiU?>DeT82ity}bMtcQwE4Wav#<<(DdFcfn}-O;L11t-7|`sp~c z6wS3BQBzin_GHZg@7;Tr;CJkX6ue>$9iKjw#2n^~{j-rNki zQKmYV0!~upNTx@pEU=N8A8a~+Z9*$F_EL_dq8Yk)V;GPVv6EXO8s>wr14olp-m$Eq zZoyY)_=a7Y%XQ>NGus)py|mXBb*a?E>t^jXwx*P)-E459++{9ejtq}et0}^@n;f33 ze5jr&)@YxoDJyZdZ<&W=?CTG%-MfGN_N~?GigE`QSul@I)w~(fAcC-2*c{kC#%4x0 zhy@QfP~P49<2$!+-MhBxM#@8vzXKfrnp_P61?k12+wwH2I zO4GRygIA`_-fGKR<|ij~u*l&Ow{6-o$&qht4?qwIk^&Hb({L)$&OIgZa$+rMq9pl; z!4L$gK%nZGJP_+;?(^X}DG2ITXDtVZ2WZi}nzHBZ{K=okh)tgKMG#(Bx`;#-2bpTG zq_N1g_fS9h`Z%jPl246|csEU7i&Z-6wrlRun&1+bCUWC~;AsOtv!W{Juu_x2vV#wW zqQi&YV9KJ}z7W|5&aZEB9QJ~hs8aewX%jKL`u4E0Jcx2=Y*svAJzK1tz{>rTaB3K$ zEb2DMPZdi~vcfWLRzKF`fP;LiN=V41 zB6=j-A-4Jk(NNl+DkwR=k!(GR`9zfYmaV^3Ak{8v9l)x`v1H^A)rSi383%B-3?lYU zZ4jtImV%oKgAsgp=WOVNhXfAEnMk$8f)zI>K68;h`Gzd~*MhiErE3sqbsj zvd@ipqr_OuJ{_u}MWZ_HLYQrI`cA`LvSQ+?U?^Nu1C?^&YB{%9TkSgi>D+_DbQ~px zX0k8Zz)Ul9N|$Z>GX~8KxigFq{3*UQ7q>RButlafi5&NvrKPhJRptlsa!1z z`Ivg8Gh^*}qgs{)Y2swZv}qxL5m`Uj`*Y5x68A^!VvQw1Tkk*V=q$+mVgRSts3Pto zMw5)t9eOg#U45|BU}P3~I>GAExWUiw1IqNt0mBMn7l&+H^&VvQ7$MPCHd!lP!l zBbUX2;pD|hVniE>v+vfosnjKLWtmD$Ks%?V#pe=ZXJ_;wFT_a)n0hab=aGcT0pCL3 zk@TvR;@W8MhoKbnfz~cXL{GBqxR!6|;60HJ?X-e@2dZ5`Kq$j!9U8)@ z!bB==z=cn(R#{~7x^##!I)UtToQ18(2-Uz@e8cMYov)gF6y~gjo4)r&llcvx;X`YX**N#L{qXW zVtS=&a*)o@vi(L_y2-zzuuw0Hg$Z0jVmEjQ#ey~PRoFQ0h*Rupr9%+ecxc;6eJF8{ z19|<%?XNtzmalDjS=Gn}0_i=#k$c8@EwoO{BM$tOYuBu0Jk2(3{zALz;92#5r$Vh* zJ{;$0aa}lEv`dCQfMhxLf}^ zd+CJaz$&;>)-hbKjd?!=iyaz7#!lzK7-Y8*QANyg8hQkH3Is8Xs3;%a1am09ZGlsS z=$pdr;poDr_{r!h3J`sAsmX6dUn;PdPOJe~vTDDkSrq)O3z#AL1txz;7a0~<*(A#J zPR&@Ehr?rpBk?Ug3l{YgGJQB9vVFopX^}i>yK3sg!*qpCrezg>-`)WJ`7lix@087Y zoRcdpWf^I)mgHywirsuZI^57Q;0?x#d<3ejG0_WNSRx&>Q^GF+gXb{?RCmIiu_RT}BBxY?;zUjx+?D9czUEXl^9v4;|0Qc0dmZ^W%`fTj&_e3CK;$?e zC$=+3OoPNpvXBq$$xgy#mXnk&bn7?DQF-RST#Q1j^F(=aG~k~VAAF1;89ES_QxI86 z^rhrMbCeR2>U0|cJ3jd+g`W#NAI)mP=F((go~`%WL=S$01HGXi7^2KjKH?xdpwi>H zmoSmXT}j&L=W~iVpapM71*=XZc3@O{x^WbMGpiuwTFpX44XC-k9J5lyfbQo~wTieD zY_Q$zDU@x;}bF)%>G31nH=r zeapp%LE$k8NseSSa*{K)JE#k`)qS}juIK<}MiVuKLYQ=c#v%>oF&!b00$}@*O<>ES zqmdGFWUuMZQcef&z8X{Nha@k2#Jur%H(| zYz~e)7Oo@36P7q?#pnu%29i2$Uv@O_7V!Q|ED_FBl&mnIY5yEUP&0+NN)0cl$ZOdp zz$K#^C(KnUXZQvi2tsFC%-E-hO>=q?{&I@XwoL*-rV)*2Q^>dz71ms(Mi6;dpLDi- z<)*vNh@!nonR!Lc!7G?zv7u937H0#WIWTr?G8or5A~fU4o#1Q;ycyx4U;*t9`yu}D zsvf0Q`KVUVg`lN!wol4KGL%Y@z=jKuX3G%PZEW!K@ma8ty(IGmmnp814I}~1G|;oM zU>Gi#7iDxPrOO=%zaqZi1_Xw{*TL+Fh>6}sg_68qkiI^~ORljP(XB+v(&`0_T(&?y zyXuxKK2H;bDP1#>>!@J^fGw;euYzP3sE`GYM&+WR%rFL;ACVUAvVflQJi|RyKLD(R zspE6iJ!u<)W=R8MGqgEppVTMB<6vxwRL(s#ge7LQ3t_8G%V@j>X-fZS?%zboCmrs& zQS9dVp?cg*E58d>2=r3jv1%xrqaZ6-HVS0TWOj)oXrQfsNR_Hx?jdCk*`TmgYqW>k zdYhh&N8HLw%20y4O`qHw?nKN>R-l`7EFNjjFXj1`U~dA)x(U^u%P#o5{Jq2V3`pl(YGh(fago}y-Hv)v|b6Z>j~@~~6qnE0U2J~ymsoS&!gptqG?E2t0# zrbT9(op^aSh*iglh0VJ#uoR9a791+w!szLx3U+PT%r~R1(j9N%YFNUXRE)Km*egM= z*aFoANxYX|njXDm2TZ~NalOyiZl{23Vh=^O1Tc;%+z)9{%AV?Qz2m)FQb%fu$m6T& z2K@tTi5SvVJTB#NCk?N)(d7e_68c5zWm_pjSFA-TQg`rO94TfWQSMd_qq0buj@a8^ zYJSU3wUnL|vYA3_ly~Kgh42gdpRg{*A`)p0FfKCZ`6*cC><2_WGmF!?xVbP$Z5N=b<5q z(R|>yz-9Lq5=^8^5`-sE=eF0<<5bHSf_h<4S`j*zBTVB+k=DOC9ug6OKlk-$?Cu!H7*|&c=N+} z0H12QjER2`uE>JP&?f0-_SXmSA&azL!$F7u^bkv;y78lGGD1Wahd4%-6wBvao<-+L zhvJgD?~Y=i27V+W`eYnNi3b!c2YW~MZ;^GI{XWRm!kz?|X1ujeVvmks^=XPt&Kfpi zHtRYW41??wLc8-#(OJJWTT3$y7oVD~k;YEm0u)!xrsqDc0U|+v+K}!a?p^s|EAYS>W=XAxIT1WqwCH6!D0+z>XGk|*kJ>CKoCwAok%8>9Y(nB&e!gB<SGaW9t|^g>OfLKjzmO0)mK=Fg@DCTW$Ew zdHHzd1VmOd7jJO&YnapP3=SqD?~q6nY|g_fS@~3oKoUP_g)EMej#nOqov5Ki8?pfA{JioIBLTE8J5ivmuMxv3{1wA=*1pDx%oRm!yZS1Fhtsap zC#GXMN~Q1}`2#1>5;O_>CjJ`mefCu03|_#s*>2EN*Awv$-e3|^#GDQU{4tJTZG3-) zmF(i9+Y0VmI)424rB&jV%b&m1KlbyVuV4D+iO<((|9Lh2w|@Ir`BM-m2owYg0tJDB zKtZ4&P!K2x6a)$aKP>|P!KGh5e(b~VoqFY^k87V$N4m*L2U30N8X-!%t{nh$k12;h zB>IRTkpJ%1oF?g#?C#cVHomaB`uOqVmEL+i>#b}|Ry|fnBXydQI_iRnk@!Xfb@-3- zHuFF$TeI!Kzq&;5N95Uj`u6hqGb^vYdG?Lh&c1n$f8Sib>>zP`Ig%4U`tEr&xbBsNY^DEw0B_#M9c{$eFN2<=n-K zHd4>LQogi5^k}H{OX2Qqb?F3G2~yRO4CW@fk?R~Q7lW(!kr_M zW5B^oSCvH6M)df_Rcqg$(qm-UXbgo);tkck)$y@D(m|I>$Wwg%5>Qt%q-Au^6iIzhY4qkcJr(`(9Z!kFTx5QoHQd?o@8`lOl~YGjUu z+r13<-~EE-6o%IwhX^x8xo!4mTk|!OdD!obv_m>{-16o4Dr8G*KS=*W7E%NPr1d!jgPU2J)F3&! z1dQewQy8h7?}|~pktAykuJ`#c-m(bpev|sCLm;BSrsO`45Q@jMjqzZGci^(x_eRw> zd6jlGVt8>8hoe&p?CHWAaj)qk?Q-+a7bgK>%Eq6X-1Z_*h&dwY4gnofF+wm!3> zR=_UJm?m=kxcX}M?5k(GXY1Z;Yuz)w^KZTN=Id{Dd+X=VzVXIuy;nCj-kHAr`kTMo z-5y_Df7|~|-u_B6oftz!2uAI^{wCYss32n{>i*^y6L|g2LU)5>&D26TyR^zB z0_8n*W%Ir-bvRd#>ow{U6W)UDD^lLoNoJuf<5P}Lq)?bII9)wCd-hba*;JkptCjJH zEPuE$)7k2L@?>Y$$=3%R)F}b5@S!TR1KxeRzI%WS=igj8^Vaz@=gyyd?X@@0z6w#Q z^aobF(mcPNAeL>=e}t{U{Ltt$VlzYk5yylu13*g~HE-O68F`T_p3`PELd$60EN%-< z=RDZ_%Wz%Q3YxG%jx$xKG8@C*t}n%DrHV0K;%vLPoN108bM;8IJ;th;uLi@A>oXlH zZO&Q)mbB`@RC|AJV^`2~p2iX<4N&7gu)x7|B+H9QoAM0GDeJ0Tv7*_Cw`7Q3YJJL& z*pjX1%k}Q=>7^NJR3sh_N4u~ETe?ON=rQXY@aAPPN{qzua7@ZWqq(>Iv);O<{#}R} z2+o#JmRP%hh?OJY>GTjiVjW8>4LwrrW|!~qSam4CVwdeb&dt!wxPBA&##{urnj014 z4cZCeHu)QyST4^E((m-QS>0?t7IU4P)TeYP=?FmU^`(OxHXUuu9z&;Us}5?WcW zSI3VwS1pgU*Y)TRPON6(59@hwl=0(wCRt`7t5yN4az+TLAK2oj`PI>v) zWkUwEnLuHgFHwd=SeCl$><>>%N>T(^vn}>@ZEg`P011K%6KtEcQnyyHq6dC(_eKl# zl91M!^Ir;LP9Y9$o(wpuyd?IU=zVQ8%537u+W4R3JKp&SFD_jY0+&BpUHOCcn&U|Z zGhBRsbV|as4~PR!%(_(^7i;8Is&;GOBeq$6vf2!`d>ISWcLz2NiA!Ix4L<&W8OuK{1;i#OEkYVhJqUDQOm^E< zn`EnL$;^qmYoZOrbMy-Cx;4OI4Rv*rGqnbqrk`|*!*XX!t+=c9Dcx$lk#N=sjt~=J z;bh+55JRx!ClV7shPyD469jdH=Eh#?&Si|zGW)A$>=H#oD$YrwTB?UTMX&Znk-+oCu0|8Rqr9s+`RH|yty16&bi>B-~IC0 zGZz_ree`%)UPg?187l-8rKE5Xy~tQKlWwQ;sNP*0=?w(fpvjEWgAsPzf`%{BMCXIc zmj?aI!H@U0`Wv(A_pA3tkB5mkf=H61_7rS$ckL3QczNmYJ$I$B@=dGN{sC{)8_jnN zXgX--OQOOmP6-=D)QI@$!#^13vAI7Jn*|fySXmu$>s7iw^a7*bua2Bs`}Kj@ZKKqr z)|<(!3Z(I6S=r&}F%AgN9SsLqL;Q4W?=WT(U456bD}pYp5K7Xo59OB8WC?oopE5U? zmkps@4^!CVSqZ9Z80l~~FEIuI*-Qe071n_x&<1T)T|Y4x*v5-#WqH&XNCz^;0_!W2 zWS3;0B&S@@=5W3;nryCSExn3ATGvBd{;%A6z#AR)iv)P-GTNW(n?K zJ8v(GP0<^j`@Fr70408Vds*G)tIE1v)Yb#?470cAvyIN1))O($H9T8VQg1IS9g%qQ z2ygebGb?YMeLeqC$hEX|Y3tl&5S)zGW9u97#yLJm2rl7&(W)7GZ7iKjSPWv9R`tbY z_PwH(!e{u=cmHET$onw3*4j9RptIX7_t-CJUiXR#%xQL6n)xp zMph&$cxKZnN>8pX#yS%e?VD&U$MgeS5_?;GPk^V(amaDml8?63Pg+ z1EaP1bZKKWTEd7ftrIk_#@}MnrQfc4dRMtV?5&fdZl1)nh8)^+!Cx+5{o`0HID!sT z6IQbgXb9_QfrM}1rX^^Nq8^R*lf{%iditK`{;yUwYt3J5sHNEU8=leR5grS1WIY+| zN*iTH`Eip+kEfF_g?|p0pz~Kba?N^NjCa&usK1! z#6(&2&VsT(>bWhWELk>ImX@wNoZAg8r(oDaD5H(I`gkVVUzN8xYCIS-Y=GAiW1c?p z%0=^MheZlX9rD&sHe#}G(n$DfBcxFtbhyw-sJ>R$oMN`1w(*MSyd;JN%MeXoJg=F& z!1Vgr+j=&Ma2Y1lR>qMZ8vh3~plk?6qgJvCck-}(h-|^3dEVQI5P-gW}laYx;1?dkR5~BGt1Xo1ZzBn%}5Kr$Y zsPEQ<3_6`K4~#qb6M(Et0()Ot4&_lgbrHB(K~m0IQ)1U-n-=NrZ~|SdC~Gk4LgL1` z${BgFROgN4iEKVqEz`m1mB*3Z5E>YHzzJM+d{XS=WB%g#t2RM(Y0 zjpOOGhl};~r%T!{uNqy5dYz4KvM%>xJD8t;(2g#{ z?diEz!SVX4&UFW?kI5GkfAO;W_`u;8sSxeJBXa`oR}Mu{8tu~~7_kr|QWF5cJdUF) z=+$Ha*lA_WWO=l>P3FT)zVME>Hqkk<>seHsXzd)%6W|c_&b8)0xUs#K+B8=TDpT(lUyT zJy^=BBd-d*tv-$-&|sGX!pVdW`g4f2`Vq-BxrG!B$)vspq#P9hiP_vRUE-VrDyT`| zzQtthhAG3uEa%gC3dJKHTGSBsse(JBpth9s#RW@A3S3A;nuWE-pV0hy{mpcaQqCAm z)!>q`c0Ti{%jUz@vI6?OI#iuh$MswH?q9ia!>JY8nC3jKi#1SOhkq8Wx`~+uITZRJrmW7qYJnGp|cAGlF&pygyQVRB) zkqUxqHvLdWV&_qa1;?B-R$`pTsq~o`$TWr#D58-VcgWhIC`h{mT)8C9>#AgcJo(Bt zGnZXs(*Ki@sv&IQSFFz#$>F}pG<8Wh>Ao^1mJiOQj)r8SC*NM3PbaGbw$u+AksDvm z6iT|7^3Rc^(h41NiV4`nskBIjc~z+c@{QWe@fZKjue@?>d-s)B{`cO!`*%j{-}x`A zciAiAAIK4KhNq$p)fs)n3xyz^5uklG3kqX88;_LA*DjM)jB}lC>gXnVYdXHE!%at| zUe2CByYl)QXWn}Ct@G#3zIAr_axTYx+P{%S!KX;2zJ)sFLJH-E?ab_Vzx?FdTNl%O zlx0)N%_oUq19XG}`osPr_I*B{=<+u&sw}IcFeY*M>~;(StE%XuVja9PC1vf)-?|98 z8~C#8aamKb#pUzi5F4)Y+HPpenIcQ?*PNc>H7BD@5r?8J?K7jvFoxg~MT0t8RNG%q zSANjBi?Lsgt<`Bl@lTpXx=FJ*cjhd!(w0{4r5ZgQ=W*3U(OfZF<3x3q6w7|=qBdSJ z51;*`8_r$a^veJ>m%QPWbUtT@leo(x!qw=nn3*nm!f@Ub%t)-C&6*h{Dt2*`dtJC} zH0|nx_b;kRgk~iy149pEyH)J!qi_ujpw7DnG&U6{va91QkNM_BYL%6SICD`O_%LO> zI9|)Ua^~VYO}?$=6v}A}9P-mKu!bFHKb_}|N6VKJuQkH*>_U0YrslD$v{N(Ms-oWRJ)cUiJeRR87hc8;Lq5EioWA?uP0&dX&pMg_Y zuMd-1;1It^;V0z|9eLy3vMpD9@^MahVP-_IhD}>Lb16k*`@UwkB065yA)4(Llp;-g z0K(U_SNW7DRBRd|cU{HyT5J!WYi~2>2crhX{2LLZMvD);Fm?Y|^kdO#FyNpJ-bF#| z(UTDJFSnB5j)pi|z6jQ>IEZq2ZHKp$!YsH+kwu=Xh#0EMS8$RlT{%pG=qL`c0)u2T zxj?@txl2t6ouU3S4tUfI)A2qY0}dfv?Z~F{sP-G7Nn?NvO>7JG=1!i^E@w9lGDAdV zs53lRRn^lA{IWS3U(VfMsrjwhh)f@%c002O<($259doO*AhD|e!ZA1ImUQ@ErXCN= z--OlTCSyVUM7M!`N_=xhekNDj|426~6a}gfXAs@bIzXegI9cT!*}W&*$WzGlGk$!l zY(m8L;t{>6&OMS+b+ovt2~xR2>05oImP^j0;4eEU8GBuH<;oQ)QoSJn5!~=ba36|j ziM&?#H=IbW8-e~VHl_JOlzNUlC;R8N}S@HYYMpifi7Q>23Bf}kP zZG`OnOzovRigQao`B5RV z#lByWJGBw22$aQEg%u#T)r({tOp=Oq#@L<&ECh3xFx?Cg-`yoPd)}WUnyN9 zHf>Z1WNf>Q0g~wblaD|7%a-`cRLZWH$Cl$uYZrW^0ah+1bt=?%UmlG(=?b8m;%rwxx)6n8J z=OI5IbUC)w(*juS)Q?}J1VJOW$oqsLCM694Cu4xqkwluAW;^7hWc29~G}Mu1*~8HO z2+0ksbkecYDfJAdCTi;0E)oiPY#Ve%*)b z0S05~3~PO<%MolH_{KRjeix3Fe`sv2E-1s}+ZfB9q;(I*4gHm$?Po5$Y)XSmR-%`t z&)bp~mXqZG3%f}_7sk#Os=vWuXVQ`)J<#|NdvAn@)0=g6o}>U`*+lSLY}^nta1st_ zGu#`dExM>Z>j0@ZixPDY;2VBTTeIp=hk56PoFU8yC@FwP_>5FsR3c&m`2vUJ$GZOzf%V!)d`Zgfs;XxiwJPT%#5$-AGT!N|C7svQSFC!p z^-I1Kyt=)%&fBGUAZ-rz^QacNLVrl`%*IFp^kYh?8IKPY=;<5_$?7m4%PDyKDmpZ0PQyL%i9Qrsrm5wcDlkQVq6q~ z&hg+EoiZpDjV7_k9Pquj<%&r`gk{Vy$TVuT3(M@xI(AsAE1pUETmPga+xYS|w1{wV ze!%TX;GVSz)QdOrqGeH3I#jBgQxrOO&N={Re#H4)o|QNK$f;-VjsphCTFYNuA0_5e zPFgAiH$&osP)Om$Po$f}MtN6N&^$RmY?4y33);$W2z~Aolg!PWWZG>ej&$SA-~cED ziJdHsAx@@{6kIzokEzrJ4qrHMG;+d6C#6li(}FMBcTcKumR%Xh<9f3I7veddlJTHlE+S+(v^@`tAm9YUzyS#a5cM(MXc^uFAv=LUI9xRdfcjA~WA@_s%yy2` zre!Ln)K$k5SfS3Cgg8zL4e+8J4dtHXAZ_$gaPbpzNb503^)L;lD4Eq>M9K@8{7n(s zlwv~bq*M6^q?k~{rqcf{t_!Z$dzOBri4aN34UZu#R``S2nO89DEogBJZhM}f?2)-( z)F(s1>_l#qyPPO_L44vJT^!J5u?3pF=iDO=mF;no6BGfKK0YuGm8 z+_QK?2@z#7^lY7ENGEGtrKj^g8lUxu1Sr2GTlc+o_+0% zHjXWr$*=VXqs|BuHJh@Hl5&y_B-=K5aKg$97f&c43yz_zqA&nf4tqt9^-!kG;aOP1 z(ECKBS_Z~mx|jAoq#AQ6;AkL)%UHrEAAf1#W0i{seAHMo?l?lZG^VxDlH!KuR90b% zNtP!LJLfciNO!9mPsZ$pF1vBrVnqbK5GQ}ilR4)M(q}&BP(g8xvJ1jU#Dn_nGeXG0 zYJ@da(lYBPdCC#@G`cG5}~iLw0)Uzb-w zq~l;y!>7uXl95jvcOCDR9cG+{r!6SI#^z{1a^yu{mxS_j;y!IJwh%DQ^ws|6l zC-ENHruFK*`kp7MNvy=dC%EeCoZsLb+2qsN&qk)0CL%tfi8lgSoaD5%Q|Uc&MEL(| zTK2&M=_K zL;KlUJ$uZC&2~z(Q08!9hb~IZCJruX0)YHD9Yio!>jdrTY<%9_WPkN8yb?dWQ{82e z+SudS>m3Z#uE~hI9T&P2nX9yw+VVL{fVuRD9@4%Cq84U8TJzrCSRT#2uq0VTw_U=} zjKaAk{f(M)dM1MnB^TN5L7I1ba$Tscj7!$Dvm+0xVSaLjuK+?9*(Z&XE zjUR0@*Z%aK>fQNZKnHfct87?o-a8o6H8OEDZ#JxMj`-wb&TMcSvCi8j$X>_O@d zo^U&}WRDyGdlG31_1T`=i!ErI;k z7KhykCLJil_J4rVh2HoWnaZr3(g;4$@7?;bZC`{x^M;X2{IoDJriV zXTocFq9)q1cPJ!Hzxrma277JtWCj$%gY6i23$+?cVE?wBCWY1xG1!ekSg6jZ({@_U zUNVhzq5A3Ci{sKo9(Kadn}@`DdNmUy^Z6h&0rm&$1J1Y^kAR4U!{dk1Z3pj!=F}Z| z?|e{|T(OTgG2iHj6K^Ak4HcqKZnYO#d8?9Cy$+6H51AGMuG?g6SUE@fBr;R#xerWV z!D>ZS2g2XyaJ`zQzrlQ41$fjVV;j9V6d(@E(uWusDQjo^qHt66YByD%VQh^T-70FI^wxXrERo8^9Pq|K zu{{RP&2F3aDs>`N9Fwvl8%tQbz*xSlHR$YF9d_}U#zhLM%UaRW@x^b1MEbb-JPjuN z{Z*y2z;u)LzQhoJqt||kLj(25PN$u3^XSES;0o%90Xc(-8NsmSMK}{7FtKz1 zn7sV>xaHiF5wGE}^B&>HxDK#VKEY{<3zcp-`})F2F@}!3O@qfFUh#4MOjBhbIfNRA zwaN`bh~YB34^cYvF0u^@&(U~}I@Wgc;H5Mp*g2V`cA$g0h%WcuBOPqE!O2dJ9lNWq zzxKxK=g%E>x5xE}8e|xLltz1e>_JB5co!J;%ZWRe{%Gm=cK3<)i_Nw!RIi_@w=Y(! zzgNBWI_Bz0^?R%OdV<2bP@PfvWpr`r!=>Y276&gK=icM##f-4NRhbP1HBJNw}&rbx`i+SG;jS( zc1#x=T3ZD(yFpL9sZm?s!sj%L6ScH7ny%0w!&>jPDg>45%IcMAzF1Kp--II<=`=b$V+ zj7=rT!*8DiZXIt{om$aW_>-8*ct7~bQ<4$BKVbr#;DVG=I$8Y@+N|Eb$3%3j+=c3d z+3`g6VRZ^QIfWq$U5Pt#2Yn|vasOL~C=7gZR&M!d9i0Ei5@RSvJ|hJZm_d<1uOka* zgCL6IBvGYOJnP-Df1oi5BI0Q8^f^PtS)mJwAH^JtK`k3x?1b!BL69y*Fi%Gg$Zf)< zq1gqs8Qg%Zf*R#p^ zG&98cWar;ZR;aO6A7T(c%T&%CIhCU<>6zb=c}gZQtmGL!`80DU?XMdS-_d+D(UFK) za9zfHvsxNoPL@n=b{423lrHa1Ov*4hPFSg{5hI_JlCydG?dx*BY(Ajt?AQ!4%%N+ zL638eVqdof*`C=}2Nkh;SOe}?%;U=hd?c||ER5u`3>6XJ(0UQ$qN89HUh&a7UJ#1N z=cYy4TIu-EacE`fbqzz7SlWvhN)ff@N-A084aKut-F4N{bwPVh(SUY?nd9Gdxb3A; z92S0yz;haBx)FMkKDmDFEo*1qy)k->hp1ECj_Zi<_nMapsGLd+sLTdi7kD#9dw=8# zNgChlQjzR?q^lguuidGUD0kV0Qe6`OzE8DwWAwzA-bUYdmElMSM3d~sBD>BTg%@?q z*Hf*JVu6$YU_q5wxs_BO!!r9Mx>c=EW|326*zJTpY|P=O*qgowj#NxO9>dk*UHTGI z{t+3@8POx_c-=)S=h{IO#3+lZHukM_ww2=XpuZ1lQzp?X`3 zm#I`Q4W5+G*mAcAK0{x~!q}XSq7xq5Gb>$;HjB!)_vL<6cuoXc$5Gq-rN5&E{KI3% zUjCDxUGLV1fA!MwpWSFbgONUZ_t+~M=EJ`_HvSn5_nC*_d8?oK;$z329Xs}O|K?9{ z{fjSUw3mK{js4HA9((rEvEhrK`7{3ZU;VYm`>zgznZmYQAjo7Jyg?$~9VQRmm9u+V2(AEsUqJCMOI{9^^Z!e#H zeTCiT=iWN|=IgJYIj0ThRmCO;zuNV_b{%?$s2gwHvE%j+bt0|PEJhO&2NJ1`hx4Y> z%kEtWq)rxlOKB8IGtszuiA+2Eb@oN@S}eaPk-cu3)^5CN(>t*Bq9*khQ>AK~O4#^I z(aTL|)W5X9N5w`8RqipT**cBD22vN@sAAE>%By_U%MvqD6R(@Wsk>_TSnPJDB&ox+ zH*4PQGTMok!n;kkKilt&-}eVNq&G(G8hTb1`YRJW-XPagY}<8~_+ zF4u3&-1+R zB_+2-ilPOs6LDtlec$KD`F+lFo^vQF#o2Hwu5)@wU(=QK{@NzU+2OI{w3?w(q;G#y zKbg0G_(s~?rm(@aoO0eC;Y>T#*?IZk6e{e)@cqt}tpu#gySPm0+N!{|W6Be>VWDj96vGu( zn>N*rlKk|^PNhai_iOvLQDSgj;)?{sOkzn&N@*_cx$sIDI6t?dHJhuWSXlJqcCW%p zYG$|g^btPtF{kXmT{5p%=2ZGCzMYo#h~ilBNS*!yB`o1gW? zdWR%d>TFk4I;g47NID&PdgLky{8agDmB+Yv$w%Dz;R2oWng^}jE6c$lP&RQ2QW zpY9=~)+^qnuAR?=gDT}_1w_&iW)0>8bUHoS@`rM(TqNNpUy`*Tje~&rb?%o~exTO; z7gv;YY|}rWT%J9w&Sz5sKxzzL*ij>enqOx|cHS=dkyv^!btnsNRms}wmZ#vW0sj3| za|B7ecDPI#*^?bhzZE6A%~KX*p<(WmNY(a@$uxC&VsY*8^jTei{Nn1#-ufD4k9k<) z;87&0Et9xzpEXTXnKl3szq2yCFMj<65ZybcBq_LbYv)W#PKkw{71`%Pj+7Se!KEF6 zW#}Yi7HgGMl0adQvo)o`gX9BhHhR%I7vm7MSGj3fvmj~Prw1GHX$dd-8aAY_7BsR8|?$%JgDYUuiFm9N|veeOPkje6bXS$ z)RQ8|fD*7<*cEPI$H<8)JK1psg88_LFYb`e6h=R>em`3eX{X??#J|5sw=KZeIfojW zhldDOOUSEF7w?t?U{8C`0Ip=tr@|TG!VOgJ{fl1!33H%dj%Xzb((O!gpj0B&>deQ_ z00gRstY83ZH!KlFZV}ZE(FHUJ0iH0eLS5vDBTEI+e}Mc=mPhe*E4ccW-Edek^FV>e zWc$#1>~D&OgI6SLco#lu5{5D{l+kozXasPP@Rg@#D7QU|U zP%gBu=~M$=5koBKxvMT}fd>^C!4~~h&i9uj76O}I6O^@+cGHD9fMGP){e&^>KSK5n zq?gi`XG@9++cFJ%17#*KOroLWueh4YhWj$^-j#fDuydqDZTKCvryB&AN}`*K!L2#8Ie(@^`-5{fO@@0e3*I%XXCDz&b-q8xZkVQ(Q zbQxVz$l(OsF(>5Y^>%0y3+AN`O;Rf@Nh}xwT(fI3u(ATBOtm#}5NHTPL3RcUYHK^{ zAXohZ*6I{p=&1&MfK4XYQ5d;td7$_%dV~ITb&Cd^l^o`2M2%mfaKwnMP9i1p4d{j~@+?%TG{dYoVx34mQVyG% zX++T}mC~)+NHcy1kQOXuaW6`TpW35W(wddv81TtTAD_PXd)CsvV;R(CmP2AkX>(yt zS;n6qQo6$8!VehfIdmN1g$RL&t{Ay#K^bM^6G@8u@O;PxO|t6Lwu>tFU^+67HO0n-AuMSqt%u|i-b6;8vHkJ@J)MS2)fxCBwio|HLO!FY2qN> zWe}_&R(g*s-fTRbpRC8Bno{8?SG;t7#Lx;V)WZ|hH=kNJ*iJ{t6&8U8ii~CpOHDSK zS2D2d1=O6at%NmEDc{+ZHryuxg?0H=;V58`{q@y;Sqqdxul6C%_AJen6jssDE{AKo z(1ms>GhL9T>5d?Uy{mo4;r}DXQO%c&s>D5b^uG5}gqasyPuC%16{hf)r4dmDA9b>* zfyDcU+5h?S#ot~j%AQhaSprn>@_AjvihlyBaN%Bk7UEYTPbL*fyuE_^uw#&yWo{3G z!g@GgL8d8xjJ}FR=APLZTCF_r7-Cxk{ z0#qvHB0s>&1r|G8Ay2%*3RuEp8iIleL&1U??3Fk}p#oddLA0g{l-y5-i5hA$RZ(9E zAr>5MAE+;)Fo{O>^*EKH7U@P8MK07+_lj)VYu&(t48RNV2T0QGaIZwk9)|(;)QE$C zo7;kui>#14nw3N;w$crn+*wmE{_o^$M-#bOsrg#s#v+8;*=qS@!^JA4efi=aWEqNn zhv=-k4S&KMbFBk080jccqqJ9ZE6Ww7fca&zG`+Xkwc#-~P0=**3?`&MT18z-5L`!g zWSfNK*VpOPl*R>&+qM=V={&ZII)Sy=Dj1bxVH;v4S%KUwvMX<0dkY-}5r=p*V+#LS zFw5WjwsX zzd=JxFomzObVUvHSaw3J_(O;gfeY0soroO)=rAR2=lQwY(KvCds$|)jp)kDIh8r7F z;-28rYmq97bV|cHcEuQ)b%OYk2h#H*S`$(pwAHkXTgJT#_E^UplGtZX@$S$O02~ov zhId)RfclsTS#i_kn6c=q^ts69hC{OROsmwm4sU&-ZoV8)`j};KbA^Sux{6^aFCB_?r~O%5l$yc& z{xQ{f2`Jj6sqEk&45{SIVkRUUWuo}llveSNRT@qL@x;^Bh9as%42{?o)VvMUSEsH}0rh-qeFP3$^h+_9!B2M$U?syv_ADV`=o zU<^3f$RX80%P43r-XRer+XB>#$fAA3?u%dR=Vjk`iF(ozdfv)qAFZvHs5vH-3Yyx} z0tQruPSGTZ#HwRMktHGk$389=I{}a4i`|3;6jj*txN~4We0iXM>g3VP`q`RLXSDM3 z2~@|$)!GVF(x?p~sZvsMM+%;q>+JqOwIM`etp`0!W7IKgweBR4ep-D95j4b)EEi0(g~FL68){PS>hr}ZGS1lqO(1S7nS_K zq6KJ5_UUm_b^vVJEN^zum=PVN;TC+sIVV@Dn$vE1Wfbgq&Q4_5iXkfBF${+hj}L$c zTNbTyppf0v5Z#4%9$f#|zgexhjdX51YPtd=aMpWxW{8=xBHsM|;%M>xH`s7Yo=3(v zx+%lLF~#0reDbHL1H~aKs;H32fKvC75yf;sBPNP3w2!@X9@NEDn$PfqF~etzI}d61 z`RSuseEzc!7k57W^v>O%-1~I#@ROe|?tc8?qfZvJfN`85=+5#1E zMNqIN=?~mX{hZx+hmOA{*J6yQsFhq*0SN>_e-M47Y4bd7H;XQSH1N>lJ^@wD1ztoz z@UL}rXZisKQohf^H}LqdqL*1yVIJk-N|6k>5voQV8aS80}lK0zTBpK#?AIptXvO3Twi?V}da~z~%HDQ>6Up67QVZua=3M(qVV6_Fx zn+DS+)@F^7@mS7|3Y09YA3g_9`%2~N4h~l9y)!~lig^Vtf|`X}yvuDShRR3PKgh*U zO-p-LMn|&M34^9Am?%2!#DbV)Yo`Dv9!SQ+u1CydYvBiC#T<1|^@Ik@yNnVo=#c|3 zKrpZk_ro`EMkIL?Dg;tB#_+Ryy^b$h5KvH#F zF%VLx9F4kWRMnm=i~uRN^5XAXHkt^oLi@$ZqjN6{h!AX471Ya+%2!2riWWUr& z)D4(A-ouA(=1{6ekBU_-#q&}UC|?fohMv$l%q?zw z5&N%VwVR97vKVHkG|7UILFKdqFD?7Cy|cZ!qE&dvV%-(>x6?BO#tI1Jo6r=Ob+dcg zcxzxvjXHuAgJV-A+sY8yg$Ab>jz+`@5uKTPHY9PMoGI9G)*H@<%7>nfUuL&ykBE;8 zY%@nK%~H7f%?W@N7iPx=RSrvS&?Ov11p3{b7)6~JBQ90hdNH%vNoiH0AV!Uf7N6=7 z9%_mrN^9_S2qJzT*fUD)Dc$sarYa05oPjP&XLMw2EZ~SBq$(a*0%6D}NaUPT^Ng=? z1AkscOK62L9&(~~@;I?GN_)NZ6KTU^Mu|>w$9cGk<1SSpmJF13%I!Pw*aBCFIg(bN z+|fJ|WM`Iz2h_0{QCW%a8L7am&`7nWXX0>|=&G`X;FyaB~l+)R>wx)amMA zH>zm|+rJzC+V~{oSwZu5Qoe_*C@I}BBfJoH6&H#U>*(a&4r7nS&}D!r1grK%da61> zPUo>g%ua$l-~-oEOU=z`R2Z)>t^(PB2uYkC#mrJc$NsRA*QUzh8s1UU$J3Md$c#Wh zqTU9By+Xv%>X}mju#l1fvYi`kjAsME5T>G)8tSYl0UDz9EZJS=kfr2Ki(#^418HF~ z*2jo&M)@d$Cae;Pak{^6=p=^{+6%ozIm1F-uc*ubQcZE1Hw=}rD??fZ8RpVJ!XF{= z$HMKIicj%^=K|@&b%K2QNCkE5@n#|i4eTD2QMm7PRYO!`wtg0i-UIMrzY#9>3U*=E zq!uI=Qo};BE!qxd)i&M8YOezpgN^JKKO*PnSkG%oVG@;*HbzV&Rbe!<2}uNLAuT%} zvlw=*-JxkI{4-~EX+8s=bXo>!1{bbJ?#QN$=$_>fR3F?or-3%bW*~i*k^+JSvO}uC#wPs<-7DqQRf1*^WmX+ z7%|`^G%Rd~%EuX`)KMg-RG>zx@NNLDHK!GT*TU4($)Vr_4__b$Ji(D@_9*rXD#1}C zV-5mifN9%@btlxpHR?6|kwS8PGi3Ef6;u>BRM@DGlVG*#1HAx~y zuMm7P?U5z~No@SrB~BDp{Gm!67|dQfXFP%u7&TXPRlyAa`H^sp@cBJX{ZzBAZ=9=P z7h-Z3swKaXLc+T(@>K?AqAePPFq|)hFeA|IV^1HFt7ASaGv2r@DjqjJreo;BP3VeP z=0P*UzeBQRXoo1}TZjfu!mNWH+X4*c5UKEUD?YY>@w5lX2FxaNDh*M=TnTU!pKAXo z6r;yFSH#{Og!NNWlHaC>`uS532wTP-LL8I}UYQ)B>gF4$z_f_*gjpR#S%*VzVM1#3 zOT6zUWMkr??Vbm(`=?ZfiJ%Og5Mure#3~U(x0A6)PMM^U1Bw=gZ(6Gx2O~5prW00~ zwHdxJSe$X+e& z#^Td5FrQOPij&m)P})h0I8`lRb!nzxuG;>?N zC(cepv_c4Mk$l`FO_>Nmz;@z%IVQOzIlj__EU0NvwVX(dQ4lR&Ie~m;EJ;E7lw*xp zMjOf$WdI1K^t??oKncRvqz^QcxKR{`N7&mp7N6(ehx1M5(5t|O$v<2%Pe_+Jyxy3u z0s=W71%^_hc~atWaaDdu9Lbd!8tmk4>!&EaOj3hE4e8p3kH6s1@bL^1( z50Ww>n-X07jFrLNU!p?S9u1Q?P2dB^2FW1tw%*{R%_6?W+z*f`eXMVojSz(u=NZ}4 z3G+xIOAt|&+mXX1j0Gb>3R!X3WbgZnkEHa?B;j%|70Ga!<{pdg5K&fN%r-PBjo93k zirnLdks8?AN(~7E$p=2`z{4PcS#jV=d(;RbteK|QU9c#`dqyuDLbvYO8AL=8bt*B3 zBieaDV}b=4LGC7JOLtJe>Vvo;CG6?2;?42<=2Yr_@ar>06vy5^j;?#ElVllA#7hZ zR=HY%J^PJaKbh8A_ZLIpG=Q?}3OYUnyJwulsI3Lf0k!K08gi6+v9otb4-B{wERexN zx*Ok{21?0eCBbgJy92lGMt}EngB(%B_|Wa(05KlSW^_Dga0!Cg6yB&<8B0tgC8%T{ znk@7KZt>q=iV=&xgvk^Ai}{LR5`5h-)i*#>4uDVlEnYSoNx~ggeCalGUD<87w0xqb zAgjQ*aD`l@E|3;)qpLhWXs$f#FvFUgGOudru1bYa<*aLQV$y`WC-tzA;g~2?NxB&6 z=8Qta3Eg{kSlUh6fo%yj#`G-Jh)06)kh7a+RC!(;>JwTqy`__Ia@ltAJAi5Ae81Fy zTTwa7sF!~JJ1iFzh>)aMZQV){L5Fc?uu6 zk1$}rNHKQV((e2k-lH>QQeiY+8OhCzg~hs}(mp1o0B3*ewbw)#HF1=thG(afWwo%I z@JaKa+jtz**BaW#NZbVDXxNbhikySM2iU-mNqg-*K!L6DmYg_)z-;=kCO5wAa+B}!_dqc0{GwVPdJ zNy^yWin&jC#`ED!P3RR-5yx)avEVRue_uJNa>-Ys$@B}9Q!>Z-&?Ew=s1g&{L|3=$ z)EJUnU3>&InGG09g^@Pu`Y`}k9%?Bs^LAMSjjE&OTrf{*rDPV7S)PG2F)eDM#exe5 zglFOR!Xjh~W*%eh^Drz0Y0*?pJv6pM0@2ppp9ydz$X3 z^?qL`vQ7Ihoa=!WI6=5Q<;)~GOZr1)J}^E`XHKS!EhNr0$#u;n0%s(Gim0Wv>JC=$>kB}rgGKvV?BLI?TX&SlKB7&P| z3(A^X={_P=KTT|b#6WWp0WewS%ZuIoT83+WeFouYm(L3gV*18Hr$YNY1x}M0OAt`* z4OwUlp^n*kMYF}@jV@2-f_5@0Dv#w&|@&67B1&s5y;;M}-6C0DT+P_spuWYFbAsV^yG|kypK~I1(<486%G~6E#VnuQz3s554Z8Xyws>9&(KhnI6N48SX-m$?JY1nyD{3k$@OGtA4$7&Nv9)%I8k zx@(#ym(D__V8g}d0!W48w6SFfxnEW6&&Ij#T4gK2x!Q5}HyDZ=k=*r>f0>s-NO2yd zfBNM!F6aYsLoMU_&E1koxc$)qF@D^l4ok3ew8s?=C<`e6DuluxVG=bPEYD|`n3m+m zf)N$X$}?Oyj7QZmoX`Vz$cJyMnbhr*VpH7BUz5aW0L%EldBQkfq0fc`mm>gLv>XpS zP7>V#D{HfPB89wZ`7Y-S2xHbRoPGD9$<@++LKm`VyXrjk>}+fCAvB$=_F@Ez(u5uYG zJO_J)(;!or)nl@DlHVx`$kb(pv5=Cf<(^lHn&dpABL51akL$u&m#1ZXL_QAGv-1i# zB||qT^d@3#yCy-;U;@a7+tkQ6EKAOzq7!zg-w!o$!Ge4ASWybrnk=L>vq--6JnY4-o-eW4XxA6P+VA@aBj(@u|xJ#XDz1?Cs05Oz&N1$o9v<8CcC@zkHrNzAfK6L@l9_{ z#eh&{K=lA0$5QuMWrD5Fe_lsQ1d*e>3j`?Yq zXoyS+FiHv+?31q$YM;DD8uG;LnHfJV%=F@~GvDiIz~vbSw|M96qa3%+v1!<%Cz3b1 z9a?n*C?t*Rox?K99}@|{ez}&5PZCcm;?HrmbYkafV&3-$*GEBnz&N>XD!+K@_nFlc*^W#$m`QRW#~Gc{ zv!oWP#xqYQ)s$(zXjk%MPT(_>dC(q8b|=~~f$-l=&x_wD+&Uxmnavu}SC@!9LYWD5 zsU%We15uHp_2wxc;LFkMJ2|1*z4kND<=$V^<&sdWc+CeqSB7TUGx)mSg6oI)7KtM_ znSb%(x229R$lIu(gsf5GGt3+xD3s1*n0TzNOL?dFX_*G8K?$fBbT58epKv1BrUK27 zVpO#6l!bojQ=9PW#u|oD&WFls-#APcTA+f1<%nwUEMi{>s~$=2s~avt)Cxk~U?BXM zL`LYiPlENCBi0dP_LMbUpI~>D!OjuRPkg!vz1WbD`u_QqedWy{nI8v4&ThCX$+AJL z)uB#zkCc`Iu7F-l9#8l44?p?%UH}f9qU%NS2X&P|C%*2NW6HG%SEK%lZSrvlo&A;n zEs!xfW>on>`MHzN1oM&^Po_=WlJbQ5J1EgZ;=2cMXVVn4?~p)#*~%%L%lbk-@^@?8GNyz*aGHK zJa$Asx!N#&JCekL^8O+h<}^;wLG21D0nAff7LD?DsYI5Ta+J%Hta=nKn{O>YqqhWe z*KKn%vfvHXSgw3>`VB~v^KZhq0j&lYRyYM(=U=pwtoB$P-)2uqG^5@S2&;f7{LFDT z)91tZ94zPT$P#rW;P_E#GOV3daDtyRH6Yh=ASiNZ_Rm!|j$?CZH6X@dN$dG|BH9&7TvS8tdJ{1Vs(`KB0W9eGwzfJtJZ>j*b_ ziAX^rN<*v^(|J|N$Q%)0)6>uhx4ABPb>!dL&bZc6K(iO0h#9~-OpOyd~KXKucwg*jj zgZ7>%Gg;R-YXn?EC*4>K^Zb;ZZ9(|FsxpgtL7>V%vDH5gCI~#l%s)+5D&>l2| zIB*EaNqu@Qmw*lx;(+ba+_E0}XgE{R8;!3D9k76WTSO4+TA zTGy7tMM)`9I$bQkyy_yCd8usyRVX$ta%(z3E%X1f9XO&DiL*AKw3E#U@Dc%mRmUI( z)I6edyw}PrM0cZ1IR|5hVMWMbwib9IQ4*jT-H;uGG#e}`1kzLs48x5WuJ=LZ6414^ z7p=$xTefdzE}JeG?*oxOa63 zXqGdOTK-IvC_~jsmA9{weG|YrzK_k*|96!?mecc7N za;!{H9;harCqEbH6T8Kd3Y%b3LX2M2N)mpkrdw&^&+{>~&^ZPGvR2y$%9;yiNSY;{ zSyy*)4YN;I2SmU4t-0!D1zCyNO>lzS1HIBL8_=CB1L++A?ctM(x*T?^988$%S`R{x z!wW7b5m9$Vhran8k;6zbz|X0;nHq86_!BdM_%$1Bp;(Qrl3pz{0)S!%a^0gRF{uip zme*p;<6ky@hW(h^%cN_%uP`|`N{-2i=qY8KO4Ti-a{@fdVU-vuHpni18F`jf<+`+*|~QD(2GW1i;1U>wjX({SS}X{RDFl9MG3col3N zW?^yE^ui30#9zoKtk{af;h8&dBo*zn9EdVhcrQ_bydyW|yZWM8PZeBj5~qT$#3bqh zu&@k`^L}KVVBq*Kmtniy!bw%J?ZY^V5@P_DsO*9M!f`~?z~c2NrS*~(tpn(GJ0bAX zJrkEHn^GnO>_{PM4n|kY*T9Shxf-(Z5E?~o0vlTPTe!ubba4J-X{V#ajn*(=X1l0X z_#65UYSpOPJ}BR2RKrxM>sno^bDbq|l@$N>xt zqCz`~P$8Gv(H$uDwA_K;yXRS4*W7I^SHdv zT{FMyG?A_aSthsy;#;m)$(bSpR8oa4I>4hiTQM#c<$#|M;N!764z%1VX@jZL_N0yf zPD1NWi}7r^-IsuVx1kbWR0XUpGL^>)d&tUVMmK>fn};v42^z=c;8_|@%4O~*n!1w& zYUu@vcsb!AV>j>B2x$2k`p6+%kXWYHQ%2bgEHvSQX`)?0k%1w#Ijc(0QgKr*kYc8v zcE!F5cS^ar9+P7*cqTxslgqj}TuS4@glxo=2$K^vB9cxYAp~Hwf3Yu=z>zq58>%sE zAC{n8;{y8?KWhRcJbFx7=%H_LkE=uGocVF~L20bq{YN2~yDNr*eCCkm|7F4wTHK~Htq|IeH^a3X!!zNM z%Eqzk8^{_Cqv8z&wYXT4gpy!cXac8kH5*r5Bt4>4VGJj>(gQLG?A9)>Nc-THY7;B` zl8`wM48uzLK`_*|!zer)szOW<80%_)Wwn5YE5`2~lCKfZ1zs&ehTsDTZVN6)iSb^t zd%>XxF(9E%7ycA=Rv$%Y$4LU&Rf(CPyo}m_toJ?E;)6=isD!cwy3XEfYvBZkAloK8k!LnRMS;Db9d*a%g2 zTHRm8mxVlfB#Vh5~(CRYFSpYhK>d2r+R9xeH2`@by&wh-7tU<-jQ1hx>^LSPGl zEd;g@*g{|nfh`2K5ZFRs3xO>Jwh-7tU<-jQ1hx>^LSPGlEd;g@*g{|nfh`2K5ZFRs z3xO>Jwh-7tU<-jQ1hx>^LSPGlEd;g@*g{|nfh`2K5ZFRs3xO>Jwh-7tU<-jQ1hx>^ zLSPGlEd;g@*g{|nfh`2K5ZFRs3xO>Jwh-7tU<-jQ1hx>^LSPGlEd;g@*g{|nfh`2K z5cq!p0{`KU{^fUm@6NyZ&UgOflLtR}uy|R|ho|RHmNzISdGo!4)l(`{?j2NrsAoUA_U8Al-Tv;P{qMbt zvpp(LuBj9JqifVMw1DBY+c$46?(z;r7oCl|rN@iUKK=N;n?8K|y~pP_rvd)Ji5EXw zyhrC``byoVgzEC=PnMTI_jBH;Pp@yx^K|e0+U=dbeC9qnIK8~Vl5g&`IbM7By|ddJ z@66Zs|aDxh?>9qYXTk zV;_${Xdk||__qPU*I(8D{Q<=(kN<3Av9&=e7q6Fnh(GU-fBn^u#!&zJ3)>j;b)Y^& zUuJ!yLvUm4*3R?G%dzu!|JU|FanAG8SN8Ca*}=w${}*;}u%v|P`eL?+f6VG%8twn! z`Wtm!{MnzwO`)km(Z91`JlsHq+>^_npI`4}c=6i1jf?ZZP0054fBRSe;=8}M`pbX* zo$vhRfBBP-{`Ea!?R<-*!VO7*4JC#*LJiwf^c2ikwon zK0Nuw-fEXYcGr}#r(YPq?Cmj%i1N!Tchnyj%qY6IrAP5JcJ zOgt&2#_Rq8ARh2$swfYc^Fvw(U7kK>Q@7rD^VT5o)Vl6$|9Nd}@liH5o~a%Pj_m*5 z3lwJyKD+&JRqd)Ma&Dz#>$OuT+aDs5een&X^!0D;Ios;& zBZCj=ctcTKM%g<$q89+2jRK*R#LWQJVYn@J@%3-1@>~Ua{nq`R&~&b^kP6#*lV(B) zd&><|s+hvP7T4|IWv&hbO2QvrtyBS}aDY^5|F)G|sXoI|(wv5el=!WZ{`^8iD?bAA z`&9SlI~GH|WZHFDJD3NGbNquE@%yR4Ph*SfL~;Yw?8lblr%rGx2k%uSbsiUL)hD)= zvsyf%Wls8C+u4^!@U6CXTLXjzT^;+{S#_{#olTz7XYPtR+7_u7HTEW;M>AbDXy0JT zbh<&=?U6#}^0SZ8#xz(QI}f4V2-vBa-J3mtI0`INZ}}hijm=uP9cWls8Ins$d1=tz z0Ji3W#za60J*WN0io))9V>Z~qCM`Ow%}?R>vLz^BEqSrfL8Rc459pQ9ADI>}z1#YQ zf72X~HY3HL?;YK`{eT70N`mF~E-%-|<~D@f`>(xsljpyStN=Ty>JN<{QTCbF$1Ajj zqnjuT6WmuYA>36QC#oCyP|msu0JUAVe#>L(Fh&8rJZk1GCQMmrJU~vo<&{rTxqH z3uOZW8+Ug&D+6rP?2xdg4MQxR-`FEagtTFCtNt@I)4HX>Kq=@=TPmlfu>w`og^zuK zs(EzwZ`JUXIB}~}PmtE}f>B$Csa|HjkZ~f!pZQ)RZ+ly0kDVgIoSTG2v$*|GymU$f z*Q=?04Wg7LJ`yilC|n=d1ceA8xlCaU6YbeJ5WzGdZ^(nr5Hs=;=*13FMBaJ2662ec z0N0N8I!$OvQ@{?qh>-@l>R(9P*-n(zo&qiofdz?25WiA1#l~AB7TB>**diAV~56Tmhoe*G)(Dfi(6>kc#Low4E zm(R{t;wWtoTo=7(+?$jZJ}=QkH!{1s0c?m)wod|m zfU@wRQe1hpGKffQaw!i%Hq6XxZrdJIVKc}(-_kvXSBJQxo15J!r0K5waLP(qGOgmF zv`jH`4N+Ps-HRq*zyI36^rM2iM5pnQ4m9jA{jd+A2m!Pje=lGqF2@|P?6H=QqT+H-?mnt#aPc9%I=e!=hb1Y|V`!2b5zM|s=61pwz8 zzWyWUZVejvfo8_x?Qj3krP;va&9`fjuRPJeIA0>Js$0(K$ut08XM#2{E2N1rZs;Gr zypQRXTW=n{;f7z{=*9q!Y<(;9IqpJM_7< ztx(#VR+IUTpa$gGJKsCHdFy+%_YY1FPTMy(Mcvu$7k_16yJO{X6Ct#yKxScB4Gk89 z8=Z5G)dWqS(9E*i05tR+{UyWnO?he(ReKFJcqhe3s>l)ae>7qjA#hC0falWK35|>F zI)_!>*eowbI@_G2(~-Lm1<`w>NJ=%m4-uaJ!ak)%Z9Wxn^HlmwFRrfyGrB&|PfYvQAesNX_~Y~CVH1J-h-Np{S@l)9>B=`ZHCT1<>1QxkY@DKa{pX7 z9c#PI-fpW~){KehC5WQj28@0BVoSGnrm;Mk3-E`Q2G~-D*_w)O?k|2#*B@-4*wri{ z4v!5nG*g$0H{W0qBrLxiu=iwYKFm5b}>x4uQ!37bKfHRRWS_w`pie|k)N6jrUQBb4*o0pw}} zGUROc1Zs;jNv!cCA!{4N-AlWb*Culy#AZ)i^?7$YSUMvfu9si3r{>_HSWD%zmWH2i zCW~(&#~ZU5&Dv159!sEhS)zIwBG#@8$z}IQfp%iDir?1 zW-g;4y{!Hi*(2m$pZSgGPfVTC0oi}qM7DnEv*1xqOKRQ}P{};wnK5BAIH6Bv>i$}k z(MHDKeFqZ=ROekNtdT)8Y#6jJ@x~A|=+M?ulCQ%t&2}oOaAT0oCnsvK05zCqR@tPv z6rlA|CpM;U=&QQ##9Zr?x<6SogLCgskYy(@I_65e4(E?g02NPmN_b=FW`-#iaAed+Yi%y5F z)>-eX&f$QfK?;Be zn(2UgU52f{V>nJ8Yi)kUP<{6e9q~y5<++p4k*Nvw1t`C`B95{+)$vvrJpo6)}8`3LT+n0G?1R5yWn;m$>sIC6mGs z|1m_}ER~4bi)Y7xOF<1$P79iQ2sW_OmN{1^>SdIHLxoiFkBZ*f4BLXkWZb7pSwq{ro9gcuQPOAPy2>&rf zBc##`M|D{I^8(&BJZ7~Ya?bNaYH*A8}1oWo$vHu_+XPHJGOob#Tdsb z0)cYJwirwsMN^kW4s;@N=#$BK%~EWC4TG`$O=n%2{t5XTi%->QZJf!Lh>GYEtBJAJ z4sfuwk{h0Cea+PDc30Eg0c;5fobMg1P8Z)FmNv95#!rHw&H^E(w%%?&uCduG6w$g8 zrrB^OrcYn|nvQOwN&W*SID?lfDAt`nkt(H#2pl2Ib5iM}GPToKx1@tY56NCAk_XzT zEhhca;6Xf~NblmZ&SvxVIAMexx*F#E&1K9M1yx$ou{bpZmJZgC-(pAob!)5KqkzAy(QK577*!E;L$7AOxr6LO&`;g>w#y`aZ=7{k(n^|jIx$ObaV{f_ zQJ9Bd3RXW^t|OEskXoh=qgK#_p5`4hFp#U5{@C9PIulf!Yy%wl;0cXPi^l+4osbD| zbv_&6^S0})?e!lQ zeHIu_=)lzrgV(%3yJlI*Y_Bd>kpQg zbR*3dyx>3z%54q;L6LaN)tuBFb(d_8jSj-5t3E%~aAs8{&m3!MV9Is2gDbSCK1>T8 zj&rAPQ8*vDKHN6ogE&o3(c;j<=CTcP=!cp>%Cms7jCfnG2FprZbr&jf{l=#uy3(AR_~TfEwdci;bnA zOR>{VuS)0O>vF^;sem(WmIxEo1ghHYqSk~Rksyu*ktcN;qj~r;a0wdmN2~5Cf#&7%vGS415mf!u`Y! z+%$K7P*KMog{}CT0awFcL|fNEA1VRzsqI2#05tJm8zM`nF{h;o^!QV_pf3_CdaHtz z{~rcxCnmV{Ms1=;2MZ?TXMwv-fX&{y#XIjTUJaf$Ht$T(&Lju%Lqt#Z3^i3u?v}Lt zz2@>v^gm377)DZ#r!~z}k~vH%P>^ML<3JOs$Gn7Fag|U^VyryV$4gH0kO)ndzy&HO zWEm*0^N;SLB%W8pJKdZ+daxT>#+&9`o7rWbGVT)HAYk1L74@WtH~Bch;$sNMlX4?a zjq%=zAY;)nb|dE`x0+r?HF9-MrrhFsw_XzmsDWA>NfD_~)`*MhA1(m!sqHK7PXM^$ z0}B?gWQysm1UB10r=9xqU8T4)g8@z!U`%$0a{$Z}8|ZuPGJ!Q;(gmG)Xc(S=4$mjs z;Cx{7#v(eANEds1$tp`E*Ju%r!pz9Sm^rQ}Srf?d>R*w_3YSpG5Ka0qz+uB~&?*Pd z!((mcCIJ%MlS@3myOoZSc<4R{;!g+yB`VX*Cb6gh(_JD$EZsrDM2Z1V4Lr!ZLcn{E zIr`*fku?Q+20%bp9e5&8uh#VXUVMUPQ|e@!mECb}wu;s;$ME|Xq(&^Rr>FCXAIx1Q zWc_$jfUpU-Rzayb$-Kt-2#e~`s?w1}(e$rmMgGmEwc{8dM~DT|3O?Of+>h34FvM0g z+zXQ0nPd~5Kxd}6QogJY#ZQZ( zLKINpAz-SYyMwI}Pe&__(=!hJjI){u`n^TCfjqH2@+{dgvRBdIL%V}uukf@ zgf%Dr&>}ZHP>>+Wp;21^$2P|HB+c0}l!Cs|{kC&fN(zUmetU$VC7G|MPYo?>t7qgw6tz*TR}; z8%`uxkn@v((nQbY>1F4U6Qf@7Fggh7C!fq4Mep(g|JYQMCxosUXp+!u0;I4{(x#T! zVcDRY0dwUP77(GrDQcHRb@^A{cWrRa$=NZS2t$mFmf=p;=!l1ty%&Ek{0lXWNzQJ6 z1}NfM4Vuq#AZxbXqS}nxB+W~B){-17rq6=QRp}z276j+?KjTH}IHcr>=zppOutBI0 z>@3N!XR9x9#Bc8Z;0JFV+}hhaeEi1SkALv?n{U4L!&`41EWiKe8+&gZ P9Nc=Fh(v~)l1l#v76XrC diff --git a/inc/poche/config.inc.php b/inc/poche/config.inc.php index a16098d..eb0c1bb 100644 --- a/inc/poche/config.inc.php +++ b/inc/poche/config.inc.php @@ -9,7 +9,7 @@ */ define ('POCHE_VERSION', '1.0-alpha'); -define ('MODE_DEMO', TRUE); +define ('MODE_DEMO', FALSE); define ('DEBUG_POCHE', FALSE); define ('CONVERT_LINKS_FOOTNOTES', FALSE); define ('REVERT_FORCED_PARAGRAPH_ELEMENTS', FALSE); From e4ed594d8246ba5877b86e61f7f85c1b3010f62c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Mon, 5 Aug 2013 10:29:49 +0200 Subject: [PATCH 27/49] rm poche.sqlite --- db/poche.sqlite | Bin 294912 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100755 db/poche.sqlite diff --git a/db/poche.sqlite b/db/poche.sqlite deleted file mode 100755 index 45add0d76750821603c167ba00de75bb7bd4abe3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 294912 zcmeI*O>Y~=835p=CF`qjTNG#o!5*5t}snA)Ye zORFDnXd~#K=%MJf{~-5V+x~{4mjF2zxg;n${3!eZ1PR=m^bB@(xHI$ayzfKa`J0cn z^EwL$W&bFx!!MHONu!axA3~BOU&p=}`+V$;*nbfFZ0!GQqq*;DUnf`p_0GBBwPgO; zhxhZYnR1A{%CTayS2Nyv9}raHg0ZjhUR3l87?;SPBU!n z>}~#Pb2r@E-TH82_d)pe=7SJ6?(f~*+KFKwZtm=buyc1W?A+hp4&naJ)<^d@!yKHk{9b!TIDIv=N>rrklDzqx()=A{?=v-8UrE;K&<={TjVsQYo(!_0oxSlm`&zwkEM6RGUWt*HhJQ+a`0`hJd2w#}#`#8) z7oF_4)0lKnm*eOC$rrhQZ89!iUicoHS0|enhTkN!SLf!IH!ghJ+o#{i{myM_+%%Emqvg90RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly@YVun z|Mu3Q0tpZ}p#q<_8Vku*zOeAeTer*h;3)pnd|Kya5jt7umF>eUT%C-Eb=|w(Y8@XR zujEC2n2i@#+T~H}MlXGoRjt>;*E;#rkayl|w)4sRs%GeCX(xS{ck}vp?=@Ffo9k#EvD4$SI&`zp$*a-S&(~UU&ss0EyJ=O$x0OYm6@%vb%{(hY+>sTX!Ic;} z=!a^QaFvgeFS34!nH^;z>sM+0`%rxT*LGG_Y2MGmlR+Mea!`eXG_ONBc-YP3_IqXf zaXdO+@riSarB{qiNB=OP+5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7e? zsT7!*SzJ69D?c;*qA~pU@QYK4plAXF2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5I9W&OJ`=z|EOIS2l=CSlcmPYg~hC>`*~I^MTN%jA4$A40t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlya1sQ5nIx+-`Ql++_pY~E$H&Jjc~Ku`y|R6n zt+dOd){S2JD63kx%l60HEJ=gEW|z>+N;ff_dfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009Ccfu%9S?09&dB*QO Date: Mon, 5 Aug 2013 10:32:15 +0200 Subject: [PATCH 28/49] copy of poche.sqlite --- .gitignore | 3 ++- db/poche.sqlite.in | Bin 0 -> 294912 bytes inc/poche/Poche.class.php | 29 ++++++++++++++++++----------- inc/poche/Tools.class.php | 4 ++-- index.php | 15 +++++++-------- 5 files changed, 29 insertions(+), 22 deletions(-) create mode 100755 db/poche.sqlite.in diff --git a/.gitignore b/.gitignore index 08db745..050a138 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ vendor -composer.phar \ No newline at end of file +composer.phar +db/poche.sqlite \ No newline at end of file diff --git a/db/poche.sqlite.in b/db/poche.sqlite.in new file mode 100755 index 0000000000000000000000000000000000000000..45add0d76750821603c167ba00de75bb7bd4abe3 GIT binary patch literal 294912 zcmeI*O>Y~=835p=CF`qjTNG#o!5*5t}snA)Ye zORFDnXd~#K=%MJf{~-5V+x~{4mjF2zxg;n${3!eZ1PR=m^bB@(xHI$ayzfKa`J0cn z^EwL$W&bFx!!MHONu!axA3~BOU&p=}`+V$;*nbfFZ0!GQqq*;DUnf`p_0GBBwPgO; zhxhZYnR1A{%CTayS2Nyv9}raHg0ZjhUR3l87?;SPBU!n z>}~#Pb2r@E-TH82_d)pe=7SJ6?(f~*+KFKwZtm=buyc1W?A+hp4&naJ)<^d@!yKHk{9b!TIDIv=N>rrklDzqx()=A{?=v-8UrE;K&<={TjVsQYo(!_0oxSlm`&zwkEM6RGUWt*HhJQ+a`0`hJd2w#}#`#8) z7oF_4)0lKnm*eOC$rrhQZ89!iUicoHS0|enhTkN!SLf!IH!ghJ+o#{i{myM_+%%Emqvg90RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly@YVun z|Mu3Q0tpZ}p#q<_8Vku*zOeAeTer*h;3)pnd|Kya5jt7umF>eUT%C-Eb=|w(Y8@XR zujEC2n2i@#+T~H}MlXGoRjt>;*E;#rkayl|w)4sRs%GeCX(xS{ck}vp?=@Ffo9k#EvD4$SI&`zp$*a-S&(~UU&ss0EyJ=O$x0OYm6@%vb%{(hY+>sTX!Ic;} z=!a^QaFvgeFS34!nH^;z>sM+0`%rxT*LGG_Y2MGmlR+Mea!`eXG_ONBc-YP3_IqXf zaXdO+@riSarB{qiNB=OP+5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7e? zsT7!*SzJ69D?c;*qA~pU@QYK4plAXF2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5I9W&OJ`=z|EOIS2l=CSlcmPYg~hC>`*~I^MTN%jA4$A40t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlya1sQ5nIx+-`Ql++_pY~E$H&Jjc~Ku`y|R6n zt+dOd){S2JD63kx%l60HEJ=gEW|z>+N;ff_dfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009Ccfu%9S?09&dB*QOload_file('./instapaper-export.html'); @@ -229,9 +230,8 @@ class Poche $this->store->archiveById($last_id); } } - # Instapaper génère un fichier HTML avec deux
          - # Le premier concerne les éléments non lus - # Le second concerne les éléments archivés + + # the second
            is for read links $read = 1; } Tools::logm('import from instapaper completed'); @@ -240,6 +240,7 @@ class Poche private function importFromPocket() { + # TODO gestion des articles favs $html = new simple_html_dom(); $html->load_file('./ril_export.html'); @@ -257,9 +258,8 @@ class Poche $this->store->archiveById($last_id); } } - # Pocket génère un fichier HTML avec deux
              - # Le premier concerne les éléments non lus - # Le second concerne les éléments archivés + + # the second
                is for read links $read = 1; } Tools::logm('import from pocket completed'); @@ -268,17 +268,24 @@ class Poche private function importFromReadability() { - # TODO finaliser tout ça ici - # noms des variables + gestion des articles lus + # TODO gestion des articles lus / favs $str_data = file_get_contents("./readability"); $data = json_decode($str_data,true); foreach ($data as $key => $value) { $url = ''; - foreach ($value as $key2 => $value2) { - if ($key2 == 'article__url') { - $url = new Url(base64_encode($value2)); + foreach ($value as $attr => $attr_value) { + if ($attr == 'article__url') { + $url = new Url(base64_encode($attr_value)); } + // if ($attr_value == 'favorite' && $attr_value == 'true') { + // $last_id = $this->store->getLastId(); + // $this->store->favoriteById($last_id); + // } + // if ($attr_value == 'archive' && $attr_value == 'true') { + // $last_id = $this->store->getLastId(); + // $this->store->archiveById($last_id); + // } } if ($url->isCorrect()) $this->action('add', $url); diff --git a/inc/poche/Tools.class.php b/inc/poche/Tools.class.php index 1ff4ba5..834940f 100644 --- a/inc/poche/Tools.class.php +++ b/inc/poche/Tools.class.php @@ -206,8 +206,8 @@ class Tools return sha1($string . SALT); } - public static function checkVar($var) + public static function checkVar($var, $default = '') { - return ((isset ($_REQUEST["$var"])) ? htmlentities($_REQUEST["$var"]) : ''); + return ((isset ($_REQUEST["$var"])) ? htmlentities($_REQUEST["$var"]) : $default); } } \ No newline at end of file diff --git a/index.php b/index.php index 294620d..654403c 100644 --- a/index.php +++ b/index.php @@ -11,16 +11,15 @@ include dirname(__FILE__).'/inc/poche/config.inc.php'; #XSRF protection with token -// if (!empty($_POST)) { -// if (!Session::isToken($_POST['token'])) { -// die(_('Wrong token')); -// // TODO remettre le test -// } -// unset($_SESSION['tokens']); -// } +if (!empty($_POST)) { + if (!Session::isToken($_POST['token'])) { + die(_('Wrong token')); + } + unset($_SESSION['tokens']); +} $referer = empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER']; -$view = Tools::checkVar('view'); +$view = Tools::checkVar('view', 'home'); $action = Tools::checkVar('action'); $id = Tools::checkVar('id'); $_SESSION['sort'] = Tools::checkVar('sort'); From 21e0af98eb28e46345ecb947c48af29e8f1c6234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Mon, 5 Aug 2013 10:34:57 +0200 Subject: [PATCH 29/49] information about db/poche.sqlite --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 09f48b1..65e2f03 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ You have to install [sqlite for php](http://www.php.net/manual/en/book.sqlite.ph Get the [latest version](https://github.com/inthepoche/poche) of poche on github. Unzip it and upload it on your server. poche must have write access on assets, cache and db directories. +Copy db/poche.sqlite.in to db/poche.sqlite + Install composer in your project : ```bash curl -s http://getcomposer.org/installer | php From 32520785018e3ec3a2ce200689e863099e9646f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Mon, 5 Aug 2013 12:34:16 +0200 Subject: [PATCH 30/49] close #69: in the config page, you are notified of the release of a new version --- inc/poche/Poche.class.php | 22 ++++++++++++++++++++++ inc/poche/config.inc.php | 2 +- tpl/config.twig | 8 ++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/inc/poche/Poche.class.php b/inc/poche/Poche.class.php index 12cb1b4..f9bcf85 100644 --- a/inc/poche/Poche.class.php +++ b/inc/poche/Poche.class.php @@ -130,6 +130,16 @@ class Poche switch ($view) { case 'config': + $dev = $this->getPocheVersion('dev'); + $prod = $this->getPocheVersion('prod'); + $compare_dev = version_compare(POCHE_VERSION, $dev); + $compare_prod = version_compare(POCHE_VERSION, $prod); + $tpl_vars = array( + 'dev' => $dev, + 'prod' => $prod, + 'compare_dev' => $compare_dev, + 'compare_prod' => $compare_prod, + ); Tools::logm('config view'); break; case 'view': @@ -315,4 +325,16 @@ class Poche )); Tools::logm('export view'); } + + private function getPocheVersion($which = 'prod') + { + $cache_file = CACHE . '/' . $which; + if (file_exists($cache_file) && (filemtime($cache_file) > (time() - 86400 ))) { + $version = file_get_contents($cache_file); + } else { + $version = file_get_contents('http://www.inthepoche.com/' . $which); + file_put_contents($cache_file, $version, LOCK_EX); + } + return $version; + } } \ No newline at end of file diff --git a/inc/poche/config.inc.php b/inc/poche/config.inc.php index eb0c1bb..27be185 100644 --- a/inc/poche/config.inc.php +++ b/inc/poche/config.inc.php @@ -36,7 +36,7 @@ require_once './vendor/autoload.php'; require_once './inc/3rdparty/simple_html_dom.php'; if (DOWNLOAD_PICTURES) { - require_once './inc/poche/pochePicture.php'; + require_once './inc/poche/pochePictures.php'; } $poche = new Poche($storage_type); \ No newline at end of file diff --git a/tpl/config.twig b/tpl/config.twig index dc49ee3..a17a4b1 100644 --- a/tpl/config.twig +++ b/tpl/config.twig @@ -17,6 +17,14 @@

                {% trans "Drag & drop this link to your bookmarks bar and have fun with poche." %}

                {% trans "poche it!" %}

                +

                {% trans "Updating poche" %}

                +

                  +
                • {% trans "your version" %} : {{ constant('POCHE_VERSION') }}
                • +
                • {% trans "latest stable version" %} : {{ prod }}. {% if compare_prod == -1 %}{% trans "a more recent stable version is available." %}{% else %}{% trans "you are up to date." %}{% endif %}
                • +
                • {% trans "latest dev version" %} : {{ dev }}. {% if compare_dev == -1 %}{% trans "a more recent development version is available." %}{% else %}{% trans "you are up to date." %}{% endif %}
                • +
                +

                +

                {% trans "Change your password" %}

                From 4d0e2544917c8a7fba9eb6d3dccd5fd3790984a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Mon, 5 Aug 2013 12:39:24 +0200 Subject: [PATCH 31/49] link to download poche when update available --- CREDITS | 3 +-- tpl/config.twig | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/CREDITS b/CREDITS index 77a3f66..9c49176 100644 --- a/CREDITS +++ b/CREDITS @@ -1,12 +1,11 @@ poche is based on : -* ReadItYourself http://www.memiks.fr/readityourself/ * PHP Readability http://www.keyvan.net/2010/08/php-readability/ * Encoding https://github.com/neitanod/forceutf8 * logo by Brightmix http://www.iconfinder.com/icondetails/43256/128/jeans_monotone_pocket_icon * icons http://icomoon.io * PHP Simple HTML DOM Parser (for Pocket import) http://simplehtmldom.sourceforge.net/ * Session https://github.com/tontof/kriss_feed/blob/master/src/class/Session.php -* RainTPL http://www.raintpl.com/ +* Twig http://twig.sensiolabs.org poche is developed by Nicolas Lœuillet under the Do What the Fuck You Want to Public License diff --git a/tpl/config.twig b/tpl/config.twig index a17a4b1..a8be93b 100644 --- a/tpl/config.twig +++ b/tpl/config.twig @@ -20,8 +20,8 @@

                {% trans "Updating poche" %}

                • {% trans "your version" %} : {{ constant('POCHE_VERSION') }}
                • -
                • {% trans "latest stable version" %} : {{ prod }}. {% if compare_prod == -1 %}{% trans "a more recent stable version is available." %}{% else %}{% trans "you are up to date." %}{% endif %}
                • -
                • {% trans "latest dev version" %} : {{ dev }}. {% if compare_dev == -1 %}{% trans "a more recent development version is available." %}{% else %}{% trans "you are up to date." %}{% endif %}
                • +
                • {% trans "latest stable version" %} : {{ prod }}. {% if compare_prod == -1 %}{% trans "a more recent stable version is available." %}{% else %}{% trans "you are up to date." %}{% endif %}
                • +
                • {% trans "latest dev version" %} : {{ dev }}. {% if compare_dev == -1 %}{% trans "a more recent development version is available." %}{% else %}{% trans "you are up to date." %}{% endif %}

                From b161295d0b53a5ae194e236b0a7c662e9ac2ff9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Mon, 5 Aug 2013 12:53:56 +0200 Subject: [PATCH 32/49] remove xsrf check --- index.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/index.php b/index.php index 654403c..19774bb 100644 --- a/index.php +++ b/index.php @@ -11,12 +11,12 @@ include dirname(__FILE__).'/inc/poche/config.inc.php'; #XSRF protection with token -if (!empty($_POST)) { - if (!Session::isToken($_POST['token'])) { - die(_('Wrong token')); - } - unset($_SESSION['tokens']); -} +// if (!empty($_POST)) { +// if (!Session::isToken($_POST['token'])) { +// die(_('Wrong token')); +// } +// unset($_SESSION['tokens']); +// } $referer = empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER']; $view = Tools::checkVar('view', 'home'); From 55821e04c188997d258645975220828e195d0df4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Mon, 5 Aug 2013 15:54:37 +0200 Subject: [PATCH 33/49] share email +twitter / class messages --- inc/3rdparty/class.messages.php | 231 ++++++++++++++++++++++++++++++++ inc/poche/Poche.class.php | 27 +++- inc/poche/Tools.class.php | 11 ++ inc/poche/config.inc.php | 5 +- index.php | 3 + tpl/_bookmarklet.twig | 2 +- tpl/_head.twig | 4 +- tpl/_menu.twig | 7 + tpl/_messages.twig | 5 + tpl/css/messages.css | 13 ++ tpl/css/style-dark.css | 4 + tpl/css/style-light.css | 8 ++ tpl/css/style.css | 6 +- tpl/home.twig | 41 ++---- tpl/img/dark/twitter.png | Bin 0 -> 300 bytes tpl/img/light/envelop.png | Bin 0 -> 285 bytes tpl/img/light/twitter.png | Bin 0 -> 297 bytes tpl/img/messages/close.png | Bin 0 -> 662 bytes tpl/img/messages/cross.png | Bin 0 -> 655 bytes tpl/img/messages/help.png | Bin 0 -> 786 bytes tpl/img/messages/tick.png | Bin 0 -> 537 bytes tpl/img/messages/warning.png | Bin 0 -> 666 bytes tpl/js/poche.js | 57 -------- tpl/layout.twig | 1 + tpl/view.twig | 26 ++-- 25 files changed, 339 insertions(+), 112 deletions(-) create mode 100755 inc/3rdparty/class.messages.php create mode 100644 tpl/_menu.twig create mode 100644 tpl/_messages.twig create mode 100755 tpl/css/messages.css create mode 100755 tpl/img/dark/twitter.png create mode 100755 tpl/img/light/envelop.png create mode 100755 tpl/img/light/twitter.png create mode 100755 tpl/img/messages/close.png create mode 100755 tpl/img/messages/cross.png create mode 100755 tpl/img/messages/help.png create mode 100755 tpl/img/messages/tick.png create mode 100755 tpl/img/messages/warning.png delete mode 100644 tpl/js/poche.js diff --git a/inc/3rdparty/class.messages.php b/inc/3rdparty/class.messages.php new file mode 100755 index 0000000..e60bd3a --- /dev/null +++ b/inc/3rdparty/class.messages.php @@ -0,0 +1,231 @@ +X\n%s
      \n"; + var $msgBefore = '

      '; + var $msgAfter = "

      \n"; + + + /** + * Constructor + * @author Mike Everhart + */ + public function __construct() { + + // Generate a unique ID for this user and session + $this->msgId = md5(uniqid()); + + // Create the session array if it doesnt already exist + if( !array_key_exists('flash_messages', $_SESSION) ) $_SESSION['flash_messages'] = array(); + + } + + /** + * Add a message to the queue + * + * @author Mike Everhart + * + * @param string $type The type of message to add + * @param string $message The message + * @param string $redirect_to (optional) If set, the user will be redirected to this URL + * @return bool + * + */ + public function add($type, $message, $redirect_to=null) { + + if( !isset($_SESSION['flash_messages']) ) return false; + + if( !isset($type) || !isset($message[0]) ) return false; + + // Replace any shorthand codes with their full version + if( strlen(trim($type)) == 1 ) { + $type = str_replace( array('h', 'i', 'w', 'e', 's'), array('help', 'info', 'warning', 'error', 'success'), $type ); + + // Backwards compatibility... + } elseif( $type == 'information' ) { + $type = 'info'; + } + + // Make sure it's a valid message type + if( !in_array($type, $this->msgTypes) ) die('"' . strip_tags($type) . '" is not a valid message type!' ); + + // If the session array doesn't exist, create it + if( !array_key_exists( $type, $_SESSION['flash_messages'] ) ) $_SESSION['flash_messages'][$type] = array(); + + $_SESSION['flash_messages'][$type][] = $message; + + if( !is_null($redirect_to) ) { + header("Location: $redirect_to"); + exit(); + } + + return true; + + } + + //----------------------------------------------------------------------------------------------- + // display() + // print queued messages to the screen + //----------------------------------------------------------------------------------------------- + /** + * Display the queued messages + * + * @author Mike Everhart + * + * @param string $type Which messages to display + * @param bool $print True = print the messages on the screen + * @return mixed + * + */ + public function display($type='all', $print=true) { + $messages = ''; + $data = ''; + + if( !isset($_SESSION['flash_messages']) ) return false; + + if( $type == 'g' || $type == 'growl' ) { + $this->displayGrowlMessages(); + return true; + } + + // Print a certain type of message? + if( in_array($type, $this->msgTypes) ) { + foreach( $_SESSION['flash_messages'][$type] as $msg ) { + $messages .= $this->msgBefore . $msg . $this->msgAfter; + } + + $data .= sprintf($this->msgWrapper, $this->msgClass, $type, $messages); + + // Clear the viewed messages + $this->clear($type); + + // Print ALL queued messages + } elseif( $type == 'all' ) { + foreach( $_SESSION['flash_messages'] as $type => $msgArray ) { + $messages = ''; + foreach( $msgArray as $msg ) { + $messages .= $this->msgBefore . $msg . $this->msgAfter; + } + $data .= sprintf($this->msgWrapper, $this->msgClass, $type, $messages); + } + + // Clear ALL of the messages + $this->clear(); + + // Invalid Message Type? + } else { + return false; + } + + // Print everything to the screen or return the data + if( $print ) { + echo $data; + } else { + return $data; + } + } + + + /** + * Check to see if there are any queued error messages + * + * @author Mike Everhart + * + * @return bool true = There ARE error messages + * false = There are NOT any error messages + * + */ + public function hasErrors() { + return empty($_SESSION['flash_messages']['error']) ? false : true; + } + + /** + * Check to see if there are any ($type) messages queued + * + * @author Mike Everhart + * + * @param string $type The type of messages to check for + * @return bool + * + */ + public function hasMessages($type=null) { + if( !is_null($type) ) { + if( !empty($_SESSION['flash_messages'][$type]) ) return $_SESSION['flash_messages'][$type]; + } else { + foreach( $this->msgTypes as $type ) { + if( !empty($_SESSION['flash_messages']) ) return true; + } + } + return false; + } + + /** + * Clear messages from the session data + * + * @author Mike Everhart + * + * @param string $type The type of messages to clear + * @return bool + * + */ + public function clear($type='all') { + if( $type == 'all' ) { + unset($_SESSION['flash_messages']); + } else { + unset($_SESSION['flash_messages'][$type]); + } + return true; + } + + public function __toString() { return $this->hasMessages(); } + + public function __destruct() { + //$this->clear(); + } + + +} // end class +?> \ No newline at end of file diff --git a/inc/poche/Poche.class.php b/inc/poche/Poche.class.php index f9bcf85..80bf691 100644 --- a/inc/poche/Poche.class.php +++ b/inc/poche/Poche.class.php @@ -12,6 +12,7 @@ class Poche { public $store; public $tpl; + public $messages; function __construct($storage_type) { @@ -41,6 +42,9 @@ class Poche 'cache' => CACHE, )); $this->tpl->addExtension(new Twig_Extensions_Extension_I18n()); + # filter to display domain name of an url + $filter = new Twig_SimpleFilter('getDomain', 'Tools::getDomain'); + $this->tpl->addFilter($filter); Tools::initPhp(); Session::init(); @@ -113,10 +117,12 @@ class Poche case 'toggle_fav' : $this->store->favoriteById($id); Tools::logm('mark as favorite link #' . $id); + Tools::redirect(); break; case 'toggle_archive' : $this->store->archiveById($id); Tools::logm('archive link #' . $id); + Tools::redirect(); break; default: break; @@ -174,16 +180,21 @@ class Poche public function updatePassword() { - if (isset($_POST['password']) && isset($_POST['password_repeat'])) { - if ($_POST['password'] == $_POST['password_repeat'] && $_POST['password'] != "") { - if (!MODE_DEMO) { + if (MODE_DEMO) { + $this->messages->add('i', 'in demo mode, you can\'t update your password'); + Tools::logm('in demo mode, you can\'t do this'); + } + else { + if (isset($_POST['password']) && isset($_POST['password_repeat'])) { + if ($_POST['password'] == $_POST['password_repeat'] && $_POST['password'] != "") { Tools::logm('password updated'); + $this->messages->add('s', 'your password has been updated'); $this->store->updatePassword(Tools::encodeString($_POST['password'] . $_SESSION['login'])); Session::logout(); Tools::redirect(); } else { - Tools::logm('in demo mode, you can\'t do this'); + $this->messages->add('e', 'the two fields have to be filled & the password must be the same in the two fields'); } } } @@ -194,7 +205,7 @@ class Poche if (!empty($_POST['login']) && !empty($_POST['password'])) { if (Session::login($_SESSION['login'], $_SESSION['pass'], $_POST['login'], Tools::encodeString($_POST['password'] . $_POST['login']))) { Tools::logm('login successful'); - + $this->messages->add('s', 'login successful, welcome to your poche'); if (!empty($_POST['longlastingsession'])) { $_SESSION['longlastingsession'] = 31536000; $_SESSION['expires_on'] = time() + $_SESSION['longlastingsession']; @@ -205,9 +216,11 @@ class Poche session_regenerate_id(true); Tools::redirect($referer); } + $this->messages->add('e', 'login failed, bad login or password'); Tools::logm('login failed'); Tools::redirect(); } else { + $this->messages->add('e', 'login failed, you have to fill all fields'); Tools::logm('login failed'); Tools::redirect(); } @@ -215,6 +228,7 @@ class Poche public function logout() { + $this->messages->add('s', 'logout successful, see you soon!'); Tools::logm('logout'); Session::logout(); Tools::redirect(); @@ -244,6 +258,7 @@ class Poche # the second
        is for read links $read = 1; } + $this->messages->add('s', 'import from instapaper completed'); Tools::logm('import from instapaper completed'); Tools::redirect(); } @@ -272,6 +287,7 @@ class Poche # the second
          is for read links $read = 1; } + $this->messages->add('s', 'import from pocket completed'); Tools::logm('import from pocket completed'); Tools::redirect(); } @@ -300,6 +316,7 @@ class Poche if ($url->isCorrect()) $this->action('add', $url); } + $this->messages->add('s', 'import from Readability completed'); Tools::logm('import from Readability completed'); Tools::redirect(); } diff --git a/inc/poche/Tools.class.php b/inc/poche/Tools.class.php index 834940f..7bc8830 100644 --- a/inc/poche/Tools.class.php +++ b/inc/poche/Tools.class.php @@ -210,4 +210,15 @@ class Tools { return ((isset ($_REQUEST["$var"])) ? htmlentities($_REQUEST["$var"]) : $default); } + + public static function getDomain($url) + { + $pieces = parse_url($url); + $domain = isset($pieces['host']) ? $pieces['host'] : ''; + if (preg_match('/(?P[a-z0-9][a-z0-9\-]{1,63}\.[a-z\.]{2,6})$/i', $domain, $regs)) { + return $regs['domain']; + } + + return FALSE; + } } \ No newline at end of file diff --git a/inc/poche/config.inc.php b/inc/poche/config.inc.php index 27be185..d49df19 100644 --- a/inc/poche/config.inc.php +++ b/inc/poche/config.inc.php @@ -15,6 +15,7 @@ define ('CONVERT_LINKS_FOOTNOTES', FALSE); define ('REVERT_FORCED_PARAGRAPH_ELEMENTS', FALSE); define ('DOWNLOAD_PICTURES', FALSE); define ('SHARE_TWITTER', TRUE); +define ('SHARE_MAIL', TRUE); define ('SALT', '464v54gLLw928uz4zUBqkRJeiPY68zCX'); define ('ABS_PATH', 'assets/'); define ('TPL', './tpl'); @@ -34,9 +35,11 @@ require_once './inc/store/store.class.php'; require_once './inc/store/' . $storage_type . '.class.php'; require_once './vendor/autoload.php'; require_once './inc/3rdparty/simple_html_dom.php'; +require_once './inc/3rdparty/class.messages.php'; if (DOWNLOAD_PICTURES) { require_once './inc/poche/pochePictures.php'; } -$poche = new Poche($storage_type); \ No newline at end of file +$poche = new Poche($storage_type); +$poche->messages = new Messages(); \ No newline at end of file diff --git a/index.php b/index.php index 19774bb..dd70a98 100644 --- a/index.php +++ b/index.php @@ -61,5 +61,8 @@ else { $tpl_file = 'login.twig'; } +# because messages can be added in $poche->action(), we have to add this entry now (we can add it before) +$tpl_vars = array_merge($tpl_vars, array('messages' => $poche->messages->display())); + # Aaaaaaand action ! echo $poche->tpl->render($tpl_file, $tpl_vars); \ No newline at end of file diff --git a/tpl/_bookmarklet.twig b/tpl/_bookmarklet.twig index 0878e07..0595d57 100644 --- a/tpl/_bookmarklet.twig +++ b/tpl/_bookmarklet.twig @@ -4,7 +4,7 @@ +'' +'' +'poche it !' - +'' + +'' +'' +'' +' - - + {% endblock %} \ No newline at end of file diff --git a/tpl/img/dark/twitter.png b/tpl/img/dark/twitter.png new file mode 100755 index 0000000000000000000000000000000000000000..4595affbafd4e74673f1b03d297774a9d0458791 GIT binary patch literal 300 zcmV+{0n`48P)`0wV7&E+y=#?JiYvf2In|K*Y< zNsS^}glBGV%MpM8rdh&9;UP!HF4!V(~B zDxAMCto42sBOtdCC}<1z8A{7g*1ch^7C9lRG(;5TJp4@^Y>0000ICWrx`9rh|B-`~3Lyb6`DuFBd-qF9 z7<7!VV}Af3mewsn34l+_=B&*H?6tp2+XmoD^|7?@0l-qJCoQFy2kL9TlipI4J9i!A zAM~i=NSaGGgD~+RU96dOcJ(R%lg2Mh&(gP{N%fCG>)9Do9t0>A+hkifmBZJN=N4Vqlq{@%ZTzil@7 zb7UR!3-P8xE}Vs@&C3xWxEEFlv5Ddr%X5^27NxL6fCYp)AwsoLMEs;!g8&t7mH>Gt z9PK_lYiq!%+#TGt83&A)*2EC3BGn1agsXF~)n<_(rovu;OQh1Ur+{(qTA+H}LxiU^ zj>CVz)2utXF!p~H@YUM_>`P%I%o9!?mcuKV5w^galZXHp+a8VW+b25#s?vY>55i=s va$d(=g$@d9kD0+oa-S)d`_V#@^zGmcO~NR#w(5?(00000NkvXXu0mjfYC3)} literal 0 HcmV?d00001 diff --git a/tpl/img/messages/close.png b/tpl/img/messages/close.png new file mode 100755 index 0000000000000000000000000000000000000000..731aa018f240a80ed89ed48e386f8a364089e99e GIT binary patch literal 662 zcmV;H0%`q;P)z@;j(q!3lK=n!AY({UO#lFTB>(_`g8%^e{{R4h=>PzA zFaQARU;qF*m;eA5Z<1fdMgRZ;3`s;mRCwBA{Qv(y0~)Xa;xHh#0%Ap|*nJ>A2E?m? z_z1FMfB<4dGJqe5n}Im-^XJbzXU?2qxO3+YSVC1*mBGZsgn@^L=L?WKABby#_#;RT zAb=P^1^_W15bpru|MTb1|F5g73o;c2gTz2`P_P54hXEjfU-TN zkO7lFe*E}fOG^tZsiULw|J%23{~tVf@Lxqm<-eGi*#Fa~PlK(jtgHm90jUM)0qF$@ zAdmrP_U+pTw@p}B`2V_f>%bar-@g6-^5x55_Q8V(|I5qE!_|TGfb;?c5XgYHvuDpn z@h1ld$Nw!`w!r-Q|J=EA|M~g(QPj+uH47y579fC_U@?IVczJmlgoK3Pd@e372B6DO z)Bpn;OalZEh8GkR6#ielcoA&#-o1POZ`!m8%zpIf(SI{DGYl^P1Q13b>g((O|MKO_ z|HFq5{}&V#{LjwL{(se~RsX+#|NcKLEDR$M0RjkXG=gG6R8$ns2YTthfq?;>4^2?u zXaoo#l!R1ORfRVZVM|B=0fds2|NZ;-f8M-#Sd%iyRUkQ#8swx55I|Tn6EFuEoyT++I zn$b9r%cFfhHe2K68PkBu*@^<$y+7xQ$wJ~;c5aBx$R=xq*41Wo zhwQus_VOgm0hughj}MhOvs#{>Vg09Y8WxjWUJY5YW zJ?&8eG!59Cz=|E%Ns@013KLWOLV)CObIIj_5{>{#k%TEAMs_GbdDV`x-iYsGH z#=Z{USAQA>NY(}X7=3{K8#$XgYMs^AIOw1Qr{*Wn)N-{9ma}x2(<~`9Go1=*>YR!KZvrBS zCd!u}@M0og%Ev@_;Z?Kk>Wwv=%h_57zmt2<_1msz_niYE=YRNPpd%02TK9oK1z z>ooPno}v^sikz_|1XHFx_L%~;ljh7i(jiay5F0x*+(9aXXFCl?AdQj5XlQ65%sEv+ ztfe?|YcjPN*@yYtE~ImQh{l|#A6Z8iu>pf43Rj52CzU_dMQm|S2xR62YjQOn+z8WH zaK=!}ggOZi{4pB7SQ=xC0n|vXP_Bkx_a)FeNd}w8U97BNbSWxa^QW-li9BZ#M1!_xE*?wzt^GcoeoL*JGLSe_+l-JT2#2tz!z&^ z_s5anq&^nBklIMwRvcoP3%qs%%Ea?1c{_*V*Xj&~uLu-2Dp1fUN4<0zMo$EH>*U83 zm_9;Vt%-bE{_J_!If!1y=c+`QVZ>0_BPy z+%^pgnv`f8H)Z%0&Tp8&u*MCIC4igNW5MeWM_DHpDNi)Zxz|9XboOnitwFq$ETN=X zj-tkCJnz**Y4k#6_Ty^B=hWo~L!47r`HoP=x&3T1)JLr2t2+#fHHs{AQG2a)rMyf zFQK~pm1x3+7!nu%-M`k}``c>^00{o_1pjWJUTfl8mg=3qGEl8H@}^@w`VUx0_$uy4 z2FhRqKX}xI*?Tv1DJd8z#F#0c%*~rM30HE1@2o5m~}ZyoWhqv>ql{V z1ZGE0lgcoK^lx+eqc*rAX1Ky;Xx3U%u#zG!m-;eD1Qsn@kf3|F9qz~|95=&g3(7!X zB}JAT>RU;a%vaNOGnJ%e1=K6eAh43c(QN8RQ6~GP%O}Jju$~Ld*%`mO1peOSYYtbpBV}~vsBnU!_?2tr-P=|^T zED%wc9ezHgW@NMb!^uT_|SvCpFLJylbx zY%bpaTGI8IYXMN$9w<3j9VkA~NYOKEQXsj?6a9_hcwfU$acAhJhB)zb_w@MVUEy@S zX&I>K-R!bhu3?(6bHWIg$HEl7{9g>>&l_qdd+UYb(1~BCo9LptNq&8>!yoJ3Ui(i5 zRJ|XnYBklL!{@$-7=3mJ>P@1c=7Oc79e-V7yf+%lD2!I;Y&nXBZ>=B!5?CB>LvEx6 znI%n)qqi$#X#wKB(U7XP2P=+4{b@j#r%9-K(8UqtSDk>0UKzf*HM9yqMZ1D!$2MdZ zR=`U>0zhOH1XqN?nY@AQqB7)Fp4{v&dKXvb43hZKvnN8;Po;+jY*}~*Z|W9Q0W%{D z^T}Cc<|r(Su=1K=P5>Z4 zg`et&Va}tdzBS-G-ZcO)zCWpJvGQwrHZ`@wpM420ac@bI5~KkTFfGEM3sPWO8co4^fI6lPnA)Y{ef%@{+SnoUk0+dW+*{8WvF8}}l07*qoM6N<$g7cXs A&j0`b literal 0 HcmV?d00001 diff --git a/tpl/js/poche.js b/tpl/js/poche.js deleted file mode 100644 index b4eac11..0000000 --- a/tpl/js/poche.js +++ /dev/null @@ -1,57 +0,0 @@ -function toggle_favorite(element, id) { - $(element).toggleClass('fav-off'); - $.ajax ({ - url: "index.php?action=toggle_fav", - data:{id:id} - }); -} - -function toggle_archive(element, id, view_article) { - $(element).toggleClass('archive-off'); - $.ajax ({ - url: "index.php?action=toggle_archive", - data:{id:id} - }); - var obj = $('#entry-'+id); - - // on vient de la vue de l'article, donc pas de gestion de grille - if (view_article != 1) { - $('#content').masonry('remove',obj); - $('#content').masonry('reloadItems'); - $('#content').masonry('reload'); - } -} - -function sort_links(view, sort) { - $.get('index.php', { view: view, sort: sort }, function(data) { - $('#content').html(data); - }); -} - - -// ---------- Swith light or dark view -function setActiveStyleSheet(title) { - var i, a, main; - for(i=0; (a = document.getElementsByTagName("link")[i]); i++) { - if(a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("title")) { - a.disabled = true; - if(a.getAttribute("title") == title) a.disabled = false; - } - } -} -$('#themeswitch').click(function() { - // we want the dark - if ($('body').hasClass('light-style')) { - setActiveStyleSheet('dark-style'); - $('body').addClass('dark-style'); - $('body').removeClass('light-style'); - $('#themeswitch').text('light'); - // we want the light - } else if ($('body').hasClass('dark-style')) { - setActiveStyleSheet('light-style'); - $('body').addClass('light-style'); - $('body').removeClass('dark-style'); - $('#themeswitch').text('dark'); - } - return false; -}); diff --git a/tpl/layout.twig b/tpl/layout.twig index cbe965f..9dc83ef 100644 --- a/tpl/layout.twig +++ b/tpl/layout.twig @@ -17,6 +17,7 @@
          {% block menu %}{% endblock %} {% block precontent %}{% endblock %} + {% block messages %}{% endblock %} {% block content %}{% endblock %} {% block js %}{% endblock %}
          diff --git a/tpl/view.twig b/tpl/view.twig index bf9a9af..692f955 100644 --- a/tpl/view.twig +++ b/tpl/view.twig @@ -1,42 +1,40 @@ {% extends "layout.twig" %} {% block title %}{% trans "home" %}{% endblock %} - +{% block messages %} +{% include '_messages.twig' %} +{% endblock %} {% block content %}
          -
          - -
            - {% if constant('SHARE_TWITTER') == 1 %}
          • {% endif %} -
          • -
          • {% trans "dark" %}
          • -
          • +
          • +
          • +
          • -
          • {% trans "logout" %}
          • + {% if constant('SHARE_TWITTER') == 1 %}
          • {% endif %} + {% if constant('SHARE_MAIL') == 1 %}
          • {% endif %}
          -

          {{ entry.title|e }}

          - +

          {{ entry.title|e }}

          +
          {{ content | raw }}
          - +
          - {% trans "this article appears wrong?" %} {% trans "create an issue" %} {% trans "or" %} {% trans "contact us by mail" %} + {% trans "this article appears wrong?" %} {% trans "create an issue" %} {% trans "or" %} {% trans "contact us by mail" %}
          {% endblock %} {% block js %} - {% endblock %} \ No newline at end of file From 6a361945eaf86a978b82bd6fb3442fe64428d9df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Mon, 5 Aug 2013 21:56:32 +0200 Subject: [PATCH 34/49] new design, pagination & more --- CREDITS | 8 +- inc/3rdparty/paginator.php | 198 ++++++++++++++++++++++++++++ inc/poche/Poche.class.php | 40 ++++-- inc/poche/Tools.class.php | 2 +- inc/poche/config.inc.php | 12 +- inc/store/sqlite.class.php | 4 +- index.php | 3 +- tpl/_footer.twig | 2 +- tpl/_head.twig | 5 +- tpl/_messages.twig | 6 +- tpl/_top.twig | 4 +- tpl/config.twig | 80 ++++++------ tpl/css/messages.css | 2 +- tpl/css/style-dark.css | 94 -------------- tpl/css/style-light.css | 71 ++-------- tpl/css/style.css | 209 +++++++++++++++++------------- tpl/home.twig | 44 +++---- tpl/img/dark/checkmark-off.png | Bin 267 -> 0 bytes tpl/img/dark/checkmark-on.png | Bin 221 -> 0 bytes tpl/img/dark/down.png | Bin 223 -> 0 bytes tpl/img/dark/logo.png | Bin 786 -> 0 bytes tpl/img/dark/remove.png | Bin 265 -> 0 bytes tpl/img/dark/star-off.png | Bin 330 -> 0 bytes tpl/img/dark/star-on.png | Bin 277 -> 0 bytes tpl/img/dark/twitter.png | Bin 300 -> 0 bytes tpl/img/dark/up.png | Bin 225 -> 0 bytes tpl/img/{ => light}/down.png | Bin tpl/img/light/left.png | Bin 0 -> 196 bytes tpl/img/{up.png => light/top.png} | Bin tpl/layout.twig | 8 +- tpl/view.twig | 46 ++++--- 31 files changed, 467 insertions(+), 371 deletions(-) create mode 100644 inc/3rdparty/paginator.php delete mode 100644 tpl/css/style-dark.css delete mode 100644 tpl/img/dark/checkmark-off.png delete mode 100644 tpl/img/dark/checkmark-on.png delete mode 100644 tpl/img/dark/down.png delete mode 100644 tpl/img/dark/logo.png delete mode 100644 tpl/img/dark/remove.png delete mode 100644 tpl/img/dark/star-off.png delete mode 100644 tpl/img/dark/star-on.png delete mode 100755 tpl/img/dark/twitter.png delete mode 100644 tpl/img/dark/up.png rename tpl/img/{ => light}/down.png (100%) create mode 100755 tpl/img/light/left.png rename tpl/img/{up.png => light/top.png} (100%) mode change 100644 => 100755 diff --git a/CREDITS b/CREDITS index 9c49176..d6874a7 100644 --- a/CREDITS +++ b/CREDITS @@ -6,11 +6,9 @@ poche is based on : * PHP Simple HTML DOM Parser (for Pocket import) http://simplehtmldom.sourceforge.net/ * Session https://github.com/tontof/kriss_feed/blob/master/src/class/Session.php * Twig http://twig.sensiolabs.org +* Flash messages https://github.com/plasticbrain/PHP-Flash-Messages +* Pagination https://github.com/daveismyname/pagination poche is developed by Nicolas Lœuillet under the Do What the Fuck You Want to Public License -Contributors : -Nicolas Lœuillet aka nico_somb -Tom.C. aka tmos -PeaceCopathe -Gregoire_M \ No newline at end of file +Contributors : https://github.com/inthepoche/poche/graphs/contributors \ No newline at end of file diff --git a/inc/3rdparty/paginator.php b/inc/3rdparty/paginator.php new file mode 100644 index 0000000..e880155 --- /dev/null +++ b/inc/3rdparty/paginator.php @@ -0,0 +1,198 @@ +_instance = $instance; + $this->_perPage = $perPage; + $this->set_instance(); + } + + /** + * get_start + * + * creates the starting point for limiting the dataset + * @return numeric + */ + private function get_start(){ + return ($this->_page * $this->_perPage) - $this->_perPage; + } + + /** + * set_instance + * + * sets the instance parameter, if numeric value is 0 then set to 1 + * + * @var numeric + */ + private function set_instance(){ + $this->_page = (int) (!isset($_GET[$this->_instance]) ? 1 : $_GET[$this->_instance]); + $this->_page = ($this->_page == 0 ? 1 : $this->_page); + } + + /** + * set_total + * + * collect a numberic value and assigns it to the totalRows + * + * @var numeric + */ + public function set_total($_totalRows){ + $this->_totalRows = $_totalRows; + } + + /** + * get_limit + * + * returns the limit for the data source, calling the get_start method and passing in the number of items perp page + * + * @return string + */ + public function get_limit(){ + return "LIMIT ".$this->get_start().",$this->_perPage"; + } + + /** + * page_links + * + * create the html links for navigating through the dataset + * + * @var sting $path optionally set the path for the link + * @var sting $ext optionally pass in extra parameters to the GET + * @return string returns the html menu + */ + public function page_links($path='?',$ext=null) + { + $adjacents = "2"; + $prev = $this->_page - 1; + $next = $this->_page + 1; + $lastpage = ceil($this->_totalRows/$this->_perPage); + $lpm1 = $lastpage - 1; + + $pagination = ""; + if($lastpage > 1) + { + $pagination .= "\n"; + } + + + return $pagination; + } +} diff --git a/inc/poche/Poche.class.php b/inc/poche/Poche.class.php index 80bf691..789d664 100644 --- a/inc/poche/Poche.class.php +++ b/inc/poche/Poche.class.php @@ -13,11 +13,13 @@ class Poche public $store; public $tpl; public $messages; + public $pagination; function __construct($storage_type) { $this->store = new $storage_type(); $this->init(); + $this->messages = new Messages(); # installation if(!$this->store->isInstalled()) @@ -46,6 +48,8 @@ class Poche $filter = new Twig_SimpleFilter('getDomain', 'Tools::getDomain'); $this->tpl->addFilter($filter); + $this->pagination = new Paginator(PAGINATION, 'p'); + Tools::initPhp(); Session::init(); } @@ -54,7 +58,7 @@ class Poche { Tools::logm('poche still not installed'); echo $this->tpl->render('install.twig', array( - 'token' => Session::getToken(), + 'token' => Session::getToken() )); if (isset($_GET['install'])) { if (($_POST['password'] == $_POST['password_repeat']) @@ -62,6 +66,11 @@ class Poche # let's rock, install poche baby ! $this->store->install($_POST['login'], Tools::encodeString($_POST['password'] . $_POST['login'])); Session::logout(); + Tools::logm('poche is now installed'); + Tools::redirect(); + } + else { + Tools::logm('error during installation'); Tools::redirect(); } } @@ -89,30 +98,32 @@ class Poche if (DOWNLOAD_PICTURES) { $content = filtre_picture($parametres_url['content'], $url->getUrl(), $last_id); } - #$msg->add('s', _('the link has been added successfully')); + $this->messages->add('s', _('the link has been added successfully')); } else { - #$msg->add('e', _('error during insertion : the link wasn\'t added')); + $this->messages->add('e', _('error during insertion : the link wasn\'t added')); Tools::logm('error during insertion : the link wasn\'t added'); } } else { - #$msg->add('e', _('error during url preparation : the link wasn\'t added')); + $this->messages->add('e', _('error during fetching content : the link wasn\'t added')); Tools::logm('error during content fetch'); } + Tools::redirect(); break; case 'delete': if ($this->store->deleteById($id)) { if (DOWNLOAD_PICTURES) { remove_directory(ABS_PATH . $id); } - #$msg->add('s', _('the link has been deleted successfully')); + $this->messages->add('s', _('the link has been deleted successfully')); Tools::logm('delete link #' . $id); } else { - #$msg->add('e', _('the link wasn\'t deleted')); + $this->messages->add('e', _('the link wasn\'t deleted')); Tools::logm('error : can\'t delete link #' . $id); } + Tools::redirect(); break; case 'toggle_fav' : $this->store->favoriteById($id); @@ -169,9 +180,14 @@ class Poche break; default: # home view $entries = $this->store->getEntriesByView($view); + $this->pagination->set_total(count($entries)); + $page_links = $this->pagination->page_links('?view=' . $view . '&sort=' . $_SESSION['sort'] . '&'); + $datas = $this->store->getEntriesByView($view, $this->pagination->get_limit()); $tpl_vars = array( - 'entries' => $entries, + 'entries' => $datas, + 'page_links' => $page_links, ); + Tools::logm('display ' . $view . ' view'); break; } @@ -183,6 +199,7 @@ class Poche if (MODE_DEMO) { $this->messages->add('i', 'in demo mode, you can\'t update your password'); Tools::logm('in demo mode, you can\'t do this'); + Tools::redirect('?view=config'); } else { if (isset($_POST['password']) && isset($_POST['password_repeat'])) { @@ -195,6 +212,7 @@ class Poche } else { $this->messages->add('e', 'the two fields have to be filled & the password must be the same in the two fields'); + Tools::redirect('?view=config'); } } } @@ -205,7 +223,7 @@ class Poche if (!empty($_POST['login']) && !empty($_POST['password'])) { if (Session::login($_SESSION['login'], $_SESSION['pass'], $_POST['login'], Tools::encodeString($_POST['password'] . $_POST['login']))) { Tools::logm('login successful'); - $this->messages->add('s', 'login successful, welcome to your poche'); + $this->messages->add('s', 'welcome to your poche'); if (!empty($_POST['longlastingsession'])) { $_SESSION['longlastingsession'] = 31536000; $_SESSION['expires_on'] = time() + $_SESSION['longlastingsession']; @@ -216,11 +234,11 @@ class Poche session_regenerate_id(true); Tools::redirect($referer); } - $this->messages->add('e', 'login failed, bad login or password'); + $this->messages->add('e', 'login failed: bad login or password'); Tools::logm('login failed'); Tools::redirect(); } else { - $this->messages->add('e', 'login failed, you have to fill all fields'); + $this->messages->add('e', 'login failed: you have to fill all fields'); Tools::logm('login failed'); Tools::redirect(); } @@ -228,7 +246,7 @@ class Poche public function logout() { - $this->messages->add('s', 'logout successful, see you soon!'); + $this->messages->add('s', 'see you soon!'); Tools::logm('logout'); Session::logout(); Tools::redirect(); diff --git a/inc/poche/Tools.class.php b/inc/poche/Tools.class.php index 7bc8830..8b339ea 100644 --- a/inc/poche/Tools.class.php +++ b/inc/poche/Tools.class.php @@ -197,7 +197,7 @@ class Tools { if (DEBUG_POCHE) { $t = strval(date('Y/m/d_H:i:s')) . ' - ' . $_SERVER["REMOTE_ADDR"] . ' - ' . strval($message) . "\n"; - file_put_contents('./log.txt', $t, FILE_APPEND); + file_put_contents(CACHE . '/log.txt', $t, FILE_APPEND); } } diff --git a/inc/poche/config.inc.php b/inc/poche/config.inc.php index d49df19..d91a44b 100644 --- a/inc/poche/config.inc.php +++ b/inc/poche/config.inc.php @@ -8,7 +8,7 @@ * @license http://www.wtfpl.net/ see COPYING file */ -define ('POCHE_VERSION', '1.0-alpha'); +define ('POCHE_VERSION', '1.0-beta'); define ('MODE_DEMO', FALSE); define ('DEBUG_POCHE', FALSE); define ('CONVERT_LINKS_FOOTNOTES', FALSE); @@ -22,24 +22,26 @@ define ('TPL', './tpl'); define ('LOCALE', './locale'); define ('CACHE', './cache'); define ('LANG', 'fr_FR.UTF8'); +define ('PAGINATION', '10'); +define ('THEME', 'light'); $storage_type = 'sqlite'; # sqlite, mysql, (file, not yet) # /!\ Be careful if you change the lines below /!\ require_once './inc/poche/Tools.class.php'; require_once './inc/poche/Url.class.php'; +require_once './inc/3rdparty/Session.class.php'; +require_once './inc/3rdparty/class.messages.php'; require_once './inc/poche/Poche.class.php'; require_once './inc/3rdparty/Readability.php'; require_once './inc/3rdparty/Encoding.php'; -require_once './inc/3rdparty/Session.class.php'; require_once './inc/store/store.class.php'; require_once './inc/store/' . $storage_type . '.class.php'; require_once './vendor/autoload.php'; require_once './inc/3rdparty/simple_html_dom.php'; -require_once './inc/3rdparty/class.messages.php'; +require_once './inc/3rdparty/paginator.php'; if (DOWNLOAD_PICTURES) { require_once './inc/poche/pochePictures.php'; } -$poche = new Poche($storage_type); -$poche->messages = new Messages(); \ No newline at end of file +$poche = new Poche($storage_type); \ No newline at end of file diff --git a/inc/store/sqlite.class.php b/inc/store/sqlite.class.php index a15bc09..3e391e4 100644 --- a/inc/store/sqlite.class.php +++ b/inc/store/sqlite.class.php @@ -114,7 +114,7 @@ class Sqlite extends Store { return $entry[0]; } - public function getEntriesByView($view) { + public function getEntriesByView($view, $limit = '') { parent::__construct(); switch ($_SESSION['sort']) @@ -152,6 +152,8 @@ class Sqlite extends Store { break; } + $sql .= ' ' . $limit; + $query = $this->executeQuery($sql, $params); $entries = $query->fetchAll(); diff --git a/index.php b/index.php index dd70a98..98ada1b 100644 --- a/index.php +++ b/index.php @@ -62,7 +62,8 @@ else { } # because messages can be added in $poche->action(), we have to add this entry now (we can add it before) -$tpl_vars = array_merge($tpl_vars, array('messages' => $poche->messages->display())); +$messages = $poche->messages->display('all', FALSE); +$tpl_vars = array_merge($tpl_vars, array('messages' => $messages)); # Aaaaaaand action ! echo $poche->tpl->render($tpl_file, $tpl_vars); \ No newline at end of file diff --git a/tpl/_footer.twig b/tpl/_footer.twig index b1d7b8d..a541e6e 100644 --- a/tpl/_footer.twig +++ b/tpl/_footer.twig @@ -1,3 +1,3 @@ -
          +

          {% trans "powered by" %} poche

          \ No newline at end of file diff --git a/tpl/_head.twig b/tpl/_head.twig index 9e82437..f25f047 100644 --- a/tpl/_head.twig +++ b/tpl/_head.twig @@ -4,5 +4,6 @@ - - \ No newline at end of file + + + \ No newline at end of file diff --git a/tpl/_messages.twig b/tpl/_messages.twig index c9f01b5..679aa09 100644 --- a/tpl/_messages.twig +++ b/tpl/_messages.twig @@ -1,5 +1 @@ -
            - {% for message in messages %} -
          • {{ message|e }}
          • - {% endfor %} -
          \ No newline at end of file + {{ messages | raw }} \ No newline at end of file diff --git a/tpl/_top.twig b/tpl/_top.twig index daee44f..ae01cc3 100644 --- a/tpl/_top.twig +++ b/tpl/_top.twig @@ -1,3 +1,3 @@ -
          -

          logo pochepoche

          +
          +

          logo poche

          \ No newline at end of file diff --git a/tpl/config.twig b/tpl/config.twig index a8be93b..95d5d8c 100644 --- a/tpl/config.twig +++ b/tpl/config.twig @@ -11,49 +11,47 @@
        {% endblock %} {% block content %} -
        -

        {% trans "Bookmarklet" %}

        -

        {% trans "Thanks to the bookmarklet, you will be able to easily add a link to your poche." %} {% trans "Have a look to this documentation:" %} inthepoche.com.

        -

        {% trans "Drag & drop this link to your bookmarks bar and have fun with poche." %}

        -

        {% trans "poche it!" %}

        +

        {% trans "Bookmarklet" %}

        +

        {% trans "Thanks to the bookmarklet, you will be able to easily add a link to your poche." %} {% trans "Have a look to this documentation:" %} inthepoche.com.

        +

        {% trans "Drag & drop this link to your bookmarks bar and have fun with poche." %}

        +

        {% trans "poche it!" %}

        -

        {% trans "Updating poche" %}

        -

        -

        +

        {% trans "Updating poche" %}

        +

        +

        -

        {% trans "Change your password" %}

        -
        -
        -
        - - -
        -
        - - -
        -
        - -
        -
        - - -
        +

        {% trans "Change your password" %}

        +
        +
        +
        + + +
        +
        + + +
        +
        + +
        +
        + + +
        -

        {% trans "Import" %}

        -

        {% trans "Please execute the import script locally, it can take a very long time." %}

        -

        {% trans "More infos in the official doc:" %} inthepoche.com

        -

        +

        {% trans "Import" %}

        +

        {% trans "Please execute the import script locally, it can take a very long time." %}

        +

        {% trans "More infos in the official doc:" %} inthepoche.com

        +

        -

        {% trans "Export your poche datas" %}

        -

        {% trans "Click here" %} {% trans "to export your poche datas." %}

        -
        +

        {% trans "Export your poche datas" %}

        +

        {% trans "Click here" %} {% trans "to export your poche datas." %}

        {% endblock %} \ No newline at end of file diff --git a/tpl/css/messages.css b/tpl/css/messages.css index 702fac4..9222bb8 100755 --- a/tpl/css/messages.css +++ b/tpl/css/messages.css @@ -1,4 +1,4 @@ -.messages { width: 100%; -moz-border-radius: 4px; border-radius: 4px; display: block; padding: 10px 0; margin: 10px auto 10px; clear: both; } +.messages { width: 400px; -moz-border-radius: 4px; border-radius: 4px; display: block; padding: 10px 0; margin: 10px auto 10px; clear: both; } .messages a.closeMessage { margin: -14px -8px 0 0; display:none; width: 16px; height: 16px; float: right; background: url(../img/messages/close.png) no-repeat; } /*.messages:hover a.closeMessage { visibility:visible; }*/ .messages p { margin: 3px 0 3px 10px !important; padding: 0 10px 0 23px !important; font-size: 14px; line-height: 16px; } diff --git a/tpl/css/style-dark.css b/tpl/css/style-dark.css deleted file mode 100644 index 49fe101..0000000 --- a/tpl/css/style-dark.css +++ /dev/null @@ -1,94 +0,0 @@ -/*** GENERAL ***/ -body { - color: #fff; - background-color: #0d0d0d; -} - -a, a:hover, a:visited { - color: #fff; -} - -#main ul#links li a.current { - background-color: #000; - color: #fff; -} - -#links a:hover, .backhome a:hover, .support a:hover{ - background-color: #fff; - color: #000; -} - -input[type=submit].delete { - background : url('../img/dark/remove.png') no-repeat center center; - color : transparent; -} - -#main .entrie { - color: #fff; - background-color: #000; - border: 1px solid #fff; -} - -#main .entrie h2 a:hover { - color: #29B1E3; -} - -a.fav span { - background: url('../img/dark/star-on.png') no-repeat; -} - -a.fav span:hover { - background: url('../img/dark/star-off.png') no-repeat; -} - -a.fav-off span { - background: url('../img/dark/star-off.png') no-repeat; -} - -a.fav-off span:hover { - background: url('../img/dark/star-on.png') no-repeat; -} - -a.archive span { - background: url('../img/dark/checkmark-on.png') no-repeat; -} - -a.archive span:hover { - background: url('../img/dark/checkmark-off.png') no-repeat; -} - -a.archive-off span { - background: url('../img/dark/checkmark-off.png') no-repeat; -} - -a.archive-off span:hover { - background: url('../img/dark/checkmark-on.png') no-repeat; -} - -a.twitter span { - background: url('../img/dark/twitter.png') no-repeat; -} - -/*** ***/ -/*** ARTICLE PAGE ***/ - -body.article { - color: #fff; - background-color: #0d0d0d; -} - -#article header { - border-bottom: 1px solid #222222; -} - -#article article { - border-bottom: 1px solid #222222; -} - -.vieworiginal a { - color: #888888; -} - -.entrie { - background-color: #fff; -} diff --git a/tpl/css/style-light.css b/tpl/css/style-light.css index 5d584eb..9ea7955 100644 --- a/tpl/css/style-light.css +++ b/tpl/css/style-light.css @@ -1,47 +1,12 @@ -/*** GENERAL ***/ -body { - color: #222222; - background-color: #F1F1F1; + +a.back span { + background: url('../img/light/left.png') no-repeat; } -a, a:hover, a:visited { - color: #000; +a.top span { + background: url('../img/light/top.png') no-repeat; } -.bouton { - background-color: #000; - color: #fff; - border: none; -} -.bouton:hover { - background-color: #222222; - color: #F1F1F1; -} - -#main ul#links li a.current { - background-color: #000; - color: #fff; -} - -#links a:hover, .backhome a:hover, .support a:hover{ - background-color: #040707; - color: #F1F1F1; -} - -input[type=submit].delete { - background : url('../img/light/remove.png') no-repeat center center; - color : transparent; -} - -#main .entrie { - color: #2e2e2e; - background-color: #ffffff; - border: 1px solid #000; -} - -#main .entrie h2 a:hover { - color: #F5BE00; -} a.fav span { background: url('../img/light/star-on.png') no-repeat; @@ -83,26 +48,6 @@ a.email span { background: url('../img/light/envelop.png') no-repeat; } -/*** ***/ -/*** ARTICLE PAGE ***/ - -body.article { - color: #222222; - background-color: #F1F1F1; -} - -#article header { - border-bottom: 1px solid #222222; -} - -#article article { - border-bottom: 1px solid #222222; -} - -.vieworiginal a { - color: #888888; -} - -.entrie { - background-color: #fff; -} +a.delete span { + background: url('../img/light/remove.png') no-repeat; +} \ No newline at end of file diff --git a/tpl/css/style.css b/tpl/css/style.css index 333a0b7..8808b7e 100644 --- a/tpl/css/style.css +++ b/tpl/css/style.css @@ -1,6 +1,6 @@ -/*** GENERAL ***/ body { - font: 20px/1.3em Palatino,Georgia,serif; + font-size: 16px; + font-family: 'Roboto', sans-serif; margin: 10px; } @@ -16,6 +16,10 @@ header h1 { border-radius: 2px; } +#main { + margin: 0 auto; +} + #main ul#links { padding: 0; list-style-type: none; @@ -36,6 +40,7 @@ header h1 { padding: 0; list-style-type: none; text-align: center; + opacity: 0.5; } #main ul#sort li { @@ -47,31 +52,16 @@ header h1 { cursor: pointer; } -ul#messages { - -} -#main, #article { - margin: 0 auto; -} - -#links a, .backhome a, .support a{ +#links a{ text-decoration: none; padding: 5px 10px; } -#links a:hover, .backhome a:hover, .support a:hover{ +#links a:hover{ -webkit-border-radius: 2px; border-radius: 2px; } -.support { - font-size: 14px; -} - -footer { - text-align: right; -} - /*** ***/ /*** LINKS DISPLAY ***/ @@ -80,33 +70,41 @@ footer { cursor: pointer; } -input[type=submit].delete { - width : 16px; - height :16px; - border : none; - cursor: pointer; - font-size : 0; -} - #main #content { margin-top: 20px; } -#main .entrie { - padding: 15px; - min-height: 8em; - border: 1px solid; +#main #content h2 { + font-size: 1.3em; + text-decoration: none; +} + +#main #content .entrie { + border-bottom: 1px solid #222222; } #main .entrie h2 a { text-decoration: none; } +#main .entrie ul.tools { + list-style-type: none; +} + +#main .entrie ul.tools li { + /*display: inline;*/ +} + .tools { float: right; text-align: right; + opacity: 0.5; } +.tools p { + font-size: 0.8em;} + +/* .tools ul { padding: 0; margin: 0; list-style-type: none; @@ -118,19 +116,7 @@ input[type=submit].delete { .tools a.tool { cursor: pointer; -} - -#article .tools { - position: relative; - display: inline; - top: 0px; - right: 0px; - width: 100%; -} - -#article .tools ul li{ - display: inline; -} +}*/ #main .entrie .tools a.tool span, #article .tools a.tool span { display: inline-block; @@ -146,10 +132,9 @@ input[type=submit].delete { /*** ***/ /*** ARTICLE PAGE ***/ -body.article { - font: 20px/1.3em Palatino,Georgia,serif; +#article { + margin: 0 auto; } - #article header { text-align: left; } @@ -158,58 +143,106 @@ body.article { text-decoration: none; } -.vieworiginal a { +.vieworiginal a, .vieworiginal a:hover, .vieworiginal a:visited { text-decoration: none; + color: #888888; } .backhome { display: inline; } +#article .tools { + position: relative; + display: inline; + top: 0px; + right: 0px; + width: 100%; +} + +#article .tools ul li{ + display: inline; +} + + +/*** GENERAL ***/ +body { + color: #000; +} + +a, a:hover, a:visited { + color: #000; +} + +.bouton { + background-color: #000; + color: #fff; + border: none; +} +.bouton:hover { + background-color: #222222; + color: #F1F1F1; +} + +#main ul#links li a.current { + background-color: #000; + color: #fff; +} + +#links a:hover{ + background-color: #040707; + color: #F1F1F1; +} + + /*** ***/ +/*** ARTICLE PAGE ***/ -#main -{ - max-width: 60em; /* 960 px */ - margin: 0 auto; -} -#content -{ - width: 103.125%; /* 990px */ - overflow: hidden; - margin-left: -1.562%; /* 15px */ - margin-bottom: -1.875em; /* 30px */ +#article header, #article article { + border-bottom: 1px solid #222222; } -.entrie -{ - width: 30.303%; /* 300px */ - background-color: #fff; - float: left; - margin: 0 1.515% 1.875em; /* 15px 30px */ + +/* Pagination */ +.pagination { + clear: both; + padding-bottom: 20px; + padding-top: 10px; + text-align: right; +} +.pagination a { + border: 1px solid #D5D5D5; + color: #333; + font-size: 11px; + font-weight: bold; + height: 25px; + padding: 4px 8px; + text-decoration: none; + margin:2px; +} +.pagination a:hover, .pagination a:active { + background:#efefef; +} +.pagination span.current { + background-color: #ccc; + border: 1px solid #D5D5D5; + color: #000; + font-size: 11px; + font-weight: bold; + height: 25px; + padding: 4px 8px; + text-decoration: none; + margin:2px; +} +.pagination span.disabled { + border: 1px solid #EEEEEE; + color: #DDDDDD; + margin:2px; + padding: 4px 8px; + font-size: 11px; + font-weight: bold; } -@media only screen and ( max-width: 40em ) /* 640px */ -{ - .entrie - { - width: 46.876%; /* 305px */ - margin-bottom: 0.938em; /* 15px */ - } -} - -@media only screen and ( max-width: 20em ) /* 320px */ -{ - #content - { - width: 100%; - margin-left: 0; - } - - .entrie - { - width: 100%; - margin-left: 0; - margin-right: 0; - } +footer { + clear: both; } \ No newline at end of file diff --git a/tpl/home.twig b/tpl/home.twig index 6d0f1a6..3cccbb7 100644 --- a/tpl/home.twig +++ b/tpl/home.twig @@ -5,39 +5,29 @@ {% endblock %} {% block precontent %}
          -
        • {% trans "by date" %}
        • -
        • {% trans "by title" %}
        • +
        • {% trans {% trans "by date" %} {% trans
        • +
        • {% trans {% trans "by title" %} {% trans
        {% endblock %} -{% block messages %} -{% include '_messages.twig' %} -{% endblock %} {% block content %} -
        - {% for entry in entries %} -
        - -

        - {{ entry.title|e }} -

        -
        -
          -
        • -
        • -
        • -
        • -
        • -
        -
        -
        {{ entry.url | e | getDomain }}
        -
        -
        - {% endfor %} + {{ page_links | raw }} + {% for entry in entries %} +
        +

        {{ entry.title|e }}

        +
          +
        • +
        • +
        • +
        • + +
        +

        {{ entry.content|striptags|slice(0, 300) }}...

        +

        {{ entry.url | e | getDomain }}

        + {% endfor %} + {{ page_links | raw }} {% endblock %} {% block js %} - - {% endblock %} \ No newline at end of file diff --git a/tpl/img/dark/checkmark-off.png b/tpl/img/dark/checkmark-off.png deleted file mode 100644 index efc3439fe7f8955d9c9b44cbe079da77c8f5f4da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 267 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmP)i?EQj;G7@MCP1MXo-U3d7N_@4e#m)PL7+9>x>32L zMS7uf35)wFj++aVmq@qxUC5h|^Wkg%CFdhY#EKLu&VRS%cYOJRiAmsJKC~?4D@f_VGVbBclU#TvbmF9`=4|CtF_fm9Neh(4CqD%Pgg&e IbxsLQ03l{&`2YX_ diff --git a/tpl/img/dark/checkmark-on.png b/tpl/img/dark/checkmark-on.png deleted file mode 100644 index 24391c2ee8cc72acee6f4c99177e18ab98d5698b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 221 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmP)i?EQjmhR_OAY-yTT^vI!PA4Zw6g~L=|34ED-v{D) z<|5Pi|M?l;dm6F$8uQpJaQV!>gZsnJ?ufGye_aENLoPCzu)YT>%y*cQrV_1}VA!%D zjj2sw`AiOHI|14I5j;N~in^SQo-jwA6g?@xyembIfk!aI%<#vq7ND&Rp00i_>zopr E0G=sIqyPW_ diff --git a/tpl/img/dark/down.png b/tpl/img/dark/down.png deleted file mode 100644 index 41ea9604e57af634d3ce0d7676279f6da52f4259..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 223 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmP)i?EO-$El^AWk8`EPZ!4!i_>o>I`TCr@UX1bf1vsA ze^C8}e;IqGv`DNFdUV#7@#b8;l_#b%$!rj1V3?U6pZk+pilKmsZAyx^s*P1vK}W|o zg^$Y~-gGPJzZ!jK^{bRa!JE2tTW;Untp816?YzUBPil8C&*VSC0000pP)t-sj`;XK zK0dX&x_W$k%IN5ro0||25FZ~OFE1}#U0uG($%%`LY;A4G+1XA`PNu4=0001tk_XHH z0004WQchCn^^r{)gQ$ng`Q@ z^-ImON!U#fKz$G)nNDZ}6HGsuZWEj~7!p-X27~1wu^$bdxY+(@KrXgFu3*&usDg3( zpD%|c{!inH&d`9)2bxHLar5#or6UGI2P!2dLIIdV*eUIsJQ@MIpYsm-F%Ja95vtQa zl(7vM7tnh{0&)R%CI={J>(?KY zdsYlikGu#l=ookiIK`!YJoOUg`uvE(-wO^V0nG=QnFMJ3O^Cz^+nvC03201)flYuy zuOt!x$TT!t0I&%ts|7U1VOcFei-2G610VucXTs(0g5__+<@bR~X=3t?$TA6Nz7s|! z0nPv7nFO?#2ojHg_O+QsKu47(to}M)ULSb57*h!7DiSTP5nXc;fdJjN+OMt?UbbLd zfbLy>@q)d#*6X4kY$adsMLN0;>xH0YQTeoM({Natrj4rA{mX%{j&0rLn+DLmhG6SU z%7){%X=7_H6*6_#ta2~79oX@}GEAY~_ksRt@O$Fw;eXRRPN0;6#M!+bZ(fe}@9kpy zx4}5g{@4rwh5cIbeC7c-M+zP`;BmRn)~Lwh zq$uU3AmXIJ;-oO6@t5$~idV{6%YG+EZ|jSVywEr^_ga!Nm+|Hf-h`EMDgICSO!wv7 z{97_Tk;U`T`W2ci6Qp??zP8t_6n{{XcP@VmyO(}Lu&3~>%o}bGc3iu@q-6i?YQ|H- zAE&qbW}8mf;;!jol_t8x35w$#sYJ=p00i_>zopr E0IptK0{{R3 diff --git a/tpl/img/dark/star-off.png b/tpl/img/dark/star-off.png deleted file mode 100644 index 90651b54a1dff1dee91bc8aa79234d053b5d2a81..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 330 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmP)i?A?*p7^`hAOo&@x;TbdoSr-BVqsH)K+Aq_=7W4X zjeJX3-W=fbVZZ37qo|s|dWkWjG3wy6)R~TMe3NJI6fb+f`*(Zm5*5)&U1vT!#>Kze zKYjasmAjj@l^A6jOamBlU1a`k-dl7&PH^!;p&u+-3s}Mg10%B=q%Sb>e(2~BQ>b~h z-7@__-sAHLMtjV5FqnOlZ-1-qIelroS=Bw)OPN|aPHFA;8+G{leY{G1A6{0OcEGgr z)75z*7y6s7FwR}F_iG*JhHp#fe%>H^>we1lH`3Ccdzbub-M0B!O2e;S8Jo&F#!U;( V#ahNSr~|#o;OXk;vd$@?2>=ekfK>nh diff --git a/tpl/img/dark/star-on.png b/tpl/img/dark/star-on.png deleted file mode 100644 index 7fc144772d5247254c04c6cb5641d5ee9c0c6716..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 277 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmP)i?A>w>mpBOcA(HAPZ!4!i_=>tMe-gt5OA5l`rmZ{ zu7d(mY>fdNZ31ehBJGX}QyfECF3;aOMXpfDTQXC{d$)0Q#X=^}xJm1u%B)b9+J1PI zeeb@d%^VfUIcwt#ZXYdF;yS>&gW;f-c*O&~OctXLMdx~5Z>*NBHT2|t^u@M3ODx~S z)BARO)B3xP5g$({hBtqHV#^Tz*kJPK&CNP56<7Wi-W{oPooAP5`oD>v*>=43kSi6q R;staqgQu&X%Q~loCIBQ)V3q&? diff --git a/tpl/img/dark/twitter.png b/tpl/img/dark/twitter.png deleted file mode 100755 index 4595affbafd4e74673f1b03d297774a9d0458791..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 300 zcmV+{0n`48P)`0wV7&E+y=#?JiYvf2In|K*Y< zNsS^}glBGV%MpM8rdh&9;UP!HF4!V(~B zDxAMCto42sBOtdCC}<1z8A{7g*1ch^7C9lRG(;5TJp4@^Y>0000>oIfycFe33G~y zdA|9Iyi2@w`hiUmQ^fNBK2ZG?r@|5;I?3zPmc{uGCslf~&zo*iP*fo0WwpVokxx~H zpFg@+<)xI!$+#UCDvTO5l)`Tx$hvs%#2bg#{PTqNy*sVXuz9DoX*JP z(EQz**ZfF2rx|m?EZznwd8Um_nrsT$`#7$=U^pV*VcL*#R-rjDPQlxF6C- - + {% include '_top.twig' %}
        {% block menu %}{% endblock %} {% block precontent %}{% endblock %} - {% block messages %}{% endblock %} + {% block messages %} + {% include '_messages.twig' %} + {% endblock %} +
        {% block content %}{% endblock %} +
        {% block js %}{% endblock %}
        {% include '_footer.twig' %} diff --git a/tpl/view.twig b/tpl/view.twig index 692f955..d0d85f8 100644 --- a/tpl/view.twig +++ b/tpl/view.twig @@ -1,18 +1,17 @@ {% extends "layout.twig" %} {% block title %}{% trans "home" %}{% endblock %} -{% block messages %} -{% include '_messages.twig' %} -{% endblock %} {% block content %} -
        +
        -
          -
        • -
        • -
        • -
        • - {% if constant('SHARE_TWITTER') == 1 %}
        • {% endif %} - {% if constant('SHARE_MAIL') == 1 %}
        • {% endif %} +
            +
          • +
          • + +
          • +
          • + {% if constant('SHARE_TWITTER') == 1 %}
          • {% endif %} + {% if constant('SHARE_MAIL') == 1 %}
          • {% endif %} +
        @@ -20,17 +19,22 @@
        - -
        - - -
        -
        - {% trans "this article appears wrong?" %} {% trans "create an issue" %} {% trans "or" %} {% trans "contact us by mail" %} +
        +
          +
        • +
        • +
        • + +
        • +
        • + {% if constant('SHARE_TWITTER') == 1 %}
        • {% endif %} + {% if constant('SHARE_MAIL') == 1 %}
        • {% endif %} + +
        +

        {% trans "this article appears wrong?" %} {% trans "create an issue" %} {% trans "or" %} {% trans "contact us by mail" %}

        {% endblock %} From d28a7ca30fa50845a54b0e21844b20b373b7fd44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Mon, 5 Aug 2013 21:59:21 +0200 Subject: [PATCH 35/49] remove js --- tpl/home.twig | 4 ---- tpl/js/jquery-1.9.1.min.js | 5 ----- tpl/js/jquery.masonry.min.js | 10 ---------- tpl/layout.twig | 1 - tpl/view.twig | 4 ---- 5 files changed, 24 deletions(-) delete mode 100644 tpl/js/jquery-1.9.1.min.js delete mode 100644 tpl/js/jquery.masonry.min.js diff --git a/tpl/home.twig b/tpl/home.twig index 3cccbb7..a6da641 100644 --- a/tpl/home.twig +++ b/tpl/home.twig @@ -26,8 +26,4 @@
        {% endfor %} {{ page_links | raw }} -{% endblock %} - -{% block js %} - {% endblock %} \ No newline at end of file diff --git a/tpl/js/jquery-1.9.1.min.js b/tpl/js/jquery-1.9.1.min.js deleted file mode 100644 index 006e953..0000000 --- a/tpl/js/jquery-1.9.1.min.js +++ /dev/null @@ -1,5 +0,0 @@ -/*! jQuery v1.9.1 | (c) 2005, 2012 jQuery Foundation, Inc. | jquery.org/license -//@ sourceMappingURL=jquery.min.map -*/(function(e,t){var n,r,i=typeof t,o=e.document,a=e.location,s=e.jQuery,u=e.$,l={},c=[],p="1.9.1",f=c.concat,d=c.push,h=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=p.trim,b=function(e,t){return new b.fn.init(e,t,r)},x=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,w=/\S+/g,T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,k=/^[\],:{}\s]*$/,E=/(?:^|:|,)(?:\s*\[)+/g,S=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,A=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,j=/^-ms-/,D=/-([\da-z])/gi,L=function(e,t){return t.toUpperCase()},H=function(e){(o.addEventListener||"load"===e.type||"complete"===o.readyState)&&(q(),b.ready())},q=function(){o.addEventListener?(o.removeEventListener("DOMContentLoaded",H,!1),e.removeEventListener("load",H,!1)):(o.detachEvent("onreadystatechange",H),e.detachEvent("onload",H))};b.fn=b.prototype={jquery:p,constructor:b,init:function(e,n,r){var i,a;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof b?n[0]:n,b.merge(this,b.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:o,!0)),C.test(i[1])&&b.isPlainObject(n))for(i in n)b.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(a=o.getElementById(i[2]),a&&a.parentNode){if(a.id!==i[2])return r.find(e);this.length=1,this[0]=a}return this.context=o,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):b.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),b.makeArray(e,this))},selector:"",length:0,size:function(){return this.length},toArray:function(){return h.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=b.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return b.each(this,e,t)},ready:function(e){return b.ready.promise().done(e),this},slice:function(){return this.pushStack(h.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(b.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:d,sort:[].sort,splice:[].splice},b.fn.init.prototype=b.fn,b.extend=b.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},u=1,l=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},u=2),"object"==typeof s||b.isFunction(s)||(s={}),l===u&&(s=this,--u);l>u;u++)if(null!=(o=arguments[u]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(b.isPlainObject(r)||(n=b.isArray(r)))?(n?(n=!1,a=e&&b.isArray(e)?e:[]):a=e&&b.isPlainObject(e)?e:{},s[i]=b.extend(c,a,r)):r!==t&&(s[i]=r));return s},b.extend({noConflict:function(t){return e.$===b&&(e.$=u),t&&e.jQuery===b&&(e.jQuery=s),b},isReady:!1,readyWait:1,holdReady:function(e){e?b.readyWait++:b.ready(!0)},ready:function(e){if(e===!0?!--b.readyWait:!b.isReady){if(!o.body)return setTimeout(b.ready);b.isReady=!0,e!==!0&&--b.readyWait>0||(n.resolveWith(o,[b]),b.fn.trigger&&b(o).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===b.type(e)},isArray:Array.isArray||function(e){return"array"===b.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[m.call(e)]||"object":typeof e},isPlainObject:function(e){if(!e||"object"!==b.type(e)||e.nodeType||b.isWindow(e))return!1;try{if(e.constructor&&!y.call(e,"constructor")&&!y.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||y.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||o;var r=C.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=b.buildFragment([e],t,i),i&&b(i).remove(),b.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=b.trim(n),n&&k.test(n.replace(S,"@").replace(A,"]").replace(E,"")))?Function("return "+n)():(b.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||b.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&b.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(j,"ms-").replace(D,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:v&&!v.call("\ufeff\u00a0")?function(e){return null==e?"":v.call(e)}:function(e){return null==e?"":(e+"").replace(T,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?b.merge(n,"string"==typeof e?[e]:e):d.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(g)return g.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return f.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),b.isFunction(e)?(r=h.call(arguments,2),i=function(){return e.apply(n||this,r.concat(h.call(arguments)))},i.guid=e.guid=e.guid||b.guid++,i):t},access:function(e,n,r,i,o,a,s){var u=0,l=e.length,c=null==r;if("object"===b.type(r)){o=!0;for(u in r)b.access(e,n,u,r[u],!0,a,s)}else if(i!==t&&(o=!0,b.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(b(e),n)})),n))for(;l>u;u++)n(e[u],r,s?i:i.call(e[u],u,n(e[u],r)));return o?e:c?n.call(e):l?n(e[0],r):a},now:function(){return(new Date).getTime()}}),b.ready.promise=function(t){if(!n)if(n=b.Deferred(),"complete"===o.readyState)setTimeout(b.ready);else if(o.addEventListener)o.addEventListener("DOMContentLoaded",H,!1),e.addEventListener("load",H,!1);else{o.attachEvent("onreadystatechange",H),e.attachEvent("onload",H);var r=!1;try{r=null==e.frameElement&&o.documentElement}catch(i){}r&&r.doScroll&&function a(){if(!b.isReady){try{r.doScroll("left")}catch(e){return setTimeout(a,50)}q(),b.ready()}}()}return n.promise(t)},b.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){l["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=b.type(e);return b.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=b(o);var _={};function F(e){var t=_[e]={};return b.each(e.match(w)||[],function(e,n){t[n]=!0}),t}b.Callbacks=function(e){e="string"==typeof e?_[e]||F(e):b.extend({},e);var n,r,i,o,a,s,u=[],l=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=u.length,n=!0;u&&o>a;a++)if(u[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,u&&(l?l.length&&c(l.shift()):r?u=[]:p.disable())},p={add:function(){if(u){var t=u.length;(function i(t){b.each(t,function(t,n){var r=b.type(n);"function"===r?e.unique&&p.has(n)||u.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=u.length:r&&(s=t,c(r))}return this},remove:function(){return u&&b.each(arguments,function(e,t){var r;while((r=b.inArray(t,u,r))>-1)u.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?b.inArray(e,u)>-1:!(!u||!u.length)},empty:function(){return u=[],this},disable:function(){return u=l=r=t,this},disabled:function(){return!u},lock:function(){return l=t,r||p.disable(),this},locked:function(){return!l},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!u||i&&!l||(n?l.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},b.extend({Deferred:function(e){var t=[["resolve","done",b.Callbacks("once memory"),"resolved"],["reject","fail",b.Callbacks("once memory"),"rejected"],["notify","progress",b.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return b.Deferred(function(n){b.each(t,function(t,o){var a=o[0],s=b.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&b.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?b.extend(e,r):r}},i={};return r.pipe=r.then,b.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=h.call(arguments),r=n.length,i=1!==r||e&&b.isFunction(e.promise)?r:0,o=1===i?e:b.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?h.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,u,l;if(r>1)for(s=Array(r),u=Array(r),l=Array(r);r>t;t++)n[t]&&b.isFunction(n[t].promise)?n[t].promise().done(a(t,l,n)).fail(o.reject).progress(a(t,u,s)):--i;return i||o.resolveWith(l,n),o.promise()}}),b.support=function(){var t,n,r,a,s,u,l,c,p,f,d=o.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
        a",n=d.getElementsByTagName("*"),r=d.getElementsByTagName("a")[0],!n||!r||!n.length)return{};s=o.createElement("select"),l=s.appendChild(o.createElement("option")),a=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={getSetAttribute:"t"!==d.className,leadingWhitespace:3===d.firstChild.nodeType,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:"/a"===r.getAttribute("href"),opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:!!a.value,optSelected:l.selected,enctype:!!o.createElement("form").enctype,html5Clone:"<:nav>"!==o.createElement("nav").cloneNode(!0).outerHTML,boxModel:"CSS1Compat"===o.compatMode,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},a.checked=!0,t.noCloneChecked=a.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!l.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}a=o.createElement("input"),a.setAttribute("value",""),t.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),t.radioValue="t"===a.value,a.setAttribute("checked","t"),a.setAttribute("name","t"),u=o.createDocumentFragment(),u.appendChild(a),t.appendChecked=a.checked,t.checkClone=u.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;return d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip,b(function(){var n,r,a,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",u=o.getElementsByTagName("body")[0];u&&(n=o.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",u.appendChild(n).appendChild(d),d.innerHTML="
        t
        ",a=d.getElementsByTagName("td"),a[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===a[0].offsetHeight,a[0].style.display="",a[1].style.display="none",t.reliableHiddenOffsets=p&&0===a[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=4===d.offsetWidth,t.doesNotIncludeMarginInBodyOffset=1!==u.offsetTop,e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(o.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="
        ",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(u.style.zoom=1)),u.removeChild(n),n=d=a=r=null)}),n=s=u=l=r=a=null,t}();var O=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,B=/([A-Z])/g;function P(e,n,r,i){if(b.acceptData(e)){var o,a,s=b.expando,u="string"==typeof n,l=e.nodeType,p=l?b.cache:e,f=l?e[s]:e[s]&&s;if(f&&p[f]&&(i||p[f].data)||!u||r!==t)return f||(l?e[s]=f=c.pop()||b.guid++:f=s),p[f]||(p[f]={},l||(p[f].toJSON=b.noop)),("object"==typeof n||"function"==typeof n)&&(i?p[f]=b.extend(p[f],n):p[f].data=b.extend(p[f].data,n)),o=p[f],i||(o.data||(o.data={}),o=o.data),r!==t&&(o[b.camelCase(n)]=r),u?(a=o[n],null==a&&(a=o[b.camelCase(n)])):a=o,a}}function R(e,t,n){if(b.acceptData(e)){var r,i,o,a=e.nodeType,s=a?b.cache:e,u=a?e[b.expando]:b.expando;if(s[u]){if(t&&(o=n?s[u]:s[u].data)){b.isArray(t)?t=t.concat(b.map(t,b.camelCase)):t in o?t=[t]:(t=b.camelCase(t),t=t in o?[t]:t.split(" "));for(r=0,i=t.length;i>r;r++)delete o[t[r]];if(!(n?$:b.isEmptyObject)(o))return}(n||(delete s[u].data,$(s[u])))&&(a?b.cleanData([e],!0):b.support.deleteExpando||s!=s.window?delete s[u]:s[u]=null)}}}b.extend({cache:{},expando:"jQuery"+(p+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?b.cache[e[b.expando]]:e[b.expando],!!e&&!$(e)},data:function(e,t,n){return P(e,t,n)},removeData:function(e,t){return R(e,t)},_data:function(e,t,n){return P(e,t,n,!0)},_removeData:function(e,t){return R(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&b.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),b.fn.extend({data:function(e,n){var r,i,o=this[0],a=0,s=null;if(e===t){if(this.length&&(s=b.data(o),1===o.nodeType&&!b._data(o,"parsedAttrs"))){for(r=o.attributes;r.length>a;a++)i=r[a].name,i.indexOf("data-")||(i=b.camelCase(i.slice(5)),W(o,i,s[i]));b._data(o,"parsedAttrs",!0)}return s}return"object"==typeof e?this.each(function(){b.data(this,e)}):b.access(this,function(n){return n===t?o?W(o,e,b.data(o,e)):null:(this.each(function(){b.data(this,e,n)}),t)},null,n,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){b.removeData(this,e)})}});function W(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(B,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:O.test(r)?b.parseJSON(r):r}catch(o){}b.data(e,n,r)}else r=t}return r}function $(e){var t;for(t in e)if(("data"!==t||!b.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}b.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=b._data(e,n),r&&(!i||b.isArray(r)?i=b._data(e,n,b.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=b.queue(e,t),r=n.length,i=n.shift(),o=b._queueHooks(e,t),a=function(){b.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),o.cur=i,i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return b._data(e,n)||b._data(e,n,{empty:b.Callbacks("once memory").add(function(){b._removeData(e,t+"queue"),b._removeData(e,n)})})}}),b.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?b.queue(this[0],e):n===t?this:this.each(function(){var t=b.queue(this,e,n);b._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&b.dequeue(this,e)})},dequeue:function(e){return this.each(function(){b.dequeue(this,e)})},delay:function(e,t){return e=b.fx?b.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=b.Deferred(),a=this,s=this.length,u=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=b._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(u));return u(),o.promise(n)}});var I,z,X=/[\t\r\n]/g,U=/\r/g,V=/^(?:input|select|textarea|button|object)$/i,Y=/^(?:a|area)$/i,J=/^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i,G=/^(?:checked|selected)$/i,Q=b.support.getSetAttribute,K=b.support.input;b.fn.extend({attr:function(e,t){return b.access(this,b.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){b.removeAttr(this,e)})},prop:function(e,t){return b.access(this,b.prop,e,t,arguments.length>1)},removeProp:function(e){return e=b.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,u="string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=b.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,u=0===arguments.length||"string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?b.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,r="boolean"==typeof t;return b.isFunction(e)?this.each(function(n){b(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var o,a=0,s=b(this),u=t,l=e.match(w)||[];while(o=l[a++])u=r?u:!s.hasClass(o),s[u?"addClass":"removeClass"](o)}else(n===i||"boolean"===n)&&(this.className&&b._data(this,"__className__",this.className),this.className=this.className||e===!1?"":b._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(X," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=b.isFunction(e),this.each(function(n){var o,a=b(this);1===this.nodeType&&(o=i?e.call(this,n,a.val()):e,null==o?o="":"number"==typeof o?o+="":b.isArray(o)&&(o=b.map(o,function(e){return null==e?"":e+""})),r=b.valHooks[this.type]||b.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=b.valHooks[o.type]||b.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(U,""):null==n?"":n)}}}),b.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,u=0>i?s:o?i:0;for(;s>u;u++)if(n=r[u],!(!n.selected&&u!==i||(b.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&b.nodeName(n.parentNode,"optgroup"))){if(t=b(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n=b.makeArray(t);return b(e).find("option").each(function(){this.selected=b.inArray(b(this).val(),n)>=0}),n.length||(e.selectedIndex=-1),n}}},attr:function(e,n,r){var o,a,s,u=e.nodeType;if(e&&3!==u&&8!==u&&2!==u)return typeof e.getAttribute===i?b.prop(e,n,r):(a=1!==u||!b.isXMLDoc(e),a&&(n=n.toLowerCase(),o=b.attrHooks[n]||(J.test(n)?z:I)),r===t?o&&a&&"get"in o&&null!==(s=o.get(e,n))?s:(typeof e.getAttribute!==i&&(s=e.getAttribute(n)),null==s?t:s):null!==r?o&&a&&"set"in o&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r):(b.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(w);if(o&&1===e.nodeType)while(n=o[i++])r=b.propFix[n]||n,J.test(n)?!Q&&G.test(n)?e[b.camelCase("default-"+n)]=e[r]=!1:e[r]=!1:b.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!b.support.radioValue&&"radio"===t&&b.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!b.isXMLDoc(e),a&&(n=b.propFix[n]||n,o=b.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var n=e.getAttributeNode("tabindex");return n&&n.specified?parseInt(n.value,10):V.test(e.nodeName)||Y.test(e.nodeName)&&e.href?0:t}}}}),z={get:function(e,n){var r=b.prop(e,n),i="boolean"==typeof r&&e.getAttribute(n),o="boolean"==typeof r?K&&Q?null!=i:G.test(n)?e[b.camelCase("default-"+n)]:!!i:e.getAttributeNode(n);return o&&o.value!==!1?n.toLowerCase():t},set:function(e,t,n){return t===!1?b.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&b.propFix[n]||n,n):e[b.camelCase("default-"+n)]=e[n]=!0,n}},K&&Q||(b.attrHooks.value={get:function(e,n){var r=e.getAttributeNode(n);return b.nodeName(e,"input")?e.defaultValue:r&&r.specified?r.value:t},set:function(e,n,r){return b.nodeName(e,"input")?(e.defaultValue=n,t):I&&I.set(e,n,r)}}),Q||(I=b.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&("id"===n||"name"===n||"coords"===n?""!==r.value:r.specified)?r.value:t},set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},b.attrHooks.contenteditable={get:I.get,set:function(e,t,n){I.set(e,""===t?!1:t,n)}},b.each(["width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}})})),b.support.hrefNormalized||(b.each(["href","src","width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{get:function(e){var r=e.getAttribute(n,2);return null==r?t:r}})}),b.each(["href","src"],function(e,t){b.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}})),b.support.style||(b.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),b.support.optSelected||(b.propHooks.selected=b.extend(b.propHooks.selected,{get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}})),b.support.enctype||(b.propFix.enctype="encoding"),b.support.checkOn||b.each(["radio","checkbox"],function(){b.valHooks[this]={get:function(e){return null===e.getAttribute("value")?"on":e.value}}}),b.each(["radio","checkbox"],function(){b.valHooks[this]=b.extend(b.valHooks[this],{set:function(e,n){return b.isArray(n)?e.checked=b.inArray(b(e).val(),n)>=0:t}})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}b.event={global:{},add:function(e,n,r,o,a){var s,u,l,c,p,f,d,h,g,m,y,v=b._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=b.guid++),(u=v.events)||(u=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof b===i||e&&b.event.triggered===e.type?t:b.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(w)||[""],l=n.length;while(l--)s=rt.exec(n[l])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),p=b.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=b.event.special[g]||{},d=b.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&b.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=u[g])||(h=u[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),b.event.global[g]=!0;e=null}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,p,f,d,h,g,m=b.hasData(e)&&b._data(e);if(m&&(c=m.events)){t=(t||"").match(w)||[""],l=t.length;while(l--)if(s=rt.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=b.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),u=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));u&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||b.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)b.event.remove(e,d+t[l],n,r,!0);b.isEmptyObject(c)&&(delete m.handle,b._removeData(e,"events"))}},trigger:function(n,r,i,a){var s,u,l,c,p,f,d,h=[i||o],g=y.call(n,"type")?n.type:n,m=y.call(n,"namespace")?n.namespace.split("."):[];if(l=f=i=i||o,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+b.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),u=0>g.indexOf(":")&&"on"+g,n=n[b.expando]?n:new b.Event(g,"object"==typeof n&&n),n.isTrigger=!0,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:b.makeArray(r,[n]),p=b.event.special[g]||{},a||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!a&&!p.noBubble&&!b.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(l=l.parentNode);l;l=l.parentNode)h.push(l),f=l;f===(i.ownerDocument||o)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((l=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(b._data(l,"events")||{})[n.type]&&b._data(l,"handle"),s&&s.apply(l,r),s=u&&l[u],s&&b.acceptData(l)&&s.apply&&s.apply(l,r)===!1&&n.preventDefault();if(n.type=g,!(a||n.isDefaultPrevented()||p._default&&p._default.apply(i.ownerDocument,r)!==!1||"click"===g&&b.nodeName(i,"a")||!b.acceptData(i)||!u||!i[g]||b.isWindow(i))){f=i[u],f&&(i[u]=null),b.event.triggered=g;try{i[g]()}catch(v){}b.event.triggered=t,f&&(i[u]=f)}return n.result}},dispatch:function(e){e=b.event.fix(e);var n,r,i,o,a,s=[],u=h.call(arguments),l=(b._data(this,"events")||{})[e.type]||[],c=b.event.special[e.type]||{};if(u[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=b.event.handlers.call(this,e,l),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((b.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,u),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],u=n.delegateCount,l=e.target;if(u&&l.nodeType&&(!e.button||"click"!==e.type))for(;l!=this;l=l.parentNode||this)if(1===l.nodeType&&(l.disabled!==!0||"click"!==e.type)){for(o=[],a=0;u>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?b(r,this).index(l)>=0:b.find(r,this,null,[l]).length),o[r]&&o.push(i);o.length&&s.push({elem:l,handlers:o})}return n.length>u&&s.push({elem:this,handlers:n.slice(u)}),s},fix:function(e){if(e[b.expando])return e;var t,n,r,i=e.type,a=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new b.Event(a),t=r.length;while(t--)n=r[t],e[n]=a[n];return e.target||(e.target=a.srcElement||o),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,a):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,a,s=n.button,u=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||o,a=i.documentElement,r=i.body,e.pageX=n.clientX+(a&&a.scrollLeft||r&&r.scrollLeft||0)-(a&&a.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(a&&a.scrollTop||r&&r.scrollTop||0)-(a&&a.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&u&&(e.relatedTarget=u===e.target?n.toElement:u),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},click:{trigger:function(){return b.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t}},focus:{trigger:function(){if(this!==o.activeElement&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===o.activeElement&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=b.extend(new b.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?b.event.trigger(i,null,t):b.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},b.removeEvent=o.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},b.Event=function(e,n){return this instanceof b.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&b.extend(this,n),this.timeStamp=e&&e.timeStamp||b.now(),this[b.expando]=!0,t):new b.Event(e,n)},b.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},b.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){b.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj; -return(!i||i!==r&&!b.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),b.support.submitBubbles||(b.event.special.submit={setup:function(){return b.nodeName(this,"form")?!1:(b.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=b.nodeName(n,"input")||b.nodeName(n,"button")?n.form:t;r&&!b._data(r,"submitBubbles")&&(b.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),b._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&b.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return b.nodeName(this,"form")?!1:(b.event.remove(this,"._submit"),t)}}),b.support.changeBubbles||(b.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(b.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),b.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),b.event.simulate("change",this,e,!0)})),!1):(b.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!b._data(t,"changeBubbles")&&(b.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||b.event.simulate("change",this.parentNode,e,!0)}),b._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return b.event.remove(this,"._change"),!Z.test(this.nodeName)}}),b.support.focusinBubbles||b.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){b.event.simulate(t,e.target,b.event.fix(e),!0)};b.event.special[t]={setup:function(){0===n++&&o.addEventListener(e,r,!0)},teardown:function(){0===--n&&o.removeEventListener(e,r,!0)}}}),b.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return b().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=b.guid++)),this.each(function(){b.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,b(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){b.event.remove(this,e,r,n)})},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},trigger:function(e,t){return this.each(function(){b.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?b.event.trigger(e,n,r,!0):t}}),function(e,t){var n,r,i,o,a,s,u,l,c,p,f,d,h,g,m,y,v,x="sizzle"+-new Date,w=e.document,T={},N=0,C=0,k=it(),E=it(),S=it(),A=typeof t,j=1<<31,D=[],L=D.pop,H=D.push,q=D.slice,M=D.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},_="[\\x20\\t\\r\\n\\f]",F="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=F.replace("w","w#"),B="([*^$|!~]?=)",P="\\["+_+"*("+F+")"+_+"*(?:"+B+_+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+O+")|)|)"+_+"*\\]",R=":("+F+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+P.replace(3,8)+")*)|.*)\\)|)",W=RegExp("^"+_+"+|((?:^|[^\\\\])(?:\\\\.)*)"+_+"+$","g"),$=RegExp("^"+_+"*,"+_+"*"),I=RegExp("^"+_+"*([\\x20\\t\\r\\n\\f>+~])"+_+"*"),z=RegExp(R),X=RegExp("^"+O+"$"),U={ID:RegExp("^#("+F+")"),CLASS:RegExp("^\\.("+F+")"),NAME:RegExp("^\\[name=['\"]?("+F+")['\"]?\\]"),TAG:RegExp("^("+F.replace("w","w*")+")"),ATTR:RegExp("^"+P),PSEUDO:RegExp("^"+R),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+_+"*(even|odd|(([+-]|)(\\d*)n|)"+_+"*(?:([+-]|)"+_+"*(\\d+)|))"+_+"*\\)|)","i"),needsContext:RegExp("^"+_+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+_+"*((?:-\\d)?\\d*)"+_+"*\\)|)(?=[^-]|$)","i")},V=/[\x20\t\r\n\f]*[+~]/,Y=/^[^{]+\{\s*\[native code/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,G=/^(?:input|select|textarea|button)$/i,Q=/^h\d$/i,K=/'|\\/g,Z=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,et=/\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,tt=function(e,t){var n="0x"+t-65536;return n!==n?t:0>n?String.fromCharCode(n+65536):String.fromCharCode(55296|n>>10,56320|1023&n)};try{q.call(w.documentElement.childNodes,0)[0].nodeType}catch(nt){q=function(e){var t,n=[];while(t=this[e++])n.push(t);return n}}function rt(e){return Y.test(e+"")}function it(){var e,t=[];return e=function(n,r){return t.push(n+=" ")>i.cacheLength&&delete e[t.shift()],e[n]=r}}function ot(e){return e[x]=!0,e}function at(e){var t=p.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}}function st(e,t,n,r){var i,o,a,s,u,l,f,g,m,v;if((t?t.ownerDocument||t:w)!==p&&c(t),t=t||p,n=n||[],!e||"string"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(!d&&!r){if(i=J.exec(e))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&y(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return H.apply(n,q.call(t.getElementsByTagName(e),0)),n;if((a=i[3])&&T.getByClassName&&t.getElementsByClassName)return H.apply(n,q.call(t.getElementsByClassName(a),0)),n}if(T.qsa&&!h.test(e)){if(f=!0,g=x,m=t,v=9===s&&e,1===s&&"object"!==t.nodeName.toLowerCase()){l=ft(e),(f=t.getAttribute("id"))?g=f.replace(K,"\\$&"):t.setAttribute("id",g),g="[id='"+g+"'] ",u=l.length;while(u--)l[u]=g+dt(l[u]);m=V.test(e)&&t.parentNode||t,v=l.join(",")}if(v)try{return H.apply(n,q.call(m.querySelectorAll(v),0)),n}catch(b){}finally{f||t.removeAttribute("id")}}}return wt(e.replace(W,"$1"),t,n,r)}a=st.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},c=st.setDocument=function(e){var n=e?e.ownerDocument||e:w;return n!==p&&9===n.nodeType&&n.documentElement?(p=n,f=n.documentElement,d=a(n),T.tagNameNoComments=at(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),T.attributes=at(function(e){e.innerHTML="";var t=typeof e.lastChild.getAttribute("multiple");return"boolean"!==t&&"string"!==t}),T.getByClassName=at(function(e){return e.innerHTML="",e.getElementsByClassName&&e.getElementsByClassName("e").length?(e.lastChild.className="e",2===e.getElementsByClassName("e").length):!1}),T.getByName=at(function(e){e.id=x+0,e.innerHTML="
        ",f.insertBefore(e,f.firstChild);var t=n.getElementsByName&&n.getElementsByName(x).length===2+n.getElementsByName(x+0).length;return T.getIdNotName=!n.getElementById(x),f.removeChild(e),t}),i.attrHandle=at(function(e){return e.innerHTML="",e.firstChild&&typeof e.firstChild.getAttribute!==A&&"#"===e.firstChild.getAttribute("href")})?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},T.getIdNotName?(i.find.ID=function(e,t){if(typeof t.getElementById!==A&&!d){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){return e.getAttribute("id")===t}}):(i.find.ID=function(e,n){if(typeof n.getElementById!==A&&!d){var r=n.getElementById(e);return r?r.id===e||typeof r.getAttributeNode!==A&&r.getAttributeNode("id").value===e?[r]:t:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){var n=typeof e.getAttributeNode!==A&&e.getAttributeNode("id");return n&&n.value===t}}),i.find.TAG=T.tagNameNoComments?function(e,n){return typeof n.getElementsByTagName!==A?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},i.find.NAME=T.getByName&&function(e,n){return typeof n.getElementsByName!==A?n.getElementsByName(name):t},i.find.CLASS=T.getByClassName&&function(e,n){return typeof n.getElementsByClassName===A||d?t:n.getElementsByClassName(e)},g=[],h=[":focus"],(T.qsa=rt(n.querySelectorAll))&&(at(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||h.push("\\["+_+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||h.push(":checked")}),at(function(e){e.innerHTML="",e.querySelectorAll("[i^='']").length&&h.push("[*^$]="+_+"*(?:\"\"|'')"),e.querySelectorAll(":enabled").length||h.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),h.push(",.*:")})),(T.matchesSelector=rt(m=f.matchesSelector||f.mozMatchesSelector||f.webkitMatchesSelector||f.oMatchesSelector||f.msMatchesSelector))&&at(function(e){T.disconnectedMatch=m.call(e,"div"),m.call(e,"[s!='']:x"),g.push("!=",R)}),h=RegExp(h.join("|")),g=RegExp(g.join("|")),y=rt(f.contains)||f.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},v=f.compareDocumentPosition?function(e,t){var r;return e===t?(u=!0,0):(r=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t))?1&r||e.parentNode&&11===e.parentNode.nodeType?e===n||y(w,e)?-1:t===n||y(w,t)?1:0:4&r?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return u=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:0;if(o===a)return ut(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?ut(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},u=!1,[0,0].sort(v),T.detectDuplicates=u,p):p},st.matches=function(e,t){return st(e,null,null,t)},st.matchesSelector=function(e,t){if((e.ownerDocument||e)!==p&&c(e),t=t.replace(Z,"='$1']"),!(!T.matchesSelector||d||g&&g.test(t)||h.test(t)))try{var n=m.call(e,t);if(n||T.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(r){}return st(t,p,null,[e]).length>0},st.contains=function(e,t){return(e.ownerDocument||e)!==p&&c(e),y(e,t)},st.attr=function(e,t){var n;return(e.ownerDocument||e)!==p&&c(e),d||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):d||T.attributes?e.getAttribute(t):((n=e.getAttributeNode(t))||e.getAttribute(t))&&e[t]===!0?t:n&&n.specified?n.value:null},st.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},st.uniqueSort=function(e){var t,n=[],r=1,i=0;if(u=!T.detectDuplicates,e.sort(v),u){for(;t=e[r];r++)t===e[r-1]&&(i=n.push(r));while(i--)e.splice(n[i],1)}return e};function ut(e,t){var n=t&&e,r=n&&(~t.sourceIndex||j)-(~e.sourceIndex||j);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function lt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function ct(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function pt(e){return ot(function(t){return t=+t,ot(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}o=st.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=o(t);return n},i=st.selectors={cacheLength:50,createPseudo:ot,match:U,find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(et,tt),e[3]=(e[4]||e[5]||"").replace(et,tt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||st.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&st.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return U.CHILD.test(e[0])?null:(e[4]?e[2]=e[4]:n&&z.test(n)&&(t=ft(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){return"*"===e?function(){return!0}:(e=e.replace(et,tt).toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[e+" "];return t||(t=RegExp("(^|"+_+")"+e+"("+_+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==A&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=st.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[x]||(m[x]={}),l=c[e]||[],d=l[0]===N&&l[1],f=l[0]===N&&l[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[N,d,f];break}}else if(v&&(l=(t[x]||(t[x]={}))[e])&&l[0]===N)f=l[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[x]||(p[x]={}))[e]=[N,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||st.error("unsupported pseudo: "+e);return r[x]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?ot(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=M.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ot(function(e){var t=[],n=[],r=s(e.replace(W,"$1"));return r[x]?ot(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:ot(function(e){return function(t){return st(e,t).length>0}}),contains:ot(function(e){return function(t){return(t.textContent||t.innerText||o(t)).indexOf(e)>-1}}),lang:ot(function(e){return X.test(e||"")||st.error("unsupported lang: "+e),e=e.replace(et,tt).toLowerCase(),function(t){var n;do if(n=d?t.getAttribute("xml:lang")||t.getAttribute("lang"):t.lang)return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===f},focus:function(e){return e===p.activeElement&&(!p.hasFocus||p.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!i.pseudos.empty(e)},header:function(e){return Q.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:pt(function(){return[0]}),last:pt(function(e,t){return[t-1]}),eq:pt(function(e,t,n){return[0>n?n+t:n]}),even:pt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:pt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:pt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:pt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})i.pseudos[n]=lt(n);for(n in{submit:!0,reset:!0})i.pseudos[n]=ct(n);function ft(e,t){var n,r,o,a,s,u,l,c=E[e+" "];if(c)return t?0:c.slice(0);s=e,u=[],l=i.preFilter;while(s){(!n||(r=$.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),u.push(o=[])),n=!1,(r=I.exec(s))&&(n=r.shift(),o.push({value:n,type:r[0].replace(W," ")}),s=s.slice(n.length));for(a in i.filter)!(r=U[a].exec(s))||l[a]&&!(r=l[a](r))||(n=r.shift(),o.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?st.error(e):E(e,u).slice(0)}function dt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function ht(e,t,n){var i=t.dir,o=n&&"parentNode"===i,a=C++;return t.first?function(t,n,r){while(t=t[i])if(1===t.nodeType||o)return e(t,n,r)}:function(t,n,s){var u,l,c,p=N+" "+a;if(s){while(t=t[i])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[i])if(1===t.nodeType||o)if(c=t[x]||(t[x]={}),(l=c[i])&&l[0]===p){if((u=l[1])===!0||u===r)return u===!0}else if(l=c[i]=[p],l[1]=e(t,n,s)||r,l[1]===!0)return!0}}function gt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function mt(e,t,n,r,i){var o,a=[],s=0,u=e.length,l=null!=t;for(;u>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),l&&t.push(s));return a}function yt(e,t,n,r,i,o){return r&&!r[x]&&(r=yt(r)),i&&!i[x]&&(i=yt(i,o)),ot(function(o,a,s,u){var l,c,p,f=[],d=[],h=a.length,g=o||xt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:mt(g,f,e,s,u),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,u),r){l=mt(y,d),r(l,[],s,u),c=l.length;while(c--)(p=l[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){l=[],c=y.length;while(c--)(p=y[c])&&l.push(m[c]=p);i(null,y=[],l,u)}c=y.length;while(c--)(p=y[c])&&(l=i?M.call(o,p):f[c])>-1&&(o[l]=!(a[l]=p))}}else y=mt(y===a?y.splice(h,y.length):y),i?i(null,a,y,u):H.apply(a,y)})}function vt(e){var t,n,r,o=e.length,a=i.relative[e[0].type],s=a||i.relative[" "],u=a?1:0,c=ht(function(e){return e===t},s,!0),p=ht(function(e){return M.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;o>u;u++)if(n=i.relative[e[u].type])f=[ht(gt(f),n)];else{if(n=i.filter[e[u].type].apply(null,e[u].matches),n[x]){for(r=++u;o>r;r++)if(i.relative[e[r].type])break;return yt(u>1&>(f),u>1&&dt(e.slice(0,u-1)).replace(W,"$1"),n,r>u&&vt(e.slice(u,r)),o>r&&vt(e=e.slice(r)),o>r&&dt(e))}f.push(n)}return gt(f)}function bt(e,t){var n=0,o=t.length>0,a=e.length>0,s=function(s,u,c,f,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,T=l,C=s||a&&i.find.TAG("*",d&&u.parentNode||u),k=N+=null==T?1:Math.random()||.1;for(w&&(l=u!==p&&u,r=n);null!=(h=C[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,u,c)){f.push(h);break}w&&(N=k,r=++n)}o&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,o&&b!==v){g=0;while(m=t[g++])m(x,y,u,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=L.call(f));y=mt(y)}H.apply(f,y),w&&!s&&y.length>0&&v+t.length>1&&st.uniqueSort(f)}return w&&(N=k,l=T),x};return o?ot(s):s}s=st.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=ft(e)),n=t.length;while(n--)o=vt(t[n]),o[x]?r.push(o):i.push(o);o=S(e,bt(i,r))}return o};function xt(e,t,n){var r=0,i=t.length;for(;i>r;r++)st(e,t[r],n);return n}function wt(e,t,n,r){var o,a,u,l,c,p=ft(e);if(!r&&1===p.length){if(a=p[0]=p[0].slice(0),a.length>2&&"ID"===(u=a[0]).type&&9===t.nodeType&&!d&&i.relative[a[1].type]){if(t=i.find.ID(u.matches[0].replace(et,tt),t)[0],!t)return n;e=e.slice(a.shift().value.length)}o=U.needsContext.test(e)?0:a.length;while(o--){if(u=a[o],i.relative[l=u.type])break;if((c=i.find[l])&&(r=c(u.matches[0].replace(et,tt),V.test(a[0].type)&&t.parentNode||t))){if(a.splice(o,1),e=r.length&&dt(a),!e)return H.apply(n,q.call(r,0)),n;break}}}return s(e,p)(r,t,d,n,V.test(e)),n}i.pseudos.nth=i.pseudos.eq;function Tt(){}i.filters=Tt.prototype=i.pseudos,i.setFilters=new Tt,c(),st.attr=b.attr,b.find=st,b.expr=st.selectors,b.expr[":"]=b.expr.pseudos,b.unique=st.uniqueSort,b.text=st.getText,b.isXMLDoc=st.isXML,b.contains=st.contains}(e);var at=/Until$/,st=/^(?:parents|prev(?:Until|All))/,ut=/^.[^:#\[\.,]*$/,lt=b.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};b.fn.extend({find:function(e){var t,n,r,i=this.length;if("string"!=typeof e)return r=this,this.pushStack(b(e).filter(function(){for(t=0;i>t;t++)if(b.contains(r[t],this))return!0}));for(n=[],t=0;i>t;t++)b.find(e,this[t],n);return n=this.pushStack(i>1?b.unique(n):n),n.selector=(this.selector?this.selector+" ":"")+e,n},has:function(e){var t,n=b(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(b.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e,!1))},filter:function(e){return this.pushStack(ft(this,e,!0))},is:function(e){return!!e&&("string"==typeof e?lt.test(e)?b(e,this.context).index(this[0])>=0:b.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,o=[],a=lt.test(e)||"string"!=typeof e?b(e,t||this.context):0;for(;i>r;r++){n=this[r];while(n&&n.ownerDocument&&n!==t&&11!==n.nodeType){if(a?a.index(n)>-1:b.find.matchesSelector(n,e)){o.push(n);break}n=n.parentNode}}return this.pushStack(o.length>1?b.unique(o):o)},index:function(e){return e?"string"==typeof e?b.inArray(this[0],b(e)):b.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?b(e,t):b.makeArray(e&&e.nodeType?[e]:e),r=b.merge(this.get(),n);return this.pushStack(b.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),b.fn.andSelf=b.fn.addBack;function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}b.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return b.dir(e,"parentNode")},parentsUntil:function(e,t,n){return b.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return b.dir(e,"nextSibling")},prevAll:function(e){return b.dir(e,"previousSibling")},nextUntil:function(e,t,n){return b.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return b.dir(e,"previousSibling",n)},siblings:function(e){return b.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return b.sibling(e.firstChild)},contents:function(e){return b.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:b.merge([],e.childNodes)}},function(e,t){b.fn[e]=function(n,r){var i=b.map(this,t,n);return at.test(e)||(r=n),r&&"string"==typeof r&&(i=b.filter(r,i)),i=this.length>1&&!ct[e]?b.unique(i):i,this.length>1&&st.test(e)&&(i=i.reverse()),this.pushStack(i)}}),b.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),1===t.length?b.find.matchesSelector(t[0],e)?[t[0]]:[]:b.find.matches(e,t)},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!b(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(t=t||0,b.isFunction(t))return b.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return b.grep(e,function(e){return e===t===n});if("string"==typeof t){var r=b.grep(e,function(e){return 1===e.nodeType});if(ut.test(t))return b.filter(t,r,!n);t=b.filter(t,r)}return b.grep(e,function(e){return b.inArray(e,t)>=0===n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""],legend:[1,"
        ","
        "],area:[1,"",""],param:[1,"",""],thead:[1,"","
        "],tr:[2,"","
        "],col:[2,"","
        "],td:[3,"","
        "],_default:b.support.htmlSerialize?[0,"",""]:[1,"X
        ","
        "]},jt=dt(o),Dt=jt.appendChild(o.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,b.fn.extend({text:function(e){return b.access(this,function(e){return e===t?b.text(this):this.empty().append((this[0]&&this[0].ownerDocument||o).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(b.isFunction(e))return this.each(function(t){b(this).wrapAll(e.call(this,t))});if(this[0]){var t=b(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return b.isFunction(e)?this.each(function(t){b(this).wrapInner(e.call(this,t))}):this.each(function(){var t=b(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=b.isFunction(e);return this.each(function(n){b(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){b.nodeName(this,"body")||b(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.insertBefore(e,this.firstChild)})},before:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=0;for(;null!=(n=this[r]);r++)(!e||b.filter(e,[n]).length>0)&&(t||1!==n.nodeType||b.cleanData(Ot(n)),n.parentNode&&(t&&b.contains(n.ownerDocument,n)&&Mt(Ot(n,"script")),n.parentNode.removeChild(n)));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&b.cleanData(Ot(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&b.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return b.clone(this,e,t)})},html:function(e){return b.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!b.support.htmlSerialize&&mt.test(e)||!b.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(b.cleanData(Ot(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(e){var t=b.isFunction(e);return t||"string"==typeof e||(e=b(e).not(this).detach()),this.domManip([e],!0,function(e){var t=this.nextSibling,n=this.parentNode;n&&(b(this).remove(),n.insertBefore(e,t))})},detach:function(e){return this.remove(e,!0)},domManip:function(e,n,r){e=f.apply([],e);var i,o,a,s,u,l,c=0,p=this.length,d=this,h=p-1,g=e[0],m=b.isFunction(g);if(m||!(1>=p||"string"!=typeof g||b.support.checkClone)&&Ct.test(g))return this.each(function(i){var o=d.eq(i);m&&(e[0]=g.call(this,i,n?o.html():t)),o.domManip(e,n,r)});if(p&&(l=b.buildFragment(e,this[0].ownerDocument,!1,this),i=l.firstChild,1===l.childNodes.length&&(l=i),i)){for(n=n&&b.nodeName(i,"tr"),s=b.map(Ot(l,"script"),Ht),a=s.length;p>c;c++)o=l,c!==h&&(o=b.clone(o,!0,!0),a&&b.merge(s,Ot(o,"script"))),r.call(n&&b.nodeName(this[c],"table")?Lt(this[c],"tbody"):this[c],o,c);if(a)for(u=s[s.length-1].ownerDocument,b.map(s,qt),c=0;a>c;c++)o=s[c],kt.test(o.type||"")&&!b._data(o,"globalEval")&&b.contains(u,o)&&(o.src?b.ajax({url:o.src,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}):b.globalEval((o.text||o.textContent||o.innerHTML||"").replace(St,"")));l=i=null}return this}});function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function Ht(e){var t=e.getAttributeNode("type");return e.type=(t&&t.specified)+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function Mt(e,t){var n,r=0;for(;null!=(n=e[r]);r++)b._data(n,"globalEval",!t||b._data(t[r],"globalEval"))}function _t(e,t){if(1===t.nodeType&&b.hasData(e)){var n,r,i,o=b._data(e),a=b._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)b.event.add(t,n,s[n][r])}a.data&&(a.data=b.extend({},a.data))}}function Ft(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!b.support.noCloneEvent&&t[b.expando]){i=b._data(t);for(r in i.events)b.removeEvent(t,r,i.handle);t.removeAttribute(b.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),b.support.html5Clone&&e.innerHTML&&!b.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Nt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}b.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){b.fn[e]=function(e){var n,r=0,i=[],o=b(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),b(o[r])[t](n),d.apply(i,n.get());return this.pushStack(i)}});function Ot(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||b.nodeName(o,n)?s.push(o):b.merge(s,Ot(o,n));return n===t||n&&b.nodeName(e,n)?b.merge([e],s):s}function Bt(e){Nt.test(e.type)&&(e.defaultChecked=e.checked)}b.extend({clone:function(e,t,n){var r,i,o,a,s,u=b.contains(e.ownerDocument,e);if(b.support.html5Clone||b.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(b.support.noCloneEvent&&b.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||b.isXMLDoc(e)))for(r=Ot(o),s=Ot(e),a=0;null!=(i=s[a]);++a)r[a]&&Ft(i,r[a]);if(t)if(n)for(s=s||Ot(e),r=r||Ot(o),a=0;null!=(i=s[a]);a++)_t(i,r[a]);else _t(e,o);return r=Ot(o,"script"),r.length>0&&Mt(r,!u&&Ot(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,u,l,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===b.type(o))b.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),u=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[u]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!b.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!b.support.tbody){o="table"!==u||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)b.nodeName(l=o.childNodes[i],"tbody")&&!l.childNodes.length&&o.removeChild(l) -}b.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),b.support.appendChecked||b.grep(Ot(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===b.inArray(o,r))&&(a=b.contains(o.ownerDocument,o),s=Ot(f.appendChild(o),"script"),a&&Mt(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,u=b.expando,l=b.cache,p=b.support.deleteExpando,f=b.event.special;for(;null!=(n=e[s]);s++)if((t||b.acceptData(n))&&(o=n[u],a=o&&l[o])){if(a.events)for(r in a.events)f[r]?b.event.remove(n,r):b.removeEvent(n,r,a.handle);l[o]&&(delete l[o],p?delete n[u]:typeof n.removeAttribute!==i?n.removeAttribute(u):n[u]=null,c.push(o))}}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+x+")(.*)$","i"),Yt=RegExp("^("+x+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+x+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===b.css(e,"display")||!b.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=b._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=b._data(r,"olddisplay",un(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&b._data(r,"olddisplay",i?n:b.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}b.fn.extend({css:function(e,n){return b.access(this,function(e,n,r){var i,o,a={},s=0;if(b.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=b.css(e,n[s],!1,o);return a}return r!==t?b.style(e,n,r):b.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:nn(this))?b(this).show():b(this).hide()})}}),b.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":b.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,u=b.camelCase(n),l=e.style;if(n=b.cssProps[u]||(b.cssProps[u]=tn(l,u)),s=b.cssHooks[n]||b.cssHooks[u],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:l[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(b.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||b.cssNumber[u]||(r+="px"),b.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(l[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{l[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,u=b.camelCase(n);return n=b.cssProps[u]||(b.cssProps[u]=tn(e.style,u)),s=b.cssHooks[n]||b.cssHooks[u],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||b.isNumeric(o)?o||0:a):a},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s.getPropertyValue(n)||s[n]:t,l=e.style;return s&&(""!==u||b.contains(e.ownerDocument,e)||(u=b.style(e,n)),Yt.test(u)&&Ut.test(n)&&(i=l.width,o=l.minWidth,a=l.maxWidth,l.minWidth=l.maxWidth=l.width=u,u=s.width,l.width=i,l.minWidth=o,l.maxWidth=a)),u}):o.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s[n]:t,l=e.style;return null==u&&l&&l[n]&&(u=l[n]),Yt.test(u)&&!zt.test(n)&&(i=l.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),l.left="fontSize"===n?"1em":u,u=l.pixelLeft+"px",l.left=i,a&&(o.left=a)),""===u?"auto":u});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=b.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=b.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=b.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=b.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=b.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=b.support.boxSizing&&"border-box"===b.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(b.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function un(e){var t=o,n=Gt[e];return n||(n=ln(e,t),"none"!==n&&n||(Pt=(Pt||b("