Version 4.5.0
This commit is contained in:
1 parent
1a0c7fd3c2
commit
f79dcf067a
3791 files changed
+248032
-76372
No files matched your search
+361
-125
@@ -11,7 +11,7 @@
|
||||
namespace IPS;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
@@ -24,7 +24,7 @@ class _Output
|
||||
{
|
||||
/**
|
||||
* @brief HTTP Statuses
|
||||
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
||||
* @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">RFC 2616</a>
|
||||
*/
|
||||
public static $httpStatuses = array( 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', 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', 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' );
|
||||
|
||||
@@ -43,7 +43,7 @@ class _Output
|
||||
*/
|
||||
protected static $javascriptObjects = null;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief File object classes
|
||||
*/
|
||||
protected static $fileObjectClasses = array();
|
||||
@@ -52,9 +52,19 @@ class _Output
|
||||
* @brief Meta tags for the current page
|
||||
*/
|
||||
public $metaTags = array();
|
||||
|
||||
/**
|
||||
* @brief Custom meta tags for the current page
|
||||
*/
|
||||
public $customMetaTags = array();
|
||||
|
||||
/**
|
||||
* @brief Automatic meta tags for the current page
|
||||
*/
|
||||
public $autoMetaTags = array();
|
||||
|
||||
/**
|
||||
* @brief Other <link rel=""> tags
|
||||
* @brief Other `<link rel="">` tags
|
||||
*/
|
||||
public $linkTags = array();
|
||||
|
||||
@@ -82,17 +92,17 @@ class _Output
|
||||
{
|
||||
if( static::$instance === NULL )
|
||||
{
|
||||
$classname = get_called_class();
|
||||
$classname = \get_called_class();
|
||||
static::$instance = new $classname;
|
||||
}
|
||||
|
||||
/* Inline Message */
|
||||
if( isset( $_SESSION['inlineMessage'] ) )
|
||||
if( $message = static::getInlineMessage() )
|
||||
{
|
||||
if( !\IPS\Request::i()->isAjax() )
|
||||
{
|
||||
static::$instance->inlineMessage = $_SESSION['inlineMessage'];
|
||||
$_SESSION['inlineMessage'] = NULL;
|
||||
static::$instance->inlineMessage = $message;
|
||||
static::setInlineMessage();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,6 +121,11 @@ class _Output
|
||||
*/
|
||||
public $title = '';
|
||||
|
||||
/**
|
||||
* @brief Default page title (may differ from $title if the meta tag editor was used)
|
||||
*/
|
||||
public $defaultPageTitle = '';
|
||||
|
||||
/**
|
||||
* @brief Should the title show in the header (ACP only)?
|
||||
*/
|
||||
@@ -142,7 +157,7 @@ class _Output
|
||||
public $jsVars = array();
|
||||
|
||||
/**
|
||||
* @brief Other raw JS - this is included inside an existing <script> tag already, so you should omit wrapping tags
|
||||
* @brief Other raw JS - this is included inside an existing `<script>` tag already, so you should omit wrapping tags
|
||||
*/
|
||||
public $headJs = '';
|
||||
|
||||
@@ -152,7 +167,7 @@ class _Output
|
||||
public $headCss = '';
|
||||
|
||||
/**
|
||||
* @brief Anything set in this property will be output right before </body> - useful for certain third party scripts that need to be output at end of page
|
||||
* @brief Anything set in this property will be output right before `</body>` - useful for certain third party scripts that need to be output at end of page
|
||||
*/
|
||||
public $endBodyCode = '';
|
||||
|
||||
@@ -197,7 +212,7 @@ class _Output
|
||||
public $editUrl = NULL;
|
||||
|
||||
/**
|
||||
* @brief <base target="">
|
||||
* @brief `<base target="">`
|
||||
*/
|
||||
public $base = NULL;
|
||||
|
||||
@@ -211,15 +226,36 @@ class _Output
|
||||
*/
|
||||
public $pageCaching = TRUE;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if ( \IPS\Dispatcher::hasInstance() and \IPS\Dispatcher::i()->controllerLocation !== 'setup' )
|
||||
{
|
||||
if ( \IPS\Settings::i()->clickjackprevention == 'csp' )
|
||||
{
|
||||
$this->httpHeaders['Content-Security-Policy'] = \IPS\Settings::i()->csp_header;
|
||||
$this->httpHeaders['X-Content-Security-Policy'] = \IPS\Settings::i()->csp_header; // This is just for IE11
|
||||
}
|
||||
elseif ( \IPS\Settings::i()->clickjackprevention != 'none' )
|
||||
{
|
||||
$this->httpHeaders['X-Frame-Options'] = 'sameorigin';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a JS bundle
|
||||
*
|
||||
* JS Bundle Cheatsheet
|
||||
* library.js (this is jQuery, mustache, underscore, jstz, etc)
|
||||
* framework.js (this is ui/*, utils/*, ips.model.js, ips.controller.js and the editor controllers)
|
||||
* admin.js or front.js (these are controllers, templates and models which are used everywhere for that location)
|
||||
* app.js (this is all models for a single application)
|
||||
* {location}_{section}.js (this is all controllers and templates for this section called ad-hoc when needed)
|
||||
* @par JS Bundle Cheatsheet
|
||||
* @li library.js (this is jQuery, mustache, underscore, jstz, etc)
|
||||
* @li framework.js (this is ui/, utils/*, ips.model.js, ips.controller.js and the editor controllers)
|
||||
* @li admin.js or front.js (these are controllers, templates and models which are used everywhere for that location)
|
||||
* @li app.js (this is all models for a single application)
|
||||
* @li {location}_{section}.js (this is all controllers and templates for this section called ad-hoc when needed)
|
||||
*
|
||||
* @param string $file Filename
|
||||
* @param string|null $app Application
|
||||
@@ -232,7 +268,7 @@ class _Output
|
||||
|
||||
if ( $location === 'interface' AND mb_substr( $file, -3 ) === '.js' )
|
||||
{
|
||||
return array( rtrim( \IPS\Http\Url::baseUrl( \IPS\Http\Url::PROTOCOL_RELATIVE ), '/' ) . "/applications/{$app}/interface/{$file}?v=" . ( defined( '\IPS\CACHEBUST_KEY' ) ? \IPS\CACHEBUST_KEY : time() ) );
|
||||
return array( rtrim( \IPS\Http\Url::baseUrl( \IPS\Http\Url::PROTOCOL_RELATIVE ), '/' ) . "/applications/{$app}/interface/{$file}?v=" . ( \defined( '\IPS\CACHEBUST_KEY' ) ? \IPS\CACHEBUST_KEY : time() ) );
|
||||
}
|
||||
elseif ( \IPS\IN_DEV )
|
||||
{
|
||||
@@ -240,14 +276,14 @@ class _Output
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( class_exists( 'IPS\Dispatcher', FALSE ) and \IPS\Dispatcher::i()->controllerLocation === 'setup' )
|
||||
if ( class_exists( 'IPS\Dispatcher', FALSE ) and ( !\IPS\Dispatcher::hasInstance() OR \IPS\Dispatcher::i()->controllerLocation === 'setup' ) )
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
if ( $app === null OR $app === 'global' )
|
||||
{
|
||||
if ( in_array( $file, static::$globalJavascript ) )
|
||||
if ( \in_array( $file, static::$globalJavascript ) )
|
||||
{
|
||||
/* Global bundle (admin.js, front.js, library.js, framework.js, map.js) */
|
||||
return array( static::_getJavascriptFileObject( 'global', 'root', $file )->url );
|
||||
@@ -347,7 +383,7 @@ class _Output
|
||||
{
|
||||
$key = md5( $app .'-' . $location . '-' . $file );
|
||||
|
||||
if ( isset( $javascriptObjects[ $app ] ) and is_array( $javascriptObjects[ $app ] ) and in_array( $key, array_keys( $javascriptObjects[ $app ] ) ) )
|
||||
if ( isset( $javascriptObjects[ $app ] ) and \is_array( $javascriptObjects[ $app ] ) and \in_array( $key, array_keys( $javascriptObjects[ $app ] ) ) )
|
||||
{
|
||||
if ( $javascriptObjects[ $app ][ $key ] !== NULL )
|
||||
{
|
||||
@@ -357,7 +393,9 @@ class _Output
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
\IPS\Settings::i()->changeValues( array( 'javascript_updated' => time() ) );
|
||||
|
||||
\IPS\Data\Store::i()->javascript_map = $javascriptObjects;
|
||||
}
|
||||
|
||||
@@ -460,7 +498,7 @@ class _Output
|
||||
|
||||
$javascriptObjects = ( isset( \IPS\Data\Store::i()->javascript_map ) ) ? \IPS\Data\Store::i()->javascript_map : array();
|
||||
|
||||
if ( isset( $javascriptObjects[ $app ] ) and in_array( $key, array_keys( $javascriptObjects[ $app ] ) ) )
|
||||
if ( isset( $javascriptObjects[ $app ] ) and \in_array( $key, array_keys( $javascriptObjects[ $app ] ) ) )
|
||||
{
|
||||
if ( $javascriptObjects[ $app ][ $key ] === NULL )
|
||||
{
|
||||
@@ -499,7 +537,7 @@ class _Output
|
||||
$javascriptObjects = ( isset( \IPS\Data\Store::i()->javascript_map ) ) ? \IPS\Data\Store::i()->javascript_map : array();
|
||||
|
||||
/* Test again */
|
||||
if ( isset( $javascriptObjects[ $app ] ) and in_array( $key, array_keys( $javascriptObjects[ $app ] ) ) and $javascriptObjects[ $app ][ $key ] )
|
||||
if ( isset( $javascriptObjects[ $app ] ) and \in_array( $key, array_keys( $javascriptObjects[ $app ] ) ) and $javascriptObjects[ $app ][ $key ] )
|
||||
{
|
||||
return \IPS\File::get( 'core_Theme', $javascriptObjects[ $app ][ $key ] );
|
||||
}
|
||||
@@ -545,6 +583,12 @@ class _Output
|
||||
{
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=system&controller=login", 'front', 'login' )->setQueryString( '_mfaLogin', 1 ) );
|
||||
}
|
||||
|
||||
/* Do not show advertisements that shouldn't display on non-content pages on error pages */
|
||||
if( \IPS\Dispatcher::i()->dispatcherController )
|
||||
{
|
||||
\IPS\Dispatcher::i()->dispatcherController->isContentPage = FALSE;
|
||||
}
|
||||
|
||||
/* If we're in an external script, just show a simple message */
|
||||
if ( !\IPS\Dispatcher::hasInstance() )
|
||||
@@ -594,7 +638,7 @@ class _Output
|
||||
\IPS\Member::loggedIn()->language()->parseOutputForDisplay( $message );
|
||||
|
||||
/* Log */
|
||||
$level = intval( \substr( $code, 0, 1 ) );
|
||||
$level = \intval( \substr( $code, 0, 1 ) );
|
||||
if( !\IPS\Session::i()->userAgent->spider )
|
||||
{
|
||||
if( $code and \IPS\Settings::i()->error_log_level and $level >= \IPS\Settings::i()->error_log_level )
|
||||
@@ -607,11 +651,8 @@ class _Output
|
||||
'log_ip_address' => \IPS\Request::i()->ipAddress(),
|
||||
'log_request_uri' => $_SERVER['REQUEST_URI'],
|
||||
) );
|
||||
}
|
||||
|
||||
if( \IPS\Settings::i()->error_notify_level and $level >= \IPS\Settings::i()->error_notify_level )
|
||||
{
|
||||
\IPS\Email::buildFromTemplate( 'core', 'error_log', array( $code, $message ), \IPS\Email::TYPE_TRANSACTIONAL )->send( \IPS\Settings::i()->email_in );
|
||||
\IPS\core\AdminNotification::send( 'core', 'Error', NULL, TRUE, array( $code, $message ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -627,7 +668,7 @@ class _Output
|
||||
/* Try to find the breaking hook */
|
||||
if ( $faultyAppOrHookId )
|
||||
{
|
||||
if ( is_numeric( $faultyAppOrHookId ) )
|
||||
if ( \is_numeric( $faultyAppOrHookId ) )
|
||||
{
|
||||
$hookSource = \IPS\Db::i()->select( 'plugin', 'core_hooks', array( 'id=?', $faultyAppOrHookId ) )->first();
|
||||
$plugin = \IPS\Plugin::load( $hookSource );
|
||||
@@ -639,10 +680,13 @@ class _Output
|
||||
$faulty = \IPS\Member::loggedIn()->language()->addToStack( 'faulty_app', FALSE, array( 'sprintf' => array( $app->_title, \IPS\Http\Url::internal( 'app=core&module=applications&controller=applications', 'admin' ) ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Do not cache error pages
|
||||
$allowGuestCache = FALSE;
|
||||
|
||||
/* Send output */
|
||||
\IPS\Output::i()->sidebar['enabled'] = FALSE;
|
||||
$this->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( $title, \IPS\Theme::i()->getTemplate( 'global', 'core' )->error( $title, $message, $code, $extra, $member, $faulty ), array( 'app' => \IPS\Dispatcher::i()->application ? \IPS\Dispatcher::i()->application->directory : NULL, 'module' => \IPS\Dispatcher::i()->module ? \IPS\Dispatcher::i()->module->key : NULL, 'controller' => \IPS\Dispatcher::i()->controller ) ), $httpStatusCode, 'text/html', $httpHeaders, FALSE, FALSE );
|
||||
$this->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( $title, \IPS\Theme::i()->getTemplate( 'global', 'core' )->error( $title, $message, $code, $extra, $member, $faulty ), array( 'app' => \IPS\Dispatcher::i()->application ? \IPS\Dispatcher::i()->application->directory : NULL, 'module' => \IPS\Dispatcher::i()->module ? \IPS\Dispatcher::i()->module->key : NULL, 'controller' => \IPS\Dispatcher::i()->controller ) ), $httpStatusCode, 'text/html', $httpHeaders, $allowGuestCache, FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -684,20 +728,21 @@ class _Output
|
||||
/**
|
||||
* Send output
|
||||
*
|
||||
* @param string $output Content to output
|
||||
* @param int $httpStatusCode HTTP Status Code
|
||||
* @param string $contentType HTTP Content-type
|
||||
* @param array $httpHeaders Additional HTTP Headers
|
||||
* @param bool $cacheThisPage Can/should this page be cached?
|
||||
* @param bool $pageIsCached Is the page from a cache? If TRUE, no language parsing will be done
|
||||
* @param bool $parseFileObjects Should <fileStore.xxx> and <___base_url___> be replaced in the output?
|
||||
* @param bool $parseEmoji Should Emoji be parsed?
|
||||
* @return void
|
||||
* @param string $output Content to output
|
||||
* @param int $httpStatusCode HTTP Status Code
|
||||
* @param string $contentType HTTP Content-type
|
||||
* @param array $httpHeaders Additional HTTP Headers
|
||||
* @param bool $cacheThisPage Can/should this page be cached?
|
||||
* @param bool $pageIsCached Is the page from a cache? If TRUE, no language parsing will be done
|
||||
* @param bool $parseFileObjects Should `<fileStore.xxx>` and `<___base_url___>` be replaced in the output?
|
||||
* @param bool $parseEmoji Should Emoji be parsed?
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function sendOutput( $output='', $httpStatusCode=200, $contentType='text/html', $httpHeaders=array(), $cacheThisPage=TRUE, $pageIsCached=FALSE, $parseFileObjects=TRUE, $parseEmoji=TRUE )
|
||||
{
|
||||
/* Replace language stack keys with actual content */
|
||||
if ( \IPS\Dispatcher::hasInstance() and !in_array( $contentType, array( 'text/javascript', 'text/css', 'application/json' ) ) and $output and !$pageIsCached )
|
||||
if ( \IPS\Dispatcher::hasInstance() and !\in_array( $contentType, array( 'text/javascript', 'text/css', 'application/json' ) ) and $output and !$pageIsCached )
|
||||
{
|
||||
\IPS\Member::loggedIn()->language()->parseOutputForDisplay( $output );
|
||||
}
|
||||
@@ -707,39 +752,65 @@ class _Output
|
||||
{
|
||||
$this->parseFileObjectUrls( $output );
|
||||
}
|
||||
|
||||
|
||||
/* Replace emoji */
|
||||
if ( $output and $parseEmoji and \IPS\Dispatcher::hasInstance() and \IPS\Dispatcher::i()->controllerLocation !== 'setup' )
|
||||
{
|
||||
$output = $this->replaceEmojiWithImages( $output );
|
||||
}
|
||||
|
||||
/* Do we want to preload anything? Only send preload headers for full page requests with a text/html response that have output. */
|
||||
if( !$pageIsCached AND !\IPS\IN_DEV AND $output AND !\IPS\Request::i()->isAjax() AND $contentType == 'text/html' )
|
||||
{
|
||||
if( $urls = $this->getPreloadUrls() AND \count( $urls ) )
|
||||
{
|
||||
/* Send a link header for preloading */
|
||||
//$httpHeaders['Link'] = implode( ', ', $urls );
|
||||
}
|
||||
}
|
||||
|
||||
/* Full page caching for guests */
|
||||
if (
|
||||
\IPS\CACHE_PAGE_TIMEOUT and $this->pageCaching and // Page caching is enabled
|
||||
$cacheThisPage and // Some pages can specify not to be cached (for example, when displaying a cached page, you don't want it recached)
|
||||
!isset( \IPS\Request::i()->cookie['noCache'] ) and // A noCache cookie might get set to not cache a particular guest (for example, if they have added items to their cart in the store)
|
||||
mb_strtolower( $_SERVER['REQUEST_METHOD'] ) == 'get' and // Is a HTTP GET request (don't cache output for POSTs)
|
||||
( mb_strtolower( $_SERVER['REQUEST_METHOD'] ) == 'get' OR mb_strtolower( $_SERVER['REQUEST_METHOD'] ) == 'head' ) and // Is a HTTP GET/HEAD request (don't cache output for POSTs)
|
||||
!isset( \IPS\Request::i()->csrfKey ) and // CSRF key isn't present (which would be like a POST request)
|
||||
( $contentType == 'text/html' or $contentType == 'text/xml' ) and // Output is HTML or XML (don't cache JSON output, etc)
|
||||
( $contentType == 'text/html' or $contentType == 'text/xml' or $contentType == 'application/manifest+json' ) and // Output is HTML, XML or [PWA] application manifest (don't cache JSON output, etc)
|
||||
\IPS\Dispatcher::hasInstance() and class_exists( 'IPS\Dispatcher', FALSE ) and \IPS\Dispatcher::i()->controllerLocation === 'front' and // Is a normal, front-end page (necessary to know if user is logged in)
|
||||
!\IPS\Member::loggedIn()->member_id // User is not logged in
|
||||
) {
|
||||
$theme = isset( \IPS\Request::i()->cookie['theme'] ) ? \IPS\Request::i()->cookie['theme'] : '';
|
||||
|
||||
\IPS\Data\Cache::i()->storeWithExpire( 'page_' . md5( (int) \IPS\Request::i()->isSecure() . ( !empty( $_SERVER['HTTP_HOST'] ) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME'] ) . '/' . $_SERVER['REQUEST_URI'] ) . '_' . \IPS\Member::loggedIn()->language()->id . '_' . $theme, array(
|
||||
'output' => str_replace( \IPS\Session::i()->csrfKey, '{{csrfKey}}', $output ),
|
||||
'code' => $httpStatusCode,
|
||||
'contentType' => $contentType,
|
||||
'httpHeaders' => array_merge( $httpHeaders, array( 'X-IPS-Cached-Response' => \IPS\DateTime::create()->rfc1123() ) ),
|
||||
'lastUpdated' => time()
|
||||
), \IPS\DateTime::create()->add( new\DateInterval( 'PT' . \IPS\CACHE_PAGE_TIMEOUT . 'S' ) ), TRUE );
|
||||
\IPS\Output\Cache::i()->set( 'page_' . md5( (string) \IPS\Request::i()->url() ) . '_' . \IPS\Member::loggedIn()->language()->id . '_' . $theme,
|
||||
str_replace( \IPS\Session::i()->csrfKey, '{{csrfKey}}', $output ),
|
||||
array(
|
||||
'code' => $httpStatusCode,
|
||||
'contentType' => $contentType,
|
||||
'httpHeaders' => array_merge( $httpHeaders, array( 'X-IPS-Cached-Response' => \IPS\DateTime::create()->rfc1123() ) ),
|
||||
'lastUpdated' => time()
|
||||
), \IPS\DateTime::create()->add( new\DateInterval( 'PT' . \IPS\CACHE_PAGE_TIMEOUT . 'S' ) ), TRUE );
|
||||
|
||||
/* Add caching headers */
|
||||
if( !isset( $httpHeaders['Cache-Control'] ) )
|
||||
{
|
||||
$httpHeaders += \IPS\Output::getCacheHeaders( time(), \IPS\CACHE_PAGE_TIMEOUT );
|
||||
}
|
||||
}
|
||||
|
||||
/* Include headers set in constructor, intentionally after guest caching. */
|
||||
$httpHeaders = $this->httpHeaders + $httpHeaders;
|
||||
|
||||
/* We will only push resources (http/2) on the first visit, i.e. if the session cookie is not present yet */
|
||||
$location = ( \IPS\Dispatcher::hasInstance() ) ? mb_ucfirst( \IPS\Dispatcher::i()->controllerLocation ) : 'Front';
|
||||
|
||||
if( isset( \IPS\Request::i()->cookie['IPSSession' . $location ] ) AND \IPS\Request::i()->cookie['IPSSession' . $location ] AND isset( $httpHeaders['Link'] ) )
|
||||
{
|
||||
unset( $httpHeaders['Link'] );
|
||||
}
|
||||
|
||||
/* Query Log (has to be done after parseOutputForDisplay because runs queries and after page caching so the log isn't misleading) */
|
||||
$contentTypeTypes = explode( '/', $contentType );
|
||||
|
||||
if ( ( \IPS\QUERY_LOG or \IPS\CACHING_LOG ) and in_array( $contentType, array( 'text/html', 'application/json' ) ) )
|
||||
if ( $output and ( \IPS\QUERY_LOG or \IPS\CACHING_LOG or \IPS\REDIS_LOG ) and \in_array( $contentType, array( 'text/html', 'application/json' ) ) and ( \IPS\Dispatcher::hasInstance() and \IPS\Dispatcher::i()->controllerLocation !== 'setup' ) )
|
||||
{
|
||||
/* Close the session and run tasks now so we can see those queries */
|
||||
session_write_close();
|
||||
@@ -766,7 +837,7 @@ class _Output
|
||||
{
|
||||
$output = str_replace( '<!--ipsQueryLog-->', \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->queryLog( $queryLog ), $output );
|
||||
}
|
||||
if ( \IPS\CACHING_LOG )
|
||||
if ( \IPS\CACHING_LOG or \IPS\REDIS_LOG )
|
||||
{
|
||||
$output = str_replace( '<!--ipsCachingLog-->', \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->cachingLog( $cachingLog ), $output );
|
||||
}
|
||||
@@ -784,15 +855,41 @@ class _Output
|
||||
/* Start buffering */
|
||||
ob_start();
|
||||
|
||||
/* If the browser supports gzip, gzip the content - we do this ourselves so that we can send Content-Length even with mod_gzip */
|
||||
if ( isset( $_SERVER['HTTP_ACCEPT_ENCODING'] ) and \strpos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip' ) !== false )
|
||||
/* Generated by a logged in user? */
|
||||
if( \IPS\Dispatcher::hasInstance() )
|
||||
{
|
||||
if ( function_exists( 'gzencode' ) and (bool) ini_get('zlib.output_compression') === false )
|
||||
$this->sendHeader( "X-IPS-LoggedIn: " . ( ( \IPS\Member::loggedIn()->member_id ) ? 1 : 0 ) );
|
||||
}
|
||||
|
||||
$vary = array( 'cookie' );
|
||||
|
||||
/* Can we compress the content? */
|
||||
if ( $output and isset( $_SERVER['HTTP_ACCEPT_ENCODING'] ) )
|
||||
{
|
||||
/* If php zlib.output_compression is on, don't do anything since PHP will */
|
||||
if( (bool) ini_get('zlib.output_compression') === false )
|
||||
{
|
||||
$output = gzencode( $output ); // mod_gzip will encode pages, but we want to encode ourselves so that Content-Length is correct
|
||||
$this->sendHeader("Content-Encoding: gzip"); // Tells the server we've alredy encoded so it doesn't need to
|
||||
/* Try brotli first - support will be rare, but preferred if it is available */
|
||||
if ( \strpos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'br' ) !== false and \function_exists( 'brotli_compress' ) )
|
||||
{
|
||||
$output = brotli_compress( $output );
|
||||
$this->sendHeader( "Content-Encoding: br" ); // Tells the server we've alredy encoded so it doesn't need to
|
||||
$vary[] = "Accept-Encoding"; // Tells proxy caches that the response varies depending upon encoding
|
||||
}
|
||||
/* If the browser supports gzip, gzip the content - we do this ourselves so that we can send Content-Length even with mod_gzip */
|
||||
elseif ( \strpos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip' ) !== false and \function_exists( 'gzencode' ) )
|
||||
{
|
||||
$output = gzencode( $output ); // mod_gzip will encode pages, but we want to encode ourselves so that Content-Length is correct
|
||||
$this->sendHeader( "Content-Encoding: gzip" ); // Tells the server we've alredy encoded so it doesn't need to
|
||||
$vary[] = "Accept-Encoding"; // Tells proxy caches that the response varies depending upon encoding
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( \count( $vary ) )
|
||||
{
|
||||
$this->sendHeader( "Vary: " . implode( ", ", $vary ) );
|
||||
}
|
||||
|
||||
/* Output */
|
||||
print $output;
|
||||
@@ -810,17 +907,6 @@ class _Output
|
||||
$size = ob_get_length();
|
||||
$this->sendHeader( "Content-Length: {$size}" ); // Makes sure the connection closes after sending output so that tasks etc aren't holding it open
|
||||
}
|
||||
|
||||
/* Do we want to preload anything? */
|
||||
foreach( $this->linkTags as $tag )
|
||||
{
|
||||
/* We are only doing this for rel=preload, and the 'as' parameter is not optional */
|
||||
if( is_array( $tag ) AND isset( $tag['rel'] ) AND $tag['rel'] == 'preload' AND isset( $tag['as'] ) AND $tag['as'] )
|
||||
{
|
||||
$as = ( isset( $tag['as'] ) ) ? "; as=" . $tag['as'] : '';
|
||||
$this->sendHeader( "Link: " . $tag['href'] . '; rel=preload' . $as );
|
||||
}
|
||||
}
|
||||
|
||||
/* Rest of our HTTP headers */
|
||||
foreach ( $httpHeaders as $key => $header )
|
||||
@@ -839,21 +925,98 @@ class _Output
|
||||
@ob_end_flush();
|
||||
@flush();
|
||||
|
||||
/* Log headers if we are set to do so */
|
||||
if( \IPS\DEV_LOG_HEADERS === TRUE )
|
||||
{
|
||||
$this->_logHeadersSent();
|
||||
}
|
||||
|
||||
/* If using PHP-FPM, close the request so that __destruct tasks are run after data is flushed to the browser
|
||||
@see http://www.php.net/manual/en/function.fastcgi-finish-request.php */
|
||||
if( function_exists( 'fastcgi_finish_request' ) )
|
||||
if( \function_exists( 'fastcgi_finish_request' ) )
|
||||
{
|
||||
fastcgi_finish_request();
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the headers that have been sent, if we are able to do so
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _logHeadersSent()
|
||||
{
|
||||
$headers = NULL;
|
||||
|
||||
if( \function_exists('headers_list') )
|
||||
{
|
||||
$headers = headers_list();
|
||||
}
|
||||
elseif( \function_exists('apache_response_headers') )
|
||||
{
|
||||
$headers = apache_response_headers();
|
||||
}
|
||||
elseif( \function_exists('xdebug_get_headers') )
|
||||
{
|
||||
$headers = xdebug_get_headers();
|
||||
}
|
||||
|
||||
if( $headers !== NULL )
|
||||
{
|
||||
\IPS\Log::log( $headers, 'httpHeaders' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the URLs to preload (via Link: HTTP header)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPreloadUrls()
|
||||
{
|
||||
/* http/2 push resources */
|
||||
$preload = array();
|
||||
|
||||
foreach( $this->linkTags as $tag )
|
||||
{
|
||||
/* We are only doing this for rel=preload, and the 'as' parameter is not optional. Preloading fonts currently does not work as expected in Chrome, resulting in the font file downloading twice, so we will skip fonts. */
|
||||
if( \is_array( $tag ) AND isset( $tag['rel'] ) AND $tag['rel'] == 'preload' AND isset( $tag['as'] ) AND $tag['as'] AND $tag['as'] != 'font' )
|
||||
{
|
||||
$preload[] = $tag['href'] . '; rel=preload; as=' . $tag['as'];
|
||||
}
|
||||
}
|
||||
|
||||
$cssFilesToCheck = ( \IPS\Dispatcher::hasInstance() AND \IPS\Dispatcher::i()->controllerLocation == 'setup' ) ? \IPS\Output::i()->cssFiles : array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'custom.css', 'core', 'front' ) );
|
||||
|
||||
foreach( array_unique( $cssFilesToCheck, SORT_STRING ) as $css )
|
||||
{
|
||||
$url = \IPS\Http\Url::external( $css )->setQueryString( 'v', \IPS\CACHEBUST_KEY );
|
||||
$preload[] = (string) $url . '; rel=preload; as=style';
|
||||
}
|
||||
|
||||
foreach( array_unique( array_filter( array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->jsFilesAsync ) ), SORT_STRING ) as $js )
|
||||
{
|
||||
$url = \IPS\Http\Url::external( $js );
|
||||
|
||||
if( $url->data['host'] == parse_url( \IPS\Settings::i()->base_url, PHP_URL_HOST ) )
|
||||
{
|
||||
$url = $url->setQueryString( 'v', \IPS\CACHEBUST_KEY );
|
||||
}
|
||||
|
||||
$preload[] = (string) $url . '; rel=preload; as=script';
|
||||
}
|
||||
|
||||
/* Only include the first 30 entries if there are a lot */
|
||||
return \array_slice( $preload, 0, 30 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Send JSON output
|
||||
*
|
||||
* @param string $data Data to be JSON-encoded
|
||||
* @param int $httpStatusCode HTTP Status Code
|
||||
* @param string|array $data Data to be JSON-encoded
|
||||
* @param int $httpStatusCode HTTP Status Code
|
||||
* @return void
|
||||
*/
|
||||
public function json( $data, $httpStatusCode=200 )
|
||||
@@ -900,12 +1063,12 @@ class _Output
|
||||
{
|
||||
$message = \IPS\Member::loggedIn()->language()->addToStack( $message );
|
||||
\IPS\Member::loggedIn()->language()->parseOutputForDisplay( $message );
|
||||
$_SESSION['inlineMessage'] = $message;
|
||||
static::setInlineMessage( $message );
|
||||
session_write_close();
|
||||
}
|
||||
elseif( $this->inlineMessage )
|
||||
{
|
||||
$_SESSION['inlineMessage'] = $this->inlineMessage;
|
||||
static::setInlineMessage( $this->inlineMessage );
|
||||
session_write_close();
|
||||
}
|
||||
|
||||
@@ -969,7 +1132,7 @@ class _Output
|
||||
*/
|
||||
public function replaceEmojiWithImages( $output )
|
||||
{
|
||||
if ( \IPS\Settings::i()->emoji_style == 'twemoji' or \IPS\Settings::i()->emoji_style == 'emojione' )
|
||||
if ( \IPS\Settings::i()->emoji_style == 'twemoji' )
|
||||
{
|
||||
return preg_replace_callback( '/<span class="ipsEmoji">(.+?)<\/span>/', function( $matches ) {
|
||||
$hex = bin2hex( mb_convert_encoding( $matches[1], 'UTF-32', 'UTF-8' ) );
|
||||
@@ -1004,26 +1167,19 @@ class _Output
|
||||
|
||||
$chunks[ $i ] = $res;
|
||||
}
|
||||
|
||||
if ( \IPS\Settings::i()->emoji_style == 'twemoji' )
|
||||
{
|
||||
$image = implode( '-', $chunks );
|
||||
|
||||
if ( \strstr( $image, '200d' ) === FALSE or $image === '1f441-fe0f-200d-1f5e8-fe0f' )
|
||||
{
|
||||
$image = str_replace( '-fe0f', '', $image );
|
||||
}
|
||||
if ( in_array( $image, array( '0031-20e3', '0030-20e3', '0032-20e3', '0034-20e3', '0035-20e3', '0036-20e3', '0037-20e3', '0038-20e3', '0033-20e3', '0039-20e3', '0023-20e3', '002a-20e3', '00a9', '00ae' ) ) )
|
||||
{
|
||||
$image = str_replace( '00', '', $image );
|
||||
}
|
||||
|
||||
return '<img src="https://twemoji.maxcdn.com/2/72x72/' . $image . '.png" class="ipsEmoji" alt="' . $matches[1] . '">';
|
||||
}
|
||||
else
|
||||
|
||||
$image = implode( '-', $chunks );
|
||||
|
||||
if ( \strstr( $image, '200d' ) === FALSE or $image === '1f441-fe0f-200d-1f5e8-fe0f' )
|
||||
{
|
||||
return '<img src="https://cdn.jsdelivr.net/emojione/assets/3.1/png/64/' . str_replace( array( '-200d', '-fe0f' ), '', implode( '-', $chunks ) ) . '.png" class="ipsEmoji" alt="' . $matches[1] . '">';
|
||||
$image = str_replace( '-fe0f', '', $image );
|
||||
}
|
||||
if ( \in_array( $image, array( '0031-20e3', '0030-20e3', '0032-20e3', '0034-20e3', '0035-20e3', '0036-20e3', '0037-20e3', '0038-20e3', '0033-20e3', '0039-20e3', '0023-20e3', '002a-20e3', '00a9', '00ae' ) ) )
|
||||
{
|
||||
$image = str_replace( '00', '', $image );
|
||||
}
|
||||
|
||||
return '<img src="https://twemoji.maxcdn.com/2/72x72/' . $image . '.png" class="ipsEmoji" alt="' . $matches[1] . '">';
|
||||
}, $output );
|
||||
}
|
||||
return $output;
|
||||
@@ -1104,7 +1260,7 @@ class _Output
|
||||
{
|
||||
$javascriptObjects = ( isset( \IPS\Data\Store::i()->javascript_map ) ) ? \IPS\Data\Store::i()->javascript_map : array();
|
||||
|
||||
if ( ! is_array( $javascriptObjects ) OR ! count( $javascriptObjects ) OR ! isset( $javascriptObjects[ $app ] ) )
|
||||
if ( ! \is_array( $javascriptObjects ) OR ! \count( $javascriptObjects ) OR ! isset( $javascriptObjects[ $app ] ) )
|
||||
{
|
||||
/* Map is broken or missing, recompile all JS */
|
||||
\IPS\Output\Javascript::compile( $app );
|
||||
@@ -1132,6 +1288,8 @@ class _Output
|
||||
{
|
||||
$this->title .= ' - ' . \IPS\Settings::i()->board_name;
|
||||
}
|
||||
|
||||
$this->defaultPageTitle = $this->title;
|
||||
|
||||
/* Add Admin-specified ones */
|
||||
if( !$this->metaTagsUrl )
|
||||
@@ -1148,8 +1306,11 @@ class _Output
|
||||
\IPS\Data\Store::i()->metaTags = $rows;
|
||||
}
|
||||
|
||||
if( is_array( $rows ) )
|
||||
if( \is_array( $rows ) )
|
||||
{
|
||||
/* We duplicate these so we can know what is generated automatically for the live meta tag editor */
|
||||
$this->autoMetaTags = $this->metaTags;
|
||||
|
||||
foreach ( $rows as $row )
|
||||
{
|
||||
if( \strpos( $row['meta_url'], '*' ) !== FALSE )
|
||||
@@ -1158,11 +1319,20 @@ class _Output
|
||||
{
|
||||
$_tags = json_decode( $row['meta_tags'], TRUE );
|
||||
|
||||
if( is_array( $_tags ) )
|
||||
if( \is_array( $_tags ) )
|
||||
{
|
||||
foreach( $_tags as $_tagName => $_tagContent )
|
||||
{
|
||||
$this->metaTags[ $_tagName ] = $_tagContent;
|
||||
if( $_tagContent === NULL )
|
||||
{
|
||||
unset( $this->metaTags[ $_tagName ] );
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->metaTags[ $_tagName ] = $_tagContent;
|
||||
}
|
||||
|
||||
$this->customMetaTags[ $_tagName ] = $_tagContent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1176,15 +1346,25 @@ class _Output
|
||||
}
|
||||
else
|
||||
{
|
||||
if( trim( $row['meta_url'], '/' ) == trim( $this->metaTagsUrl, '/' ) )
|
||||
$rootPath = \IPS\Http\Url::external( \IPS\Settings::i()->base_url )->data['path'];
|
||||
if( trim( $row['meta_url'], '/' ) == trim( $this->metaTagsUrl, '/' ) and ( ( $row['meta_url'] == '/' and \IPS\Request::i()->url()->data['path'] == $rootPath ) or $row['meta_url'] !== '/' ) )
|
||||
{
|
||||
$_tags = json_decode( $row['meta_tags'], TRUE );
|
||||
|
||||
if ( is_array( $_tags ) )
|
||||
if ( \is_array( $_tags ) )
|
||||
{
|
||||
foreach( $_tags as $_tagName => $_tagContent )
|
||||
{
|
||||
$this->metaTags[ $_tagName ] = $_tagContent;
|
||||
if( $_tagContent === NULL )
|
||||
{
|
||||
unset( $this->metaTags[ $_tagName ] );
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->metaTags[ $_tagName ] = $_tagContent;
|
||||
}
|
||||
|
||||
$this->customMetaTags[ $_tagName ] = $_tagContent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1204,7 +1384,7 @@ class _Output
|
||||
|
||||
foreach( $this->metaTags as $name => $value )
|
||||
{
|
||||
if ( ! is_array( $value ) )
|
||||
if ( ! \is_array( $value ) )
|
||||
{
|
||||
$value = array( $value );
|
||||
}
|
||||
@@ -1232,7 +1412,10 @@ class _Output
|
||||
$jsonLd = array(
|
||||
'website' => array(
|
||||
'@context' => "http://www.schema.org",
|
||||
'publisher' => \IPS\Settings::i()->base_url . '#organization',
|
||||
'@type' => "WebSite",
|
||||
'@id' => \IPS\Settings::i()->base_url . '#website',
|
||||
'mainEntityOfPage' => \IPS\Settings::i()->base_url,
|
||||
'name' => \IPS\Settings::i()->board_name,
|
||||
'url' => \IPS\Settings::i()->base_url,
|
||||
'potentialAction' => array(
|
||||
@@ -1245,17 +1428,23 @@ class _Output
|
||||
'organization' => array(
|
||||
'@context' => "http://www.schema.org",
|
||||
'@type' => "Organization",
|
||||
'@id' => \IPS\Settings::i()->base_url . '#organization',
|
||||
'mainEntityOfPage' => \IPS\Settings::i()->base_url,
|
||||
'name' => \IPS\Settings::i()->board_name,
|
||||
'url' => \IPS\Settings::i()->base_url,
|
||||
)
|
||||
);
|
||||
|
||||
if( \IPS\Theme::i()->logo_front )
|
||||
if( $logo = \IPS\Theme::i()->logo_front )
|
||||
{
|
||||
$jsonLd['organization']['logo'] = (string) \IPS\Theme::i()->logo_front;
|
||||
$jsonLd['organization']['logo'] = array(
|
||||
'@type' => 'ImageObject',
|
||||
'@id' => \IPS\Settings::i()->base_url . '#logo',
|
||||
'url' => (string) $logo
|
||||
);
|
||||
}
|
||||
|
||||
if( \IPS\Settings::i()->site_social_profiles AND $links = json_decode( \IPS\Settings::i()->site_social_profiles, TRUE ) AND count( $links ) )
|
||||
if( \IPS\Settings::i()->site_social_profiles AND $links = json_decode( \IPS\Settings::i()->site_social_profiles, TRUE ) AND \count( $links ) )
|
||||
{
|
||||
if( !isset( $jsonLd['organization']['sameAs'] ) )
|
||||
{
|
||||
@@ -1270,14 +1459,17 @@ class _Output
|
||||
|
||||
if( \IPS\Settings::i()->site_address AND $address = \IPS\GeoLocation::buildFromJson( \IPS\Settings::i()->site_address ) )
|
||||
{
|
||||
$jsonLd['organization']['address'] = array(
|
||||
'@type' => 'PostalAddress',
|
||||
'streetAddress' => implode( ', ', $address->addressLines ),
|
||||
'addressLocality' => $address->city,
|
||||
'addressRegion' => $address->region,
|
||||
'postalCode' => $address->postalCode,
|
||||
'addressCountry' => $address->country,
|
||||
);
|
||||
if ( ! empty( $address->country ) )
|
||||
{
|
||||
$jsonLd['organization']['address'] = array(
|
||||
'@type' => 'PostalAddress',
|
||||
'streetAddress' => implode( ', ', $address->addressLines ),
|
||||
'addressLocality' => $address->city,
|
||||
'addressRegion' => $address->region,
|
||||
'postalCode' => $address->postalCode,
|
||||
'addressCountry' => $address->country,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
foreach( \IPS\Lang::getEnabledLanguages() as $language )
|
||||
@@ -1290,21 +1482,17 @@ class _Output
|
||||
}
|
||||
|
||||
/* Add breadcrumbs */
|
||||
if( count( $this->breadcrumb ) )
|
||||
if( \count( $this->breadcrumb ) )
|
||||
{
|
||||
$jsonLd['breadcrumbs'] = array(
|
||||
'@context' => "http://schema.org",
|
||||
'@type' => "BreadcrumbList",
|
||||
'itemListElement' => array(),
|
||||
);
|
||||
|
||||
$position = 1;
|
||||
$elements = [];
|
||||
|
||||
foreach( $this->breadcrumb as $breadcrumb )
|
||||
{
|
||||
if( $breadcrumb[0] )
|
||||
{
|
||||
$jsonLd['breadcrumbs']['itemListElement'][] = array(
|
||||
$elements[] = array(
|
||||
'@type' => "ListItem",
|
||||
'position' => $position,
|
||||
'item' => array(
|
||||
@@ -1316,6 +1504,15 @@ class _Output
|
||||
$position++;
|
||||
}
|
||||
}
|
||||
|
||||
if( \count( $elements ) )
|
||||
{
|
||||
$jsonLd['breadcrumbs'] = array(
|
||||
'@context' => "http://schema.org",
|
||||
'@type' => "BreadcrumbList",
|
||||
'itemListElement' => $elements,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if( \IPS\Member::loggedIn()->canUseContactUs() )
|
||||
@@ -1329,11 +1526,12 @@ class _Output
|
||||
|
||||
$this->jsonLd = array_merge( $this->jsonLd, $jsonLd );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* License Warning
|
||||
*
|
||||
* @return string|NULL "none" = no license key. "expired" = license key expired. NULL = no error
|
||||
* @return string|NULL "none" = no license key. "expired" = license key expired. NULL = no error
|
||||
* @deprecated to be removed in 4.5
|
||||
*/
|
||||
public function licenseKeyWarning()
|
||||
{
|
||||
@@ -1361,6 +1559,7 @@ class _Output
|
||||
* Days remaining before license expires
|
||||
*
|
||||
* @return int|NULL NULL or the number of days remaining
|
||||
* @deprecated to be removed in 4.5
|
||||
*/
|
||||
public function licenseKeyDaysRemaining()
|
||||
{
|
||||
@@ -1381,7 +1580,7 @@ class _Output
|
||||
|
||||
return $daysLeft;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Global search menu options
|
||||
*/
|
||||
@@ -1443,4 +1642,41 @@ class _Output
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get any inline message
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function getInlineMessage()
|
||||
{
|
||||
if ( ( \IPS\Dispatcher::hasInstance() and \IPS\Dispatcher::i()->controllerLocation == 'front' ) and ( \IPS\Session\Front::loggedIn() or isset( \IPS\Request::i()->cookie['noCache'] ) ) ) # Don't attempt to initiate a full member object here
|
||||
{
|
||||
return ( ( isset( $_SESSION['inlineMessage'] ) and ! empty( $_SESSION['inlineMessage'] ) ) ? $_SESSION['inlineMessage'] : NULL );
|
||||
}
|
||||
else if ( isset( \IPS\Request::i()->cookie['inlineMessage'] ) )
|
||||
{
|
||||
return \IPS\Request::i()->cookie['inlineMessage'];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an inline message
|
||||
*
|
||||
* @param string $message The message
|
||||
* @return void
|
||||
*/
|
||||
protected static function setInlineMessage( $message=NULL )
|
||||
{
|
||||
if ( ( \IPS\Dispatcher::hasInstance() and \IPS\Dispatcher::i()->controllerLocation == 'front' ) and ( \IPS\Session\Front::loggedIn() or isset( \IPS\Request::i()->cookie['noCache'] ) ) ) # Don't attempt to initiate a full member object here
|
||||
{
|
||||
$_SESSION['inlineMessage'] = $message;
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Request::i()->setCookie( 'inlineMessage', $message );
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user