Version 4.5.0

This commit is contained in:
Neo committed 2025-12-19 05:51:58 -08:00
1 parent 1a0c7fd3c2
commit f79dcf067a
3791 files changed
+248032 -76372

No files matched your search

+114 -28
View File
@@ -11,7 +11,7 @@
namespace IPS\Dispatcher;
/* 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;
@@ -99,9 +99,14 @@ class _Admin extends \IPS\Dispatcher\Standard
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'core.css', 'core', 'admin' ) );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'responsive.css', 'core', 'front' ) );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'responsive.css', 'core', 'admin' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js('admin_newFeatures.js', 'core') );
/* JS */
\IPS\Output::i()->globalControllers[] = 'core.admin.core.app';
if ( \IPS\Member::loggedIn()->getFeatureHighlights( FALSE ) )
{
\IPS\Output::i()->globalControllers[] = 'core.admin.core.newFeatures';
}
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'admin.js' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'jquery/jquery-ui.js', 'core', 'interface' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'jquery/jquery-touchpunch.js', 'core', 'interface' ) );
@@ -113,7 +118,7 @@ class _Admin extends \IPS\Dispatcher\Standard
{
/* These are just defaults in case we hit an immediate error, e.g. app or controller doesn't exist */
\IPS\Output::i()->sidebar['sidebar'] = \IPS\Theme::i()->getTemplate( 'global', 'core' )->sidebar( array(), 'core_overview' );
\IPS\Output::i()->sidebar['appmenu'] = \IPS\Theme::i()->getTemplate( 'global', 'core' )->appmenu( $menu, 'core' );
\IPS\Output::i()->sidebar['appmenu'] = \IPS\Theme::i()->getTemplate( 'global', 'core' )->appmenu( $menu, 'core', 'core_overview_dashboard' );
\IPS\Output::i()->sidebar['mobilenav'] = \IPS\Theme::i()->getTemplate( 'global', 'core' )->mobileNavigation( $menu, 'core' );
}
@@ -144,18 +149,18 @@ class _Admin extends \IPS\Dispatcher\Standard
/* If someone calls this from command line, while it wouldn't work, the key won't be set */
if( isset( $_SERVER['QUERY_STRING'] ) )
{
$url = $url->setQueryString( 'ref', base64_encode( preg_replace( '!adsess=((\w){32}|&)!', "", $_SERVER['QUERY_STRING'] ) ) );
$url = $url->setQueryString( 'ref', base64_encode( $_SERVER['QUERY_STRING'] ) );
}
}
else if( isset( $_SERVER['HTTP_REFERER'] ) )
{
$previous = preg_replace( "/^(.+?)\/\?/", "", $_SERVER['HTTP_REFERER'] );
$url = $url->setQueryString( 'ref', base64_encode( preg_replace( '!adsess=.*?(&|$)!', "", $previous ) ) );
$url = $url->setQueryString( 'ref', base64_encode( $previous ) );
}
\IPS\Output::i()->redirect( $url );
}
/* Init */
try
{
@@ -166,8 +171,14 @@ class _Admin extends \IPS\Dispatcher\Standard
\IPS\Output::i()->error( $e->getMessage(), '2S100/' . $e->getCode(), $e->getCode() === 4 ? 403 : 404, '' );
}
/* Unless there is a flag telling us we have specifically added CSRF checks, assume any AdminCP action which contains more than app/module/controller/id (i.e. anything with "do") requires CSRF-protection */
if ( !isset( $this->classname::$csrfProtected ) and array_diff( array_keys( \IPS\Request::i()->url()->queryString ), array( 'app', 'module', 'controller', 'id' ) ) )
{
\IPS\Session::i()->csrfCheck();
}
/* If we are in recovery mode, but not actually doing the recovery process, or logging in, then we need them to remove the constant */
if ( \IPS\RECOVERY_MODE === TRUE AND !in_array( $this->controller, array( 'recovery', 'login' ) ) )
if ( \IPS\RECOVERY_MODE === TRUE AND !\in_array( $this->controller, array( 'recovery', 'login' ) ) )
{
\IPS\Output::i()->error( 'recovery_mode_remove_constant', '1S107/3', 403, '' );
}
@@ -176,12 +187,14 @@ class _Admin extends \IPS\Dispatcher\Standard
if (
(
$this->module->key !== 'system' or
!in_array( $this->controller, array( 'login', 'language', 'theme', 'livesearch', 'editor', 'ajax' ) )
!\in_array( $this->controller, array( 'login', 'language', 'theme', 'livesearch', 'editor', 'ajax' ) )
) and
/* Every admin can view and manage his own acp notification */
( $this->module->key !== 'overview' or $this->controller !== 'notifications' ) and
(
$this->module->key !== 'members' or
$this->controller !== 'members' or
!in_array( \IPS\Request::i()->do, array( 'adminDetails', 'adminEmail', 'adminPassword' ) )
!\in_array( \IPS\Request::i()->do, array( 'adminDetails', 'adminEmail', 'adminPassword' ) )
) and
/* This is slightly hacky, but the upgrader was moved to the system module, however the ACP restriction is still set to overview.
To avoid unintentionally removing restrictions via an upgrade by moving the restriction, we reference the overview module for the restriction check instead */
@@ -191,6 +204,12 @@ class _Admin extends \IPS\Dispatcher\Standard
\IPS\Output::i()->error( 'no_module_permission', '2S107/1', 403, '' );
}
/* Support is not available for demos */
if ( \IPS\DEMO_MODE AND $this->module->application === 'core' AND $this->module->key === 'support' )
{
\IPS\Output::i()->error( 'demo_mode_function_blocked', '1S107/4', 403, '' );
}
/* ACP search keywords */
if ( \IPS\IN_DEV )
{
@@ -203,7 +222,7 @@ class _Admin extends \IPS\Dispatcher\Standard
$restrictions = array();
$file = \IPS\ROOT_PATH . "/applications/{$this->application->directory}/data/acprestrictions.json";
$file = $this->application->getApplicationPath() . "/data/acprestrictions.json";
if ( file_exists( $file ) )
{
$restrictions = json_decode( file_get_contents( $file ), TRUE );
@@ -239,9 +258,23 @@ class _Admin extends \IPS\Dispatcher\Standard
{
/* Work out what tab we're on */
$currentTab = NULL;
$currentItem = NULL;
foreach ( $this->application->acpMenu() as $moduleKey => $items )
{
if ( $moduleKey === $this->module->key )
/* If the module key does not match, we still need to inspect each item to see if a module_url that matches was specified */
$moduleUrlMatches = FALSE;
foreach( $items as $item )
{
if( isset( $item['module_url'] ) AND $item['module_url'] == $this->module->key )
{
$moduleUrlMatches = TRUE;
break;
}
}
if ( $moduleUrlMatches OR $moduleKey === $this->module->key )
{
foreach ( $items as $itemKey => $item )
{
@@ -249,28 +282,48 @@ class _Admin extends \IPS\Dispatcher\Standard
{
$currentTab = $item['tab'];
}
if ( $item['controller'] === $this->controller )
$additionalChecksPass = TRUE;
if( isset( $item['menu_checks'] ) AND \is_array( $item['menu_checks'] ) )
{
foreach( $item['menu_checks'] as $key => $value )
{
if( !isset( \IPS\Request::i()->$key ) OR \IPS\Request::i()->$key != $value )
{
$additionalChecksPass = FALSE;
break;
}
}
}
if ( $additionalChecksPass === TRUE and ( $item['controller'] === $this->controller or ( isset( $item['subcontrollers'] ) and \in_array( $this->controller, explode( ",", $item['subcontrollers'] ) ) ) ) )
{
break;
$controllerForKey = ( isset( $item['menu_controller'] ) ) ? $item['menu_controller'] : $item['controller'];
$currentItem = $this->application->directory . "_" . $moduleKey . "_" . $controllerForKey;
if( $currentTab != $item['tab'] )
{
$currentTab = $item['tab'];
}
}
}
}
}
if ( !$currentTab )
{
$currentTab = $this->application->directory;
}
/* Display */
if( $this->controller !== 'login' )
if ( isset( $menu['tabs'][ $currentTab ] ) )
{
if ( isset( $menu['tabs'][ $currentTab ] ) )
{
\IPS\Output::i()->sidebar['sidebar'] = \IPS\Theme::i()->getTemplate( 'global', 'core' )->sidebar( $menu['tabs'][ $currentTab ], $this->application->directory . '_' . $this->module->key );
}
\IPS\Output::i()->sidebar['appmenu'] = \IPS\Theme::i()->getTemplate( 'global', 'core' )->appmenu( $menu, $currentTab );
\IPS\Output::i()->sidebar['mobilenav'] = \IPS\Theme::i()->getTemplate( 'global', 'core' )->mobileNavigation( $menu, $currentTab );
\IPS\Output::i()->sidebar['sidebar'] = \IPS\Theme::i()->getTemplate( 'global', 'core' )->sidebar( $menu['tabs'][ $currentTab ], $this->application->directory . '_' . $this->module->key );
}
\IPS\Output::i()->sidebar['appmenu'] = \IPS\Theme::i()->getTemplate( 'global', 'core' )->appmenu( $menu, $currentTab, $currentItem );
\IPS\Output::i()->sidebar['mobilenav'] = \IPS\Theme::i()->getTemplate( 'global', 'core' )->mobileNavigation( $menu, $currentTab, $currentItem );
}
}
}
@@ -278,7 +331,7 @@ class _Admin extends \IPS\Dispatcher\Standard
/**
* Build Menu
*
* @param bool If TRUE, will rebuild
* @param bool $rebuild If TRUE, will rebuild
* @return array
*/
public function buildMenu( $rebuild=FALSE )
@@ -307,21 +360,54 @@ class _Admin extends \IPS\Dispatcher\Standard
{
foreach ( $items as $itemKey => $item )
{
if ( isset( $item['callback'] ) and !eval( $item['callback'] ) )
{
continue;
}
$moduleUrl = ( isset( $item['module_url'] ) ) ? $item['module_url'] : $moduleKey;
$moduleToCheck = ( isset( $item['restriction_module'] ) ) ? $item['restriction_module'] : $moduleKey;
if ( \IPS\Member::loggedIn()->hasAcpRestriction( $app, $moduleToCheck ) )
{
if ( !$item['restriction'] or \IPS\Member::loggedIn()->hasAcpRestriction( $app, $moduleToCheck, $item['restriction'] ) )
{
if( !$item['restriction'] )
{
$canAccess = TRUE;
}
else
{
if( mb_strpos( $item['restriction'], ',' ) )
{
$restrictions = explode( ',', $item['restriction'] );
}
else
{
$restrictions = array( $item['restriction'] );
}
$canAccess = FALSE;
foreach( $restrictions as $restrictionKey )
{
if( \IPS\Member::loggedIn()->hasAcpRestriction( $app, $moduleToCheck, $restrictionKey ) )
{
$canAccess = TRUE;
break;
}
}
}
if ( $canAccess )
{
$this->menu['tabs'][ $item['tab'] ][ "{$app->directory}_{$moduleKey}" ][ $itemKey ] = "app={$app->directory}&module={$moduleKey}&controller={$item['controller']}" . ( $item['do'] ? "&do={$item['do']}" : '' );
$this->menu['tabs'][ $item['tab'] ][ "{$app->directory}_{$moduleKey}" ][ $itemKey ] = "app={$app->directory}&module={$moduleUrl}&controller={$item['controller']}" . ( $item['do'] ? "&do={$item['do']}" : '' );
}
}
}
}
}
}
}
}
if ( $acpTabOrder !== NULL )
{
$_apps = array_keys( $acpTabOrder );
@@ -398,7 +484,7 @@ class _Admin extends \IPS\Dispatcher\Standard
}
catch( \UnderflowException $ex )
{
$this->acpTabOrder = array( 'core' => array(), 'community' => array(), 'members' => array(), 'nexus' => array(), 'cms' => array(), 'stats' => array(), 'customization' => array() );
$this->acpTabOrder = array( 'core' => array(), 'community' => array(), 'members' => array(), 'nexus' => array(), 'cms' => array(), 'stats' => array(), 'customization' => array(), 'marketplace' => array() );
}
\IPS\Request::i()->setCookie( 'acpTabs', json_encode( $this->acpTabOrder ) );
+19 -68
View File
@@ -11,7 +11,7 @@
namespace IPS\Dispatcher;
/* 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;
@@ -78,6 +78,10 @@ class _Api extends \IPS\Dispatcher
if ( $this->rawAccessToken )
{
$this->_setAccessToken();
if ( !$this->accessToken['scope'] or !json_decode( $this->accessToken['scope'] ) )
{
throw new \IPS\Api\Exception( 'NO_SCOPES', '3S290/B', 401, 'insufficient_scope' );
}
}
elseif ( $this->rawApiKey )
{
@@ -97,9 +101,9 @@ class _Api extends \IPS\Dispatcher
$response = json_encode( array( 'errorCode' => $e->exceptionCode, 'errorMessage' => $e->getMessage() ), JSON_PRETTY_PRINT );
/* Do we need to log this? */
if ( $this->rawApiKey !== 'test' and in_array( $e->exceptionCode, array( '2S290/8', '2S290/B', '3S290/7', '3S290/9' ) ) )
if ( $this->rawApiKey !== 'test' and \in_array( $e->exceptionCode, array( '2S290/8', '2S290/B', '3S290/7', '3S290/9' ) ) )
{
$this->_log( $response, $e->getCode(), in_array( $e->exceptionCode, array( '3S290/7', '3S290/9', '3S290/B' ) ) );
$this->_log( $response, $e->getCode(), \in_array( $e->exceptionCode, array( '3S290/7', '3S290/9', '3S290/B' ) ) );
}
/* Output */
@@ -154,48 +158,7 @@ class _Api extends \IPS\Dispatcher
*/
public function _setRawCredentials()
{
/* Check if an API Key or Access Token has been passed as a parameter in the query string. Because of the
obvious security issues with this, we do not recommend it, but sometimes it is the only choice */
if ( isset( \IPS\Request::i()->key ) )
{
$this->rawApiKey = \IPS\Request::i()->key;
return;
}
if ( isset( \IPS\Request::i()->access_token ) and ( !\IPS\OAUTH_REQUIRES_HTTPS or \IPS\Request::i()->isSecure() ) )
{
$this->rawAccessToken = \IPS\Request::i()->access_token;
return;
}
/* Look for an API key in an automatically decoded HTTP Basic header */
if ( isset( $_SERVER['PHP_AUTH_USER'] ) )
{
$this->rawApiKey = $_SERVER['PHP_AUTH_USER'];
return;
}
/* If we're still here, try to find an Authorization header - start with $_SERVER... */
$authorizationHeader = NULL;
foreach ( $_SERVER as $k => $v )
{
if ( mb_substr( $k, -18 ) == 'HTTP_AUTHORIZATION' )
{
$authorizationHeader = $v;
}
}
/* ...if we didn't find anything there, try apache_request_headers() */
if ( !$authorizationHeader and function_exists('apache_request_headers') )
{
$headers = @apache_request_headers();
if ( isset( $headers['Authorization'] ) )
{
$authorizationHeader = $headers['Authorization'];
}
}
/* If we managed to get one, set if it's an API Key or an Access Token */
if ( $authorizationHeader )
if ( $authorizationHeader = \IPS\Request::i()->authorizationHeader() )
{
if ( mb_substr( $authorizationHeader, 0, 7 ) === 'Bearer ' and ( !\IPS\OAUTH_REQUIRES_HTTPS or \IPS\Request::i()->isSecure() ) )
{
@@ -263,7 +226,7 @@ class _Api extends \IPS\Dispatcher
{
$this->apiKey = \IPS\Api\Key::load( $this->rawApiKey );
if ( $this->apiKey->allowed_ips and !in_array( \IPS\Request::i()->ipAddress(), explode( ',', $this->apiKey->allowed_ips ) ) )
if ( $this->apiKey->allowed_ips and !\in_array( \IPS\Request::i()->ipAddress(), explode( ',', $this->apiKey->allowed_ips ) ) )
{
throw new \IPS\Api\Exception( 'IP_ADDRESS_NOT_ALLOWED', '2S290/8', 403 );
}
@@ -283,22 +246,7 @@ class _Api extends \IPS\Dispatcher
{
try
{
$exploded = explode( '_', $this->rawAccessToken );
if ( !isset( $exploded[0] ) or !isset( $exploded[1] ) )
{
throw new \UnderflowException;
}
$this->accessToken = \IPS\Db::i()->select( '*', 'core_oauth_server_access_tokens', array( 'client_id=? AND access_token=?', $exploded[0], $exploded[1] ) )->first();
if ( $this->accessToken['access_token_expires'] and $this->accessToken['access_token_expires'] < time() )
{
throw new \IPS\Api\Exception( 'EXPIRED_ACCESS_TOKEN', '1S290/E', 401, 'invalid_token' );
}
if ( !$this->accessToken['scope'] or !json_decode( $this->accessToken['scope'] ) )
{
throw new \IPS\Api\Exception( 'NO_SCOPES', '3S290/B', 401, 'insufficient_scope' );
}
$this->accessToken = \IPS\Api\OAuthClient::accessTokenDetails( $this->rawAccessToken );
}
catch ( \UnderflowException $e )
{
@@ -317,7 +265,7 @@ class _Api extends \IPS\Dispatcher
{
if ( isset( $_SERVER['HTTP_X_IPS_LANGUAGE'] ) )
{
$this->language = \IPS\Lang::load( intval( $_SERVER['HTTP_X_IPS_LANGUAGE'] ) );
$this->language = \IPS\Lang::load( \intval( $_SERVER['HTTP_X_IPS_LANGUAGE'] ) );
}
else
{
@@ -381,9 +329,10 @@ class _Api extends \IPS\Dispatcher
$response = $controller->execute( $pathBits, $shouldLog );
/* Send Output */
$output = json_encode( $response->getOutput(), JSON_PRETTY_PRINT );
$output = $response->getOutput();
$this->language->parseOutputForDisplay( $output );
$this->_respond( $output, $response->httpCode, NULL, $shouldLog, TRUE );
$this->_respond( json_encode( $output, JSON_PRETTY_PRINT ), $response->httpCode, NULL, $shouldLog, TRUE );
}
catch ( \IPS\Api\Exception $e )
{
@@ -430,9 +379,11 @@ class _Api extends \IPS\Dispatcher
/**
* Output response
*
* @param string $response Response to output
* @param int $httpResponseCode HTTP Response Code
* @param bool $parseLanguage If response should be language parsed
* @param string $response Response to output
* @param int $httpResponseCode HTTP Response Code
* @param NULL|string $oauthError OAuth error
* @param bool $log Whether or not to log the response
* @return void
*/
protected function _respond( $response, $httpResponseCode, $oauthError=NULL, $log=FALSE )
{
+9 -1
View File
@@ -11,7 +11,7 @@
namespace IPS\Dispatcher;
/* 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;
@@ -26,7 +26,15 @@ class _Build extends \IPS\Dispatcher
* @brief Controller Location
*/
public $controllerLocation = 'front';
/**
* @brief Application
*/
public $application = 'core';
/**
* @brief Module
*/
public $module = 'system';
/**
+9 -4
View File
@@ -11,7 +11,7 @@
namespace IPS\Dispatcher;
/* 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;
@@ -26,6 +26,11 @@ abstract class _Controller
* @brief Base URL
*/
public $url;
/**
* @brief Is this for displaying "content"? Affects if advertisements may be shown
*/
public $isContentPage = TRUE;
/**
* Constructor
@@ -37,7 +42,7 @@ abstract class _Controller
{
if ( $url === NULL )
{
$class = get_called_class();
$class = \get_called_class();
$exploded = explode( '\\', $class );
$this->url = \IPS\Http\Url::internal( "app={$exploded[1]}&module={$exploded[4]}&controller={$exploded[5]}", \IPS\Dispatcher::i()->controllerLocation );
}
@@ -148,7 +153,7 @@ abstract class _Controller
{
if ( isset( \IPS\Request::i()->page ) and \IPS\Request::i()->page > 1 )
{
$params['page'] = intval( \IPS\Request::i()->page );
$params['page'] = \intval( \IPS\Request::i()->page );
}
if ( isset( \IPS\Request::i()->embedDo ) )
{
@@ -207,7 +212,7 @@ abstract class _Controller
\IPS\Output::i()->metaTags['robots'] = 'noindex';
}
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->embedInternal( $output, $js ), 200, 'text/html', \IPS\Output::i()->httpHeaders );
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->embedInternal( $output, $js ), 200, 'text/html' );
}
/**
+18 -12
View File
@@ -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;
@@ -47,7 +47,7 @@ abstract class _Dispatcher
{
if( static::$instance === NULL )
{
$class = get_called_class();
$class = \get_called_class();
if( $class == 'IPS\\Dispatcher' )
{
@@ -59,14 +59,14 @@ abstract class _Dispatcher
if( static::$instance->controllerLocation != 'setup' )
{
$_redirect = FALSE;
if ( !file_exists( \IPS\ROOT_PATH . '/conf_global.php' ) )
if ( !file_exists( \IPS\SITE_FILES_PATH . '/conf_global.php' ) )
{
$_redirect = TRUE;
}
else
{
require \IPS\ROOT_PATH . '/conf_global.php';
require \IPS\SITE_FILES_PATH . '/conf_global.php';
if( !isset( $INFO['sql_database'] ) )
{
@@ -94,9 +94,15 @@ abstract class _Dispatcher
if( $_redirect === TRUE )
{
/* conf_global.php does not exist, forward to installer */
$url = ( \IPS\Request::i()->isSecure() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . rtrim( dirname( $_SERVER['SCRIPT_NAME'] ), '/' );
\IPS\Output::i()->redirect( "{$url}/" . \IPS\CP_DIRECTORY . "/install/" );
/* conf_global.php does not exist, forward to installer - we'll do this manually to avoid any code in Output.php that anticipates the installation already being complete (such as setting CSP header in __construct()) */
$url = ( \IPS\Request::i()->isSecure() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . rtrim( \dirname( $_SERVER['SCRIPT_NAME'] ), '/' );
header( "HTTP/1.1 307 Temporary Redirect" );
header( "Cache-Control: no-cache, no-store, must-revalidate" );
header( "Pragma: no-cache" );
header( "Expires: 0" );
header( "Location: {$url}/" . \IPS\CP_DIRECTORY . "/install/" );
exit;
}
}
@@ -107,12 +113,12 @@ abstract class _Dispatcher
}
/**
* Controller Classname
* @brief Controller Classname
*/
protected $classname;
/**
* Controller instance
* @brief Controller instance
*/
public $dispatcherController;
@@ -158,12 +164,12 @@ abstract class _Dispatcher
/* If we're still here - output */
if ( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->blankTemplate( \IPS\Output::i()->output ), 200, 'text/html', \IPS\Output::i()->httpHeaders );
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->blankTemplate( \IPS\Output::i()->output ), 200, 'text/html' );
}
else
{
/* Just prefetch this to save a query later */
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( \IPS\Output::i()->title, \IPS\Output::i()->output, array( 'app' => \IPS\Dispatcher::i()->application->directory, 'module' => \IPS\Dispatcher::i()->module->key, 'controller' => \IPS\Dispatcher::i()->controller ) ), 200, 'text/html', \IPS\Output::i()->httpHeaders );
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( \IPS\Output::i()->title, \IPS\Output::i()->output, array( 'app' => \IPS\Dispatcher::i()->application->directory, 'module' => \IPS\Dispatcher::i()->module->key, 'controller' => \IPS\Dispatcher::i()->controller ) ), 200, 'text/html' );
}
}
}
+1 -1
View File
@@ -11,7 +11,7 @@
namespace IPS\Dispatcher;
/* 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;
+115 -50
View File
@@ -11,7 +11,7 @@
namespace IPS\Dispatcher;
/* 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;
@@ -26,11 +26,12 @@ class _Front extends \IPS\Dispatcher\Standard
* Controller Location
*/
public $controllerLocation = 'front';
/**
* Init
*
* @return void
* @return void
* @throws \Exception
*/
public function init()
{
@@ -45,6 +46,11 @@ class _Front extends \IPS\Dispatcher\Standard
{
$protocol = '1.1';
}
/* Don't allow the setup in progress page to be cached, it will only be displayed for a very short period of time */
header( "Cache-Control: no-cache, no-store, must-revalidate" );
header( "Pragma: no-cache" );
header( "Expires: 0" );
if ( \IPS\CIC and ! \IPS\Session\Front::loggedIn() and ! \IPS\Session\Front::i()->userAgent->spider )
{
@@ -114,14 +120,22 @@ class _Front extends \IPS\Dispatcher\Standard
\IPS\Output::i()->error( $e->getMessage(), '2S100/' . $e->getCode(), $e->getCode() === 4 ? 403 : 404, '' );
}
}
/* Set a referral cookie */
if( \IPS\Settings::i()->ref_on and isset( \IPS\Request::i()->_rid ) )
{
\IPS\Request::i()->setCookie( 'referred_by', \intval( \IPS\Request::i()->_rid ), \IPS\DateTime::create()->add( new \DateInterval( 'P1Y' ) ) );
}
/* Enable sidebar by default (controllers can turn it off if needed) */
\IPS\Output::i()->sidebar['enabled'] = ( \IPS\Request::i()->isAjax() ) ? FALSE : TRUE;
/* Add in RSS Feeds */
foreach( \IPS\core\Rss::getAllFeeds() AS $feed_id => $feed )
foreach( \IPS\core\Rss::getStore() AS $feed_id => $feed )
{
if ( $feed->groups == '*' OR \IPS\Member::loggedIn()->inGroup( $feed->groups ) )
$feed = \IPS\core\Rss::constructFromData( $feed );
if ( $feed->_enabled AND ( $feed->groups == '*' OR \IPS\Member::loggedIn()->inGroup( $feed->groups ) ) )
{
\IPS\Output::i()->rssFeeds[ $feed->_title ] = $feed->url();
}
@@ -164,7 +178,7 @@ class _Front extends \IPS\Dispatcher\Standard
{
\IPS\Output::i()->sidebar = FALSE;
\IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimal';
if( !in_array( $this->controller, array( 'contact', 'warnings' ) ) )
if( !\in_array( $this->controller, array( 'contact', 'warnings', 'privacy', 'guidelines', 'metatags' ) ) )
{
\IPS\Output::i()->showBanned();
}
@@ -174,13 +188,13 @@ class _Front extends \IPS\Dispatcher\Standard
/* Do we need more info from the member or do they need to validate? */
/* This controllers should always be accessible, no matter if the member is awaiting validation or needs to set up the email or name */
$legalControllers = array( 'privacy', 'contact', 'terms' );
$legalControllers = array( 'privacy', 'contact', 'terms', 'embed', 'metatags' );
/* Do we need more info from the member or do they need to validate? */
if( \IPS\Member::loggedIn()->member_id and $this->controller !== 'language' and $this->controller !== 'theme' and $this->controller !== 'ajax' and !\in_array( $this->controller, $legalControllers ) )
{
/* Need their name or email... */
if( ( \IPS\Member::loggedIn()->real_name === '' or !\IPS\Member::loggedIn()->email ) and $this->controller !== 'register' )
if( ( \IPS\Member::loggedIn()->real_name === '' or !\IPS\Member::loggedIn()->email ) and $this->controller !== 'register' and $this->controller !== 'login' )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=register&do=complete' )->setQueryString( 'ref', base64_encode( \IPS\Request::i()->url() ) ) );
}
@@ -195,18 +209,29 @@ class _Front extends \IPS\Dispatcher\Standard
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=register&do=reconfirm', 'front', 'register' )->setQueryString( 'ref', base64_encode( \IPS\Request::i()->url() ) ) );
}
/* Have required profile actions that need completing */
else if ( \IPS\Settings::i()->quick_register AND !\IPS\Member::loggedIn()->members_bitoptions['profile_completed'] AND !in_array( $this->controller, array( 'register', 'login', 'redirect', 'ajax', 'settings' ) ) AND $completion = \IPS\Member::loggedIn()->profileCompletion() AND count( $completion['required'] ) )
else if ( \IPS\Settings::i()->quick_register AND !\IPS\Member::loggedIn()->members_bitoptions['profile_completed'] AND !\in_array( $this->controller, array( 'register', 'login', 'redirect', 'ajax', 'settings', 'checkout', 'pixabay', 'editor' ) ) AND $completion = \IPS\Member::loggedIn()->profileCompletion() AND \count( $completion['required'] ) )
{
foreach( $completion['required'] AS $id => $completed )
{
if ( $completed === FALSE )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=system&controller=register&do=finish", 'front', 'register' ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=system&controller=register&do=finish&_new=1", 'front', 'register' )->setQueryString( 'ref', base64_encode( \IPS\Request::i()->url() ) ) );
}
}
}
/* Need to reset password */
elseif ( $this->controller !== 'settings' AND ( !isset( \IPS\Request::i()->area ) OR \IPS\Request::i()->area !== 'password' ) )
{
foreach( \IPS\Login::methods() AS $method )
{
if ( $url = $method->forcePasswordResetUrl( \IPS\Member::loggedIn(), \IPS\Request::i()->url() ) )
{
\IPS\Output::i()->redirect( $url );
}
}
}
/* Need to set up MFA... */
elseif ( !in_array( $this->controller, array( 'register', 'login', 'redirect', 'ajax', 'settings' ) ) )
elseif ( !\in_array( $this->controller, array( 'register', 'login', 'redirect', 'ajax', 'settings', 'embed' ) ) )
{
$haveAcceptableHandlers = FALSE;
$haveConfiguredHandler = FALSE;
@@ -237,17 +262,24 @@ class _Front extends \IPS\Dispatcher\Standard
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&do=initialMfa', 'front', 'settings' )->setQueryString( 'ref', base64_encode( \IPS\Request::i()->url() ) ) );
}
}
}
}
}
/* Permission Check */
if ( !\IPS\Member::loggedIn()->canAccessModule( $this->module ) )
try
{
if ( !\IPS\Member::loggedIn()->member_id and isset( \IPS\Request::i()->_mfaLogin ) )
if ( !\IPS\Member::loggedIn()->canAccessModule( $this->module ) )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=system&controller=login", 'front', 'login' )->setQueryString( '_mfaLogin', 1 ) );
if ( !\IPS\Member::loggedIn()->member_id and isset( \IPS\Request::i()->_mfaLogin ) )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=system&controller=login", 'front', 'login' )->setQueryString( '_mfaLogin', 1 ) );
}
\IPS\Output::i()->error( ( \IPS\Member::loggedIn()->member_id ? 'no_module_permission' : 'no_module_permission_guest' ), '2S100/2', 403, 'no_module_permission_admin' );
}
\IPS\Output::i()->error( ( \IPS\Member::loggedIn()->member_id ? 'no_module_permission' : 'no_module_permission_guest' ), '2S100/2', 403, 'no_module_permission_admin' );
}
catch( \InvalidArgumentException $e ) # invalid module
{
\IPS\Output::i()->error( 'requested_route_404', '2S160/5', 404, '' );
}
/* Stuff for output */
@@ -276,7 +308,7 @@ class _Front extends \IPS\Dispatcher\Standard
/* Figure out what the global search is */
foreach ( $this->application->extensions( 'core', 'ContentRouter' ) as $object )
{
if ( count( $object->classes ) === 1 )
if ( \count( $object->classes ) === 1 )
{
$classes = $object->classes;
foreach ( $classes as $class )
@@ -319,18 +351,28 @@ class _Front extends \IPS\Dispatcher\Standard
/* If they are accessing "index.php/whatever", we want "index.php?/whatever */
if ( !( $correctUrl instanceof \IPS\Http\Url ) and $url instanceof \IPS\Http\Url\Internal and mb_strpos( $url->data[ \IPS\Http\Url::COMPONENT_PATH ], '/index.php/' ) !== FALSE )
{
$pathFromBaseUrl = mb_substr( $url->data[ \IPS\Http\Url::COMPONENT_PATH ], mb_strlen( \IPS\Http\Url::internal('')->data[ \IPS\Http\Url::COMPONENT_PATH ] ) );
if ( preg_match( '/index.php\//', $pathFromBaseUrl ) )
$pathFromBaseUrl = ltrim( mb_substr( $url->data[ \IPS\Http\Url::COMPONENT_PATH ], mb_strlen( \IPS\Http\Url::internal('')->data[ \IPS\Http\Url::COMPONENT_PATH ] ) ), '/' );
if ( mb_substr( $pathFromBaseUrl, 0, 10 ) === 'index.php/' )
{
$correctUrl = \IPS\Http\Url\Friendly::friendlyUrlFromComponent( 0, trim( mb_substr( $pathFromBaseUrl, 10 ), '/' ), $url->queryString );
}
}
/* Redirect to the correct URL if we got one */
if ( $correctUrl instanceof \IPS\Http\Url )
{
if( $correctUrl->seoPagination and \in_array( 'page', array_keys( $url->hiddenQueryString ) ) )
{
$correctUrl = $correctUrl->setPage( 'page', $url->hiddenQueryString['page'] );
}
\IPS\Output::i()->redirect( $correctUrl, NULL, 301 );
}
/* Check pagination */
if ( $url instanceof \IPS\Http\Url\Friendly and $url->seoPagination and \in_array( 'page', array_keys( $url->queryString ) ) )
{
\IPS\Output::i()->redirect( $url->setPage( 'page', $url->queryString['page'] )->stripQueryString('page'), NULL, 301 );
}
}
/* If the accessed URL is friendly, set the "real" query string properties */
@@ -346,7 +388,13 @@ class _Front extends \IPS\Dispatcher\Standard
{
$this->_controller = NULL;
}
/* If this is a POST request, and this key has already been populated, do not overwrite it as this allows form input to be ignored and the query string data used */
if ( \IPS\Request::i()->requestMethod() == 'POST' and isset( \IPS\Request::i()->$k ) )
{
continue;
}
\IPS\Request::i()->$k = $v;
}
}
@@ -382,25 +430,6 @@ class _Front extends \IPS\Dispatcher\Standard
*/
protected function checkCached()
{
/* Store page view in Redis */
if ( \IPS\CACHE_METHOD == 'Redis' and \IPS\CIC and !\IPS\Request::i()->isAjax() )
{
try
{
if( \IPS\Redis::i() === NULL )
{
throw new \RedisException( 'Redis instance is NULL', -1 );
}
\IPS\Redis::i()->logPageHit();
}
catch( \RedisException $e )
{
/* Sometimes the connection can be reset - we don't want to let an uncaught exception bubble up in those cases */
\IPS\Log::log( $e, 'redis' );
}
}
/* If this is a guest and there's a full cached page, we can serve that */
if( mb_strtolower( $_SERVER['REQUEST_METHOD'] ) == 'get' and !isset( \IPS\Request::i()->csrfKey ) and !isset( \IPS\Request::i()->_mfaLogin ) and !\IPS\Session\Front::loggedIn() and !isset( \IPS\Request::i()->cookie['noCache'] ) and !\IPS\Request::i()->ipAddressIsBanned() and \IPS\CACHE_PAGE_TIMEOUT )
{
@@ -423,13 +452,25 @@ class _Front extends \IPS\Dispatcher\Standard
/* Which theme? */
$theme = isset( \IPS\Request::i()->cookie['theme'] ) ? \IPS\Request::i()->cookie['theme'] : '';
/* Make sure a session is initiated */
\IPS\Session\Front::i();
/* Get cache */
try
{
$cache = \IPS\Data\Cache::i()->getWithExpire( 'page_' . md5( (int) \IPS\Request::i()->isSecure() . ( !empty( $_SERVER['HTTP_HOST'] ) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME'] ) . '/' . $_SERVER['REQUEST_URI'] ) . "_{$language}_{$theme}", TRUE );
$cache = \IPS\Output\Cache::i()->get( 'page_' . md5( (string) \IPS\Request::i()->url() ) . "_{$language}_{$theme}", TRUE );
$sessionId = isset( \IPS\Request::i()->cookie['IPSSessionFront'] ) ? \IPS\Request::i()->cookie['IPSSessionFront'] : \IPS\Session::i()->id;
\IPS\Output::i()->sendOutput( str_replace( '{{csrfKey}}', md5( '& 0&' . $sessionId ), $cache['output'] ), $cache['code'], $cache['contentType'], $cache['httpHeaders'], FALSE, TRUE );
/* RSS feeds can be shown cached, all other pages contain a cache buster once logged in */
$cache['meta']['httpHeaders'] += \IPS\Output::getCacheHeaders( $cache['meta']['lastUpdated'], \IPS\CACHE_PAGE_TIMEOUT );
if( isset( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) AND $_SERVER['HTTP_IF_MODIFIED_SINCE'] == $cache['meta']['httpHeaders']['Last-Modified'] )
{
\IPS\Output::i()->sendOutput( NULL, 304, $cache['meta']['contentType'], $cache['meta']['httpHeaders'], FALSE, TRUE, FALSE, FALSE );
}
\IPS\Output::i()->sendOutput( str_replace( '{{csrfKey}}', md5( \IPS\SUITE_UNIQUE_KEY . '&& 0&' . $sessionId ), $cache['output'] ), $cache['meta']['code'], $cache['meta']['contentType'], $cache['meta']['httpHeaders'], FALSE, TRUE );
}
catch ( \OutOfRangeException $e ) {}
}
@@ -466,11 +507,11 @@ class _Front extends \IPS\Dispatcher\Standard
{
$widgets = array();
if ( ! isset( \IPS\Output::i()->sidebar['widgets'] ) OR ! is_array( \IPS\Output::i()->sidebar['widgets'] ) )
if ( ! isset( \IPS\Output::i()->sidebar['widgets'] ) OR ! \is_array( \IPS\Output::i()->sidebar['widgets'] ) )
{
\IPS\Output::i()->sidebar['widgets'] = array();
}
try
{
$widgetConfig = \IPS\Db::i()->select( '*', 'core_widget_areas', array( 'app=? AND module=? AND controller=?', $this->application->directory, $this->module->key, $this->controller ) );
@@ -497,7 +538,7 @@ class _Front extends \IPS\Dispatcher\Standard
}
}
if( count( $widgets ) )
if( \count( $widgets ) )
{
if ( ( \IPS\Data\Cache::i() instanceof \IPS\Data\Cache\None ) and ! \IPS\Theme::isUsingTemplateDiskCache() )
{
@@ -514,7 +555,7 @@ class _Front extends \IPS\Dispatcher\Standard
}
}
if( count( $templateLoad ) )
if( \count( $templateLoad ) )
{
\IPS\Data\Store::i()->loadIntoMemory( $templateLoad );
}
@@ -522,6 +563,7 @@ class _Front extends \IPS\Dispatcher\Standard
$widgetObjects = array();
$storeLoad = array();
$googleFonts = array();
foreach ( $widgets as $areaKey => $area )
{
foreach ( $area as $widget )
@@ -540,6 +582,23 @@ class _Front extends \IPS\Dispatcher\Standard
{
$storeLoad[] = $_widget->cacheKey;
}
if ( \in_array( 'IPS\Widget\Builder', class_implements( $_widget ) ) )
{
if ( ! empty( $_widget->configuration['widget_adv__font'] ) and $_widget->configuration['widget_adv__font'] !== 'inherit' )
{
$font = $_widget->configuration['widget_adv__font'];
if ( \mb_substr( $font, -6 ) === ' black' )
{
$fontWeight = 900;
$font = \mb_substr( $font, 0, -6 ) . ':400,900';
}
$googleFonts[ $font ] = $font;
}
}
$widgetObjects[ $areaKey ][] = $_widget;
}
catch ( \Exception $e )
@@ -549,7 +608,12 @@ class _Front extends \IPS\Dispatcher\Standard
}
}
if( ( \IPS\Data\Cache::i() instanceof \IPS\Data\Cache\None ) and count( $storeLoad ) )
if ( \count( $googleFonts ) )
{
\IPS\Output::i()->linkTags['googlefonts'] = array('rel' => 'stylesheet', 'href' => "https://fonts.googleapis.com/css?family=" . implode( "|", array_values( $googleFonts ) ) . "&display=swap");
}
if( ( \IPS\Data\Cache::i() instanceof \IPS\Data\Cache\None ) and \count( $storeLoad ) )
{
\IPS\Data\Store::i()->loadIntoMemory( $storeLoad );
}
@@ -563,10 +627,10 @@ class _Front extends \IPS\Dispatcher\Standard
}
}
}
/* Meta tags */
\IPS\Output::i()->buildMetaTags();
/* Check MFA */
$this->checkMfa();
@@ -617,6 +681,7 @@ class _Front extends \IPS\Dispatcher\Standard
{
\IPS\Output::i()->globalControllers[] = 'core.front.core.app';
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'front.js' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'front_core.js', 'core', 'front' ) );
if ( \IPS\Member::loggedIn()->members_bitoptions['bw_using_skin_gen'] AND ( isset( \IPS\Request::i()->cookie['vseThemeId'] ) AND \IPS\Request::i()->cookie['vseThemeId'] ) and \IPS\Member::loggedIn()->isAdmin() and \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'customization', 'theme_easy_editor' ) )
{
@@ -674,7 +739,7 @@ class _Front extends \IPS\Dispatcher\Standard
{
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/query_log.css', 'core', 'front' ) );
}
if ( \IPS\CACHING_LOG )
if ( \IPS\CACHING_LOG or \IPS\REDIS_LOG )
{
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/caching_log.css', 'core', 'front' ) );
}
+150
View File
@@ -0,0 +1,150 @@
<?php
/**
* @brief GraphQL API Dispatcher
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Community Suite
* @since 3 Dec 2015
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Dispatcher;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/* Register our GraphQL library */
\IPS\IPS::$PSR0Namespaces['GraphQL'] = \IPS\ROOT_PATH . "/system/3rd_party/graphql-php";
/**
* @brief API Dispatcher
*/
class _GraphQL extends Api
{
/**
* @brief Path
*/
public $path = '/graphql';
/**
* Init
*
* @return void
* @throws \DomainException
*/
public function init()
{
try
{
/* Check our IP address isn't banned */
$this->_checkIpAddressIsAllowed();
/* If this is a pre-flight request, acknowldge it */
// @todo review this approach
if( $_SERVER['REQUEST_METHOD'] === 'OPTIONS' )
{
$this->_respond( '', 200 );
}
/* Authenticate */
$client = NULL;
$this->_setRawCredentials();
try
{
if ( $this->rawAccessToken )
{
$this->_setAccessToken();
$client = \IPS\Api\OAuthClient::load( $this->accessToken['client_id'] );
\IPS\Member::$loggedInMember = \IPS\Member::load( $this->accessToken['member_id'] );
}
else
{
$client = \IPS\Api\OAuthClient::load( $this->rawApiKey );
\IPS\Member::$loggedInMember = new \IPS\Member;
}
}
catch ( \OutOfRangeException $e )
{
throw new \IPS\Api\Exception( 'INVALID_API_KEY', '3S290/7', 401 );
}
/* Check that the OAuth client has access to the GraphQL API */
if ( !$client->graphql )
{
throw new \IPS\Api\Exception( 'NO_GRAPHQL_ACCESS', '2S291/3', 403 );
}
}
catch ( \IPS\Api\Exception $e )
{
/* Build resonse */
$response = json_encode( array( 'errors' => array( array( 'message' => $e->getMessage(), 'id' => $e->exceptionCode ) ) ), JSON_PRETTY_PRINT );
/* Do we need to log this? */
if ( \in_array( $e->exceptionCode, array( '2S290/8', '2S290/B', '3S290/7', '3S290/9' ) ) )
{
$this->_log( $response, $e->getCode(), \in_array( $e->exceptionCode, array( '3S290/7', '3S290/9', '3S290/B' ) ) );
}
/* Output */
$this->_respond( $response, $e->getCode(), $e->oauthError );
}
}
/**
* Run
*
* @return void
*/
public function run()
{
try
{
/* Work out the query (can either use a JSON-encoded or form-encoded) body */
$query = NULL;
$variables = NULL;
if( \IPS\Request::i()->query )
{
$query = \IPS\Request::i()->query;
$variables = ( isset( \IPS\Request::i()->variables ) AND \is_array( \IPS\Request::i()->variables ) ) ? \IPS\Request::i()->variables : [];
}
elseif ( $json = json_decode( file_get_contents('php://input'), TRUE ) )
{
$query = $json['query'];
if( isset( $json['variables'] ) )
{
$variables = $json['variables'];
}
}
/* Execute! */
$result = \GraphQL\GraphQL::executeQuery(
new \GraphQL\Schema([
'query' => \IPS\Api\GraphQL\TypeRegistry::query(),
'mutation' => \IPS\Api\GraphQL\TypeRegistry::mutation()
]),
$query,
NULL, // $rootValue
[
'member' => \IPS\Member::loggedIn()
], // $context
$variables
);
/* Convert result into JSON and send */
$output = $result->toArray( ( \IPS\IN_DEV OR \IPS\DEBUG_GRAPHQL ) ? \GraphQL\Error\Debug::INCLUDE_DEBUG_MESSAGE | \GraphQL\Error\Debug::INCLUDE_TRACE : false );
\IPS\Member::loggedIn()->language()->parseOutputForDisplay( $output );
$this->_respond( json_encode( $output, JSON_PRETTY_PRINT ), 200 );
}
catch ( \Exception $e )
{
$response = json_encode( array( 'errors' => array( array( 'message' => ( \IPS\IN_DEV OR \IPS\DEBUG_GRAPHQL ) ? $e->getMessage() : 'UNKNOWN_ERROR', 'id' => $e->getCode() ) ) ), JSON_PRETTY_PRINT );
$this->_respond( $response, 500 );
}
}
}
+7 -8
View File
@@ -11,7 +11,7 @@
namespace IPS\Dispatcher;
/* 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;
@@ -116,7 +116,7 @@ class _Setup extends \IPS\Dispatcher
{
try
{
file_put_contents( \IPS\ROOT_PATH . '/conf_global.php', '' );
file_put_contents( \IPS\ROOT_PATH. '/conf_global.php', '' );
}
catch ( \Exception $e ) { }
}
@@ -137,7 +137,7 @@ class _Setup extends \IPS\Dispatcher
/* Upgrader Checks */
else
{
if ( !file_exists( \IPS\ROOT_PATH . '/conf_global.php' ) )
if ( !file_exists( \IPS\SITE_FILES_PATH . '/conf_global.php' ) )
{
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( \IPS\Member::loggedIn()->language()->addToStack('upgrade_error'), '', \IPS\Member::loggedIn()->language()->addToStack('no_conf_global'), \IPS\ROOT_PATH ), 200, 'text/html', array(), FALSE );
}
@@ -152,7 +152,7 @@ class _Setup extends \IPS\Dispatcher
}
}
require \IPS\ROOT_PATH . '/conf_global.php';
require \IPS\SITE_FILES_PATH . '/conf_global.php';
if ( !isset( $INFO ) )
{
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( \IPS\Member::loggedIn()->language()->addToStack('upgrade_error'), '', \IPS\Member::loggedIn()->language()->addToStack('bad_conf_global'), \IPS\ROOT_PATH ), 200, 'text/html', array(), FALSE );
@@ -197,8 +197,7 @@ class _Setup extends \IPS\Dispatcher
if( !@session_start() )
{
$lastError = error_get_last();
\IPS\Output::i()->error( \IPS\Member::loggedIn()->language()->addToStack( 'session_no_good', FALSE, array( 'sprintf' => array( $lastError['message'] ) ) ), '4S109/5', 500, '' );
\IPS\Output::i()->error( \IPS\Member::loggedIn()->language()->addToStack( 'session_no_good', FALSE, array( 'sprintf' => array( \IPS\IPS::$lastError ? \IPS\IPS::$lastError->getMessage() : NULL ) ) ), '4S109/5', 500, '' );
}
if( $this->classname != 'IPS\core\modules\setup\upgrade\done' and $this->setupLocation != 'install' and $this->step > 1 AND ( !isset( $_SESSION['uniqueKey'] ) OR $_SESSION['uniqueKey'] != \IPS\Request::i()->key ) )
@@ -223,11 +222,11 @@ class _Setup extends \IPS\Dispatcher
/* If we're still here - output */
if ( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->blankTemplate( \IPS\Output::i()->output ), 200, 'text/html', \IPS\Output::i()->httpHeaders, FALSE, FALSE, TRUE, FALSE );
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->blankTemplate( \IPS\Output::i()->output ), 200, 'text/html', array(), FALSE, FALSE, TRUE, FALSE );
}
else
{
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( \IPS\Output::i()->title, \IPS\Output::i()->output ), 200, 'text/html', \IPS\Output::i()->httpHeaders, FALSE, FALSE, TRUE, FALSE );
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( \IPS\Output::i()->title, \IPS\Output::i()->output ), 200, 'text/html', array(), FALSE, FALSE, TRUE, FALSE );
}
}
+13 -16
View File
@@ -11,7 +11,7 @@
namespace IPS\Dispatcher;
/* 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;
@@ -58,7 +58,7 @@ abstract class _Standard extends \IPS\Dispatcher
}
}
if ( count( \IPS\Lang::languages() ) > 1 )
if ( \count( \IPS\Lang::languages() ) > 1 )
{
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'flags.css', 'core', 'global' ) );
}
@@ -82,20 +82,15 @@ abstract class _Standard extends \IPS\Dispatcher
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'plugins.js', 'core', 'plugins' ) );
\IPS\Output::i()->jsVars['date_format'] = mb_strtolower( \IPS\Member::loggedIn()->language()->preferredDateFormat() );
\IPS\Output::i()->jsVars['date_first_day'] = 0;
\IPS\Output::i()->jsVars['remote_image_proxy'] = intval( \IPS\Settings::i()->remote_image_proxy );
\IPS\Output::i()->jsVars['ipb_url_filter_option'] = \IPS\Settings::i()->ipb_url_filter_option;
\IPS\Output::i()->jsVars['url_filter_any_action'] = \IPS\Settings::i()->url_filter_any_action;
\IPS\Output::i()->jsVars['bypass_profanity'] = intval( \IPS\Member::loggedIn()->group['g_bypass_badwords'] );
\IPS\Output::i()->jsVars['bypass_profanity'] = \intval( \IPS\Member::loggedIn()->group['g_bypass_badwords'] );
\IPS\Output::i()->jsVars['emoji_style'] = ( \IPS\Settings::i()->getFromConfGlobal('sql_utf8mb4') === TRUE ) ? \IPS\Settings::i()->emoji_style : 'disabled';
\IPS\Output::i()->jsVars['emoji_shortcodes'] = \IPS\Settings::i()->emoji_shortcodes;
\IPS\Output::i()->jsVars['emoji_ascii'] = \IPS\Settings::i()->emoji_ascii;
\IPS\Output::i()->jsVars['emoji_cache'] = \IPS\Settings::i()->emoji_cache;
/* Load the howler JS package if notifications are enabled */
if( !\IPS\Member::loggedIn()->members_bitoptions['disable_notification_sounds'] )
{
array_unshift( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'howler/howler.core.min.js', 'core', 'interface' )[0] );
}
\IPS\Output::i()->jsVars['emoji_shortcodes'] = (bool) \IPS\Settings::i()->emoji_shortcodes;
\IPS\Output::i()->jsVars['emoji_ascii'] = (bool) \IPS\Settings::i()->emoji_ascii;
\IPS\Output::i()->jsVars['emoji_cache'] = (int) \IPS\Settings::i()->emoji_cache;
\IPS\Output::i()->jsVars['image_jpg_quality'] = (int) \IPS\Settings::i()->image_jpg_quality ?: 85;
\IPS\Output::i()->jsVars['cloud2'] = (bool) \IPS\CIC2;
}
}
@@ -229,7 +224,7 @@ abstract class _Standard extends \IPS\Dispatcher
/* Set controller */
$this->controller = isset( \IPS\Request::i()->controller ) ? \IPS\Request::i()->controller : $this->module->default_controller;
if( is_array( $this->controller ) )
if( \is_array( $this->controller ) )
{
$this->controller = NULL;
throw new \DomainException( 'requested_route_404', 7 );
@@ -311,15 +306,17 @@ abstract class _Standard extends \IPS\Dispatcher
public function __destruct()
{
/* If you visit and you are redirected to installer, this code should not run */
if( !file_exists( \IPS\ROOT_PATH . '/conf_global.php' ) )
if( !file_exists( \IPS\SITE_FILES_PATH . '/conf_global.php' ) )
{
return;
}
if ( \IPS\Session\Front::loggedIn() and \IPS\Settings::i()->task_use_cron == 'normal' )
if ( \IPS\Member::loggedIn()->member_id and \IPS\Settings::i()->task_use_cron == 'normal' and !\IPS\Request::i()->isAjax() )
{
try
{
\IPS\Db::i()->readWriteSeparation = FALSE;
$task = \IPS\Task::queued();
if ( $task )
{