Version 5.0.0 beta 1

This commit is contained in:
Neo committed 2025-12-19 16:27:35 -08:00
1 parent 25ddeb65d6
commit 15c7beabc5
6736 files changed
+627902 -497943

No files matched your search

@@ -11,100 +11,116 @@
namespace IPS\core\modules\admin\overview;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use DateInterval;
use IPS\Application;
use IPS\DateTime;
use IPS\Dispatcher;
use IPS\Dispatcher\Controller;
use IPS\Http\Url;
use IPS\Member;
use IPS\Output;
use IPS\Request;
use IPS\Session;
use IPS\Settings;
use IPS\Theme;
use function defined;
use function in_array;
use function intval;
use function strpos;
use function substr;
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;
}
/**
* ACP Dashboard
*/
class _dashboard extends \IPS\Dispatcher\Controller
class dashboard extends Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
public static bool $csrfProtected = TRUE;
/**
* Show the ACP dashboard
*
* @return void
*/
protected function manage()
protected function manage() : void
{
\IPS\Dispatcher::i()->checkAcpPermission( 'view_dashboard' );
Dispatcher::i()->checkAcpPermission( 'view_dashboard' );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js('admin_dashboard.js', 'core') );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'system/dashboard.css', 'core', 'admin' ) );
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js('admin_dashboard.js', 'core') );
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'system/dashboard.css', 'core', 'admin' ) );
/* Figure out which blocks we should show */
$toShow = $this->current( TRUE );
/* Now grab dashboard extensions */
$blocks = array();
$info = array();
foreach ( \IPS\Application::allExtensions( 'core', 'Dashboard', TRUE, 'core' ) as $key => $extension )
foreach ( Application::allExtensions( 'core', 'Dashboard', TRUE, 'core' ) as $key => $extension )
{
if ( !method_exists( $extension, 'canView' ) or $extension->canView() )
if ( $extension->canView() )
{
$info[ $key ] = array(
'name' => \IPS\Member::loggedIn()->language()->addToStack('block_' . $key ),
'name' => Member::loggedIn()->language()->addToStack('block_' . $key ),
'key' => $key,
'app' => \substr( $key, 0, \strpos( $key, '_' ) )
'app' => substr( $key, 0, strpos( $key, '_' ) )
);
if( method_exists( $extension, 'getBlock' ) )
foreach( $toShow as $row )
{
foreach( $toShow as $row )
if( in_array( $key, $row ) )
{
if( \in_array( $key, $row ) )
{
$blocks[ $key ] = $extension->getBlock();
break;
}
$blocks[ $key ] = $extension->getBlock();
break;
}
}
}
}
/* Display */
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('dashboard');
\IPS\Output::i()->customHeader = \IPS\Theme::i()->getTemplate( 'dashboard' )->dashboardHeader( $info, $blocks );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'dashboard' )->dashboard( $toShow, $blocks, $info );
Output::i()->title = Member::loggedIn()->language()->addToStack('dashboard');
Output::i()->customHeader = Theme::i()->getTemplate( 'dashboard' )->dashboardHeader( $info, $blocks );
Output::i()->output = Theme::i()->getTemplate( 'dashboard' )->dashboard( $toShow, $blocks, $info );
}
/**
* Return a json-encoded array of the current blocks to show
*
* @param bool $return Flag to indicate if the array should be returned instead of output
* @return void
* @return array|null
*/
public function current( $return=FALSE )
public function current( bool $return=FALSE ) : ?array
{
if( \IPS\Settings::i()->acp_dashboard_blocks )
if( Settings::i()->acp_dashboard_blocks )
{
$blocks = json_decode( \IPS\Settings::i()->acp_dashboard_blocks, TRUE );
$blocks = json_decode( Settings::i()->acp_dashboard_blocks, TRUE );
}
else
{
$blocks = array();
}
$toShow = isset( $blocks[ \IPS\Member::loggedIn()->member_id ] ) ? $blocks[ \IPS\Member::loggedIn()->member_id ] : array();
$toShow = isset( $blocks[ Member::loggedIn()->member_id ] ) ? $blocks[ Member::loggedIn()->member_id ] : array();
if( !$toShow OR !isset( $toShow['main'] ) OR !isset( $toShow['side'] ) )
{
$toShow = array(
'main' => array( 'core_AdminNotes', 'core_Registrations', 'core_AwaitingValidation', 'core_BackgroundQueue' ),
'side' => array( 'core_OnlineAdmins', 'core_FailedLogins', 'core_OnlineUsers' ),
'main' => array( 'core_Registrations', 'core_BackgroundQueue' ),
'side' => array( 'core_AdminNotes', 'core_OnlineUsers' ),
'collapsed' => array( 'core_BackgroundQueue' ),
);
$blocks[ \IPS\Member::loggedIn()->member_id ] = $toShow;
$blocks[ Member::loggedIn()->member_id ] = $toShow;
\IPS\Settings::i()->changeValues( array( 'acp_dashboard_blocks' => json_encode( $blocks ) ) );
Settings::i()->changeValues( array( 'acp_dashboard_blocks' => json_encode( $blocks ) ) );
}
/* Upon initial upgrade to 4.3 the key won't exist, so apply to bg queue by default */
elseif( !array_key_exists( 'collapsed', $toShow ) )
@@ -117,7 +133,8 @@ class _dashboard extends \IPS\Dispatcher\Controller
return $toShow;
}
\IPS\Output::i()->output = json_encode( $toShow );
Output::i()->output = json_encode( $toShow );
return null;
}
/**
@@ -125,14 +142,14 @@ class _dashboard extends \IPS\Dispatcher\Controller
*
* @return void
*/
public function getBlock()
public function getBlock() : void
{
$output = '';
/* Loop through the dashboard extensions in the specified application */
foreach( \IPS\Application::load( \IPS\Request::i()->appKey )->extensions( 'core', 'Dashboard', 'core' ) as $key => $_extension )
foreach( Application::load( Request::i()->appKey )->extensions( 'core', 'Dashboard', 'core' ) as $key => $_extension )
{
if( \IPS\Request::i()->appKey . '_' . $key == \IPS\Request::i()->blockKey )
if( Request::i()->appKey . '_' . $key == Request::i()->blockKey )
{
if( method_exists( $_extension, 'getBlock' ) )
{
@@ -143,7 +160,7 @@ class _dashboard extends \IPS\Dispatcher\Controller
}
}
\IPS\Output::i()->output = $output;
Output::i()->output = $output;
}
/**
@@ -152,20 +169,20 @@ class _dashboard extends \IPS\Dispatcher\Controller
* @return void
* @note When submitted via AJAX, the array should be json-encoded
*/
public function update()
public function update() : void
{
\IPS\Session::i()->csrfCheck();
Session::i()->csrfCheck();
if( \IPS\Settings::i()->acp_dashboard_blocks )
if( Settings::i()->acp_dashboard_blocks )
{
$blocks = json_decode( \IPS\Settings::i()->acp_dashboard_blocks, TRUE );
$blocks = json_decode( Settings::i()->acp_dashboard_blocks, TRUE );
}
else
{
$blocks = array();
}
$saveBlocks = \IPS\Request::i()->blocks;
$saveBlocks = Request::i()->blocks;
foreach( array( 'main', 'side', 'collapsed' ) as $saveKey )
{
@@ -175,17 +192,17 @@ class _dashboard extends \IPS\Dispatcher\Controller
}
}
$blocks[ \IPS\Member::loggedIn()->member_id ] = $saveBlocks;
$blocks[ Member::loggedIn()->member_id ] = $saveBlocks;
\IPS\Settings::i()->changeValues( array( 'acp_dashboard_blocks' => json_encode( $blocks ) ) );
Settings::i()->changeValues( array( 'acp_dashboard_blocks' => json_encode( $blocks ) ) );
if( \IPS\Request::i()->isAjax() )
if( Request::i()->isAjax() )
{
\IPS\Output::i()->output = 1;
Output::i()->output = 1;
return;
}
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=overview&controller=dashboard" ), 'saved' );
Output::i()->redirect( Url::internal( "app=core&module=overview&controller=dashboard" ), 'saved' );
}
/**
@@ -193,17 +210,17 @@ class _dashboard extends \IPS\Dispatcher\Controller
*
* @return void
*/
public function switchSnooze()
public function switchSnooze() : void
{
$value = [ 'hits' => 1, 'lastClick' => time() ];
if ( isset( \IPS\Request::i()->cookie['acpLinkSnooze'] ) and $value = json_decode( \IPS\Request::i()->cookie['acpLinkSnooze'], true ) )
if ( isset( Request::i()->cookie['acpLinkSnooze'] ) and $value = json_decode( Request::i()->cookie['acpLinkSnooze'], true ) )
{
$value['hits'] = \intval( $value['hits'] ) + 1;
$value['hits'] = intval( $value['hits'] ) + 1;
$value['lastClick'] = time();
}
\IPS\Request::i()->setCookie( 'acpLinkSnooze', json_encode( $value ), ( new \IPS\DateTime )->add( new \DateInterval( 'P2Y' ) ) );
Request::i()->setCookie( 'acpLinkSnooze', json_encode( $value ), ( new DateTime )->add( new DateInterval( 'P2Y' ) ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=overview&controller=dashboard" ), 'The Switch to Cloud link won\'t show for a while' );
Output::i()->redirect( Url::internal( "app=core&module=overview&controller=dashboard" ), 'The Switch to Cloud link won\'t show for a while' );
}
}
File diff suppressed because it is too large. Load diff
@@ -12,50 +12,59 @@
namespace IPS\core\modules\admin\overview;
use IPS\core\Statistics\Chart;
use IPS\Dispatcher\Controller;
use IPS\Http\Url;
use IPS\Member;
use IPS\Output;
use IPS\Request;
use IPS\Theme;
use Throwable;
use function count;
use function defined;
/* 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' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* mycharts
*/
class _mycharts extends \IPS\Dispatcher\Controller
class mycharts extends Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
public static bool $csrfProtected = TRUE;
/**
* @brief Allow MySQL RW separation for efficiency
*/
public static $allowRWSeparation = TRUE;
public static bool $allowRWSeparation = TRUE;
/**
* Manage
*
* @return void
*/
protected function manage()
protected function manage() : void
{
$charts = [];
foreach( Chart::getChartsForMember( \IPS\Http\Url::internal( "app=core&module=overview&controller=mycharts" ), TRUE ) AS $id )
foreach( Chart::getChartsForMember( Url::internal( "app=core&module=overview&controller=mycharts" ), TRUE ) AS $id )
{
$charts[] = $id;
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_overview_mycharts');
if ( !\count( $charts ) )
Output::i()->title = Member::loggedIn()->language()->addToStack('menu__core_overview_mycharts');
if ( !count( $charts ) )
{
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('stats')->mychartsEmpty();
Output::i()->output = Theme::i()->getTemplate('stats')->mychartsEmpty();
}
else
{
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('stats')->mycharts( $charts );
Output::i()->output = Theme::i()->getTemplate('stats')->mycharts( $charts );
}
}
@@ -64,35 +73,35 @@ class _mycharts extends \IPS\Dispatcher\Controller
*
* @return void
*/
public function getChart()
public function getChart() : void
{
if ( !isset( \IPS\Request::i()->chartId ) )
if ( !isset( Request::i()->chartId ) )
{
if ( \IPS\Request::i()->isAjax() )
if ( Request::i()->isAjax() )
{
\IPS\Output::i()->output = '';
Output::i()->output = '';
}
else
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=overview&controller=mycharts" ) );
Output::i()->redirect( Url::internal( "app=core&module=overview&controller=mycharts" ) );
}
}
try
{
$chart = Chart::constructMemberChartFromData( \IPS\Request::i()->chartId, \IPS\Http\Url::internal( "app=core&module=overview&controller=mycharts&do=getChart&chartId=" . \IPS\Request::i()->chartId ) );
\IPS\Output::i()->output = (string) $chart;
$chart = Chart::constructMemberChartFromData( Request::i()->chartId, Url::internal( "app=core&module=overview&controller=mycharts&do=getChart&chartId=" . Request::i()->chartId ) );
Output::i()->output = (string) $chart;
}
catch( \Throwable )
catch( Throwable )
{
if ( \IPS\Request::i()->isAjax() )
if ( Request::i()->isAjax() )
{
\IPS\Output::i()->output = '';
Output::i()->output = '';
}
else
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=overview&controller=mycharts" ) );
Output::i()->redirect( Url::internal( "app=core&module=overview&controller=mycharts" ) );
}
}
}
@@ -11,46 +11,66 @@
namespace IPS\core\modules\admin\overview;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Application;
use IPS\core\AdminNotification;
use IPS\core\extensions\core\AdminNotifications\ConfigurationError;
use IPS\Data\Store;
use IPS\Db;
use IPS\Dispatcher\Controller;
use IPS\Helpers\Form\Matrix;
use IPS\Helpers\Form\Select;
use IPS\Helpers\Form\YesNo;
use IPS\Http\Url;
use IPS\Member;
use IPS\Output;
use IPS\Request;
use IPS\Session;
use IPS\Task;
use IPS\Theme;
use function count;
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;
}
/**
* ACP Notification Center
*/
class _notifications extends \IPS\Dispatcher\Controller
class notifications extends Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
public static bool $csrfProtected = TRUE;
/**
* Show notifications
*
* @return void
*/
protected function manage()
protected function manage() : void
{
$notifications = \IPS\core\AdminNotification::notifications();
$notifications = AdminNotification::notifications();
if ( \IPS\Request::i()->isAjax() and !isset( \IPS\Request::i()->_table ) )
if ( Request::i()->isAjax() and !isset( Request::i()->_table ) )
{
\IPS\Output::i()->json( array( 'data' => \IPS\Theme::i()->getTemplate('notifications')->popupList( $notifications ), 'count' => \count( $notifications ) ) );
Output::i()->json( array( 'data' => Theme::i()->getTemplate('notifications')->popupList( $notifications ), 'count' => count( $notifications ) ) );
}
else
{
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('acp_notifications');
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'system/notifications.css', 'core', 'admin' ) );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('notifications')->index( $notifications );
Output::i()->title = Member::loggedIn()->language()->addToStack('acp_notifications');
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'system/notifications.css', 'core', 'admin' ) );
Output::i()->output = Theme::i()->getTemplate('notifications')->index( $notifications );
\IPS\Output::i()->sidebar['actions'] = array(
Output::i()->sidebar['actions'] = array(
'settings' => array(
'title' => 'notification_options',
'icon' => 'cog',
'link' => \IPS\Http\Url::internal( 'app=core&module=overview&controller=notifications&do=settings' ),
'link' => Url::internal( 'app=core&module=overview&controller=notifications&do=settings' ),
),
);
}
@@ -61,19 +81,19 @@ class _notifications extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function dismiss()
protected function dismiss() : void
{
\IPS\Session::i()->csrfCheck();
Session::i()->csrfCheck();
\IPS\core\AdminNotification::dismissNotification( \IPS\Request::i()->id );
AdminNotification::dismissNotification( Request::i()->id );
if ( \IPS\Request::i()->isAjax() )
if ( Request::i()->isAjax() )
{
\IPS\Output::i()->json( array( 'status' => 'OK' ) );
Output::i()->json( array( 'status' => 'OK' ) );
}
else
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=overview&controller=notifications' ) );
Output::i()->redirect( Url::internal( 'app=core&module=overview&controller=notifications' ) );
}
}
@@ -82,11 +102,11 @@ class _notifications extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function settings()
protected function settings() : void
{
$preferences = iterator_to_array( \IPS\Db::i()->select( '*', 'core_acp_notifications_preferences', array( '`member`=?', \IPS\Member::loggedIn()->member_id ) )->setKeyField('type') );
$preferences = iterator_to_array( Db::i()->select( '*', 'core_acp_notifications_preferences', array( '`member`=?', Member::loggedIn()->member_id ) )->setKeyField('type') );
$matrix = new \IPS\Helpers\Form\Matrix;
$matrix = new Matrix;
$matrix->langPrefix = 'acp_notifications_';
$matrix->manageable = FALSE;
$matrix->columns = array(
@@ -96,7 +116,7 @@ class _notifications extends \IPS\Dispatcher\Controller
},
'view' => function( $key, $value, $data )
{
return new \IPS\Helpers\Form\YesNo( $key, $data['maybeOptional'] ? $value : TRUE, FALSE, array( 'disabled' => !$data['maybeOptional'] ) );
return new YesNo( $key, $data['maybeOptional'] ? $value : TRUE, FALSE, array( 'disabled' => !$data['maybeOptional'] ) );
},
'email' => function( $key, $value, $data )
{
@@ -115,15 +135,15 @@ class _notifications extends \IPS\Dispatcher\Controller
'once' => 'acp_notifications_email_yes',
);
return new \IPS\Helpers\Form\Select( $key, $value, FALSE, array( 'options' => $options, 'class' => 'ipsField_medium' ) );
return new Select( $key, $value, FALSE, array( 'options' => $options, 'class' => 'ipsField_medium' ) );
}
},
);
$rows = array();
foreach ( \IPS\Application::allExtensions( 'core', 'AdminNotifications', TRUE, NULL, NULL, FALSE ) as $ext )
foreach ( Application::allExtensions( 'core', 'AdminNotifications', TRUE, NULL, NULL, FALSE ) as $ext )
{
if ( $ext::permissionCheck( \IPS\Member::loggedIn() ) )
if ( $ext::permissionCheck( Member::loggedIn() ) )
{
$exploded = explode( '\\', $ext );
$key = "{$exploded[1]}_{$exploded[5]}";
@@ -131,11 +151,11 @@ class _notifications extends \IPS\Dispatcher\Controller
$rows[ $ext::$group ]['priority'] = $ext::$groupPriority;
$rows[ $ext::$group ]['rows'][ $ext ] = array(
'name' => $ext::settingsTitle(),
'view' => isset( $preferences[ $key ]['view'] ) ? $preferences[ $key ]['view'] : $ext::defaultValue(),
'email' => isset( $preferences[ $key ]['email'] ) ? $preferences[ $key ]['email'] : 'never',
'view' => $preferences[$key]['view'] ?? $ext::defaultValue(),
'email' => $preferences[$key]['email'] ?? 'never',
'maybeOptional' => $ext::mayBeOptional(),
'mayRecur' => $ext::mayRecur(),
'customEmail' => $ext::customEmailConfigurationSetting( "{$ext}[email]", isset( $preferences[ $key ]['email'] ) ? $preferences[ $key ]['email'] : NULL ),
'customEmail' => $ext::customEmailConfigurationSetting( "{$ext}[email]", $preferences[$key]['email'] ?? NULL ),
'priority' => $ext::$itemPriority,
);
}
@@ -147,7 +167,7 @@ class _notifications extends \IPS\Dispatcher\Controller
foreach ( $rows as $group => $data )
{
$matrix->rows[] = \IPS\Member::loggedIn()->language()->addToStack("acp_notification_group_{$group}");
$matrix->rows[] = Member::loggedIn()->language()->addToStack("acp_notification_group_{$group}");
uasort( $data['rows'], function( $a, $b ) {
return $a['priority'] - $b['priority'];
@@ -166,28 +186,29 @@ class _notifications extends \IPS\Dispatcher\Controller
$exploded = explode( '\\', $ext );
$key = "{$exploded[1]}_{$exploded[5]}";
$v = \IPS\Request::i()->$ext;
\IPS\Db::i()->insert( 'core_acp_notifications_preferences', array(
'member' => \IPS\Member::loggedIn()->member_id,
$v = Request::i()->$ext;
/* @var AdminNotification $ext */
Db::i()->insert( 'core_acp_notifications_preferences', array(
'member' => Member::loggedIn()->member_id,
'type' => $key,
'view' => $ext::mayBeOptional() ? $_values['view'] : TRUE,
'email' => isset( $v['email'] ) ? $v['email'] : 'never',
'email' => $v['email'] ?? 'never',
), TRUE );
}
if( isset( \IPS\Data\Store::i()->acpNotificationIds ) )
if( isset( Store::i()->acpNotificationIds ) )
{
$notificationCache = \IPS\Data\Store::i()->acpNotificationIds;
unset( $notificationCache[ \IPS\Member::loggedIn()->member_id ] );
\IPS\Data\Store::i()->acpNotificationIds = $notificationCache;
$notificationCache = Store::i()->acpNotificationIds;
unset( $notificationCache[ Member::loggedIn()->member_id ] );
Store::i()->acpNotificationIds = $notificationCache;
}
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=overview&controller=notifications' ) );
Output::i()->redirect( Url::internal( 'app=core&module=overview&controller=notifications' ) );
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('notification_options');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('forms')->blurb( 'acp_notifications_settings_blurb' ) . $matrix;
Output::i()->title = Member::loggedIn()->language()->addToStack('notification_options');
Output::i()->output = Theme::i()->getTemplate('forms')->blurb( 'acp_notifications_settings_blurb' ) . $matrix;
}
/**
@@ -195,11 +216,11 @@ class _notifications extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function configurationErrorChecks()
protected function configurationErrorChecks() : void
{
\IPS\Session::i()->csrfCheck();
\IPS\core\extensions\core\AdminNotifications\ConfigurationError::runChecksAndSendNotifications();
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=overview&controller=notifications' ) );
Session::i()->csrfCheck();
ConfigurationError::runChecksAndSendNotifications();
Output::i()->redirect( Url::internal( 'app=core&module=overview&controller=notifications' ) );
}
/**
@@ -207,15 +228,15 @@ class _notifications extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function removeOrigTables()
protected function removeOrigTables() : void
{
\IPS\Session::i()->csrfCheck();
Session::i()->csrfCheck();
$tables = \IPS\Db::i()->getTables( 'orig_' . \IPS\Db::i()->prefix );
\IPS\Task::queue( 'core', 'CleanupOrigTables', array( 'originalCount' => \count( $tables ) ), 5 );
$tables = Db::i()->getTables( 'orig_' . Db::i()->prefix );
Task::queue( 'core', 'CleanupOrigTables', array( 'originalCount' => count( $tables ) ), 5 );
\IPS\core\AdminNotification::remove( 'core', 'ConfigurationError', 'origTables' );
AdminNotification::remove( 'core', 'ConfigurationError', 'origTables' );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=overview&controller=notifications' ) );
Output::i()->redirect( Url::internal( 'app=core&module=overview&controller=notifications' ) );
}
}
@@ -12,35 +12,61 @@ namespace IPS\core\modules\admin\overview;
/* To prevent PHP errors (extending class does not exist) revealing path */
use IPS\core\modules\admin\moderation\spam;
use DateInterval;
use IPS\core\modules\admin\promotion\seo;
use IPS\core\modules\admin\settings\advanced;
use IPS\core\modules\admin\settings\terms;
use IPS\core\modules\admin\settings\webapp;
use IPS\Data\Store;
use IPS\DateTime;
use IPS\Dispatcher\Controller;
use IPS\File;
use IPS\GeoLocation;
use IPS\Helpers\Form;
use IPS\Helpers\Form\Address;
use IPS\Helpers\Form\Color;
use IPS\Helpers\Form\Email;
use IPS\Helpers\Form\Stack;
use IPS\Helpers\Form\Text;
use IPS\Helpers\Form\Upload;
use IPS\Http\Url;
use IPS\Image;
use IPS\Member;
use IPS\Output;
use IPS\Request;
use IPS\Session;
use IPS\Settings;
use IPS\Theme;
use function defined;
use const IPS\CIC;
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' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Initial installation onboarding
*/
class _onboard extends \IPS\Dispatcher\Controller
class onboard extends Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
public static bool $csrfProtected = TRUE;
/**
* Execute
*
* @return void
*/
public function execute()
public function execute() : void
{
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'dashboard/onboard.css', 'core', 'admin' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'admin_dashboard.js', 'core', 'admin' ) );
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'dashboard/onboard.css', 'core', 'admin' ) );
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'admin_dashboard.js', 'core', 'admin' ) );
return parent::execute();
parent::execute();
}
/**
@@ -48,15 +74,15 @@ class _onboard extends \IPS\Dispatcher\Controller
*
* @return void
*/
public function initial()
public function initial() : void
{
\IPS\Session::i()->csrfCheck();
\IPS\Settings::i()->changeValues( array( 'onboard_complete' => 1 ) ); // Set the flag that we've hit this page so that it doesn't keep trying to redirect us back
Session::i()->csrfCheck();
Settings::i()->changeValues( array( 'onboard_complete' => 1 ) ); // Set the flag that we've hit this page so that it doesn't keep trying to redirect us back
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('onboard_pagetitle');
\IPS\Output::i()->showTitle = FALSE;
\IPS\Output::i()->bypassCsrfKeyCheck = TRUE;
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate( 'dashboard' )->onboardWelcome();
Output::i()->title = Member::loggedIn()->language()->addToStack('onboard_pagetitle');
Output::i()->showTitle = FALSE;
Output::i()->bypassCsrfKeyCheck = TRUE;
Output::i()->output .= Theme::i()->getTemplate( 'dashboard' )->onboardWelcome();
}
/**
@@ -64,11 +90,11 @@ class _onboard extends \IPS\Dispatcher\Controller
*
* @return void
*/
public function dismiss()
public function dismiss() : void
{
\IPS\Session::i()->csrfCheck();
\IPS\Email::buildFromTemplate( 'core', 'onboard_reminder', array(), \IPS\Email::TYPE_TRANSACTIONAL )->send( \IPS\Member::loggedIn() );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=overview&controller=dashboard' ) );
Session::i()->csrfCheck();
\IPS\Email::buildFromTemplate( 'core', 'onboard_reminder', array(), \IPS\Email::TYPE_TRANSACTIONAL )->send( Member::loggedIn() );
Output::i()->redirect( Url::internal( 'app=core&module=overview&controller=dashboard' ) );
}
/**
@@ -76,36 +102,36 @@ class _onboard extends \IPS\Dispatcher\Controller
*
* @return void
*/
public function remind()
public function remind() : void
{
\IPS\Session::i()->csrfCheck();
\IPS\Settings::i()->changeValues( array( 'onboard_complete' => \IPS\DateTime::ts( time() )->add( new \DateInterval( 'PT20M' ) )->getTimestamp() ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=overview&controller=dashboard' ), 'onboard_will_remind' );
Session::i()->csrfCheck();
Settings::i()->changeValues( array( 'onboard_complete' => DateTime::ts( time() )->add( new DateInterval( 'PT20M' ) )->getTimestamp() ) );
Output::i()->redirect( Url::internal( 'app=core&module=overview&controller=dashboard' ), 'onboard_will_remind' );
}
/**
* Save the form values
*
* @param $values
* @param \IPS\Helpers\Form $form
* @param array $values
* @param Form $form
* @return void
*/
public function handleForm( $values, \IPS\Helpers\Form $form )
public function handleForm( array $values, Form $form ) : void
{
/* If we dismissed, do that but also send an email */
if( isset( \IPS\Request::i()->dismiss ) and \IPS\Request::i()->dismiss == 1 )
if( isset( Request::i()->dismiss ) and Request::i()->dismiss == 1 )
{
\IPS\Email::buildFromTemplate( 'core', 'onboard_reminder', array(), \IPS\Email::TYPE_TRANSACTIONAL )->send( \IPS\Member::loggedIn() );
\IPS\Email::buildFromTemplate( 'core', 'onboard_reminder', array(), \IPS\Email::TYPE_TRANSACTIONAL )->send( Member::loggedIn() );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=overview&controller=dashboard' ) );
Output::i()->redirect( Url::internal( 'app=core&module=overview&controller=dashboard' ) );
}
/* If we said to remind us later, set the flag and redirect */
if( isset( \IPS\Request::i()->remind ) and \IPS\Request::i()->remind == 1 )
if( isset( Request::i()->remind ) and Request::i()->remind == 1 )
{
\IPS\Settings::i()->changeValues( array( 'onboard_complete'=>\IPS\DateTime::ts( time() )->add( new \DateInterval( 'PT20M' ) )->getTimestamp() ) );
Settings::i()->changeValues( array( 'onboard_complete'=> DateTime::ts( time() )->add( new DateInterval( 'PT20M' ) )->getTimestamp() ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=overview&controller=dashboard' ), 'onboard_will_remind' );
Output::i()->redirect( Url::internal( 'app=core&module=overview&controller=dashboard' ), 'onboard_will_remind' );
}
array_walk( $values[ 'site_social_profiles' ], function( &$value ){
@@ -118,8 +144,8 @@ class _onboard extends \IPS\Dispatcher\Controller
$values[ 'site_address' ] = json_encode( $values[ 'site_address' ] );
$values[ 'icons_favicon' ] = (string) $values[ 'icons_favicon' ];
$values=\IPS\core\modules\admin\settings\webapp::processApplicationIcon( $values );
$values=\IPS\core\modules\admin\settings\terms::processForm( $values );
$values= webapp::processApplicationIcon( $values );
$values= terms::processForm( $values );
/* If a logo was uploaded, do stuff with it... */
if( isset( $values[ 'initial_logo' ] ) )
@@ -127,14 +153,14 @@ class _onboard extends \IPS\Dispatcher\Controller
$values[ 'email_logo' ]=(string)$values[ 'initial_logo' ];
/* Do this once for efficiency */
$image=\IPS\Image::create( $values[ 'initial_logo' ]->contents() );
$image= Image::create( $values[ 'initial_logo' ]->contents() );
$width=$image->width;
$height=$image->height;
unset( $image );
foreach( \IPS\Theme::themes() as $theme )
foreach( Theme::themes() as $theme )
{
$logoUrl=(string)\IPS\File::create( 'core_Theme', $values[ 'initial_logo' ]->originalFilename, $values[ 'initial_logo' ]->contents(), NULL, TRUE );
$logoUrl=(string)File::create( 'core_Theme', $values[ 'initial_logo' ]->originalFilename, $values[ 'initial_logo' ]->contents(), NULL, TRUE );
$theme->saveSet( array( 'logo'=>array( 'front'=>array( 'url'=>$logoUrl, 'width'=>$width, 'height'=>$height ) ) ) );
}
@@ -152,30 +178,30 @@ class _onboard extends \IPS\Dispatcher\Controller
/**
* Returns the onboarding form
*
* @return \IPS\Helpers\Form
* @return Form
*/
public function getForm(): \IPS\Helpers\Form
public function getForm(): Form
{
/* Create the form and add our message to it */
$form = new \IPS\Helpers\Form;
$form->class = "ipsForm_vertical";
$form = new Form;
$form->class = "ipsForm--vertical ipsForm--onboard";
$form->addTab( 'onboard_tab_identity' );
/* Super basic configuration options */
$form->add( new \IPS\Helpers\Form\Text( 'board_name', \IPS\Settings::i()->board_name, TRUE ) );
$form->add( new \IPS\Helpers\Form\Address( 'site_address', \IPS\GeoLocation::buildFromJson( \IPS\Settings::i()->site_address ), FALSE ) );
$form->add( new \IPS\Helpers\Form\Stack( 'site_social_profiles', \IPS\Settings::i()->site_social_profiles ? json_decode( \IPS\Settings::i()->site_social_profiles, true ) : array(), FALSE, array( 'stackFieldType' => '\IPS\core\Form\SocialProfiles', 'maxItems' => 50, 'key' => array( 'placeholder' => 'http://example.com', 'size' => 20 ) ) ) );
$form->add( new Text( 'board_name', Settings::i()->board_name, TRUE ) );
$form->add( new Address( 'site_address', GeoLocation::buildFromJson( Settings::i()->site_address ), FALSE ) );
$form->add( new Stack( 'site_social_profiles', Settings::i()->site_social_profiles ? json_decode( Settings::i()->site_social_profiles, true ) : array(), FALSE, array( 'stackFieldType' => '\IPS\core\Form\SocialProfiles', 'maxItems' => 50, 'key' => array( 'placeholder' => 'https://example.com', 'size' => 20 ) ) ) );
$form->add( new \IPS\Helpers\Form\Email( 'email_out', \IPS\Settings::i()->email_out, TRUE, array(), NULL, NULL, NULL, 'email_out' ) );
$form->add( new \IPS\Helpers\Form\Email( 'email_in', \IPS\Settings::i()->email_in, TRUE ) );
$form->add( new Email( 'email_out', Settings::i()->email_out, TRUE, array(), NULL, NULL, NULL, 'email_out' ) );
$form->add( new Email( 'email_in', Settings::i()->email_in, TRUE ) );
$form->addTab( 'onboard_tab_appearance' );
/* If we do not have an email logo and none of our themes have a logo, show the logo field */
$hasCustomLogo = FALSE;
foreach( \IPS\Theme::themes() as $theme )
foreach( Theme::themes() as $theme )
{
if( $theme->logo_front )
{
@@ -184,45 +210,41 @@ class _onboard extends \IPS\Dispatcher\Controller
}
}
if( !\IPS\Settings::i()->email_logo and !$hasCustomLogo )
if( !Settings::i()->email_logo and !$hasCustomLogo )
{
$form->add( new \IPS\Helpers\Form\Upload( 'initial_logo', NULL, FALSE, array( 'image' => true, 'storageExtension' => 'core_Theme' ) ) );
$form->add( new Upload( 'initial_logo', NULL, FALSE, array( 'image' => true, 'storageExtension' => 'core_Theme' ) ) );
}
/* Generic favicon - easy enough */
$form->add( new \IPS\Helpers\Form\Upload( 'icons_favicon', \IPS\Settings::i()->icons_favicon ? \IPS\File::get( 'core_Icons', \IPS\Settings::i()->icons_favicon ) : NULL, FALSE, array( 'obscure' => false, 'allowedFileTypes' => array( 'ico', 'png', 'gif', 'jpeg', 'jpg', 'jpe' ), 'storageExtension' => 'core_Icons' ) ) );
$form->add( new Upload( 'icons_favicon', Settings::i()->icons_favicon ? File::get( 'core_Icons', Settings::i()->icons_favicon ) : NULL, FALSE, array( 'obscure' => false, 'allowedFileTypes' => array( 'ico', 'png', 'gif', 'jpeg', 'jpg', 'jpe' ), 'storageExtension' => 'core_Icons' ) ) );
/* Homescreen icons - we accept one upload and create the images we need */
$homeScreen = json_decode( \IPS\Settings::i()->icons_homescreen, TRUE ) ?? array();
$form->add( new \IPS\Helpers\Form\Upload( 'icons_homescreen', ( isset( $homeScreen[ 'original' ] ) ) ? \IPS\File::get( 'core_Icons', $homeScreen[ 'original' ] ) : NULL, FALSE, array( 'image' => true, 'storageExtension' => 'core_Icons' ) ) );
$homeScreen = json_decode( Settings::i()->icons_homescreen, TRUE ) ?? array();
$form->add( new Upload( 'icons_homescreen', ( isset( $homeScreen[ 'original' ] ) ) ? File::get( 'core_Icons', $homeScreen[ 'original' ] ) : NULL, FALSE, array( 'image' => true, 'storageExtension' => 'core_Icons' ) ) );
$form->add( new \IPS\Helpers\Form\Color( 'email_color', \IPS\Settings::i()->email_color, TRUE ) );
$form->add( new Color( 'email_color', Settings::i()->email_color, TRUE ) );
/* These options are only for non-cic */
if( !\IPS\CIC )
if( !CIC )
{
/* Get the task setting */
$form->addTab( 'onboard_tab_tasks' );
\IPS\core\modules\admin\settings\advanced::taskSetting( $form );
advanced::taskSetting( $form );
/* Get the htaccess mod_Rewrite setting */
$form->addTab( 'onboard_tab_furls' );
\IPS\core\modules\admin\promotion\seo::htaccessSetting( $form );
/* Add captcha. */
$form->addTab( 'onboard_tab_spamprevention' );
spam::captchaForm( $form );
seo::htaccessSetting( $form );
}
/* Add the terms/privacy policy/etc. */
$form->addTab( 'onboard_tab_privacyterms' );
\IPS\core\modules\admin\settings\terms::buildForm( $form );
terms::buildForm( $form );
/* Add dismiss and remind me later buttons */
if( \IPS\Request::i()->initial )
if( Request::i()->initial )
{
$form->addButton( 'onboard_remind_later', 'submit', NULL, 'ipsButton ipsButton_secondary', array( 'name' => 'remind', 'value' => 1, 'csrfKey' => \IPS\Session::i()->csrfKey ) );
$form->addButton( 'dismiss', 'submit', NULL, 'ipsButton ipsButton_secondary', array( 'name' => 'dismiss', 'value' => 1, 'csrfKey' => \IPS\Session::i()->csrfKey ) );
$form->addButton( 'onboard_remind_later', 'submit', NULL, 'ipsButton ipsButton--secondary', array( 'name' => 'remind', 'value' => 1, 'csrfKey' => Session::i()->csrfKey ) );
$form->addButton( 'dismiss', 'submit', NULL, 'ipsButton ipsButton--secondary', array( 'name' => 'dismiss', 'value' => 1, 'csrfKey' => Session::i()->csrfKey ) );
}
return $form;
}
@@ -232,7 +254,7 @@ class _onboard extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function manage()
protected function manage() : void
{
$form = $this->getForm();
@@ -240,26 +262,20 @@ class _onboard extends \IPS\Dispatcher\Controller
{
$this->handleForm( $values, $form );
/* Clear create menu caches */
\IPS\Member::clearCreateMenu();
/* Clear guest page caches */
\IPS\Data\Cache::i()->clearAll();
/* Clear data stores */
unset(
\IPS\Data\Store::i()->manifest,
\IPS\Data\Store::i()->iebrowserconfig,
\IPS\Data\Store::i()->frontNavigation
Store::i()->manifest,
Store::i()->iebrowserconfig,
Store::i()->frontNavigation
);
\IPS\Session::i()->log( 'acplogs__onboard_settings' );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=overview&controller=onboard&do=next' ), 'saved' );
Session::i()->log( 'acplogs__onboard_settings' );
Output::i()->redirect( Url::internal( 'app=core&module=overview&controller=onboard&do=next' ), 'saved' );
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('onboard_pagetitle_basic');
\IPS\Output::i()->output .= $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'dashboard' ), 'onboardForm' ) );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'settings/general.css', 'core', 'admin' ) );
Output::i()->title = Member::loggedIn()->language()->addToStack('onboard_pagetitle_basic');
Output::i()->output .= $form->customTemplate( array( Theme::i()->getTemplate( 'dashboard' ), 'onboardForm' ) );
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'settings/general.css', 'core', 'admin' ) );
}
/**
@@ -267,14 +283,14 @@ class _onboard extends \IPS\Dispatcher\Controller
*
* @return void
*/
protected function next()
protected function next() : void
{
\IPS\Output::i()->showTitle = FALSE;
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('onboard_pagetitle');
Output::i()->showTitle = FALSE;
Output::i()->title = Member::loggedIn()->language()->addToStack('onboard_pagetitle');
/* Add Emoji - We can't put this directly in the template since those without utf8mb4 will have issues */
\IPS\Member::loggedIn()->language()->words['onboard_complete'] .= ' 🎉';
Member::loggedIn()->language()->words['onboard_complete'] .= ' 🎉';
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'dashboard' )->onboard();
Output::i()->output = Theme::i()->getTemplate( 'dashboard' )->onboard();
}
}