Version 4.5.0

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

No files matched your search

+114 -28
View File
@@ -11,7 +11,7 @@
namespace IPS\Dispatcher;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
@@ -99,9 +99,14 @@ class _Admin extends \IPS\Dispatcher\Standard
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'core.css', 'core', 'admin' ) );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'responsive.css', 'core', 'front' ) );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'responsive.css', 'core', 'admin' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js('admin_newFeatures.js', 'core') );
/* JS */
\IPS\Output::i()->globalControllers[] = 'core.admin.core.app';
if ( \IPS\Member::loggedIn()->getFeatureHighlights( FALSE ) )
{
\IPS\Output::i()->globalControllers[] = 'core.admin.core.newFeatures';
}
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'admin.js' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'jquery/jquery-ui.js', 'core', 'interface' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'jquery/jquery-touchpunch.js', 'core', 'interface' ) );
@@ -113,7 +118,7 @@ class _Admin extends \IPS\Dispatcher\Standard
{
/* These are just defaults in case we hit an immediate error, e.g. app or controller doesn't exist */
\IPS\Output::i()->sidebar['sidebar'] = \IPS\Theme::i()->getTemplate( 'global', 'core' )->sidebar( array(), 'core_overview' );
\IPS\Output::i()->sidebar['appmenu'] = \IPS\Theme::i()->getTemplate( 'global', 'core' )->appmenu( $menu, 'core' );
\IPS\Output::i()->sidebar['appmenu'] = \IPS\Theme::i()->getTemplate( 'global', 'core' )->appmenu( $menu, 'core', 'core_overview_dashboard' );
\IPS\Output::i()->sidebar['mobilenav'] = \IPS\Theme::i()->getTemplate( 'global', 'core' )->mobileNavigation( $menu, 'core' );
}
@@ -144,18 +149,18 @@ class _Admin extends \IPS\Dispatcher\Standard
/* If someone calls this from command line, while it wouldn't work, the key won't be set */
if( isset( $_SERVER['QUERY_STRING'] ) )
{
$url = $url->setQueryString( 'ref', base64_encode( preg_replace( '!adsess=((\w){32}|&)!', "", $_SERVER['QUERY_STRING'] ) ) );
$url = $url->setQueryString( 'ref', base64_encode( $_SERVER['QUERY_STRING'] ) );
}
}
else if( isset( $_SERVER['HTTP_REFERER'] ) )
{
$previous = preg_replace( "/^(.+?)\/\?/", "", $_SERVER['HTTP_REFERER'] );
$url = $url->setQueryString( 'ref', base64_encode( preg_replace( '!adsess=.*?(&|$)!', "", $previous ) ) );
$url = $url->setQueryString( 'ref', base64_encode( $previous ) );
}
\IPS\Output::i()->redirect( $url );
}
/* Init */
try
{
@@ -166,8 +171,14 @@ class _Admin extends \IPS\Dispatcher\Standard
\IPS\Output::i()->error( $e->getMessage(), '2S100/' . $e->getCode(), $e->getCode() === 4 ? 403 : 404, '' );
}
/* Unless there is a flag telling us we have specifically added CSRF checks, assume any AdminCP action which contains more than app/module/controller/id (i.e. anything with "do") requires CSRF-protection */
if ( !isset( $this->classname::$csrfProtected ) and array_diff( array_keys( \IPS\Request::i()->url()->queryString ), array( 'app', 'module', 'controller', 'id' ) ) )
{
\IPS\Session::i()->csrfCheck();
}
/* If we are in recovery mode, but not actually doing the recovery process, or logging in, then we need them to remove the constant */
if ( \IPS\RECOVERY_MODE === TRUE AND !in_array( $this->controller, array( 'recovery', 'login' ) ) )
if ( \IPS\RECOVERY_MODE === TRUE AND !\in_array( $this->controller, array( 'recovery', 'login' ) ) )
{
\IPS\Output::i()->error( 'recovery_mode_remove_constant', '1S107/3', 403, '' );
}
@@ -176,12 +187,14 @@ class _Admin extends \IPS\Dispatcher\Standard
if (
(
$this->module->key !== 'system' or
!in_array( $this->controller, array( 'login', 'language', 'theme', 'livesearch', 'editor', 'ajax' ) )
!\in_array( $this->controller, array( 'login', 'language', 'theme', 'livesearch', 'editor', 'ajax' ) )
) and
/* Every admin can view and manage his own acp notification */
( $this->module->key !== 'overview' or $this->controller !== 'notifications' ) and
(
$this->module->key !== 'members' or
$this->controller !== 'members' or
!in_array( \IPS\Request::i()->do, array( 'adminDetails', 'adminEmail', 'adminPassword' ) )
!\in_array( \IPS\Request::i()->do, array( 'adminDetails', 'adminEmail', 'adminPassword' ) )
) and
/* This is slightly hacky, but the upgrader was moved to the system module, however the ACP restriction is still set to overview.
To avoid unintentionally removing restrictions via an upgrade by moving the restriction, we reference the overview module for the restriction check instead */
@@ -191,6 +204,12 @@ class _Admin extends \IPS\Dispatcher\Standard
\IPS\Output::i()->error( 'no_module_permission', '2S107/1', 403, '' );
}
/* Support is not available for demos */
if ( \IPS\DEMO_MODE AND $this->module->application === 'core' AND $this->module->key === 'support' )
{
\IPS\Output::i()->error( 'demo_mode_function_blocked', '1S107/4', 403, '' );
}
/* ACP search keywords */
if ( \IPS\IN_DEV )
{
@@ -203,7 +222,7 @@ class _Admin extends \IPS\Dispatcher\Standard
$restrictions = array();
$file = \IPS\ROOT_PATH . "/applications/{$this->application->directory}/data/acprestrictions.json";
$file = $this->application->getApplicationPath() . "/data/acprestrictions.json";
if ( file_exists( $file ) )
{
$restrictions = json_decode( file_get_contents( $file ), TRUE );
@@ -239,9 +258,23 @@ class _Admin extends \IPS\Dispatcher\Standard
{
/* Work out what tab we're on */
$currentTab = NULL;
$currentItem = NULL;
foreach ( $this->application->acpMenu() as $moduleKey => $items )
{
if ( $moduleKey === $this->module->key )
/* If the module key does not match, we still need to inspect each item to see if a module_url that matches was specified */
$moduleUrlMatches = FALSE;
foreach( $items as $item )
{
if( isset( $item['module_url'] ) AND $item['module_url'] == $this->module->key )
{
$moduleUrlMatches = TRUE;
break;
}
}
if ( $moduleUrlMatches OR $moduleKey === $this->module->key )
{
foreach ( $items as $itemKey => $item )
{
@@ -249,28 +282,48 @@ class _Admin extends \IPS\Dispatcher\Standard
{
$currentTab = $item['tab'];
}
if ( $item['controller'] === $this->controller )
$additionalChecksPass = TRUE;
if( isset( $item['menu_checks'] ) AND \is_array( $item['menu_checks'] ) )
{
foreach( $item['menu_checks'] as $key => $value )
{
if( !isset( \IPS\Request::i()->$key ) OR \IPS\Request::i()->$key != $value )
{
$additionalChecksPass = FALSE;
break;
}
}
}
if ( $additionalChecksPass === TRUE and ( $item['controller'] === $this->controller or ( isset( $item['subcontrollers'] ) and \in_array( $this->controller, explode( ",", $item['subcontrollers'] ) ) ) ) )
{
break;
$controllerForKey = ( isset( $item['menu_controller'] ) ) ? $item['menu_controller'] : $item['controller'];
$currentItem = $this->application->directory . "_" . $moduleKey . "_" . $controllerForKey;
if( $currentTab != $item['tab'] )
{
$currentTab = $item['tab'];
}
}
}
}
}
if ( !$currentTab )
{
$currentTab = $this->application->directory;
}
/* Display */
if( $this->controller !== 'login' )
if ( isset( $menu['tabs'][ $currentTab ] ) )
{
if ( isset( $menu['tabs'][ $currentTab ] ) )
{
\IPS\Output::i()->sidebar['sidebar'] = \IPS\Theme::i()->getTemplate( 'global', 'core' )->sidebar( $menu['tabs'][ $currentTab ], $this->application->directory . '_' . $this->module->key );
}
\IPS\Output::i()->sidebar['appmenu'] = \IPS\Theme::i()->getTemplate( 'global', 'core' )->appmenu( $menu, $currentTab );
\IPS\Output::i()->sidebar['mobilenav'] = \IPS\Theme::i()->getTemplate( 'global', 'core' )->mobileNavigation( $menu, $currentTab );
\IPS\Output::i()->sidebar['sidebar'] = \IPS\Theme::i()->getTemplate( 'global', 'core' )->sidebar( $menu['tabs'][ $currentTab ], $this->application->directory . '_' . $this->module->key );
}
\IPS\Output::i()->sidebar['appmenu'] = \IPS\Theme::i()->getTemplate( 'global', 'core' )->appmenu( $menu, $currentTab, $currentItem );
\IPS\Output::i()->sidebar['mobilenav'] = \IPS\Theme::i()->getTemplate( 'global', 'core' )->mobileNavigation( $menu, $currentTab, $currentItem );
}
}
}
@@ -278,7 +331,7 @@ class _Admin extends \IPS\Dispatcher\Standard
/**
* Build Menu
*
* @param bool If TRUE, will rebuild
* @param bool $rebuild If TRUE, will rebuild
* @return array
*/
public function buildMenu( $rebuild=FALSE )
@@ -307,21 +360,54 @@ class _Admin extends \IPS\Dispatcher\Standard
{
foreach ( $items as $itemKey => $item )
{
if ( isset( $item['callback'] ) and !eval( $item['callback'] ) )
{
continue;
}
$moduleUrl = ( isset( $item['module_url'] ) ) ? $item['module_url'] : $moduleKey;
$moduleToCheck = ( isset( $item['restriction_module'] ) ) ? $item['restriction_module'] : $moduleKey;
if ( \IPS\Member::loggedIn()->hasAcpRestriction( $app, $moduleToCheck ) )
{
if ( !$item['restriction'] or \IPS\Member::loggedIn()->hasAcpRestriction( $app, $moduleToCheck, $item['restriction'] ) )
{
if( !$item['restriction'] )
{
$canAccess = TRUE;
}
else
{
if( mb_strpos( $item['restriction'], ',' ) )
{
$restrictions = explode( ',', $item['restriction'] );
}
else
{
$restrictions = array( $item['restriction'] );
}
$canAccess = FALSE;
foreach( $restrictions as $restrictionKey )
{
if( \IPS\Member::loggedIn()->hasAcpRestriction( $app, $moduleToCheck, $restrictionKey ) )
{
$canAccess = TRUE;
break;
}
}
}
if ( $canAccess )
{
$this->menu['tabs'][ $item['tab'] ][ "{$app->directory}_{$moduleKey}" ][ $itemKey ] = "app={$app->directory}&module={$moduleKey}&controller={$item['controller']}" . ( $item['do'] ? "&do={$item['do']}" : '' );
$this->menu['tabs'][ $item['tab'] ][ "{$app->directory}_{$moduleKey}" ][ $itemKey ] = "app={$app->directory}&module={$moduleUrl}&controller={$item['controller']}" . ( $item['do'] ? "&do={$item['do']}" : '' );
}
}
}
}
}
}
}
}
if ( $acpTabOrder !== NULL )
{
$_apps = array_keys( $acpTabOrder );
@@ -398,7 +484,7 @@ class _Admin extends \IPS\Dispatcher\Standard
}
catch( \UnderflowException $ex )
{
$this->acpTabOrder = array( 'core' => array(), 'community' => array(), 'members' => array(), 'nexus' => array(), 'cms' => array(), 'stats' => array(), 'customization' => array() );
$this->acpTabOrder = array( 'core' => array(), 'community' => array(), 'members' => array(), 'nexus' => array(), 'cms' => array(), 'stats' => array(), 'customization' => array(), 'marketplace' => array() );
}
\IPS\Request::i()->setCookie( 'acpTabs', json_encode( $this->acpTabOrder ) );