Merge pull request #1470 from EXreaction/feature/twig

Feature/twig
This commit is contained in:
Nils Adermann 2013-07-11 12:11:41 -07:00
commit 59f34bef08
74 changed files with 2646 additions and 2639 deletions

1
.gitignore vendored
View file

@ -1,5 +1,6 @@
*~ *~
/phpunit.xml /phpunit.xml
/phpBB/cache/twig/*
/phpBB/cache/*.html /phpBB/cache/*.html
/phpBB/cache/*.php /phpBB/cache/*.php
/phpBB/cache/*.lock /phpBB/cache/*.lock

View file

@ -437,7 +437,7 @@
</div> </div>
<!-- ENDIF --> <!-- 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 --> <!-- IF .forums -->
<table cellspacing="1"> <table cellspacing="1">

View file

@ -6,7 +6,8 @@
"symfony/event-dispatcher": "2.1.*", "symfony/event-dispatcher": "2.1.*",
"symfony/http-kernel": "2.1.*", "symfony/http-kernel": "2.1.*",
"symfony/routing": "2.1.*", "symfony/routing": "2.1.*",
"symfony/yaml": "2.1.*" "symfony/yaml": "2.1.*",
"twig/twig": "1.13.*"
}, },
"require-dev": { "require-dev": {
"fabpot/goutte": "v0.1.0", "fabpot/goutte": "v0.1.0",

196
phpBB/composer.lock generated
View file

@ -3,21 +3,21 @@
"This file locks the dependencies of your project to a known state", "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" "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"
], ],
"hash": "3792dc25490f24210ece3b40789c5b98", "hash": "e4a4f4848a7201d7e044446001afda29",
"packages": [ "packages": [
{ {
"name": "symfony/config", "name": "symfony/config",
"version": "v2.1.10", "version": "v2.1.11",
"target-dir": "Symfony/Component/Config", "target-dir": "Symfony/Component/Config",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/Config.git", "url": "https://github.com/symfony/Config.git",
"reference": "v2.1.10" "reference": "v2.1.11"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/Config/zipball/v2.1.10", "url": "https://api.github.com/repos/symfony/Config/zipball/v2.1.11",
"reference": "v2.1.10", "reference": "v2.1.11",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -29,7 +29,6 @@
"Symfony\\Component\\Config": "" "Symfony\\Component\\Config": ""
} }
}, },
"notification-url": "https://packagist.org/downloads/",
"license": [ "license": [
"MIT" "MIT"
], ],
@ -45,21 +44,21 @@
], ],
"description": "Symfony Config Component", "description": "Symfony Config Component",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2013-04-22 04:28:40" "time": "2013-05-09 15:22:40"
}, },
{ {
"name": "symfony/dependency-injection", "name": "symfony/dependency-injection",
"version": "v2.1.10", "version": "v2.1.11",
"target-dir": "Symfony/Component/DependencyInjection", "target-dir": "Symfony/Component/DependencyInjection",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/DependencyInjection.git", "url": "https://github.com/symfony/DependencyInjection.git",
"reference": "v2.1.10" "reference": "v2.1.11"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/DependencyInjection/zipball/v2.1.10", "url": "https://api.github.com/repos/symfony/DependencyInjection/zipball/v2.1.11",
"reference": "v2.1.10", "reference": "v2.1.11",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -79,7 +78,6 @@
"Symfony\\Component\\DependencyInjection": "" "Symfony\\Component\\DependencyInjection": ""
} }
}, },
"notification-url": "https://packagist.org/downloads/",
"license": [ "license": [
"MIT" "MIT"
], ],
@ -99,17 +97,17 @@
}, },
{ {
"name": "symfony/event-dispatcher", "name": "symfony/event-dispatcher",
"version": "v2.1.10", "version": "v2.1.11",
"target-dir": "Symfony/Component/EventDispatcher", "target-dir": "Symfony/Component/EventDispatcher",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/EventDispatcher.git", "url": "https://github.com/symfony/EventDispatcher.git",
"reference": "v2.1.10" "reference": "v2.1.11"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/v2.1.10", "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/v2.1.11",
"reference": "v2.1.10", "reference": "v2.1.11",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -128,7 +126,6 @@
"Symfony\\Component\\EventDispatcher": "" "Symfony\\Component\\EventDispatcher": ""
} }
}, },
"notification-url": "https://packagist.org/downloads/",
"license": [ "license": [
"MIT" "MIT"
], ],
@ -148,17 +145,17 @@
}, },
{ {
"name": "symfony/http-foundation", "name": "symfony/http-foundation",
"version": "v2.1.10", "version": "v2.1.11",
"target-dir": "Symfony/Component/HttpFoundation", "target-dir": "Symfony/Component/HttpFoundation",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/HttpFoundation.git", "url": "https://github.com/symfony/HttpFoundation.git",
"reference": "v2.1.10" "reference": "v2.1.11"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/v2.1.10", "url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/v2.1.11",
"reference": "v2.1.10", "reference": "v2.1.11",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -171,7 +168,6 @@
"SessionHandlerInterface": "Symfony/Component/HttpFoundation/Resources/stubs" "SessionHandlerInterface": "Symfony/Component/HttpFoundation/Resources/stubs"
} }
}, },
"notification-url": "https://packagist.org/downloads/",
"license": [ "license": [
"MIT" "MIT"
], ],
@ -187,21 +183,21 @@
], ],
"description": "Symfony HttpFoundation Component", "description": "Symfony HttpFoundation Component",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2013-04-30 17:01:33" "time": "2013-05-26 18:42:07"
}, },
{ {
"name": "symfony/http-kernel", "name": "symfony/http-kernel",
"version": "v2.1.10", "version": "v2.1.11",
"target-dir": "Symfony/Component/HttpKernel", "target-dir": "Symfony/Component/HttpKernel",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/HttpKernel.git", "url": "https://github.com/symfony/HttpKernel.git",
"reference": "v2.1.10" "reference": "v2.1.11"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/HttpKernel/zipball/v2.1.10", "url": "https://api.github.com/repos/symfony/HttpKernel/zipball/v2.1.11",
"reference": "v2.1.10", "reference": "v2.1.11",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -233,7 +229,6 @@
"Symfony\\Component\\HttpKernel": "" "Symfony\\Component\\HttpKernel": ""
} }
}, },
"notification-url": "https://packagist.org/downloads/",
"license": [ "license": [
"MIT" "MIT"
], ],
@ -249,21 +244,21 @@
], ],
"description": "Symfony HttpKernel Component", "description": "Symfony HttpKernel Component",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2013-05-06 11:01:51" "time": "2013-06-02 12:29:05"
}, },
{ {
"name": "symfony/routing", "name": "symfony/routing",
"version": "v2.1.9", "version": "v2.1.11",
"target-dir": "Symfony/Component/Routing", "target-dir": "Symfony/Component/Routing",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/Routing.git", "url": "https://github.com/symfony/Routing.git",
"reference": "v2.1.9" "reference": "v2.1.11"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/Routing/zipball/v2.1.9", "url": "https://api.github.com/repos/symfony/Routing/zipball/v2.1.11",
"reference": "v2.1.9", "reference": "v2.1.11",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -286,7 +281,6 @@
"Symfony\\Component\\Routing": "" "Symfony\\Component\\Routing": ""
} }
}, },
"notification-url": "https://packagist.org/downloads/",
"license": [ "license": [
"MIT" "MIT"
], ],
@ -302,21 +296,21 @@
], ],
"description": "Symfony Routing Component", "description": "Symfony Routing Component",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2013-03-23 07:47:35" "time": "2013-05-06 10:48:41"
}, },
{ {
"name": "symfony/yaml", "name": "symfony/yaml",
"version": "v2.1.9", "version": "v2.1.11",
"target-dir": "Symfony/Component/Yaml", "target-dir": "Symfony/Component/Yaml",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/Yaml.git", "url": "https://github.com/symfony/Yaml.git",
"reference": "v2.1.9" "reference": "v2.1.11"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/Yaml/zipball/v2.1.9", "url": "https://api.github.com/repos/symfony/Yaml/zipball/v2.1.11",
"reference": "v2.1.9", "reference": "v2.1.11",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -328,7 +322,6 @@
"Symfony\\Component\\Yaml": "" "Symfony\\Component\\Yaml": ""
} }
}, },
"notification-url": "https://packagist.org/downloads/",
"license": [ "license": [
"MIT" "MIT"
], ],
@ -344,7 +337,55 @@
], ],
"description": "Symfony Yaml Component", "description": "Symfony Yaml Component",
"homepage": "http://symfony.com", "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": [ "packages-dev": [
@ -552,16 +593,16 @@
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
"version": "1.2.9", "version": "1.2.11",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "1.2.9" "reference": "1.2.11"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/1.2.9", "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/1.2.11",
"reference": "1.2.9", "reference": "1.2.11",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -570,6 +611,9 @@
"phpunit/php-text-template": ">=1.1.1@stable", "phpunit/php-text-template": ">=1.1.1@stable",
"phpunit/php-token-stream": ">=1.1.3@stable" "phpunit/php-token-stream": ">=1.1.3@stable"
}, },
"require-dev": {
"phpunit/phpunit": "3.7.*"
},
"suggest": { "suggest": {
"ext-dom": "*", "ext-dom": "*",
"ext-xdebug": ">=2.0.5" "ext-xdebug": ">=2.0.5"
@ -601,7 +645,7 @@
"testing", "testing",
"xunit" "xunit"
], ],
"time": "2013-02-26 18:55:56" "time": "2013-05-23 18:23:24"
}, },
{ {
"name": "phpunit/php-file-iterator", "name": "phpunit/php-file-iterator",
@ -783,16 +827,16 @@
}, },
{ {
"name": "phpunit/phpunit", "name": "phpunit/phpunit",
"version": "3.7.19", "version": "3.7.21",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git", "url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "3.7.19" "reference": "3.7.21"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3.7.19", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3.7.21",
"reference": "3.7.19", "reference": "3.7.21",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -806,7 +850,7 @@
"phpunit/php-text-template": ">=1.1.1", "phpunit/php-text-template": ">=1.1.1",
"phpunit/php-timer": ">=1.0.2,<1.1.0", "phpunit/php-timer": ">=1.0.2,<1.1.0",
"phpunit/phpunit-mock-objects": ">=1.2.0,<1.3.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": { "require-dev": {
"pear-pear/pear": "1.9.4" "pear-pear/pear": "1.9.4"
@ -853,7 +897,7 @@
"testing", "testing",
"xunit" "xunit"
], ],
"time": "2013-03-25 11:45:06" "time": "2013-05-23 18:54:29"
}, },
{ {
"name": "phpunit/phpunit-mock-objects", "name": "phpunit/phpunit-mock-objects",
@ -906,17 +950,17 @@
}, },
{ {
"name": "symfony/browser-kit", "name": "symfony/browser-kit",
"version": "v2.1.10", "version": "v2.1.11",
"target-dir": "Symfony/Component/BrowserKit", "target-dir": "Symfony/Component/BrowserKit",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/BrowserKit.git", "url": "https://github.com/symfony/BrowserKit.git",
"reference": "v2.1.10" "reference": "v2.1.11"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/BrowserKit/zipball/v2.1.10", "url": "https://api.github.com/repos/symfony/BrowserKit/zipball/v2.1.11",
"reference": "v2.1.10", "reference": "v2.1.11",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -956,17 +1000,17 @@
}, },
{ {
"name": "symfony/css-selector", "name": "symfony/css-selector",
"version": "v2.1.10", "version": "v2.1.11",
"target-dir": "Symfony/Component/CssSelector", "target-dir": "Symfony/Component/CssSelector",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/CssSelector.git", "url": "https://github.com/symfony/CssSelector.git",
"reference": "v2.1.10" "reference": "v2.1.11"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/CssSelector/zipball/v2.1.10", "url": "https://api.github.com/repos/symfony/CssSelector/zipball/v2.1.11",
"reference": "v2.1.10", "reference": "v2.1.11",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -994,21 +1038,21 @@
], ],
"description": "Symfony CssSelector Component", "description": "Symfony CssSelector Component",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2013-01-09 08:51:07" "time": "2013-05-17 00:31:34"
}, },
{ {
"name": "symfony/dom-crawler", "name": "symfony/dom-crawler",
"version": "v2.1.10", "version": "v2.1.11",
"target-dir": "Symfony/Component/DomCrawler", "target-dir": "Symfony/Component/DomCrawler",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/DomCrawler.git", "url": "https://github.com/symfony/DomCrawler.git",
"reference": "v2.1.10" "reference": "v2.1.11"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/DomCrawler/zipball/v2.1.10", "url": "https://api.github.com/repos/symfony/DomCrawler/zipball/v2.1.11",
"reference": "v2.1.10", "reference": "v2.1.11",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1042,21 +1086,21 @@
], ],
"description": "Symfony DomCrawler Component", "description": "Symfony DomCrawler Component",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2013-03-27 17:13:16" "time": "2013-05-16 00:06:15"
}, },
{ {
"name": "symfony/finder", "name": "symfony/finder",
"version": "v2.1.10", "version": "v2.1.11",
"target-dir": "Symfony/Component/Finder", "target-dir": "Symfony/Component/Finder",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/Finder.git", "url": "https://github.com/symfony/Finder.git",
"reference": "v2.1.10" "reference": "v2.1.11"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/Finder/zipball/v2.1.10", "url": "https://api.github.com/repos/symfony/Finder/zipball/v2.1.11",
"reference": "v2.1.10", "reference": "v2.1.11",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1084,21 +1128,21 @@
], ],
"description": "Symfony Finder Component", "description": "Symfony Finder Component",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2013-03-06 19:26:55" "time": "2013-05-25 15:47:15"
}, },
{ {
"name": "symfony/process", "name": "symfony/process",
"version": "v2.1.9", "version": "v2.1.11",
"target-dir": "Symfony/Component/Process", "target-dir": "Symfony/Component/Process",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/Process.git", "url": "https://github.com/symfony/Process.git",
"reference": "v2.1.9" "reference": "v2.1.11"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/Process/zipball/v2.1.9", "url": "https://api.github.com/repos/symfony/Process/zipball/v2.1.11",
"reference": "v2.1.9", "reference": "v2.1.11",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1126,7 +1170,7 @@
], ],
"description": "Symfony Process Component", "description": "Symfony Process Component",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2013-03-23 07:44:01" "time": "2013-05-06 10:21:56"
} }
], ],
"aliases": [ "aliases": [

View file

@ -79,7 +79,7 @@ services:
arguments: arguments:
- @user - @user
- @service_container - @service_container
- @ext.finder - @style
cron.task_collection: cron.task_collection:
class: phpbb_di_service_collection class: phpbb_di_service_collection
@ -255,15 +255,15 @@ services:
class: phpbb_style_path_provider class: phpbb_style_path_provider
template: template:
class: phpbb_template class: phpbb_template_twig
arguments: arguments:
- %core.root_path% - %core.root_path%
- %core.php_ext% - %core.php_ext%
- @config - @config
- @user - @user
- @style.resource_locator
- @template_context - @template_context
- @ext.manager - @ext.manager
- %core.adm_relative_path%
template_context: template_context:
class: phpbb_template_context class: phpbb_template_context

View file

@ -134,11 +134,11 @@ class bbcode
$style_resource_locator = new phpbb_style_resource_locator(); $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); $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 = new phpbb_style($phpbb_root_path, $phpEx, $config, $user, $style_resource_locator, $style_path_provider, $template);
$style->set_style(); $style->set_style();
$template->set_filenames(array('bbcode.html' => 'bbcode.html')); $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(); $bbcode_ids = $rowset = $sql = array();

View file

@ -37,16 +37,24 @@ class phpbb_controller_resolver implements ControllerResolverInterface
*/ */
protected $container; protected $container;
/**
* phpbb_style object
* @var phpbb_style
*/
protected $style;
/** /**
* Construct method * Construct method
* *
* @param phpbb_user $user User Object * @param phpbb_user $user User Object
* @param ContainerInterface $container ContainerInterface 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->user = $user;
$this->container = $container; $this->container = $container;
$this->style = $style;
} }
/** /**
@ -80,6 +88,24 @@ class phpbb_controller_resolver implements ControllerResolverInterface
$controller_object = $this->container->get($service); $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); return array($controller_object, $method);
} }

View file

@ -27,8 +27,9 @@ class messenger
var $mail_priority = MAIL_NORMAL_PRIORITY; var $mail_priority = MAIL_NORMAL_PRIORITY;
var $use_queue = true; var $use_queue = true;
var $tpl_obj = NULL; /** @var phpbb_template */
var $tpl_msg = array(); protected $template;
var $eol = "\n"; var $eol = "\n";
/** /**
@ -210,6 +211,8 @@ class messenger
{ {
global $config, $phpbb_root_path, $phpEx, $user, $phpbb_extension_manager; global $config, $phpbb_root_path, $phpEx, $user, $phpbb_extension_manager;
$this->setup_template();
if (!trim($template_file)) if (!trim($template_file))
{ {
trigger_error('No template file for emailing set.', E_USER_ERROR); 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 // fall back to board default language if the user's language is
// missing $template_file. If this does not exist either, // 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']); $template_lang = basename($config['default_lang']);
} }
// tpl_msg now holds a template object we can use to parse the template file if ($template_path)
if (!isset($this->tpl_msg[$template_lang . $template_file]))
{ {
$style_resource_locator = new phpbb_style_resource_locator(); $template_paths = array(
$style_path_provider = new phpbb_style_extension_path_provider($phpbb_extension_manager, new phpbb_style_path_provider(), $phpbb_root_path); $template_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); }
else
$this->tpl_msg[$template_lang . $template_file] = $tpl;
$fallback_template_path = false;
if (!$template_path)
{ {
$template_path = (!empty($user->lang_path)) ? $user->lang_path : $phpbb_root_path . 'language/'; $template_path = (!empty($user->lang_path)) ? $user->lang_path : $phpbb_root_path . 'language/';
$template_path .= $template_lang . '/email'; $template_path .= $template_lang . '/email';
$template_paths = array(
$template_path,
);
// we can only specify default language fallback when the path is not a custom one for which we // 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 // do not know the default language alternative
if ($template_lang !== basename($config['default_lang'])) if ($template_lang !== basename($config['default_lang']))
{ {
$fallback_template_path = (!empty($user->lang_path)) ? $user->lang_path : $phpbb_root_path . 'language/'; $fallback_template_path = (!empty($user->lang_path)) ? $user->lang_path : $phpbb_root_path . 'language/';
$fallback_template_path .= basename($config['default_lang']) . '/email'; $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( $this->template->set_filenames(array(
'body' => $template_file . '.txt', 'body' => $template_file . '.txt',
)); ));
}
$this->tpl_obj = &$this->tpl_msg[$template_lang . $template_file]; $this->vars = $this->template->get_template_vars();
$this->vars = &$this->tpl_obj->_rootref;
$this->tpl_msg = '';
return true; return true;
} }
@ -268,22 +268,16 @@ class messenger
*/ */
function assign_vars($vars) function assign_vars($vars)
{ {
if (!is_object($this->tpl_obj)) $this->setup_template();
{
return;
}
$this->tpl_obj->assign_vars($vars); $this->template->assign_vars($vars);
} }
function assign_block_vars($blockname, $vars) function assign_block_vars($blockname, $vars)
{ {
if (!is_object($this->tpl_obj)) $this->setup_template();
{
return;
}
$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 // 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 // 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); $this->msg = str_replace("\r\n", "\n", $this->msg);
@ -643,6 +637,31 @@ class messenger
unset($addresses); unset($addresses);
return true; 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);
}
} }
/** /**

View file

@ -455,7 +455,7 @@ class p_master
*/ */
function load_active($mode = false, $module_url = false, $execute_module = true) 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; $module_path = $this->include_path . $this->p_class;
$icat = request_var('icat', ''); $icat = request_var('icat', '');
@ -494,6 +494,24 @@ class p_master
// We pre-define the action parameter we are using all over the place // We pre-define the action parameter we are using all over the place
if (defined('IN_ADMIN')) 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? // 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']) if (!$icat && $this->module_ary[$this->active_module_row_id]['is_duplicate'])
{ {
@ -505,6 +523,24 @@ class p_master
} }
else 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 user specified the module url we will use it...
if ($module_url !== false) if ($module_url !== false)
{ {

View file

@ -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_list = array(
$style_dirs = ($this->user->style['style_parent_id']) ? array_reverse(explode('/', $this->user->style['style_parent_tree'])) : array(); $this->user->style['style_path'],
);
$names = array($style_path); if ($this->user->style['style_parent_id'])
foreach ($style_dirs as $dir)
{ {
$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(); $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 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 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/). * @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) 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->provider->set_styles($paths);
$this->locator->set_paths($this->provider); $this->locator->set_paths($this->provider);
$this->template->set_style_names($names);
if ($template_path !== false) if ($template_path !== false)
{ {
$this->locator->set_template_path($template_path); $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; return true;
} }
@ -155,11 +190,14 @@ class phpbb_style
* Get location of style directory for specific style_path * Get location of style directory for specific style_path
* *
* @param string $path Style path, such as "prosilver" * @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 * @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;
} }
/** /**

View file

@ -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);
}
}

View file

@ -138,7 +138,7 @@ class phpbb_template_context
} }
$s_row_count = isset($str[$blocks[$blockcount]]) ? sizeof($str[$blocks[$blockcount]]) : 0; $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 // Assign S_FIRST_ROW
if (!$s_row_count) if (!$s_row_count)
@ -146,6 +146,9 @@ class phpbb_template_context
$vararray['S_FIRST_ROW'] = true; $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 // 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) // This is much more clever than going through the complete template data on display (phew)
$vararray['S_LAST_ROW'] = true; $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 // We're adding a new iteration to this block with the given
// variable assignments. // variable assignments.
$str[$blocks[$blockcount]][] = $vararray; $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 else
{ {
// Top-level block. // Top-level block.
$s_row_count = (isset($this->tpldata[$blockname])) ? sizeof($this->tpldata[$blockname]) : 0; $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 // Assign S_FIRST_ROW
if (!$s_row_count) if (!$s_row_count)
@ -171,6 +180,9 @@ class phpbb_template_context
$vararray['S_FIRST_ROW'] = true; $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 // We always assign S_LAST_ROW and remove the entry before
$vararray['S_LAST_ROW'] = true; $vararray['S_LAST_ROW'] = true;
if ($s_row_count > 0) 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. // Add a new iteration to this block with the variable assignments we were given.
$this->tpldata[$blockname][] = $vararray; $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; return true;
@ -298,14 +316,26 @@ class phpbb_template_context
$vararray['S_FIRST_ROW'] = true; $vararray['S_FIRST_ROW'] = true;
} }
// Assign S_BLOCK_NAME
$vararray['S_BLOCK_NAME'] = $blockname;
// Re-position template blocks // Re-position template blocks
for ($i = sizeof($block); $i > $key; $i--) for ($i = sizeof($block); $i > $key; $i--)
{ {
$block[$i] = $block[$i-1]; $block[$i] = $block[$i-1];
$block[$i]['S_ROW_COUNT'] = $block[$i]['S_ROW_NUM'] = $i;
} }
// Insert vararray at given position // Insert vararray at given position
$block[$key] = $vararray; $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; return true;
} }

File diff suppressed because it is too large Load diff

View file

@ -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);
}

View file

@ -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 ');
}
}

View file

@ -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);
}
}

View file

@ -2,7 +2,7 @@
/** /**
* *
* @package phpBB3 * @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 * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
* *
*/ */
@ -15,143 +15,48 @@ if (!defined('IN_PHPBB'))
exit; exit;
} }
/** interface phpbb_template
* @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
{ {
/**
* Template context.
* Stores template data used during template rendering.
* @var phpbb_template_context
*/
private $context;
/** /**
* Path of the cache directory for the template * Clear the cache
* @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 * @return phpbb_template
*/ */
private $extension_manager; public function clear_cache();
/**
* 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.
*
* @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
*/
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;
}
/** /**
* Sets the template filenames for handles. * Sets the template filenames for handles.
* *
* @param array $filename_array Should be a hash of handle => filename pairs. * @param array $filename_array Should be a hash of handle => filename pairs.
* @return phpbb_template $this
*/ */
public function set_filenames(array $filename_array) public function set_filenames(array $filename_array);
{
$this->locator->set_filenames($filename_array);
return true;
}
/** /**
* Sets the style names corresponding to style hierarchy being compiled * Sets the style names/paths corresponding to style hierarchy being compiled
* and/or rendered. * and/or rendered.
* *
* @param array $style_names List of style names in inheritance tree order * @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) public function set_style_names(array $style_names, array $style_paths);
{
$this->style_names = $style_names;
}
/** /**
* Clears all variables and blocks assigned to this template. * Clears all variables and blocks assigned to this template.
*
* @return phpbb_template $this
*/ */
public function destroy() public function destroy();
{
$this->context->clear();
}
/** /**
* Reset/empty complete block * Reset/empty complete block
* *
* @param string $blockname Name of block to destroy * @param string $blockname Name of block to destroy
* @return phpbb_template $this
*/ */
public function destroy_block_vars($blockname) public function destroy_block_vars($blockname);
{
$this->context->destroy_block_vars($blockname);
}
/** /**
* Display a template for provided handle. * Display a template for provided handle.
@ -161,81 +66,9 @@ class phpbb_template
* This function calls hooks. * This function calls hooks.
* *
* @param string $handle Handle to display * @param string $handle Handle to display
* @return bool True on success, false on failure * @return phpbb_template $this
*/ */
public function display($handle) 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.
*/
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;
}
/** /**
* Display the handle and assign the output to a template variable * 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 $handle Handle to operate on
* @param string $template_var Template variable to assign compiled handle to * @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 * @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 * @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) 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
*/
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;
}
/** /**
* Assign key variable pairs from an array * Assign key variable pairs from an array
* *
* @param array $vararray A hash of variable name => value pairs * @param array $vararray A hash of variable name => value pairs
* @return phpbb_template $this
*/ */
public function assign_vars(array $vararray) public function assign_vars(array $vararray);
{
foreach ($vararray as $key => $val)
{
$this->assign_var($key, $val);
}
}
/** /**
* Assign a single scalar value to a single key. * Assign a single scalar value to a single key.
@ -364,11 +96,9 @@ class phpbb_template
* *
* @param string $varname Variable name * @param string $varname Variable name
* @param string $varval Value to assign to variable * @param string $varval Value to assign to variable
* @return phpbb_template $this
*/ */
public function assign_var($varname, $varval) public function assign_var($varname, $varval);
{
$this->context->assign_var($varname, $varval);
}
/** /**
* Append text to the string value stored in a key. * Append text to the string value stored in a key.
@ -377,24 +107,18 @@ class phpbb_template
* *
* @param string $varname Variable name * @param string $varname Variable name
* @param string $varval Value to append to variable * @param string $varval Value to append to variable
* @return phpbb_template $this
*/ */
public function append_var($varname, $varval) public function append_var($varname, $varval);
{
$this->context->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 * Assign key variable pairs from an array to a specified block
* @param string $blockname Name of block to assign $vararray to * @param string $blockname Name of block to assign $vararray to
* @param array $vararray A hash of variable name => value pairs * @param array $vararray A hash of variable name => value pairs
* @return phpbb_template $this
*/ */
public function assign_block_vars($blockname, array $vararray) public function assign_block_vars($blockname, array $vararray);
{
return $this->context->assign_block_vars($blockname, $vararray);
}
// Docstring is copied from phpbb_template_context method with the same name.
/** /**
* Change already assigned key variable pair (one-dimensional - single loop entry) * 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 * @return bool false on error, true on success
*/ */
public function alter_block_array($blockname, array $vararray, $key = false, $mode = 'insert') 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. * Get path to template for handle (required for BBCode parser)
* *
* This function is marked public due to the way the template * @return string
* 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) public function get_source_file_for_handle($handle);
{
$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";
}
}
/**
* 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.
*
* 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
*/
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);
}
} }

View file

@ -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;
}
}

View file

@ -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);
}
}
}

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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")
;
}
}

View file

@ -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")
;
}
}
}
}

View file

@ -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('===');
}
}

View file

@ -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('!==');
}
}

View file

@ -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")
;
}
}

View file

@ -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")
;
}
}

View file

@ -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")
;
}
}

View file

@ -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")
;
}
}
}

View file

@ -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'))
;
}
}

View file

@ -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';
}
}

View file

@ -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';
}
}

View file

@ -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';
}
}

View file

@ -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';
}
}

View file

@ -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';
}
}

View file

@ -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';
}
}

View file

@ -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));
}
}

View file

@ -214,7 +214,7 @@ $config = new phpbb_config(array(
$phpbb_style_resource_locator = new phpbb_style_resource_locator(); $phpbb_style_resource_locator = new phpbb_style_resource_locator();
$phpbb_style_path_provider = new phpbb_style_path_provider(); $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 = 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_ext_dir_prefix('adm/');
$phpbb_style->set_custom_style('admin', $phpbb_admin_path . 'style', array(), ''); $phpbb_style->set_custom_style('admin', $phpbb_admin_path . 'style', array(), '');

View file

@ -38,7 +38,7 @@ function insert_single(user)
// ]]> // ]]>
</script> </script>
<!-- ENDIF --> <!-- ENDIF -->
<!-- INCLUDEJS template/forum_fn.js --> <!-- INCLUDEJS forum_fn.js -->
<h2 class="solo">{L_FIND_USERNAME}</h2> <h2 class="solo">{L_FIND_USERNAME}</h2>
<form method="post" action="{S_MODE_ACTION}" id="search_memberlist"> <form method="post" action="{S_MODE_ACTION}" id="search_memberlist">

View file

@ -53,11 +53,13 @@
<script type="text/javascript" src="{T_JQUERY_LINK}"></script> <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 --> <!-- 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> <!-- INCLUDEJS forum_fn.js -->
<script type="text/javascript" src="{T_ASSETS_PATH}/javascript/core.js?assets_version={T_ASSETS_VERSION}"></script> <!-- INCLUDEJS {T_ASSETS_PATH}/javascript/core.js -->
<!-- INCLUDEJS template/ajax.js --> <!-- INCLUDEJS ajax.js -->
{SCRIPTS}
<!-- EVENT overall_footer_after --> <!-- EVENT overall_footer_after -->
{$SCRIPTS}
</body> </body>
</html> </html>

View file

@ -52,7 +52,7 @@
// ]]> // ]]>
</script> </script>
<!-- INCLUDEJS template/editor.js --> <!-- INCLUDEJS editor.js -->
<!-- IF S_BBCODE_ALLOWED --> <!-- IF S_BBCODE_ALLOWED -->
<div id="colour_palette" style="display: none;"> <div id="colour_palette" style="display: none;">

View file

@ -15,6 +15,6 @@
{S_TZ_OPTIONS} {S_TZ_OPTIONS}
</select> </select>
<!-- INCLUDEJS template/timezone.js --> <!-- INCLUDEJS timezone.js -->
</dd> </dd>
</dl> </dl>

View file

@ -47,4 +47,4 @@
</div> </div>
</div> </div>
<!-- INCLUDEJS template/avatars.js --> <!-- INCLUDEJS avatars.js -->

View file

@ -13,9 +13,11 @@
<script type="text/javascript" src="{T_JQUERY_LINK}"></script> <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 --> <!-- 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> <!-- INCLUDEJS {T_ASSETS_PATH}/javascript/core.js -->
{SCRIPTS}
<!-- EVENT overall_footer_after --> <!-- EVENT overall_footer_after -->
{$SCRIPTS}
</body> </body>
</html> </html>

View file

@ -15,6 +15,6 @@
{S_TZ_OPTIONS} {S_TZ_OPTIONS}
</select> </select>
<!-- INCLUDEJS template/timezone.js --> <!-- INCLUDEJS timezone.js -->
</td> </td>
</tr> </tr>

View file

@ -95,7 +95,7 @@
</tr> </tr>
</table> </table>
<!-- INCLUDEJS template/avatars.js --> <!-- INCLUDEJS avatars.js -->
<!-- ELSEIF S_LIST --> <!-- ELSEIF S_LIST -->

View file

@ -48,6 +48,6 @@
</tr> </tr>
</table> </table>
<!-- INCLUDEJS template/avatars.js --> <!-- INCLUDEJS avatars.js -->
<!-- INCLUDE ucp_footer.html --> <!-- INCLUDE ucp_footer.html -->

View file

@ -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) 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; $phpbb_dispatcher = new phpbb_mock_event_dispatcher;
$this->style_resource_locator = new phpbb_style_resource_locator(); $this->style_resource_locator = new phpbb_style_resource_locator();
$this->user = $this->getMock('phpbb_user'); $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'); $helper = new phpbb_controller_helper($this->template, $this->user, '', 'php');
$this->assertEquals($helper->url($route, $params, $is_amp, $session_id), $expected); $this->assertEquals($helper->url($route, $params, $is_amp, $session_id), $expected);
} }
} }

View file

@ -42,12 +42,11 @@ class phpbb_extension_metadata_manager_test extends phpbb_database_test_case
$this->user = new phpbb_user(); $this->user = new phpbb_user();
$this->table_prefix = 'phpbb_'; $this->table_prefix = 'phpbb_';
$this->template = new phpbb_template( $this->template = new phpbb_template_twig(
$this->phpbb_root_path, $this->phpbb_root_path,
$this->phpEx, $this->phpEx,
$this->config, $this->config,
$this->user, $this->user,
new phpbb_style_resource_locator(),
new phpbb_template_context() new phpbb_template_context()
); );

View file

@ -15,9 +15,7 @@ class phpbb_template_includephp_test extends phpbb_template_template_test_case
{ {
$this->setup_engine(array('tpl_allow_php' => true)); $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");
$this->run_template('includephp_relative.html', array(), array(), array(), "Path is relative to board root.\ntesting included php", $cache_file);
$this->template->set_filenames(array('test' => 'includephp_relative.html')); $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"); $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)); $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");
$this->run_template('includephp_variables.html', array('TEMPLATES' => 'templates'), array(), array(), "Path includes variables.\ntesting included php", $cache_file);
$this->template->set_filenames(array('test' => 'includephp_variables.html')); $this->template->set_filenames(array('test' => 'includephp_variables.html'));
$this->assertEquals("Path includes variables.\ntesting included php", $this->display('test'), "Testing INCLUDEPHP"); $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() 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)); $this->assertTrue(phpbb_is_absolute($path_to_php));
$template_text = "Path is absolute.\n<!-- INCLUDEPHP $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'); $fp = fopen($cache_dir . 'includephp_absolute.html', 'w');
fputs($fp, $template_text); fputs($fp, $template_text);
fclose($fp); 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->setup_engine(array('tpl_allow_php' => true));
$this->style->set_custom_style('tests', $cache_dir, array(), ''); $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->template->set_filenames(array('test' => 'includephp_absolute.html'));
$this->assertEquals("Path is absolute.\ntesting included php", $this->display('test'), "Testing INCLUDEPHP"); $this->assertEquals("Path is absolute.\ntesting included php", $this->display('test'), "Testing INCLUDEPHP");

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -19,9 +19,7 @@ class phpbb_template_subdir_includephp_from_subdir_test extends phpbb_template_t
{ {
$this->setup_engine(array('tpl_allow_php' => true)); $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");
$this->run_template('includephp_relative.html', array(), array(), array(), "Path is relative to board root.\ntesting included php", $cache_file);
$this->template->set_filenames(array('test' => 'includephp_relative.html')); $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"); $this->assertEquals("Path is relative to board root.\ntesting included php", $this->display('test'), "Testing INCLUDEPHP");

View file

@ -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));
}
}

View file

@ -54,11 +54,9 @@ class phpbb_template_template_events_test extends phpbb_template_template_test_c
array(), array(),
array(), array(),
array(), array(),
'Kappa test event in all 'Kappa test event in silver
Omega test event in all Omega test event in silver
Zeta test event in all Zeta test event in all',
Kappa test event in silver
Omega test event in silver',
), ),
array( array(
'Template event with inheritance - child', 'Template event with inheritance - child',
@ -68,10 +66,9 @@ Omega test event in silver',
array(), array(),
array(), array(),
array(), array(),
'Kappa test event in all 'Kappa test event in silver_inherit
Omega test event in all Omega test event in silver
Zeta test event in all Zeta test event in all',
Kappa test event in silver_inherit',
), ),
array( array(
'Definition in parent style', 'Definition in parent style',
@ -95,8 +92,7 @@ Kappa test event in silver_inherit',
$this->setup_engine_for_events($dataset, $style_names); $this->setup_engine_for_events($dataset, $style_names);
// Run test // Run test
$cache_file = $this->template->cachepath . str_replace('/', '.', $file) . '.php'; $this->run_template($file, $vars, $block_vars, $destroy, $expected);
$this->run_template($file, $vars, $block_vars, $destroy, $expected, $cache_file);
} }
protected function setup_engine_for_events($dataset, $style_names, array $new_config = array()) 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( $this->extension_manager = new phpbb_mock_filesystem_extension_manager(
dirname(__FILE__) . "/datasets/$dataset/" 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_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 = 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, ''); $this->style->set_custom_style('silver', array($this->template_path), $style_names, '');

View file

@ -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 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&amp;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&amp;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&amp;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=&quot;&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 // Reset the engine state
$this->setup_engine(array('assets_version' => 1)); $this->setup_engine(array('assets_version' => 1));
// Prepare correct result $this->template->assign_vars($vars);
$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>',
);
// Run test // Run test
$cache_file = $this->template->cachepath . 'includejs.html.php'; $this->run_template('includejs.html', array_merge(array('PARENT' => 'parent_only.js', 'SUBDIR' => 'subdir', 'EXT' => 'js'), $vars), array(), array(), $expected);
$this->run_template('includejs.html', array('PARENT' => 'parent_only.js', 'SUBDIR' => 'subdir', 'EXT' => 'js'), array(), array(), implode('', $scripts), $cache_file);
} }
} }

View file

@ -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) public function test_template($name, $file, array $vars, array $block_vars, array $destroy, $expected)
{ {
$cache_file = $this->template->cachepath . str_replace('/', '.', $file) . '.php'; $this->run_template($file, $vars, $block_vars, $destroy, $expected);
$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);
} }
} }

View file

@ -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);
}
}

View file

@ -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, '');
}
}

View file

@ -46,28 +46,42 @@ class phpbb_template_template_test extends phpbb_template_template_test_case
array(), array(),
array(), array(),
array(), array(),
'03', '03!false',
), ),
array( array(
'if.html', 'if.html',
array('S_VALUE' => true), array('S_VALUE' => true),
array(), array(),
array(), array(),
'1', '1!false',
), ),
array( array(
'if.html', 'if.html',
array('S_VALUE' => true, 'S_OTHER_VALUE' => true), array('S_VALUE' => true, 'S_OTHER_VALUE' => true),
array(), array(),
array(), array(),
'1', '1!false',
), ),
array( array(
'if.html', 'if.html',
array('S_VALUE' => false, 'S_OTHER_VALUE' => true), array('S_VALUE' => false, 'S_OTHER_VALUE' => true),
array(), array(),
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( array(
'loop.html', 'loop.html',
@ -116,7 +130,7 @@ class phpbb_template_template_test extends phpbb_template_template_test_case
array(), array(),
array('loop' => array(array('VARIABLE' => 'x'), array('VARIABLE' => 'y')), 'loop.inner' => array(array(), array())), array('loop' => array(array('VARIABLE' => 'x'), array('VARIABLE' => 'y')), 'loop.inner' => array(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( array(
'loop_advanced.html', 'loop_advanced.html',
@ -125,6 +139,13 @@ class phpbb_template_template_test extends phpbb_template_template_test_case
array(), array(),
"101234561\nx\n101234561\nx\n101234561\nx\n1234561\nx\n1\nx\n101\nx\n234\nx\n10\nx\n561\nx\n561", "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( array(
'define.html', 'define.html',
array(), array(),
@ -139,13 +160,6 @@ class phpbb_template_template_test extends phpbb_template_template_test_case
array(), array(),
"abc\nzxc\ncde\nbcd", "abc\nzxc\ncde\nbcd",
), ),
array(
'define_unclosed.html',
array(),
array(),
array(),
"test",
),
array( array(
'expressions.html', 'expressions.html',
array(), array(),
@ -247,21 +261,15 @@ class phpbb_template_template_test extends phpbb_template_template_test_case
array(), array(),
array(), array(),
array(), array(),
"{ VARIABLE }\n{ 1_VARIABLE }\n{ VARIABLE }\n{ 1_VARIABLE }", "VARIABLE\n1_VARIABLE\nVARIABLE\n1_VARIABLE",
), ),
array( array(
'lang.html', 'lang.html',
array('L_VARIABLE' => "Value'", 'L_1_VARIABLE' => "1 O'Clock"), array(),
array(), array(),
array(), array(),
"Value'\n1 O'Clock\nValue\'\n1 O\'Clock", "Value'\n1 O'Clock\nValue\'\n1 O\'Clock",
), array('VARIABLE' => "Value'", '1_VARIABLE' => "1 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",
), ),
array( array(
'loop_nested_multilevel_ref.html', 'loop_nested_multilevel_ref.html',
@ -275,7 +283,6 @@ class phpbb_template_template_test extends phpbb_template_template_test_case
array(), array(),
array('outer' => array(array('VARIABLE' => 'x'), array('VARIABLE' => 'y')), 'outer.inner' => array(array('VARIABLE' => 'z'), array('VARIABLE' => 'zz'))), array('outer' => array(array('VARIABLE' => 'x'), array('VARIABLE' => 'y')), 'outer.inner' => array(array('VARIABLE' => 'z'), array('VARIABLE' => 'zz'))),
array(), 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", "top-level content\nouter x\nouter y\ninner z\nfirst row\n\ninner zz",
), ),
array( array(
@ -283,7 +290,6 @@ class phpbb_template_template_test extends phpbb_template_template_test_case
array(), array(),
array('outer' => array(array()), 'outer.middle' => array(array()), 'outer.middle.inner' => array(array('VARIABLE' => 'z'), array('VARIABLE' => 'zz'))), array('outer' => array(array()), 'outer.middle' => array(array()), 'outer.middle.inner' => array(array('VARIABLE' => 'z'), array('VARIABLE' => 'zz'))),
array(), 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", "top-level content\nouter\nmiddle\ninner z\nfirst row of 2 in inner\n\ninner zz",
), ),
array( array(
@ -303,6 +309,13 @@ class phpbb_template_template_test extends phpbb_template_template_test_case
"a\nb\nc\nd", "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->template->set_filenames(array('test' => $filename));
$this->assertFileNotExists($this->template_path . '/' . $filename, 'Testing missing file, file cannot exist'); $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->setExpectedException('Twig_Error_Loader');
$this->setExpectedTriggerError(E_USER_ERROR, $expecting);
$this->display('test'); $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() public function test_invalid_handle()
{ {
$expecting = 'No file specified for handle test'; $this->setExpectedException('Twig_Error_Loader');
$this->setExpectedTriggerError(E_USER_ERROR, $expecting);
$this->display('test'); $this->display('test');
} }
@ -338,49 +342,23 @@ class phpbb_template_template_test extends phpbb_template_template_test_case
/** /**
* @dataProvider template_data * @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->run_template($file, $vars, $block_vars, $destroy, $expected, $lang_vars);
$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);
} }
/** public function test_assign_display()
* @dataProvider template_data
*/
public function test_assign_display($file, array $vars, array $block_vars, array $destroy, $expected)
{ {
$this->run_template('basic.html', array(), array(), array(), "pass\npass\npass\n<!-- DUMMY var -->");
$this->template->set_filenames(array( $this->template->set_filenames(array(
'test' => $file, 'test' => 'basic.html',
'container' => 'variable.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->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() public function test_append_var_without_assign_var()
@ -421,13 +399,20 @@ class phpbb_template_template_test extends phpbb_template_template_test_case
public function test_php() public function test_php()
{ {
global $phpbb_root_path;
$template_text = '<!-- PHP -->echo "test";<!-- ENDPHP -->';
$cache_dir = dirname($phpbb_root_path . 'cache') . '/';
$fp = fopen($cache_dir . 'php.html', 'w');
fputs($fp, $template_text);
fclose($fp);
$this->setup_engine(array('tpl_allow_php' => true)); $this->setup_engine(array('tpl_allow_php' => true));
$cache_file = $this->template->cachepath . 'php.html.php'; $this->style->set_custom_style('tests', $cache_dir, array(), '');
$this->assertFileNotExists($cache_file); $this->run_template('php.html', array(), array(), array(), 'test');
$this->run_template('php.html', array(), array(), array(), 'test', $cache_file);
} }
public function alter_block_array_data() 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->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->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');
}
} }

View file

@ -16,6 +16,7 @@ class phpbb_template_template_test_case extends phpbb_test_case
protected $template_path; protected $template_path;
protected $style_resource_locator; protected $style_resource_locator;
protected $style_provider; protected $style_provider;
protected $user;
protected $test_path = 'tests/template'; protected $test_path = 'tests/template';
@ -28,7 +29,7 @@ class phpbb_template_template_test_case extends phpbb_test_case
try try
{ {
$this->assertTrue($this->template->display($handle, false)); $this->template->display($handle, false);
} }
catch (Exception $exception) 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()) protected function setup_engine(array $new_config = array())
{ {
global $phpbb_root_path, $phpEx, $user; global $phpbb_root_path, $phpEx;
$defaults = $this->config_defaults(); $defaults = $this->config_defaults();
$config = new phpbb_config(array_merge($defaults, $new_config)); $config = new phpbb_config(array_merge($defaults, $new_config));
$this->user = new phpbb_user;
$this->template_path = $this->test_path . '/templates'; $this->template_path = $this->test_path . '/templates';
$this->style_resource_locator = new phpbb_style_resource_locator(); $this->style_resource_locator = new phpbb_style_resource_locator();
$this->style_provider = new phpbb_style_path_provider(); $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, $this->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 = 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(), ''); $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 // Test the engine can be used
$this->setup_engine(); $this->setup_engine();
$template_cache_dir = dirname($this->template->cachepath); $this->template->clear_cache();
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();
} }
protected function tearDown() protected function tearDown()
{ {
if (is_object($this->template)) $this->template->clear_cache();
{
$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);
}
}
}
} }
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->set_filenames(array('test' => $file));
$this->template->assign_vars($vars); $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); $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"); foreach ($lang_vars as $name => $value)
$this->assertFileExists($cache_file); {
$this->user->lang[$name] = $value;
} }
catch (ErrorException $e)
{
if (file_exists($cache_file))
{
copy($cache_file, str_replace('ctpl_', 'tests_ctpl_', $cache_file));
}
throw $e;
} }
// For debugging. $expected = str_replace(array("\n", "\r", "\t"), '', $expected);
// When testing eval path the cache file may not exist. $output = str_replace(array("\n", "\r", "\t"), '', $this->display('test'));
if (self::PRESERVE_CACHE && file_exists($cache_file)) $this->assertEquals($expected, $output, "Testing $file");
{
copy($cache_file, str_replace('ctpl_', 'tests_ctpl_', $cache_file));
}
} }
} }

View file

@ -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->parent_template_path = $this->test_path . '/parent_templates';
$this->style_resource_locator = new phpbb_style_resource_locator(); $this->style_resource_locator = new phpbb_style_resource_locator();
$this->style_provider = new phpbb_style_path_provider(); $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 = 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(), ''); $this->style->set_custom_style('tests', array($this->template_path, $this->parent_template_path), array(), '');
} }

View file

@ -1,2 +0,0 @@
<!-- DEFINE $VALUE -->
test

View file

@ -9,3 +9,11 @@
<!-- IF S_VALUE and S_OTHER_VALUE and (S_VALUE > S_OTHER_VALUE) --> <!-- IF S_VALUE and S_OTHER_VALUE and (S_VALUE > S_OTHER_VALUE) -->
04 04
<!-- ENDIF --> <!-- ENDIF -->
<!-- IF S_TEST === false -->
false
<!-- ENDIF -->
<!-- IF S_TEST !== false -->
!false
<!-- ENDIF -->

View file

@ -1,8 +1,38 @@
<!-- INCLUDEJS parent_and_child.js --> <!-- IF TEST === 1 -->
<!-- INCLUDEJS {PARENT} --> <!-- INCLUDEJS parent_and_child.js -->
<!-- DEFINE $TEST = 'child_only.js' --> <!-- ELSEIF TEST === 2 -->
<!-- INCLUDEJS {$TEST} --> <!-- INCLUDEJS parent_and_child.js?assets_version=0 -->
<!-- INCLUDEJS subdir/{PARENT} --> <!-- ELSEIF TEST === 3 -->
<!-- INCLUDEJS {SUBDIR}/subsubdir/{PARENT} --> <!-- INCLUDEJS parent_and_child.js?test=1&assets_version=0 -->
<!-- INCLUDEJS {SUBDIR}/parent_only.{EXT} --> <!-- ELSEIF TEST === 4 -->
{SCRIPTS} <!-- INCLUDEJS parent_and_child.js?test=1&amp;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&amp;test2=2#test3' -->
<!-- INCLUDEJS {$TEST} -->
<!-- ELSEIF TEST === 12 -->
<!-- INCLUDEJS parent_only.js?test1=1&amp;test2=2#test3 -->
<!-- ELSEIF TEST === 13 -->
<!-- INCLUDEJS parent_only.js?test1=1;test2=2#test3 -->
<!-- ELSEIF TEST === 14 -->
<!-- INCLUDEJS parent_only.js?test1=&quot;#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}

View file

@ -1 +0,0 @@
<!-- INCLUDE nonexistent.html -->

View file

@ -1 +0,0 @@
<!-- UNKNOWNTAG variable.html -->

View file

@ -1,6 +1,6 @@
<!-- BEGIN outer --> <!-- BEGIN outer -->
outer - {outer.S_ROW_COUNT}<!-- IF outer.VARIABLE --> - {outer.VARIABLE}<!-- ENDIF --> 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 --> <!-- BEGIN middle -->
middle - {middle.S_ROW_COUNT}<!-- IF middle.VARIABLE --> - {middle.VARIABLE}<!-- ENDIF --> 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 middle -->
<!-- END outer --> <!-- END outer -->

View file

@ -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 -->

View file

@ -36,4 +36,4 @@
<!-- BEGIN loop --> <!-- BEGIN loop -->
in loop in loop
<!-- END --> <!-- END loop -->

View file

@ -2,7 +2,7 @@
loop loop
<!-- BEGINELSE --> <!-- BEGINELSE -->
noloop noloop
<!-- END loop --> <!-- END _underscore_loop -->
<!-- IF ._underscore_loop --> <!-- IF ._underscore_loop -->
loop loop

View file

@ -11,4 +11,3 @@ last
<!-- IF inner.S_LAST_ROW and inner.S_ROW_COUNT and inner.S_NUM_ROWS -->last inner<!-- ENDIF --> <!-- IF inner.S_LAST_ROW and inner.S_ROW_COUNT and inner.S_NUM_ROWS -->last inner<!-- ENDIF -->
<!-- END inner --> <!-- END inner -->
<!-- END loop --> <!-- END loop -->
<!-- IF .loop.inner -->inner loop<!-- ENDIF -->

View file

@ -0,0 +1,6 @@
<!-- EXTENDS "twig_parent.html" -->
<!-- BLOCK overwritten -->
3{VARIABLE|upper}|{VARIABLE|lower}
<!-- ENDBLOCK -->

View file

@ -0,0 +1,7 @@
<!-- BLOCK notoverwritten -->
1
<!-- ENDBLOCK -->
<!-- BLOCK overwritten -->
2
<!-- ENDBLOCK -->