Skip to content

Commit

Permalink
Start implementing Twig for the front-end (#2963)
Browse files Browse the repository at this point in the history
* Starting work on moving front end to Twig

This marks the start of integrating Twig into the front end. There are two global variables shared with all views - mybb and lang.

Signed-off-by: Euan Torano <euantorano@gmail.com>

* Starting the long process of template translation

Not much to show yet, but this is a first step towards seeing that key variables such as $mybb, $lang and $theme can be accessed.

Need to sort out loading of stylesheets still to make things look decent.

Signed-off-by: Euan Torano <euantorano@gmail.com>

* Changing how language is registered

There is a new LangExtension that provides a `trans` function along with the `lang` global.

Signed-off-by: Euan Torano <euantorano@gmail.com>

* Showing drafts by adding drafts to an array

Moved setting of theme global to theme extension.

Need to fix the use of |raw in some places - we should generate the HTML inside the views themselves instead…

Signed-off-by: Euan Torano <euantorano@gmail.com>

* Finished the majority of work on the drafts page

Signed-off-by: Euan Torano <euantorano@gmail.com>

* Add return type hints

Signed-off-by: Euan Torano <euantorano@gmail.com>

* Fixing trow usage currently following 1.8 standard

Signed-off-by: Euan Torano <euantorano@gmail.com>
  • Loading branch information
euantorano committed Jan 5, 2018
1 parent 37fb723 commit 97b3aed
Show file tree
Hide file tree
Showing 16 changed files with 513 additions and 82 deletions.
Empty file removed inc/config.default.php
Empty file.
23 changes: 1 addition & 22 deletions inc/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -285,32 +285,11 @@ function send_mail_queue($count=10)
*/
function parse_page($contents)
{
global $lang, $theme, $mybb, $htmldoctype, $archive_url, $error_handler;
global $lang, $theme, $mybb, $archive_url, $error_handler;

$contents = str_replace('<navigation>', build_breadcrumb(), $contents);
$contents = str_replace('<archive_url>', $archive_url, $contents);

if($htmldoctype)
{
$contents = $htmldoctype.$contents;
}
else
{
$contents = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n".$contents;
}

$contents = str_replace("<html", "<html xmlns=\"http://www.w3.org/1999/xhtml\"", $contents);

if($lang->settings['rtl'] == 1)
{
$contents = str_replace("<html", "<html dir=\"rtl\"", $contents);
}

if($lang->settings['htmllang'])
{
$contents = str_replace("<html", "<html xml:lang=\"".$lang->settings['htmllang']."\" lang=\"".$lang->settings['htmllang']."\"", $contents);
}

if($error_handler->warnings)
{
$contents = str_replace("<body>", "<body>\n".$error_handler->show_warnings(), $contents);
Expand Down
56 changes: 56 additions & 0 deletions inc/src/Twig/Extensions/LangExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

namespace MyBB\Twig\Extensions;

/**
* A Twig extension class to provide functionality related to translations.
*/
class LangExtension extends \Twig_Extension implements \Twig_Extension_GlobalsInterface
{
/**
* @var \MyLanguage $lang
*/
private $lang;

/**
* Create a new instance of the LangExtension.
*
* @param \MyLanguage $lang
*/
public function __construct(\MyLanguage $lang)
{
$this->lang = $lang;
}

public function getFunctions()
{
return [
new \Twig_SimpleFunction('trans', [$this, 'trans']),
];
}

/**
* Returns a list of global variables to add to the existing list.
*
* @return array An array of global variables
*/
public function getGlobals()
{
return [
'lang' => $this->lang,
];
}

/**
* Get a translation that takes a number of parameters.
*
* @param string $languageVariable The name of the language variable to translate.
* @param array ...$params A list of parameters to inject into the translation.
*
* @return string The resolved translation string.
*/
public function trans(string $languageVariable, ...$params) : string
{
return $this->lang->sprintf($this->lang->$languageVariable, ...$params);
}
}
167 changes: 167 additions & 0 deletions inc/src/Twig/Extensions/ThemeExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
<?php

namespace MyBB\Twig\Extensions;

/**
* A Twig extension class to provide functionality related to themes and assets.
*/
class ThemeExtension extends \Twig_Extension implements \Twig_Extension_GlobalsInterface
{
/**
* @var \MyBB $mybb
*/
private $mybb;

/**
* @var \DB_Base $db
*/
private $db;

/**
* @var string $altRowState
*/
private $altRowState;

/**
* Create a new instance of the ThemeExtension.
*
* @param \MyBB $mybb
* @param \DB_Base $db
*/
public function __construct(\MyBB $mybb, \DB_Base $db)
{
$this->mybb = $mybb;
$this->db = $db;

$this->altRowState = null;
}

public function getFunctions()
{
return [
new \Twig_SimpleFunction('asset_url', [$this, 'getAssetUrl']),
new \Twig_SimpleFunction('alt_trow', [$this, 'altTrow']),
new \Twig_SimpleFunction('get_stylesheets', [$this, 'getStylesheets']),
];
}

/**
* Returns a list of global variables to add to the existing list.
*
* @return array An array of global variables
*/
public function getGlobals()
{
return [
'theme' => $GLOBALS['theme'],
];
}

/**
* Get the path to an asset using the CDN URL if configured.
*
* @param string $path The path to the file.
* @param bool $useCdn Whether to use the configured CDN options.
*
* @return string The complete URL to the asset.
*/
public function getAssetUrl(string $path, bool $useCdn = true): string
{
// TODO: This could be smart and add cache busting query parameters to the path automatically...
return $this->mybb->get_asset_url($path, $useCdn);
}

/**
* Select an alternating row colour based on the previous call to this function.
*
* @param bool $reset Whether to reset the row state to `trow1`.
*
* @return string `trow1` or `trow2` depending on the previous call.
*/
public function altTrow(bool $reset = false) : string
{
if (is_null($this->altRowState) || $this->altRowState === 'trow2' || $reset) {
$this->altRowState = 'trow1';
} else {
$this->altRowState = 'trow2';
}

return $this->altRowState;
}

/**
* Get a list of all the stylesheets applicable for the current page.
*
* @return \Generator A generator object that yields each stylesheet, as a full URL.
*/
public function getStylesheets() : \Generator
{
// TODO: Optimise this function - it looks like it can be improved at a glance
$theme = $GLOBALS['theme'];

$alreadyLoaded = [];

if (!is_array($theme['stylesheets'])) {
$theme['stylesheets'] = my_unserialize($theme['stylesheets']);
}

$stylesheetScripts = array("global", basename($_SERVER['PHP_SELF']));
if (!empty($theme['color'])) {
$stylesheetScripts[] = $theme['color'];
}

$stylesheetActions = array("global");
if (!empty($this->mybb->input['action'])) {
$stylesheetActions[] = $this->mybb->get_input('action');
}
foreach ($stylesheetScripts as $stylesheetScript) {
// Load stylesheets for global actions and the current action
foreach ($stylesheetActions as $stylesheet_action) {
if (!$stylesheet_action) {
continue;
}

if (!empty($theme['stylesheets'][$stylesheetScript][$stylesheet_action])) {
// Actually add the stylesheets to the list
foreach ($theme['stylesheets'][$stylesheetScript][$stylesheet_action] as $pageStylesheet) {
if (!empty($alreadyLoaded[$pageStylesheet])) {
continue;
}

if (strpos($pageStylesheet, 'css.php') !== false) {
$stylesheetUrl = $this->mybb->settings['bburl'] . '/' . $pageStylesheet;
} else {
$stylesheetUrl = $this->mybb->get_asset_url($pageStylesheet);
}

if ($this->mybb->settings['minifycss']) {
$stylesheetUrl = str_replace('.css', '.min.css', $stylesheetUrl);
}

if (strpos($pageStylesheet, 'css.php') !== false) {
// We need some modification to get it working with the displayorder
$queryString = parse_url($stylesheetUrl, PHP_URL_QUERY);
$id = (int)my_substr($queryString, 11);
$query = $this->db->simple_select("themestylesheets", "name", "sid={$id}");
$realName = $this->db->fetch_field($query, "name");
$themeStylesheets[$realName] = $stylesheetUrl;
} else {
$themeStylesheets[basename($pageStylesheet)] = $stylesheetUrl;
}

$alreadyLoaded[$pageStylesheet] = 1;
}
}
}
}
unset($actions);

if (!empty($themeStylesheets) && is_array($theme['disporder'])) {
foreach ($theme['disporder'] as $style_name => $order) {
if (!empty($themeStylesheets[$style_name])) {
yield $themeStylesheets[$style_name];
}
}
}
}
}
68 changes: 61 additions & 7 deletions inc/src/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,79 @@
use Illuminate\Events\Dispatcher;
use Illuminate\Http\Request;
use Illuminate\Routing\Router;
use MyBB\Twig\Extensions\LangExtension;
use MyBB\Twig\Extensions\ThemeExtension;
use Psr\Container\ContainerInterface;

require_once __DIR__ . '/../vendor/autoload.php';

/** @var \Illuminate\Contracts\Container\Container $container */
$container = app();

// MyBB
$container->singleton(\MyBB::class, function() {
return $GLOBALS['mybb'];
});

$container->alias(\MyBB::class, 'mybb');

// DB
$container->singleton(\DB_Base::class, function() {
return $GLOBALS['db'];
});

$container->alias(\DB_Base::class, 'db');

// Plugins
$container->singleton(\pluginSystem::class, function() {
return $GLOBALS['plugins'];
});

$container->alias(\pluginSystem::class, 'plugins');

// Lang
$container->singleton(\MyLanguage::class, function() {
return $GLOBALS['lang'];
});

$container->alias(\MyLanguage::class, 'lang');

// Twig
$container->singleton(\Twig_Environment::class, function(ContainerInterface $container) {
// TODO: The loader should be aware of both front-end and ACP themes
$loader = new \Twig_Loader_Filesystem([
__DIR__ . '/../views'
]);
if (defined('IN_ADMINCP')) {
$paths = [
__DIR__ . '/../views/admin',
];
} else {
// TODO: views for the current theme, it's parent, it's parent's parent, etc. should be here
// The filesystem loader works by using files from the first array entry.
// If a file doesn't exist, it looks in the second array entry and so on. This allows us to easily implement template inheritance.

return new \Twig_Environment($loader, [
'debug' => true,
'cache' => __DIR__ . '/../../cache/views',
$paths = [
__DIR__ . '/../views/base',
];
}

/** @var \pluginSystem $plugins */
$plugins = $container->get(\pluginSystem::class);

$plugins->run_hooks('twig_environment_before_loader', $paths);

$loader = new \Twig_Loader_Filesystem($paths);

$env = new \Twig_Environment($loader, [
'debug' => true, // TODO: In live environments this should be false
'cache' => __DIR__ . '/../../cache/views',
]);

$env->addExtension(new ThemeExtension($container->get(\MyBB::class), $container->get(\DB_Base::class)));
$env->addExtension(new LangExtension($container->get(\MyLanguage::class)));

$plugins->run_hooks('twig_environment_env', $env);

$env->addGlobal('mybb', $container->get(\MyBB::class));

return $env;
});

$container->alias(\Twig_Environment::class, 'twig');
Expand Down
20 changes: 20 additions & 0 deletions inc/src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,23 @@ function app($className = null, array $parameters = [])

return Container::getInstance()->make($className, $parameters);
}

/**
* Render a view using the Twig template system.
*
* @param string $name The name of the template to render.
* @param array $context An array of variables to be accessible within the template.
*
* @throws \Twig_Error_Loader When the template cannot be found
* @throws \Twig_Error_Syntax When an error occurred during compilation
* @throws \Twig_Error_Runtime When an error occurred during rendering
*
* @return string The rendered HTML content of the template.
*/
function template(string $name, array $context = [])
{
/** @var \Twig_Environment $twig */
$twig = app(\Twig_Environment::class);

return $twig->render($name, $context);
}
File renamed without changes.
2 changes: 1 addition & 1 deletion inc/views/admin/tools/php_info.twig
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{% extends "layouts/admin.twig" %}
{% extends "layouts/master.twig" %}

{% block head %}
<title>PHP Info</title>
Expand Down
Loading

0 comments on commit 97b3aed

Please sign in to comment.