mirror of
https://github.com/LBRYFoundation/lbry.com.git
synced 2025-08-23 09:37:26 +00:00
216 lines
6.8 KiB
PHP
216 lines
6.8 KiB
PHP
<?php
|
|
|
|
function js_start()
|
|
{
|
|
ob_start('Response::jsOutputCallback');
|
|
}
|
|
|
|
function js_end()
|
|
{
|
|
ob_end_flush();
|
|
}
|
|
|
|
class View
|
|
{
|
|
const LAYOUT_PARAMS = '_layout_params';
|
|
|
|
const WEB_DIR = ROOT_DIR . '/web';
|
|
const COMPONENTS_DIR = self::WEB_DIR . '/components/sass';
|
|
const COLORS_DIR = self::WEB_DIR . '/scss/color';
|
|
const SCSS_DIR = self::WEB_DIR . '/scss';
|
|
const CSS_DIR = self::WEB_DIR . '/css';
|
|
const JS_DIR = self::WEB_DIR . '/js';
|
|
|
|
public static function render($template, array $vars = []): string
|
|
{
|
|
if (static::isMarkdown($template)) {
|
|
return static::markdownToHtml(static::getFullPath($template));
|
|
}
|
|
|
|
if (!static::exists($template) || substr_count($template, '/') !== 1) {
|
|
throw new InvalidArgumentException(sprintf('The template "%s" does not exist or is unreadable.', $template));
|
|
}
|
|
|
|
list($module, $view) = explode('/', $template);
|
|
|
|
$isPartial = $view[0] === '_';
|
|
$actionClass = ucfirst($module) . 'Actions';
|
|
$method = 'prepare' . ucfirst(ltrim($view, '_')) . ($isPartial ? 'Partial' : '');
|
|
|
|
if (method_exists($actionClass, $method)) {
|
|
$vars = $actionClass::$method($vars);
|
|
}
|
|
|
|
if ($vars === null) {
|
|
return [];
|
|
}
|
|
|
|
return static::interpolateTokens(static::getTemplateSafely($template, $vars));
|
|
}
|
|
|
|
/**
|
|
* This is its own function because we don't want in-scope variables to leak into the template
|
|
*
|
|
* @param string $___template
|
|
* @param array $___vars
|
|
*
|
|
* @return string
|
|
* @throws Throwable
|
|
*/
|
|
protected static function getTemplateSafely(string $___template, array $___vars): string
|
|
{
|
|
extract($___vars);
|
|
ob_start();
|
|
ob_implicit_flush(0);
|
|
|
|
try {
|
|
require(static::getFullPath($___template));
|
|
return ob_get_clean();
|
|
} catch (Throwable $e) {
|
|
// need to end output buffering before throwing the exception
|
|
ob_end_clean();
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
public static function markdownToHtml($path): string
|
|
{
|
|
return ParsedownExtra::instance()->text(trim(file_get_contents($path)));
|
|
}
|
|
|
|
public static function exists($template): bool
|
|
{
|
|
return is_readable(static::getFullPath($template));
|
|
}
|
|
|
|
protected static function isMarkdown($nameOrPath): bool
|
|
{
|
|
return strlen($nameOrPath) > 3 && substr($nameOrPath, -3) == '.md';
|
|
}
|
|
|
|
protected static function getFullPath($template): string
|
|
{
|
|
if ($template && $template[0] == '/') {
|
|
return $template;
|
|
}
|
|
|
|
if (static::isMarkdown($template)) {
|
|
return ContentActions::CONTENT_DIR . '/' . $template;
|
|
}
|
|
|
|
return ROOT_DIR . '/view/template/' . $template . '.php';
|
|
}
|
|
|
|
public static function imagePath($image): string
|
|
{
|
|
return '/img/' . $image;
|
|
}
|
|
|
|
public static function parseMarkdown($template): array
|
|
{
|
|
$path = static::getFullPath($template);
|
|
list($ignored, $frontMatter, $markdown) = explode('---', file_get_contents($path), 3);
|
|
$metadata = Spyc::YAMLLoadString(trim($frontMatter));
|
|
$html = ParsedownExtra::instance()->text(trim($markdown));
|
|
return [$metadata, $html];
|
|
}
|
|
|
|
public static function compileCss()
|
|
{
|
|
$scssCompiler = new \scssphp\ScssPhp\Compiler();
|
|
|
|
$scssCompiler->setImportPaths([self::COMPONENTS_DIR, self::COLORS_DIR, self::SCSS_DIR]);
|
|
|
|
$compress = true;
|
|
if ($compress) {
|
|
$scssCompiler->setFormatter('scssphp\ScssPhp\Formatter\Crunched');
|
|
} else {
|
|
$scssCompiler->setFormatter('scssphp\ScssPhp\Formatter\Expanded');
|
|
$scssCompiler->setLineNumberStyle(scssphp\ScssPhp\Compiler::LINE_COMMENTS);
|
|
}
|
|
|
|
$all_css = $scssCompiler->compile(file_get_contents(self::SCSS_DIR . '/all.scss'));
|
|
file_put_contents(self::CSS_DIR . '/all.css', $all_css);
|
|
}
|
|
|
|
public static function gzipAssets()
|
|
{
|
|
foreach ([self::CSS_DIR => 'css', self::JS_DIR => 'js'] as $dir => $ext) {
|
|
foreach (glob("$dir/*.$ext") as $file) {
|
|
Gzip::compressFile($file);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected static function interpolateTokens($html): string
|
|
{
|
|
return preg_replace_callback('/{{[\w\.]+}}/is', function ($m) {
|
|
return i18n::translate(trim($m[0], '}{'));
|
|
}, $html);
|
|
}
|
|
|
|
protected static function escapeOnce($value): string
|
|
{
|
|
return preg_replace('/&([a-z]+|(#\d+)|(#x[\da-f]+));/i', '&$1;', htmlspecialchars((string)$value, ENT_QUOTES, 'utf-8'));
|
|
}
|
|
|
|
protected static function attributesToHtml($attributes): string
|
|
{
|
|
return implode('', array_map(function ($k, $v) {
|
|
return $v === true ? " $k" : ($v === false || $v === null || ($v === '' && $k != 'value') ? '' : sprintf(' %s="%s"', $k, static::escapeOnce($v)));
|
|
}, array_keys($attributes), array_values($attributes)));
|
|
}
|
|
|
|
public static function renderTag($tag, $attributes = []): string
|
|
{
|
|
return $tag ? sprintf('<%s%s />', $tag, static::attributesToHtml($attributes)) : '';
|
|
}
|
|
|
|
public static function renderContentTag($tag, $content = null, $attributes = []): string
|
|
{
|
|
return $tag ? sprintf('<%s%s>%s</%s>', $tag, static::attributesToHtml($attributes), $content, $tag) : '';
|
|
}
|
|
|
|
public static function renderJson($data): array
|
|
{
|
|
Response::setHeader(Response::HEADER_CONTENT_TYPE, 'application/json');
|
|
return ['internal/json', ['json' => $data, '_no_layout' => true]];
|
|
}
|
|
|
|
public static function safeExternalLinks(string $html, string $domain): string
|
|
{
|
|
try {
|
|
$parser = new Masterminds\HTML5();
|
|
$dom = $parser->loadHTML($html);
|
|
$links = $dom->getElementsByTagName('body') ?
|
|
$dom->getElementsByTagName('body')[0]->getElementsByTagName('a') :
|
|
$dom->getElementsByTagName('a');
|
|
|
|
foreach ($links as $link) {
|
|
if ($link->getAttribute('href') && static::isLinkExternal($link->getAttribute('href'), $domain)) {
|
|
$link->setAttribute('rel', "noopener");
|
|
}
|
|
}
|
|
|
|
return $parser->saveHTML($dom);
|
|
}
|
|
catch (Error $e) {
|
|
Slack::slackGrin();
|
|
return $html;
|
|
}
|
|
}
|
|
|
|
public static function isLinkExternal(string $url, string $domain): bool
|
|
{
|
|
$components = parse_url(strtolower($url));
|
|
$domain = strtolower($domain);
|
|
|
|
return $url
|
|
&&
|
|
!empty($components['host']) // relative urls are not external
|
|
&&
|
|
$components['host'] !== $domain
|
|
&&
|
|
mb_substr($components['host'], -mb_strlen('.' . $domain)) !== '.' . $domain;
|
|
}
|
|
}
|