Files
scummvm-web/include/Controller.php
T
Le Philousophe 710b95a5f4 WEB: Better error handling
When PHP functions fail they often return false instead of the expected
object type.
CHeck for this value and fail properly.
2025-08-24 12:32:47 +02:00

250 lines
8.2 KiB
PHP

<?php
namespace ScummVM;
use Smarty\Smarty;
use Smarty\Template;
use ScummVM\Models\SimpleYamlModel;
use ScummVM\SiteUtils;
/**
* The Controller class will create an instance of the Smarty object configured
* as specified in config.inc. Should be subclassed by all webpages so they can
* take advantage of Smarty.
*/
class Controller
{
protected string $template;
private Smarty $smarty;
/** @var string[] */
private array $css_files;
/** @var string[] */
private array $js_files;
private SimpleYamlModel $menuModel;
/**
* Constructor that will create a Smarty object and configure it according
* to what's been specified in config.inc.
*/
public function __construct()
{
/* Create a Smarty object. */
$this->smarty = new Smarty();
global $lang;
global $available_languages;
/* Configure smarty. */
$this->smarty->setCompileDir(SMARTY_DIR_COMPILE);
$this->smarty->setCacheDir(SMARTY_DIR_CACHE);
$this->smarty->setTemplateDir(SMARTY_DIR_TEMPLATE);
$this->smarty->compile_id = $lang;
// First we read English, so all defaults are there
$this->smarty->configLoad(join(DIRECTORY_SEPARATOR, [DIR_DATA, DEFAULT_LOCALE, "strings.ini"]));
// Now we try to read translations
if (is_file(($fname = join(DIRECTORY_SEPARATOR, [DIR_DATA, $lang, "strings.ini"])))
&& is_readable($fname)
) {
$this->smarty->configLoad($fname);
}
/**
* Add a output-filter to make sure ampersands are properly encoded to
* HTML-entities.
*/
$this->smarty->registerFilter('output', array($this, 'outputFilter'));
/* Give Smarty-template access to date(). */
$this->smarty->registerPlugin('modifier', 'date_localized', array(&$this, 'dateLocalizedSmartyModifier'));
$this->smarty->registerPlugin('modifier', 'lang', array(SiteUtils::class, 'localizePath'));
$this->smarty->registerPlugin('modifier', 'download', array(&$this, 'downloadsSmartyModifier'));
$this->smarty->registerPlugin('modifier', 'release', array(&$this, 'releaseSmartyModifier'));
/* Register PHP functions used by pages */
$this->smarty->registerPlugin('modifier', 'htmlspecialchars', '\htmlspecialchars');
$this->smarty->registerPlugin('modifier', 'locale_get_display_language', '\locale_get_display_language');
$this->smarty->registerPlugin('modifier', 'preg_replace', '\preg_replace');
$this->smarty->registerPlugin('modifier', 'rand', '\rand');
$this->smarty->registerPlugin('modifier', 'str_contains', '\str_contains');
$this->smarty->registerPlugin('modifier', 'strpos', '\strpos');
$this->css_files = array();
$this->js_files = array();
$this->menuModel = new SimpleYamlModel("MenuItem", "menus.yaml");
$menus = [];
/* The menus have caused an exception, need to skip them. */
if (!ExceptionHandler::skipMenus()) {
$menus = $this->menuModel->getAllData();
}
// Construct lang URL
$langs = join("|", array_keys($available_languages));
$pageurl = preg_replace("/\/\b($langs)\b\/?/i", '/', $_SERVER['REQUEST_URI']);
// Remove leading /
$pageurl = substr($pageurl, 1);
/* Check RTL */
$rtl = $this->isRtl($available_languages[$lang]);
/* Set up the common variables before displaying. */
$vars = array(
'release' => RELEASE,
'baseurl' => URL_BASE,
'heroes_num' => HEROES_NUM,
'menus' => $menus,
'pageurl' => $pageurl,
'themes' => THEMES,
'available_languages' => $available_languages,
'lang' => $lang,
'rtl' => $rtl,
);
$this->smarty->assign($vars);
}
/**
* Checks whether a locale string is RTL or LTR
*/
private function isRtl(string $localeName): bool
{
$rtl_chars_pattern = '/[\x{0590}-\x{05ff}\x{0600}-\x{06ff}]/u';
return preg_match($rtl_chars_pattern, $localeName) === 1;
}
/**
* Smarty outputfilter, run just before displaying.
*/
public function outputFilter(string $string, Template $smarty): string
{
/* Properly encode all ampersands as "&amp;". */
$string = preg_replace('/&(?!([a-z]+|(#\d+));)/i', '&amp;', $string);
/* Replace weird characters that appears in some of the data. */
return $string;
}
/**
* Formating of dateAs, registered as a modifier for Smarty templates.
*/
public function dateLocalizedSmartyModifier(int $timestamp): string
{
global $lang;
$formatter = new \IntlDateFormatter($lang, \IntlDateFormatter::MEDIUM, \IntlDateFormatter::NONE);
$ret = $formatter->format($timestamp);
if ($ret === false) {
$ret = '';
}
return $ret;
}
/**
* Formating of download URLs, registered as a modifier for Smarty templates.
*/
public function downloadsSmartyModifier(string $path): string
{
if (\strpos($path, "http") === 0) {
return $path;
} elseif (\strpos($path, "/frs") === 0) {
return DOWNLOADS_BASE . $path;
} elseif (\strpos($path, "frs") === 0) {
return DOWNLOADS_BASE . "/$path";
}
return $path;
}
/**
* Formating of version, registered as a modifier for Smarty templates.
*/
public function releaseSmartyModifier(string $string): string
{
$string = preg_replace("/\{[$]?release\}/", RELEASE, $string);
$string = preg_replace("/\{[$]?release_tools\}/", RELEASE_TOOLS, $string);
return $string;
}
/* Render the HTML using the template and any set variables and displays it. */
public function display(string $content): void
{
$vars = array(
'css_files' => $this->css_files,
'js_files' => $this->js_files,
'content' => $content,
);
$this->smarty->assign($vars);
$this->smarty->display('pages/index.tpl');
}
/* Render the HTML using the template and any set variables and returns it. */
/**
* @param ?array<string, mixed> $vars
*/
public function fetch(string $template, ?array $vars = null): string
{
if (!is_null($vars)) {
$this->smarty->assign($vars);
}
return $this->smarty->fetch($template);
}
/* Set up the variables used by the template and render the page. */
/**
* @param ?array<string, mixed> $vars
*/
public function renderPage(?array $vars): void
{
$this->display($this->fetch($this->template, $vars));
}
/* Assign extra CSS files needed by the different pages/templates. */
/**
* @param string|string[] $extra_css
*/
public function addCSSFiles(string|array $extra_css): void
{
if (is_array($extra_css)) {
$this->css_files = array_merge(
$this->css_files,
$extra_css
);
} elseif (strlen($extra_css) > 0) {
$this->css_files[] = $extra_css;
}
}
/* Assign javascripts files needed by the different pages/templates. */
/**
* @param string|string[] $extra_js
*/
public function addJSFiles(string|array $extra_js): void
{
if (is_array($extra_js)) {
$this->js_files = array_merge(
$this->js_files,
$extra_js
);
} elseif (strlen($extra_js) > 0) {
$this->js_files[] = $extra_js;
}
}
protected function getConfigVars(string $title): ?string
{
return $this->smarty->getConfigVars($title);
}
protected function getHeadline(string $body): string
{
$headline = '';
for ($line = \strtok($body, PHP_EOL); $line !== false; $line = \strtok(PHP_EOL)) {
$line = \strip_tags($line);
$headline .= $line . ' ';
if (\strlen($headline) > 250) {
$headline = substr($headline, 0, 249);
$headline .= "\u{2026}";
break;
}
}
return $headline;
}
}