diff options
74 files changed, 2645 insertions, 2638 deletions
diff --git a/.gitignore b/.gitignore index c757210654..06b13923f5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@  *~  /phpunit.xml +/phpBB/cache/twig/*  /phpBB/cache/*.html  /phpBB/cache/*.php  /phpBB/cache/*.lock diff --git a/phpBB/adm/style/acp_forums.html b/phpBB/adm/style/acp_forums.html index 38369ee207..7b1466cfbd 100644 --- a/phpBB/adm/style/acp_forums.html +++ b/phpBB/adm/style/acp_forums.html @@ -437,7 +437,7 @@  		</div>  	<!-- ENDIF --> -	<p><strong>{NAVIGATION}<!-- IF S_NO_FORUMS --> [<a href="{U_EDIT}">{L_EDIT}</a> | <a href="{U_DELETE}">{L_DELETE}</a><!-- IF not S_LINK --> | <a href="{U_SYNC}">{L_RESYNC}</a><!-- ENDIF --->]<!-- ENDIF --></strong></p> +	<p><strong>{NAVIGATION}<!-- IF S_NO_FORUMS --> [<a href="{U_EDIT}">{L_EDIT}</a> | <a href="{U_DELETE}">{L_DELETE}</a><!-- IF not S_LINK --> | <a href="{U_SYNC}">{L_RESYNC}</a><!-- ENDIF -->]<!-- ENDIF --></strong></p>  	<!-- IF .forums -->  		<table cellspacing="1"> diff --git a/phpBB/composer.json b/phpBB/composer.json index abc1df57b7..a114d5c0e0 100644 --- a/phpBB/composer.json +++ b/phpBB/composer.json @@ -6,7 +6,8 @@  		"symfony/event-dispatcher": "2.1.*",  		"symfony/http-kernel": "2.1.*",  		"symfony/routing": "2.1.*", -		"symfony/yaml": "2.1.*" +		"symfony/yaml": "2.1.*", +		"twig/twig": "1.13.*"  	},  	"require-dev": {  		"fabpot/goutte": "v0.1.0", diff --git a/phpBB/composer.lock b/phpBB/composer.lock index e3b564fb1a..a20c6303ee 100644 --- a/phpBB/composer.lock +++ b/phpBB/composer.lock @@ -3,21 +3,21 @@          "This file locks the dependencies of your project to a known state",          "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"      ], -    "hash": "3792dc25490f24210ece3b40789c5b98", +    "hash": "e4a4f4848a7201d7e044446001afda29",      "packages": [          {              "name": "symfony/config", -            "version": "v2.1.10", +            "version": "v2.1.11",              "target-dir": "Symfony/Component/Config",              "source": {                  "type": "git",                  "url": "https://github.com/symfony/Config.git", -                "reference": "v2.1.10" +                "reference": "v2.1.11"              },              "dist": {                  "type": "zip", -                "url": "https://api.github.com/repos/symfony/Config/zipball/v2.1.10", -                "reference": "v2.1.10", +                "url": "https://api.github.com/repos/symfony/Config/zipball/v2.1.11", +                "reference": "v2.1.11",                  "shasum": ""              },              "require": { @@ -29,7 +29,6 @@                      "Symfony\\Component\\Config": ""                  }              }, -            "notification-url": "https://packagist.org/downloads/",              "license": [                  "MIT"              ], @@ -45,21 +44,21 @@              ],              "description": "Symfony Config Component",              "homepage": "http://symfony.com", -            "time": "2013-04-22 04:28:40" +            "time": "2013-05-09 15:22:40"          },          {              "name": "symfony/dependency-injection", -            "version": "v2.1.10", +            "version": "v2.1.11",              "target-dir": "Symfony/Component/DependencyInjection",              "source": {                  "type": "git",                  "url": "https://github.com/symfony/DependencyInjection.git", -                "reference": "v2.1.10" +                "reference": "v2.1.11"              },              "dist": {                  "type": "zip", -                "url": "https://api.github.com/repos/symfony/DependencyInjection/zipball/v2.1.10", -                "reference": "v2.1.10", +                "url": "https://api.github.com/repos/symfony/DependencyInjection/zipball/v2.1.11", +                "reference": "v2.1.11",                  "shasum": ""              },              "require": { @@ -79,7 +78,6 @@                      "Symfony\\Component\\DependencyInjection": ""                  }              }, -            "notification-url": "https://packagist.org/downloads/",              "license": [                  "MIT"              ], @@ -99,17 +97,17 @@          },          {              "name": "symfony/event-dispatcher", -            "version": "v2.1.10", +            "version": "v2.1.11",              "target-dir": "Symfony/Component/EventDispatcher",              "source": {                  "type": "git",                  "url": "https://github.com/symfony/EventDispatcher.git", -                "reference": "v2.1.10" +                "reference": "v2.1.11"              },              "dist": {                  "type": "zip", -                "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/v2.1.10", -                "reference": "v2.1.10", +                "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/v2.1.11", +                "reference": "v2.1.11",                  "shasum": ""              },              "require": { @@ -128,7 +126,6 @@                      "Symfony\\Component\\EventDispatcher": ""                  }              }, -            "notification-url": "https://packagist.org/downloads/",              "license": [                  "MIT"              ], @@ -148,17 +145,17 @@          },          {              "name": "symfony/http-foundation", -            "version": "v2.1.10", +            "version": "v2.1.11",              "target-dir": "Symfony/Component/HttpFoundation",              "source": {                  "type": "git",                  "url": "https://github.com/symfony/HttpFoundation.git", -                "reference": "v2.1.10" +                "reference": "v2.1.11"              },              "dist": {                  "type": "zip", -                "url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/v2.1.10", -                "reference": "v2.1.10", +                "url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/v2.1.11", +                "reference": "v2.1.11",                  "shasum": ""              },              "require": { @@ -171,7 +168,6 @@                      "SessionHandlerInterface": "Symfony/Component/HttpFoundation/Resources/stubs"                  }              }, -            "notification-url": "https://packagist.org/downloads/",              "license": [                  "MIT"              ], @@ -187,21 +183,21 @@              ],              "description": "Symfony HttpFoundation Component",              "homepage": "http://symfony.com", -            "time": "2013-04-30 17:01:33" +            "time": "2013-05-26 18:42:07"          },          {              "name": "symfony/http-kernel", -            "version": "v2.1.10", +            "version": "v2.1.11",              "target-dir": "Symfony/Component/HttpKernel",              "source": {                  "type": "git",                  "url": "https://github.com/symfony/HttpKernel.git", -                "reference": "v2.1.10" +                "reference": "v2.1.11"              },              "dist": {                  "type": "zip", -                "url": "https://api.github.com/repos/symfony/HttpKernel/zipball/v2.1.10", -                "reference": "v2.1.10", +                "url": "https://api.github.com/repos/symfony/HttpKernel/zipball/v2.1.11", +                "reference": "v2.1.11",                  "shasum": ""              },              "require": { @@ -233,7 +229,6 @@                      "Symfony\\Component\\HttpKernel": ""                  }              }, -            "notification-url": "https://packagist.org/downloads/",              "license": [                  "MIT"              ], @@ -249,21 +244,21 @@              ],              "description": "Symfony HttpKernel Component",              "homepage": "http://symfony.com", -            "time": "2013-05-06 11:01:51" +            "time": "2013-06-02 12:29:05"          },          {              "name": "symfony/routing", -            "version": "v2.1.9", +            "version": "v2.1.11",              "target-dir": "Symfony/Component/Routing",              "source": {                  "type": "git",                  "url": "https://github.com/symfony/Routing.git", -                "reference": "v2.1.9" +                "reference": "v2.1.11"              },              "dist": {                  "type": "zip", -                "url": "https://api.github.com/repos/symfony/Routing/zipball/v2.1.9", -                "reference": "v2.1.9", +                "url": "https://api.github.com/repos/symfony/Routing/zipball/v2.1.11", +                "reference": "v2.1.11",                  "shasum": ""              },              "require": { @@ -286,7 +281,6 @@                      "Symfony\\Component\\Routing": ""                  }              }, -            "notification-url": "https://packagist.org/downloads/",              "license": [                  "MIT"              ], @@ -302,21 +296,21 @@              ],              "description": "Symfony Routing Component",              "homepage": "http://symfony.com", -            "time": "2013-03-23 07:47:35" +            "time": "2013-05-06 10:48:41"          },          {              "name": "symfony/yaml", -            "version": "v2.1.9", +            "version": "v2.1.11",              "target-dir": "Symfony/Component/Yaml",              "source": {                  "type": "git",                  "url": "https://github.com/symfony/Yaml.git", -                "reference": "v2.1.9" +                "reference": "v2.1.11"              },              "dist": {                  "type": "zip", -                "url": "https://api.github.com/repos/symfony/Yaml/zipball/v2.1.9", -                "reference": "v2.1.9", +                "url": "https://api.github.com/repos/symfony/Yaml/zipball/v2.1.11", +                "reference": "v2.1.11",                  "shasum": ""              },              "require": { @@ -328,7 +322,6 @@                      "Symfony\\Component\\Yaml": ""                  }              }, -            "notification-url": "https://packagist.org/downloads/",              "license": [                  "MIT"              ], @@ -344,7 +337,55 @@              ],              "description": "Symfony Yaml Component",              "homepage": "http://symfony.com", -            "time": "2013-03-23 01:54:33" +            "time": "2013-05-10 00:09:46" +        }, +        { +            "name": "twig/twig", +            "version": "v1.13.1", +            "source": { +                "type": "git", +                "url": "https://github.com/fabpot/Twig.git", +                "reference": "v1.13.1" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/fabpot/Twig/zipball/v1.13.1", +                "reference": "v1.13.1", +                "shasum": "" +            }, +            "require": { +                "php": ">=5.2.4" +            }, +            "type": "library", +            "extra": { +                "branch-alias": { +                    "dev-master": "1.13-dev" +                } +            }, +            "autoload": { +                "psr-0": { +                    "Twig_": "lib/" +                } +            }, +            "license": [ +                "BSD-3" +            ], +            "authors": [ +                { +                    "name": "Fabien Potencier", +                    "email": "fabien@symfony.com" +                }, +                { +                    "name": "Armin Ronacher", +                    "email": "armin.ronacher@active-4.com" +                } +            ], +            "description": "Twig, the flexible, fast, and secure template language for PHP", +            "homepage": "http://twig.sensiolabs.org", +            "keywords": [ +                "templating" +            ], +            "time": "2013-06-06 06:06:01"          }      ],      "packages-dev": [ @@ -552,16 +593,16 @@          },          {              "name": "phpunit/php-code-coverage", -            "version": "1.2.9", +            "version": "1.2.11",              "source": {                  "type": "git",                  "url": "https://github.com/sebastianbergmann/php-code-coverage.git", -                "reference": "1.2.9" +                "reference": "1.2.11"              },              "dist": {                  "type": "zip", -                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/1.2.9", -                "reference": "1.2.9", +                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/1.2.11", +                "reference": "1.2.11",                  "shasum": ""              },              "require": { @@ -570,6 +611,9 @@                  "phpunit/php-text-template": ">=1.1.1@stable",                  "phpunit/php-token-stream": ">=1.1.3@stable"              }, +            "require-dev": { +                "phpunit/phpunit": "3.7.*" +            },              "suggest": {                  "ext-dom": "*",                  "ext-xdebug": ">=2.0.5" @@ -601,7 +645,7 @@                  "testing",                  "xunit"              ], -            "time": "2013-02-26 18:55:56" +            "time": "2013-05-23 18:23:24"          },          {              "name": "phpunit/php-file-iterator", @@ -783,16 +827,16 @@          },          {              "name": "phpunit/phpunit", -            "version": "3.7.19", +            "version": "3.7.21",              "source": {                  "type": "git",                  "url": "https://github.com/sebastianbergmann/phpunit.git", -                "reference": "3.7.19" +                "reference": "3.7.21"              },              "dist": {                  "type": "zip", -                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3.7.19", -                "reference": "3.7.19", +                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3.7.21", +                "reference": "3.7.21",                  "shasum": ""              },              "require": { @@ -806,7 +850,7 @@                  "phpunit/php-text-template": ">=1.1.1",                  "phpunit/php-timer": ">=1.0.2,<1.1.0",                  "phpunit/phpunit-mock-objects": ">=1.2.0,<1.3.0", -                "symfony/yaml": ">=2.0.0,<2.3.0" +                "symfony/yaml": ">=2.0,<3.0"              },              "require-dev": {                  "pear-pear/pear": "1.9.4" @@ -853,7 +897,7 @@                  "testing",                  "xunit"              ], -            "time": "2013-03-25 11:45:06" +            "time": "2013-05-23 18:54:29"          },          {              "name": "phpunit/phpunit-mock-objects", @@ -906,17 +950,17 @@          },          {              "name": "symfony/browser-kit", -            "version": "v2.1.10", +            "version": "v2.1.11",              "target-dir": "Symfony/Component/BrowserKit",              "source": {                  "type": "git",                  "url": "https://github.com/symfony/BrowserKit.git", -                "reference": "v2.1.10" +                "reference": "v2.1.11"              },              "dist": {                  "type": "zip", -                "url": "https://api.github.com/repos/symfony/BrowserKit/zipball/v2.1.10", -                "reference": "v2.1.10", +                "url": "https://api.github.com/repos/symfony/BrowserKit/zipball/v2.1.11", +                "reference": "v2.1.11",                  "shasum": ""              },              "require": { @@ -956,17 +1000,17 @@          },          {              "name": "symfony/css-selector", -            "version": "v2.1.10", +            "version": "v2.1.11",              "target-dir": "Symfony/Component/CssSelector",              "source": {                  "type": "git",                  "url": "https://github.com/symfony/CssSelector.git", -                "reference": "v2.1.10" +                "reference": "v2.1.11"              },              "dist": {                  "type": "zip", -                "url": "https://api.github.com/repos/symfony/CssSelector/zipball/v2.1.10", -                "reference": "v2.1.10", +                "url": "https://api.github.com/repos/symfony/CssSelector/zipball/v2.1.11", +                "reference": "v2.1.11",                  "shasum": ""              },              "require": { @@ -994,21 +1038,21 @@              ],              "description": "Symfony CssSelector Component",              "homepage": "http://symfony.com", -            "time": "2013-01-09 08:51:07" +            "time": "2013-05-17 00:31:34"          },          {              "name": "symfony/dom-crawler", -            "version": "v2.1.10", +            "version": "v2.1.11",              "target-dir": "Symfony/Component/DomCrawler",              "source": {                  "type": "git",                  "url": "https://github.com/symfony/DomCrawler.git", -                "reference": "v2.1.10" +                "reference": "v2.1.11"              },              "dist": {                  "type": "zip", -                "url": "https://api.github.com/repos/symfony/DomCrawler/zipball/v2.1.10", -                "reference": "v2.1.10", +                "url": "https://api.github.com/repos/symfony/DomCrawler/zipball/v2.1.11", +                "reference": "v2.1.11",                  "shasum": ""              },              "require": { @@ -1042,21 +1086,21 @@              ],              "description": "Symfony DomCrawler Component",              "homepage": "http://symfony.com", -            "time": "2013-03-27 17:13:16" +            "time": "2013-05-16 00:06:15"          },          {              "name": "symfony/finder", -            "version": "v2.1.10", +            "version": "v2.1.11",              "target-dir": "Symfony/Component/Finder",              "source": {                  "type": "git",                  "url": "https://github.com/symfony/Finder.git", -                "reference": "v2.1.10" +                "reference": "v2.1.11"              },              "dist": {                  "type": "zip", -                "url": "https://api.github.com/repos/symfony/Finder/zipball/v2.1.10", -                "reference": "v2.1.10", +                "url": "https://api.github.com/repos/symfony/Finder/zipball/v2.1.11", +                "reference": "v2.1.11",                  "shasum": ""              },              "require": { @@ -1084,21 +1128,21 @@              ],              "description": "Symfony Finder Component",              "homepage": "http://symfony.com", -            "time": "2013-03-06 19:26:55" +            "time": "2013-05-25 15:47:15"          },          {              "name": "symfony/process", -            "version": "v2.1.9", +            "version": "v2.1.11",              "target-dir": "Symfony/Component/Process",              "source": {                  "type": "git",                  "url": "https://github.com/symfony/Process.git", -                "reference": "v2.1.9" +                "reference": "v2.1.11"              },              "dist": {                  "type": "zip", -                "url": "https://api.github.com/repos/symfony/Process/zipball/v2.1.9", -                "reference": "v2.1.9", +                "url": "https://api.github.com/repos/symfony/Process/zipball/v2.1.11", +                "reference": "v2.1.11",                  "shasum": ""              },              "require": { @@ -1126,7 +1170,7 @@              ],              "description": "Symfony Process Component",              "homepage": "http://symfony.com", -            "time": "2013-03-23 07:44:01" +            "time": "2013-05-06 10:21:56"          }      ],      "aliases": [ diff --git a/phpBB/config/services.yml b/phpBB/config/services.yml index 4b272c6abd..e228b3d83f 100644 --- a/phpBB/config/services.yml +++ b/phpBB/config/services.yml @@ -79,7 +79,7 @@ services:          arguments:              - @user              - @service_container -            - @ext.finder +            - @style      cron.task_collection:          class: phpbb_di_service_collection @@ -255,15 +255,15 @@ services:          class: phpbb_style_path_provider      template: -        class: phpbb_template +        class: phpbb_template_twig          arguments:              - %core.root_path%              - %core.php_ext%              - @config              - @user -            - @style.resource_locator              - @template_context              - @ext.manager +            - %core.adm_relative_path%      template_context:          class: phpbb_template_context diff --git a/phpBB/includes/bbcode.php b/phpBB/includes/bbcode.php index c198abeb54..fd00728510 100644 --- a/phpBB/includes/bbcode.php +++ b/phpBB/includes/bbcode.php @@ -134,11 +134,11 @@ class bbcode  			$style_resource_locator = new phpbb_style_resource_locator();  			$style_path_provider = new phpbb_style_extension_path_provider($phpbb_extension_manager, new phpbb_style_path_provider(), $phpbb_root_path); -			$template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $style_resource_locator, new phpbb_template_context(), $phpbb_extension_manager); +			$template = new phpbb_template_twig($phpbb_root_path, $phpEx, $config, $user, new phpbb_template_context(), $phpbb_extension_manager);  			$style = new phpbb_style($phpbb_root_path, $phpEx, $config, $user, $style_resource_locator, $style_path_provider, $template);  			$style->set_style();  			$template->set_filenames(array('bbcode.html' => 'bbcode.html')); -			$this->template_filename = $style_resource_locator->get_source_file_for_handle('bbcode.html'); +			$this->template_filename = $template->get_source_file_for_handle('bbcode.html');  		}  		$bbcode_ids = $rowset = $sql = array(); diff --git a/phpBB/includes/controller/resolver.php b/phpBB/includes/controller/resolver.php index ee469aa9c8..95dfc8da8e 100644 --- a/phpBB/includes/controller/resolver.php +++ b/phpBB/includes/controller/resolver.php @@ -38,15 +38,23 @@ class phpbb_controller_resolver implements ControllerResolverInterface  	protected $container;  	/** +	* phpbb_style object +	* @var phpbb_style +	*/ +	protected $style; + +	/**  	* Construct method  	*  	* @param phpbb_user $user User Object  	* @param ContainerInterface $container ContainerInterface object +	* @param phpbb_style $style  	*/ -	public function __construct(phpbb_user $user, ContainerInterface $container) +	public function __construct(phpbb_user $user, ContainerInterface $container, phpbb_style $style = null)  	{  		$this->user = $user;  		$this->container = $container; +		$this->style = $style;  	}  	/** @@ -80,6 +88,24 @@ class phpbb_controller_resolver implements ControllerResolverInterface  		$controller_object = $this->container->get($service); +		/* +		* If this is an extension controller, we'll try to automatically set +		* the style paths for the extension (the ext author can change them +		* if necessary). +		*/ +		$controller_dir = explode('_', get_class($controller_object)); + +		// 0 phpbb, 1 ext, 2 vendor, 3 extension name, ... +		if (!is_null($this->style) && isset($controller_dir[3]) && $controller_dir[1] === 'ext') +		{ +			$controller_style_dir = 'ext/' . $controller_dir[2] . '/' . $controller_dir[3] . '/styles'; + +			if (is_dir($controller_style_dir)) +			{ +				$this->style->set_style(array($controller_style_dir, 'styles')); +			} +		} +  		return array($controller_object, $method);  	} diff --git a/phpBB/includes/functions_messenger.php b/phpBB/includes/functions_messenger.php index a646f35fdd..0222a57bcc 100644 --- a/phpBB/includes/functions_messenger.php +++ b/phpBB/includes/functions_messenger.php @@ -27,8 +27,9 @@ class messenger  	var $mail_priority = MAIL_NORMAL_PRIORITY;  	var $use_queue = true; -	var $tpl_obj = NULL; -	var $tpl_msg = array(); +	/** @var phpbb_template */ +	protected $template; +  	var $eol = "\n";  	/** @@ -55,10 +56,10 @@ class messenger  		$this->vars = $this->msg = $this->replyto = $this->from = '';  		$this->mail_priority = MAIL_NORMAL_PRIORITY;  	} -	 +  	/**  	* Set addresses for to/im as available -	*  +	*  	* @param array $user User row  	*/  	function set_addresses($user) @@ -210,6 +211,8 @@ class messenger  	{  		global $config, $phpbb_root_path, $phpEx, $user, $phpbb_extension_manager; +		$this->setup_template(); +  		if (!trim($template_file))  		{  			trigger_error('No template file for emailing set.', E_USER_ERROR); @@ -219,46 +222,43 @@ class messenger  		{  			// fall back to board default language if the user's language is  			// missing $template_file.  If this does not exist either, -			// $tpl->set_filenames will do a trigger_error +			// $this->template->set_filenames will do a trigger_error  			$template_lang = basename($config['default_lang']);  		} -		// tpl_msg now holds a template object we can use to parse the template file -		if (!isset($this->tpl_msg[$template_lang . $template_file])) +		if ($template_path)  		{ -			$style_resource_locator = new phpbb_style_resource_locator(); -			$style_path_provider = new phpbb_style_extension_path_provider($phpbb_extension_manager, new phpbb_style_path_provider(), $phpbb_root_path); -			$tpl = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $style_resource_locator, new phpbb_template_context(), $phpbb_extension_manager); -			$style = new phpbb_style($phpbb_root_path, $phpEx, $config, $user, $style_resource_locator, $style_path_provider, $tpl); - -			$this->tpl_msg[$template_lang . $template_file] = $tpl; +			$template_paths = array( +				$template_path, +			); +		} +		else +		{ +			$template_path = (!empty($user->lang_path)) ? $user->lang_path : $phpbb_root_path . 'language/'; +			$template_path .= $template_lang . '/email'; -			$fallback_template_path = false; +			$template_paths = array( +				$template_path, +			); -			if (!$template_path) +			// we can only specify default language fallback when the path is not a custom one for which we +			// do not know the default language alternative +			if ($template_lang !== basename($config['default_lang']))  			{ -				$template_path = (!empty($user->lang_path)) ? $user->lang_path : $phpbb_root_path . 'language/'; -				$template_path .= $template_lang . '/email'; +				$fallback_template_path = (!empty($user->lang_path)) ? $user->lang_path : $phpbb_root_path . 'language/'; +				$fallback_template_path .= basename($config['default_lang']) . '/email'; -				// we can only specify default language fallback when the path is not a custom one for which we -				// do not know the default language alternative -				if ($template_lang !== basename($config['default_lang'])) -				{ -					$fallback_template_path = (!empty($user->lang_path)) ? $user->lang_path : $phpbb_root_path . 'language/'; -					$fallback_template_path .= basename($config['default_lang']) . '/email'; -				} +				$template_paths[] = $fallback_template_path;  			} +		} -			$style->set_custom_style($template_lang . '_email', array($template_path, $fallback_template_path), array(), ''); +		$this->set_template_paths($template_lang . '_email', $template_paths); -			$tpl->set_filenames(array( -				'body'		=> $template_file . '.txt', -			)); -		} +		$this->template->set_filenames(array( +			'body'		=> $template_file . '.txt', +		)); -		$this->tpl_obj = &$this->tpl_msg[$template_lang . $template_file]; -		$this->vars = &$this->tpl_obj->_rootref; -		$this->tpl_msg = ''; +		$this->vars = $this->template->get_template_vars();  		return true;  	} @@ -268,22 +268,16 @@ class messenger  	*/  	function assign_vars($vars)  	{ -		if (!is_object($this->tpl_obj)) -		{ -			return; -		} +		$this->setup_template(); -		$this->tpl_obj->assign_vars($vars); +		$this->template->assign_vars($vars);  	}  	function assign_block_vars($blockname, $vars)  	{ -		if (!is_object($this->tpl_obj)) -		{ -			return; -		} +		$this->setup_template(); -		$this->tpl_obj->assign_block_vars($blockname, $vars); +		$this->template->assign_block_vars($blockname, $vars);  	}  	/** @@ -316,7 +310,7 @@ class messenger  		}  		// Parse message through template -		$this->msg = trim($this->tpl_obj->assign_display('body')); +		$this->msg = trim($this->template->assign_display('body'));  		// Because we use \n for newlines in the body message we need to fix line encoding errors for those admins who uploaded email template files in the wrong encoding  		$this->msg = str_replace("\r\n", "\n", $this->msg); @@ -643,6 +637,31 @@ class messenger  		unset($addresses);  		return true;  	} + +	/** +	* Setup template engine +	*/ +	protected function setup_template() +	{ +		global $config, $phpbb_root_path, $phpEx, $user, $phpbb_extension_manager; + +		if ($this->template instanceof phpbb_template) +		{ +			return; +		} + +		$this->template = new phpbb_template_twig($phpbb_root_path, $phpEx, $config, $user, new phpbb_template_context(), $phpbb_extension_manager); +	} + +	/** +	* Set template paths to load +	*/ +	protected function set_template_paths($path_name, $paths) +	{ +		$this->setup_template(); + +		$this->template->set_style_names(array($path_name), $paths); +	}  }  /** diff --git a/phpBB/includes/functions_module.php b/phpBB/includes/functions_module.php index 0d387ace6d..99c24fcb19 100644 --- a/phpBB/includes/functions_module.php +++ b/phpBB/includes/functions_module.php @@ -455,7 +455,7 @@ class p_master  	*/  	function load_active($mode = false, $module_url = false, $execute_module = true)  	{ -		global $phpbb_root_path, $phpbb_admin_path, $phpEx, $user; +		global $phpbb_root_path, $phpbb_admin_path, $phpEx, $user, $phpbb_style;  		$module_path = $this->include_path . $this->p_class;  		$icat = request_var('icat', ''); @@ -494,6 +494,24 @@ class p_master  		// We pre-define the action parameter we are using all over the place  		if (defined('IN_ADMIN'))  		{ +			/* +			* If this is an extension module, we'll try to automatically set +			* the style paths for the extension (the ext author can change them +			* if necessary). +			*/ +			$module_dir = explode('_', get_class($this->module)); + +			// 0 phpbb, 1 ext, 2 vendor, 3 extension name, ... +			if (isset($module_dir[3]) && $module_dir[1] === 'ext') +			{ +				$module_style_dir = $phpbb_root_path . 'ext/' . $module_dir[2] . '/' . $module_dir[3] . '/adm/style'; + +				if (is_dir($module_style_dir)) +				{ +					$phpbb_style->set_custom_style('admin', array($module_style_dir, $phpbb_admin_path . 'style'), array(), ''); +				} +			} +  			// Is first module automatically enabled a duplicate and the category not passed yet?  			if (!$icat && $this->module_ary[$this->active_module_row_id]['is_duplicate'])  			{ @@ -505,6 +523,24 @@ class p_master  		}  		else  		{ +			/* +			* If this is an extension module, we'll try to automatically set +			* the style paths for the extension (the ext author can change them +			* if necessary). +			*/ +			$module_dir = explode('_', get_class($this->module)); + +			// 0 phpbb, 1 ext, 2 vendor, 3 extension name, ... +			if (isset($module_dir[3]) && $module_dir[1] === 'ext') +			{ +				$module_style_dir = 'ext/' . $module_dir[2] . '/' . $module_dir[3] . '/styles'; + +				if (is_dir($phpbb_root_path . $module_style_dir)) +				{ +					$phpbb_style->set_style(array($module_style_dir, 'styles')); +				} +			} +  			// If user specified the module url we will use it...  			if ($module_url !== false)  			{ diff --git a/phpBB/includes/style/style.php b/phpBB/includes/style/style.php index 4703c3a219..034f518091 100644 --- a/phpBB/includes/style/style.php +++ b/phpBB/includes/style/style.php @@ -85,28 +85,62 @@ class phpbb_style  	}  	/** -	* Set style location based on (current) user's chosen style. +	* Get the style tree of the style preferred by the current user +	* +	* @return array Style tree, most specific first  	*/ -	public function set_style() +	public function get_user_style()  	{ -		$style_path = $this->user->style['style_path']; -		$style_dirs = ($this->user->style['style_parent_id']) ? array_reverse(explode('/', $this->user->style['style_parent_tree'])) : array(); +		$style_list = array( +			$this->user->style['style_path'], +		); -		$names = array($style_path); -		foreach ($style_dirs as $dir) +		if ($this->user->style['style_parent_id'])  		{ -			$names[] = $dir; +			$style_list = array_merge($style_list, array_reverse(explode('/', $this->user->style['style_parent_tree'])));  		} -		// Add 'all' path, used as last fallback path by events and extensions -		//$names[] = 'all'; + +		return $style_list; +	} + +	/** +	* Set style location based on (current) user's chosen style. +	* +	* @param array $style_directories The directories to add style paths for +	* 	E.g. array('ext/foo/bar/styles', 'styles') +	* 	Default: array('styles') (phpBB's style directory) +	* @return bool true +	*/ +	public function set_style($style_directories = array('styles')) +	{ +		$this->names = $this->get_user_style();  		$paths = array(); -		foreach ($names as $name) +		foreach ($style_directories as $directory)  		{ -			$paths[] = $this->get_style_path($name); +			foreach ($this->names as $name) +			{ +				$path = $this->get_style_path($name, $directory); + +				if (is_dir($path)) +				{ +					$paths[] = $path; +				} +			}  		} -		return $this->set_custom_style($style_path, $paths, $names); +		$this->provider->set_styles($paths); +		$this->locator->set_paths($this->provider); + +		$new_paths = array(); +		foreach ($paths as $path) +		{ +			$new_paths[] = $path . '/template/'; +		} + +		$this->template->set_style_names($this->names, $new_paths, ($style_directories === array('styles'))); + +		return true;  	}  	/** @@ -118,6 +152,7 @@ class phpbb_style  	* @param array or string $paths Array of style paths, relative to current root directory  	* @param array $names Array of names of templates in inheritance tree order, used by extensions. If empty, $name will be used.  	* @param string $template_path Path to templates, relative to style directory. False if path should be set to default (templates/). +	* @return bool true  	*/  	public function set_custom_style($name, $paths, $names = array(), $template_path = false)  	{ @@ -135,18 +170,18 @@ class phpbb_style  		$this->provider->set_styles($paths);  		$this->locator->set_paths($this->provider); -		$this->template->set_style_names($names); -  		if ($template_path !== false)  		{  			$this->locator->set_template_path($template_path);  		} -		else + +		$new_paths = array(); +		foreach ($paths as $path)  		{ -			$this->locator->set_default_template_path(); +			$new_paths[] = $path . '/' . (($template_path !== false) ? $template_path : 'template/');  		} -		$this->template->cachepath = $this->phpbb_root_path . 'cache/tpl_' . str_replace('_', '-', $name) . '_'; +		$this->template->set_style_names($names, $new_paths);  		return true;  	} @@ -155,11 +190,14 @@ class phpbb_style  	* Get location of style directory for specific style_path  	*  	* @param string $path Style path, such as "prosilver" +	* @param string $style_base_directory The base directory the style is in +	* 	E.g. 'styles', 'ext/foo/bar/styles' +	* 	Default: 'styles'  	* @return string Path to style directory, relative to current path  	*/ -	public function get_style_path($path) +	public function get_style_path($path, $style_base_directory = 'styles')  	{ -		return $this->phpbb_root_path . 'styles/' . $path; +		return $this->phpbb_root_path . trim($style_base_directory, '/') . '/' . $path;  	}  	/** diff --git a/phpBB/includes/template/compile.php b/phpBB/includes/template/compile.php deleted file mode 100644 index 76cb3011df..0000000000 --- a/phpBB/includes/template/compile.php +++ /dev/null @@ -1,166 +0,0 @@ -<?php -/** -* -* @package phpBB3 -* @copyright (c) 2005 phpBB Group -* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 -* -*/ - -/** -* @ignore -*/ -if (!defined('IN_PHPBB')) -{ -	exit; -} - -stream_filter_register('phpbb_template', 'phpbb_template_filter'); - -/** -* Extension of template class - Functions needed for compiling templates only. -* -* @package phpBB3 -* @uses template_filter As a PHP stream filter to perform compilation of templates -*/ -class phpbb_template_compile -{ -	/** -	* Array of parameters to forward to template filter -	* -	* @var array -	*/ -	private $filter_params; - -	/** -	* Array of default parameters -	* -	* @var array -	*/ -	private $default_filter_params; - -	/** -	* Constructor. -	* -	* @param bool $allow_php Whether PHP code will be allowed in templates (inline PHP code, PHP tag and INCLUDEPHP tag) -	* @param array $style_names Name of style to which the template being compiled belongs and parents in style tree order -	* @param phpbb_style_resource_locator $locator Resource locator -	* @param string $phpbb_root_path Path to phpBB root directory -	* @param phpbb_extension_manager $extension_manager Extension manager to use for finding template fragments in extensions; if null, template events will not be invoked -	* @param phpbb_user $user Current user -	*/ -	public function __construct($allow_php, $style_names, $locator, $phpbb_root_path, $extension_manager = null, $user = null) -	{ -		$this->filter_params = $this->default_filter_params = array( -			'allow_php'			=> $allow_php, -			'style_names'		=> $style_names, -			'locator'			=> $locator, -			'phpbb_root_path'	=> $phpbb_root_path, -			'extension_manager'	=> $extension_manager, -			'user'          	=> $user, -			'template_compile'	=> $this, -			'cleanup'			=> true, -		); -	} - -	/** -	* Set filter parameters -	* -	* @param array $params Array of parameters (will be merged onto $this->filter_params) -	*/ -	public function set_filter_params($params) -	{ -		$this->filter_params = array_merge( -			$this->filter_params, -			$params -		); -	} - -	/** -	* Reset filter parameters to their default settings -	*/ -	public function reset_filter_params() -	{ -		$this->filter_params = $this->default_filter_params; -	} - -	/** -	* Compiles template in $source_file and writes compiled template to -	* cache directory -	* -	* @param string $handle Template handle to compile -	* @param string $source_file Source template file -	* @return bool Return true on success otherwise false -	*/ -	public function compile_file_to_file($source_file, $compiled_file) -	{ -		$lock = new phpbb_lock_flock($compiled_file); -		$lock->acquire(); - -		$source_handle = @fopen($source_file, 'rb'); -		$destination_handle = @fopen($compiled_file, 'wb'); - -		if (!$source_handle || !$destination_handle) -		{ -			return false; -		} - -		$this->compile_stream_to_stream($source_handle, $destination_handle); - -		@fclose($source_handle); -		@fclose($destination_handle); - -		phpbb_chmod($compiled_file, CHMOD_READ | CHMOD_WRITE); - -		$lock->release(); - -		clearstatcache(); - -		return true; -	} - -	/** -	* Compiles a template located at $source_file. -	* -	* Returns PHP source suitable for eval(). -	* -	* @param string $source_file Source template file -	* @return string|bool Return compiled code on successful compilation otherwise false -	*/ -	public function compile_file($source_file) -	{ -		$source_handle = @fopen($source_file, 'rb'); -		$destination_handle = @fopen('php://temp' ,'r+b'); - -		if (!$source_handle || !$destination_handle) -		{ -			return false; -		} - -		$this->compile_stream_to_stream($source_handle, $destination_handle); - -		@fclose($source_handle); - -		rewind($destination_handle); -		$contents = stream_get_contents($destination_handle); -		@fclose($dest_handle); - -		return $contents; -	} - -	/** -	* Compiles contents of $source_stream into $dest_stream. -	* -	* A stream filter is appended to $source_stream as part of the -	* process. -	* -	* @param resource $source_stream Source stream -	* @param resource $dest_stream Destination stream -	* @return null -	*/ -	private function compile_stream_to_stream($source_stream, $dest_stream) -	{ -		stream_filter_append($source_stream, 'phpbb_template', null, $this->filter_params); -		stream_copy_to_stream($source_stream, $dest_stream); -	} -} diff --git a/phpBB/includes/template/context.php b/phpBB/includes/template/context.php index ec09da1cf3..c5ce7422b9 100644 --- a/phpBB/includes/template/context.php +++ b/phpBB/includes/template/context.php @@ -138,7 +138,7 @@ class phpbb_template_context  			}  			$s_row_count = isset($str[$blocks[$blockcount]]) ? sizeof($str[$blocks[$blockcount]]) : 0; -			$vararray['S_ROW_COUNT'] = $s_row_count; +			$vararray['S_ROW_COUNT'] = $vararray['S_ROW_NUM'] = $s_row_count;  			// Assign S_FIRST_ROW  			if (!$s_row_count) @@ -146,6 +146,9 @@ class phpbb_template_context  				$vararray['S_FIRST_ROW'] = true;  			} +			// Assign S_BLOCK_NAME +			$vararray['S_BLOCK_NAME'] = $blocks[$blockcount]; +  			// Now the tricky part, we always assign S_LAST_ROW and remove the entry before  			// This is much more clever than going through the complete template data on display (phew)  			$vararray['S_LAST_ROW'] = true; @@ -158,12 +161,18 @@ class phpbb_template_context  			// We're adding a new iteration to this block with the given  			// variable assignments.  			$str[$blocks[$blockcount]][] = $vararray; + +			// Set S_NUM_ROWS +			foreach ($str[$blocks[$blockcount]] as &$mod_block) +			{ +				$mod_block['S_NUM_ROWS'] = sizeof($str[$blocks[$blockcount]]); +			}  		}  		else  		{  			// Top-level block.  			$s_row_count = (isset($this->tpldata[$blockname])) ? sizeof($this->tpldata[$blockname]) : 0; -			$vararray['S_ROW_COUNT'] = $s_row_count; +			$vararray['S_ROW_COUNT'] = $vararray['S_ROW_NUM'] = $s_row_count;  			// Assign S_FIRST_ROW  			if (!$s_row_count) @@ -171,6 +180,9 @@ class phpbb_template_context  				$vararray['S_FIRST_ROW'] = true;  			} +			// Assign S_BLOCK_NAME +			$vararray['S_BLOCK_NAME'] = $blockname; +  			// We always assign S_LAST_ROW and remove the entry before  			$vararray['S_LAST_ROW'] = true;  			if ($s_row_count > 0) @@ -180,6 +192,12 @@ class phpbb_template_context  			// Add a new iteration to this block with the variable assignments we were given.  			$this->tpldata[$blockname][] = $vararray; + +			// Set S_NUM_ROWS +			foreach ($this->tpldata[$blockname] as &$mod_block) +			{ +				$mod_block['S_NUM_ROWS'] = sizeof($this->tpldata[$blockname]); +			}  		}  		return true; @@ -298,14 +316,26 @@ class phpbb_template_context  				$vararray['S_FIRST_ROW'] = true;  			} +			// Assign S_BLOCK_NAME +			$vararray['S_BLOCK_NAME'] = $blockname; +  			// Re-position template blocks  			for ($i = sizeof($block); $i > $key; $i--)  			{  				$block[$i] = $block[$i-1]; + +				$block[$i]['S_ROW_COUNT'] = $block[$i]['S_ROW_NUM'] = $i;  			}  			// Insert vararray at given position  			$block[$key] = $vararray; +			$block[$key]['S_ROW_COUNT'] = $block[$key]['S_ROW_NUM'] = $key; + +			// Set S_NUM_ROWS +			foreach ($this->tpldata[$blockname] as &$mod_block) +			{ +				$mod_block['S_NUM_ROWS'] = sizeof($this->tpldata[$blockname]); +			}  			return true;  		} diff --git a/phpBB/includes/template/filter.php b/phpBB/includes/template/filter.php deleted file mode 100644 index 1c0a56c9f5..0000000000 --- a/phpBB/includes/template/filter.php +++ /dev/null @@ -1,1261 +0,0 @@ -<?php -/** -* -* @package phpBB3 -* @copyright (c) 2011 phpBB Group, sections (c) 2001 ispi of Lincoln Inc -* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 -* -*/ - -/** -* @ignore -*/ -if (!defined('IN_PHPBB')) -{ -	exit; -} - -/** -* The template filter that does the actual compilation -* -* psoTFX, phpBB Development Team - Completion of file caching, decompilation -* routines and implementation of conditionals/keywords and associated changes -* -* The interface was inspired by PHPLib templates, and the template file (formats are -* quite similar) -* -* The keyword/conditional implementation is currently based on sections of code from -* the Smarty templating engine (c) 2001 ispi of Lincoln, Inc. which is released -* (on its own and in whole) under the LGPL. Section 3 of the LGPL states that any code -* derived from an LGPL application may be relicenced under the GPL, this applies -* to this source -* -* DEFINE directive inspired by a request by Cyberalien -* -* @see template_compile -* @package phpBB3 -*/ -class phpbb_template_filter extends php_user_filter -{ -	const REGEX_NS = '[a-z_][a-z_0-9]+'; - -	const REGEX_VAR = '[A-Z_][A-Z_0-9]+'; -	const REGEX_VAR_SUFFIX = '[A-Z_0-9]+'; - -	const REGEX_TAG = '<!-- ([A-Z][A-Z_0-9]+)(?: (.*?) ?)?-->'; - -	const REGEX_TOKENS = '~<!-- ([A-Z][A-Z_0-9]+)(?: (.*?) ?)?-->|{((?:[a-z_][a-z_0-9]+\.)*\\$?[A-Z][A-Z_0-9]+)}~'; - -	/** -	* @var array -	*/ -	private $block_names = array(); - -	/** -	* @var array -	*/ -	private $block_else_level = array(); - -	/** -	* @var string -	*/ -	private $chunk; - -	/** -	* @var bool -	*/ -	private $in_php; - -	/** -	* Whether inline PHP code, <!-- PHP --> and <!-- INCLUDEPHP --> tags -	* are allowed. If this is false all PHP code will be silently -	* removed from the template during compilation. -	* -	* @var bool -	*/ -	private $allow_php; - -	/** -	* Whether cleanup will be performed on resulting code, see compile() -	* (Preserve whitespace) -	* -	* @var bool -	*/ -	private $cleanup = true; - -	/** -	* Resource locator. -	* -	* @var phpbb_template_locator -	*/ -	private $locator; - -	/** -	* @var string phpBB root path -	*/ -	private $phpbb_root_path; - -	/** -	* Name of the style that the template being compiled and/or rendered -	* belongs to, and its parents, in inheritance tree order. -	* -	* Used to invoke style-specific template events. -	* -	* @var array -	*/ -	private $style_names; - -	/** -	* Extension manager. -	* -	* @var phpbb_extension_manager -	*/ -	private $extension_manager; - -	/** -	* Current user -	* -	* @var phpbb_user -	*/ -	private $user; - -	/** -	* Template compiler. -	* -	* @var phpbb_template_compile -	*/ -	private $template_compile; - -	/** -	* Stream filter -	* -	* Is invoked for evey chunk of the stream, allowing us -	* to work on a chunk at a time, which saves memory. -	*/ -	public function filter($in, $out, &$consumed, $closing) -	{ -		$written = false; -		$first = false; - -		while ($bucket = stream_bucket_make_writeable($in)) -		{ -			$consumed += $bucket->datalen; - -			$data = $this->chunk . $bucket->data; -			$last_nl = strrpos($data, "\n"); -			$this->chunk = substr($data, $last_nl); -			$data = substr($data, 0, $last_nl); - -			if (!strlen($data)) -			{ -				continue; -			} - -			$written = true; - -			$data = $this->compile($data); -			if (!$first) -			{ -				$data = $this->prepend_preamble($data); -				$first = false; -			} -			$bucket->data = $data; -			$bucket->datalen = strlen($bucket->data); -			stream_bucket_append($out, $bucket); -		} - -		if ($closing && strlen($this->chunk)) -		{ -			$written = true; -			$bucket = stream_bucket_new($this->stream, $this->compile($this->chunk)); -			stream_bucket_append($out, $bucket); -		} - -		return $written ? PSFS_PASS_ON : PSFS_FEED_ME; -	} - -	/** -	* Initializer, called on creation. -	* -	* Get the allow_php option, style_names, root directory and locator from params, -	* which are passed to stream_filter_append. -	* -	* @return boolean Returns true -	*/ -	public function onCreate() -	{ -		$this->chunk = ''; -		$this->in_php = false; -		$this->allow_php = $this->params['allow_php']; -		$this->locator = $this->params['locator']; -		$this->phpbb_root_path = $this->params['phpbb_root_path']; -		$this->style_names = $this->params['style_names']; -		$this->extension_manager = $this->params['extension_manager']; -		$this->cleanup = $this->params['cleanup']; -		if (isset($this->params['user'])) -		{ -			$this->user = $this->params['user']; -		} -		$this->template_compile = $this->params['template_compile']; -		return true; -	} - -	/** -	* Compiles a chunk of template. -	* -	* The chunk must comprise of one or more complete lines from the source -	* template. -	* -	* @param string $data Chunk of source template to compile -	* @return string Compiled PHP/HTML code -	*/ -	private function compile($data) -	{ -		$block_start_in_php = $this->in_php; - -		$data = preg_replace('#<(?:[\\?%]|script)#s', '<?php echo\'\\0\';?>', $data); -		$data = preg_replace_callback(self::REGEX_TOKENS, array($this, 'replace'), $data); - -		// Remove php -		if (!$this->allow_php) -		{ -			if ($block_start_in_php -				&& $this->in_php -				&& strpos($data, '<!-- PHP -->') === false -				&& strpos($data, '<!-- ENDPHP -->') === false) -			{ -				// This is just php code -				return ''; -			} -			$data = preg_replace('~^.*?<!-- ENDPHP -->~', '', $data); -			$data = preg_replace('~<!-- PHP -->.*?<!-- ENDPHP -->~', '', $data); -			$data = preg_replace('~<!-- ENDPHP -->.*?$~', '', $data); -		} - -		if ($this->cleanup) -		{ -			/* - -			Preserve whitespace. -			PHP removes a newline after the closing tag (if it's there). -			This is by design: - -			http://www.php.net/manual/en/language.basic-syntax.phpmode.php -			http://www.php.net/manual/en/language.basic-syntax.instruction-separation.php - - -			Consider the following template: - -				<!-- IF condition --> -				some content -				<!-- ENDIF --> - -			If we were to simply preserve all whitespace, we could simply -			replace all "?>" tags with "?>\n". -			Doing that, would add additional newlines to the compiled -			template in place of the IF and ENDIF statements. These -			newlines are unwanted (and one is conditional). The IF and -			ENDIF are usually on their own line for ease of reading. - -			This replacement preserves newlines only for statements that -			are not the only statement on a line. It will NOT preserve -			newlines at the end of statements in the above example. -			It will preserve newlines in situations like: - -				<!-- IF condition -->inline content<!-- ENDIF --> - -			*/ - -			$data = preg_replace('~(?<!^)(<\?php.+(?<!/\*\*/)\?>)$~m', "$1\n", $data); -			$data = str_replace('/**/?>', "?>\n", $data); -			$data = str_replace('?><?php', '', $data); -		} - -		return $data; -	} - -	/** -	* Prepends a preamble to compiled template. -	* Currently preamble checks if IN_PHPBB is defined and calls exit() if it is not. -	* -	* @param string $data Compiled template chunk -	* @return string Compiled template chunk with preamble prepended -	*/ -	private function prepend_preamble($data) -	{ -		$data = "<?php if (!defined('IN_PHPBB')) exit;" . ((strncmp($data, '<?php', 5) === 0) ? substr($data, 5) : ' ?>' . $data); -		return $data; -	} - -	/** -	* Callback for replacing matched tokens with compiled template code. -	* -	* Compiled template code is an HTML stream with embedded PHP. -	* -	* @param array $matches Regular expression matches -	* @return string compiled template code -	*/ -	private function replace($matches) -	{ -		if ($this->in_php && $matches[1] != 'ENDPHP') -		{ -			return ''; -		} - -		if (isset($matches[3])) -		{ -			return $this->compile_var_tags($matches[0]); -		} - -		switch ($matches[1]) -		{ -			case 'BEGIN': -				$this->block_else_level[] = false; -				return '<?php ' . $this->compile_tag_block($matches[2]) . ' ?>'; -			break; - -			case 'BEGINELSE': -				$this->block_else_level[sizeof($this->block_else_level) - 1] = true; -				return '<?php }} else { ?>'; -			break; - -			case 'END': -				array_pop($this->block_names); -				return '<?php ' . ((array_pop($this->block_else_level)) ? '}' : '}}') . ' ?>'; -			break; - -			case 'IF': -				return '<?php ' . $this->compile_tag_if($matches[2], false) . ' ?>'; -			break; - -			case 'ELSE': -				return '<?php } else { ?>'; -			break; - -			case 'ELSEIF': -				return '<?php ' . $this->compile_tag_if($matches[2], true) . ' ?>'; -			break; - -			case 'ENDIF': -				return '<?php } ?>'; -			break; - -			case 'DEFINE': -				return '<?php ' . $this->compile_tag_define($matches[2], true) . ' ?>'; -			break; - -			case 'UNDEFINE': -				return '<?php ' . $this->compile_tag_define($matches[2], false) . ' ?>'; -			break; - -			case 'ENDDEFINE': -				return '<?php ' . $this->compile_tag_enddefine() . ' ?>'; -			break; - -			case 'INCLUDE': -				return '<?php ' . $this->compile_tag_include($matches[2]) . ' ?>'; -			break; - -			case 'INCLUDEPHP': -				return ($this->allow_php) ? '<?php ' . $this->compile_tag_include_php($matches[2]) . ' ?>' : ''; -			break; - -			case 'INCLUDEJS': -				return '<?php ' . $this->compile_tag_include_js($matches[2]) . ' ?>'; -			break; - -			case 'PHP': -				if ($this->allow_php) -				{ -					$this->in_php = true; -					return '<?php '; -				} -				return '<!-- PHP -->'; -			break; - -			case 'ENDPHP': -				if ($this->allow_php) -				{ -					$this->in_php = false; -					return ' ?>'; -				} -				return '<!-- ENDPHP -->'; -			break; - -			case 'EVENT': -				return '<?php ' . $this->compile_tag_event($matches[2]) . '?>'; -			break; - -			default: -				return $matches[0]; -			break; - -		} -		return ''; -	} - -	/** -	* Convert template variables into PHP varrefs -	* -	* @param string $text_blocks Variable reference in source template -	* @param bool $is_expr Returns whether the source was an expression type variable (i.e. S_FIRST_ROW) -	* @return string PHP variable name -	*/ -	private function get_varref($text_blocks, &$is_expr) -	{ -		// change template varrefs into PHP varrefs -		$varrefs = array(); - -		// This one will handle varrefs WITH namespaces -		preg_match_all('#\{((?:' . self::REGEX_NS . '\.)+)(\$)?(' . self::REGEX_VAR . ')\}#', $text_blocks, $varrefs, PREG_SET_ORDER); - -		foreach ($varrefs as $var_val) -		{ -			$namespace = $var_val[1]; -			$varname = $var_val[3]; -			$new = $this->generate_block_varref($namespace, $varname, $is_expr, $var_val[2]); - -			$text_blocks = str_replace($var_val[0], $new, $text_blocks); -		} - -		// Language variables cannot be reduced to a single varref, so they must be skipped -		// These two replacements would break language variables, so we can only run them on non-language types -		if (strpos($text_blocks, '{L_') === false && strpos($text_blocks, '{LA_') === false) -		{ -			// This will handle the remaining root-level varrefs -			$text_blocks = preg_replace('#\{(' . self::REGEX_VAR . ')\}#', "\$_rootref['\\1']", $text_blocks); -			$text_blocks = preg_replace('#\{\$(' . self::REGEX_VAR . ')\}#', "\$_tpldata['DEFINE']['.']['\\1']", $text_blocks); -		} - -		return $text_blocks; -	} - -	/** -	* Parse paths of the form {FOO}/a/{BAR}/b -	* -	* Note: this method assumes at least one variable in the path, this should -	* be checked before this method is called. -	* -	* @param string $path The path to parse -	* @param string $include_type The type of template function to call -	* @return string An appropriately formatted string to include in the -	* 	template or an empty string if an expression like S_FIRST_ROW was -	* 	incorrectly used -	*/ -	private function parse_dynamic_path($path, $include_type) -	{ -		$matches = array(); -		$replace = array(); -		$is_expr = true; - -		preg_match_all('#\{((?:' . self::REGEX_NS . '\.)*)(\$)?(' . self::REGEX_VAR . ')\}#', $path, $matches); -		foreach ($matches[0] as $var_str) -		{ -			$tmp_is_expr = false; -			$var = $this->get_varref($var_str, $tmp_is_expr); -			$is_expr = $is_expr && $tmp_is_expr; -			$replace[] = "' . $var . '"; -		} - -		if (!$is_expr) -		{ -			return " \$_template->$include_type('" . str_replace($matches[0], $replace, $path) . "', true);"; -		} -		else -		{ -			return ''; -		} -	} - -	/** -	* Compile variables -	* -	* @param string $text_blocks Variable reference in source template -	* @return string compiled template code -	*/ -	private function compile_var_tags(&$text_blocks) -	{ -		$is_expr = null; -		$text_blocks = $this->get_varref($text_blocks, $is_expr); -		$lang_replaced = $this->compile_language_tags($text_blocks); - -		if(!$lang_replaced) -		{ -			$text_blocks = '<?php echo ' . ($is_expr ? "$text_blocks" : "(isset($text_blocks)) ? $text_blocks : ''") . '; /**/?>'; -		} - -		return $text_blocks; -	} - -	/** -	* Handles special language tags L_ and LA_ -	* -	* @param string $text_blocks Variable reference in source template -	* @return bool Whether a replacement occurred or not -	*/ -	private function compile_language_tags(&$text_blocks) -	{ -		$replacements = 0; - -		// transform vars prefixed by L_ into their language variable pendant if nothing is set within the tpldata array -		if (strpos($text_blocks, '{L_') !== false) -		{ -			$text_blocks = preg_replace('#\{L_(' . self::REGEX_VAR_SUFFIX . ')\}#', "<?php echo ((isset(\$_rootref['L_\\1'])) ? \$_rootref['L_\\1'] : ((isset(\$_lang['\\1'])) ? \$_lang['\\1'] : '{ \\1 }')); /**/?>", $text_blocks, -1, $replacements); -			return (bool) $replacements; -		} - -		// Handle addslashed language variables prefixed with LA_ -		// If a template variable already exist, it will be used in favor of it... -		if (strpos($text_blocks, '{LA_') !== false) -		{ -			$text_blocks = preg_replace('#\{LA_(' . self::REGEX_VAR_SUFFIX . '+)\}#', "<?php echo ((isset(\$_rootref['LA_\\1'])) ? \$_rootref['LA_\\1'] : ((isset(\$_rootref['L_\\1'])) ? addslashes(\$_rootref['L_\\1']) : ((isset(\$_lang['\\1'])) ? addslashes(\$_lang['\\1']) : '{ \\1 }'))); /**/?>", $text_blocks, -1, $replacements); -			return (bool) $replacements; -		} - -		return false; -	} - -	/** -	* Compile blocks -	* -	* @param string $tag_args Block contents in source template -	* @return string compiled template code -	*/ -	private function compile_tag_block($tag_args) -	{ -		$no_nesting = false; - -		// Is the designer wanting to call another loop in a loop? -		// <!-- BEGIN loop --> -		// <!-- BEGIN !loop2 --> -		// <!-- END !loop2 --> -		// <!-- END loop --> -		// 'loop2' is actually on the same nesting level as 'loop' you assign -		// variables to it with template->assign_block_vars('loop2', array(...)) -		if (strpos($tag_args, '!') === 0) -		{ -			// Count the number if ! occurrences (not allowed in vars) -			$no_nesting = substr_count($tag_args, '!'); -			$tag_args = substr($tag_args, $no_nesting); -		} - -		// Allow for control of looping (indexes start from zero): -		// foo(2)    : Will start the loop on the 3rd entry -		// foo(-2)   : Will start the loop two entries from the end -		// foo(3,4)  : Will start the loop on the fourth entry and end it on the fifth -		// foo(3,-4) : Will start the loop on the fourth entry and end it four from last -		$match = array(); - -		if (preg_match('#^([^()]*)\(([\-\d]+)(?:,([\-\d]+))?\)$#', $tag_args, $match)) -		{ -			$tag_args = $match[1]; - -			if ($match[2] < 0) -			{ -				$loop_start = '($_' . $tag_args . '_count ' . $match[2] . ' < 0 ? 0 : $_' . $tag_args . '_count ' . $match[2] . ')'; -			} -			else -			{ -				$loop_start = '($_' . $tag_args . '_count < ' . $match[2] . ' ? $_' . $tag_args . '_count : ' . $match[2] . ')'; -			} - -			if (!isset($match[3]) || strlen($match[3]) < 1 || $match[3] == -1) -			{ -				$loop_end = '$_' . $tag_args . '_count'; -			} -			else if ($match[3] >= 0) -			{ -				$loop_end = '(' . ($match[3] + 1) . ' > $_' . $tag_args . '_count ? $_' . $tag_args . '_count : ' . ($match[3] + 1) . ')'; -			} -			else //if ($match[3] < -1) -			{ -				$loop_end = '$_' . $tag_args . '_count' . ($match[3] + 1); -			} -		} -		else -		{ -			$loop_start = 0; -			$loop_end = '$_' . $tag_args . '_count'; -		} - -		$tag_template_php = ''; -		array_push($this->block_names, $tag_args); - -		if ($no_nesting !== false) -		{ -			// We need to implode $no_nesting times from the end... -			$block = array_slice($this->block_names, -$no_nesting); -		} -		else -		{ -			$block = $this->block_names; -		} - -		if (sizeof($block) < 2) -		{ -			// Block is not nested. -			$tag_template_php = '$_' . $tag_args . "_count = (isset(\$_tpldata['$tag_args'])) ? sizeof(\$_tpldata['$tag_args']) : 0;"; -			$varref = "\$_tpldata['$tag_args']"; -		} -		else -		{ -			// This block is nested. -			// Generate a namespace string for this block. -			$namespace = implode('.', $block); - -			// Get a reference to the data array for this block that depends on the -			// current indices of all parent blocks. -			$varref = $this->generate_block_data_ref($namespace, false); - -			// Create the for loop code to iterate over this block. -			$tag_template_php = '$_' . $tag_args . '_count = (isset(' . $varref . ')) ? sizeof(' . $varref . ') : 0;'; -		} - -		$tag_template_php .= 'if ($_' . $tag_args . '_count) {'; - -		/** -		* The following uses foreach for iteration instead of a for loop, foreach is faster but requires PHP to make a copy of the contents of the array which uses more memory -		* <code> -		*	if (!$offset) -		*	{ -		*		$tag_template_php .= 'foreach (' . $varref . ' as $_' . $tag_args . '_i => $_' . $tag_args . '_val){'; -		*	} -		* </code> -		*/ - -		$tag_template_php .= 'for ($_' . $tag_args . '_i = ' . $loop_start . '; $_' . $tag_args . '_i < ' . $loop_end . '; ++$_' . $tag_args . '_i){'; -		$tag_template_php .= '$_' . $tag_args . '_val = &' . $varref . '[$_' . $tag_args . '_i];'; - -		return $tag_template_php; -	} - -	/** -	* Compile a general expression - much of this is from Smarty with -	* some adaptions for our block level methods -	* -	* @param string $tag_args Expression (tag arguments) in source template -	* @return string compiled template code -	*/ -	private function compile_expression($tag_args) -	{ -		$match = array(); -		preg_match_all('/(?: -			"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"         | -			\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'     | -			[(),]                                  | -			[^\s(),]+)/x', $tag_args, $match); - -		$tokens = $match[0]; -		$is_arg_stack = array(); - -		for ($i = 0, $size = sizeof($tokens); $i < $size; $i++) -		{ -			$token = &$tokens[$i]; - -			switch ($token) -			{ -				case '!==': -				case '===': -				case '<<': -				case '>>': -				case '|': -				case '^': -				case '&': -				case '~': -				case ')': -				case ',': -				case '+': -				case '-': -				case '*': -				case '/': -				case '@': -				break; - -				case '==': -				case 'eq': -					$token = '=='; -				break; - -				case '!=': -				case '<>': -				case 'ne': -				case 'neq': -					$token = '!='; -				break; - -				case '<': -				case 'lt': -					$token = '<'; -				break; - -				case '<=': -				case 'le': -				case 'lte': -					$token = '<='; -				break; - -				case '>': -				case 'gt': -					$token = '>'; -				break; - -				case '>=': -				case 'ge': -				case 'gte': -					$token = '>='; -				break; - -				case '&&': -				case 'and': -					$token = '&&'; -				break; - -				case '||': -				case 'or': -					$token = '||'; -				break; - -				case '!': -				case 'not': -					$token = '!'; -				break; - -				case '%': -				case 'mod': -					$token = '%'; -				break; - -				case '(': -					array_push($is_arg_stack, $i); -				break; - -				case 'is': -					$is_arg_start = ($tokens[$i-1] == ')') ? array_pop($is_arg_stack) : $i-1; -					$is_arg = implode(' ', array_slice($tokens, $is_arg_start, $i - $is_arg_start)); - -					$new_tokens = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+1)); - -					array_splice($tokens, $is_arg_start, sizeof($tokens), $new_tokens); - -					$i = $is_arg_start; - -				// no break - -				default: -					$varrefs = array(); -					if (preg_match('#^((?:' . self::REGEX_NS . '\.)+)?(\$)?(?=[A-Z])([A-Z0-9\-_]+)#s', $token, $varrefs)) -					{ -						if (!empty($varrefs[1])) -						{ -							$namespace = substr($varrefs[1], 0, -1); -							$dot_pos = strrchr($namespace, '.'); -							if ($dot_pos !== false) -							{ -								$namespace = substr($dot_pos, 1); -							} - -							// S_ROW_COUNT is deceptive, it returns the current row number not the number of rows -							// hence S_ROW_COUNT is deprecated in favour of S_ROW_NUM -							switch ($varrefs[3]) -							{ -								case 'S_ROW_NUM': -								case 'S_ROW_COUNT': -									$token = "\$_${namespace}_i"; -								break; - -								case 'S_NUM_ROWS': -									$token = "\$_${namespace}_count"; -								break; - -								case 'S_FIRST_ROW': -									$token = "(\$_${namespace}_i == 0)"; -								break; - -								case 'S_LAST_ROW': -									$token = "(\$_${namespace}_i == \$_${namespace}_count - 1)"; -								break; - -								case 'S_BLOCK_NAME': -									$token = "'$namespace'"; -								break; - -								default: -									$token = $this->generate_block_data_ref(substr($varrefs[1], 0, -1), true, $varrefs[2]) . '[\'' . $varrefs[3] . '\']'; -									$token = '(isset(' . $token . ') ? ' . $token . ' : null)'; -								break; -							} -						} -						else -						{ -							$token = ($varrefs[2]) ? '$_tpldata[\'DEFINE\'][\'.\'][\'' . $varrefs[3] . '\']' : '$_rootref[\'' . $varrefs[3] . '\']'; -							$token = '(isset(' . $token . ') ? ' . $token . ' : null)'; -						} - -					} -					else if (preg_match('#^\.((?:' . self::REGEX_NS . '\.?)+)$#s', $token, $varrefs)) -					{ -						// Allow checking if loops are set with .loopname -						// It is also possible to check the loop count by doing <!-- IF .loopname > 1 --> for example -						$blocks = explode('.', $varrefs[1]); - -						// If the block is nested, we have a reference that we can grab. -						// If the block is not nested, we just go and grab the block from _tpldata -						if (sizeof($blocks) > 1) -						{ -							$block = array_pop($blocks); -							$namespace = implode('.', $blocks); -							$varref = $this->generate_block_data_ref($namespace, true); - -							// Add the block reference for the last child. -							$varref .= "['" . $block . "']"; -						} -						else -						{ -							$varref = '$_tpldata'; - -							// Add the block reference for the last child. -							$varref .= "['" . $blocks[0] . "']"; -						} -						$token = "(isset($varref) ? sizeof($varref) : 0)"; -					} - -				break; -			} -		} - -		return $tokens; -	} - -	/** -	* Compile IF tags -	* -	* @param string $tag_args Expression given with IF in source template -	* @param bool $elseif True if compiling an IF tag, false if compiling an ELSEIF tag -	* @return string compiled template code -	*/ -	private function compile_tag_if($tag_args, $elseif) -	{ -		$tokens = $this->compile_expression($tag_args); - -		$tpl = ($elseif) ? '} else if (' : 'if ('; - -		$tpl .= implode(' ', $tokens); -		$tpl .= ') { '; - -		return $tpl; -	} - -	/** -	* Compile DEFINE tags -	* -	* @param string $tag_args Expression given with DEFINE in source template -	* @param bool $op True if compiling a DEFINE tag, false if compiling an UNDEFINE tag -	* @return string compiled template code -	*/ -	private function compile_tag_define($tag_args, $op) -	{ -		$match = array(); -		preg_match('#^((?:' . self::REGEX_NS . '\.)+)?\$(?=[A-Z])([A-Z0-9_\-]*)(?: = (.*?))?$#', $tag_args, $match); - -		if (!empty($match[2]) && !isset($match[3]) && $op) -		{ -			// DEFINE tag with ENDDEFINE -			$array = "\$_tpldata['DEFINE']['.vars']"; -			$code = 'ob_start(); '; -			$code .= "if (!isset($array)) { $array = array(); } "; -			$code .= "{$array}[] = '{$match[2]}'"; -			return $code; -		} - -		if (empty($match[2]) || (!isset($match[3]) && $op)) -		{ -			return ''; -		} - -		if (!$op) -		{ -			return 'unset(' . (($match[1]) ? $this->generate_block_data_ref(substr($match[1], 0, -1), true, true) . '[\'' . $match[2] . '\']' : '$_tpldata[\'DEFINE\'][\'.\'][\'' . $match[2] . '\']') . ');'; -		} - -		/* -		* Define tags that contain template variables (enclosed in curly brackets) -		* need to be treated differently. -		*/ -		if (substr($match[3], 1, 1) == '{' && substr($match[3], -2, 1) == '}') -		{ -			$parsed_statement = implode(' ', $this->compile_expression(substr($match[3], 2, -2))); -		} -		else -		{ -			$parsed_statement = implode(' ', $this->compile_expression($match[3])); -		} - -		return (($match[1]) ? $this->generate_block_data_ref(substr($match[1], 0, -1), true, true) . '[\'' . $match[2] . '\']' : '$_tpldata[\'DEFINE\'][\'.\'][\'' . $match[2] . '\']') . ' = ' . $parsed_statement . ';'; -	} - -	/** -	* Compile ENDDEFINE tag -	* -	* @return string compiled template code -	*/ -	private function compile_tag_enddefine() -	{ -		$array = "\$_tpldata['DEFINE']['.vars']"; -		$code = "if (!isset($array) || !sizeof($array)) { trigger_error('ENDDEFINE tag without DEFINE in ' . basename(__FILE__), E_USER_ERROR); }"; -		$code .= "\$define_var = array_pop($array); "; -		$code .= "\$_tpldata['DEFINE']['.'][\$define_var] = ob_get_clean();"; -		return $code; -	} - -	/** -	* Compile INCLUDE tag -	* -	* @param string $tag_args Expression given with INCLUDE in source template -	* @return string compiled template code -	*/ -	private function compile_tag_include($tag_args) -	{ -		// Process dynamic includes -		if (strpos($tag_args, '{') !== false) -		{ -			return $this->parse_dynamic_path($tag_args, '_tpl_include'); -		} - -		return "\$_template->_tpl_include('$tag_args');"; -	} - -	/** -	* Compile INCLUDE_PHP tag -	* -	* @param string $tag_args Expression given with INCLUDEPHP in source template -	* @return string compiled template code -	*/ -	private function compile_tag_include_php($tag_args) -	{ -		if (strpos($tag_args, '{') !== false) -		{ -			return $this->parse_dynamic_path($tag_args, '_php_include'); -		} - -		return "\$_template->_php_include('$tag_args');"; -	} - -	/** -	* Compile EVENT tag. -	* -	* $tag_args should be a single string identifying the event. -	* The event name can contain letters, numbers and underscores only. -	* If an invalid event name is specified, an E_USER_ERROR will be -	* triggered. -	* -	* Event tags are only functional when the template engine has -	* an instance of the extension manager. Extension manager would -	* be called upon to find all extensions listening for the specified -	* event, and to obtain additional template fragments. All such -	* template fragments will be compiled and included in the generated -	* compiled template code for the current template being compiled. -	* -	* The above means that whenever an extension is enabled or disabled, -	* template cache should be cleared in order to update the compiled -	* template code for the active set of template event listeners. -	* -	* This also means that extensions cannot return different template -	* fragments at different times. Once templates are compiled, changing -	* such template fragments would have no effect. -	* -	* @param string $tag_args EVENT tag arguments, as a string - for EVENT this is the event name -	* @return string compiled template code -	*/ -	private function compile_tag_event($tag_args) -	{ -		if (!preg_match('/^\w+$/', $tag_args)) -		{ -			// The event location is improperly formatted, -			if ($this->user) -			{ -				trigger_error($this->user->lang('ERR_TEMPLATE_EVENT_LOCATION', $tag_args), E_USER_ERROR); -			} -			else -			{ -				trigger_error(sprintf('The specified template event location <em>[%s]</em> is improperly formatted.', $tag_args), E_USER_ERROR); -			} -		} -		$location = $tag_args; - -		if ($this->extension_manager) -		{ -			$finder = $this->extension_manager->get_finder(); - -			$files = $finder -				->extension_prefix($location) -				->extension_suffix('.html') -				->extension_directory("/styles/all/template") -				->get_files(); - -			foreach ($this->style_names as $style_name) -			{ -				$more_files = $finder -					->extension_prefix($location) -					->extension_suffix('.html') -					->extension_directory("/styles/" . $style_name . "/template") -					->get_files(); -				if (!empty($more_files)) -				{ -					$files = array_merge($files, $more_files); -					break; -				} -			} - -			$all_compiled = ''; -			foreach ($files as $file) -			{ -				$this->template_compile->set_filter_params(array( -					'cleanup'	=> false, -				)); - -				$compiled = $this->template_compile->compile_file($file); - -				$this->template_compile->reset_filter_params(); - -				if ($compiled === false) -				{ -					if ($this->user) -					{ -						trigger_error($this->user->lang('ERR_TEMPLATE_COMPILATION', phpbb_filter_root_path($file)), E_USER_ERROR); -					} -					else -					{ -						trigger_error(sprintf('The file could not be compiled: %s', phpbb_filter_root_path($file)), E_USER_ERROR); -					} -				} - -				$all_compiled .= $compiled; -			} -			// Need spaces inside php tags as php cannot grok -			// < ?php? > sans the spaces -			return ' ?' . '>' . $all_compiled . '<?php '; -		} -	} - -	/** -	* parse expression -	* This is from Smarty -	*/ -	private function _parse_is_expr($is_arg, $tokens) -	{ -		$expr_end = 0; -		$negate_expr = false; - -		if (($first_token = array_shift($tokens)) == 'not') -		{ -			$negate_expr = true; -			$expr_type = array_shift($tokens); -		} -		else -		{ -			$expr_type = $first_token; -		} - -		switch ($expr_type) -		{ -			case 'even': -				if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') -				{ -					$expr_end++; -					$expr_arg = $tokens[$expr_end++]; -					$expr = "!(($is_arg / $expr_arg) & 1)"; -				} -				else -				{ -					$expr = "!($is_arg & 1)"; -				} -			break; - -			case 'odd': -				if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') -				{ -					$expr_end++; -					$expr_arg = $tokens[$expr_end++]; -					$expr = "(($is_arg / $expr_arg) & 1)"; -				} -				else -				{ -					$expr = "($is_arg & 1)"; -				} -			break; - -			case 'div': -				if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') -				{ -					$expr_end++; -					$expr_arg = $tokens[$expr_end++]; -					$expr = "!($is_arg % $expr_arg)"; -				} -			break; -		} - -		if ($negate_expr) -		{ -			if ($expr[0] == '!') -			{ -				// Negated expression, de-negate it. -				$expr = substr($expr, 1); -			} -			else -			{ -				$expr = "!($expr)"; -			} -		} - -		array_splice($tokens, 0, $expr_end, $expr); - -		return $tokens; -	} - -	/** -	* Compile INCLUDEJS tag -	* -	* @param string $tag_args Expression given with INCLUDEJS in source template -	* @return string compiled template code -	*/ -	private function compile_tag_include_js($tag_args) -	{ -		// Process dynamic includes -		if (strpos($tag_args, '{') !== false) -		{ -			return $this->parse_dynamic_path($tag_args, '_js_include'); -		} - -		// Locate file -		$filename = $this->locator->get_first_file_location(array($tag_args), false, true); - -		if ($filename === false) -		{ -			// File does not exist, find it during run time -			return ' $_template->_js_include(\'' . addslashes($tag_args) . '\', true); '; -		} - -		if (substr($filename, 0, strlen($this->phpbb_root_path)) != $this->phpbb_root_path) -		{ -			// Absolute path, include as is -			return ' $_template->_js_include(\'' . addslashes($filename) . '\', false, false); '; -		} - -		// Relative path, remove root path from it -		$filename = substr($filename, strlen($this->phpbb_root_path)); -		return ' $_template->_js_include(\'' . addslashes($filename) . '\', false, true); '; -	} - -	/** -	* Generates a reference to the given variable inside the given (possibly nested) -	* block namespace. This is a string of the form: -	* ' . $_tpldata['parent'][$_parent_i]['$child1'][$_child1_i]['$child2'][$_child2_i]...['varname'] . ' -	* It's ready to be inserted into an "echo" line in one of the templates. -	* -	* @param string $namespace Namespace to access (expects a trailing "." on the namespace) -	* @param string $varname Variable name to use -	* @param bool $expr Returns whether the source was an expression type -	* @param bool $defop If true this is a variable created with the DEFINE construct, otherwise template variable -	* @return string Code to access variable or echo it if $echo is true -	*/ -	private function generate_block_varref($namespace, $varname, &$expr, $defop = false) -	{ -		// Strip the trailing period. -		$namespace = substr($namespace, 0, -1); - -		if (($pos = strrpos($namespace, '.')) !== false) -		{ -			$local_namespace = substr($namespace, $pos + 1); -		} -		else -		{ -			$local_namespace = $namespace; -		} - -		$expr = true; - -		// S_ROW_COUNT is deceptive, it returns the current row number now the number of rows -		// hence S_ROW_COUNT is deprecated in favour of S_ROW_NUM -		switch ($varname) -		{ -			case 'S_ROW_NUM': -			case 'S_ROW_COUNT': -				$varref = "\$_${local_namespace}_i"; -			break; - -			case 'S_NUM_ROWS': -				$varref = "\$_${local_namespace}_count"; -			break; - -			case 'S_FIRST_ROW': -				$varref = "(\$_${local_namespace}_i == 0)"; -			break; - -			case 'S_LAST_ROW': -				$varref = "(\$_${local_namespace}_i == \$_${local_namespace}_count - 1)"; -			break; - -			case 'S_BLOCK_NAME': -				$varref = "'$local_namespace'"; -			break; - -			default: -				// Get a reference to the data block for this namespace. -				$varref = $this->generate_block_data_ref($namespace, true, $defop); -				// Prepend the necessary code to stick this in an echo line. - -				// Append the variable reference. -				$varref .= "['$varname']"; - -				$expr = false; -			break; -		} -		// @todo Test the !$expr more - -		return $varref; -	} - -	/** -	* Generates a reference to the array of data values for the given -	* (possibly nested) block namespace. This is a string of the form: -	* $_tpldata['parent'][$_parent_i]['$child1'][$_child1_i]['$child2'][$_child2_i]...['$childN'] -	* -	* @param string $blockname Block to access (does not expect a trailing "." on the blockname) -	* @param bool $include_last_iterator If $include_last_iterator is true, then [$_childN_i] will be appended to the form shown above. -	* @param bool $defop If true this is a variable created with the DEFINE construct, otherwise template variable -	* @return string Code to access variable -	*/ -	private function generate_block_data_ref($blockname, $include_last_iterator, $defop = false) -	{ -		// Get an array of the blocks involved. -		$blocks = explode('.', $blockname); -		$blockcount = sizeof($blocks) - 1; - -		// DEFINE is not an element of any referenced variable, we must use _tpldata to access it -		if ($defop) -		{ -			$varref = '$_tpldata[\'DEFINE\']'; -			// Build up the string with everything but the last child. -			for ($i = 0; $i < $blockcount; $i++) -			{ -				$varref .= "['" . $blocks[$i] . "'][\$_" . $blocks[$i] . '_i]'; -			} -			// Add the block reference for the last child. -			$varref .= "['" . $blocks[$blockcount] . "']"; -			// Add the iterator for the last child if requried. -			if ($include_last_iterator) -			{ -				$varref .= '[$_' . $blocks[$blockcount] . '_i]'; -			} -			return $varref; -		} -		else if ($include_last_iterator) -		{ -			return '$_'. $blocks[$blockcount] . '_val'; -		} -		else -		{ -			return '$_'. $blocks[$blockcount - 1] . '_val[\''. $blocks[$blockcount]. '\']'; -		} -	} -} diff --git a/phpBB/includes/template/renderer.php b/phpBB/includes/template/renderer.php deleted file mode 100644 index 30e234a733..0000000000 --- a/phpBB/includes/template/renderer.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php -/** -* -* @package phpBB3 -* @copyright (c) 2011 phpBB Group -* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 -* -*/ - -/** -* @ignore -*/ -if (!defined('IN_PHPBB')) -{ -	exit; -} - -/** -* Template renderer interface. -* -* Objects implementing this interface encapsulate a means of displaying -* a template. -* -* @package phpBB3 -*/ -interface phpbb_template_renderer -{ -	/** -	* Displays the template managed by this renderer. -	* -	* @param phpbb_template_context $context Template context to use -	* @param array $lang Language entries to use -	*/ -	public function render($context, $lang); -} diff --git a/phpBB/includes/template/renderer_eval.php b/phpBB/includes/template/renderer_eval.php deleted file mode 100644 index f8e4cb7b10..0000000000 --- a/phpBB/includes/template/renderer_eval.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php -/** -* -* @package phpBB3 -* @copyright (c) 2011 phpBB Group -* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 -* -*/ - -/** -* @ignore -*/ -if (!defined('IN_PHPBB')) -{ -	exit; -} - -/** -* Template renderer that stores compiled template's php code and -* displays it via eval. -* -* @package phpBB3 -*/ -class phpbb_template_renderer_eval implements phpbb_template_renderer -{ -	/** -	* Template code to be eval'ed. -	*/ -	private $code; - -	/** -	* Constructor. Stores provided code for future evaluation. -	* Template includes are delegated to template object $template. -	* -	* @param string $code php code of the template -	* @param phpbb_template $template template object -	*/ -	public function __construct($code, $template) -	{ -		$this->code = $code; -		$this->template = $template; -	} - -	/** -	* Displays the template managed by this renderer by eval'ing php code -	* of the template. -	* -	* @param phpbb_template_context $context Template context to use -	* @param array $lang Language entries to use -	*/ -	public function render($context, $lang) -	{ -		$_template = $this->template; -		$_tpldata = &$context->get_data_ref(); -		$_rootref = &$context->get_root_ref(); -		$_lang = $lang; - -		eval(' ?>' . $this->code . '<?php '); -	} -} diff --git a/phpBB/includes/template/renderer_include.php b/phpBB/includes/template/renderer_include.php deleted file mode 100644 index f5c9026abf..0000000000 --- a/phpBB/includes/template/renderer_include.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php -/** -* -* @package phpBB3 -* @copyright (c) 2011 phpBB Group -* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 -* -*/ - -/** -* @ignore -*/ -if (!defined('IN_PHPBB')) -{ -	exit; -} - - -/** -* Template renderer that stores path to php file with template code -* and displays it by including the file. -* -* @package phpBB3 -*/ -class phpbb_template_renderer_include implements phpbb_template_renderer -{ -	/** -	* Template path to be included. -	*/ -	private $path; - -	/** -	* Constructor. Stores path to the template for future inclusion. -	* Template includes are delegated to template object $template. -	* -	* @param string $path path to the template -	*/ -	public function __construct($path, $template) -	{ -		$this->path = $path; -		$this->template = $template; -	} - -	/** -	* Displays the template managed by this renderer by including -	* the php file containing the template. -	* -	* @param phpbb_template_context $context Template context to use -	* @param array $lang Language entries to use -	*/ -	public function render($context, $lang) -	{ -		$_template = $this->template; -		$_tpldata = &$context->get_data_ref(); -		$_rootref = &$context->get_root_ref(); -		$_lang = $lang; - -		include($this->path); -	} -} diff --git a/phpBB/includes/template/template.php b/phpBB/includes/template/template.php index bbec768613..89a01e924d 100644 --- a/phpBB/includes/template/template.php +++ b/phpBB/includes/template/template.php @@ -2,7 +2,7 @@  /**  *  * @package phpBB3 -* @copyright (c) 2005 phpBB Group, sections (c) 2001 ispi of Lincoln Inc +* @copyright (c) 2013 phpBB Group  * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2  *  */ @@ -15,143 +15,48 @@ if (!defined('IN_PHPBB'))  	exit;  } -/** -* @todo -* IMG_ for image substitution? -* {IMG_[key]:[alt]:[type]} -* {IMG_ICON_CONTACT:CONTACT:full} -> $user->img('icon_contact', 'CONTACT', 'full'); -* -* More in-depth... -* yadayada -*/ - -/** -* Base Template class. -* @package phpBB3 -*/ -class phpbb_template +interface phpbb_template  { -	/** -	* Template context. -	* Stores template data used during template rendering. -	* @var phpbb_template_context -	*/ -	private $context; - -	/** -	* Path of the cache directory for the template -	* @var string -	*/ -	public $cachepath = ''; - -	/** -	* phpBB root path -	* @var string -	*/ -	private $phpbb_root_path; - -	/** -	* PHP file extension -	* @var string -	*/ -	private $php_ext; - -	/** -	* phpBB config instance -	* @var phpbb_config -	*/ -	private $config; - -	/** -	* Current user -	* @var phpbb_user -	*/ -	private $user; - -	/** -	* Template locator -	* @var phpbb_template_locator -	*/ -	private $locator; - -	/** -	* Extension manager. -	* -	* @var phpbb_extension_manager -	*/ -	private $extension_manager; - -	/** -	* Name of the style that the template being compiled and/or rendered -	* belongs to, and its parents, in inheritance tree order. -	* -	* Used to invoke style-specific template events. -	* -	* @var array -	*/ -	private $style_names;  	/** -	* Constructor. +	* Clear the cache  	* -	* @param string $phpbb_root_path phpBB root path -	* @param user $user current user -	* @param phpbb_template_locator $locator template locator -	* @param phpbb_template_context $context template context -	* @param phpbb_extension_manager $extension_manager extension manager, if null then template events will not be invoked +	* @return phpbb_template  	*/ -	public function __construct($phpbb_root_path, $php_ext, $config, $user, phpbb_template_locator $locator, phpbb_template_context $context, phpbb_extension_manager $extension_manager = null) -	{ -		$this->phpbb_root_path = $phpbb_root_path; -		$this->php_ext = $php_ext; -		$this->config = $config; -		$this->user = $user; -		$this->locator = $locator; -		$this->context = $context; -		$this->extension_manager = $extension_manager; -	} +	public function clear_cache();  	/**  	* Sets the template filenames for handles.  	*  	* @param array $filename_array Should be a hash of handle => filename pairs. +	* @return phpbb_template $this  	*/ -	public function set_filenames(array $filename_array) -	{ -		$this->locator->set_filenames($filename_array); - -		return true; -	} +	public function set_filenames(array $filename_array);  	/** -	* Sets the style names corresponding to style hierarchy being compiled +	* Sets the style names/paths corresponding to style hierarchy being compiled  	* and/or rendered.  	*  	* @param array $style_names List of style names in inheritance tree order -	* @return null +	* @param array $style_paths List of style paths in inheritance tree order +	* @return phpbb_template $this  	*/ -	public function set_style_names(array $style_names) -	{ -		$this->style_names = $style_names; -	} +	public function set_style_names(array $style_names, array $style_paths);  	/**  	* Clears all variables and blocks assigned to this template. +	* +	* @return phpbb_template $this  	*/ -	public function destroy() -	{ -		$this->context->clear(); -	} +	public function destroy();  	/**  	* Reset/empty complete block  	*  	* @param string $blockname Name of block to destroy +	* @return phpbb_template $this  	*/ -	public function destroy_block_vars($blockname) -	{ -		$this->context->destroy_block_vars($blockname); -	} +	public function destroy_block_vars($blockname);  	/**  	* Display a template for provided handle. @@ -161,81 +66,9 @@ class phpbb_template  	* This function calls hooks.  	*  	* @param string $handle Handle to display -	* @return bool True on success, false on failure -	*/ -	public function display($handle) -	{ -		$result = $this->call_hook($handle, __FUNCTION__); -		if ($result !== false) -		{ -			return $result[0]; -		} - -		return $this->load_and_render($handle); -	} - -	/** -	* Loads a template for $handle, compiling it if necessary, and -	* renders the template. -	* -	* @param string $handle Template handle to render -	* @return bool True on success, false on failure -	*/ -	private function load_and_render($handle) -	{ -		$renderer = $this->_tpl_load($handle); - -		if ($renderer) -		{ -			$renderer->render($this->context, $this->get_lang()); -			return true; -		} -		else -		{ -			return false; -		} -	} - -	/** -	* Calls hook if any is defined. -	* -	* @param string $handle Template handle being displayed. -	* @param string $method Method name of the caller. +	* @return phpbb_template $this  	*/ -	private function call_hook($handle, $method) -	{ -		global $phpbb_hook; - -		if (!empty($phpbb_hook) && $phpbb_hook->call_hook(array(__CLASS__, $method), $handle, $this)) -		{ -			if ($phpbb_hook->hook_return(array(__CLASS__, $method))) -			{ -				$result = $phpbb_hook->hook_return_result(array(__CLASS__, $method)); -				return array($result); -			} -		} - -		return false; -	} - -	/** -	* Obtains language array. -	* This is either lang property of $user property, or if -	* it is not set an empty array. -	* @return array language entries -	*/ -	public function get_lang() -	{ -		if (isset($this->user->lang)) -		{ -			$lang = $this->user->lang; -		} -		else -		{ -			$lang = array(); -		} -		return $lang; -	} +	public function display($handle);  	/**  	* Display the handle and assign the output to a template variable @@ -244,118 +77,17 @@ class phpbb_template  	* @param string $handle Handle to operate on  	* @param string $template_var Template variable to assign compiled handle to  	* @param bool $return_content If true return compiled handle, otherwise assign to $template_var -	* @return bool|string false on failure, otherwise if $return_content is true return string of the compiled handle, otherwise return true -	*/ -	public function assign_display($handle, $template_var = '', $return_content = true) -	{ -		ob_start(); -		$result = $this->display($handle); -		$contents = ob_get_clean(); -		if ($result === false) -		{ -			return false; -		} - -		if ($return_content) -		{ -			return $contents; -		} - -		$this->assign_var($template_var, $contents); - -		return true; -	} - -	/** -	* Obtains a template renderer for a template identified by specified -	* handle. The template renderer can display the template later. -	* -	* Template source will first be compiled into php code. -	* If template cache is writable the compiled php code will be stored -	* on filesystem and template will not be subsequently recompiled. -	* If template cache is not writable template source will be recompiled -	* every time it is needed. DEBUG define and load_tplcompile -	* configuration setting may be used to force templates to be always -	* recompiled. -	* -	* Returns an object implementing phpbb_template_renderer, or null -	* if template loading or compilation failed. Call render() on the -	* renderer to display the template. This will result in template -	* contents sent to the output stream (unless, of course, output -	* buffering is in effect). -	* -	* @param string $handle Handle of the template to load -	* @return phpbb_template_renderer Template renderer object, or null on failure -	* @uses phpbb_template_compile is used to compile template source -	*/ -	private function _tpl_load($handle) -	{ -		$output_file = $this->_compiled_file_for_handle($handle); - -		$recompile = defined('DEBUG') || -			!file_exists($output_file) || -			@filesize($output_file) === 0; - -		if ($recompile || $this->config['load_tplcompile']) -		{ -			// Set only if a recompile or an mtime check are required. -			$source_file = $this->locator->get_source_file_for_handle($handle); - -			if (!$recompile && @filemtime($output_file) < @filemtime($source_file)) -			{ -				$recompile = true; -			} -		} - -		// Recompile page if the original template is newer, otherwise load the compiled version -		if (!$recompile) -		{ -			return new phpbb_template_renderer_include($output_file, $this); -		} - -		$compile = new phpbb_template_compile($this->config['tpl_allow_php'], $this->style_names, $this->locator, $this->phpbb_root_path, $this->extension_manager, $this->user); - -		if ($compile->compile_file_to_file($source_file, $output_file) !== false) -		{ -			$renderer = new phpbb_template_renderer_include($output_file, $this); -		} -		else if (($code = $compile->compile_file($source_file)) !== false) -		{ -			$renderer = new phpbb_template_renderer_eval($code, $this); -		} -		else -		{ -			$renderer = null; -		} - -		return $renderer; -	} - -	/** -	* Determines compiled file path for handle $handle. -	* -	* @param string $handle Template handle (i.e. "friendly" template name) -	* @return string Compiled file path +	* @return phpbb_template|string if $return_content is true return string of the compiled handle, otherwise return $this  	*/ -	private function _compiled_file_for_handle($handle) -	{ -		$source_file = $this->locator->get_filename_for_handle($handle); -		$compiled_file = $this->cachepath . str_replace('/', '.', $source_file) . '.' . $this->php_ext; -		return $compiled_file; -	} +	public function assign_display($handle, $template_var = '', $return_content = true);  	/**  	* Assign key variable pairs from an array  	*  	* @param array $vararray A hash of variable name => value pairs +	* @return phpbb_template $this  	*/ -	public function assign_vars(array $vararray) -	{ -		foreach ($vararray as $key => $val) -		{ -			$this->assign_var($key, $val); -		} -	} +	public function assign_vars(array $vararray);  	/**  	* Assign a single scalar value to a single key. @@ -364,11 +96,9 @@ class phpbb_template  	*  	* @param string $varname Variable name  	* @param string $varval Value to assign to variable +	* @return phpbb_template $this  	*/ -	public function assign_var($varname, $varval) -	{ -		$this->context->assign_var($varname, $varval); -	} +	public function assign_var($varname, $varval);  	/**  	* Append text to the string value stored in a key. @@ -377,24 +107,18 @@ class phpbb_template  	*  	* @param string $varname Variable name  	* @param string $varval Value to append to variable +	* @return phpbb_template $this  	*/ -	public function append_var($varname, $varval) -	{ -		$this->context->append_var($varname, $varval); -	} +	public function append_var($varname, $varval); -	// Docstring is copied from phpbb_template_context method with the same name.  	/**  	* Assign key variable pairs from an array to a specified block  	* @param string $blockname Name of block to assign $vararray to  	* @param array $vararray A hash of variable name => value pairs +	* @return phpbb_template $this  	*/ -	public function assign_block_vars($blockname, array $vararray) -	{ -		return $this->context->assign_block_vars($blockname, $vararray); -	} +	public function assign_block_vars($blockname, array $vararray); -	// Docstring is copied from phpbb_template_context method with the same name.  	/**  	* Change already assigned key variable pair (one-dimensional - single loop entry)  	* @@ -422,94 +146,12 @@ class phpbb_template  	*  	* @return bool false on error, true on success  	*/ -	public function alter_block_array($blockname, array $vararray, $key = false, $mode = 'insert') -	{ -		return $this->context->alter_block_array($blockname, $vararray, $key, $mode); -	} - -	/** -	* Include a separate template. -	* -	* This function is marked public due to the way the template -	* implementation uses it. It is actually an implementation function -	* and should not be considered part of template class's public API. -	* -	* @param string $filename Template filename to include -	* @param bool $include True to include the file, false to just load it -	* @uses template_compile is used to compile uncached templates -	*/ -	public function _tpl_include($filename, $include = true) -	{ -		$this->locator->set_filenames(array($filename => $filename)); - -		if (!$this->load_and_render($filename)) -		{ -			// trigger_error cannot be used here, as the output already started -			echo 'template->_tpl_include(): Failed including ' . htmlspecialchars($handle) . "\n"; -		} -	} +	public function alter_block_array($blockname, array $vararray, $key = false, $mode = 'insert');  	/** -	* Include a PHP file. -	* -	* If a relative path is passed in $filename, it is considered to be -	* relative to board root ($phpbb_root_path). Absolute paths are -	* also allowed. +	* Get path to template for handle (required for BBCode parser)  	* -	* This function is marked public due to the way the template -	* implementation uses it. It is actually an implementation function -	* and should not be considered part of template class's public API. -	* -	* @param string $filename Path to PHP file to include +	* @return string  	*/ -	public function _php_include($filename) -	{ -		if (phpbb_is_absolute($filename)) -		{ -			$file = $filename; -		} -		else -		{ -			$file = $this->phpbb_root_path . $filename; -		} - -		if (!file_exists($file)) -		{ -			// trigger_error cannot be used here, as the output already started -			echo 'template->_php_include(): File ' . htmlspecialchars($file) . " does not exist\n"; -			return; -		} -		include($file); -	} - -	/** -	* Include JS file -	* -	* @param string $file file name -	* @param bool $locate True if file needs to be located -	* @param bool $relative True if path is relative to phpBB root directory. Ignored if $locate == true -	*/ -	public function _js_include($file, $locate = false, $relative = false) -	{ -		// Locate file -		if ($locate) -		{ -			$located = $this->locator->get_first_file_location(array($file), false, true); -			if ($located) -			{ -				$file = $located; -			} -		} -		else if ($relative) -		{ -			$file = $this->phpbb_root_path . $file; -		} - -		$file .= (strpos($file, '?') === false) ? '?' : '&'; -		$file .= 'assets_version=' . $this->config['assets_version']; - -		// Add HTML code -		$code = '<script src="' . htmlspecialchars($file) . '"></script>'; -		$this->context->append_var('SCRIPTS', $code); -	} +	public function get_source_file_for_handle($handle);  } diff --git a/phpBB/includes/template/twig/definition.php b/phpBB/includes/template/twig/definition.php new file mode 100644 index 0000000000..6557b209eb --- /dev/null +++ b/phpBB/includes/template/twig/definition.php @@ -0,0 +1,69 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2013 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* This class holds all DEFINE variables from the current page load +*/ +class phpbb_template_twig_definition +{ +	/** @var array **/ +	protected $definitions = array(); + +	/** +	* Get a DEFINE'd variable +	* +	* @param string $name +	* @return mixed Null if not found +	*/ +	public function __call($name, $arguments) +	{ +		return (isset($this->definitions[$name])) ? $this->definitions[$name] : null; +	} + +	/** +	* DEFINE a variable +	* +	* @param string $name +	* @param mixed $value +	* @return phpbb_template_twig_definition +	*/ +	public function set($name, $value) +	{ +		$this->definitions[$name] = $value; + +		return $this; +	} + +	/** +	* Append to a variable +	* +	* @param string $name +	* @param string $value +	* @return phpbb_template_twig_definition +	*/ +	public function append($name, $value) +	{ +		if (!isset($this->definitions[$name])) +		{ +			$this->definitions[$name] = ''; +		} + +		$this->definitions[$name] .= $value; + +		return $this; +	} +} diff --git a/phpBB/includes/template/twig/environment.php b/phpBB/includes/template/twig/environment.php new file mode 100644 index 0000000000..b60cd72325 --- /dev/null +++ b/phpBB/includes/template/twig/environment.php @@ -0,0 +1,140 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2013 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +class phpbb_template_twig_environment extends Twig_Environment +{ +	/** @var array */ +	protected $phpbb_extensions; + +	/** @var phpbb_config */ +	protected $phpbb_config; + +	/** @var string */ +	protected $phpbb_root_path; + +	/** @var array **/ +	protected $namespace_look_up_order = array('__main__'); + +	/** +	* Constructor +	* +	* @param phpbb_config $phpbb_config +	* @param array $phpbb_extensions Array of enabled extensions (name => path) +	* @param string $phpbb_root_path +	* @param Twig_LoaderInterface $loader +	* @param array $options Array of options to pass to Twig +	*/ +	public function __construct($phpbb_config, $phpbb_extensions, $phpbb_root_path, Twig_LoaderInterface $loader = null, $options = array()) +	{ +		$this->phpbb_config = $phpbb_config; +		$this->phpbb_extensions = $phpbb_extensions; +		$this->phpbb_root_path = $phpbb_root_path; + +		return parent::__construct($loader, $options); +	} + +	/** +	* Get the list of enabled phpBB extensions +	* +	* Used in EVENT node +	* +	* @return array +	*/ +	public function get_phpbb_extensions() +	{ +		return $this->phpbb_extensions; +	} + +	/** +	* Get phpBB config +	* +	* @return phpbb_config +	*/ +	public function get_phpbb_config() +	{ +		return $this->phpbb_config; +	} + +	/** +	* Get the phpBB root path +	* +	* @return string +	*/ +	public function get_phpbb_root_path() +	{ +		return $this->phpbb_root_path; +	} + +	/** +	* Get the namespace look up order +	* +	* @return array +	*/ +	public function getNamespaceLookUpOrder() +	{ +		return $this->namespace_look_up_order; +	} + +	/** +	* Set the namespace look up order to load templates from +	* +	* @param array $namespace +	* @return Twig_Environment +	*/ +	public function setNamespaceLookUpOrder($namespace) +	{ +		$this->namespace_look_up_order = $namespace; + +		return $this; +	} + +	/** +	 * 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) +	{ +		if (strpos($name, '@') === false) +		{ +			foreach ($this->getNamespaceLookUpOrder() as $namespace) +			{ +				try +				{ +					if ($namespace === '__main__') +					{ +						return parent::loadTemplate($name, $index); +					} + +					return parent::loadTemplate('@' . $namespace . '/' . $name, $index); +				} +				catch (Twig_Error_Loader $e) +				{ +				} +			} + +			// We were unable to load any templates +			throw $e; +		} +		else +		{ +			return parent::loadTemplate($name, $index); +		} +	} +} diff --git a/phpBB/includes/template/twig/extension.php b/phpBB/includes/template/twig/extension.php new file mode 100644 index 0000000000..5ffc45e75a --- /dev/null +++ b/phpBB/includes/template/twig/extension.php @@ -0,0 +1,187 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2013 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +class phpbb_template_twig_extension extends Twig_Extension +{ +	/** @var phpbb_template_context */ +	protected $context; + +	/** @var phpbb_user */ +	protected $user; + +	/** +	* Constructor +	* +	* @param phpbb_template_context $context +	* @param phpbb_user $user +	* @return phpbb_template_twig_extension +	*/ +	public function __construct(phpbb_template_context $context, $user) +	{ +		$this->context = $context; +		$this->user = $user; +	} + +	/** +	* Get the name of this extension +	* +	* @return string +	*/ +	public function getName() +	{ +		return 'phpbb'; +	} + +    /** +     * 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 phpbb_template_twig_tokenparser_define, +			new phpbb_template_twig_tokenparser_include, +			new phpbb_template_twig_tokenparser_includejs, +			new phpbb_template_twig_tokenparser_event, +			new phpbb_template_twig_tokenparser_includephp, +			new phpbb_template_twig_tokenparser_php, +		); +	} + +    /** +     * 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('subset', array($this, 'loop_subset'), array('needs_environment' => true)), +			new Twig_SimpleFilter('addslashes', 'addslashes'), +		); +    } + +    /** +     * 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('lang', array($this, 'lang')), +		); +	} + +    /** +     * Returns a list of operators to add to the existing list. +     * +     * @return array An array of operators +     */ +	public function getOperators() +	{ +		return array( +			array( +				'!' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'), +			), +			array( +				// precedence settings are copied from similar operators in Twig core extension +				'||' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), +				'&&' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + +				'eq' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Equal', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + +				'ne' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), +				'neq' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), +				'<>' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + +				'===' => array('precedence' => 20, 'class' => 'phpbb_template_twig_node_expression_binary_equalequal', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), +				'!==' => array('precedence' => 20, 'class' => 'phpbb_template_twig_node_expression_binary_notequalequal', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + +				'gt' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Greater', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), +				'gte' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_GreaterEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), +				'ge' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_GreaterEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), +				'lt' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Less', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), +				'lte' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_LessEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), +				'le' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_LessEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + +				'mod' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mod', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), +			), +		); +    } + +	/** +	 * Grabs a subset of a loop +	 * +	 * @param Twig_Environment $env          A Twig_Environment instance +	 * @param mixed            $item         A variable +	 * @param integer          $start        Start of the subset +	 * @param integer          $end   	     End of the subset +	 * @param Boolean          $preserveKeys Whether to preserve key or not (when the input is an array) +	 * +	 * @return mixed The sliced variable +	 */ +	function loop_subset(Twig_Environment $env, $item, $start, $end = null, $preserveKeys = false) +	{ +		// We do almost the same thing as Twig's slice (array_slice), except when $end is positive +		if ($end >= 1) +		{ +			// When end is > 1, subset will end on the last item in an array with the specified $end +			// This is different from slice in that it is the number we end on rather than the number +			//  of items to grab (length) + +			// Start must always be the actual starting number for this calculation (not negative) +			$start = ($start < 0) ? sizeof($item) + $start : $start; +			$end = $end - $start; +		} + +		// We always include the last element (this was the past design) +		$end = ($end == -1 || $end === null) ? null : $end + 1; + +		return twig_slice($env, $item, $start, $end, $preserveKeys); +	} + +	/** +	* Get output for a language variable (L_FOO, LA_FOO) +	* +	* This function checks to see if the language var was outputted to $context +	* (e.g. in the ACP, L_TITLE) +	* If not, we return the result of $user->lang() +	* +	* @param string $lang name +	* @return string +	*/ +	function lang() +	{ +		$args = func_get_args(); +		$key = $args[0]; + +		$context = $this->context->get_data_ref(); +		$context_vars = $context['.'][0]; + +		if (isset($context_vars['L_' . $key])) +		{ +			return $context_vars['L_' . $key]; +		} + +		// LA_ is transformed into lang(\'$1\')|addslashes, so we should not +		// need to check for it + +		return call_user_func_array(array($this->user, 'lang'), $args); +	} +} diff --git a/phpBB/includes/template/twig/lexer.php b/phpBB/includes/template/twig/lexer.php new file mode 100644 index 0000000000..d0a84a8b7f --- /dev/null +++ b/phpBB/includes/template/twig/lexer.php @@ -0,0 +1,298 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2013 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +class phpbb_template_twig_lexer extends Twig_Lexer +{ +	public function tokenize($code, $filename = null) +	{ +		// Our phpBB tags +		// Commented out tokens are handled separately from the main replace +		$phpbb_tags = array( +			/*'BEGIN', +			'BEGINELSE', +			'END', +			'IF', +			'ELSE', +			'ELSEIF', +			'ENDIF', +			'DEFINE', +			'UNDEFINE',*/ +			'ENDDEFINE', +			'INCLUDE', +			'INCLUDEPHP', +			'INCLUDEJS', +			'PHP', +			'ENDPHP', +			'EVENT', +		); + +		// Twig tag masks +		$twig_tags = array( +			'autoescape', +			'endautoescape', +			'if', +			'elseif', +			'else', +			'endif', +			'block', +			'endblock', +			'use', +			'extends', +			'embed', +			'filter', +			'endfilter', +			'flush', +			'for', +			'endfor', +			'macro', +			'endmacro', +			'import', +			'from', +			'sandbox', +			'endsandbox', +			'set', +			'endset', +			'spaceless', +			'endspaceless', +			'verbatim', +			'endverbatim', +		); + +		// Fix tokens that may have inline variables (e.g. <!-- DEFINE $TEST = '{FOO}') +		$code = $this->fix_inline_variable_tokens(array( +			'DEFINE.+=', +			'INCLUDE', +			'INCLUDEPHP', +			'INCLUDEJS', +		), $code); + +		// Fix our BEGIN statements +		$code = $this->fix_begin_tokens($code); + +		// Fix our IF tokens +		$code = $this->fix_if_tokens($code); + +		// Fix our DEFINE tokens +		$code = $this->fix_define_tokens($code); + +		// Replace all of our starting tokens, <!-- TOKEN --> with Twig style, {% TOKEN %} +		// This also strips outer parenthesis, <!-- IF (blah) --> becomes <!-- IF blah --> +		$code = preg_replace('#<!-- (' . implode('|', $phpbb_tags) . ')(?: (.*?) ?)?-->#', '{% $1 $2 %}', $code); + +		// Replace all of our twig masks with Twig code (e.g. <!-- BLOCK .+ --> with {% block $1 %}) +		$code = $this->replace_twig_tag_masks($code, $twig_tags); + +		// Replace all of our language variables, {L_VARNAME}, with Twig style, {{ lang('NAME') }} +		// Appends any filters after lang() +		$code = preg_replace('#{L_([a-zA-Z0-9_\.]+)(\|[^}]+?)?}#', '{{ lang(\'$1\')$2 }}', $code); + +		// Replace all of our escaped language variables, {LA_VARNAME}, with Twig style, {{ lang('NAME')|addslashes }} +		// Appends any filters after lang(), but before addslashes +		$code = preg_replace('#{LA_([a-zA-Z0-9_\.]+)(\|[^}]+?)?}#', '{{ lang(\'$1\')$2|addslashes }}', $code); + +		// Replace all of our variables, {VARNAME}, with Twig style, {{ VARNAME }} +		// Appends any filters +		$code = preg_replace('#{([a-zA-Z0-9_\.]+)(\|[^}]+?)?}#', '{{ $1$2 }}', $code); + +		return parent::tokenize($code, $filename); +	} + +	/** +	* Fix tokens that may have inline variables +	* +	* E.g. <!-- INCLUDE {TEST}.html +	* +	* @param array $tokens array of tokens to search for (imploded to a regular expression) +	* @param string $code +	* @return string +	*/ +	protected function fix_inline_variable_tokens($tokens, $code) +	{ +		$callback = function($matches) +		{ +			// Remove any quotes that may have been used in different implementations +			// E.g. DEFINE $TEST = 'blah' vs INCLUDE foo +			// Replace {} with start/end to parse variables (' ~ TEST ~ '.html) +			$matches[2] = str_replace(array('"', "'", '{', '}'), array('', '', "' ~ ", " ~ '"), $matches[2]); + +			// Surround the matches in single quotes ('' ~ TEST ~ '.html') +			return "<!-- {$matches[1]} '{$matches[2]}' -->"; +		}; + +		return preg_replace_callback('#<!-- (' . implode('|', $tokens) . ') (.+?) -->#', $callback, $code); +	} + +	/** +	* Fix begin tokens (convert our BEGIN to Twig for) +	* +	* Not meant to be used outside of this context, public because the anonymous function calls this +	* +	* @param string $code +	* @param array $parent_nodes (used in recursion) +	* @return string +	*/ +	public function fix_begin_tokens($code, $parent_nodes = array()) +	{ +		// PHP 5.3 cannot use $this in an anonymous function, so use this as a work-around +		$parent_class = $this; +		$callback = function ($matches) use ($parent_class, $parent_nodes) +		{ +			$name = $matches[1]; +			$subset = trim(substr($matches[2], 1, -1)); // Remove parenthesis +			$body = $matches[3]; + +			// Is the designer wanting to call another loop in a loop? +			// <!-- BEGIN loop --> +			// <!-- BEGIN !loop2 --> +			// <!-- END !loop2 --> +			// <!-- END loop --> +			// 'loop2' is actually on the same nesting level as 'loop' you assign +			// variables to it with template->assign_block_vars('loop2', array(...)) +			if (strpos($name, '!') === 0) +			{ +				// Count the number if ! occurrences +				$count = substr_count($name, '!'); +				for ($i = 0; $i < $count; $i++) +				{ +					array_pop($parent_nodes); +					$name = substr($name, 1); +				} +			} + +			// Remove all parent nodes, e.g. foo, bar from foo.bar.foobar.VAR +			foreach ($parent_nodes as $node) +			{ +				$body = preg_replace('#([^a-zA-Z0-9_])' . $node . '\.([a-zA-Z0-9_]+)\.#', '$1$2.', $body); +			} + +			// Add current node to list of parent nodes for child nodes +			$parent_nodes[] = $name; + +			// Recursive...fix any child nodes +			$body = $parent_class->fix_begin_tokens($body, $parent_nodes); + +			// Rename loopname vars (to prevent collisions, loop children are named (loop name)_loop_element) +			$body = str_replace($name . '.', $name . '_loop_element.', $body); + +			// Need the parent variable name +			array_pop($parent_nodes); +			$parent = (!empty($parent_nodes)) ? end($parent_nodes) . '_loop_element.' : ''; + +			if ($subset !== '') +			{ +				$subset = '|subset(' . $subset . ')'; +			} + +			// Turn into a Twig for loop, using (loop name)_loop_element for each child +			return "{% for {$name}_loop_element in {$parent}{$name}{$subset} %}{$body}{% endfor %}"; +		}; + +		// Replace <!-- BEGINELSE --> correctly, only needs to be done once +		$code = str_replace('<!-- BEGINELSE -->', '{% else %}', $code); + +		return preg_replace_callback('#<!-- BEGIN ([!a-zA-Z0-9_]+)(\([0-9,\-]+\))? -->(.+?)<!-- END \1 -->#s', $callback, $code); +	} + +	/** +	* Fix IF statements +	* +	* @param string $code +	* @return string +	*/ +	protected function fix_if_tokens($code) +	{ +		$callback = function($matches) +		{ +			// Replace $TEST with definition.TEST +			$matches[1] = preg_replace('#\s\$([a-zA-Z_0-9]+)#', ' definition.$1', $matches[1]); + +			// Replace .test with test|length +			$matches[1] = preg_replace('#\s\.([a-zA-Z_0-9\.]+)#', ' $1|length', $matches[1]); + +			return '<!-- IF' . $matches[1] . '-->'; +		}; + +		// Replace our "div by" with Twig's divisibleby (Twig does not like test names with spaces) +		$code = preg_replace('# div by ([0-9]+)#', ' divisibleby($1)', $code); + +		return preg_replace_callback('#<!-- IF((.*)[\s][\$|\.|!]([^\s]+)(.*))-->#', $callback, $code); +	} + +	/** +	* Fix DEFINE statements and {$VARNAME} variables +	* +	* @param string $code +	* @return string +	*/ +	protected function fix_define_tokens($code) +	{ +		/** +		* Changing $VARNAME to definition.varname because set is only local +		* context (e.g. DEFINE $TEST will only make $TEST available in current +		* template and any child templates, but not any parent templates). +		* +		* DEFINE handles setting it properly to definition in its node, but the +		* variables reading FROM it need to be altered to definition.VARNAME +		* +		* Setting up definition as a class in the array passed to Twig +		* ($context) makes set definition.TEST available in the global context +		*/ + +		// Replace <!-- DEFINE $NAME with {% DEFINE definition.NAME +		$code = preg_replace('#<!-- DEFINE \$(.*)-->#', '{% DEFINE $1 %}', $code); + +		// Changing UNDEFINE NAME to DEFINE NAME = null to save from creating an extra token parser/node +		$code = preg_replace('#<!-- UNDEFINE \$(.*)-->#', '{% DEFINE $1= null %}', $code); + +		// Replace all of our variables, {$VARNAME}, with Twig style, {{ definition.VARNAME }} +		$code = preg_replace('#{\$([a-zA-Z0-9_\.]+)}#', '{{ definition.$1 }}', $code); + +		// Replace all of our variables, ~ $VARNAME ~, with Twig style, ~ definition.VARNAME ~ +		$code = preg_replace('#~ \$([a-zA-Z0-9_\.]+) ~#', '~ definition.$1 ~', $code); + +		return $code; +	} + +	/** +	* Replace Twig tag masks with Twig tag calls +	* +	* E.g. <!-- BLOCK foo --> with {% block foo %} +	* +	* @param string $code +	* @param array $twig_tags All tags we want to create a mask for +	* @return string +	*/ +	protected function replace_twig_tag_masks($code, $twig_tags) +	{ +		$callback = function ($matches) +		{ +			$matches[1] = strtolower($matches[1]); + +			return "{% {$matches[1]}{$matches[2]}%}"; +		}; + +		foreach ($twig_tags as &$tag) +		{ +			$tag = strtoupper($tag); +		} + +		// twig_tags is an array of the twig tags, which are all lowercase, but we use all uppercase tags +		$code = preg_replace_callback('#<!-- (' . implode('|', $twig_tags) . ')(.*?)-->#',$callback, $code); + +		return $code; +	} +} diff --git a/phpBB/includes/template/twig/node/define.php b/phpBB/includes/template/twig/node/define.php new file mode 100644 index 0000000000..fcb19cc773 --- /dev/null +++ b/phpBB/includes/template/twig/node/define.php @@ -0,0 +1,58 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2013 phpBB Group, sections (c) 2009 Fabien Potencier, Armin Ronacher +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + + +class phpbb_template_twig_node_define extends Twig_Node +{ +	public function __construct($capture, Twig_NodeInterface $name, Twig_NodeInterface $value, $lineno, $tag = null) +	{ +		parent::__construct(array('name' => $name, 'value' => $value), array('capture' => $capture, 'safe' => false), $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('capture')) { +			$compiler +				->write("ob_start();\n") +				->subcompile($this->getNode('value')) +			; + +			$compiler->write("\$value = ('' === \$value = ob_get_clean()) ? '' : new Twig_Markup(\$value, \$this->env->getCharset());\n"); +		} +		else +		{ +			$compiler +				->write("\$value = ") +				->subcompile($this->getNode('value')) +				->raw(";\n") +			; +		} + +		$compiler +			->write("\$context['definition']->set('") +			->raw($this->getNode('name')->getAttribute('name')) +			->raw("', \$value);\n") +		; +	} +} diff --git a/phpBB/includes/template/twig/node/event.php b/phpBB/includes/template/twig/node/event.php new file mode 100644 index 0000000000..971dea14fa --- /dev/null +++ b/phpBB/includes/template/twig/node/event.php @@ -0,0 +1,79 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2013 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + + +class phpbb_template_twig_node_event extends Twig_Node +{ +	/** @var Twig_Environment */ +	protected $environment; + +	public function __construct(Twig_Node_Expression $expr, phpbb_template_twig_environment $environment, $lineno, $tag = null) +	{ +		$this->environment = $environment; + +		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); + +		$location = $this->getNode('expr')->getAttribute('name'); + +		foreach ($this->environment->get_phpbb_extensions() as $ext_namespace => $ext_path) +		{ +			$ext_namespace = str_replace('/', '_', $ext_namespace); + +			if (defined('DEBUG')) +			{ +				// If debug mode is enabled, lets check for new/removed EVENT +				//  templates on page load rather than at compile. This is +				//  slower, but makes developing extensions easier (no need to +				//  purge the cache when a new event template file is added) +		        $compiler +		            ->write("if (\$this->env->getLoader()->exists('@{$ext_namespace}/{$location}.html')) {\n") +		            ->indent() +		        ; +			} + +			if (defined('DEBUG') || $this->environment->getLoader()->exists('@' . $ext_namespace . '/' . $location . '.html')) +			{ +				$compiler +					->write("\$previous_look_up_order = \$this->env->getNamespaceLookUpOrder();\n") + +					// We set the namespace lookup order to be this extension first, then the main path +					->write("\$this->env->setNamespaceLookUpOrder(array('{$ext_namespace}', '__main__'));\n") +					->write("\$this->env->loadTemplate('@{$ext_namespace}/{$location}.html')->display(\$context);\n") +					->write("\$this->env->setNamespaceLookUpOrder(\$previous_look_up_order);\n") +				; +			} + +			if (defined('DEBUG')) +			{ +				$compiler +					->outdent() +		            ->write("}\n\n") +				; +			} +		} +	} +} diff --git a/phpBB/includes/template/twig/node/expression/binary/equalequal.php b/phpBB/includes/template/twig/node/expression/binary/equalequal.php new file mode 100644 index 0000000000..8ec2069114 --- /dev/null +++ b/phpBB/includes/template/twig/node/expression/binary/equalequal.php @@ -0,0 +1,25 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2013 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + + +class phpbb_template_twig_node_expression_binary_equalequal extends Twig_Node_Expression_Binary +{ +	public function operator(Twig_Compiler $compiler) +	{ +		return $compiler->raw('==='); +	} +} diff --git a/phpBB/includes/template/twig/node/expression/binary/notequalequal.php b/phpBB/includes/template/twig/node/expression/binary/notequalequal.php new file mode 100644 index 0000000000..96f32c502e --- /dev/null +++ b/phpBB/includes/template/twig/node/expression/binary/notequalequal.php @@ -0,0 +1,25 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2013 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + + +class phpbb_template_twig_node_expression_binary_notequalequal extends Twig_Node_Expression_Binary +{ +	public function operator(Twig_Compiler $compiler) +	{ +		return $compiler->raw('!=='); +	} +} diff --git a/phpBB/includes/template/twig/node/include.php b/phpBB/includes/template/twig/node/include.php new file mode 100644 index 0000000000..5c6ae1bbcf --- /dev/null +++ b/phpBB/includes/template/twig/node/include.php @@ -0,0 +1,56 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2013 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + + +class phpbb_template_twig_node_include extends Twig_Node_Include +{ +	/** +	 * Compiles the node to PHP. +	 * +	 * @param Twig_Compiler A Twig_Compiler instance +	 */ +	public function compile(Twig_Compiler $compiler) +	{ +		$compiler->addDebugInfo($this); + +		$compiler +			->write("\$location = ") +			->subcompile($this->getNode('expr')) +			->raw(";\n") +			->write("\$namespace = false;\n") +			->write("if (strpos(\$location, '@') === 0) {\n") +			->indent() +				->write("\$namespace = substr(\$location, 1, strpos(\$location, '/') - 1);\n") +				->write("\$previous_look_up_order = \$this->env->getNamespaceLookUpOrder();\n") + +				// We set the namespace lookup order to be this namespace first, then the main path +				->write("\$this->env->setNamespaceLookUpOrder(array(\$namespace, '__main__'));\n") +			->outdent() +			->write("}\n") +		; + +		parent::compile($compiler); + +		$compiler +			->write("if (\$namespace) {\n") +			->indent() +				->write("\$this->env->setNamespaceLookUpOrder(\$previous_look_up_order);\n") +			->outdent() +			->write("}\n") +		; +	} +} diff --git a/phpBB/includes/template/twig/node/includeasset.php b/phpBB/includes/template/twig/node/includeasset.php new file mode 100644 index 0000000000..5abff10e3f --- /dev/null +++ b/phpBB/includes/template/twig/node/includeasset.php @@ -0,0 +1,65 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2013 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +class phpbb_template_twig_node_includeasset extends Twig_Node +{ +	/** @var Twig_Environment */ +	protected $environment; + +	public function __construct(Twig_Node_Expression $expr, phpbb_template_twig_environment $environment, $lineno, $tag = null) +	{ +		$this->environment = $environment; + +		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); + +		$config = $this->environment->get_phpbb_config(); + +		$compiler +			->write("\$asset_file = ") +			->subcompile($this->getNode('expr')) +			->raw(";\n") +			->write("\$argument_string = \$anchor_string = '';\n") +			->write("if ((\$argument_string_start = strpos(\$asset_file, '?')) !== false) {\n") +			->indent() +				->write("\$argument_string = substr(\$asset_file, \$argument_string_start);\n") +				->write("\$asset_file = substr(\$asset_file, 0, \$argument_string_start);\n") +				->write("if ((\$anchor_string_start = strpos(\$argument_string, '#')) !== false) {\n") +				->indent() +					->write("\$anchor_string = substr(\$argument_string, \$anchor_string_start);\n") +					->write("\$argument_string = substr(\$argument_string, 0, \$anchor_string_start);\n") +				->outdent() +				->write("}\n") +			->outdent() +			->write("}\n") +			->write("if (strpos(\$asset_file, '//') !== 0 && strpos(\$asset_file, 'http://') !== 0 && strpos(\$asset_file, 'https://') !== 0 && !file_exists(\$asset_file)) {\n") +			->indent() +				->write("\$asset_file = \$this->getEnvironment()->getLoader()->getCacheKey(\$asset_file);\n") +				->write("\$argument_string .= ((\$argument_string) ? '&' : '?') . 'assets_version={$config['assets_version']}';\n") +			->outdent() +			->write("}\n") +			->write("\$asset_file .= \$argument_string . \$anchor_string;\n") +			->write("\$context['definition']->append('{$this->get_definition_name()}', '") +		; + +		$this->append_asset($compiler); + +		$compiler +			->raw("\n');\n") +		; +	} +} diff --git a/phpBB/includes/template/twig/node/includejs.php b/phpBB/includes/template/twig/node/includejs.php new file mode 100644 index 0000000000..fdf2bea3ed --- /dev/null +++ b/phpBB/includes/template/twig/node/includejs.php @@ -0,0 +1,32 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2013 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +class phpbb_template_twig_node_includejs extends phpbb_template_twig_node_includeasset +{ +	public function get_definition_name() +	{ +		return 'SCRIPTS'; +	} + +	/** +	 * Compiles the node to PHP. +	 * +	 * @param Twig_Compiler A Twig_Compiler instance +	 */ +	protected function append_asset(Twig_Compiler $compiler) +	{ +		$config = $this->environment->get_phpbb_config(); + +		$compiler +			->raw("<script type=\"text/javascript\" src=\"' . ") +			->raw("\$asset_file") +			->raw(". '\"></script>\n") +		; +	} +} diff --git a/phpBB/includes/template/twig/node/includephp.php b/phpBB/includes/template/twig/node/includephp.php new file mode 100644 index 0000000000..dbe54f0e1a --- /dev/null +++ b/phpBB/includes/template/twig/node/includephp.php @@ -0,0 +1,91 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2013 phpBB Group, sections (c) 2009 Fabien Potencier, Armin Ronacher +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + + +class phpbb_template_twig_node_includephp extends Twig_Node +{ +	/** @var Twig_Environment */ +	protected $environment; + +	public function __construct(Twig_Node_Expression $expr, phpbb_template_twig_environment $environment, $ignoreMissing = false, $lineno, $tag = null) +	{ +		$this->environment = $environment; + +		parent::__construct(array('expr' => $expr), array('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); + +		$config = $this->environment->get_phpbb_config(); + +		if (!$config['tpl_allow_php']) +		{ +			$compiler +				->write("// INCLUDEPHP Disabled\n") +			; + +			return; +		} + +		if ($this->getAttribute('ignore_missing')) { +			$compiler +				->write("try {\n") +				->indent() +			; +		} + +		$compiler +			->write("\$location = ") +			->subcompile($this->getNode('expr')) +			->raw(";\n") +			->write("if (phpbb_is_absolute(\$location)) {\n") +			->indent() +				// Absolute path specified +				->write("require(\$location);\n") +			->outdent() +			->write("} else if (file_exists(\$this->getEnvironment()->get_phpbb_root_path() . \$location)) {\n") +			->indent() +				// PHP file relative to phpbb_root_path +				->write("require(\$this->getEnvironment()->get_phpbb_root_path() . \$location);\n") +			->outdent() +			->write("} else {\n") +			->indent() +				// Local path (behaves like INCLUDE) +				->write("require(\$this->getEnvironment()->getLoader()->getCacheKey(\$location));\n") +			->outdent() +			->write("}\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") +			; +		} +	} +} diff --git a/phpBB/includes/template/twig/node/php.php b/phpBB/includes/template/twig/node/php.php new file mode 100644 index 0000000000..c11539ea7f --- /dev/null +++ b/phpBB/includes/template/twig/node/php.php @@ -0,0 +1,55 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2013 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + + +class phpbb_template_twig_node_php extends Twig_Node +{ +	/** @var Twig_Environment */ +	protected $environment; + +	public function __construct(Twig_Node_Text $text, phpbb_template_twig_environment $environment, $lineno, $tag = null) +	{ +		$this->environment = $environment; + +		parent::__construct(array('text' => $text), array(), $lineno, $tag); +	} + +	/** +	 * Compiles the node to PHP. +	 * +	 * @param Twig_Compiler A Twig_Compiler instance +	 */ +	public function compile(Twig_Compiler $compiler) +	{ +		$compiler->addDebugInfo($this); + +		$config = $this->environment->get_phpbb_config(); + +		if (!$config['tpl_allow_php']) +		{ +			$compiler +				->write("// PHP Disabled\n") +			; + +			return; +		} + +		$compiler +			->raw($this->getNode('text')->getAttribute('data')) +		; +	} +} diff --git a/phpBB/includes/template/twig/tokenparser/define.php b/phpBB/includes/template/twig/tokenparser/define.php new file mode 100644 index 0000000000..4ea15388c4 --- /dev/null +++ b/phpBB/includes/template/twig/tokenparser/define.php @@ -0,0 +1,66 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2013 phpBB Group, sections (c) 2009 Fabien Potencier +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + + +class phpbb_template_twig_tokenparser_define 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 = $this->parser->getExpressionParser()->parseExpression(); + +		$capture = false; +		if ($stream->test(Twig_Token::OPERATOR_TYPE, '=')) { +			$stream->next(); +			$value = $this->parser->getExpressionParser()->parseExpression(); + +			$stream->expect(Twig_Token::BLOCK_END_TYPE); +		} else { +			$capture = true; + +			$stream->expect(Twig_Token::BLOCK_END_TYPE); + +			$value = $this->parser->subparse(array($this, 'decideBlockEnd'), true); +			$stream->expect(Twig_Token::BLOCK_END_TYPE); +		} + +		return new phpbb_template_twig_node_define($capture, $name, $value, $lineno, $this->getTag()); +	} + +	public function decideBlockEnd(Twig_Token $token) +	{ +		return $token->test('ENDDEFINE'); +	} + +	/** +	 * Gets the tag name associated with this token parser. +	 * +	 * @return string The tag name +	 */ +	public function getTag() +	{ +		return 'DEFINE'; +	} +} diff --git a/phpBB/includes/template/twig/tokenparser/event.php b/phpBB/includes/template/twig/tokenparser/event.php new file mode 100644 index 0000000000..e4dddd6dcc --- /dev/null +++ b/phpBB/includes/template/twig/tokenparser/event.php @@ -0,0 +1,47 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2013 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + + +class phpbb_template_twig_tokenparser_event 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(); + +		$stream = $this->parser->getStream(); +		$stream->expect(Twig_Token::BLOCK_END_TYPE); + +		return new phpbb_template_twig_node_event($expr, $this->parser->getEnvironment(), $token->getLine(), $this->getTag()); +	} + +	/** +	 * Gets the tag name associated with this token parser. +	 * +	 * @return string The tag name +	 */ +	public function getTag() +	{ +		return 'EVENT'; +	} +} diff --git a/phpBB/includes/template/twig/tokenparser/include.php b/phpBB/includes/template/twig/tokenparser/include.php new file mode 100644 index 0000000000..520f9fd1a0 --- /dev/null +++ b/phpBB/includes/template/twig/tokenparser/include.php @@ -0,0 +1,46 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2013 phpBB Group, sections (c) 2009 Fabien Potencier +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + + +class phpbb_template_twig_tokenparser_include extends Twig_TokenParser_Include +{ +	/** +	 * 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 phpbb_template_twig_node_include($expr, $variables, $only, $ignoreMissing, $token->getLine(), $this->getTag()); +	} + +	/** +	 * Gets the tag name associated with this token parser. +	 * +	 * @return string The tag name +	 */ +	public function getTag() +	{ +		return 'INCLUDE'; +	} +} diff --git a/phpBB/includes/template/twig/tokenparser/includejs.php b/phpBB/includes/template/twig/tokenparser/includejs.php new file mode 100644 index 0000000000..b02b2f89ba --- /dev/null +++ b/phpBB/includes/template/twig/tokenparser/includejs.php @@ -0,0 +1,47 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2013 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + + +class phpbb_template_twig_tokenparser_includejs 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(); + +		$stream = $this->parser->getStream(); +		$stream->expect(Twig_Token::BLOCK_END_TYPE); + +		return new phpbb_template_twig_node_includejs($expr, $this->parser->getEnvironment(), $token->getLine(), $this->getTag()); +	} + +	/** +	 * Gets the tag name associated with this token parser. +	 * +	 * @return string The tag name +	 */ +	public function getTag() +	{ +		return 'INCLUDEJS'; +	} +} diff --git a/phpBB/includes/template/twig/tokenparser/includephp.php b/phpBB/includes/template/twig/tokenparser/includephp.php new file mode 100644 index 0000000000..13fe6de8a6 --- /dev/null +++ b/phpBB/includes/template/twig/tokenparser/includephp.php @@ -0,0 +1,56 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2013 phpBB Group, sections (c) 2009 Fabien Potencier, Armin Ronacher +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + + +class phpbb_template_twig_tokenparser_includephp 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(); + +		$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; +		} + +		$stream->expect(Twig_Token::BLOCK_END_TYPE); + +		return new phpbb_template_twig_node_includephp($expr, $this->parser->getEnvironment(), $ignoreMissing, $token->getLine(), $this->getTag()); +	} + +	/** +	 * Gets the tag name associated with this token parser. +	 * +	 * @return string The tag name +	 */ +	public function getTag() +	{ +		return 'INCLUDEPHP'; +	} +} diff --git a/phpBB/includes/template/twig/tokenparser/php.php b/phpBB/includes/template/twig/tokenparser/php.php new file mode 100644 index 0000000000..197980a59a --- /dev/null +++ b/phpBB/includes/template/twig/tokenparser/php.php @@ -0,0 +1,55 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2013 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + + +class phpbb_template_twig_tokenparser_php 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) +	{ +		$stream = $this->parser->getStream(); + +		$stream->expect(Twig_Token::BLOCK_END_TYPE); + +		$body = $this->parser->subparse(array($this, 'decideEnd'), true); + +		$stream->expect(Twig_Token::BLOCK_END_TYPE); + +		return new phpbb_template_twig_node_php($body, $this->parser->getEnvironment(), $token->getLine(), $this->getTag()); +	} + +	public function decideEnd(Twig_Token $token) +	{ +		return $token->test('ENDPHP'); +	} + +	/** +	 * Gets the tag name associated with this token parser. +	 * +	 * @return string The tag name +	 */ +	public function getTag() +	{ +		return 'PHP'; +    } +} diff --git a/phpBB/includes/template/twig/twig.php b/phpBB/includes/template/twig/twig.php new file mode 100644 index 0000000000..f5638972a3 --- /dev/null +++ b/phpBB/includes/template/twig/twig.php @@ -0,0 +1,465 @@ +<?php +/** +* +* @package phpBB3 +* @copyright (c) 2013 phpBB Group +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ +	exit; +} + +/** +* Twig Template class. +* @package phpBB3 +*/ +class phpbb_template_twig implements phpbb_template +{ +	/** +	* Template context. +	* Stores template data used during template rendering. +	* @var phpbb_template_context +	*/ +	protected $context; + +	/** +	* Path of the cache directory for the template +	* +	* Cannot be changed during runtime. +	* +	* @var string +	*/ +	private $cachepath = ''; + +	/** +	* phpBB root path +	* @var string +	*/ +	protected $phpbb_root_path; + +	/** +	* adm relative path +	* @var string +	*/ +	protected $adm_relative_path; + +	/** +	* PHP file extension +	* @var string +	*/ +	protected $php_ext; + +	/** +	* phpBB config instance +	* @var phpbb_config +	*/ +	protected $config; + +	/** +	* Current user +	* @var phpbb_user +	*/ +	protected $user; + +	/** +	* Extension manager. +	* +	* @var phpbb_extension_manager +	*/ +	protected $extension_manager; + +	/** +	* Name of the style that the template being compiled and/or rendered +	* belongs to, and its parents, in inheritance tree order. +	* +	* Used to invoke style-specific template events. +	* +	* @var array +	*/ +	protected $style_names; + +	/** +	* Twig Environment +	* +	* @var Twig_Environment +	*/ +	protected $twig; + +	/** +	* Array of filenames assigned to set_filenames +	* +	* @var array +	*/ +	protected $filenames = array(); + +	/** +	* Constructor. +	* +	* @param string $phpbb_root_path phpBB root path +	* @param string $php_ext php extension (typically 'php') +	* @param phpbb_config $config +	* @param phpbb_user $user +	* @param phpbb_template_context $context template context +	* @param phpbb_extension_manager $extension_manager extension manager, if null then template events will not be invoked +	* @param string $adm_relative_path relative path to adm directory +	*/ +	public function __construct($phpbb_root_path, $php_ext, $config, $user, phpbb_template_context $context, phpbb_extension_manager $extension_manager = null, $adm_relative_path = null) +	{ +		$this->phpbb_root_path = $phpbb_root_path; +		$this->adm_relative_path = $adm_relative_path; +		$this->php_ext = $php_ext; +		$this->config = $config; +		$this->user = $user; +		$this->context = $context; +		$this->extension_manager = $extension_manager; + +		$this->cachepath = $phpbb_root_path . 'cache/twig/'; + +		// Initiate the loader, __main__ namespace paths will be setup later in set_style_names() +		$loader = new Twig_Loader_Filesystem(''); + +		$this->twig = new phpbb_template_twig_environment( +			$this->config, +			($this->extension_manager) ? $this->extension_manager->all_enabled() : array(), +			$this->phpbb_root_path, +			$loader, +			array( +				'cache'			=> (defined('IN_INSTALL')) ? false : $this->cachepath, +				'debug'			=> defined('DEBUG'), +				'auto_reload'	=> (bool) $this->config['load_tplcompile'], +				'autoescape'	=> false, +			) +		); + +		$this->twig->addExtension( +			new phpbb_template_twig_extension( +				$this->context, +				$this->user +			) +		); + +		$lexer = new phpbb_template_twig_lexer($this->twig); + +		$this->twig->setLexer($lexer); +	} + +	/** +	* Clear the cache +	* +	* @return phpbb_template +	*/ +	public function clear_cache() +	{ +		if (is_dir($this->cachepath)) +		{ +			$this->twig->clearCacheFiles(); +		} + +		return $this; +	} + +	/** +	* Sets the template filenames for handles. +	* +	* @param array $filename_array Should be a hash of handle => filename pairs. +	* @return phpbb_template $this +	*/ +	public function set_filenames(array $filename_array) +	{ +		$this->filenames = array_merge($filename_array, $this->filenames); + +		return $this; +	} + +	/** +	* Sets the style names/paths corresponding to style hierarchy being compiled +	* and/or rendered. +	* +	* @param array $style_names List of style names in inheritance tree order +	* @param array $style_paths List of style paths in inheritance tree order +	* @param bool $is_core True if the style names are the "core" styles for this page load +	* 	Core means the main phpBB template files +	* @return phpbb_template $this +	*/ +	public function set_style_names(array $style_names, array $style_paths, $is_core = false) +	{ +		$this->style_names = $style_names; + +		// Set as __main__ namespace +		$this->twig->getLoader()->setPaths($style_paths); + +		// Core style namespace from phpbb_style::set_style() +		if ($is_core) +		{ +			$this->twig->getLoader()->setPaths($style_paths, 'core'); +		} + +		// Add admin namespace +		if (is_dir($this->phpbb_root_path . $this->adm_relative_path . 'style/')) +		{ +			$this->twig->getLoader()->setPaths($this->phpbb_root_path . $this->adm_relative_path . 'style/', 'admin'); +		} + +		// Add all namespaces for all extensions +		if ($this->extension_manager instanceof phpbb_extension_manager) +		{ +			$style_names[] = 'all'; + +			foreach ($this->extension_manager->all_enabled() as $ext_namespace => $ext_path) +			{ +				// namespaces cannot contain / +				$namespace = str_replace('/', '_', $ext_namespace); +				$paths = array(); + +				foreach ($style_names as $style_name) +				{ +					$ext_style_path = $ext_path . 'styles/' . $style_name . '/template'; + +					if (is_dir($ext_style_path)) +					{ +						$paths[] = $ext_style_path; +					} +				} + +				$this->twig->getLoader()->setPaths($paths, $namespace); +			} +		} + +		return $this; +	} + +	/** +	* Clears all variables and blocks assigned to this template. +	* +	* @return phpbb_template $this +	*/ +	public function destroy() +	{ +		$this->context = array(); + +		return $this; +	} + +	/** +	* Reset/empty complete block +	* +	* @param string $blockname Name of block to destroy +	* @return phpbb_template $this +	*/ +	public function destroy_block_vars($blockname) +	{ +		$this->context->destroy_block_vars($blockname); + +		return $this; +	} + +	/** +	* Display a template for provided handle. +	* +	* The template will be loaded and compiled, if necessary, first. +	* +	* This function calls hooks. +	* +	* @param string $handle Handle to display +	* @return phpbb_template $this +	*/ +	public function display($handle) +	{ +		$result = $this->call_hook($handle, __FUNCTION__); +		if ($result !== false) +		{ +			return $result[0]; +		} + +		$this->twig->display($this->get_filename_from_handle($handle), $this->get_template_vars()); + +		return $this; +	} + +	/** +	* Calls hook if any is defined. +	* +	* @param string $handle Template handle being displayed. +	* @param string $method Method name of the caller. +	*/ +	protected function call_hook($handle, $method) +	{ +		global $phpbb_hook; + +		if (!empty($phpbb_hook) && $phpbb_hook->call_hook(array(__CLASS__, $method), $handle, $this)) +		{ +			if ($phpbb_hook->hook_return(array(__CLASS__, $method))) +			{ +				$result = $phpbb_hook->hook_return_result(array(__CLASS__, $method)); +				return array($result); +			} +		} + +		return false; +	} + +	/** +	* Display the handle and assign the output to a template variable +	* or return the compiled result. +	* +	* @param string $handle Handle to operate on +	* @param string $template_var Template variable to assign compiled handle to +	* @param bool $return_content If true return compiled handle, otherwise assign to $template_var +	* @return phpbb_template|string if $return_content is true return string of the compiled handle, otherwise return $this +	*/ +	public function assign_display($handle, $template_var = '', $return_content = true) +	{ +		if ($return_content) +		{ +			return $this->twig->render($this->get_filename_from_handle($handle), $this->get_template_vars()); +		} + +		$this->assign_var($template_var, $this->twig->render($this->get_filename_from_handle($handle, $this->get_template_vars()))); + +		return $this; +	} + +	/** +	* Assign key variable pairs from an array +	* +	* @param array $vararray A hash of variable name => value pairs +	* @return phpbb_template $this +	*/ +	public function assign_vars(array $vararray) +	{ +		foreach ($vararray as $key => $val) +		{ +			$this->assign_var($key, $val); +		} + +		return $this; +	} + +	/** +	* Assign a single scalar value to a single key. +	* +	* Value can be a string, an integer or a boolean. +	* +	* @param string $varname Variable name +	* @param string $varval Value to assign to variable +	* @return phpbb_template $this +	*/ +	public function assign_var($varname, $varval) +	{ +		$this->context->assign_var($varname, $varval); + +		return $this; +	} + +	/** +	* Append text to the string value stored in a key. +	* +	* Text is appended using the string concatenation operator (.). +	* +	* @param string $varname Variable name +	* @param string $varval Value to append to variable +	* @return phpbb_template $this +	*/ +	public function append_var($varname, $varval) +	{ +		$this->context->append_var($varname, $varval); + +		return $this; +	} + +	/** +	* Assign key variable pairs from an array to a specified block +	* @param string $blockname Name of block to assign $vararray to +	* @param array $vararray A hash of variable name => value pairs +	* @return phpbb_template $this +	*/ +	public function assign_block_vars($blockname, array $vararray) +	{ +		$this->context->assign_block_vars($blockname, $vararray); + +		return $this; +	} + +	/** +	* Change already assigned key variable pair (one-dimensional - single loop entry) +	* +	* An example of how to use this function: +	* {@example alter_block_array.php} +	* +	* @param	string	$blockname	the blockname, for example 'loop' +	* @param	array	$vararray	the var array to insert/add or merge +	* @param	mixed	$key		Key to search for +	* +	* array: KEY => VALUE [the key/value pair to search for within the loop to determine the correct position] +	* +	* int: Position [the position to change or insert at directly given] +	* +	* If key is false the position is set to 0 +	* If key is true the position is set to the last entry +	* +	* @param	string	$mode		Mode to execute (valid modes are 'insert' and 'change') +	* +	*	If insert, the vararray is inserted at the given position (position counting from zero). +	*	If change, the current block gets merged with the vararray (resulting in new key/value pairs be added and existing keys be replaced by the new value). +	* +	* Since counting begins by zero, inserting at the last position will result in this array: array(vararray, last positioned array) +	* and inserting at position 1 will result in this array: array(first positioned array, vararray, following vars) +	* +	* @return bool false on error, true on success +	*/ +	public function alter_block_array($blockname, array $vararray, $key = false, $mode = 'insert') +	{ +		return $this->context->alter_block_array($blockname, $vararray, $key, $mode); +	} + +	/** +	* Get template vars in a format Twig will use (from the context) +	* +	* @return array +	*/ +	public function get_template_vars() +	{ +		$context_vars = $this->context->get_data_ref(); + +		$vars = array_merge( +			$context_vars['.'][0], // To get normal vars +			$context_vars, // To get loops +			array( +				'definition'	=> new phpbb_template_twig_definition(), +				'user'			=> $this->user, +			) +		); + +		// cleanup +		unset($vars['.']); + +		return $vars; +	} + +	/** +	* Get a filename from the handle +	* +	* @param string $handle +	* @return string +	*/ +	protected function get_filename_from_handle($handle) +	{ +		return (isset($this->filenames[$handle])) ? $this->filenames[$handle] : $handle; +	} + +	/** +	* Get path to template for handle (required for BBCode parser) +	* +	* @return string +	*/ +	public function get_source_file_for_handle($handle) +	{ +		return $this->twig->getLoader()->getCacheKey($this->get_filename_from_handle($handle)); +	} +} diff --git a/phpBB/install/index.php b/phpBB/install/index.php index f745f51974..84a2a023f8 100644 --- a/phpBB/install/index.php +++ b/phpBB/install/index.php @@ -214,7 +214,7 @@ $config = new phpbb_config(array(  $phpbb_style_resource_locator = new phpbb_style_resource_locator();  $phpbb_style_path_provider = new phpbb_style_path_provider(); -$template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $phpbb_style_resource_locator, new phpbb_template_context()); +$template = new phpbb_template_twig($phpbb_root_path, $phpEx, $config, $user, new phpbb_template_context());  $phpbb_style = new phpbb_style($phpbb_root_path, $phpEx, $config, $user, $phpbb_style_resource_locator, $phpbb_style_path_provider, $template);  $phpbb_style->set_ext_dir_prefix('adm/');  $phpbb_style->set_custom_style('admin', $phpbb_admin_path . 'style', array(), ''); diff --git a/phpBB/styles/prosilver/template/memberlist_search.html b/phpBB/styles/prosilver/template/memberlist_search.html index a4468b4af4..4f029627fc 100644 --- a/phpBB/styles/prosilver/template/memberlist_search.html +++ b/phpBB/styles/prosilver/template/memberlist_search.html @@ -38,7 +38,7 @@ function insert_single(user)  // ]]>  </script>  <!-- ENDIF --> -<!-- INCLUDEJS template/forum_fn.js --> +<!-- INCLUDEJS forum_fn.js -->  <h2 class="solo">{L_FIND_USERNAME}</h2>  <form method="post" action="{S_MODE_ACTION}" id="search_memberlist"> diff --git a/phpBB/styles/prosilver/template/overall_footer.html b/phpBB/styles/prosilver/template/overall_footer.html index ba0412ddd3..eef9f53409 100644 --- a/phpBB/styles/prosilver/template/overall_footer.html +++ b/phpBB/styles/prosilver/template/overall_footer.html @@ -53,11 +53,13 @@  <script type="text/javascript" src="{T_JQUERY_LINK}"></script>  <!-- IF S_JQUERY_FALLBACK --><script type="text/javascript">window.jQuery || document.write(unescape('%3Cscript src="{T_ASSETS_PATH}/javascript/jquery.js?assets_version={T_ASSETS_VERSION}" type="text/javascript"%3E%3C/script%3E'));</script><!-- ENDIF --> -<script type="text/javascript" src="{T_SUPER_TEMPLATE_PATH}/forum_fn.js?assets_version={T_ASSETS_VERSION}"></script> -<script type="text/javascript" src="{T_ASSETS_PATH}/javascript/core.js?assets_version={T_ASSETS_VERSION}"></script> -<!-- INCLUDEJS template/ajax.js --> -{SCRIPTS} +<!-- INCLUDEJS forum_fn.js --> +<!-- INCLUDEJS {T_ASSETS_PATH}/javascript/core.js --> +<!-- INCLUDEJS ajax.js -->  <!-- EVENT overall_footer_after --> + +{$SCRIPTS} +  </body>  </html> diff --git a/phpBB/styles/prosilver/template/posting_buttons.html b/phpBB/styles/prosilver/template/posting_buttons.html index fadbc9b3ca..8a153a5ee8 100644 --- a/phpBB/styles/prosilver/template/posting_buttons.html +++ b/phpBB/styles/prosilver/template/posting_buttons.html @@ -39,7 +39,7 @@  	{  		dE('colour_palette');  		e = document.getElementById('colour_palette'); -		 +  		if (e.style.display == 'block')  		{  			document.getElementById('bbpalette').value = '{LA_FONT_COLOR_HIDE}'; @@ -52,7 +52,7 @@  // ]]>  </script> -<!-- INCLUDEJS template/editor.js --> +<!-- INCLUDEJS editor.js -->  <!-- IF S_BBCODE_ALLOWED -->  <div id="colour_palette" style="display: none;"> diff --git a/phpBB/styles/prosilver/template/timezone_option.html b/phpBB/styles/prosilver/template/timezone_option.html index a77e82f9a1..fc0579bffd 100644 --- a/phpBB/styles/prosilver/template/timezone_option.html +++ b/phpBB/styles/prosilver/template/timezone_option.html @@ -15,6 +15,6 @@  			{S_TZ_OPTIONS}  		</select> -		<!-- INCLUDEJS template/timezone.js --> +		<!-- INCLUDEJS timezone.js -->  	</dd>  </dl> diff --git a/phpBB/styles/prosilver/template/ucp_avatar_options.html b/phpBB/styles/prosilver/template/ucp_avatar_options.html index e7f4a48e11..072f235eb1 100644 --- a/phpBB/styles/prosilver/template/ucp_avatar_options.html +++ b/phpBB/styles/prosilver/template/ucp_avatar_options.html @@ -47,4 +47,4 @@  	</div>  </div> -<!-- INCLUDEJS template/avatars.js --> +<!-- INCLUDEJS avatars.js --> diff --git a/phpBB/styles/subsilver2/template/overall_footer.html b/phpBB/styles/subsilver2/template/overall_footer.html index 3a29a0d752..d2b30c22a0 100644 --- a/phpBB/styles/subsilver2/template/overall_footer.html +++ b/phpBB/styles/subsilver2/template/overall_footer.html @@ -13,9 +13,11 @@  <script type="text/javascript" src="{T_JQUERY_LINK}"></script>  <!-- IF S_JQUERY_FALLBACK --><script type="text/javascript">window.jQuery || document.write(unescape('%3Cscript src="{T_ASSETS_PATH}/javascript/jquery.js?assets_version={T_ASSETS_VERSION}" type="text/javascript"%3E%3C/script%3E'));</script><!-- ENDIF --> -<script type="text/javascript" src="{T_ASSETS_PATH}/javascript/core.js?assets_version={T_ASSETS_VERSION}"></script> -{SCRIPTS} +<!-- INCLUDEJS {T_ASSETS_PATH}/javascript/core.js -->  <!-- EVENT overall_footer_after --> + +{$SCRIPTS} +  </body>  </html> diff --git a/phpBB/styles/subsilver2/template/timezone_option.html b/phpBB/styles/subsilver2/template/timezone_option.html index 26ba2388c9..0f27719f86 100644 --- a/phpBB/styles/subsilver2/template/timezone_option.html +++ b/phpBB/styles/subsilver2/template/timezone_option.html @@ -15,6 +15,6 @@  			{S_TZ_OPTIONS}  		</select> -		<!-- INCLUDEJS template/timezone.js --> +		<!-- INCLUDEJS timezone.js -->  	</td>  </tr> diff --git a/phpBB/styles/subsilver2/template/ucp_groups_manage.html b/phpBB/styles/subsilver2/template/ucp_groups_manage.html index 8064e1e6e9..b8e7e29481 100644 --- a/phpBB/styles/subsilver2/template/ucp_groups_manage.html +++ b/phpBB/styles/subsilver2/template/ucp_groups_manage.html @@ -95,7 +95,7 @@  	</tr>  	</table> -<!-- INCLUDEJS template/avatars.js --> +<!-- INCLUDEJS avatars.js -->  <!-- ELSEIF S_LIST --> diff --git a/phpBB/styles/subsilver2/template/ucp_profile_avatar.html b/phpBB/styles/subsilver2/template/ucp_profile_avatar.html index 697cceabb9..60a816d00a 100644 --- a/phpBB/styles/subsilver2/template/ucp_profile_avatar.html +++ b/phpBB/styles/subsilver2/template/ucp_profile_avatar.html @@ -48,6 +48,6 @@  	</tr>  </table> -<!-- INCLUDEJS template/avatars.js --> +<!-- INCLUDEJS avatars.js -->  <!-- INCLUDE ucp_footer.html --> diff --git a/tests/controller/helper_url_test.php b/tests/controller/helper_url_test.php index 2c22700ca6..6686b77e8f 100644 --- a/tests/controller/helper_url_test.php +++ b/tests/controller/helper_url_test.php @@ -45,15 +45,17 @@ class phpbb_controller_helper_url_test extends phpbb_test_case  	*/  	public function test_helper_url($route, $params, $is_amp, $session_id, $expected, $description)  	{ -		global $phpbb_dispatcher; +		global $phpbb_dispatcher, $phpbb_root_path, $phpEx;  		$phpbb_dispatcher = new phpbb_mock_event_dispatcher;  		$this->style_resource_locator = new phpbb_style_resource_locator();  		$this->user = $this->getMock('phpbb_user'); -		$this->template = new phpbb_template($phpbb_root_path, $phpEx, $config, $this->user, $this->style_resource_locator, new phpbb_template_context()); +		$this->template = new phpbb_template_twig($phpbb_root_path, $phpEx, $config, $this->user, new phpbb_template_context()); +		$this->style_resource_locator = new phpbb_style_resource_locator(); +		$this->style_provider = new phpbb_style_path_provider(); +		$this->style = new phpbb_style($phpbb_root_path, $phpEx, new phpbb_config(array()), $this->user, $this->style_resource_locator, $this->style_provider, $this->template);  		$helper = new phpbb_controller_helper($this->template, $this->user, '', 'php');  		$this->assertEquals($helper->url($route, $params, $is_amp, $session_id), $expected);  	}  } - diff --git a/tests/extension/metadata_manager_test.php b/tests/extension/metadata_manager_test.php index d410333f09..bd88f396d9 100644 --- a/tests/extension/metadata_manager_test.php +++ b/tests/extension/metadata_manager_test.php @@ -42,12 +42,11 @@ class phpbb_extension_metadata_manager_test extends phpbb_database_test_case  		$this->user = new phpbb_user();  		$this->table_prefix = 'phpbb_'; -		$this->template = new phpbb_template( +		$this->template = new phpbb_template_twig(  			$this->phpbb_root_path,  			$this->phpEx,  			$this->config,  			$this->user, -			new phpbb_style_resource_locator(),  			new phpbb_template_context()  		); diff --git a/tests/template/includephp_test.php b/tests/template/includephp_test.php index f1012b6939..a3dc9bd5c5 100644 --- a/tests/template/includephp_test.php +++ b/tests/template/includephp_test.php @@ -15,9 +15,7 @@ class phpbb_template_includephp_test extends phpbb_template_template_test_case  	{  		$this->setup_engine(array('tpl_allow_php' => true)); -		$cache_file = $this->template->cachepath . 'includephp_relative.html.php'; - -		$this->run_template('includephp_relative.html', array(), array(), array(), "Path is relative to board root.\ntesting included php", $cache_file); +		$this->run_template('includephp_relative.html', array(), array(), array(), "Path is relative to board root.\ntesting included php");  		$this->template->set_filenames(array('test' => 'includephp_relative.html'));  		$this->assertEquals("Path is relative to board root.\ntesting included php", $this->display('test'), "Testing INCLUDEPHP"); @@ -27,9 +25,7 @@ class phpbb_template_includephp_test extends phpbb_template_template_test_case  	{  		$this->setup_engine(array('tpl_allow_php' => true)); -		$cache_file = $this->template->cachepath . 'includephp_variables.html.php'; - -		$this->run_template('includephp_variables.html', array('TEMPLATES' => 'templates'), array(), array(), "Path includes variables.\ntesting included php", $cache_file); +		$this->run_template('includephp_variables.html', array('TEMPLATES' => 'templates'), array(), array(), "Path includes variables.\ntesting included php");  		$this->template->set_filenames(array('test' => 'includephp_variables.html'));  		$this->assertEquals("Path includes variables.\ntesting included php", $this->display('test'), "Testing INCLUDEPHP"); @@ -37,11 +33,13 @@ class phpbb_template_includephp_test extends phpbb_template_template_test_case  	public function test_includephp_absolute()  	{ -		$path_to_php = dirname(__FILE__) . '/templates/_dummy_include.php.inc'; +		global $phpbb_root_path; + +		$path_to_php = str_replace('\\', '/', dirname(__FILE__)) . '/templates/_dummy_include.php.inc';  		$this->assertTrue(phpbb_is_absolute($path_to_php));  		$template_text = "Path is absolute.\n<!-- INCLUDEPHP $path_to_php -->"; -		$cache_dir = dirname($this->template->cachepath) . '/'; +		$cache_dir = dirname($phpbb_root_path . 'cache') . '/';  		$fp = fopen($cache_dir . 'includephp_absolute.html', 'w');  		fputs($fp, $template_text);  		fclose($fp); @@ -49,9 +47,8 @@ class phpbb_template_includephp_test extends phpbb_template_template_test_case  		$this->setup_engine(array('tpl_allow_php' => true));  		$this->style->set_custom_style('tests', $cache_dir, array(), ''); -		$cache_file = $this->template->cachepath . 'includephp_absolute.html.php'; -		$this->run_template('includephp_absolute.html', array(), array(), array(), "Path is absolute.\ntesting included php", $cache_file); +		$this->run_template('includephp_absolute.html', array(), array(), array(), "Path is absolute.\ntesting included php");  		$this->template->set_filenames(array('test' => 'includephp_absolute.html'));  		$this->assertEquals("Path is absolute.\ntesting included php", $this->display('test'), "Testing INCLUDEPHP"); diff --git a/tests/template/invalid_constructs_test.php b/tests/template/invalid_constructs_test.php deleted file mode 100644 index 19d192b8b6..0000000000 --- a/tests/template/invalid_constructs_test.php +++ /dev/null @@ -1,87 +0,0 @@ -<?php -/** -* -* @package testing -* @copyright (c) 2012 phpBB Group -* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 -* -*/ - -require_once dirname(__FILE__) . '/template_test_case.php'; - -class phpbb_template_invalid_constructs_test extends phpbb_template_template_test_case -{ -	public function template_data() -	{ -		return array( -			array( -				'Unknown tag', -				'invalid/unknown_tag.html', -				array(), -				array(), -				array(), -				'invalid/output/unknown_tag.html', -			), -			/* -			* Produces a parse error which is fatal, therefore -			* destroying the test suite. -			array( -				'ENDIF without IF', -				'invalid/endif_without_if.html', -				array(), -				array(), -				array(), -				'invalid/output/endif_without_if.html', -			), -			*/ -		); -	} - -	public function template_data_error() -	{ -		return array( -			array( -				'Include a nonexistent file', -				'invalid/include_nonexistent_file.html', -				array(), -				array(), -				array(), -				E_USER_ERROR, -				'invalid/output/include_nonexistent_file.html', -			), -		); -	} - -	/** -	* @dataProvider template_data -	*/ -	public function test_template($description, $file, $vars, $block_vars, $destroy, $expected) -	{ -		$cache_file = $this->template->cachepath . str_replace('/', '.', $file) . '.php'; - -		$this->assertFileNotExists($cache_file); - -		$expected = file_get_contents(dirname(__FILE__) . '/templates/' . $expected); -		// apparently the template engine does not put -		// the trailing newline into compiled templates -		$expected = trim($expected); -		$this->run_template($file, $vars, $block_vars, $destroy, $expected, $cache_file); -	} - -	/** -	* @dataProvider template_data_error -	*/ -	public function test_template_error($description, $file, $vars, $block_vars, $destroy, $error, $expected) -	{ -		$cache_file = $this->template->cachepath . str_replace('/', '.', $file) . '.php'; - -		$this->assertFileNotExists($cache_file); - -		$expected = file_get_contents(dirname(__FILE__) . '/templates/' . $expected); -		// apparently the template engine does not put -		// the trailing newline into compiled templates -		$expected = trim($expected); -		$this->setExpectedTriggerError($error, $expected); -		$this->run_template($file, $vars, $block_vars, $destroy, '', $cache_file); -	} -} diff --git a/tests/template/renderer_eval_test.php b/tests/template/renderer_eval_test.php deleted file mode 100644 index 7ebb8b9bda..0000000000 --- a/tests/template/renderer_eval_test.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php -/** -* -* @package testing -* @copyright (c) 2011 phpBB Group -* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 -* -*/ - -class phpbb_template_renderer_eval_test extends phpbb_test_case -{ -	public function test_eval() -	{ -		$compiled_code = '<a href="<?php echo \'Test\'; ?>">'; -		$valid_code = '<a href="Test">'; -		$context = new phpbb_template_context(); -		$template = new phpbb_template_renderer_eval($compiled_code, NULL); -		ob_start(); -		try -		{ -			$template->render($context, array()); -		} -		catch (Exception $exception) -		{ -			ob_end_clean(); -			throw $exception; -		} -		$output = ob_get_clean(); -		$this->assertEquals($valid_code, $output); -	} -} diff --git a/tests/template/subdir/includephp_from_subdir_test.php b/tests/template/subdir/includephp_from_subdir_test.php index 517cb85a30..6f9bc1efa6 100644 --- a/tests/template/subdir/includephp_from_subdir_test.php +++ b/tests/template/subdir/includephp_from_subdir_test.php @@ -19,9 +19,7 @@ class phpbb_template_subdir_includephp_from_subdir_test extends phpbb_template_t  	{  		$this->setup_engine(array('tpl_allow_php' => true)); -		$cache_file = $this->template->cachepath . 'includephp_relative.html.php'; - -		$this->run_template('includephp_relative.html', array(), array(), array(), "Path is relative to board root.\ntesting included php", $cache_file); +		$this->run_template('includephp_relative.html', array(), array(), array(), "Path is relative to board root.\ntesting included php");  		$this->template->set_filenames(array('test' => 'includephp_relative.html'));  		$this->assertEquals("Path is relative to board root.\ntesting included php", $this->display('test'), "Testing INCLUDEPHP"); diff --git a/tests/template/template_compile_test.php b/tests/template/template_compile_test.php deleted file mode 100644 index 7393fc1747..0000000000 --- a/tests/template/template_compile_test.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php -/** -* -* @package testing -* @copyright (c) 2011 phpBB Group -* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 -* -*/ - -require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php'; - -class phpbb_template_template_compile_test extends phpbb_test_case -{ -	private $template_compile; -	private $template_path; - -	protected function setUp() -	{ -		$this->template_compile = new phpbb_template_compile(false, null, $this->style_resource_locator, ''); -		$this->template_path = dirname(__FILE__) . '/templates'; -	} - -	public function test_in_phpbb() -	{ -		$output = $this->template_compile->compile_file($this->template_path . '/trivial.html'); -		$this->assertTrue(strlen($output) > 0); -		$statements = explode(';', $output); -		$first_statement = $statements[0]; -		$this->assertTrue(!!preg_match('#if.*defined.*IN_PHPBB.*exit#', $first_statement)); -	} -} diff --git a/tests/template/template_events_test.php b/tests/template/template_events_test.php index 0ac50c7f2b..f7bcd2dcc6 100644 --- a/tests/template/template_events_test.php +++ b/tests/template/template_events_test.php @@ -54,11 +54,9 @@ class phpbb_template_template_events_test extends phpbb_template_template_test_c  				array(),  				array(),  				array(), -'Kappa test event in all -Omega test event in all -Zeta test event in all -Kappa test event in silver -Omega test event in silver', +'Kappa test event in silver +Omega test event in silver +Zeta test event in all',  			),  			array(  				'Template event with inheritance - child', @@ -68,10 +66,9 @@ Omega test event in silver',  				array(),  				array(),  				array(), -'Kappa test event in all -Omega test event in all -Zeta test event in all -Kappa test event in silver_inherit', +'Kappa test event in silver_inherit +Omega test event in silver +Zeta test event in all',  			),  			array(  				'Definition in parent style', @@ -95,8 +92,7 @@ Kappa test event in silver_inherit',  		$this->setup_engine_for_events($dataset, $style_names);  		// Run test -		$cache_file = $this->template->cachepath . str_replace('/', '.', $file) . '.php'; -		$this->run_template($file, $vars, $block_vars, $destroy, $expected, $cache_file); +		$this->run_template($file, $vars, $block_vars, $destroy, $expected);  	}  	protected function setup_engine_for_events($dataset, $style_names, array $new_config = array()) @@ -111,7 +107,7 @@ Kappa test event in silver_inherit',  		$this->extension_manager = new phpbb_mock_filesystem_extension_manager(  			dirname(__FILE__) . "/datasets/$dataset/"  		); -		$this->template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $this->style_resource_locator, new phpbb_template_context, $this->extension_manager); +		$this->template = new phpbb_template_twig($phpbb_root_path, $phpEx, $config, $user, new phpbb_template_context, $this->extension_manager);  		$this->style_provider = new phpbb_style_path_provider();  		$this->style = new phpbb_style($phpbb_root_path, $phpEx, $config, $user, $this->style_resource_locator, $this->style_provider, $this->template);  		$this->style->set_custom_style('silver', array($this->template_path), $style_names, ''); diff --git a/tests/template/template_includejs_test.php b/tests/template/template_includejs_test.php index 22b020208b..ea5c30891b 100644 --- a/tests/template/template_includejs_test.php +++ b/tests/template/template_includejs_test.php @@ -11,23 +11,97 @@ require_once dirname(__FILE__) . '/template_test_case_with_tree.php';  class phpbb_template_template_includejs_test extends phpbb_template_template_test_case_with_tree  { -	public function test_includejs_compilation() +	public function template_data() +	{ +		return array( +			/* +			array( +				// vars +				// expected +			), +			*/ +			array( +				array('TEST' => 1), +				'<script type="text/javascript" src="' . $this->test_path . '/templates/parent_and_child.js?assets_version=1"></script>', +			), +			array( +				array('TEST' => 2), +				'<script type="text/javascript" src="' . $this->test_path . '/templates/parent_and_child.js?assets_version=0&assets_version=1"></script>', +			), +			array( +				array('TEST' => 3), +			'<script type="text/javascript" src="' . $this->test_path . '/templates/parent_and_child.js?test=1&assets_version=0&assets_version=1"></script>', +			), +			array( +				array('TEST' => 4), +				'<script type="text/javascript" src="' . $this->test_path . '/templates/parent_and_child.js?test=1&assets_version=0&assets_version=1"></script>', +			), +			array( +				array('TEST' => 5), +				'<script type="text/javascript" src="' . $this->test_path . '/templates/parent_and_child.js?test=1;assets_version=0&assets_version=1"></script>', +			), +			array( +				array('TEST' => 6), +				'<script type="text/javascript" src="' . $this->test_path . '/parent_templates/parent_only.js?assets_version=1"></script>', +			), +			array( +				array('TEST' => 7), +				'<script type="text/javascript" src="' . $this->test_path . '/templates/child_only.js?assets_version=1"></script>', +			), +			array( +				array('TEST' => 8), +				'<script type="text/javascript" src="' . $this->test_path . '/templates/subdir/parent_only.js?assets_version=1"></script>', +			), +			array( +				array('TEST' => 9), +				'<script type="text/javascript" src="' . $this->test_path . '/templates/subdir/subsubdir/parent_only.js?assets_version=1"></script>', +			), +			array( +				array('TEST' => 10), +				'<script type="text/javascript" src="' . $this->test_path . '/templates/subdir/parent_only.js?assets_version=1"></script>', +			), +			array( +				array('TEST' => 11), +				'<script type="text/javascript" src="' . $this->test_path . '/templates/child_only.js?test1=1&test2=2&assets_version=1#test3"></script>', +			), +			array( +				array('TEST' => 12), +				'<script type="text/javascript" src="' . $this->test_path . '/parent_templates/parent_only.js?test1=1&test2=2&assets_version=1#test3"></script>', +			), +			array( +				array('TEST' => 13), +				'<script type="text/javascript" src="' . $this->test_path . '/parent_templates/parent_only.js?test1=1;test2=2&assets_version=1#test3"></script>', +			), +			array( +				array('TEST' => 14), +				'<script type="text/javascript" src="' . $this->test_path . '/parent_templates/parent_only.js?test1="&assets_version=1#test3"></script>', +			), +			array( +				array('TEST' => 15), +				'<script type="text/javascript" src="http://phpbb.com/b.js?c=d#f"></script>', +			), +			array( +				array('TEST' => 16), +				'<script type="text/javascript" src="http://phpbb.com/b.js?c=d&assets_version=1#f"></script>', +			), +			array( +				array('TEST' => 17), +				'<script type="text/javascript" src="//phpbb.com/b.js"></script>', +			), +		); +	} + +	/** +	* @dataProvider template_data +	*/ +	public function test_includejs_compilation($vars, $expected)  	{  		// Reset the engine state  		$this->setup_engine(array('assets_version' => 1)); -		// Prepare correct result -		$scripts = array( -			'<script src="' . $this->test_path . '/templates/parent_and_child.js?assets_version=1"></script>', -			'<script src="' . $this->test_path . '/parent_templates/parent_only.js?assets_version=1"></script>', -			'<script src="' . $this->test_path . '/templates/child_only.js?assets_version=1"></script>', -			'<script src="' . $this->test_path . '/templates/subdir/parent_only.js?assets_version=1"></script>', -			'<script src="' . $this->test_path . '/templates/subdir/subsubdir/parent_only.js?assets_version=1"></script>', -			'<script src="' . $this->test_path . '/templates/subdir/parent_only.js?assets_version=1"></script>', -		); +		$this->template->assign_vars($vars);  		// Run test -		$cache_file = $this->template->cachepath . 'includejs.html.php'; -		$this->run_template('includejs.html', array('PARENT' => 'parent_only.js', 'SUBDIR' => 'subdir', 'EXT' => 'js'), array(), array(), implode('', $scripts), $cache_file); +		$this->run_template('includejs.html', array_merge(array('PARENT' => 'parent_only.js', 'SUBDIR' => 'subdir', 'EXT' => 'js'), $vars), array(), array(), $expected);  	}  } diff --git a/tests/template/template_inheritance_test.php b/tests/template/template_inheritance_test.php index febfed9ef0..cc71ff99e0 100644 --- a/tests/template/template_inheritance_test.php +++ b/tests/template/template_inheritance_test.php @@ -50,15 +50,6 @@ class phpbb_template_template_inheritance_test extends phpbb_template_template_t  	*/  	public function test_template($name, $file, array $vars, array $block_vars, array $destroy, $expected)  	{ -		$cache_file = $this->template->cachepath . str_replace('/', '.', $file) . '.php'; - -		$this->assertFileNotExists($cache_file); - -		$this->run_template($file, $vars, $block_vars, $destroy, $expected, $cache_file); - -		// Reset the engine state -		$this->setup_engine(); - -		$this->run_template($file, $vars, $block_vars, $destroy, $expected, $cache_file); +		$this->run_template($file, $vars, $block_vars, $destroy, $expected);  	}  } diff --git a/tests/template/template_locate_test.php b/tests/template/template_locate_test.php deleted file mode 100644 index 851dcae8ea..0000000000 --- a/tests/template/template_locate_test.php +++ /dev/null @@ -1,68 +0,0 @@ -<?php -/** -* -* @package testing -* @copyright (c) 2011 phpBB Group -* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 -* -*/ - -require_once dirname(__FILE__) . '/template_test_case_with_tree.php'; - -class phpbb_template_template_locate_test extends phpbb_template_template_test_case_with_tree -{ -	public function template_data() -	{ -		return array( -			// First element of the array is test name - keep them distinct -			array( -				'simple inheritance - only parent template exists', -				$this->test_path . '/parent_templates/parent_only.html', -				'parent_only.html', -				false, -				true, -			), -			array( -				'simple inheritance - only child template exists', -				$this->test_path . '/templates/child_only.html', -				'child_only.html', -				false, -				true, -			), -			array( -				'simple inheritance - both parent and child templates exist', -				$this->test_path . '/templates/parent_and_child.html', -				'parent_and_child.html', -				false, -				true, -			), -			array( -				'find first template - only child template exists in main style', -				'child_only.html', -				array('parent_only.html', 'child_only.html'), -				false, -				false, -			), -			array( -				'find first template - both templates exist in main style', -				'parent_and_child.html', -				array('parent_and_child.html', 'child_only.html'), -				false, -				false, -			), -		); -	} - -	/** -	* @dataProvider template_data -	*/ -	public function test_template($name, $expected, $files, $return_default, $return_full_path) -	{ -		// Reset the engine state -		$this->setup_engine(); - -		// Locate template -		$result = $this->style_resource_locator->get_first_template_location($files, $return_default, $return_full_path); -		$this->assertSame($expected, $result); -	} -} diff --git a/tests/template/template_spacing_test.php b/tests/template/template_spacing_test.php deleted file mode 100644 index 83f8711b38..0000000000 --- a/tests/template/template_spacing_test.php +++ /dev/null @@ -1,87 +0,0 @@ -<?php -/** -* -* @package testing -* @copyright (c) 2013 phpBB Group -* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 -* -*/ - -require_once dirname(__FILE__) . '/template_test_case.php'; - -class phpbb_template_template_spacing_test extends phpbb_template_template_test_case -{ -	public function template_data() -	{ -		return array( -			/* -			array( -				'', // Description -				'', // dataset -				array(), // style names -				'', // file -				array(), // vars -				array(), // block vars -				array(), // destroy -				'', // expected result -			), -			*/ -			array( -				'Spacing in templates', -				'ext_trivial', -				array(), -				'variable_spacing.html', -				array( -					'VARIABLE'	=> '{}', -				), -				array(), -				array(), -				'|{}| -{}|{}| -|{} -<div class="{}">test</div>', -			), -		); -	} - -	/** -	* @dataProvider template_data -	*/ -	public function test_template($desc, $dataset, $style_names, $file, array $vars, array $block_vars, array $destroy, $expected) -	{ -		// Run test -		$cache_file = $this->template->cachepath . str_replace('/', '.', $file) . '.php'; -		$this->run_template($file, $vars, $block_vars, $destroy, $expected, $cache_file); -	} - -	/** -	* @dataProvider template_data -	*/ -	public function test_event($desc, $dataset, $style_names, $file, array $vars, array $block_vars, array $destroy, $expected) -	{ -		// Reset the engine state -		$this->setup_engine_for_events($dataset, $style_names); - -		// Run test -		$cache_file = $this->template->cachepath . str_replace('/', '.', $file) . '.php'; -		$this->run_template($file, $vars, $block_vars, $destroy, $expected, $cache_file); -	} - -	protected function setup_engine_for_events($dataset, $style_names, array $new_config = array()) -	{ -		global $phpbb_root_path, $phpEx, $user; - -		$defaults = $this->config_defaults(); -		$config = new phpbb_config(array_merge($defaults, $new_config)); - -		$this->template_path = dirname(__FILE__) . "/datasets/$dataset/styles/silver/template"; -		$this->style_resource_locator = new phpbb_style_resource_locator(); -		$this->extension_manager = new phpbb_mock_filesystem_extension_manager( -			dirname(__FILE__) . "/datasets/$dataset/" -		); -		$this->template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $this->style_resource_locator, new phpbb_template_context, $this->extension_manager); -		$this->style_provider = new phpbb_style_path_provider(); -		$this->style = new phpbb_style($phpbb_root_path, $phpEx, $config, $user, $this->style_resource_locator, $this->style_provider, $this->template); -		$this->style->set_custom_style('silver', array($this->template_path), $style_names, ''); -	} -} diff --git a/tests/template/template_test.php b/tests/template/template_test.php index a3c0b69123..fedfeba33a 100644 --- a/tests/template/template_test.php +++ b/tests/template/template_test.php @@ -46,28 +46,42 @@ class phpbb_template_template_test extends phpbb_template_template_test_case  				array(),  				array(),  				array(), -				'03', +				'03!false',  			),  			array(  				'if.html',  				array('S_VALUE' => true),  				array(),  				array(), -				'1', +				'1!false',  			),  			array(  				'if.html',  				array('S_VALUE' => true, 'S_OTHER_VALUE' => true),  				array(),  				array(), -				'1', +				'1!false',  			),  			array(  				'if.html',  				array('S_VALUE' => false, 'S_OTHER_VALUE' => true),  				array(),  				array(), -				'2', +				'2!false', +			), +			array( +				'if.html', +				array('S_TEST' => false), +				array(), +				array(), +				'03false', +			), +			array( +				'if.html', +				array('S_TEST' => 0), +				array(), +				array(), +				'03!false',  			),  			array(  				'loop.html', @@ -116,7 +130,7 @@ class phpbb_template_template_test extends phpbb_template_template_test_case  				array(),  				array('loop' => array(array('VARIABLE' => 'x'), array('VARIABLE' => 'y')), 'loop.inner' => array(array(), array())),  				array(), -				"first\n0 - a\nx - b\nset\n1 - a\ny - b\nset\nlast\n0 - c\n1 - c\nlast inner\ninner loop", +				"first\n0 - a\nx - b\nset\n1 - a\ny - b\nset\nlast\n0 - c\n1 - c\nlast inner",  			),  			array(  				'loop_advanced.html', @@ -126,6 +140,13 @@ class phpbb_template_template_test extends phpbb_template_template_test_case  				"101234561\nx\n101234561\nx\n101234561\nx\n1234561\nx\n1\nx\n101\nx\n234\nx\n10\nx\n561\nx\n561",  			),  			array( +				'loop_nested2.html', +				array(), +				array('outer' => array(array(), array()), 'outer.middle' => array(array(), array())), +				array(), +				"o0o1m01m11", +			), +			array(  				'define.html',  				array(),  				array('loop' => array(array(), array(), array(), array(), array(), array(), array()), 'test' => array(array()), 'test.deep' => array(array()), 'test.deep.defines' => array(array())), @@ -140,13 +161,6 @@ class phpbb_template_template_test extends phpbb_template_template_test_case  				"abc\nzxc\ncde\nbcd",  			),  			array( -				'define_unclosed.html', -				array(), -				array(), -				array(), -				"test", -			), -			array(  				'expressions.html',  				array(),  				array(), @@ -247,21 +261,15 @@ class phpbb_template_template_test extends phpbb_template_template_test_case  				array(),  				array(),  				array(), -				"{ VARIABLE }\n{ 1_VARIABLE }\n{ VARIABLE }\n{ 1_VARIABLE }", +				"VARIABLE\n1_VARIABLE\nVARIABLE\n1_VARIABLE",  			),  			array(  				'lang.html', -				array('L_VARIABLE' => "Value'", 'L_1_VARIABLE' => "1 O'Clock"),  				array(),  				array(), -				"Value'\n1 O'Clock\nValue\'\n1 O\'Clock", -			), -			array( -				'lang.html', -				array('LA_VARIABLE' => "Value'", 'LA_1_VARIABLE' => "1 O'Clock"), -				array(),  				array(), -				"{ VARIABLE }\n{ 1_VARIABLE }\nValue'\n1 O'Clock", +				"Value'\n1 O'Clock\nValue\'\n1 O\'Clock", +				array('VARIABLE' => "Value'", '1_VARIABLE' => "1 O'Clock"),  			),  			array(  				'loop_nested_multilevel_ref.html', @@ -275,7 +283,6 @@ class phpbb_template_template_test extends phpbb_template_template_test_case  				array(),  				array('outer' => array(array('VARIABLE' => 'x'), array('VARIABLE' => 'y')), 'outer.inner' => array(array('VARIABLE' => 'z'), array('VARIABLE' => 'zz'))),  				array(), -				// I don't completely understand this output, hopefully it's correct  				"top-level content\nouter x\nouter y\ninner z\nfirst row\n\ninner zz",  			),  			array( @@ -283,7 +290,6 @@ class phpbb_template_template_test extends phpbb_template_template_test_case  				array(),  				array('outer' => array(array()), 'outer.middle' => array(array()), 'outer.middle.inner' => array(array('VARIABLE' => 'z'), array('VARIABLE' => 'zz'))),  				array(), -				// I don't completely understand this output, hopefully it's correct  				"top-level content\nouter\nmiddle\ninner z\nfirst row of 2 in inner\n\ninner zz",  			),  			array( @@ -303,6 +309,13 @@ class phpbb_template_template_test extends phpbb_template_template_test_case  				"a\nb\nc\nd",  			),  			*/ +			array( +				'twig.html', +				array('VARIABLE' => 'FOObar',), +				array(), +				array(), +				"13FOOBAR|foobar", +			),  		);  	} @@ -313,24 +326,15 @@ class phpbb_template_template_test extends phpbb_template_template_test_case  		$this->template->set_filenames(array('test' => $filename));  		$this->assertFileNotExists($this->template_path . '/' . $filename, 'Testing missing file, file cannot exist'); -		$expecting = sprintf('style resource locator: File for handle test does not exist. Could not find: %s', $this->test_path . '/templates/' . $filename); -		$this->setExpectedTriggerError(E_USER_ERROR, $expecting); +		$this->setExpectedException('Twig_Error_Loader');  		$this->display('test');  	} -	public function test_empty_file() -	{ -		$expecting = 'style resource locator: set_filenames: Empty filename specified for test'; - -		$this->setExpectedTriggerError(E_USER_ERROR, $expecting); -		$this->template->set_filenames(array('test' => '')); -	}  	public function test_invalid_handle()  	{ -		$expecting = 'No file specified for handle test'; -		$this->setExpectedTriggerError(E_USER_ERROR, $expecting); +		$this->setExpectedException('Twig_Error_Loader');  		$this->display('test');  	} @@ -338,49 +342,23 @@ class phpbb_template_template_test extends phpbb_template_template_test_case  	/**  	* @dataProvider template_data  	*/ -	public function test_template($file, array $vars, array $block_vars, array $destroy, $expected) +	public function test_template($file, array $vars, array $block_vars, array $destroy, $expected, $lang_vars = array())  	{ -		$cache_file = $this->template->cachepath . str_replace('/', '.', $file) . '.php'; - -		$this->assertFileNotExists($cache_file); - -		$this->run_template($file, $vars, $block_vars, $destroy, $expected, $cache_file); - -		// Reset the engine state -		$this->setup_engine(); - -		$this->run_template($file, $vars, $block_vars, $destroy, $expected, $cache_file); +		$this->run_template($file, $vars, $block_vars, $destroy, $expected, $lang_vars);  	} -	/** -	* @dataProvider template_data -	*/ -	public function test_assign_display($file, array $vars, array $block_vars, array $destroy, $expected) +	public function test_assign_display()  	{ +		$this->run_template('basic.html', array(), array(), array(), "pass\npass\npass\n<!-- DUMMY var -->"); +  		$this->template->set_filenames(array( -			'test' => $file, -			'container' => 'variable.html', +			'test'		=> 'basic.html', +			'container'	=> 'variable.html',  		)); -		$this->template->assign_vars($vars); - -		foreach ($block_vars as $block => $loops) -		{ -			foreach ($loops as $_vars) -			{ -				$this->template->assign_block_vars($block, $_vars); -			} -		} - -		foreach ($destroy as $block) -		{ -			$this->template->destroy_block_vars($block); -		} - -		$this->assertEquals($expected, self::trim_template_result($this->template->assign_display('test')), "Testing assign_display($file)");  		$this->template->assign_display('test', 'VARIABLE', false); -		$this->assertEquals($expected, $this->display('container'), "Testing assign_display($file)"); +		$this->assertEquals("pass\npass\npass\n<!-- DUMMY var -->", $this->display('container'), "Testing assign_display($file)");  	}  	public function test_append_var_without_assign_var() @@ -391,7 +369,7 @@ class phpbb_template_template_test extends phpbb_template_template_test_case  		$items = array('This ', 'is ', 'a ', 'test');  		$expecting = implode('', $items); -		 +  		foreach ($items as $word)  		{  			$this->template->append_var('VARIABLE', $word); @@ -409,7 +387,7 @@ class phpbb_template_template_test extends phpbb_template_template_test_case  		$start = 'This ';  		$items = array('is ', 'a ', 'test');  		$expecting = $start . implode('', $items); -		 +  		$this->template->assign_var('VARIABLE', $start);  		foreach ($items as $word)  		{ @@ -421,13 +399,20 @@ class phpbb_template_template_test extends phpbb_template_template_test_case  	public function test_php()  	{ -		$this->setup_engine(array('tpl_allow_php' => true)); +		global $phpbb_root_path; -		$cache_file = $this->template->cachepath . 'php.html.php'; +		$template_text = '<!-- PHP -->echo "test";<!-- ENDPHP -->'; -		$this->assertFileNotExists($cache_file); +		$cache_dir = dirname($phpbb_root_path . 'cache') . '/'; +		$fp = fopen($cache_dir . 'php.html', 'w'); +		fputs($fp, $template_text); +		fclose($fp); -		$this->run_template('php.html', array(), array(), array(), 'test', $cache_file); +		$this->setup_engine(array('tpl_allow_php' => true)); + +		$this->style->set_custom_style('tests', $cache_dir, array(), ''); + +		$this->run_template('php.html', array(), array(), array(), 'test');  	}  	public function alter_block_array_data() @@ -533,10 +518,40 @@ EOT  		$this->template->assign_block_vars('outer.middle', array());  		$this->template->assign_block_vars('outer.middle', array()); -		$this->assertEquals("outer - 0\nmiddle - 0\nmiddle - 1\nouter - 1\nmiddle - 0\nmiddle - 1\nouter - 2\nmiddle - 0\nmiddle - 1", $this->display('test'), 'Ensuring template is built correctly before modification'); +		$this->assertEquals("outer - 0middle - 0middle - 1outer - 1middle - 0middle - 1outer - 2middle - 0middle - 1", $this->display('test'), 'Ensuring template is built correctly before modification');  		$this->template->alter_block_array($alter_block, $vararray, $key, $mode); -		$this->assertEquals($expect, $this->display('test'), $description); +		$this->assertEquals(str_replace(array("\n", "\r", "\t"), '', $expect), str_replace(array("\n", "\r", "\t"), '', $this->display('test')), $description);  	} +	public function test_more_alter_block_array() +	{ +		$this->template->set_filenames(array('test' => 'loop_nested.html')); + +		$this->template->assign_var('TEST_MORE', true); + +		// @todo Change this +		$this->template->assign_block_vars('outer', array()); +		$this->template->assign_block_vars('outer.middle', array()); +		$this->template->assign_block_vars('outer', array()); +		$this->template->assign_block_vars('outer.middle', array()); +		$this->template->assign_block_vars('outer.middle', array()); +		$this->template->assign_block_vars('outer', array()); +		$this->template->assign_block_vars('outer.middle', array()); +		$this->template->assign_block_vars('outer.middle', array()); +		$this->template->assign_block_vars('outer.middle', array()); + +		$expect = 'outer - 0[outer|3]middle - 0[middle|1]outer - 1[outer|3]middle - 0[middle|2]middle - 1[middle|2]outer - 2[outer|3]middle - 0[middle|3]middle - 1[middle|3]middle - 2[middle|3]'; +		$this->assertEquals($expect, str_replace(array("\n", "\r", "\t"), '', $this->display('test')), 'Ensuring template is built correctly before modification'); + +		$this->template->alter_block_array('outer', array()); + +		$expect = 'outer - 0[outer|4]outer - 1[outer|4]middle - 0[middle|1]outer - 2[outer|4]middle - 0[middle|2]middle - 1[middle|2]outer - 3[outer|4]middle - 0[middle|3]middle - 1[middle|3]middle - 2[middle|3]'; +		$this->assertEquals($expect, str_replace(array("\n", "\r", "\t"), '', $this->display('test')), 'Ensuring S_NUM_ROWS is correct after insertion'); + +		$this->template->alter_block_array('outer', array('VARIABLE' => 'test'), 2, 'change'); + +		$expect = 'outer - 0[outer|4]outer - 1[outer|4]middle - 0[middle|1]outer - 2 - test[outer|4]middle - 0[middle|2]middle - 1[middle|2]outer - 3[outer|4]middle - 0[middle|3]middle - 1[middle|3]middle - 2[middle|3]'; +		$this->assertEquals($expect, str_replace(array("\n", "\r", "\t"), '', $this->display('test')), 'Ensuring S_NUM_ROWS is correct after modification'); +	}  } diff --git a/tests/template/template_test_case.php b/tests/template/template_test_case.php index 5d38ebed67..3e2cd5a387 100644 --- a/tests/template/template_test_case.php +++ b/tests/template/template_test_case.php @@ -16,6 +16,7 @@ class phpbb_template_template_test_case extends phpbb_test_case  	protected $template_path;  	protected $style_resource_locator;  	protected $style_provider; +	protected $user;  	protected $test_path = 'tests/template'; @@ -28,7 +29,7 @@ class phpbb_template_template_test_case extends phpbb_test_case  		try  		{ -			$this->assertTrue($this->template->display($handle, false)); +			$this->template->display($handle, false);  		}  		catch (Exception $exception)  		{ @@ -59,16 +60,17 @@ class phpbb_template_template_test_case extends phpbb_test_case  	protected function setup_engine(array $new_config = array())  	{ -		global $phpbb_root_path, $phpEx, $user; +		global $phpbb_root_path, $phpEx;  		$defaults = $this->config_defaults();  		$config = new phpbb_config(array_merge($defaults, $new_config)); +		$this->user = new phpbb_user;  		$this->template_path = $this->test_path . '/templates';  		$this->style_resource_locator = new phpbb_style_resource_locator();  		$this->style_provider = new phpbb_style_path_provider(); -		$this->template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $this->style_resource_locator, new phpbb_template_context()); -		$this->style = new phpbb_style($phpbb_root_path, $phpEx, $config, $user, $this->style_resource_locator, $this->style_provider, $this->template); +		$this->template = new phpbb_template_twig($phpbb_root_path, $phpEx, $config, $this->user, new phpbb_template_context()); +		$this->style = new phpbb_style($phpbb_root_path, $phpEx, $config, $this->user, $this->style_resource_locator, $this->style_provider, $this->template);  		$this->style->set_custom_style('tests', $this->template_path, array(), '');  	} @@ -77,43 +79,15 @@ class phpbb_template_template_test_case extends phpbb_test_case  		// Test the engine can be used  		$this->setup_engine(); -		$template_cache_dir = dirname($this->template->cachepath); -		if (!is_writable($template_cache_dir)) -		{ -			$this->markTestSkipped("Template cache directory ({$template_cache_dir}) is not writable."); -		} - -		$file_array = scandir($template_cache_dir); -		$file_prefix = basename($this->template->cachepath); -		foreach ($file_array as $file) -		{ -			if (strpos($file, $file_prefix) === 0) -			{ -				unlink($template_cache_dir . '/' . $file); -			} -		} - -		$this->setup_engine(); +		$this->template->clear_cache();  	}  	protected function tearDown()  	{ -		if (is_object($this->template)) -		{ -			$template_cache_dir = dirname($this->template->cachepath); -			$file_array = scandir($template_cache_dir); -			$file_prefix = basename($this->template->cachepath); -			foreach ($file_array as $file) -			{ -				if (strpos($file, $file_prefix) === 0) -				{ -					unlink($template_cache_dir . '/' . $file); -				} -			} -		} +		$this->template->clear_cache();  	} -	protected function run_template($file, array $vars, array $block_vars, array $destroy, $expected, $cache_file) +	protected function run_template($file, array $vars, array $block_vars, array $destroy, $expected, $lang_vars = array())  	{  		$this->template->set_filenames(array('test' => $file));  		$this->template->assign_vars($vars); @@ -131,25 +105,17 @@ class phpbb_template_template_test_case extends phpbb_test_case  			$this->template->destroy_block_vars($block);  		} -		try +		// Previous functionality was $cachefile (string), which was removed, check to prevent errors +		if (is_array($lang_vars))  		{ -			$this->assertEquals($expected, $this->display('test'), "Testing $file"); -			$this->assertFileExists($cache_file); -		} -		catch (ErrorException $e) -		{ -			if (file_exists($cache_file)) +			foreach ($lang_vars as $name => $value)  			{ -				copy($cache_file, str_replace('ctpl_', 'tests_ctpl_', $cache_file)); +				$this->user->lang[$name] = $value;  			} -			throw $e;  		} -		// For debugging. -		// When testing eval path the cache file may not exist. -		if (self::PRESERVE_CACHE && file_exists($cache_file)) -		{ -			copy($cache_file, str_replace('ctpl_', 'tests_ctpl_', $cache_file)); -		} +		$expected = str_replace(array("\n", "\r", "\t"), '', $expected); +		$output = str_replace(array("\n", "\r", "\t"), '', $this->display('test')); +		$this->assertEquals($expected, $output, "Testing $file");  	}  } diff --git a/tests/template/template_test_case_with_tree.php b/tests/template/template_test_case_with_tree.php index 7585be5728..4b8cbada45 100644 --- a/tests/template/template_test_case_with_tree.php +++ b/tests/template/template_test_case_with_tree.php @@ -22,7 +22,7 @@ class phpbb_template_template_test_case_with_tree extends phpbb_template_templat  		$this->parent_template_path = $this->test_path . '/parent_templates';  		$this->style_resource_locator = new phpbb_style_resource_locator();  		$this->style_provider = new phpbb_style_path_provider(); -		$this->template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $this->style_resource_locator, new phpbb_template_context()); +		$this->template = new phpbb_template_twig($phpbb_root_path, $phpEx, $config, $user, new phpbb_template_context());  		$this->style = new phpbb_style($phpbb_root_path, $phpEx, $config, $user, $this->style_resource_locator, $this->style_provider, $this->template);  		$this->style->set_custom_style('tests', array($this->template_path, $this->parent_template_path), array(), '');  	} diff --git a/tests/template/templates/define_unclosed.html b/tests/template/templates/define_unclosed.html deleted file mode 100644 index 1c975eab2b..0000000000 --- a/tests/template/templates/define_unclosed.html +++ /dev/null @@ -1,2 +0,0 @@ -<!-- DEFINE $VALUE --> -test diff --git a/tests/template/templates/if.html b/tests/template/templates/if.html index eed431019e..c010aff7fa 100644 --- a/tests/template/templates/if.html +++ b/tests/template/templates/if.html @@ -9,3 +9,11 @@  <!-- IF S_VALUE and S_OTHER_VALUE and (S_VALUE > S_OTHER_VALUE) -->  04  <!-- ENDIF --> + +<!-- IF S_TEST === false --> +false +<!-- ENDIF --> + +<!-- IF S_TEST !== false --> +!false +<!-- ENDIF --> diff --git a/tests/template/templates/includejs.html b/tests/template/templates/includejs.html index ef73700eeb..3bcad76af5 100644 --- a/tests/template/templates/includejs.html +++ b/tests/template/templates/includejs.html @@ -1,8 +1,38 @@ -<!-- INCLUDEJS parent_and_child.js --> -<!-- INCLUDEJS {PARENT} --> -<!-- DEFINE $TEST = 'child_only.js' --> -<!-- INCLUDEJS {$TEST} --> -<!-- INCLUDEJS subdir/{PARENT} --> -<!-- INCLUDEJS {SUBDIR}/subsubdir/{PARENT} --> -<!-- INCLUDEJS {SUBDIR}/parent_only.{EXT} --> -{SCRIPTS} +<!-- IF TEST === 1 --> +	<!-- INCLUDEJS parent_and_child.js --> +<!-- ELSEIF TEST === 2 --> +	<!-- INCLUDEJS parent_and_child.js?assets_version=0 --> +<!-- ELSEIF TEST === 3 --> +	<!-- INCLUDEJS parent_and_child.js?test=1&assets_version=0 --> +<!-- ELSEIF TEST === 4 --> +	<!-- INCLUDEJS parent_and_child.js?test=1&assets_version=0 --> +<!-- ELSEIF TEST === 5 --> +	<!-- INCLUDEJS parent_and_child.js?test=1;assets_version=0 --> +<!-- ELSEIF TEST === 6 --> +	<!-- INCLUDEJS {PARENT} --> +<!-- ELSEIF TEST === 7 --> +	<!-- DEFINE $TEST = 'child_only.js' --> +	<!-- INCLUDEJS {$TEST} --> +<!-- ELSEIF TEST === 8 --> +	<!-- INCLUDEJS subdir/{PARENT} --> +<!-- ELSEIF TEST === 9 --> +	<!-- INCLUDEJS {SUBDIR}/subsubdir/{PARENT} --> +<!-- ELSEIF TEST === 10 --> +	<!-- INCLUDEJS {SUBDIR}/parent_only.{EXT} --> +<!-- ELSEIF TEST === 11 --> +	<!-- DEFINE $TEST = 'child_only.js?test1=1&test2=2#test3' --> +	<!-- INCLUDEJS {$TEST} --> +<!-- ELSEIF TEST === 12 --> +	<!-- INCLUDEJS parent_only.js?test1=1&test2=2#test3 --> +<!-- ELSEIF TEST === 13 --> +	<!-- INCLUDEJS parent_only.js?test1=1;test2=2#test3 --> +<!-- ELSEIF TEST === 14 --> +	<!-- INCLUDEJS parent_only.js?test1="#test3 --> +<!-- ELSEIF TEST === 15 --> +	<!-- INCLUDEJS http://phpbb.com/b.js?c=d#f --> +<!-- ELSEIF TEST === 16 --> +	<!-- INCLUDEJS http://phpbb.com/b.js?c=d&assets_version=1#f --> +<!-- ELSEIF TEST === 17 --> +	<!-- INCLUDEJS //phpbb.com/b.js --> +<!-- ENDIF --> +{$SCRIPTS} diff --git a/tests/template/templates/invalid/include_nonexistent_file.html b/tests/template/templates/invalid/include_nonexistent_file.html deleted file mode 100644 index 617d2fdaaa..0000000000 --- a/tests/template/templates/invalid/include_nonexistent_file.html +++ /dev/null @@ -1 +0,0 @@ -<!-- INCLUDE nonexistent.html --> diff --git a/tests/template/templates/invalid/unknown_tag.html b/tests/template/templates/invalid/unknown_tag.html deleted file mode 100644 index 1489e5e31a..0000000000 --- a/tests/template/templates/invalid/unknown_tag.html +++ /dev/null @@ -1 +0,0 @@ -<!-- UNKNOWNTAG variable.html --> diff --git a/tests/template/templates/loop_nested.html b/tests/template/templates/loop_nested.html index 45b1ef85d4..cf099ecc15 100644 --- a/tests/template/templates/loop_nested.html +++ b/tests/template/templates/loop_nested.html @@ -1,6 +1,6 @@  <!-- BEGIN outer --> -	outer - {outer.S_ROW_COUNT}<!-- IF outer.VARIABLE --> - {outer.VARIABLE}<!-- ENDIF --> -	<!-- BEGIN middle --> -		middle - {middle.S_ROW_COUNT}<!-- IF middle.VARIABLE --> - {middle.VARIABLE}<!-- ENDIF --> -	<!-- END middle --> +outer - {outer.S_ROW_COUNT}<!-- IF outer.VARIABLE --> - {outer.VARIABLE}<!-- ENDIF --><!-- IF TEST_MORE -->[{outer.S_BLOCK_NAME}|{outer.S_NUM_ROWS}]<!-- ENDIF --> +<!-- BEGIN middle --> +middle - {outer.middle.S_ROW_COUNT}<!-- IF outer.middle.VARIABLE --> - {outer.middle.VARIABLE}<!-- ENDIF --><!-- IF TEST_MORE -->[{outer.middle.S_BLOCK_NAME}|{outer.middle.S_NUM_ROWS}]<!-- ENDIF --> +<!-- END middle -->  <!-- END outer --> diff --git a/tests/template/templates/loop_nested2.html b/tests/template/templates/loop_nested2.html new file mode 100644 index 0000000000..3eeeb5e36a --- /dev/null +++ b/tests/template/templates/loop_nested2.html @@ -0,0 +1,6 @@ +<!-- BEGIN outer --> +o{outer.S_ROW_COUNT} +<!-- BEGIN middle --> +m{outer.middle.S_ROW_COUNT}{outer.S_ROW_COUNT} +<!-- END middle --> +<!-- END outer --> diff --git a/tests/template/templates/loop_size.html b/tests/template/templates/loop_size.html index f1938441df..8f581cef10 100644 --- a/tests/template/templates/loop_size.html +++ b/tests/template/templates/loop_size.html @@ -36,4 +36,4 @@  <!-- BEGIN loop -->  	in loop -<!-- END --> +<!-- END loop --> diff --git a/tests/template/templates/loop_underscore.html b/tests/template/templates/loop_underscore.html index dafce5dea6..4001007868 100644 --- a/tests/template/templates/loop_underscore.html +++ b/tests/template/templates/loop_underscore.html @@ -2,7 +2,7 @@  loop  <!-- BEGINELSE -->  noloop -<!-- END loop --> +<!-- END _underscore_loop -->  <!-- IF ._underscore_loop -->  loop diff --git a/tests/template/templates/loop_vars.html b/tests/template/templates/loop_vars.html index d94a0ae0f7..7d86d4b7b6 100644 --- a/tests/template/templates/loop_vars.html +++ b/tests/template/templates/loop_vars.html @@ -11,4 +11,3 @@ last  <!-- IF inner.S_LAST_ROW and inner.S_ROW_COUNT and inner.S_NUM_ROWS -->last inner<!-- ENDIF -->  <!-- END inner -->  <!-- END loop --> -<!-- IF .loop.inner -->inner loop<!-- ENDIF --> diff --git a/tests/template/templates/twig.html b/tests/template/templates/twig.html new file mode 100644 index 0000000000..17b94ad8d4 --- /dev/null +++ b/tests/template/templates/twig.html @@ -0,0 +1,6 @@ +<!-- EXTENDS "twig_parent.html" --> + +<!-- BLOCK overwritten --> +3{VARIABLE|upper}|{VARIABLE|lower} +<!-- ENDBLOCK --> + diff --git a/tests/template/templates/twig_parent.html b/tests/template/templates/twig_parent.html new file mode 100644 index 0000000000..af528e0da4 --- /dev/null +++ b/tests/template/templates/twig_parent.html @@ -0,0 +1,7 @@ +<!-- BLOCK notoverwritten --> +1 +<!-- ENDBLOCK --> + +<!-- BLOCK overwritten --> +2 +<!-- ENDBLOCK -->
\ No newline at end of file  | 
