add response class, caching

This commit is contained in:
Alex Grintsvayg 2016-09-01 18:48:05 -04:00
parent 934c0d92ef
commit 77480f029f
16 changed files with 354 additions and 137 deletions

View file

@ -34,6 +34,6 @@ class Actions
'okhttp', 'python' 'okhttp', 'python'
]; ];
return preg_match('/(' . join('|', $bots) . ')/i', $_SERVER['HTTP_USER_AGENT']); return preg_match('/(' . join('|', $bots) . ')/i', Request::getUserAgent());
} }
} }

View file

@ -2,8 +2,6 @@
class Controller class Controller
{ {
const HEADER_STATUS = 'Status';
public static function dispatch($uri) public static function dispatch($uri)
{ {
try try
@ -11,29 +9,19 @@ class Controller
$viewAndParams = static::execute($uri); $viewAndParams = static::execute($uri);
$viewTemplate = $viewAndParams[0]; $viewTemplate = $viewAndParams[0];
$viewParameters = isset($viewAndParams[1]) ? $viewAndParams[1] : []; $viewParameters = isset($viewAndParams[1]) ? $viewAndParams[1] : [];
$headers = isset($viewAndParams[2]) ? $viewAndParams[2] : []; if (!IS_PRODUCTION && isset($viewAndParams[2]))
$defaultHeaders = [
'Content-Security-Policy' => "frame-ancestors 'none'",
'X-Frame-Options' => 'DENY',
'X-XSS-Protection'=> '1',
];
if (IS_PRODUCTION)
{ {
$defaultHeaders['Strict-Transport-Security'] = 'max-age=31536000'; throw new Exception('use response::setheader instead of returning headers');
} }
static::sendHeaders(array_merge($defaultHeaders, $headers));
if ($viewTemplate === null) if ($viewTemplate === null)
{ {
return ''; return;
} }
if (!$viewTemplate) if (!$viewTemplate)
{ {
throw new LogicException('All execute methods must return a template.'); throw new LogicException('All execute methods must return a template or NULL.');
} }
$layout = !(isset($viewParameters['_no_layout']) && $viewParameters['_no_layout']); $layout = !(isset($viewParameters['_no_layout']) && $viewParameters['_no_layout']);
@ -44,7 +32,9 @@ class Controller
$content = View::render($viewTemplate, $viewParameters + ['fullPage' => true]); $content = View::render($viewTemplate, $viewParameters + ['fullPage' => true]);
echo $layout ? View::render('layout/basic', ['content' => $content] + $layoutParams) : $content; Response::setContent($layout ? View::render('layout/basic', ['content' => $content] + $layoutParams) : $content);
Response::setDefaultSecurityHeaders();
Response::send();
} }
catch (StopException $e) catch (StopException $e)
{ {
@ -99,6 +89,7 @@ class Controller
$newsPattern = '#^' . ContentActions::URL_NEWS . '(/|$)#'; $newsPattern = '#^' . ContentActions::URL_NEWS . '(/|$)#';
if (preg_match($newsPattern, $uri)) if (preg_match($newsPattern, $uri))
{ {
Response::enableHttpCache(180);
$slug = preg_replace($newsPattern, '', $uri); $slug = preg_replace($newsPattern, '', $uri);
if ($slug == ContentActions::RSS_SLUG) if ($slug == ContentActions::RSS_SLUG)
{ {
@ -110,6 +101,7 @@ class Controller
$faqPattern = '#^' . ContentActions::URL_FAQ . '(/|$)#'; $faqPattern = '#^' . ContentActions::URL_FAQ . '(/|$)#';
if (preg_match($faqPattern, $uri)) if (preg_match($faqPattern, $uri))
{ {
Response::enableHttpCache(180);
$slug = preg_replace($faqPattern, '', $uri); $slug = preg_replace($faqPattern, '', $uri);
return $slug ? ContentActions::executeFaqPost($uri) : ContentActions::executeFaq(); return $slug ? ContentActions::executeFaqPost($uri) : ContentActions::executeFaq();
} }
@ -117,6 +109,7 @@ class Controller
$bountyPattern = '#^' . BountyActions::URL_BOUNTY_LIST . '(/|$)#'; $bountyPattern = '#^' . BountyActions::URL_BOUNTY_LIST . '(/|$)#';
if (preg_match($bountyPattern, $uri)) if (preg_match($bountyPattern, $uri))
{ {
Response::enableHttpCache(180);
$slug = preg_replace($bountyPattern, '', $uri); $slug = preg_replace($bountyPattern, '', $uri);
return $slug ? BountyActions::executeShow($uri) : BountyActions::executeList($uri); return $slug ? BountyActions::executeShow($uri) : BountyActions::executeList($uri);
} }
@ -131,11 +124,13 @@ class Controller
$noSlashUri = ltrim($uri, '/'); $noSlashUri = ltrim($uri, '/');
if (View::exists('page/' . $noSlashUri)) if (View::exists('page/' . $noSlashUri))
{ {
Response::enableHttpCache(180);
return ['page/' . $noSlashUri, []]; return ['page/' . $noSlashUri, []];
} }
else else
{ {
return ['page/404', [], [static::HEADER_STATUS => 404]]; Response::setStatus(404);
return ['page/404', []];
} }
} }
@ -148,88 +143,13 @@ class Controller
$url = str_replace('&', '&', $url); $url = str_replace('&', '&', $url);
$headers = [static::HEADER_STATUS => $statusCode]; Response::setStatus($statusCode);
if ($statusCode == 201 || ($statusCode >= 300 && $statusCode < 400)) if ($statusCode == 201 || ($statusCode >= 300 && $statusCode < 400))
{ {
$headers['Location'] = $url; Response::setHeader(Response::HEADER_LOCATION, $url);
} }
return ['internal/redirect', ['url' => $url], $headers]; return ['internal/redirect', ['url' => $url]];
}
protected static function sendHeaders(array $headers)
{
if (isset($headers[static::HEADER_STATUS]))
{
$status = 'HTTP/1.0 ' . $headers[static::HEADER_STATUS] . ' ' . static::getStatusTextForCode($headers[static::HEADER_STATUS]);
header($status);
if (substr(php_sapi_name(), 0, 3) == 'cgi')
{
// fastcgi servers cannot send this status information because it was sent by them already due to the HTT/1.0 line
// so we can safely unset them. see ticket #3191
unset($headers[static::HEADER_STATUS]);
}
}
foreach($headers as $name => $value)
{
header($name . ': ' . $value);
}
}
public static function getStatusTextForCode($code)
{
$statusTexts = [
'100' => 'Continue',
'101' => 'Switching Protocols',
'200' => 'OK',
'201' => 'Created',
'202' => 'Accepted',
'203' => 'Non-Authoritative Information',
'204' => 'No Content',
'205' => 'Reset Content',
'206' => 'Partial Content',
'300' => 'Multiple Choices',
'301' => 'Moved Permanently',
'302' => 'Found',
'303' => 'See Other',
'304' => 'Not Modified',
'305' => 'Use Proxy',
'306' => '(Unused)',
'307' => 'Temporary Redirect',
'400' => 'Bad Request',
'401' => 'Unauthorized',
'402' => 'Payment Required',
'403' => 'Forbidden',
'404' => 'Not Found',
'405' => 'Method Not Allowed',
'406' => 'Not Acceptable',
'407' => 'Proxy Authentication Required',
'408' => 'Request Timeout',
'409' => 'Conflict',
'410' => 'Gone',
'411' => 'Length Required',
'412' => 'Precondition Failed',
'413' => 'Request Entity Too Large',
'414' => 'Request-URI Too Long',
'415' => 'Unsupported Media Type',
'416' => 'Requested Range Not Satisfiable',
'417' => 'Expectation Failed',
'419' => 'Authentication Timeout',
'422' => 'Unprocessable Entity',
'426' => 'Upgrade Required',
'429' => 'Too Many Requests',
'500' => 'Internal Server Error',
'501' => 'Not Implemented',
'502' => 'Bad Gateway',
'503' => 'Service Unavailable',
'504' => 'Gateway Timeout',
'505' => 'HTTP Version Not Supported',
];
return isset($statusTexts[$code]) ? $statusTexts[$code] : null;
} }
} }

View file

@ -0,0 +1,59 @@
<?php
class Request
{
const GET = 'GET';
const POST = 'POST';
const HEAD = 'HEAD';
const OPTIONS = 'OPTIONS';
protected static $method;
public static function getMethod(): string
{
if (!static::$method)
{
$method = isset($_SERVER['REQUEST_METHOD']) ? strtoupper($_SERVER['REQUEST_METHOD']) : null;
static::$method = in_array($method, [static::GET, static::POST, static::HEAD, static::OPTIONS]) ? $method : static::GET;
}
return static::$method;
}
public static function isGet(): bool
{
return static::getMethod() == static::GET;
}
public static function isPost(): bool
{
return static::getMethod() == static::POST;
}
public static function isCacheableMethod(): bool
{
return in_array(static::getMethod(), [static::GET, static::HEAD]);
}
public static function getOriginalIp(): string
{
return isset($_SERVER['HTTP_X_FORWARDED_FOR']) ?
$_SERVER['HTTP_X_FORWARDED_FOR'] :
(isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '');
}
public static function getUserAgent(): string
{
return isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
}
public static function getHost(): string
{
// apparently trailing period is legal: http://www.dns-sd.org/TrailingDotsInDomainNames.html
return isset($_SERVER['HTTP_HOST']) ? rtrim($_SERVER['HTTP_HOST'], '.') : '';
}
public static function getRelativeUri(): string
{
return isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
}
}

View file

@ -10,6 +10,7 @@ class ContentActions extends Actions
public static function executeHome(): array public static function executeHome(): array
{ {
Response::enableHttpCache(180);
return ['page/home', [ return ['page/home', [
'totalUSD' => CreditApi::getTotalDollarSales(), 'totalUSD' => CreditApi::getTotalDollarSales(),
'totalPeople' => CreditApi::getTotalPeople() 'totalPeople' => CreditApi::getTotalPeople()
@ -70,11 +71,10 @@ class ContentActions extends Actions
public static function executeRss(): array public static function executeRss(): array
{ {
$posts = Post::find(static::VIEW_FOLDER_NEWS, Post::SORT_DATE_DESC); $posts = Post::find(static::VIEW_FOLDER_NEWS, Post::SORT_DATE_DESC);
Response::setHeader(Response::HEADER_CONTENT_TYPE, 'text/xml; charset=utf-8');
return ['content/rss', [ return ['content/rss', [
'posts' => array_slice($posts, 0, 10), 'posts' => array_slice($posts, 0, 10),
'_no_layout' => true '_no_layout' => true
], [
'Content-Type' => 'text/xml; charset=utf-8'
]]; ]];
} }
@ -161,14 +161,12 @@ class ContentActions extends Actions
$zip->close(); $zip->close();
Response::enableHttpCache(180);
Response::setDownloadHttpHeaders($zipFileName, 'application/zip', filesize($zipPath));
return ['internal/zip', [ return ['internal/zip', [
'_no_layout' => true, '_no_layout' => true,
'zipPath' => $zipPath 'zipPath' => $zipPath
], [
'Content-Disposition' => 'attachment;filename=' . $zipFileName,
'X-Content-Type-Options' => 'nosniff',
'Content-Type' => 'application/zip',
'Content-Length' => filesize($zipPath),
]]; ]];
} }

View file

@ -169,7 +169,7 @@ class DownloadActions extends Actions
protected static function guessOs() protected static function guessOs()
{ {
//if exact OS is requested, use that //if exact OS is requested, use that
$uri = strtok($_SERVER['REQUEST_URI'], '?'); $uri = strtok(Request::getRelativeUri(), '?');
foreach (static::getOses() as $os => $osChoice) foreach (static::getOses() as $os => $osChoice)
{ {
if ($osChoice[0] == $uri) if ($osChoice[0] == $uri)
@ -184,7 +184,7 @@ class DownloadActions extends Actions
} }
//otherwise guess from UA //otherwise guess from UA
$ua = $_SERVER['HTTP_USER_AGENT']; $ua = Request::getUserAgent();
if (stripos($ua, 'OS X') !== false) if (stripos($ua, 'OS X') !== false)
{ {
return strpos($ua, 'iPhone') !== false || stripos($ua, 'iPad') !== false ? static::OS_IOS : static::OS_OSX; return strpos($ua, 'iPhone') !== false || stripos($ua, 'iPad') !== false ? static::OS_IOS : static::OS_OSX;

View file

@ -11,7 +11,7 @@ class MailActions extends Actions
{ {
$nextUrl = isset($_POST['returnUrl']) && $_POST['returnUrl'] ? $_POST['returnUrl'] : '/join-list'; $nextUrl = isset($_POST['returnUrl']) && $_POST['returnUrl'] ? $_POST['returnUrl'] : '/join-list';
if ($_SERVER['REQUEST_METHOD'] !== 'POST') if (!Request::isPost())
{ {
return Controller::redirect($nextUrl); return Controller::redirect($nextUrl);
} }
@ -62,7 +62,7 @@ class MailActions extends Actions
public static function prepareJoinListPartial(array $vars) public static function prepareJoinListPartial(array $vars)
{ {
$vars['listSig'] = md5(serialize($vars)); $vars['listSig'] = md5(serialize($vars));
$vars += ['btnClass' => 'btn-primary', 'returnUrl' => $_SERVER['REQUEST_URI']]; $vars += ['btnClass' => 'btn-primary', 'returnUrl' => Request::getRelativeUri()];
if (Session::get(Session::KEY_LIST_SUB_SIGNATURE) == $vars['listSig']) if (Session::get(Session::KEY_LIST_SUB_SIGNATURE) == $vars['listSig'])
{ {

View file

@ -16,7 +16,7 @@ class NavActions extends Actions
public static function getNavUri() public static function getNavUri()
{ {
return static::$navUri ?: $_SERVER['REQUEST_URI']; return static::$navUri ?: Request::getRelativeUri();
} }
public static function prepareFooterPartial(array $vars) public static function prepareFooterPartial(array $vars)

View file

@ -24,7 +24,7 @@ class i18n
{ {
if ($culture === null) if ($culture === null)
{ {
$urlTokens = $_SERVER['HTTP_HOST'] ? explode('.', $_SERVER['HTTP_HOST']) : []; $urlTokens = Request::getHost() ? explode('.', Request::getHost()) : [];
$code = $urlTokens ? reset($urlTokens) : 'en'; $code = $urlTokens ? reset($urlTokens) : 'en';
switch($code) switch($code)
{ {

View file

@ -61,8 +61,8 @@ class Prefinery
if (!$user) if (!$user)
{ {
// dont record ip for lbry.io addresses, for testing // dont record ip for lbry.io addresses, for testing
$ip = isset($_SERVER['REMOTE_ADDR']) && !preg_match('/@lbry\.io$/', $email) ? $_SERVER['REMOTE_ADDR'] : null; $ip = !preg_match('/@lbry\.io$/', $email) ? Request::getOriginalIp() : null;
$ua = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null; $ua = Request::getUserAgent();
$user = Prefinery::createTester(array_filter([ $user = Prefinery::createTester(array_filter([
'email' => $email, 'email' => $email,
'status' => $inviteCode ? static::STATE_ACTIVE: static::STATE_APPLIED, # yes, has to be ACTIVE to validate invite code 'status' => $inviteCode ? static::STATE_ACTIVE: static::STATE_APPLIED, # yes, has to be ACTIVE to validate invite code

View file

@ -13,7 +13,7 @@ class Slack
$slackErrorNotificationUrl = Config::get('slack_error_notification_url'); $slackErrorNotificationUrl = Config::get('slack_error_notification_url');
if ($slackErrorNotificationUrl) if ($slackErrorNotificationUrl)
{ {
Curl::post($slackErrorNotificationUrl, ['text' => '<!everyone> ' . $_SERVER['REQUEST_URI'] . "\n" . $e], ['json_data' => true]); Curl::post($slackErrorNotificationUrl, ['text' => '<!everyone> ' . Request::getRelativeUri() . "\n" . $e], ['json_data' => true]);
} }
} }
} }

View file

@ -276,7 +276,7 @@ class Post
$cover = $this->getCover(); $cover = $this->getCover();
if ($cover) if ($cover)
{ {
$urls[] = 'https://' . $_SERVER['SERVER_NAME'] . '/img/blog-covers/' . $cover; $urls[] = 'https://' . Request::getHost() . '/img/blog-covers/' . $cover;
} }
$matches = []; $matches = [];

View file

@ -1,4 +1,5 @@
<?php <?php
/** /**
* Description of Response * Description of Response
* *
@ -6,18 +7,36 @@
*/ */
class Response class Response
{ {
protected static $metaDescription = '', const HEADER_STATUS = 'Status';
$metaTitle = '', const HEADER_LOCATION = 'Location';
$jsCalls = [],
$assets = [ const HEADER_CACHE_CONTROL = 'Cache-Control';
'js' => [ const HEADER_LAST_MODIFIED = 'Last-Modified';
'/js/jquery-2.1.3.min.js', const HEADER_ETAG = 'Etag';
'/js/global.js'
], const HEADER_CONTENT_TYPE = 'Content-Type';
'css' => [] const HEADER_CONTENT_LENGTH = 'Content-Length';
], const HEADER_CONTENT_DISPOSITION = 'Content-Disposition';
const HEADER_CONTENT_TYPE_OPTIONS = 'X-Content-Type-Options';
protected static
$metaDescription = '',
$metaTitle = '',
$jsCalls = [],
$assets = [
'js' => [
'/js/jquery-2.1.3.min.js',
'/js/global.js'
],
'css' => []
],
$headers = [],
$headersSent = false,
$content = '',
$contentSent = false,
$isHeadersOnly = false,
// $bodyCssClasses = [], // $bodyCssClasses = [],
$metaImages = []; $metaImages = [];
public static function setMetaDescription($description) public static function setMetaDescription($description)
{ {
@ -31,7 +50,7 @@ class Response
public static function addMetaImages(array $urls) public static function addMetaImages(array $urls)
{ {
foreach($urls as $url) foreach ($urls as $url)
{ {
static::addMetaImage($url); static::addMetaImage($url);
} }
@ -61,7 +80,7 @@ class Response
{ {
$title = ''; $title = '';
preg_match_all('/<h(1|2)[^>]*>([^<]+)</', $content, $titleMatches); preg_match_all('/<h(1|2)[^>]*>([^<]+)</', $content, $titleMatches);
foreach($titleMatches[1] as $matchIndex => $headerValue) foreach ($titleMatches[1] as $matchIndex => $headerValue)
{ {
if ($headerValue == '1' || !$title) if ($headerValue == '1' || !$title)
{ {
@ -96,6 +115,225 @@ class Response
return static::$assets['js']; return static::$assets['js'];
} }
public static function send()
{
static::sendHeaders();
static::sendContent();
}
public static function setContent(string $content)
{
static::$content = $content;
}
public static function getContent(): string
{
return static::$content;
}
public static function sendContent()
{
if (static::$contentSent)
{
throw new LogicException('Content has already been sent. It cannot be sent twice');
}
if (!static::$isHeadersOnly)
{
echo static::$content;
}
static::$contentSent = true;
}
public static function setIsHeadersOnly(bool $isHeadersOnly = true)
{
static::$isHeadersOnly = $isHeadersOnly;
}
public static function setDownloadHttpHeaders($name, $type = null, $size = null, $noSniff = true)
{
static::setHeaders(array_filter([
'Content-Disposition' => 'attachment;filename=' . $name,
'Content-Type' => $type ? 'application/zip' : null,
'Content-Length' => $size ?: null,
'X-Content-Type-Options' => $noSniff ? 'nosniff' : null,
]));
}
public static function setContentEtag()
{
static::setHeader(static::HEADER_ETAG, md5(static::getContent()));
}
public static function enableHttpCache(int $seconds = 300)
{
static::addCacheControlHeader('max-age', $seconds);
}
public static function addCacheControlHeader(string $name, $value = null)
{
$cacheControl = static::getHeader(static::HEADER_CACHE_CONTROL);
$currentHeaders = [];
if ($cacheControl)
{
foreach (preg_split('/\s*,\s*/', $cacheControl) as $tmp)
{
$tmp = explode('=', $tmp);
$currentHeaders[$tmp[0]] = isset($tmp[1]) ? $tmp[1] : null;
}
}
$currentHeaders[strtr(strtolower($name), '_', '-')] = $value;
$headers = [];
foreach ($currentHeaders as $key => $currentVal)
{
$headers[] = $key . ($currentVal !== null ? '=' . $currentVal : '');
}
static::setHeader(static::HEADER_CACHE_CONTROL, implode(', ', $headers));
}
public static function setHeader($name, $value)
{
static::$headers[$name] = $value;
}
public static function setHeaders($headers, $overwrite = true)
{
foreach ($headers as $name => $value)
{
if ($overwrite || !static::getHeader($name))
{
static::setHeader($name, $value);
}
}
}
public static function getHeader($name, $default = null)
{
return isset(static::$headers[$name]) ? static::$headers[$name] : $default;
}
public static function getHeaders(): array
{
return static::$headers;
}
public static function setStatus($status)
{
static::setHeader(static::HEADER_STATUS, $status);
}
public static function setDefaultSecurityHeaders()
{
$defaultHeaders = [
'Content-Security-Policy' => "frame-ancestors 'none'",
'X-Frame-Options' => 'DENY',
'X-XSS-Protection' => '1',
];
if (IS_PRODUCTION)
{
$defaultHeaders['Strict-Transport-Security'] = 'max-age=31536000';
}
static::setHeaders($defaultHeaders, false);
}
public static function sendHeaders()
{
if (static::$headersSent)
{
throw new LogicException('Headers have already been sent. They cannot be sent twice');
}
$headers = static::getHeaders();
if (isset($headers[static::HEADER_STATUS]))
{
$status = 'HTTP/1.0 ' . $headers[static::HEADER_STATUS] . ' ' . static::getStatusTextForCode($headers[static::HEADER_STATUS]);
header($status);
if (substr(php_sapi_name(), 0, 3) == 'cgi')
{
// fastcgi servers cannot send this status information because it was sent by them already due to the HTT/1.0 line
// so we can safely unset them. see ticket #3191
unset($headers[static::HEADER_STATUS]);
}
}
foreach ($headers as $name => $value)
{
header($name . ': ' . $value);
}
static::$headersSent = true;
}
public static function getStatusTextForCode($code)
{
$statusTexts = [
'100' => 'Continue',
'101' => 'Switching Protocols',
'200' => 'OK',
'201' => 'Created',
'202' => 'Accepted',
'203' => 'Non-Authoritative Information',
'204' => 'No Content',
'205' => 'Reset Content',
'206' => 'Partial Content',
'300' => 'Multiple Choices',
'301' => 'Moved Permanently',
'302' => 'Found',
'303' => 'See Other',
'304' => 'Not Modified',
'305' => 'Use Proxy',
'306' => '(Unused)',
'307' => 'Temporary Redirect',
'400' => 'Bad Request',
'401' => 'Unauthorized',
'402' => 'Payment Required',
'403' => 'Forbidden',
'404' => 'Not Found',
'405' => 'Method Not Allowed',
'406' => 'Not Acceptable',
'407' => 'Proxy Authentication Required',
'408' => 'Request Timeout',
'409' => 'Conflict',
'410' => 'Gone',
'411' => 'Length Required',
'412' => 'Precondition Failed',
'413' => 'Request Entity Too Large',
'414' => 'Request-URI Too Long',
'415' => 'Unsupported Media Type',
'416' => 'Requested Range Not Satisfiable',
'417' => 'Expectation Failed',
'419' => 'Authentication Timeout',
'422' => 'Unprocessable Entity',
'426' => 'Upgrade Required',
'429' => 'Too Many Requests',
'500' => 'Internal Server Error',
'501' => 'Not Implemented',
'502' => 'Bad Gateway',
'503' => 'Service Unavailable',
'504' => 'Gateway Timeout',
'505' => 'HTTP Version Not Supported',
];
return isset($statusTexts[$code]) ? $statusTexts[$code] : null;
}
protected static function normalizeHeaderName($name)
{
return preg_replace_callback(
'/\-(.)/',
function ($matches) { return '-' . strtoupper($matches[1]); },
strtr(ucfirst(strtolower($name)), '_', '-')
);
}
// public static function addBodyCssClass($classOrClasses) // public static function addBodyCssClass($classOrClasses)
// { // {
// static::$bodyCssClasses = array_unique(array_merge(static::$bodyCssClasses, (array)$classOrClasses)); // static::$bodyCssClasses = array_unique(array_merge(static::$bodyCssClasses, (array)$classOrClasses));

View file

@ -78,17 +78,17 @@ class View
return ParsedownExtra::instance()->text(trim(file_get_contents($path))); return ParsedownExtra::instance()->text(trim(file_get_contents($path)));
} }
public static function exists($template) public static function exists($template): bool
{ {
return is_readable(static::getFullPath($template)); return is_readable(static::getFullPath($template));
} }
protected static function isMarkdown($nameOrPath) protected static function isMarkdown($nameOrPath): bool
{ {
return strlen($nameOrPath) > 3 && substr($nameOrPath, -3) == '.md'; return strlen($nameOrPath) > 3 && substr($nameOrPath, -3) == '.md';
} }
protected static function getFullPath($template) protected static function getFullPath($template): string
{ {
if ($template && $template[0] == '/') if ($template && $template[0] == '/')
{ {
@ -103,12 +103,12 @@ class View
return ROOT_DIR . '/view/template/' . $template . '.php'; return ROOT_DIR . '/view/template/' . $template . '.php';
} }
public static function imagePath($image) public static function imagePath($image): string
{ {
return '/img/' . $image; return '/img/' . $image;
} }
public static function parseMarkdown($template) public static function parseMarkdown($template): array
{ {
$path = static::getFullPath($template); $path = static::getFullPath($template);
list($ignored, $frontMatter, $markdown) = explode('---', file_get_contents($path), 3); list($ignored, $frontMatter, $markdown) = explode('---', file_get_contents($path), 3);

View file

@ -1,5 +1,6 @@
<?php $error = isset($error) ? $error : null ?> <?php $error = isset($error) ? $error : null ?>
<form action="/list-subscribe" method="post" novalidate> <form action="/list-subscribe" method="post" novalidate>
<?php if ($error): ?> <?php if ($error): ?>
<div class="notice notice-error spacer1"><?php echo $error ?></div> <div class="notice notice-error spacer1"><?php echo $error ?></div>
<?php elseif ($success): ?> <?php elseif ($success): ?>
@ -10,13 +11,14 @@
<?php js_end() ?> <?php js_end() ?>
<div class="notice notice-success spacer1"><?php echo $success ?></div> <div class="notice notice-success spacer1"><?php echo $success ?></div>
<?php endif ?> <?php endif ?>
<?php if ($error || !$success): ?> <?php if ($error || !$success): ?>
<div class="mail-submit"> <div class="mail-submit">
<input type="hidden" name="returnUrl" value="<?php echo $returnUrl ?>"/> <input type="hidden" name="returnUrl" value="<?php echo $returnUrl ?>"/>
<input type="hidden" name="listId" value="<?php echo $listId ?>"/> <input type="hidden" name="listId" value="<?php echo $listId ?>"/>
<input type="hidden" name="listSig" value="<?php echo $listSig ?>"/> <input type="hidden" name="listSig" value="<?php echo $listSig ?>"/>
<input type="email" value="" name="email" class="required email standard" placeholder= "<?php echo __('email.placeholder') ?>"> <input type="email" value="" name="email" class="required email standard" placeholder= "<?php echo __('email.placeholder') ?>">
<input type="submit" value="<?php echo isset($submitLabel) ? $submitLabel : __('email.subs') ?>" name="subscribe" id="mc-embedded-subscribe" class="<?php echo $btnClass ?>"> <input type="submit" value="<?php echo isset($submitLabel) ? $submitLabel : __('email.subs') ?>" name="subscribe" class="<?php echo $btnClass ?>">
<?php if (isset($fbEvent)): ?> <?php if (isset($fbEvent)): ?>
<input type="hidden" name="fbEvent" value="<?php echo $fbEvent ?>" /> <input type="hidden" name="fbEvent" value="<?php echo $fbEvent ?>" />
<?php endif ?> <?php endif ?>

View file

@ -23,13 +23,13 @@
<div class="span6"> <div class="span6">
<h3><?php echo __('publish.keepl') ?></h3> <h3><?php echo __('publish.keepl') ?></h3>
<ul> <ul>
<?php if ($_SERVER['REQUEST_URI'] != '/what'): ?> <?php if (Request::getRelativeUri() != '/what'): ?>
<li>Read "<a href="/what" class="link-primary">Art in the Internet Age</a>", an introductory essay.</li> <li>Read "<a href="/what" class="link-primary">Art in the Internet Age</a>", an introductory essay.</li>
<?php endif ?> <?php endif ?>
<?php if ($_SERVER['REQUEST_URI'] != '/team'): ?> <?php if (Request::getRelativeUri() != '/team'): ?>
<li>Find out about <a href="/team" class="link-primary">the team behind LBRY</a>.</li> <li>Find out about <a href="/team" class="link-primary">the team behind LBRY</a>.</li>
<?php endif ?> <?php endif ?>
<?php if (strpos($_SERVER['REQUEST_URI'], '/news') === false): ?> <?php if (strpos(Request::getRelativeUri(), '/news') === false): ?>
<li>Check out the latest <a href="/news" class="link-primary">news</a>.</li> <li>Check out the latest <a href="/news" class="link-primary">news</a>.</li>
<?php endif ?> <?php endif ?>
</ul> </ul>

View file

@ -21,7 +21,7 @@ try
{ {
View::compileCss(); View::compileCss();
} }
Controller::dispatch(strtok($_SERVER['REQUEST_URI'], '?')); Controller::dispatch(strtok(Request::getRelativeUri(), '?'));
} }
catch(Throwable $e) catch(Throwable $e)
{ {