Version 5.0.0 beta 1
This commit is contained in:
1 parent
25ddeb65d6
commit
15c7beabc5
6736 files changed
+627902
-497943
No files matched your search
+344
-164
@@ -11,64 +11,102 @@
|
||||
namespace IPS\Dispatcher;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use DateInterval;
|
||||
use DomainException;
|
||||
use IPS\Application;
|
||||
use IPS\DateTime;
|
||||
use IPS\Db;
|
||||
use IPS\Developer;
|
||||
use IPS\Helpers\Menu\Link;
|
||||
use IPS\Http\Url;
|
||||
use IPS\Lang;
|
||||
use IPS\Member;
|
||||
use IPS\Application\Module;
|
||||
use IPS\Output;
|
||||
use IPS\Platform\Bridge;
|
||||
use IPS\Request;
|
||||
use IPS\Session;
|
||||
use IPS\Settings;
|
||||
use IPS\Theme;
|
||||
use UnderflowException;
|
||||
use function array_merge;
|
||||
use function count;
|
||||
use function defined;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
use function substr;
|
||||
use const IPS\CIC;
|
||||
use const IPS\DEMO_MODE;
|
||||
use const IPS\ENFORCE_ACCESS;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin CP Dispatcher
|
||||
*/
|
||||
class _Admin extends \IPS\Dispatcher\Standard
|
||||
class Admin extends Standard
|
||||
{
|
||||
/**
|
||||
* Controller Location
|
||||
*/
|
||||
public $controllerLocation = 'admin';
|
||||
public string $controllerLocation = 'admin';
|
||||
|
||||
/**
|
||||
* @brief Cached Menu
|
||||
*/
|
||||
protected $menu = NULL;
|
||||
protected ?array $menu = NULL;
|
||||
|
||||
/**
|
||||
* @brief Search Keywords
|
||||
*/
|
||||
public $searchKeywords = array();
|
||||
public array $searchKeywords = array();
|
||||
|
||||
/**
|
||||
* @brief ACP Restrictions (for search keyword editing)
|
||||
*/
|
||||
public $moduleRestrictions = array();
|
||||
public array $moduleRestrictions = array();
|
||||
|
||||
/**
|
||||
* @brief ACP Restriction for the current menu item (for search keyword editing)
|
||||
*/
|
||||
public $menuRestriction = NULL;
|
||||
public ?string $menuRestriction = NULL;
|
||||
|
||||
const PLATFORM_PERMISSION_UPGRADE_NEEDED = -1;
|
||||
const PLATFORM_PERMISSION_FUTURE_EXPIRES = -2;
|
||||
|
||||
/**
|
||||
* Cache built URL map
|
||||
* @var null
|
||||
*/
|
||||
public static mixed $permissionUrls = NULL;
|
||||
|
||||
/**
|
||||
* Init
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init()
|
||||
public function init() : void
|
||||
{
|
||||
\IPS\Output::i()->sidebar['appmenu'] = '';
|
||||
Output::i()->sidebar['appmenu'] = '';
|
||||
|
||||
/* Sync stuff when in developer mode */
|
||||
if ( \IPS\IN_DEV )
|
||||
{
|
||||
\IPS\Developer::sync();
|
||||
Developer::sync();
|
||||
}
|
||||
|
||||
if ( \IPS\Member::loggedIn()->member_id )
|
||||
if ( Member::loggedIn()->member_id )
|
||||
{
|
||||
/* Build the menu */
|
||||
$menu = $this->buildMenu();
|
||||
|
||||
/* Do we need to figure out the default? */
|
||||
if ( !isset( \IPS\Request::i()->app ) )
|
||||
if ( !isset( Request::i()->app ) )
|
||||
{
|
||||
foreach ( $menu['tabs'] as $app => $appData )
|
||||
{
|
||||
@@ -77,7 +115,7 @@ class _Admin extends \IPS\Dispatcher\Standard
|
||||
parse_str( $menu['defaults'][ $app ], $defaultQueryString );
|
||||
foreach ( $defaultQueryString as $k => $v )
|
||||
{
|
||||
\IPS\Request::i()->$k = $v;
|
||||
Request::i()->$k = $v;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -89,54 +127,66 @@ class _Admin extends \IPS\Dispatcher\Standard
|
||||
static::baseCss();
|
||||
static::baseJs();
|
||||
|
||||
/* Loader extension */
|
||||
foreach( Application::allExtensions( 'core', 'Loader' ) as $loader )
|
||||
{
|
||||
foreach( $loader->js() as $js )
|
||||
{
|
||||
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, $js );
|
||||
}
|
||||
foreach( $loader->css() as $css )
|
||||
{
|
||||
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, $css );
|
||||
}
|
||||
}
|
||||
|
||||
/* Stuff needed for output */
|
||||
if ( !\IPS\Request::i()->isAjax() )
|
||||
if ( !Request::i()->isAjax() )
|
||||
{
|
||||
/* Special grouped CSS files */
|
||||
\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' ) );
|
||||
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'core/core.css', 'core', 'admin' ) );
|
||||
|
||||
/* JS */
|
||||
\IPS\Output::i()->globalControllers[] = 'core.admin.core.app';
|
||||
\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' ) );
|
||||
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'jquery/jquery.menuaim.js', 'core', 'interface' ) );
|
||||
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'jquery/jquery.nestedSortable.js', 'core', 'interface' ) );
|
||||
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'admin_core.js', 'core', 'front' ) );
|
||||
Output::i()->globalControllers[] = 'core.admin.core.app';
|
||||
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'admin.js' ) );
|
||||
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'jquery/jquery-ui.js', 'core', 'interface' ) );
|
||||
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'jquery/jquery-touchpunch.js', 'core', 'interface' ) );
|
||||
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'jquery/jquery.menuaim.js', 'core', 'interface' ) );
|
||||
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'jquery/jquery.nestedSortable.js', 'core', 'interface' ) );
|
||||
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'admin_core.js', 'core', 'front' ) );
|
||||
|
||||
if ( \IPS\Member::loggedIn()->member_id )
|
||||
if ( Member::loggedIn()->member_id )
|
||||
{
|
||||
/* 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', 'core_overview_dashboard' );
|
||||
\IPS\Output::i()->sidebar['mobilenav'] = \IPS\Theme::i()->getTemplate( 'global', 'core' )->mobileNavigation( $menu, 'core' );
|
||||
Output::i()->sidebar['sidebar'] = Theme::i()->getTemplate( 'global', 'core' )->sidebar( array(), 'core_overview' );
|
||||
Output::i()->sidebar['appmenu'] = Theme::i()->getTemplate( 'global', 'core' )->appmenu( $menu, 'core', 'core_overview_dashboard' );
|
||||
Output::i()->sidebar['mobilenav'] = Theme::i()->getTemplate( 'global', 'core' )->mobileNavigation( $menu, 'core' );
|
||||
Output::i()->sidebar['quickLinks'] = $this->_getAcpQuickLinks();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Check we're logged in and we have ACP access */
|
||||
if( ( !\IPS\Member::loggedIn()->member_id or !\IPS\Member::loggedIn()->isAdmin() )
|
||||
and ( \IPS\Request::i()->module !== 'system' or \IPS\Request::i()->controller !== 'login' )
|
||||
and ( !\IPS\ENFORCE_ACCESS )
|
||||
if( ( !Member::loggedIn()->member_id or !Member::loggedIn()->isAdmin() )
|
||||
and ( Request::i()->module !== 'system' or Request::i()->controller !== 'login' )
|
||||
and ( !ENFORCE_ACCESS )
|
||||
)
|
||||
{
|
||||
/* Make sure the right protocol is used. IIS, for example, does not like protocol relative URL's in redirects. (Ref: 970629) */
|
||||
$protocol = \IPS\Http\Url::PROTOCOL_HTTP;
|
||||
if ( \substr( \IPS\Settings::i()->base_url, 0, 5 ) == 'https' )
|
||||
$protocol = Url::PROTOCOL_HTTP;
|
||||
if ( substr( Settings::i()->base_url, 0, 5 ) == 'https' )
|
||||
{
|
||||
$protocol = \IPS\Http\Url::PROTOCOL_HTTPS;
|
||||
$protocol = Url::PROTOCOL_HTTPS;
|
||||
}
|
||||
|
||||
$url = \IPS\Http\Url::internal( "app=core&module=system&controller=login", 'admin', NULL, array(), $protocol );
|
||||
$url = Url::internal( "app=core&module=system&controller=login", 'admin', NULL, array(), $protocol );
|
||||
|
||||
if ( \IPS\Session::i()->error )
|
||||
if ( Session::i()->error and is_object( Session::i()->error ) )
|
||||
{
|
||||
$url = $url->setQueryString( 'error', \IPS\Session::i()->error->getMessage() );
|
||||
$url = $url->setQueryString( 'error', Session::i()->error );
|
||||
}
|
||||
|
||||
if( !\IPS\Request::i()->isAjax() )
|
||||
if( !Request::i()->isAjax() )
|
||||
{
|
||||
/* If someone calls this from command line, while it wouldn't work, the key won't be set */
|
||||
if( isset( $_SERVER['QUERY_STRING'] ) )
|
||||
@@ -150,7 +200,7 @@ class _Admin extends \IPS\Dispatcher\Standard
|
||||
$url = $url->setQueryString( 'ref', base64_encode( $previous ) );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->redirect( $url );
|
||||
Output::i()->redirect( $url );
|
||||
}
|
||||
|
||||
/* Init */
|
||||
@@ -158,65 +208,75 @@ class _Admin extends \IPS\Dispatcher\Standard
|
||||
{
|
||||
parent::init();
|
||||
}
|
||||
catch ( \DomainException $e )
|
||||
catch ( DomainException $e )
|
||||
{
|
||||
\IPS\Output::i()->error( $e->getMessage(), '2S100/' . $e->getCode(), $e->getCode() === 4 ? 403 : 404, '' );
|
||||
Output::i()->error( $e->getMessage(), '2S100/' . $e->getCode(), $e->getCode() === 4 ? 403 : 404, '' );
|
||||
}
|
||||
|
||||
/* Loader extension - check for redirects */
|
||||
foreach( Application::allExtensions( 'core', 'Loader' ) as $loader )
|
||||
{
|
||||
/* First come first serve here */
|
||||
if( $redirect = $loader->checkForRedirect() )
|
||||
{
|
||||
Output::i()->redirect( $redirect );
|
||||
}
|
||||
}
|
||||
|
||||
/* 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' ) ) )
|
||||
if ( !isset( $this->classname::$csrfProtected ) and array_diff( array_keys( Request::i()->url()->queryString ), array( 'app', 'module', 'controller', 'id' ) ) )
|
||||
{
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
Session::i()->csrfCheck();
|
||||
}
|
||||
|
||||
\IPS\Db::i()->readWriteSeparation = FALSE;
|
||||
Db::i()->readWriteSeparation = FALSE;
|
||||
if ( isset( $this->classname::$allowRWSeparation ) and $this->classname::$allowRWSeparation )
|
||||
{
|
||||
\IPS\Db::i()->readWriteSeparation = TRUE;
|
||||
Db::i()->readWriteSeparation = TRUE;
|
||||
}
|
||||
|
||||
/* 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 AND !in_array( $this->controller, array( 'recovery', 'login' ) ) )
|
||||
{
|
||||
\IPS\Output::i()->error( 'recovery_mode_remove_constant', '1S107/3', 403, '' );
|
||||
Output::i()->error( 'recovery_mode_remove_constant', '1S107/3', 403, '' );
|
||||
}
|
||||
|
||||
/* Permission Check */
|
||||
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( 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 */
|
||||
!\IPS\Member::loggedIn()->hasAcpRestriction( $this->application, ( $this->application->directory === 'core' and $this->module->key === 'system' and $this->controller === 'upgrade' ) ? \IPS\Application\Module::get( 'core', 'overview', 'admin' ) : $this->module )
|
||||
!Member::loggedIn()->hasAcpRestriction( $this->application, ( $this->application->directory === 'core' and $this->module->key === 'system' and $this->controller === 'upgrade' ) ? Application\Module::get( 'core', 'overview', 'admin' ) : $this->module )
|
||||
)
|
||||
{
|
||||
\IPS\Output::i()->error( 'no_module_permission', '2S107/1', 403, '' );
|
||||
Output::i()->error( 'no_module_permission', '2S107/1', 403, '' );
|
||||
}
|
||||
|
||||
if( ! \IPS\Application::appIsEnabled( $this->application->directory ) )
|
||||
if( ! Application::appIsEnabled( $this->application->directory ) )
|
||||
{
|
||||
\IPS\Output::i()->error( 'requested_route_404', '2S107/5', 404, '' );
|
||||
Output::i()->error( 'requested_route_404', '2S107/5', 404, '' );
|
||||
}
|
||||
|
||||
/* Support is not available for demos */
|
||||
if ( \IPS\DEMO_MODE AND $this->module->application === 'core' AND $this->module->key === 'support' )
|
||||
if ( DEMO_MODE AND $this->module->application === 'core' AND $this->module->key === 'support' )
|
||||
{
|
||||
\IPS\Output::i()->error( 'demo_mode_function_blocked', '1S107/4', 403, '' );
|
||||
Output::i()->error( 'demo_mode_function_blocked', '1S107/4', 403, '' );
|
||||
}
|
||||
|
||||
/* ACP search keywords */
|
||||
if ( \IPS\IN_DEV )
|
||||
{
|
||||
foreach ( \IPS\Db::i()->select( '*', 'core_acp_search_index' ) as $word )
|
||||
foreach ( Db::i()->select( '*', 'core_acp_search_index' ) as $word )
|
||||
{
|
||||
$this->searchKeywords[ $word['url'] ]['lang_key'] = $word['lang_key'];
|
||||
$this->searchKeywords[ $word['url'] ]['restriction'] = $word['restriction'];
|
||||
@@ -245,7 +305,7 @@ class _Admin extends \IPS\Dispatcher\Standard
|
||||
{
|
||||
foreach ( $appMenu[ $this->module->key ] as $menuItem )
|
||||
{
|
||||
if ( $menuItem['restriction'] and $menuItem['controller'] == $this->controller and ( !$menuItem['do'] or ( isset( \IPS\Request::i()->do ) and $menuItem['do'] == \IPS\Request::i()->do ) ) )
|
||||
if ( $menuItem['restriction'] and $menuItem['controller'] == $this->controller and ( !$menuItem['do'] or ( isset( Request::i()->do ) and $menuItem['do'] == Request::i()->do ) ) )
|
||||
{
|
||||
$this->menuRestriction = $menuItem['restriction'];
|
||||
}
|
||||
@@ -254,10 +314,10 @@ class _Admin extends \IPS\Dispatcher\Standard
|
||||
}
|
||||
|
||||
/* More stuff needed for output */
|
||||
if ( !\IPS\Request::i()->isAjax() )
|
||||
if ( !Request::i()->isAjax() )
|
||||
{
|
||||
/* Menu and base navigation */
|
||||
if ( \IPS\Member::loggedIn()->member_id )
|
||||
if ( Member::loggedIn()->member_id )
|
||||
{
|
||||
/* Work out what tab we're on */
|
||||
$currentTab = NULL;
|
||||
@@ -288,23 +348,45 @@ class _Admin extends \IPS\Dispatcher\Standard
|
||||
|
||||
$additionalChecksPass = TRUE;
|
||||
|
||||
if( isset( $item['menu_checks'] ) AND \is_array( $item['menu_checks'] ) )
|
||||
/* If we set a do=value in the menu, add that to the list of menu checks */
|
||||
if( isset( $item['do'] ) and $item['do'] )
|
||||
{
|
||||
if( isset( $item['menu_checks']['do'] ) and !is_array( $item['menu_checks']['do'] ) )
|
||||
{
|
||||
$item['menu_checks']['do'] = array( $item['menu_checks']['do'] );
|
||||
}
|
||||
$item['menu_checks']['do'][] = $item['do'];
|
||||
}
|
||||
|
||||
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( isset( Request::i()->$key ) )
|
||||
{
|
||||
if( is_array( $value ) and !in_array( Request::i()->$key, $value ) )
|
||||
{
|
||||
$additionalChecksPass = false;
|
||||
break;
|
||||
}
|
||||
elseif( !is_array( $value ) and Request::i()->$key != $value )
|
||||
{
|
||||
$additionalChecksPass = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$additionalChecksPass = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $additionalChecksPass === TRUE and ( $item['controller'] === $this->controller or ( isset( $item['subcontrollers'] ) and \in_array( $this->controller, explode( ",", $item['subcontrollers'] ) ) ) ) )
|
||||
if ( $additionalChecksPass === TRUE and ( $item['controller'] === $this->controller or ( isset( $item['subcontrollers'] ) and in_array( $this->controller, explode( ",", $item['subcontrollers'] ) ) ) ) )
|
||||
{
|
||||
$controllerForKey = ( isset( $item['menu_controller'] ) ) ? $item['menu_controller'] : $item['controller'];
|
||||
|
||||
$currentItem = $this->application->directory . "_" . $moduleKey . "_" . $controllerForKey;
|
||||
$currentItem = $item['activemenuitem'] ?? ( $this->application->directory . "_" . $moduleKey . "_" . $controllerForKey );
|
||||
|
||||
if( $currentTab != $item['tab'] )
|
||||
{
|
||||
@@ -323,10 +405,10 @@ class _Admin extends \IPS\Dispatcher\Standard
|
||||
/* Display */
|
||||
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 );
|
||||
Output::i()->sidebar['sidebar'] = 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 );
|
||||
Output::i()->sidebar['appmenu'] = Theme::i()->getTemplate( 'global', 'core' )->appmenu( $menu, $currentTab, $currentItem );
|
||||
Output::i()->sidebar['mobilenav'] = Theme::i()->getTemplate( 'global', 'core' )->mobileNavigation( $menu, $currentTab, $currentItem );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -334,23 +416,23 @@ class _Admin extends \IPS\Dispatcher\Standard
|
||||
/**
|
||||
* Build Menu
|
||||
*
|
||||
* @param bool $rebuild If TRUE, will rebuild
|
||||
* @return array
|
||||
* @param bool $rebuild If TRUE, will rebuild
|
||||
* @return array|null
|
||||
*/
|
||||
public function buildMenu( $rebuild=FALSE )
|
||||
public function buildMenu(bool $rebuild=FALSE ): ?array
|
||||
{
|
||||
$acpTabOrder = $this->_getAcpTabOrder();
|
||||
|
||||
|
||||
if ( $this->menu === NULL or $rebuild === TRUE )
|
||||
{
|
||||
$this->menu = array( 'tabs' => array(), 'defaults' => array() );
|
||||
|
||||
foreach ( \IPS\Application::applications() as $app )
|
||||
$this->menu = array( 'tabs' => array(), 'defaults' => array(), 'badges' );
|
||||
|
||||
foreach ( Application::applications() as $app )
|
||||
{
|
||||
if ( \IPS\Application::appIsEnabled( $app->directory ) and \IPS\Application::load( $app->directory )->canAccess() )
|
||||
if ( Application::appIsEnabled( $app->directory ) and Application::load( $app->directory )->canAccess() )
|
||||
{
|
||||
$appMenu = $app->acpMenu();
|
||||
|
||||
|
||||
if ( $acpTabOrder !== NULL and isset( $acpTabOrder[ $app->directory ] ) and $app->directory == 'nexus' )
|
||||
{
|
||||
uksort( $appMenu, function( $a, $b ) use ( $acpTabOrder, $app )
|
||||
@@ -361,18 +443,18 @@ class _Admin extends \IPS\Dispatcher\Standard
|
||||
|
||||
foreach ( $appMenu as $moduleKey => $items )
|
||||
{
|
||||
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 ) )
|
||||
{
|
||||
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 ( Member::loggedIn()->hasAcpRestriction( $app, $moduleToCheck ) )
|
||||
{
|
||||
if( !$item['restriction'] )
|
||||
{
|
||||
$canAccess = TRUE;
|
||||
@@ -392,7 +474,7 @@ class _Admin extends \IPS\Dispatcher\Standard
|
||||
|
||||
foreach( $restrictions as $restrictionKey )
|
||||
{
|
||||
if( \IPS\Member::loggedIn()->hasAcpRestriction( $app, $moduleToCheck, $restrictionKey ) )
|
||||
if( Member::loggedIn()->hasAcpRestriction( $app, $moduleToCheck, $restrictionKey ) )
|
||||
{
|
||||
$canAccess = TRUE;
|
||||
break;
|
||||
@@ -400,28 +482,60 @@ class _Admin extends \IPS\Dispatcher\Standard
|
||||
}
|
||||
}
|
||||
|
||||
if ( $canAccess )
|
||||
{
|
||||
$this->menu['tabs'][ $item['tab'] ][ "{$app->directory}_{$moduleKey}" ][ $itemKey ] = "app={$app->directory}&module={$moduleUrl}&controller={$item['controller']}" . ( $item['do'] ? "&do={$item['do']}" : '' );
|
||||
}
|
||||
}
|
||||
}
|
||||
if( isset( $item['activemenuitem'] ) and $item['activemenuitem'] )
|
||||
{
|
||||
$groupKey = substr( $item['activemenuitem'], 0, strrpos( $item['activemenuitem'], '_' ) );
|
||||
$itemKey = substr( $item['activemenuitem'], strrpos( $item['activemenuitem'], '_' ) + 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
$groupKey = $app->directory . "_" . $moduleKey;
|
||||
}
|
||||
|
||||
if ( $canAccess )
|
||||
{
|
||||
$this->menu['tabs'][ $item['tab'] ][ "{$groupKey}" ][ $itemKey ] = "app={$app->directory}&module={$moduleUrl}&controller={$item['controller']}" . ( $item['do'] ? "&do={$item['do']}" : '' );
|
||||
}
|
||||
|
||||
/* Custom badge */
|
||||
if( isset( $item['badge'] ) and $item['badge'] )
|
||||
{
|
||||
$this->menu['badges'][ "{$groupKey}_{$itemKey}" ] = $item['badge'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Developer Center */
|
||||
if( \IPS\IN_DEV )
|
||||
{
|
||||
$currentApp = Request::i()->appKey ?? 'core';
|
||||
foreach( $this->menu['tabs']['developer'] as $module => $controllers )
|
||||
{
|
||||
foreach( $controllers as $key => $url )
|
||||
{
|
||||
parse_str( $url, $params );
|
||||
$params['module'] = 'developer';
|
||||
$params['appKey'] = $currentApp;
|
||||
$this->menu['tabs']['developer'][ $module ][ $key ] = http_build_query( $params );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $acpTabOrder !== NULL )
|
||||
{
|
||||
$_apps = array_keys( $acpTabOrder );
|
||||
uksort( $this->menu['tabs'], function($a, $b) use ( $_apps )
|
||||
{
|
||||
if( !\in_array( $a, $_apps ) )
|
||||
if( !in_array( $a, $_apps ) )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( !\in_array( $b, $_apps ) )
|
||||
if( !in_array( $b, $_apps ) )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
@@ -437,12 +551,12 @@ class _Admin extends \IPS\Dispatcher\Standard
|
||||
{
|
||||
uksort( $this->menu['tabs'][ $app ], function($a, $b) use ( $submenu )
|
||||
{
|
||||
if( !\in_array( $a, $submenu ) )
|
||||
if( !in_array( $a, $submenu ) )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( !\in_array( $b, $submenu ) )
|
||||
if( !in_array( $b, $submenu ) )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
@@ -475,47 +589,71 @@ class _Admin extends \IPS\Dispatcher\Standard
|
||||
}
|
||||
}
|
||||
|
||||
return $this->menu;
|
||||
return Bridge::i()->alterAcpMenu( $this->menu );
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an array of quick links for the top menu
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
protected function _getAcpQuickLinks() : ?array
|
||||
{
|
||||
$links = [];
|
||||
|
||||
if( Member::loggedIn()->hasAcpRestriction( 'core', 'support', 'get_support' ) )
|
||||
{
|
||||
$links[] = new Link( Url::internal( "app=core&module=support&controller=support&_new=1" ), 'support' );
|
||||
$links[] = new Link( Url::internal( "app=core&module=support&controller=systemLogs" ), 'system_logs' );
|
||||
$links[] = new Link( Url::internal( "app=core&module=support&controller=support&do=clearCaches" )->csrf(), 'health_clear_caches_button', dataAttributes: [ 'data-role' => 'clearCaches' ] );
|
||||
}
|
||||
|
||||
if( Member::loggedIn()->hasAcpRestriction( 'core', 'settings', 'advanced_manage_tasks' ) )
|
||||
{
|
||||
$links[] = new Link( Url::internal( "app=core&module=settings&controller=advanced&do=tasks" ), 'task_manager' );
|
||||
}
|
||||
|
||||
return count( $links ) ? $links : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Cached ACP tab order
|
||||
*/
|
||||
protected $acpTabOrder = NULL;
|
||||
protected ?array $acpTabOrder = NULL;
|
||||
|
||||
/**
|
||||
* Figure out the ACP tab order
|
||||
*
|
||||
* @return array
|
||||
* @return array|null
|
||||
*/
|
||||
public function _getAcpTabOrder()
|
||||
public function _getAcpTabOrder(): ?array
|
||||
{
|
||||
if( $this->acpTabOrder !== NULL )
|
||||
{
|
||||
return $this->acpTabOrder;
|
||||
}
|
||||
|
||||
if ( isset( \IPS\Request::i()->cookie['acpTabs'] ) AND !\IPS\Settings::i()->acp_menu_cookie_rebuild )
|
||||
|
||||
if ( isset( Request::i()->cookie['acpTabs'] ) AND ! Settings::i()->acp_menu_cookie_rebuild )
|
||||
{
|
||||
$this->acpTabOrder = json_decode( \IPS\Request::i()->cookie['acpTabs'], TRUE );
|
||||
$this->acpTabOrder = json_decode( Request::i()->cookie['acpTabs'], TRUE );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( \IPS\Settings::i()->acp_menu_cookie_rebuild )
|
||||
if ( Settings::i()->acp_menu_cookie_rebuild )
|
||||
{
|
||||
\IPS\Settings::i()->changeValues( array( 'acp_menu_cookie_rebuild' => 0 ) );
|
||||
Settings::i()->changeValues( array( 'acp_menu_cookie_rebuild' => 0 ) );
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
$this->acpTabOrder = json_decode( \IPS\Db::i()->select( 'data', 'core_acp_tab_order', array( 'id=?', \IPS\Member::loggedIn()->member_id ) )->first(), TRUE );
|
||||
$this->acpTabOrder = json_decode( Db::i()->select( 'data', 'core_acp_tab_order', array( 'id=?', Member::loggedIn()->member_id ) )->first(), TRUE );
|
||||
}
|
||||
catch( \UnderflowException $ex )
|
||||
catch( UnderflowException $ex )
|
||||
{
|
||||
$this->acpTabOrder = array( 'core' => array(), 'community' => array(), 'members' => array(), 'nexus' => array(), 'cms' => array(), 'stats' => array(), 'customization' => array() );
|
||||
}
|
||||
|
||||
\IPS\Request::i()->setCookie( 'acpTabs', json_encode( $this->acpTabOrder ) );
|
||||
Request::i()->setCookie( 'acpTabs', json_encode( $this->acpTabOrder ) );
|
||||
}
|
||||
|
||||
return $this->acpTabOrder;
|
||||
@@ -524,60 +662,24 @@ class _Admin extends \IPS\Dispatcher\Standard
|
||||
/**
|
||||
* Display a link in a custom format
|
||||
*
|
||||
* @param string $url The URL from the menu system
|
||||
* @param string $url The URL from the menu system
|
||||
* @return string|null
|
||||
*/
|
||||
public function acpMenuCustom( $url ): ?string
|
||||
public function acpMenuCustom( string $url ): ?string
|
||||
{
|
||||
if ( !\IPS\Application::appIsEnabled( 'cloud' ) AND $url == 'app=core&module=smartcommunity&controller=cloud' )
|
||||
{
|
||||
$url = \IPS\Http\Url::internal( $url, 'admin' );
|
||||
$title = \IPS\Member::loggedIn()->language()->addToStack( "menu__core_smartcommunity_features" );
|
||||
return <<<EOF
|
||||
<a href='{$url}' style="opacity:30%">{$title} <i class="fa fa-lock"></i></a>
|
||||
EOF;
|
||||
|
||||
}
|
||||
return NULL;
|
||||
return Bridge::i()->acpMenuCustom( $url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Do we have permission to use this module?
|
||||
*
|
||||
* @param \IPS\Application $app Application
|
||||
* @param \IPS\Module|string $module Module
|
||||
* @param Application $app Application
|
||||
* @param string|Module $module Module
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPermission( $app, $module )
|
||||
public function hasPermission( Application $app, Module|string $module ): bool
|
||||
{
|
||||
return \IPS\Member::loggedIn()->hasAcpRestriction( $app, $module );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check ACP Permission
|
||||
*
|
||||
* @param string $key Permission Key
|
||||
* @param \IPS\Application|null $app Application (NULL will default to current)
|
||||
* @param \IPS\Module|string|null $module Module (NULL will default to current)
|
||||
* @param boolean $return Return boolean (true/false) instead of throwing an error
|
||||
* @return void
|
||||
*/
|
||||
public function checkAcpPermission( $key, $app=NULL, $module=NULL, $return=FALSE )
|
||||
{
|
||||
if ( !\IPS\Member::loggedIn()->hasAcpRestriction( ( $app ?: $this->application ), ( $module ?: $this->module ), $key ) )
|
||||
{
|
||||
if ( $return )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
\IPS\Output::i()->error( 'no_module_permission', '2S107/2', 403, '' );
|
||||
}
|
||||
|
||||
if ( $return )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return Member::loggedIn()->hasAcpRestriction( $app, $module );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -587,23 +689,23 @@ EOF;
|
||||
*/
|
||||
final public static function showSwitchLink(): bool
|
||||
{
|
||||
if ( \IPS\CIC )
|
||||
if ( CIC )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Don't show if installation was less than a month ago */
|
||||
if ( time() < ( (int) \IPS\Settings::i()->board_start + ( 86400 * 30 ) ) )
|
||||
if ( time() < ( (int) Settings::i()->board_start + ( 86400 * 30 ) ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! isset( \IPS\Request::i()->cookie['acpLinkSnooze'] ) )
|
||||
if ( ! isset( Request::i()->cookie['acpLinkSnooze'] ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
$value = json_decode( \IPS\Request::i()->cookie['acpLinkSnooze'], true );
|
||||
$value = json_decode( Request::i()->cookie['acpLinkSnooze'], true );
|
||||
|
||||
if ( $value['hits'] < 3 )
|
||||
{
|
||||
@@ -632,4 +734,82 @@ EOF;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base CSS
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function baseCss() : void
|
||||
{
|
||||
parent::baseCss();
|
||||
|
||||
/* Stuff for output */
|
||||
if ( !Request::i()->isAjax() )
|
||||
{
|
||||
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'core/core.css', 'core', 'admin' ) );
|
||||
}
|
||||
|
||||
if ( count( Lang::languages() ) > 1 )
|
||||
{
|
||||
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'flags.css', 'core', 'global' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finish
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function finish() : void
|
||||
{
|
||||
if( !Request::i()->isAjax() )
|
||||
{
|
||||
$dismiss = [];
|
||||
$updated = false;
|
||||
if ( isset( Request::i()->cookie['acpDeprecations'] ) )
|
||||
{
|
||||
$dismiss = json_decode( Request::i()->cookie['acpDeprecations'], TRUE );
|
||||
|
||||
if ( ! is_array( $dismiss ) )
|
||||
{
|
||||
$dismiss = [];
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( Request::i()->deprecationDismiss ) )
|
||||
{
|
||||
$updated = true;
|
||||
$dismiss[ Request::i()->deprecationDismiss ] = DateTime::create()->add( new DateInterval( 'P1M' ) )->getTimestamp();
|
||||
}
|
||||
|
||||
|
||||
foreach ( $dismiss as $key => $value )
|
||||
{
|
||||
if ( $value < time() )
|
||||
{
|
||||
$updated = true;
|
||||
unset( $dismiss[ $key ] );
|
||||
}
|
||||
}
|
||||
|
||||
if ( $updated )
|
||||
{
|
||||
Request::i()->setCookie( 'acpDeprecations', json_encode( $dismiss ), DateTime::create()->add( new DateInterval( 'P1Y' ) ) );
|
||||
}
|
||||
|
||||
if ( isset( Request::i()->deprecationDismiss ) )
|
||||
{
|
||||
Output::i()->redirect( Request::i()->url()->stripQueryString( 'deprecationDismiss' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/* Loader Extension */
|
||||
foreach( Application::allExtensions( 'core', 'Loader' ) as $loader )
|
||||
{
|
||||
$loader->onFinish();
|
||||
}
|
||||
|
||||
parent::finish();
|
||||
}
|
||||
}
|
||||
+114
-88
@@ -11,51 +11,77 @@
|
||||
namespace IPS\Dispatcher;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use DateInterval;
|
||||
use DomainException;
|
||||
use IPS\Api\Exception as ApiException;
|
||||
use IPS\Api\Key;
|
||||
use IPS\Api\OAuthClient;
|
||||
use IPS\Application;
|
||||
use IPS\Data\Store;
|
||||
use IPS\DateTime;
|
||||
use IPS\Db;
|
||||
use IPS\Db\Exception;
|
||||
use IPS\Dispatcher;
|
||||
use IPS\Http\Url;
|
||||
use IPS\Lang;
|
||||
use IPS\Log;
|
||||
use IPS\Output;
|
||||
use IPS\Request;
|
||||
use IPS\Settings;
|
||||
use OutOfRangeException;
|
||||
use UnderflowException;
|
||||
use function count;
|
||||
use function defined;
|
||||
use function in_array;
|
||||
use function intval;
|
||||
use const IPS\OAUTH_REQUIRES_HTTPS;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief API Dispatcher
|
||||
*/
|
||||
class _Api extends \IPS\Dispatcher
|
||||
class Api extends Dispatcher
|
||||
{
|
||||
/**
|
||||
* @brief Controller Location
|
||||
*/
|
||||
public $controllerLocation = 'api';
|
||||
public string $controllerLocation = 'api';
|
||||
|
||||
/**
|
||||
* @brief Path
|
||||
*/
|
||||
public $path = NULL;
|
||||
public ?string $path = NULL;
|
||||
|
||||
/**
|
||||
* @brief Raw API Key
|
||||
*/
|
||||
public $rawApiKey = NULL;
|
||||
public ?string $rawApiKey = NULL;
|
||||
|
||||
/**
|
||||
* @brief Raw Access Token
|
||||
*/
|
||||
public $rawAccessToken = NULL;
|
||||
public ?string $rawAccessToken = NULL;
|
||||
|
||||
/**
|
||||
* @brief API Key Object
|
||||
*/
|
||||
public $apiKey = NULL;
|
||||
public ?Key $apiKey = NULL;
|
||||
|
||||
/**
|
||||
* @brief Access Token Details
|
||||
*/
|
||||
public $accessToken = NULL;
|
||||
public ?array $accessToken = NULL;
|
||||
|
||||
/**
|
||||
* @brief Language
|
||||
*/
|
||||
public $language = NULL;
|
||||
public ?Lang $language = NULL;
|
||||
|
||||
/**
|
||||
* Can the Response be cached?
|
||||
@@ -68,9 +94,9 @@ class _Api extends \IPS\Dispatcher
|
||||
* Init
|
||||
*
|
||||
* @return void
|
||||
* @throws \DomainException
|
||||
* @throws DomainException
|
||||
*/
|
||||
public function init()
|
||||
public function init() : void
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -87,7 +113,7 @@ class _Api extends \IPS\Dispatcher
|
||||
$this->_setAccessToken();
|
||||
if ( !$this->accessToken['scope'] or !json_decode( $this->accessToken['scope'] ) )
|
||||
{
|
||||
throw new \IPS\Api\Exception( 'NO_SCOPES', '3S290/B', 401, 'insufficient_scope' );
|
||||
throw new ApiException( 'NO_SCOPES', '3S290/B', 401, 'insufficient_scope' );
|
||||
}
|
||||
}
|
||||
elseif ( $this->rawApiKey )
|
||||
@@ -96,32 +122,32 @@ class _Api extends \IPS\Dispatcher
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \IPS\Api\Exception( 'NO_API_KEY', '2S290/6', 401 );
|
||||
throw new ApiException( 'NO_API_KEY', '2S290/6', 401 );
|
||||
}
|
||||
|
||||
/* Set other data */
|
||||
$this->_setLanguage();
|
||||
|
||||
/* We don't want to cache any output for Zapier Requests */
|
||||
if( \IPS\Request::i()->isZapier() )
|
||||
if( Request::i()->isZapier() )
|
||||
{
|
||||
$this->cacheResponse = FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
catch ( \IPS\Api\Exception $e )
|
||||
catch ( ApiException $e )
|
||||
{
|
||||
/* Build response */
|
||||
$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( (array)$response, $e->getCode(), in_array($e->exceptionCode, array('3S290/7', '3S290/9', '3S290/B')));
|
||||
}
|
||||
|
||||
/* Output */
|
||||
$this->_respond( $response, $e->getCode(), $e->oauthError );
|
||||
$this->_respond($response, $e->getCode(), $e->oauthError);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,23 +156,23 @@ class _Api extends \IPS\Dispatcher
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _setPath()
|
||||
protected function _setPath() : void
|
||||
{
|
||||
/* Decode URL */
|
||||
if ( \IPS\Settings::i()->use_friendly_urls and \IPS\Settings::i()->htaccess_mod_rewrite and mb_substr( \IPS\Request::i()->url()->data[ \IPS\Http\Url::COMPONENT_PATH ], -14 ) !== '/api/index.php' )
|
||||
if ( Settings::i()->use_friendly_urls and Settings::i()->htaccess_mod_rewrite and mb_substr( Request::i()->url()->data[ Url::COMPONENT_PATH ], -14 ) !== '/api/index.php' )
|
||||
{
|
||||
/* We are using Mod Rewrite URL's, so look in the path */
|
||||
$this->path = mb_substr( \IPS\Request::i()->url()->data[ \IPS\Http\Url::COMPONENT_PATH ], mb_strpos( \IPS\Request::i()->url()->data[ \IPS\Http\Url::COMPONENT_PATH ], '/api/' ) + 5 );
|
||||
$this->path = mb_substr( Request::i()->url()->data[ Url::COMPONENT_PATH ], mb_strpos( Request::i()->url()->data[ Url::COMPONENT_PATH ], '/api/' ) + 5 );
|
||||
|
||||
/* nginx won't convert the 'fake' query string to $_GET params, so do this now */
|
||||
if ( ! empty( \IPS\Request::i()->url()->data[ \IPS\Http\Url::COMPONENT_QUERY ] ) )
|
||||
if ( ! empty( Request::i()->url()->data[ Url::COMPONENT_QUERY ] ) )
|
||||
{
|
||||
parse_str( \IPS\Request::i()->url()->data[ \IPS\Http\Url::COMPONENT_QUERY ], $params );
|
||||
parse_str( Request::i()->url()->data[ Url::COMPONENT_QUERY ], $params );
|
||||
foreach ( $params as $k => $v )
|
||||
{
|
||||
if ( ! isset( \IPS\Request::i()->$k ) )
|
||||
if ( ! isset( Request::i()->$k ) )
|
||||
{
|
||||
\IPS\Request::i()->$k = $v;
|
||||
Request::i()->$k = $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -154,7 +180,7 @@ class _Api extends \IPS\Dispatcher
|
||||
else
|
||||
{
|
||||
/* Otherwise we are not, so we need the query string instead, which is actually easier */
|
||||
$this->path = \IPS\Request::i()->url()->data[ \IPS\Http\Url::COMPONENT_QUERY ];
|
||||
$this->path = Request::i()->url()->data[ Url::COMPONENT_QUERY ];
|
||||
|
||||
/* However, if we passed any actual query string arguments, we need to strip those */
|
||||
if( mb_strpos( $this->path, '&' ) )
|
||||
@@ -170,11 +196,11 @@ class _Api extends \IPS\Dispatcher
|
||||
* @note OAuth requires Access Tokens only be transmitted over TLS, so if the request isn't secure, we ignore OAuth credentials
|
||||
* @return void
|
||||
*/
|
||||
public function _setRawCredentials()
|
||||
public function _setRawCredentials() : void
|
||||
{
|
||||
if ( $authorizationHeader = \IPS\Request::i()->authorizationHeader() )
|
||||
if ( $authorizationHeader = Request::i()->authorizationHeader() )
|
||||
{
|
||||
if ( mb_substr( $authorizationHeader, 0, 7 ) === 'Bearer ' and ( !\IPS\OAUTH_REQUIRES_HTTPS or \IPS\Request::i()->isSecure() ) )
|
||||
if ( mb_substr( $authorizationHeader, 0, 7 ) === 'Bearer ' and ( !OAUTH_REQUIRES_HTTPS or Request::i()->isSecure() ) )
|
||||
{
|
||||
$this->rawAccessToken = mb_substr( $authorizationHeader, 7 );
|
||||
}
|
||||
@@ -193,39 +219,39 @@ class _Api extends \IPS\Dispatcher
|
||||
* Check the IP Address isn't banned
|
||||
*
|
||||
* @return void
|
||||
* @throws \IPS\Api\Exception
|
||||
* @throws ApiException
|
||||
*/
|
||||
protected function _checkIpAddressIsAllowed()
|
||||
protected function _checkIpAddressIsAllowed() : void
|
||||
{
|
||||
/* Check the IP address is banned */
|
||||
if ( \IPS\Request::i()->ipAddressIsBanned() )
|
||||
if ( Request::i()->ipAddressIsBanned() )
|
||||
{
|
||||
throw new \IPS\Api\Exception( 'IP_ADDRESS_BANNED', '1S290/A', 403 );
|
||||
throw new ApiException( 'IP_ADDRESS_BANNED', '1S290/A', 403 );
|
||||
}
|
||||
|
||||
/* If we have tried to access the API with a bad key more than 10 times, ban the IP address */
|
||||
if ( \IPS\Db::i()->select( 'COUNT(*)', 'core_api_logs', array( 'ip_address=? AND is_bad_key=1', \IPS\Request::i()->ipAddress() ) )->first() > 10 )
|
||||
if ( Db::i()->select( 'COUNT(*)', 'core_api_logs', array( 'ip_address=? AND is_bad_key=1', Request::i()->ipAddress() ) )->first() > 10 )
|
||||
{
|
||||
/* Remove the flag from these logs so that if the admin unbans the IP we aren't immediately banned again */
|
||||
\IPS\Db::i()->update( 'core_api_logs', array( 'is_bad_key' => 0 ), array( 'ip_address=?', \IPS\Request::i()->ipAddress() ) );
|
||||
Db::i()->update( 'core_api_logs', array( 'is_bad_key' => 0 ), array( 'ip_address=?', Request::i()->ipAddress() ) );
|
||||
|
||||
/* Then insert the ban... */
|
||||
\IPS\Db::i()->insert( 'core_banfilters', array(
|
||||
Db::i()->insert( 'core_banfilters', array(
|
||||
'ban_type' => 'ip',
|
||||
'ban_content' => \IPS\Request::i()->ipAddress(),
|
||||
'ban_content' => Request::i()->ipAddress(),
|
||||
'ban_date' => time(),
|
||||
'ban_reason' => 'API',
|
||||
) );
|
||||
unset( \IPS\Data\Store::i()->bannedIpAddresses );
|
||||
unset( Store::i()->bannedIpAddresses );
|
||||
|
||||
/* And throw an error */
|
||||
throw new \IPS\Api\Exception( 'IP_ADDRESS_BANNED', '1S290/C', 403 );
|
||||
throw new ApiException( 'IP_ADDRESS_BANNED', '1S290/C', 403 );
|
||||
}
|
||||
|
||||
/* If we have tried to access the API with a bad key more than once in the last 5 minutes, throw an error to prevent brute-forcing */
|
||||
if ( \IPS\Db::i()->select( 'COUNT(*)', 'core_api_logs', array( 'ip_address=? AND is_bad_key=1 AND date>?', \IPS\Request::i()->ipAddress(), \IPS\DateTime::create()->sub( new \DateInterval( 'PT5M' ) )->getTimestamp() ) )->first() > 1 )
|
||||
if ( Db::i()->select( 'COUNT(*)', 'core_api_logs', array( 'ip_address=? AND is_bad_key=1 AND date>?', Request::i()->ipAddress(), DateTime::create()->sub( new DateInterval( 'PT5M' ) )->getTimestamp() ) )->first() > 1 )
|
||||
{
|
||||
throw new \IPS\Api\Exception( 'TOO_MANY_REQUESTS_WITH_BAD_KEY', '1S290/D', 429 );
|
||||
throw new ApiException( 'TOO_MANY_REQUESTS_WITH_BAD_KEY', '1S290/D', 429 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,20 +260,20 @@ class _Api extends \IPS\Dispatcher
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function _setApiKey()
|
||||
public function _setApiKey() : void
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->apiKey = \IPS\Api\Key::load( $this->rawApiKey );
|
||||
$this->apiKey = 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( Request::i()->ipAddress(), explode( ',', $this->apiKey->allowed_ips ) ) )
|
||||
{
|
||||
throw new \IPS\Api\Exception( 'IP_ADDRESS_NOT_ALLOWED', '2S290/8', 403 );
|
||||
throw new ApiException( 'IP_ADDRESS_NOT_ALLOWED', '2S290/8', 403 );
|
||||
}
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
catch ( OutOfRangeException $e )
|
||||
{
|
||||
throw new \IPS\Api\Exception( 'INVALID_API_KEY', '3S290/7', 401 );
|
||||
throw new ApiException( 'INVALID_API_KEY', '3S290/7', 401 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,15 +282,15 @@ class _Api extends \IPS\Dispatcher
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function _setAccessToken()
|
||||
public function _setAccessToken() : void
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->accessToken = \IPS\Api\OAuthClient::accessTokenDetails( $this->rawAccessToken );
|
||||
$this->accessToken = OAuthClient::accessTokenDetails( $this->rawAccessToken );
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
catch ( UnderflowException $e )
|
||||
{
|
||||
throw new \IPS\Api\Exception( 'INVALID_ACCESS_TOKEN', '3S290/9', 401, 'invalid_token' );
|
||||
throw new ApiException( 'INVALID_ACCESS_TOKEN', '3S290/9', 401, 'invalid_token' );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,24 +299,24 @@ class _Api extends \IPS\Dispatcher
|
||||
*
|
||||
* @return Lang
|
||||
*/
|
||||
public function _setLanguage()
|
||||
public function _setLanguage() : Lang
|
||||
{
|
||||
try
|
||||
{
|
||||
if ( isset( $_SERVER['HTTP_X_IPS_LANGUAGE'] ) )
|
||||
{
|
||||
$this->language = \IPS\Lang::load( \intval( $_SERVER['HTTP_X_IPS_LANGUAGE'] ) );
|
||||
$this->language = Lang::load( intval( $_SERVER['HTTP_X_IPS_LANGUAGE'] ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->language = \IPS\Lang::load( \IPS\Lang::defaultLanguage() );
|
||||
$this->language = Lang::load( Lang::defaultLanguage() );
|
||||
}
|
||||
|
||||
return $this->language;
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
catch ( OutOfRangeException $e )
|
||||
{
|
||||
throw new \IPS\Api\Exception( 'INVALID_LANGUAGE', '2S290/9', 400, 'invalid_request' );
|
||||
throw new ApiException( 'INVALID_LANGUAGE', '2S290/9', 400, 'invalid_request' );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,7 +325,7 @@ class _Api extends \IPS\Dispatcher
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
public function run() : void
|
||||
{
|
||||
$shouldLog = FALSE;
|
||||
try
|
||||
@@ -309,35 +335,35 @@ class _Api extends \IPS\Dispatcher
|
||||
$app = array_shift( $pathBits );
|
||||
if ( !preg_match( '/^[a-z0-9]+$/', $app ) )
|
||||
{
|
||||
throw new \IPS\Api\Exception( 'INVALID_APP', '3S290/3', 400 );
|
||||
throw new ApiException( 'INVALID_APP', '3S290/3', 400 );
|
||||
}
|
||||
$controller = array_shift( $pathBits );
|
||||
if ( !preg_match( '/^[a-z0-9]+$/', $controller ) )
|
||||
{
|
||||
throw new \IPS\Api\Exception( 'INVALID_CONTROLLER', '3S290/4', 400 );
|
||||
throw new ApiException( 'INVALID_CONTROLLER', '3S290/4', 400 );
|
||||
}
|
||||
|
||||
/* Load the app */
|
||||
try
|
||||
{
|
||||
$app = \IPS\Application::load( $app );
|
||||
$app = Application::load( $app );
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
catch ( OutOfRangeException $e )
|
||||
{
|
||||
throw new \IPS\Api\Exception( 'INVALID_APP', '2S290/1', 404 );
|
||||
throw new ApiException( 'INVALID_APP', '2S290/1', 404 );
|
||||
}
|
||||
|
||||
/* Check it's enabled */
|
||||
if ( !$app->enabled )
|
||||
{
|
||||
throw new \IPS\Api\Exception( 'APP_DISABLED', '1S290/2', 503 );
|
||||
throw new ApiException( 'APP_DISABLED', '1S290/2', 503 );
|
||||
}
|
||||
|
||||
/* Get the controller */
|
||||
$class = 'IPS\\' . $app->directory . '\\api\\' . $controller;
|
||||
if ( !class_exists( $class ) )
|
||||
{
|
||||
throw new \IPS\Api\Exception( 'INVALID_CONTROLLER', '2S290/5', 404 );
|
||||
throw new ApiException( 'INVALID_CONTROLLER', '2S290/5', 404 );
|
||||
}
|
||||
|
||||
/* Run it */
|
||||
@@ -351,55 +377,55 @@ class _Api extends \IPS\Dispatcher
|
||||
$output = $response->getOutput();
|
||||
$this->language->parseOutputForDisplay( $output );
|
||||
|
||||
$this->_respond( json_encode( $output, JSON_PRETTY_PRINT ), $response->httpCode, NULL, $shouldLog, TRUE );
|
||||
$this->_respond(json_encode($output, JSON_PRETTY_PRINT), $response->httpCode, NULL, $shouldLog);
|
||||
}
|
||||
catch ( \IPS\Api\Exception $e )
|
||||
catch ( ApiException $e )
|
||||
{
|
||||
$this->_respond( json_encode( array( 'errorCode' => $e->exceptionCode, 'errorMessage' => $e->getMessage() ), JSON_PRETTY_PRINT ), $e->getCode(), $e->oauthError, $shouldLog );
|
||||
$this->_respond(json_encode(array('errorCode' => $e->exceptionCode, 'errorMessage' => $e->getMessage()), JSON_PRETTY_PRINT), $e->getCode(), $e->oauthError, $shouldLog);
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
\IPS\Log::log( $e, 'api' );
|
||||
Log::log( $e, 'api' );
|
||||
|
||||
$this->_respond( json_encode( array( 'errorCode' => 'EX' . $e->getCode(), 'errorMessage' => \IPS\IN_DEV ? $e->getMessage() : 'UNKNOWN_ERROR' ), JSON_PRETTY_PRINT ), 500 );
|
||||
$this->_respond(json_encode(array('errorCode' => 'EX' . $e->getCode(), 'errorMessage' => \IPS\IN_DEV ? $e->getMessage() : 'UNKNOWN_ERROR'), JSON_PRETTY_PRINT), 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parameters to mask in logs per the controller
|
||||
*/
|
||||
protected $parametersToMask = NULL;
|
||||
protected ?array $parametersToMask = NULL;
|
||||
|
||||
/**
|
||||
* Log
|
||||
*
|
||||
* @param array $response Response to output
|
||||
* @param int $httpResponseCode HTTP Response Code
|
||||
* @param bool $isBadKey Was the ley invalid?
|
||||
* @param array|string $response Response to output
|
||||
* @param int $httpResponseCode HTTP Response Code
|
||||
* @param bool $isBadKey Was the ley invalid?
|
||||
* @return void
|
||||
*/
|
||||
protected function _log( $response, $httpResponseCode, $isBadKey=FALSE )
|
||||
protected function _log( array|string $response, int $httpResponseCode, bool $isBadKey=FALSE ) : void
|
||||
{
|
||||
try
|
||||
{
|
||||
$_requestData = $_REQUEST;
|
||||
|
||||
if( $this->parametersToMask AND \count( $this->parametersToMask ) )
|
||||
if( $this->parametersToMask AND count( $this->parametersToMask ) )
|
||||
{
|
||||
foreach( $_requestData as $k => $v )
|
||||
{
|
||||
if( \in_array( $k, $this->parametersToMask ) )
|
||||
if( in_array( $k, $this->parametersToMask ) )
|
||||
{
|
||||
$_requestData[ $k ] = '******';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
\IPS\Db::i()->insert( 'core_api_logs', array(
|
||||
Db::i()->insert( 'core_api_logs', array(
|
||||
'endpoint' => $this->path,
|
||||
'method' => $_SERVER['REQUEST_METHOD'],
|
||||
'api_key' => $this->rawApiKey,
|
||||
'ip_address' => \IPS\Request::i()->ipAddress(),
|
||||
'ip_address' => Request::i()->ipAddress(),
|
||||
'request_data' => json_encode( $_requestData, JSON_PRETTY_PRINT ),
|
||||
'response_code' => $httpResponseCode,
|
||||
'response_output' => $response,
|
||||
@@ -410,21 +436,21 @@ class _Api extends \IPS\Dispatcher
|
||||
'access_token' => $this->rawAccessToken,
|
||||
) );
|
||||
}
|
||||
catch ( \IPS\Db\Exception $e ) {}
|
||||
catch ( Exception $e ) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output response
|
||||
*
|
||||
* @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
|
||||
* @param string $response Response to output
|
||||
* @param int $httpResponseCode HTTP Response Code
|
||||
* @param string|null $oauthError OAuth error
|
||||
* @param bool $log Whether or not to log the response
|
||||
* @return void
|
||||
*/
|
||||
protected function _respond( $response, $httpResponseCode, $oauthError=NULL, $log=FALSE )
|
||||
protected function _respond( string $response, int $httpResponseCode, string $oauthError=NULL, bool $log=FALSE ) : void
|
||||
{
|
||||
$headers = $this->canBeCached() ? \IPS\Output::getCacheHeaders( time(), 60 ) : \IPS\Output::getNoCacheHeaders();
|
||||
$headers = $this->canBeCached() ? Output::getCacheHeaders( time(), 60 ) : Output::getNoCacheHeaders();
|
||||
|
||||
if ( $this->rawAccessToken and $oauthError )
|
||||
{
|
||||
@@ -433,10 +459,10 @@ class _Api extends \IPS\Dispatcher
|
||||
|
||||
if ( $log )
|
||||
{
|
||||
$this->_log( $response, $httpResponseCode );
|
||||
$this->_log($response, $httpResponseCode);
|
||||
}
|
||||
|
||||
\IPS\Output::i()->sendOutput( $response, $httpResponseCode, 'application/json', $headers );
|
||||
Output::i()->sendOutput( $response, $httpResponseCode, 'application/json', $headers );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+31
-24
@@ -11,50 +11,57 @@
|
||||
namespace IPS\Dispatcher;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use IPS\Application;
|
||||
use IPS\Application\Module;
|
||||
use IPS\Data\Store;
|
||||
use IPS\Dispatcher;
|
||||
use IPS\Request;
|
||||
use IPS\Settings;
|
||||
use function defined;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Build/Tools Dispatcher
|
||||
*/
|
||||
class _Build extends \IPS\Dispatcher
|
||||
class Build extends Dispatcher
|
||||
{
|
||||
/**
|
||||
* @brief Controller Location
|
||||
*/
|
||||
public $controllerLocation = 'front';
|
||||
public string $controllerLocation = 'front';
|
||||
|
||||
/**
|
||||
* @brief Application
|
||||
*/
|
||||
public $application = 'core';
|
||||
public string $application = 'core';
|
||||
|
||||
/**
|
||||
* @brief Module
|
||||
*/
|
||||
public $module = 'system';
|
||||
public string $module = 'system';
|
||||
|
||||
/**
|
||||
* @brief Step
|
||||
*/
|
||||
public $step = 1;
|
||||
public int $step = 1;
|
||||
|
||||
/**
|
||||
* Initiator
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init()
|
||||
public function init() : void
|
||||
{
|
||||
$modules = \IPS\Application\Module::modules();
|
||||
$this->application = \IPS\Application::load('core');
|
||||
$modules = Module::modules();
|
||||
$this->application = Application::load('core');
|
||||
$this->module = $modules['core']['front']['system'];
|
||||
$this->controller = 'build';
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,31 +69,31 @@ class _Build extends \IPS\Dispatcher
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
public function run() : void
|
||||
{
|
||||
if ( isset( \IPS\Request::i()->force ) )
|
||||
if ( isset( Request::i()->force ) )
|
||||
{
|
||||
if ( isset( \IPS\Data\Store::i()->builder_building ) )
|
||||
if ( isset( Store::i()->builder_building ) )
|
||||
{
|
||||
unset( \IPS\Data\Store::i()->builder_building );
|
||||
unset( Store::i()->builder_building );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( isset( \IPS\Data\Store::i()->builder_building ) and ! empty( \IPS\Data\Store::i()->builder_building ) )
|
||||
if ( isset( Store::i()->builder_building ) and ! empty( Store::i()->builder_building ) )
|
||||
{
|
||||
/* We're currently rebuilding */
|
||||
if ( time() - \IPS\Data\Store::i()->builder_building < 180 )
|
||||
if ( time() - Store::i()->builder_building < 180 )
|
||||
{
|
||||
print "Builder is already running. To force a rebuild anyway, add &force=1 on the end of your URL";
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
\IPS\Data\Store::i()->builder_building = time();
|
||||
Store::i()->builder_building = time();
|
||||
}
|
||||
|
||||
\IPS\Settings::i()->changeValues( array( 'site_online' => 0 ) );
|
||||
Settings::i()->changeValues( array( 'site_online' => 0 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,13 +101,13 @@ class _Build extends \IPS\Dispatcher
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function buildDone()
|
||||
public function buildDone() : void
|
||||
{
|
||||
if ( isset( \IPS\Data\Store::i()->builder_building ) )
|
||||
if ( isset( Store::i()->builder_building ) )
|
||||
{
|
||||
unset( \IPS\Data\Store::i()->builder_building );
|
||||
unset( Store::i()->builder_building );
|
||||
}
|
||||
|
||||
\IPS\Settings::i()->changeValues( array( 'site_online' => 1 ) );
|
||||
Settings::i()->changeValues( array( 'site_online' => 1 ) );
|
||||
}
|
||||
}
|
||||
@@ -11,26 +11,45 @@
|
||||
namespace IPS\Dispatcher;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use Exception;
|
||||
use IPS\core\DataLayer;
|
||||
use IPS\Dispatcher;
|
||||
use IPS\Http\Url;
|
||||
use IPS\Log;
|
||||
use IPS\Member;
|
||||
use IPS\Node\Model;
|
||||
use IPS\Output;
|
||||
use IPS\Request;
|
||||
use IPS\Settings;
|
||||
use IPS\Theme;
|
||||
use OutOfRangeException;
|
||||
use function defined;
|
||||
use function get_called_class;
|
||||
use function intval;
|
||||
use function mb_stristr;
|
||||
use const IPS\ENFORCE_ACCESS;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract class that Controllers should extend
|
||||
*/
|
||||
abstract class _Controller
|
||||
abstract class Controller
|
||||
{
|
||||
/**
|
||||
* @brief Base URL
|
||||
*/
|
||||
public $url;
|
||||
public mixed $url;
|
||||
|
||||
/**
|
||||
* @brief Is this for displaying "content"? Affects if advertisements may be shown
|
||||
*/
|
||||
public $isContentPage = TRUE;
|
||||
public bool $isContentPage = TRUE;
|
||||
|
||||
/**
|
||||
* @brief These properties are used to specify datalayer context properties.
|
||||
@@ -39,21 +58,21 @@ abstract class _Controller
|
||||
'community_area' => array( 'value' => 'search', 'odkUpdate' => 'true' )
|
||||
* )
|
||||
*/
|
||||
public static $dataLayerContext = array();
|
||||
public static array $dataLayerContext = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \IPS\Http\Url|NULL $url The base URL for this controller or NULL to calculate automatically
|
||||
* @param Url|null $url The base URL for this controller or NULL to calculate automatically
|
||||
* @return void
|
||||
*/
|
||||
public function __construct( $url=NULL )
|
||||
public function __construct( mixed $url=NULL )
|
||||
{
|
||||
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 );
|
||||
$this->url = Url::internal( "app={$exploded[1]}&module={$exploded[4]}&controller={$exploded[5]}", Dispatcher::i()->controllerLocation );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -64,12 +83,12 @@ abstract class _Controller
|
||||
/**
|
||||
* Force a specific method within a controller to execute. Useful for unit testing.
|
||||
*
|
||||
* @param null|string $method The specific method to call
|
||||
* @param string|null $method The specific method to call
|
||||
* @return mixed
|
||||
*/
|
||||
public function forceExecute( $method=NULL )
|
||||
public function forceExecute( ?string $method=NULL ): mixed
|
||||
{
|
||||
if( \IPS\ENFORCE_ACCESS === true and $method !== null )
|
||||
if( ENFORCE_ACCESS and $method !== null )
|
||||
{
|
||||
if ( method_exists( $this, $method ) )
|
||||
{
|
||||
@@ -77,11 +96,13 @@ abstract class _Controller
|
||||
}
|
||||
else
|
||||
{
|
||||
return $this->execute();
|
||||
$this->execute();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->execute();
|
||||
$this->execute();
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,26 +110,26 @@ abstract class _Controller
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
public function execute() : void
|
||||
{
|
||||
if ( !empty( static::$dataLayerContext ) AND \IPS\Settings::i()->core_datalayer_enabled AND !\IPS\Request::i()->isAjax() )
|
||||
if ( !empty( static::$dataLayerContext ) AND Settings::i()->core_datalayer_enabled AND !Request::i()->isAjax() )
|
||||
{
|
||||
foreach ( static::$dataLayerContext as $property => $data )
|
||||
{
|
||||
\IPS\core\DataLayer::i()->addContextProperty( $property, $data['value'], $data['odkUpdate'] ?? false );
|
||||
DataLayer::i()->addContextProperty( $property, $data['value'], $data['odkUpdate'] ?? false );
|
||||
}
|
||||
}
|
||||
|
||||
if( \IPS\Request::i()->do and preg_match( '/^[a-zA-Z\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', \IPS\Request::i()->do ) )
|
||||
if( Request::i()->do and preg_match( '/^[a-zA-Z\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', Request::i()->do ) )
|
||||
{
|
||||
if ( method_exists( $this, \IPS\Request::i()->do ) or method_exists( $this, '__call' ) )
|
||||
if ( method_exists( $this, Request::i()->do ) or method_exists( $this, '__call' ) )
|
||||
{
|
||||
$method = \IPS\Request::i()->do;
|
||||
$method = Request::i()->do;
|
||||
$this->$method();
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->error( 'page_not_found', '2S106/1', 404, '' );
|
||||
Output::i()->error( 'page_not_found', '2S106/1', 404, '' );
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -119,7 +140,7 @@ abstract class _Controller
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->error( 'page_not_found', '2S106/2', 404, '' );
|
||||
Output::i()->error( 'page_not_found', '2S106/2', 404, '' );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -129,14 +150,14 @@ abstract class _Controller
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function embed()
|
||||
protected function embed() : void
|
||||
{
|
||||
$title = \IPS\Member::loggedIn()->language()->addToStack('error_title');
|
||||
$title = Member::loggedIn()->language()->addToStack('error_title');
|
||||
$content = null;
|
||||
|
||||
if( !isset( static::$contentModel ) )
|
||||
if( !isset( static::$contentModel ) OR !in_array( 'IPS\Content\Embeddable', class_implements( static::$contentModel ) ) )
|
||||
{
|
||||
$output = \IPS\Theme::i()->getTemplate( 'embed', 'core', 'global' )->embedUnavailable();
|
||||
$output = Theme::i()->getTemplate( 'embed', 'core', 'global' )->embedUnavailable();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -145,131 +166,131 @@ abstract class _Controller
|
||||
$class = static::$contentModel;
|
||||
$params = array();
|
||||
|
||||
if( \IPS\Request::i()->embedComment )
|
||||
if( Request::i()->embedComment )
|
||||
{
|
||||
|
||||
$commentClass = $class::$commentClass;
|
||||
if ( isset( $class::$archiveClass ) )
|
||||
$commentClass = $class::$commentClass;
|
||||
if ( isset( $class::$archiveClass ) )
|
||||
{
|
||||
$item = $class::load( Request::i()->id );
|
||||
if ( $item->isArchived() )
|
||||
{
|
||||
$item = $class::load( \IPS\Request::i()->id );
|
||||
if ( $item->isArchived() )
|
||||
{
|
||||
$commentClass = $class::$archiveClass;
|
||||
}
|
||||
$commentClass = $class::$archiveClass;
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
$content = $commentClass::load( \IPS\Request::i()->embedComment );
|
||||
$content = $commentClass::load( Request::i()->embedComment );
|
||||
$title = $content->item()->mapped('title');
|
||||
}
|
||||
/* Comment wasn't found, but we can still link to the item */
|
||||
catch( \OutOfRangeException $e )
|
||||
catch( OutOfRangeException $e )
|
||||
{
|
||||
$type = $commentClass::$title;
|
||||
$output = \IPS\Theme::i()->getTemplate( 'embed', 'core', 'global' )->embedCommentUnavailable( $type, $item );
|
||||
$output = Theme::i()->getTemplate( 'embed', 'core', 'global' )->embedCommentUnavailable( $type, $item );
|
||||
goto embedOutput;
|
||||
}
|
||||
}
|
||||
elseif( \IPS\Request::i()->embedReview )
|
||||
elseif( Request::i()->embedReview )
|
||||
{
|
||||
$reviewClass = $class::$reviewClass;
|
||||
$reviewClass = $class::$reviewClass;
|
||||
$item = $class::load( Request::i()->id );
|
||||
try
|
||||
{
|
||||
$content = $reviewClass::load( \IPS\Request::i()->embedReview );
|
||||
$title = $content->item()->mapped('title');
|
||||
$content = $reviewClass::load( Request::i()->embedReview );
|
||||
$title = $content->item()->mapped('title');
|
||||
}
|
||||
/* Review wasn't found, but we can still link to the item */
|
||||
catch( \OutOfRangeException $e )
|
||||
catch( OutOfRangeException $e )
|
||||
{
|
||||
$type = $reviewClass::$title;
|
||||
$output = \IPS\Theme::i()->getTemplate( 'embed', 'core', 'global' )->embedCommentUnavailable( $type, $item );
|
||||
$output = Theme::i()->getTemplate( 'embed', 'core', 'global' )->embedCommentUnavailable( $type, $item );
|
||||
goto embedOutput;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( isset( \IPS\Request::i()->page ) and \IPS\Request::i()->page > 1 )
|
||||
if ( isset( Request::i()->page ) and Request::i()->page > 1 )
|
||||
{
|
||||
$params['page'] = \intval( \IPS\Request::i()->page );
|
||||
$params['page'] = intval( Request::i()->page );
|
||||
}
|
||||
if ( isset( \IPS\Request::i()->embedDo ) )
|
||||
if ( isset( Request::i()->embedDo ) )
|
||||
{
|
||||
$params['do'] = \IPS\Request::i()->embedDo;
|
||||
$params['do'] = Request::i()->embedDo;
|
||||
}
|
||||
|
||||
$content = $class::load( \IPS\Request::i()->id );
|
||||
$title = $content instanceof \IPS\Node\Model ? $content->_title : $content->mapped( 'title' );
|
||||
$content = $class::load( Request::i()->id );
|
||||
$title = $content instanceof Model ? $content->_title : $content->mapped( 'title' );
|
||||
}
|
||||
$output = $this->getEmbedOutput( $content, $params );
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
catch ( OutOfRangeException $e )
|
||||
{
|
||||
$output = \IPS\Theme::i()->getTemplate( 'embed', 'core', 'global' )->embedUnavailable();
|
||||
$output = Theme::i()->getTemplate( 'embed', 'core', 'global' )->embedUnavailable();
|
||||
}
|
||||
catch( \Exception $e )
|
||||
catch( Exception $e )
|
||||
{
|
||||
\IPS\Log::log( $e, 'embed_error' );
|
||||
$output = \IPS\Theme::i()->getTemplate( 'embed', 'core', 'global' )->embedNoPermission();
|
||||
Log::log( $e, 'embed_error' );
|
||||
$output = Theme::i()->getTemplate( 'embed', 'core', 'global' )->embedNoPermission();
|
||||
}
|
||||
}
|
||||
embedOutput:
|
||||
|
||||
|
||||
/* Make sure our iframe contents get the necessary elements and JS */
|
||||
$js = array(
|
||||
\IPS\Output::i()->js( 'js/commonEmbedHandler.js', 'core', 'interface' ),
|
||||
\IPS\Output::i()->js( 'js/internalEmbedHandler.js', 'core', 'interface' )
|
||||
Output::i()->js( 'js/commonEmbedHandler.js', 'core', 'interface' ),
|
||||
Output::i()->js( 'js/internalEmbedHandler.js', 'core', 'interface' )
|
||||
);
|
||||
\IPS\Output::i()->base = '_parent';
|
||||
Output::i()->base = '_parent';
|
||||
|
||||
/* We need to keep any embed.css files that have been specified so that we can re-add them after we re-fetch the css framework */
|
||||
$embedCss = array();
|
||||
foreach( \IPS\Output::i()->cssFiles as $cssFile )
|
||||
foreach( Output::i()->cssFiles as $cssFile )
|
||||
{
|
||||
if( \mb_stristr( $cssFile, 'embed.css' ) )
|
||||
if( mb_stristr( $cssFile, 'embed.css' ) )
|
||||
{
|
||||
$embedCss[] = $cssFile;
|
||||
}
|
||||
}
|
||||
|
||||
/* We need to reset the included CSS files because by this point the responsive files are already in the output CSS array */
|
||||
\IPS\Output::i()->cssFiles = array();
|
||||
\IPS\Output::i()->responsive = FALSE;
|
||||
\IPS\Dispatcher\Front::baseCss();
|
||||
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, $embedCss );
|
||||
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/embeds.css', 'core', 'front' ) );
|
||||
Output::i()->cssFiles = array();
|
||||
Output::i()->responsive = FALSE;
|
||||
Front::baseCss();
|
||||
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, $embedCss );
|
||||
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'styles/embeds.css', 'core', 'front' ) );
|
||||
|
||||
/* Seo Stuffs */
|
||||
\IPS\Output::i()->title = $title;
|
||||
Output::i()->title = $title;
|
||||
|
||||
if( $content !== NULL )
|
||||
{
|
||||
\IPS\Output::i()->linkTags['canonical'] = (string) $content->url();
|
||||
Output::i()->linkTags['canonical'] = (string) $content->url();
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->metaTags['robots'] = 'noindex';
|
||||
Output::i()->metaTags['robots'] = 'noindex';
|
||||
}
|
||||
|
||||
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->embedInternal( $output, $js ), 200, 'text/html' );
|
||||
Output::i()->sendOutput( Theme::i()->getTemplate( 'global', 'core', 'front' )->embedInternal( $output, $js ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content item or the proper error message
|
||||
*
|
||||
* @param \IPS\Content $content
|
||||
* @param object $content
|
||||
* @param array $params
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getEmbedOutput( $content, array $params = array() )
|
||||
protected function getEmbedOutput( object $content, array $params = array() ): string
|
||||
{
|
||||
if ( !$content->canView() )
|
||||
{
|
||||
$output = \IPS\Theme::i()->getTemplate( 'embed', 'core', 'global' )->embedNoPermission();
|
||||
$output = Theme::i()->getTemplate( 'embed', 'core', 'global' )->embedNoPermission();
|
||||
}
|
||||
else if ( !( $content instanceof \IPS\Content\Embeddable ) )
|
||||
else if ( !in_array( 'IPS\Content\Embeddable', class_implements( $content ) ) )
|
||||
{
|
||||
$output = \IPS\Theme::i()->getTemplate( 'embed', 'core', 'global' )->embedUnavailable();
|
||||
$output = Theme::i()->getTemplate( 'embed', 'core', 'global' )->embedUnavailable();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -11,29 +11,45 @@
|
||||
namespace IPS;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use DomainException;
|
||||
use ErrorException;
|
||||
use IPS\Application\Module;
|
||||
use IPS\Dispatcher\Controller;
|
||||
use IPS\Platform\Bridge;
|
||||
use RuntimeException;
|
||||
use function defined;
|
||||
use function dirname;
|
||||
use function get_called_class;
|
||||
use function strstr;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatcher
|
||||
*
|
||||
* @property Application $application
|
||||
* @property Module $module
|
||||
* @property string $controller
|
||||
*/
|
||||
abstract class _Dispatcher
|
||||
abstract class Dispatcher
|
||||
{
|
||||
/**
|
||||
* @brief Singleton Instance
|
||||
*/
|
||||
protected static $instance = NULL;
|
||||
protected static mixed $instance = NULL;
|
||||
|
||||
/**
|
||||
* Check if a dispatcher instance is available
|
||||
*
|
||||
* @return static
|
||||
* @return bool
|
||||
* @note This should be used sparingly, primarily for gateway scripts that do not need a dispatcher but still use the framework
|
||||
*/
|
||||
public static function hasInstance()
|
||||
public static function hasInstance(): bool
|
||||
{
|
||||
return ( static::$instance !== NULL );
|
||||
}
|
||||
@@ -43,15 +59,15 @@ abstract class _Dispatcher
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function i()
|
||||
public static function i(): static
|
||||
{
|
||||
if( static::$instance === NULL )
|
||||
{
|
||||
$class = \get_called_class();
|
||||
$class = get_called_class();
|
||||
|
||||
if( $class == 'IPS\\Dispatcher' )
|
||||
{
|
||||
throw new \RuntimeException( "Only subclasses of Dispatcher can be instantiated" );
|
||||
throw new RuntimeException( "Only subclasses of Dispatcher can be instantiated" );
|
||||
}
|
||||
|
||||
static::$instance = new $class;
|
||||
@@ -60,13 +76,13 @@ abstract class _Dispatcher
|
||||
{
|
||||
$_redirect = FALSE;
|
||||
|
||||
if ( !file_exists( \IPS\SITE_FILES_PATH . '/conf_global.php' ) )
|
||||
if ( !file_exists( SITE_FILES_PATH . '/conf_global.php' ) )
|
||||
{
|
||||
$_redirect = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
require \IPS\SITE_FILES_PATH . '/conf_global.php';
|
||||
require SITE_FILES_PATH . '/conf_global.php';
|
||||
|
||||
if( !isset( $INFO['sql_database'] ) )
|
||||
{
|
||||
@@ -77,7 +93,7 @@ abstract class _Dispatcher
|
||||
/* This looks weird, but there was a period of time where "installed" was misspelled as "instaled" on Community in the Cloud after install finished. So, if that is present, assume we're okay. */
|
||||
if ( !isset( $INFO['instaled'] ) )
|
||||
{
|
||||
if( isset( $_SERVER['SERVER_PROTOCOL'] ) and \strstr( $_SERVER['SERVER_PROTOCOL'], '/1.0' ) !== false )
|
||||
if( isset( $_SERVER['SERVER_PROTOCOL'] ) and strstr( $_SERVER['SERVER_PROTOCOL'], '/1.0' ) !== false )
|
||||
{
|
||||
header( "HTTP/1.0 503 Service Unavailable" );
|
||||
}
|
||||
@@ -86,7 +102,7 @@ abstract class _Dispatcher
|
||||
header( "HTTP/1.1 503 Service Unavailable" );
|
||||
}
|
||||
|
||||
require \IPS\ROOT_PATH . '/' . \IPS\CP_DIRECTORY . '/install/installing.html';
|
||||
require ROOT_PATH . '/admin/install/installing.html';
|
||||
exit;
|
||||
}
|
||||
}
|
||||
@@ -95,14 +111,14 @@ abstract class _Dispatcher
|
||||
if( $_redirect === TRUE )
|
||||
{
|
||||
/* 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'] ), '/' );
|
||||
$url = ( Request::i()->isSecure() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . rtrim( dirname( $_SERVER['SCRIPT_NAME'] ), '/' );
|
||||
|
||||
header( "HTTP/1.1 307 Temporary Redirect" );
|
||||
foreach( \IPS\Output::getNoCacheHeaders() as $headerKey => $headerValue )
|
||||
foreach(Output::getNoCacheHeaders() as $headerKey => $headerValue )
|
||||
{
|
||||
header( "{$headerKey}: {$headerValue}" );
|
||||
}
|
||||
header( "Location: {$url}/" . \IPS\CP_DIRECTORY . "/install/" );
|
||||
header( "Location: {$url}/admin/install/" );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
@@ -116,37 +132,37 @@ abstract class _Dispatcher
|
||||
/**
|
||||
* @brief Controller Classname
|
||||
*/
|
||||
protected $classname;
|
||||
protected ?string $classname = NULL;
|
||||
|
||||
/**
|
||||
* @brief Controller instance
|
||||
*/
|
||||
public $dispatcherController;
|
||||
public mixed $dispatcherController = NULL;
|
||||
|
||||
/**
|
||||
* Init
|
||||
*
|
||||
* @return void
|
||||
* @throws \DomainException
|
||||
* @throws DomainException
|
||||
*/
|
||||
abstract public function init();
|
||||
abstract public function init() : void;
|
||||
|
||||
/**
|
||||
* Run
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
public function run() : void
|
||||
{
|
||||
/* Init class */
|
||||
if( !class_exists( $this->classname ) )
|
||||
{
|
||||
\IPS\Output::i()->error( 'page_doesnt_exist', '2S100/1', 404 );
|
||||
Output::i()->error( 'page_doesnt_exist', '2S100/1', 404 );
|
||||
}
|
||||
$this->dispatcherController = new $this->classname;
|
||||
if( !( $this->dispatcherController instanceof \IPS\Dispatcher\Controller ) )
|
||||
if( !( $this->dispatcherController instanceof Controller ) )
|
||||
{
|
||||
\IPS\Output::i()->error( 'page_not_found', '5S100/3', 500, '' );
|
||||
Output::i()->error( 'page_not_found', '5S100/3', 500, '' );
|
||||
}
|
||||
|
||||
/* Execute */
|
||||
@@ -154,23 +170,26 @@ abstract class _Dispatcher
|
||||
|
||||
$this->finish();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finish
|
||||
*
|
||||
* @return void
|
||||
* @return void
|
||||
* @throws ErrorException
|
||||
*/
|
||||
public function finish()
|
||||
public function finish() : void
|
||||
{
|
||||
Bridge::i()->dispatcherFinish();
|
||||
|
||||
/* If we're still here - output */
|
||||
if ( \IPS\Request::i()->isAjax() )
|
||||
if ( Request::i()->isAjax() )
|
||||
{
|
||||
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->blankTemplate( \IPS\Output::i()->output ), 200, 'text/html' );
|
||||
Output::i()->sendOutput( Theme::i()->getTemplate( 'global', 'core' )->blankTemplate( 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, $this->getLocationData() ), 200, 'text/html' );
|
||||
Output::i()->sendOutput( Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( Output::i()->title, Output::i()->output, $this->getLocationData() ), 200, 'text/html' );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,15 +197,87 @@ abstract class _Dispatcher
|
||||
* Get an array of data representing the user's current location
|
||||
* This gets passed to the templates in order to apply some attributes to the body tag
|
||||
*
|
||||
* @return array
|
||||
* @return array{app:string,module:string,controller:string,id?:int|null}
|
||||
*/
|
||||
public function getLocationData()
|
||||
public function getLocationData(): array
|
||||
{
|
||||
return array(
|
||||
'app' => \IPS\Dispatcher::i()->application->directory,
|
||||
'module' => \IPS\Dispatcher::i()->module->key,
|
||||
'controller' => \IPS\Dispatcher::i()->controller,
|
||||
'id' => \IPS\Request::i()->id ? (int) \IPS\Request::i()->id : NULL
|
||||
'app' => Dispatcher::i()->application->directory,
|
||||
'module' => Dispatcher::i()->module->key,
|
||||
'controller' => Dispatcher::i()->controller,
|
||||
'id' => Request::i()->id ? (int) Request::i()->id : NULL
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the current location to determine if it's a match
|
||||
*
|
||||
* @param string $location
|
||||
* @param string|null $app
|
||||
* @param string|null $module
|
||||
* @param string|null $controller
|
||||
* @return bool
|
||||
*/
|
||||
public static function checkLocation( string $location, ?string $app = null, ?string $module = null, ?string $controller = null ) : bool
|
||||
{
|
||||
/* If we have no instance it's always false */
|
||||
if( !static::hasInstance() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* This is required */
|
||||
if( static::i()->controllerLocation != $location )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If we specified an application, check that first */
|
||||
if( $app !== null )
|
||||
{
|
||||
if( isset( static::i()->application ) )
|
||||
{
|
||||
if( static::i()->application->directory != $app )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
elseif( isset( Request::i()->app ) and Request::i()->app != $app )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Repeat for module */
|
||||
if( $module !== null )
|
||||
{
|
||||
if( isset( static::i()->module ) )
|
||||
{
|
||||
if( static::i()->module->key != $module )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
elseif( isset( Request::i()->module ) and Request::i()->module != $module )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* And finally the controller */
|
||||
if( $controller !== null )
|
||||
{
|
||||
if( isset( static::i()->controller ) and static::i()->controller != $controller )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
elseif( isset( Request::i()->controller ) and Request::i()->controller != $controller )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Still here? All good */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -11,28 +11,36 @@
|
||||
namespace IPS\Dispatcher;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use DomainException;
|
||||
use IPS\Output;
|
||||
use IPS\Request;
|
||||
use IPS\Session;
|
||||
use IPS\Theme;
|
||||
use function defined;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatcher that doesn't really dispatch but sets things up for external scripts like Pages external blocks
|
||||
*/
|
||||
class _External extends \IPS\Dispatcher\Standard
|
||||
class External extends Standard
|
||||
{
|
||||
/**
|
||||
* Controller Location
|
||||
*/
|
||||
public $controllerLocation = 'front';
|
||||
public string $controllerLocation = 'front';
|
||||
|
||||
/**
|
||||
* Init
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init()
|
||||
public function init() : void
|
||||
{
|
||||
/* Base CSS */
|
||||
static::baseCss();
|
||||
@@ -46,11 +54,11 @@ class _External extends \IPS\Dispatcher\Standard
|
||||
parent::init();
|
||||
|
||||
/* Don't update sessions for this hit as it will wipe location data */
|
||||
\IPS\Session::i()->noUpdate();
|
||||
Session::i()->noUpdate();
|
||||
}
|
||||
catch ( \DomainException $e )
|
||||
catch ( DomainException $e )
|
||||
{
|
||||
\IPS\Output::i()->error( $e->getMessage(), '2S100/' . $e->getCode(), $e->getCode() === 4 ? 403 : 404, '' );
|
||||
Output::i()->error( $e->getMessage(), '2S100/' . $e->getCode(), $e->getCode() === 4 ? 403 : 404, '' );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,15 +67,15 @@ class _External extends \IPS\Dispatcher\Standard
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function baseJs()
|
||||
protected static function baseJs() : void
|
||||
{
|
||||
parent::baseJs();
|
||||
|
||||
/* Stuff for output */
|
||||
if ( !\IPS\Request::i()->isAjax() )
|
||||
if ( !Request::i()->isAjax() )
|
||||
{
|
||||
\IPS\Output::i()->globalControllers[] = 'core.front.core.app';
|
||||
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'front.js' ) );
|
||||
Output::i()->globalControllers[] = 'core.front.core.app';
|
||||
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'front.js' ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,18 +84,14 @@ class _External extends \IPS\Dispatcher\Standard
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function baseCss()
|
||||
public static function baseCss() : void
|
||||
{
|
||||
parent::baseCss();
|
||||
|
||||
/* Stuff for output */
|
||||
if ( !\IPS\Request::i()->isAjax() )
|
||||
if ( !Request::i()->isAjax() )
|
||||
{
|
||||
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'core.css', 'core', 'front' ) );
|
||||
if ( \IPS\Theme::i()->settings['responsive'] )
|
||||
{
|
||||
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'core_responsive.css', 'core', 'front' ) );
|
||||
}
|
||||
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'core.css', 'core', 'front' ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
+324
-252
File diff suppressed because it is too large.
Load diff
@@ -12,32 +12,46 @@
|
||||
namespace IPS\Dispatcher;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use DomainException;
|
||||
use Exception;
|
||||
use IPS\Api\GraphQL as GraphQLApi;
|
||||
use IPS\Api\OAuthClient;
|
||||
use IPS\IPS;
|
||||
use IPS\Member;
|
||||
use IPS\Request;
|
||||
use OutOfRangeException;
|
||||
use function defined;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
use const IPS\DEBUG_GRAPHQL;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_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";
|
||||
IPS::$PSR0Namespaces['GraphQL'] = \IPS\ROOT_PATH . "/system/3rd_party/graphql-php";
|
||||
|
||||
/**
|
||||
* @brief API Dispatcher
|
||||
*/
|
||||
class _GraphQL extends Api
|
||||
class GraphQL extends Api
|
||||
{
|
||||
/**
|
||||
* @brief Path
|
||||
*/
|
||||
public $path = '/graphql';
|
||||
public ?string $path = '/graphql';
|
||||
|
||||
/**
|
||||
* Init
|
||||
*
|
||||
* @return void
|
||||
* @throws \DomainException
|
||||
* @throws DomainException
|
||||
*/
|
||||
public function init()
|
||||
public function init() : void
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -52,27 +66,27 @@ class _GraphQL extends Api
|
||||
if ( $this->rawAccessToken )
|
||||
{
|
||||
$this->_setAccessToken();
|
||||
$client = \IPS\Api\OAuthClient::load( $this->accessToken['client_id'] );
|
||||
$client = OAuthClient::load( $this->accessToken['client_id'] );
|
||||
|
||||
if( $client->api_access == 'rest' )
|
||||
{
|
||||
throw new \IPS\Api\Exception( 'NO_GRAPHQL_ACCESS', '3S426_graphql/1', 403 );
|
||||
}
|
||||
\IPS\Member::$loggedInMember = \IPS\Member::load( $this->accessToken['member_id'] );
|
||||
Member::$loggedInMember = Member::load( $this->accessToken['member_id'] );
|
||||
}
|
||||
else
|
||||
{
|
||||
$client = \IPS\Api\OAuthClient::load( $this->rawApiKey );
|
||||
$client = OAuthClient::load( $this->rawApiKey );
|
||||
|
||||
/* Check that the API key has access to the GraphQL API */
|
||||
if( $client->api_access == 'rest' )
|
||||
{
|
||||
throw new \IPS\Api\Exception( 'NO_GRAPHQL_ACCESS', '3S426_graphql/1', 403 );
|
||||
}
|
||||
\IPS\Member::$loggedInMember = new \IPS\Member;
|
||||
Member::$loggedInMember = new Member;
|
||||
}
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
catch ( OutOfRangeException $e )
|
||||
{
|
||||
throw new \IPS\Api\Exception( 'INVALID_API_KEY', '3S290_graphql/7', 401 );
|
||||
}
|
||||
@@ -89,9 +103,9 @@ class _GraphQL extends Api
|
||||
$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_graphql/8', '2S290_graphql/B', '3S290_graphql/7', '3S290_graphql/9' ) ) )
|
||||
if ( in_array( $e->exceptionCode, array( '2S290_graphql/8', '2S290_graphql/B', '3S290_graphql/7', '3S290_graphql/9' ) ) )
|
||||
{
|
||||
$this->_log( $response, $e->getCode(), \in_array( $e->exceptionCode, array( '3S290/7', '3S290/9', '3S290/B' ) ) );
|
||||
$this->_log( (array)$response, $e->getCode(), in_array( $e->exceptionCode, array( '3S290/7', '3S290/9', '3S290/B' ) ) );
|
||||
}
|
||||
|
||||
/* Output */
|
||||
@@ -104,26 +118,26 @@ class _GraphQL extends Api
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
public function run() : void
|
||||
{
|
||||
try
|
||||
{
|
||||
/* Work out the query (can either use a JSON-encoded or form-encoded) body */
|
||||
$query = NULL;
|
||||
$variables = [];
|
||||
if( \IPS\Request::i()->query )
|
||||
if( Request::i()->query )
|
||||
{
|
||||
$query = \IPS\Request::i()->query;
|
||||
$query = Request::i()->query;
|
||||
|
||||
if( isset( \IPS\Request::i()->variables ) )
|
||||
if( isset( Request::i()->variables ) )
|
||||
{
|
||||
if( \is_array( \IPS\Request::i()->variables ) )
|
||||
if( is_array( Request::i()->variables ) )
|
||||
{
|
||||
$variables = \IPS\Request::i()->variables;
|
||||
$variables = Request::i()->variables;
|
||||
}
|
||||
else
|
||||
{
|
||||
$variables = json_decode( \IPS\Request::i()->variables, TRUE );
|
||||
$variables = json_decode( Request::i()->variables, TRUE );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,15 +155,15 @@ class _GraphQL extends Api
|
||||
$this->_respond( json_encode( array( 'errors' => array( array( 'message' => 'NO_QUERY' ) ) ), JSON_PRETTY_PRINT ), 400 );
|
||||
}
|
||||
|
||||
$output = \IPS\Api\GraphQL::execute( $query, $variables );
|
||||
$output = GraphQLApi::execute( $query, $variables );
|
||||
|
||||
\IPS\Member::loggedIn()->language()->parseOutputForDisplay( $output );
|
||||
Member::loggedIn()->language()->parseOutputForDisplay( $output );
|
||||
$this->_respond( json_encode( $output, JSON_PRETTY_PRINT ), 200 );
|
||||
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
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 );
|
||||
$response = json_encode( array( 'errors' => array( array( 'message' => ( \IPS\IN_DEV OR DEBUG_GRAPHQL ) ? $e->getMessage() : 'UNKNOWN_ERROR', 'id' => $e->getCode() ) ) ), JSON_PRETTY_PRINT );
|
||||
$this->_respond( $response, 500 );
|
||||
}
|
||||
}
|
||||
|
||||
+89
-48
@@ -11,38 +11,62 @@
|
||||
namespace IPS\Dispatcher;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use Exception;
|
||||
use IPS\Data\Cache;
|
||||
use IPS\Data\Store;
|
||||
use IPS\Db;
|
||||
use IPS\Dispatcher;
|
||||
use IPS\Http\Url;
|
||||
use IPS\IPS;
|
||||
use IPS\Lang;
|
||||
use IPS\Member;
|
||||
use IPS\Member\Group;
|
||||
use IPS\Output;
|
||||
use IPS\Request;
|
||||
use IPS\Settings;
|
||||
use IPS\Theme;
|
||||
use function defined;
|
||||
use const IPS\COOKIE_BYPASS_SSLONLY;
|
||||
use const IPS\COOKIE_DOMAIN;
|
||||
use const IPS\COOKIE_PATH;
|
||||
use const IPS\COOKIE_PREFIX;
|
||||
use const IPS\SITE_FILES_PATH;
|
||||
use const IPS\STORE_CONFIG;
|
||||
use const IPS\STORE_METHOD;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Installer/Upgrader Dispatcher
|
||||
*/
|
||||
class _Setup extends \IPS\Dispatcher
|
||||
class Setup extends Dispatcher
|
||||
{
|
||||
/**
|
||||
* @brief Controller Location
|
||||
*/
|
||||
public $controllerLocation = 'setup';
|
||||
public string $controllerLocation = 'setup';
|
||||
|
||||
/**
|
||||
* @brief Install or upgrade
|
||||
*/
|
||||
public $setupLocation = 'install';
|
||||
public string $setupLocation = 'install';
|
||||
|
||||
/**
|
||||
* @brief Step
|
||||
*/
|
||||
public $step = 1;
|
||||
public int $step = 1;
|
||||
|
||||
/**
|
||||
* Initiator
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init()
|
||||
public function init() : void
|
||||
{
|
||||
}
|
||||
|
||||
@@ -51,7 +75,7 @@ class _Setup extends \IPS\Dispatcher
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function returnSteps()
|
||||
protected function returnSteps(): array
|
||||
{
|
||||
if( $this->setupLocation == 'upgrade' )
|
||||
{
|
||||
@@ -83,14 +107,31 @@ class _Setup extends \IPS\Dispatcher
|
||||
/**
|
||||
* Set location (install or upgrade)
|
||||
*
|
||||
* @param string $location 'install' or 'upgrade'
|
||||
* @return \IPS\Dispatcher\Setup
|
||||
* @param string $location 'install' or 'upgrade'
|
||||
* @return Setup
|
||||
*/
|
||||
public function setLocation( $location )
|
||||
public function setLocation(string $location ): Setup
|
||||
{
|
||||
$this->setupLocation = $location;
|
||||
$steps = $this->returnSteps();
|
||||
$this->classname = 'IPS\core\modules\setup\\' . $location . '\\' . ( isset( \IPS\Request::i()->controller ) ? \IPS\Request::i()->controller : $steps[1] );
|
||||
$currentStep = ( isset( Request::i()->controller ) ? Request::i()->controller : $steps[1] );
|
||||
$this->classname = 'IPS\core\modules\setup\\' . $location . '\\' . $currentStep;
|
||||
|
||||
/* If we are upgrading and just starting, check the current version.
|
||||
If we are upgrading from a version prior to v5, disable all non-1st party apps. */
|
||||
if( $location == 'upgrade' and $currentStep == 'login' )
|
||||
{
|
||||
$currentBaseVersion = Db::i()->select( 'app_long_version', 'core_applications', [ 'app_directory=?', 'core' ] )->first();
|
||||
|
||||
/* If we are upgrading from a version earlier than v5, disable and lock anything that isn't 1st party */
|
||||
if( $currentBaseVersion < 5000016 )
|
||||
{
|
||||
Db::i()->update( 'core_applications', [ 'app_enabled' => 0, 'app_requires_manual_intervention' => 1 ], Db::i()->in( 'app_directory', IPS::$ipsApps, true ) );
|
||||
Cache::i()->clearAll();
|
||||
Store::i()->clearAll();
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -99,7 +140,7 @@ class _Setup extends \IPS\Dispatcher
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
public function run() : void
|
||||
{
|
||||
/* Installer checks */
|
||||
if( $this->setupLocation == 'install' )
|
||||
@@ -110,7 +151,7 @@ class _Setup extends \IPS\Dispatcher
|
||||
{
|
||||
rename( \IPS\ROOT_PATH . '/conf_global.dist.php', \IPS\ROOT_PATH . '/conf_global.php' );
|
||||
}
|
||||
catch ( \Exception $e ) { }
|
||||
catch ( Exception $e ) { }
|
||||
|
||||
if ( !file_exists( \IPS\ROOT_PATH . '/conf_global.php' ) )
|
||||
{
|
||||
@@ -118,115 +159,115 @@ class _Setup extends \IPS\Dispatcher
|
||||
{
|
||||
file_put_contents( \IPS\ROOT_PATH. '/conf_global.php', '' );
|
||||
}
|
||||
catch ( \Exception $e ) { }
|
||||
catch ( Exception $e ) { }
|
||||
}
|
||||
|
||||
if ( !file_exists( \IPS\ROOT_PATH . '/conf_global.php' ) )
|
||||
{
|
||||
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( \IPS\Member::loggedIn()->language()->addToStack( 'installation_error' ), '', true, \IPS\ROOT_PATH ), 500, 'text/html', array(), FALSE, FALSE, FALSE );
|
||||
Output::i()->sendOutput( Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( Member::loggedIn()->language()->addToStack( 'installation_error' ), '', true, \IPS\ROOT_PATH ), 500, 'text/html', array(), FALSE, FALSE, FALSE );
|
||||
}
|
||||
}
|
||||
|
||||
require \IPS\ROOT_PATH . '/conf_global.php';
|
||||
if ( isset( $INFO ) and isset( $INFO['installed'] ) )
|
||||
{
|
||||
$upgradeUrl = new \IPS\Http\Url( \IPS\Settings::i()->base_url . \IPS\CP_DIRECTORY . '/upgrade/' );
|
||||
\IPS\Output::i()->redirect( $upgradeUrl );
|
||||
$upgradeUrl = new Url( Settings::i()->base_url . 'admin/upgrade/' );
|
||||
Output::i()->redirect( $upgradeUrl );
|
||||
}
|
||||
}
|
||||
/* Upgrader Checks */
|
||||
else
|
||||
{
|
||||
if ( !file_exists( \IPS\SITE_FILES_PATH . '/conf_global.php' ) )
|
||||
if ( !file_exists( 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 );
|
||||
Output::i()->sendOutput( Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( Member::loggedIn()->language()->addToStack('upgrade_error'), '', Member::loggedIn()->language()->addToStack('no_conf_global'), \IPS\ROOT_PATH ), 200, 'text/html', array(), FALSE );
|
||||
}
|
||||
|
||||
if ( \IPS\STORE_METHOD === 'FileSystem' )
|
||||
if ( STORE_METHOD === 'FileSystem' )
|
||||
{
|
||||
$config = json_decode( \IPS\STORE_CONFIG, TRUE );
|
||||
$config = json_decode( STORE_CONFIG, TRUE );
|
||||
$path = str_replace( '{root}', \IPS\ROOT_PATH, $config['path'] );
|
||||
if ( !is_dir( $path ) or !is_writable( $path ) )
|
||||
{
|
||||
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( \IPS\Member::loggedIn()->language()->addToStack('upgrade_error'), '', \IPS\Member::loggedIn()->language()->addToStack( 'create_conf_global', FALSE, array( 'sprintf' => array( $path ) ) ), \IPS\ROOT_PATH ), 200, 'text/html', array(), FALSE );
|
||||
Output::i()->sendOutput( Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( Member::loggedIn()->language()->addToStack('upgrade_error'), '', Member::loggedIn()->language()->addToStack( 'create_conf_global', FALSE, array( 'sprintf' => array( $path ) ) ), \IPS\ROOT_PATH ), 200, 'text/html', array(), FALSE );
|
||||
}
|
||||
}
|
||||
|
||||
require \IPS\SITE_FILES_PATH . '/conf_global.php';
|
||||
require 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 );
|
||||
Output::i()->sendOutput( Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( Member::loggedIn()->language()->addToStack('upgrade_error'), '', Member::loggedIn()->language()->addToStack('bad_conf_global'), \IPS\ROOT_PATH ), 200, 'text/html', array(), FALSE );
|
||||
}
|
||||
|
||||
/* Fix languages if necessary */
|
||||
if( \IPS\Db::i()->checkForTable( 'core_sys_lang' ) AND !\IPS\Db::i()->checkForColumn( 'core_sys_lang', 'lang_order' ) )
|
||||
if( Db::i()->checkForTable( 'core_sys_lang' ) AND !Db::i()->checkForColumn( 'core_sys_lang', 'lang_order' ) )
|
||||
{
|
||||
\IPS\Lang::languages( \IPS\Db::i()->select( '*', 'core_sys_lang' ) );
|
||||
Lang::languages( Db::i()->select( '*', 'core_sys_lang' ) );
|
||||
}
|
||||
|
||||
/* Fix members if necessary */
|
||||
if( !\IPS\Db::i()->checkForTable( 'core_members' ) )
|
||||
if( !Db::i()->checkForTable( 'core_members' ) )
|
||||
{
|
||||
\IPS\Member::$databaseTable = 'members';
|
||||
Member::$databaseTable = 'members';
|
||||
|
||||
if ( !\IPS\Db::i()->checkForTable( 'core_groups' ) )
|
||||
if ( !Db::i()->checkForTable( 'core_groups' ) )
|
||||
{
|
||||
\IPS\Member\Group::$databaseTable = 'groups';
|
||||
Group::$databaseTable = 'groups';
|
||||
}
|
||||
|
||||
/* re-arrange 3.x mapping to 4.x code */
|
||||
$bits = \IPS\Member::$bitOptions;
|
||||
$bits = Member::$bitOptions;
|
||||
unset( $bits['members_bitoptions']['members_bitoptions2'] );
|
||||
\IPS\Member::$bitOptions = $bits;
|
||||
Member::$bitOptions = $bits;
|
||||
}
|
||||
}
|
||||
|
||||
\IPS\Settings::i()->base_url = ( \IPS\Request::i()->isSecure() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . mb_substr( $_SERVER['SCRIPT_NAME'], 0, -mb_strlen( \IPS\CP_DIRECTORY . '/' . $this->setupLocation . '/index.php' ) );
|
||||
Settings::i()->base_url = ( Request::i()->isSecure() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . mb_substr( $_SERVER['SCRIPT_NAME'], 0, -mb_strlen( 'admin/' . $this->setupLocation . '/index.php' ) );
|
||||
|
||||
$this->step = array_search( \IPS\Request::i()->controller, $this->returnSteps() );
|
||||
$this->step = array_search( Request::i()->controller, $this->returnSteps() );
|
||||
|
||||
session_name( ( \IPS\COOKIE_PREFIX !== NULL ) ? \IPS\COOKIE_PREFIX . 'IPSSessionSetup' : 'IPSSessionSetup' );
|
||||
session_name( ( COOKIE_PREFIX !== NULL ) ? COOKIE_PREFIX . 'IPSSessionSetup' : 'IPSSessionSetup' );
|
||||
$currentCookieParams = session_get_cookie_params();
|
||||
session_set_cookie_params(
|
||||
86400 * 14,
|
||||
( \IPS\COOKIE_PATH !== NULL ) ? \IPS\COOKIE_PATH : $currentCookieParams['path'],
|
||||
( \IPS\COOKIE_DOMAIN !== NULL ) ? \IPS\COOKIE_DOMAIN : $currentCookieParams['domain'],
|
||||
( \IPS\COOKIE_BYPASS_SSLONLY !== TRUE ) ? ( mb_substr( \IPS\Settings::i()->base_url, 0, 5 ) == 'https' ) : $currentCookieParams['secure'],
|
||||
( COOKIE_PATH !== NULL ) ? COOKIE_PATH : $currentCookieParams['path'],
|
||||
( COOKIE_DOMAIN !== NULL ) ? COOKIE_DOMAIN : $currentCookieParams['domain'],
|
||||
( !COOKIE_BYPASS_SSLONLY ) ? ( mb_substr( Settings::i()->base_url, 0, 5 ) == 'https' ) : $currentCookieParams['secure'],
|
||||
TRUE
|
||||
);
|
||||
|
||||
if( !@session_start() )
|
||||
{
|
||||
\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, '' );
|
||||
Output::i()->error( Member::loggedIn()->language()->addToStack( 'session_no_good', FALSE, array( 'sprintf' => array( IPS::$lastError?->getMessage() ) ) ), '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 ) )
|
||||
if( $this->classname != 'IPS\core\modules\setup\upgrade\done' and $this->setupLocation != 'install' and $this->step > 1 AND ( !isset( $_SESSION['uniqueKey'] ) OR $_SESSION['uniqueKey'] != Request::i()->key ) )
|
||||
{
|
||||
\IPS\Output::i()->error( 'upgrade_session_error', '3S109/4', 403, '' );
|
||||
Output::i()->error( 'upgrade_session_error', '3S109/4', 403, '' );
|
||||
}
|
||||
|
||||
/* Init class */
|
||||
if( !class_exists( $this->classname ) )
|
||||
{
|
||||
\IPS\Output::i()->error( 'page_doesnt_exist', '2S100/1', 404 );
|
||||
Output::i()->error( 'page_doesnt_exist', '2S100/1', 404 );
|
||||
}
|
||||
$controller = new $this->classname;
|
||||
if( !( $controller instanceof \IPS\Dispatcher\Controller ) )
|
||||
if( !( $controller instanceof Controller) )
|
||||
{
|
||||
\IPS\Output::i()->error( 'page_not_found', '5S100/3', 500, '' );
|
||||
Output::i()->error( 'page_not_found', '5S100/3', 500, '' );
|
||||
}
|
||||
|
||||
/* Execute */
|
||||
$controller->execute();
|
||||
|
||||
/* If we're still here - output */
|
||||
if ( \IPS\Request::i()->isAjax() )
|
||||
if ( Request::i()->isAjax() )
|
||||
{
|
||||
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->blankTemplate( \IPS\Output::i()->output ), 200, 'text/html', array(), FALSE, FALSE, TRUE, FALSE );
|
||||
Output::i()->sendOutput( Theme::i()->getTemplate( 'global', 'core' )->blankTemplate( 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', array(), FALSE, FALSE, TRUE, FALSE );
|
||||
Output::i()->sendOutput( Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( Output::i()->title, Output::i()->output ), 200, 'text/html', array(), FALSE, FALSE, TRUE, FALSE );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+151
-102
@@ -11,56 +11,80 @@
|
||||
namespace IPS\Dispatcher;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use DomainException;
|
||||
use IPS\Application;
|
||||
use IPS\Application\Module;
|
||||
use IPS\Data\Cache;
|
||||
use IPS\Data\Cache\None;
|
||||
use IPS\Data\Store;
|
||||
use IPS\Db\Exception;
|
||||
use IPS\Dispatcher;
|
||||
use IPS\Http\Url;
|
||||
use IPS\Lang;
|
||||
use IPS\Member;
|
||||
use IPS\Output;
|
||||
use IPS\Platform\Bridge;
|
||||
use IPS\Request;
|
||||
use IPS\Settings;
|
||||
use IPS\Task;
|
||||
use IPS\Theme;
|
||||
use OutOfRangeException;
|
||||
use function count;
|
||||
use function defined;
|
||||
use function intval;
|
||||
use function is_array;
|
||||
use const IPS\CIC2;
|
||||
use const IPS\SITE_FILES_PATH;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard Dispatcher
|
||||
*/
|
||||
abstract class _Standard extends \IPS\Dispatcher
|
||||
abstract class Standard extends Dispatcher
|
||||
{
|
||||
/**
|
||||
* Application
|
||||
*/
|
||||
public $application;
|
||||
public ?Application $application = NULL;
|
||||
|
||||
/**
|
||||
* Module
|
||||
*/
|
||||
public $module;
|
||||
public ?Module $module = NULL;
|
||||
|
||||
/**
|
||||
* Controller
|
||||
*/
|
||||
public $controller;
|
||||
public ?string $controller = NULL;
|
||||
|
||||
/**
|
||||
* @brief Check access permissions
|
||||
*/
|
||||
public $checkGenericPermissions = TRUE;
|
||||
public bool $checkGenericPermissions = TRUE;
|
||||
|
||||
public array $platformFeatureExpires = [];
|
||||
|
||||
/**
|
||||
* Base CSS
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function baseCss()
|
||||
public static function baseCss() : void
|
||||
{
|
||||
if ( !\IPS\Request::i()->isAjax() )
|
||||
if ( !Request::i()->isAjax() )
|
||||
{
|
||||
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'framework.css', 'core', 'global' ) );
|
||||
if ( \IPS\Theme::i()->settings['responsive'] )
|
||||
{
|
||||
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'responsive.css', 'core', 'global' ) );
|
||||
}
|
||||
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'framework/framework.css', 'core', 'global' ) );
|
||||
}
|
||||
|
||||
if ( \count( \IPS\Lang::languages() ) > 1 )
|
||||
if ( count( Lang::languages() ) > 1 )
|
||||
{
|
||||
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'flags.css', 'core', 'global' ) );
|
||||
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'flags.css', 'core', 'global' ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,28 +93,26 @@ abstract class _Standard extends \IPS\Dispatcher
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function baseJs()
|
||||
protected static function baseJs() : void
|
||||
{
|
||||
/* Stuff for output */
|
||||
if ( !\IPS\Request::i()->isAjax() )
|
||||
if ( !Request::i()->isAjax() )
|
||||
{
|
||||
/* JS */
|
||||
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'library.js' ) );
|
||||
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'js_lang_' . \IPS\Member::loggedIn()->language()->id . '.js' ) );
|
||||
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'framework.js' ) );
|
||||
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'global_core.js', 'core', 'global' ) );
|
||||
\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['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['emoji_style'] = ( \IPS\Settings::i()->getFromConfGlobal('sql_utf8mb4') === TRUE ) ? \IPS\Settings::i()->emoji_style : 'disabled';
|
||||
\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;
|
||||
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'library.js' ) );
|
||||
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, [ Output\Javascript::getLanguageUrl( Member::loggedIn()->language() ) ] );
|
||||
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'framework.js' ) );
|
||||
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'global_core.js', 'core', 'global' ) );
|
||||
Output::i()->jsVars['date_format'] = mb_strtolower( Member::loggedIn()->language()->preferredDateFormat() );
|
||||
Output::i()->jsVars['date_first_day'] = 0;
|
||||
Output::i()->jsVars['ipb_url_filter_option'] = Settings::i()->ipb_url_filter_option;
|
||||
Output::i()->jsVars['url_filter_any_action'] = Settings::i()->url_filter_any_action;
|
||||
Output::i()->jsVars['bypass_profanity'] = intval( Member::loggedIn()->group['g_bypass_badwords'] );
|
||||
Output::i()->jsVars['emoji_style'] = ( Settings::i()->getFromConfGlobal('sql_utf8mb4') === TRUE ) ? Settings::i()->emoji_style : 'disabled';
|
||||
Output::i()->jsVars['emoji_shortcodes'] = (bool) Settings::i()->emoji_shortcodes;
|
||||
Output::i()->jsVars['emoji_cache'] = (int) Settings::i()->emoji_cache;
|
||||
Output::i()->jsVars['image_jpg_quality'] = (int) Settings::i()->image_jpg_quality ?: 85;
|
||||
Output::i()->jsVars['cloud2'] = (bool) CIC2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,15 +121,15 @@ abstract class _Standard extends \IPS\Dispatcher
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function finish()
|
||||
public function finish() : void
|
||||
{
|
||||
/* If we're still here - output */
|
||||
if ( ! \IPS\Request::i()->isAjax() )
|
||||
if ( ! Request::i()->isAjax() )
|
||||
{
|
||||
/* Load all models for this app and location */
|
||||
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'app.js', \IPS\Dispatcher::i()->application->directory ) );
|
||||
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'app.js', Dispatcher::i()->application->directory ) );
|
||||
/* Map.js must come last as it will always have the correct file names */
|
||||
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'map.js' ) );
|
||||
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'map.js' ) );
|
||||
}
|
||||
|
||||
parent::finish();
|
||||
@@ -116,53 +138,53 @@ abstract class _Standard extends \IPS\Dispatcher
|
||||
/**
|
||||
* @brief Initialize tasks
|
||||
*/
|
||||
protected $runTasks = FALSE;
|
||||
protected bool $runTasks = FALSE;
|
||||
|
||||
/**
|
||||
* Init
|
||||
*
|
||||
* @return void
|
||||
* @throws \DomainException
|
||||
* @throws DomainException
|
||||
*/
|
||||
public function init()
|
||||
public function init() : void
|
||||
{
|
||||
$this->runTasks = (bool) \IPS\Member::loggedIn()->member_id;
|
||||
$this->runTasks = (bool) Member::loggedIn()->member_id;
|
||||
|
||||
/* Force HTTPs and correct domain (e.g. not "www." if that's not in the base URL) */
|
||||
if ( mb_strtolower( $_SERVER['REQUEST_METHOD'] ) == 'get' )
|
||||
{
|
||||
$baseUrl = new \IPS\Http\Url( \IPS\Settings::i()->base_url );
|
||||
$newUrl = \IPS\Request::i()->url();
|
||||
$baseUrl = new Url( Settings::i()->base_url );
|
||||
$newUrl = Request::i()->url();
|
||||
|
||||
if( $baseUrl->data['scheme'] === 'https' and \IPS\Request::i()->url()->data['scheme'] !== 'https' )
|
||||
if( $baseUrl->data['scheme'] === 'https' and Request::i()->url()->data['scheme'] !== 'https' )
|
||||
{
|
||||
$newUrl = $newUrl->setScheme('https');
|
||||
}
|
||||
|
||||
if( $baseUrl->data['host'] !== \IPS\Request::i()->url()->data['host'] )
|
||||
if( $baseUrl->data['host'] !== Request::i()->url()->data['host'] )
|
||||
{
|
||||
$newUrl = $newUrl->setHost( $baseUrl->data['host'] );
|
||||
}
|
||||
|
||||
if( $newUrl != \IPS\Request::i()->url() )
|
||||
if( $newUrl != Request::i()->url() )
|
||||
{
|
||||
\IPS\Output::i()->redirect( $newUrl );
|
||||
Output::i()->redirect( $newUrl );
|
||||
}
|
||||
}
|
||||
|
||||
/* Set locale */
|
||||
\IPS\Member::loggedIn()->language()->setLocale();
|
||||
Member::loggedIn()->language()->setLocale();
|
||||
|
||||
/* Set Application */
|
||||
if ( isset( \IPS\Request::i()->app ) )
|
||||
if ( isset( Request::i()->app ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->application = \IPS\Application::load( \IPS\Request::i()->app );
|
||||
$this->application = Application::load( Request::i()->app );
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
catch ( OutOfRangeException $e )
|
||||
{
|
||||
$applications = \IPS\Application::applications();
|
||||
$applications = Application::applications();
|
||||
|
||||
foreach( $applications as $application )
|
||||
{
|
||||
@@ -176,13 +198,13 @@ abstract class _Standard extends \IPS\Dispatcher
|
||||
{
|
||||
$this->application = array_shift( $applications );
|
||||
}
|
||||
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'app.js' ) );
|
||||
throw new \DomainException( 'requested_route_404', 5 );
|
||||
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'app.js' ) );
|
||||
throw new DomainException( 'requested_route_404', 5 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$applications = \IPS\Application::applications();
|
||||
$applications = Application::applications();
|
||||
|
||||
foreach( $applications as $application )
|
||||
{
|
||||
@@ -199,11 +221,11 @@ abstract class _Standard extends \IPS\Dispatcher
|
||||
}
|
||||
|
||||
/* Init Application */
|
||||
if( $this->checkGenericPermissions === TRUE AND !$this->application->canAccess( \IPS\Member::loggedIn() ) AND $this->controllerLocation != 'admin' )
|
||||
if( $this->checkGenericPermissions === TRUE AND !$this->application->canAccess( Member::loggedIn() ) AND $this->controllerLocation != 'admin' )
|
||||
{
|
||||
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'app.js' ) );
|
||||
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'app.js' ) );
|
||||
$message = $this->application->disabled_message ?: 'generic_offline_message';
|
||||
throw new \DomainException( $message, 4 );
|
||||
throw new DomainException( $message, 4 );
|
||||
}
|
||||
if ( method_exists( $this->application, 'init' ) )
|
||||
{
|
||||
@@ -211,16 +233,16 @@ abstract class _Standard extends \IPS\Dispatcher
|
||||
}
|
||||
|
||||
/* Set Module */
|
||||
if ( isset( \IPS\Request::i()->module ) )
|
||||
if ( isset( Request::i()->module ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->module = \IPS\Application\Module::get( $this->application->directory, \IPS\Request::i()->module, static::i()->controllerLocation );
|
||||
$this->module = Module::get( $this->application->directory, Request::i()->module, static::i()->controllerLocation );
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
catch ( OutOfRangeException $e )
|
||||
{
|
||||
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'app.js' ) );
|
||||
throw new \DomainException( 'requested_route_404', 6 );
|
||||
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'app.js' ) );
|
||||
throw new DomainException( 'requested_route_404', 6 );
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -229,54 +251,49 @@ abstract class _Standard extends \IPS\Dispatcher
|
||||
}
|
||||
|
||||
/* Set controller */
|
||||
$this->controller = isset( \IPS\Request::i()->controller ) ? \IPS\Request::i()->controller : $this->module->default_controller;
|
||||
$this->controller = isset( Request::i()->controller ) ? 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 );
|
||||
throw new DomainException( 'requested_route_404', 7 );
|
||||
}
|
||||
|
||||
/* Set classname */
|
||||
$this->classname = 'IPS\\' . $this->application->directory . '\\modules\\' . $this->controllerLocation . '\\' . $this->module->key . '\\' . $this->controller;
|
||||
|
||||
/* Base Templates, CSS and JS */
|
||||
if ( !\IPS\Request::i()->isAjax() )
|
||||
if ( !Request::i()->isAjax() )
|
||||
{
|
||||
/* Templates */
|
||||
if ( \IPS\Data\Cache::i() instanceof \IPS\Data\Cache\None )
|
||||
if ( Cache::i() instanceof None )
|
||||
{
|
||||
\IPS\Data\Store::i()->templateLoad[] = array( 'core', $this->controllerLocation, 'global' );
|
||||
\IPS\Data\Store::i()->templateLoad[] = array( 'core', 'global', 'global' );
|
||||
\IPS\Data\Store::i()->templateLoad[] = array( 'core', 'global', 'forms' );
|
||||
\IPS\Data\Store::i()->templateLoad[] = array( 'core', $this->controllerLocation, 'forms' );
|
||||
Store::i()->templateLoad[] = array( 'core', $this->controllerLocation, 'global' );
|
||||
Store::i()->templateLoad[] = array( 'core', 'global', 'global' );
|
||||
Store::i()->templateLoad[] = array( 'core', 'global', 'forms' );
|
||||
Store::i()->templateLoad[] = array( 'core', $this->controllerLocation, 'forms' );
|
||||
$templateLoad = array();
|
||||
if ( ! \IPS\Theme::isUsingTemplateDiskCache() )
|
||||
foreach ( Store::i()->templateLoad as $data )
|
||||
{
|
||||
foreach ( \IPS\Data\Store::i()->templateLoad as $data )
|
||||
{
|
||||
$templateLoad[] = 'template_' . \IPS\Theme::i()->id . '_' . \IPS\Theme::makeBuiltTemplateLookupHash( $data[0], $data[1], $data[2] ) . '_' . $data[2];
|
||||
}
|
||||
\IPS\Data\Store::i()->loadIntoMemory( $templateLoad );
|
||||
$templateLoad[] = 'template_' . Theme::i()->id . '_' . Theme::makeBuiltTemplateLookupHash( $data[0], $data[1], $data[2] ) . '_' . $data[2];
|
||||
}
|
||||
|
||||
Store::i()->loadIntoMemory( $templateLoad );
|
||||
}
|
||||
|
||||
/* App JS */
|
||||
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'app.js' ) );
|
||||
|
||||
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'app.js' ) );
|
||||
|
||||
/* App CSS */
|
||||
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( $this->application->directory . '.css', $this->application->directory, $this->controllerLocation ) );
|
||||
if ( \IPS\Theme::i()->settings['responsive'] )
|
||||
{
|
||||
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( $this->application->directory . '_responsive.css', $this->application->directory, $this->controllerLocation ) );
|
||||
}
|
||||
$currentApp = $this->application;
|
||||
$currentApp::outputCss();
|
||||
|
||||
/* VLE */
|
||||
if ( \IPS\Lang::vleActive() )
|
||||
if ( Lang::vleActive() )
|
||||
{
|
||||
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'customization/visuallanguage.css', 'core', 'admin' ) );
|
||||
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'global_customization.js', 'core', 'global' ) );
|
||||
\IPS\Output::i()->globalControllers[] = 'core.global.customization.visualLang';
|
||||
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'customization/visuallanguage.css', 'core', 'admin' ) );
|
||||
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'global_customization.js', 'core', 'global' ) );
|
||||
Output::i()->globalControllers[] = 'core.global.customization.visualLang';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -286,7 +303,7 @@ abstract class _Standard extends \IPS\Dispatcher
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setDefaultModule()
|
||||
protected function setDefaultModule() : void
|
||||
{
|
||||
$modules = $this->application->modules( static::i()->controllerLocation );
|
||||
foreach( $modules as $module )
|
||||
@@ -308,13 +325,8 @@ abstract class _Standard extends \IPS\Dispatcher
|
||||
* @brief Enable destruct method
|
||||
* @note Disable to avoid an automatic database connection
|
||||
*/
|
||||
public $destruct = true;
|
||||
public bool $destruct = true;
|
||||
|
||||
/**
|
||||
* @brief Flag to check if we are in the destructor
|
||||
*/
|
||||
public bool $inDestructor = false;
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
* Runs tasks
|
||||
@@ -324,23 +336,60 @@ 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\SITE_FILES_PATH . '/conf_global.php' ) OR !$this->destruct )
|
||||
if( !file_exists( SITE_FILES_PATH . '/conf_global.php' ) OR !$this->destruct )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $this->runTasks and \IPS\Settings::i()->task_use_cron == 'normal' and !\IPS\Request::i()->isAjax() )
|
||||
if ( $this->runTasks and Settings::i()->task_use_cron == 'normal' and !Request::i()->isAjax() )
|
||||
{
|
||||
$this->inDestructor = true;
|
||||
try
|
||||
{
|
||||
$task = \IPS\Task::queued();
|
||||
$task = Task::queued();
|
||||
if ( $task )
|
||||
{
|
||||
$task->runAndLog();
|
||||
}
|
||||
}
|
||||
catch( \IPS\Db\Exception $e ) { }
|
||||
catch( Exception $e ) { }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check ACP Permission
|
||||
*
|
||||
* @param string $key Permission Key
|
||||
* @param string|Application|null $app Application (NULL will default to current)
|
||||
* @param string|Module|null $module Module (NULL will default to current)
|
||||
* @param boolean $return Return boolean (true/false) instead of throwing an error
|
||||
* @return int|bool|null
|
||||
*/
|
||||
public function checkAcpPermission( string $key, string|Application $app=NULL, Module|string $module=NULL, bool $return=FALSE ): int|bool|null
|
||||
{
|
||||
$result = Bridge::i()->checkPlatformPermission( $key, $app, $module, $return );
|
||||
|
||||
if ( is_int( $result ) )
|
||||
{
|
||||
return $result;
|
||||
}
|
||||
|
||||
if ( !Member::loggedIn()->hasAcpRestriction( ( $app ?: $this->application ), ( $module ?: $this->module ), $key ) )
|
||||
{
|
||||
if ( $return )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Output::i()->error( 'no_module_permission', '2S107/2', 403, '' );
|
||||
}
|
||||
|
||||
if ( $return )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user