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

@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\activitystats;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _communityactivity extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -113,7 +118,7 @@ class _communityactivity extends \IPS\Dispatcher\Controller
foreach( $results as $date => $members )
{
$finalResults[ $date ] = array( 'time' => $date, \IPS\Member::loggedIn()->language()->get('activity') => count( $members ) );
$finalResults[ $date ] = array( 'time' => $date, \IPS\Member::loggedIn()->language()->get('activity') => \count( $members ) );
}
return $finalResults;
@@ -139,7 +144,7 @@ class _communityactivity extends \IPS\Dispatcher\Controller
break;
case 'weekly':
$timescale = '%Y-%u';
$timescale = '%x-%v';
break;
case 'monthly':
@@ -161,7 +166,7 @@ class _communityactivity extends \IPS\Dispatcher\Controller
/* First we need to get search index activity */
$fromUnixTime = "FROM_UNIXTIME( IFNULL( {$date}, 0 ) )";
if ( !$chart->timezoneError and \IPS\Member::loggedIn()->timezone and in_array( \IPS\Member::loggedIn()->timezone, \IPS\DateTime::getTimezoneIdentifiers() ) )
if ( !$chart->timezoneError and \IPS\Member::loggedIn()->timezone and \in_array( \IPS\Member::loggedIn()->timezone, \IPS\DateTime::getTimezoneIdentifiers() ) )
{
$fromUnixTime = "CONVERT_TZ( {$fromUnixTime}, @@session.time_zone, '" . \IPS\Db::i()->escape_string( \IPS\Member::loggedIn()->timezone ) . "' )";
}
@@ -0,0 +1,265 @@
<?php
/**
* @brief Email statistics
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 31 Oct 2018
*/
namespace IPS\core\modules\admin\activitystats;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Email statistics
*/
class _emailstats extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
* @return void
*/
public function execute()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'emailstats_manage' );
/* We can only view the stats if we have logging enabled */
if( \IPS\Settings::i()->prune_log_emailstats == 0 )
{
\IPS\Output::i()->error( 'emaillogs_not_enabled', '1C395/1', 403, '' );
}
parent::execute();
}
/**
* Show the charts
*
* @return void
*/
protected function manage()
{
$activeTab = $this->_getActiveTab();
/* Determine minimum date */
$minimumDate = NULL;
if( \IPS\Settings::i()->prune_log_emailstats > 0 )
{
$minimumDate = \IPS\DateTime::create()->sub( new \DateInterval( 'P' . \IPS\Settings::i()->prune_log_emailstats . 'D' ) );
}
/* We can't retrieve any stats prior to the new tracking being implemented */
try
{
$oldestLog = \IPS\Db::i()->select( 'MIN(time)', 'core_statistics', array( 'type=?', 'emails_sent' ) )->first();
if( !$minimumDate OR $oldestLog < $minimumDate->getTimestamp() )
{
$minimumDate = \IPS\DateTime::ts( $oldestLog );
}
}
catch( \UnderflowException $e )
{
/* We have nothing tracked, set minimum date to today */
$minimumDate = \IPS\DateTime::create();
}
$startDate = \IPS\DateTime::ts( time() - ( 60 * 60 * 24 * 30 ) );
/* If our start date is older than our minimum date, use that as the start date instead */
if ( $startDate->getTimestamp() < $minimumDate->getTimestamp() )
{
$startDate = $minimumDate;
}
$chart = new \IPS\Helpers\Chart\Callback(
\IPS\Http\Url::internal( 'app=core&module=activitystats&controller=emailstats&tab=' . $activeTab ),
array( $this, 'getResults' ),
'',
array(
'isStacked' => TRUE,
'backgroundColor' => '#ffffff',
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
'lineWidth' => 1,
'areaOpacity' => 0.4,
'height' => 450
),
'LineChart',
'daily',
array( 'start' => $startDate, 'end' => \IPS\DateTime::create() ),
'',
$minimumDate
);
$chart->availableTypes = array( 'LineChart', 'ColumnChart', 'BarChart' );
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_emailstats_' . $activeTab);
/* Force the notice that the chart is displayed in the server time zone */
$chart->timezoneError = TRUE;
$chart->hideTimezoneLink = TRUE;
foreach( $this->_getEmailTypes() as $series )
{
$chart->addSeries( $series, 'number' );
}
if ( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->output = (string) $chart;
}
else
{
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_activitystats_emailstats');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global', 'core' )->tabs( $this->_getAvailableTabs(), $activeTab, (string) $chart, \IPS\Http\Url::internal( "app=core&module=activitystats&controller=emailstats" ), 'tab', '', 'ipsPad' );
}
}
/**
* Fetch the results
*
* @param \IPS\Helpers\Chart\Callback $chart Chart object
* @return array
*/
public function getResults( $chart )
{
$activeTab = $this->_getActiveTab();
$where = array( array( "time>?", 0 ) );
switch( $activeTab )
{
case 'emails':
$where[] = array( 'type=?', 'emails_sent' );
break;
case 'clicks':
$where[] = array( 'type=?', 'email_clicks' );
break;
}
if ( $chart->start )
{
$where[] = array( "value_4>=?", $chart->start->format('Y-m-d') );
}
if ( $chart->end )
{
$where[] = array( "value_4<=?", $chart->end->format('Y-m-d') );
}
$results = array();
foreach( \IPS\Db::i()->select( '*', 'core_statistics', $where, 'value_4 ASC' ) as $row )
{
/* We need to use month/days NOT prefixed with '0' - i.e. 2019-2-12 instead of 2019-02-12 - to match the chart helper */
$_date = new \IPS\DateTime( $row['value_4'] );
switch( $chart->timescale )
{
case 'daily':
$_date = $_date->format( 'Y-n-j' );
break;
case 'weekly':
$_date = $_date->format( 'o-W' );
break;
case 'monthly':
$_date = $_date->format( 'Y-n' );
break;
}
if( !isset( $results[ $_date ] ) )
{
$results[ $_date ] = array( 'time' => $_date );
foreach( $this->_getEmailTypes() as $series )
{
$results[ $_date ][ $series ] = 0;
}
}
$lang = $row['extra_data'];
try
{
$lang = \IPS\Member::loggedIn()->language()->get( 'emailstats__' . $row['extra_data'] );
}
catch( \UnderflowException $e ){}
$results[ $_date ][ $lang ] = $row['value_1'];
}
return $results;
}
/**
* Get the active tab
*
* @return string
*/
protected function _getActiveTab()
{
return ( isset( \IPS\Request::i()->tab ) and array_key_exists( \IPS\Request::i()->tab, $this->_getAvailableTabs() ) ) ? \IPS\Request::i()->tab : 'emails';
}
/**
* Get the possible tabs
*
* @return array
*/
protected function _getAvailableTabs()
{
return array(
'emails' => 'stats_emailstats_emails',
'clicks' => 'stats_emailstats_clicks',
);
}
/**
* @brief Cached email types
*/
protected $_emailTypes = NULL;
/**
* Get all possible email types logged
*
* @return array
*/
protected function _getEmailTypes()
{
if( $this->_emailTypes === NULL )
{
$this->_emailTypes = array();
foreach( \IPS\Db::i()->select( 'extra_data', 'core_statistics', array( 'type=?', 'emails_sent' ), NULL, NULL, NULL, NULL, \IPS\Db::SELECT_DISTINCT ) as $series )
{
$lang = $series;
try
{
$lang = \IPS\Member::loggedIn()->language()->get( 'emailstats__' . $series );
}
catch( \UnderflowException $e ){}
$this->_emailTypes[] = $lang;
}
}
return array_unique( $this->_emailTypes );
}
}
Whitespace-only changes.
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\activitystats;
/* 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;
@@ -21,7 +21,12 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
* Keyword Tracking
*/
class _keywords extends \IPS\Dispatcher\Controller
{
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -60,7 +65,7 @@ class _keywords extends \IPS\Dispatcher\Controller
/* Draw a chart */
$options = json_decode( \IPS\Settings::i()->stats_keywords, true );
if( !is_array( $options ) )
if( !\is_array( $options ) )
{
$options = array();
}
@@ -88,7 +93,7 @@ class _keywords extends \IPS\Dispatcher\Controller
$chart->where = array( array( 'type=?', 'keyword' ) );
$chart->groupBy = 'value_4';
if ( is_array( $options ) )
if ( \is_array( $options ) )
{
foreach( $options as $k => $v )
{
@@ -96,10 +101,9 @@ class _keywords extends \IPS\Dispatcher\Controller
}
}
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('keyword_usage_chart');
$chart->availableTypes = array( 'AreaChart', 'ColumnChart', 'BarChart' );
\IPS\Output::i()->output = (string) $chart;
if( \IPS\Request::i()->noheader AND \IPS\Request::i()->isAjax() )
@@ -188,11 +192,11 @@ class _keywords extends \IPS\Dispatcher\Controller
{
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\Stack( 'stats_keywords', \IPS\Settings::i()->stats_keywords ? json_decode( \IPS\Settings::i()->stats_keywords, true ) : array(), FALSE, array( 'stackFieldType' => 'Text' ), NULL, NULL, NULL, 'stats_keywords' ) );
$form->add( new \IPS\Helpers\Form\Number( 'stats_keywords_prune', \IPS\Settings::i()->stats_keywords_prune, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), \IPS\Member::loggedIn()->language()->addToStack('days') ) );
$form->add( new \IPS\Helpers\Form\Interval( 'stats_keywords_prune', \IPS\Settings::i()->stats_keywords_prune, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL ) );
if ( $values = $form->values() )
{
$values['stats_keywords'] = json_encode( $values['stats_keywords'] );
$values['stats_keywords'] = json_encode( array_unique( $values['stats_keywords'] ) );
$form->saveAsSettings( $values );
\IPS\Session::i()->log( 'acplog__statskeywords_settings' );
@@ -0,0 +1,77 @@
<?php
/**
* @brief Content overview statistics
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 09 Jan 2020
*/
namespace IPS\core\modules\admin\activitystats;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Content overview statistics
*/
class _overview extends \IPS\core\modules\admin\stats\overview
{
/**
* @brief Template group to use to output
*/
protected $templateGroup = 'activitystats';
/**
* Create the general page layout, but we will load the individual cells via AJAX to ensure there are no performance concerns loading the page
*
* @return void
*/
protected function manage()
{
$formHtml = $this->form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'stats' ), 'filtersOverviewForm' ) );
$blocks = \IPS\Application::allExtensions( 'core', 'OverviewStatistics', TRUE, 'core', 'Registrations' );
$excludedApps = array();
if( isset( \IPS\Request::i()->cookie['overviewExcludedApps'] ) )
{
try {
$excludedApps = json_decode( \IPS\Request::i()->cookie['overviewExcludedApps'] );
if( !\is_array( $excludedApps ) )
{
$excludedApps = array();
}
}
catch( \Exception $e ){}
}
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'admin_stats.js', 'core' ) );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'system/statistics.css', 'core', 'admin' ) );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_stats_overview');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( $this->templateGroup )->overview( $formHtml, $blocks, $excludedApps );
}
/**
* Load the filter configuration form
*
* @return void
*/
protected function loadBlockForm()
{
$blocks = \IPS\Application::allExtensions( 'core', 'OverviewStatistics', TRUE, 'core', 'Registrations' );
if( !isset( $blocks[ \IPS\Request::i()->blockKey ] ) )
{
\IPS\Output::i()->error( 'stats_overview_block_not_found', '2C416/1', 404, '' );
}
\IPS\Output::i()->output = $blocks[ \IPS\Request::i()->blockKey ]->getBlockForm( \IPS\Request::i()->subBlockKey );
}
}
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\activitystats;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _reactions extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -89,6 +94,43 @@ class _reactions extends \IPS\Dispatcher\Controller
}
else
{
if( $activeTab == 'givers' OR $activeTab == 'getters' )
{
$start = NULL;
$end = NULL;
$defaults = array( 'start' => \IPS\DateTime::create()->setDate( date('Y'), date('m'), 1 ), 'end' => new \IPS\DateTime );
if( isset( \IPS\Request::i()->repDateStart ) AND isset( \IPS\Request::i()->repDateEnd ) )
{
$defaults = array( 'start' => \IPS\DateTime::ts( \IPS\Request::i()->repDateStart ), 'end' => \IPS\DateTime::ts( \IPS\Request::i()->repDateEnd ) );
}
$form = new \IPS\Helpers\Form( $activeTab, 'continue' );
$form->add( new \IPS\Helpers\Form\DateRange( 'date', $defaults, TRUE ) );
if( $values = $form->values() )
{
/* Determine start and end time */
$startTime = $values['date']['start']->getTimestamp();
$endTime = $values['date']['end']->getTimestamp();
$start = $values['date']['start']->html();
$end = $values['date']['end']->html();
}
else
{
/* Determine start and end time */
$startTime = $defaults['start']->getTimestamp();
$endTime = $defaults['end']->getTimestamp();
$start = $defaults['start']->html();
$end = $defaults['end']->html();
}
$where = array( array( 'rep_date BETWEEN ? AND ?', $startTime, $endTime ) );
}
/* Database table helpers don't support grouping, so we will manually fetch the results and then let the table helper display */
$contentWhere = array();
$idToRatingMap = array();
@@ -99,11 +141,11 @@ class _reactions extends \IPS\Dispatcher\Controller
}
elseif( $activeTab == 'givers' )
{
$query = \IPS\Db::i()->select( 'MAX(id) as id, SUM(rep_rating) as rating', 'core_reputation_index', NULL, 'rating DESC', array( 0, 20 ), array( 'member_id' ) );
$query = \IPS\Db::i()->select( 'MAX(id) as id, SUM(rep_rating) as rating', 'core_reputation_index', $where, 'rating DESC', array( 0, 20 ), array( 'member_id' ) );
}
else
{
$query = \IPS\Db::i()->select( 'MAX(id) as id, SUM(rep_rating) as rating', 'core_reputation_index', NULL, 'rating DESC', array( 0, 20 ), array( 'member_received' ) );
$query = \IPS\Db::i()->select( 'MAX(id) as id, SUM(rep_rating) as rating', 'core_reputation_index', $where, 'rating DESC', array( 0, 20 ), array( 'member_received' ) );
}
foreach( $query as $reputation )
@@ -129,6 +171,8 @@ class _reactions extends \IPS\Dispatcher\Controller
$column = ( $activeTab == 'givers' ) ? 'member_id' : 'member_received';
$chart->include = array( $column, 'rep_date', 'rating' );
$chart->mainColumn = $column;
$chart->baseUrl = $chart->baseUrl->setQueryString( array( 'repDateStart' => $startTime, 'repDateEnd' => $endTime, 'tab' => $activeTab ) );
}
$chart->noSort = array( 'member_id', 'type_id', 'rep_date', 'rating' );
@@ -165,7 +209,7 @@ class _reactions extends \IPS\Dispatcher\Controller
return \IPS\Theme::i()->getTemplate( 'activitystats' )->contentCell( $item );
}
catch( \Exception $e )
catch ( \Throwable $e )
{
return \IPS\Member::loggedIn()->language()->addToStack( 'unavailable' );
}
@@ -173,12 +217,18 @@ class _reactions extends \IPS\Dispatcher\Controller
);
}
if( $activeTab == 'givers' OR $activeTab == 'getters' )
{
$formHtml = $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'stats' ), 'filtersFormTemplate' ) );
$chart = \IPS\Theme::i()->getTemplate( 'activitystats', 'core' )->repWrapper( $formHtml, \count( $contentWhere ), (string) $chart );
}
if ( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->output = (string) $chart;
}
else
{
{
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_activitystats_reactions');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $activeTab, (string) $chart, \IPS\Http\Url::internal( "app=core&module=activitystats&controller=reactions" ), 'tab', '', 'ipsPad' );
}
@@ -0,0 +1,182 @@
<?php
/**
* @brief Search Statistics
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 20 Dec 2019
*/
namespace IPS\core\modules\admin\activitystats;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Search Statistics
*/
class _search extends \IPS\Dispatcher\Controller
{
/**
* @brief Default limit to number of graphed results
*/
protected $defaultLimit = 25;
/**
* Execute
*
* @return void
*/
public function execute()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'search_stats_manage' );
parent::execute();
}
/**
* View search statistics
*
* @return void
*/
protected function manage()
{
/* Show button to adjust settings */
\IPS\Output::i()->sidebar['actions']['settings'] = array(
'icon' => 'cog',
'primary' => TRUE,
'title' => 'manage_searchstats',
'link' => \IPS\Http\Url::internal( 'app=core&module=activitystats&controller=search&do=settings' ),
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('settings') )
);
/* Determine minimum date */
$minimumDate = NULL;
if( \IPS\Settings::i()->stats_search_prune )
{
$minimumDate = \IPS\DateTime::create()->sub( new \DateInterval( 'P' . \IPS\Settings::i()->stats_search_prune . 'D' ) );
}
$chart = new \IPS\Helpers\Chart\Database(
\IPS\Http\Url::internal( 'app=core&module=activitystats&controller=search' ),
'core_statistics',
'time',
'',
array(
'isStacked' => FALSE,
'backgroundColor' => '#ffffff',
'colors' => array( '#10967e', '#ea7963', '#de6470', '#6b9dde', '#b09be4', '#eec766', '#9fc973', '#e291bf', '#55c1a6', '#5fb9da' ),
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
'lineWidth' => 1,
'areaOpacity' => 0.4,
'limitSearch' => 'stats_search_term_menu',
),
'LineChart',
'daily',
array( 'start' => \IPS\DateTime::create()->sub( new \DateInterval( 'P90D' ) ), 'end' => \IPS\DateTime::ts( time() ) ),
array(),
'',
$minimumDate
);
$chart->where = array( array( 'type=?', 'search' ) );
$chart->groupBy = 'value_4';
foreach( $this->getTerms( $chart->searchTerm ) as $v )
{
$chart->addSeries( $v, 'number', 'COUNT(*)', FALSE );
}
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('search_stats_chart');
$chart->availableTypes = array( 'AreaChart', 'ColumnChart', 'BarChart' );
\IPS\Output::i()->output = (string) $chart;
if( \IPS\Request::i()->noheader AND \IPS\Request::i()->isAjax() )
{
return;
}
/* Create the table */
$table = new \IPS\Helpers\Table\Db( 'core_statistics', \IPS\Http\Url::internal( 'app=core&module=activitystats&controller=search' ), array( array( 'type=?', 'search' ) ) );
$table->langPrefix = 'searchstats_';
$table->quickSearch = 'value_4';
/* Columns we need */
$table->include = array( 'value_4', 'value_2', 'time' );
$table->mainColumn = 'value_4';
$table->sortBy = $table->sortBy ?: 'time';
$table->sortDirection = $table->sortDirection ?: 'desc';
/* Custom parsers */
$table->parsers = array(
'time' => function( $val, $row )
{
return \IPS\DateTime::ts( $val )->localeDate();
}
);
/* Display */
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_activitystats_search');
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate( 'global', 'core' )->block( 'title', (string) $table, TRUE, 'ipsPad ipsSpacer_top' );
}
/**
* @brief Cached top search terms
*/
protected $_topSearchTerms = array();
/**
* Get the top search terms
*
* @param string|null $term Term we searched for
* @return array
*/
public function getTerms( $term=NULL )
{
if( !isset( $this->_topSearchTerms[ $term ] ) )
{
$this->_topSearchTerms[ $term ] = array();
$where = array( array( 'type=?', 'search' ) );
if( $term !== NULL )
{
$where[] = \IPS\Db::i()->like( 'value_4', $term, TRUE, TRUE, TRUE );
}
foreach( \IPS\Db::i()->select( 'value_4, COUNT(*) as total', 'core_statistics', $where, 'total DESC', $this->defaultLimit, 'value_4' ) as $searchedValue )
{
$this->_topSearchTerms[ $term ][] = $searchedValue['value_4'];
}
}
return $this->_topSearchTerms[ $term ];
}
/**
* Prune Settings
*
* @return void
*/
protected function settings()
{
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\Interval( 'stats_search_prune', \IPS\Settings::i()->stats_search_prune, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL ) );
if ( $values = $form->values() )
{
$form->saveAsSettings( $values );
\IPS\Session::i()->log( 'acplog__statssearch_settings' );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=activitystats&controller=search' ), 'saved' );
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('settings');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('global')->block( 'settings', $form, FALSE );
}
}
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\activitystats;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _tags extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\applications;
/* 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;
@@ -22,9 +22,16 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _api extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Call
*
* @param string $method Method that was called
* @param mixed $args Arguments passed in
* @return void
*/
public function __call( $method, $args )
@@ -48,9 +55,21 @@ class _api extends \IPS\Dispatcher\Controller
{
$response = $url->request()->login( 'test', '' )->get()->decodeJson();
}
if ( isset( $response['errorMessage'] ) AND $response['errorMessage'] == 'IP_ADDRESS_BANNED' )
{
\IPS\Output::i()->error( 'api_blocked_self', '3C402/1', 500, '' );
}
if ( $response['errorMessage'] != 'INVALID_API_KEY' and $response['errorMessage'] != 'TOO_MANY_REQUESTS_WITH_BAD_KEY' )
{
/* Is it being blocked by a file? */
if( @\file_exists( \IPS\ROOT_PATH . '/api/core' ) )
{
\IPS\Output::i()->error( \IPS\Member::loggedIn()->language()->addToStack('api_blocked_file', NULL, array( 'sprintf' => array( \IPS\ROOT_PATH ) ) ), '3C402/2', 500, '' );
}
/* Plain not working, so bubble up. */
throw new \Exception;
}
}
@@ -97,6 +116,9 @@ class _api extends \IPS\Dispatcher\Controller
$class->execute();
$output = \IPS\Output::i()->output;
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'system/api.css', 'core', 'admin' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'admin_system.js', 'core', 'admin' ) );
if ( $method !== 'manage' or \IPS\Request::i()->isAjax() )
{
@@ -105,8 +127,6 @@ class _api extends \IPS\Dispatcher\Controller
\IPS\Output::i()->output = '';
/* Output */
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'system/api.css', 'core', 'admin' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'admin_system.js', 'core', 'admin' ) );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_applications_api');
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate( 'forms', 'core' )->blurb( 'rest_and_oauth_blurb' );
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $activeTab, $output, \IPS\Http\Url::internal( "app=core&module=applications&controller=api" ) );
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\applications;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _apiKeys extends \IPS\Node\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Node Class
*/
@@ -45,7 +50,6 @@ class _apiKeys extends \IPS\Node\Controller
public function execute()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'api_manage' );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'system/api.css', 'core', 'admin' ) );
parent::execute();
}
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\applications;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _apiLogs extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -221,7 +226,7 @@ class _apiLogs extends \IPS\Dispatcher\Controller
\IPS\Dispatcher::i()->checkAcpPermission( 'api_logs_settings' );
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\Number( 'api_log_prune', \IPS\Settings::i()->api_log_prune, FALSE, array(), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), \IPS\Member::loggedIn()->language()->addToStack('days') ) );
$form->add( new \IPS\Helpers\Form\Interval( 'api_log_prune', \IPS\Settings::i()->api_log_prune, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL ) );
if ( $values = $form->values() )
{
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\applications;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _apiReference extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -41,7 +46,7 @@ class _apiReference extends \IPS\Dispatcher\Controller
protected function manage()
{
/* If Opcache is enabled but opcache.save_comments is disabled, the API reference won't work */
if( extension_loaded( 'opcache' ) AND !ini_get( 'opcache.save_comments' ) )
if ( ( \extension_loaded( 'opcache' ) OR \extension_loaded( 'Zend Opcache' ) ) AND ini_get( 'opcache.save_comments' ) == 0 )
{
\IPS\Output::i()->error( 'api_opcache_disable', '4C331/1', 403, '' );
}
@@ -70,6 +75,25 @@ class _apiReference extends \IPS\Dispatcher\Controller
{
$params = array_merge( $params, $endpoints[ $selected ]['details']['apiparam'] );
}
foreach( $params as $data )
{
if ( mb_strpos( $data[0], '|' ) === FALSE AND !\in_array( $data[0], array( 'int', 'string', 'float', 'datetime', 'bool', 'object', 'array' ) ) )
{
if ( mb_substr( $data[0], 0, 1 ) == '[' )
{
if ( !\in_array( mb_substr( $data[0], 1, -1 ), array( 'int', 'string', 'float', 'datetime', 'bool', 'object', 'array' ) ) )
{
$additionalClassesToReference = array_merge( $additionalClassesToReference, $this->_getAdditionalClasses( mb_substr( $data[0], 1, -1 ) ) );
}
}
else
{
$additionalClassesToReference = array_merge( $additionalClassesToReference, $this->_getAdditionalClasses( $data[0] ) );
}
}
}
$exceptions = isset( $endpoints[ $selected ]['details']['throws'] ) ? $endpoints[ $selected ]['details']['throws'] : NULL;
if ( isset( $endpoints[ $selected ]['details']['apimemberonly'] ) )
{
@@ -97,11 +121,11 @@ class _apiReference extends \IPS\Dispatcher\Controller
{
foreach ( $endpoints[ $selected ]['details']['apiresponse'] as $data )
{
if ( mb_strpos( $data[0], '|' ) === FALSE AND !in_array( $data[0], array( 'int', 'string', 'float', 'datetime', 'bool', 'object', 'array' ) ) )
if ( mb_strpos( $data[0], '|' ) === FALSE AND !\in_array( $data[0], array( 'int', 'string', 'float', 'datetime', 'bool', 'object', 'array' ) ) )
{
if ( mb_substr( $data[0], 0, 1 ) == '[' )
{
if ( !in_array( mb_substr( $data[0], 1, -1 ), array( 'int', 'string', 'float', 'datetime', 'bool', 'object', 'array' ) ) )
if ( !\in_array( mb_substr( $data[0], 1, -1 ), array( 'int', 'string', 'float', 'datetime', 'bool', 'object', 'array' ) ) )
{
$additionalClassesToReference = array_merge( $additionalClassesToReference, $this->_getAdditionalClasses( mb_substr( $data[0], 1, -1 ) ) );
}
@@ -176,16 +200,16 @@ class _apiReference extends \IPS\Dispatcher\Controller
$decoded = \IPS\Api\Controller::decodeDocblock( $reflection->getDocComment() );
foreach ( $decoded['details']['apiresponse'] as $response )
{
if ( mb_strpos( $response[0], '|' ) === FALSE AND !in_array( $response[0], array( 'int', 'string', 'float', 'datetime', 'bool', 'object', 'array' ) ) )
if ( mb_strpos( $response[0], '|' ) === FALSE AND !\in_array( $response[0], array( 'int', 'string', 'float', 'datetime', 'bool', 'object', 'array' ) ) )
{
if ( mb_substr( $response[0], 0, 1 ) == '[' )
{
if ( !in_array( mb_substr( $response[0], 1, -1 ), $return ) and !in_array( mb_substr( $response[0], 1, -1 ), array( 'int', 'string', 'float', 'datetime', 'bool', 'object', 'array' ) ) )
if ( !\in_array( mb_substr( $response[0], 1, -1 ), $return ) and !\in_array( mb_substr( $response[0], 1, -1 ), array( 'int', 'string', 'float', 'datetime', 'bool', 'object', 'array' ) ) )
{
$return = array_merge( $return, $this->_getAdditionalClasses( mb_substr( $response[0], 1, -1 ) ) );
}
}
elseif ( !in_array( $response[0], $return ) )
elseif ( !\in_array( $response[0], $return ) )
{
$return = array_merge( $return, $this->_getAdditionalClasses( $response[0] ) );
}
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\applications;
/* 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;
@@ -22,10 +22,20 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _applications extends \IPS\Node\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Node Class
*/
protected $nodeClass = 'IPS\Application';
/**
* Description can contain HTML?
*/
public $_descriptionHtml = TRUE;
/**
* Execute
@@ -46,6 +56,10 @@ class _applications extends \IPS\Node\Controller
protected function manage()
{
/* Create the basic tree */
if ( !\IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('forms')->blurb( 'applications_blurb' );
}
parent::manage();
/* Find uninstalled applications */
@@ -54,7 +68,7 @@ class _applications extends \IPS\Node\Controller
foreach ( new \DirectoryIterator( \IPS\ROOT_PATH . "/applications/" ) as $file )
{
if ( $file->isDir() AND !in_array( $file->getFilename(), $installed ) AND !$file->isDot() )
if ( $file->isDir() AND !\in_array( $file->getFilename(), $installed ) AND !$file->isDot() )
{
if( file_exists( $file->getPathname() . '/data/application.json' ) )
{
@@ -68,8 +82,31 @@ class _applications extends \IPS\Node\Controller
}
}
}
if ( \IPS\CIC2 )
{
if ( \is_dir( \IPS\SITE_FILES_PATH . "/applications/" ) )
{
foreach( new \DirectoryIterator( \IPS\SITE_FILES_PATH . "/applications/" ) AS $file )
{
if ( $file->isDir() AND !\in_array( $file->getFilename(), $installed ) AND !$file->isDot() )
{
if ( file_exists( $file->getPathname() . '/data/application.json' ) )
{
$application = json_decode( file_get_contents( $file->getPathname() . '/data/application.json' ), TRUE );
$uninstalled[ $file->getFilename() ] = array(
'title' => $application['application_title'],
'author' => $application['app_author'],
'website' => $application['app_website'],
);
}
}
}
}
}
if( count( $uninstalled ) AND empty( \IPS\Request::i()->root ) )
if( \count( $uninstalled ) AND empty( \IPS\Request::i()->root ) )
{
$baseUrl = $this->url;
$tree = new \IPS\Helpers\Tree\Tree(
@@ -79,7 +116,7 @@ class _applications extends \IPS\Node\Controller
{
$rows = array();
if( !empty($uninstalled) AND is_array($uninstalled) )
if( !empty($uninstalled) AND \is_array($uninstalled) )
{
foreach ( $uninstalled as $k => $app )
{
@@ -87,7 +124,7 @@ class _applications extends \IPS\Node\Controller
'add' => array(
'icon' => 'plus-circle',
'title' => 'install',
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=applications&appKey={$k}&do=install" ),
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=applications&appKey={$k}&do=install" )->csrf(),
)
) );
}
@@ -100,7 +137,7 @@ class _applications extends \IPS\Node\Controller
'add' => array(
'icon' => 'plus-circle',
'title' => 'install',
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=applications&appKey={$key}&do=install" ),
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=applications&appKey={$key}&do=install" )->csrf(),
)
), '', NULL, NULL, $root );
},
@@ -135,7 +172,7 @@ class _applications extends \IPS\Node\Controller
}
}
}
if( count( $legacyApps ) AND empty( \IPS\Request::i()->root ) )
if( \count( $legacyApps ) AND empty( \IPS\Request::i()->root ) )
{
$baseUrl = $this->url;
$legacyTree = new \IPS\Helpers\Tree\Tree(
@@ -144,7 +181,7 @@ class _applications extends \IPS\Node\Controller
function() use ( $legacyApps, $baseUrl )
{
$rows = array();
if( !empty( $legacyApps ) AND is_array( $legacyApps ) )
if( !empty( $legacyApps ) AND \is_array( $legacyApps ) )
{
foreach ( $legacyApps as $k => $app )
{
@@ -162,7 +199,7 @@ class _applications extends \IPS\Node\Controller
$buttons['delete'] = array(
'icon' => 'times-circle',
'title' => 'uninstall',
'link' => $baseUrl->setQueryString( array( 'do' => 'delete', 'id' => $k ) ),
'link' => $baseUrl->setQueryString( array( 'do' => 'delete', 'id' => $k ) )->csrf(),
'data' => array( 'delete' => '' ),
'hotkey'=> 'd'
);
@@ -189,7 +226,7 @@ class _applications extends \IPS\Node\Controller
$buttons['delete'] = array(
'icon' => 'times-circle',
'title' => 'uninstall',
'link' => $baseUrl->setQueryString( array( 'do' => 'delete', 'id' => $key ) ),
'link' => $baseUrl->setQueryString( array( 'do' => 'delete', 'id' => $key ) )->csrf(),
'data' => array( 'delete' => '' ),
'hotkey'=> 'd'
);
@@ -214,7 +251,7 @@ class _applications extends \IPS\Node\Controller
/* Check for updates button */
\IPS\Output::i()->sidebar['actions']['settings'] = array(
'icon' => 'refresh',
'link' => \IPS\Http\Url::internal( 'app=core&module=applications&controller=applications&do=updateCheck' ),
'link' => \IPS\Http\Url::internal( 'app=core&module=applications&controller=applications&do=updateCheck' )->csrf(),
'title' => 'check_for_updates',
);
if ( \IPS\IN_DEV )
@@ -235,6 +272,8 @@ class _applications extends \IPS\Node\Controller
*/
public function updateCheck()
{
\IPS\Session::i()->csrfCheck();
$task = \IPS\Task::constructFromData( \IPS\Db::i()->select( '*', 'core_tasks', array( 'app=? AND `key`=?', 'core', 'updatecheck' ) )->first() );
$task->type = 'manual';
$task->run();
@@ -248,8 +287,17 @@ class _applications extends \IPS\Node\Controller
*/
public function setAsDefault()
{
\IPS\Session::i()->csrfCheck();
$application = \IPS\Application::load( \IPS\Request::i()->appKey );
if ( !\count( $application->modules( 'front' ) ) )
{
\IPS\Output::i()->error( 'app_cannot_be_default', '2C133/W', 403, '' );
}
$application->setAsDefault();
\IPS\Session::i()->log( 'acplog__application_set_default', array( $application->titleForLog() => FALSE ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=applications&controller=applications" ), 'saved' );
}
@@ -261,6 +309,8 @@ class _applications extends \IPS\Node\Controller
*/
public function setDefaultModule()
{
\IPS\Session::i()->csrfCheck();
try
{
$module = \IPS\Application\Module::load( \IPS\Request::i()->id );
@@ -274,26 +324,6 @@ class _applications extends \IPS\Node\Controller
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=applications&controller=applications&root={$module->application}" ), 'saved' );
}
/**
* Get Root Buttons
*
* @return array
*/
public function _getRootButtons()
{
$buttons = parent::_getRootButtons();
\IPS\Output::i()->sidebar['actions']['install'] = array(
'primary' => true,
'icon' => 'upload',
'title' => 'install',
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=applications&do=upload" ),
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('install') )
);
return $buttons;
}
/**
* Get Child Rows
*
@@ -327,6 +357,7 @@ class _applications extends \IPS\Node\Controller
}
return $rows;
}
/**
* Get Single Row
*
@@ -385,7 +416,9 @@ class _applications extends \IPS\Node\Controller
( ( $node instanceof \IPS\Node\Model ) ? $node->_badge : $this->_getRowBadge( $node ) ),
TRUE,
$this->_descriptionHtml,
$node->canAdd()
$node->canAdd(),
TRUE,
\IPS\Theme::i()->getTemplate('applications')->appRowAdditional( $node )
);
}
@@ -453,7 +486,7 @@ class _applications extends \IPS\Node\Controller
/* Clear guest page caches */
\IPS\Data\Cache::i()->clearAll();
$this->logToggleAndRedirect( $node );
$this->_logToggleAndRedirect( $node );
}
/* Display */
@@ -478,7 +511,7 @@ class _applications extends \IPS\Node\Controller
foreach ( array_reverse( $json, TRUE ) as $long => $human )
{
$exploded = explode( '.', $human );
$defaults['human'] = "{$exploded[0]}.{$exploded[1]}." . ( intval( $exploded[2] ) + 1 );
$defaults['human'] = "{$exploded[0]}.{$exploded[1]}." . ( \intval( $exploded[2] ) + 1 );
$defaults['long'] = $long + 1;
break;
}
@@ -529,6 +562,7 @@ class _applications extends \IPS\Node\Controller
}
$form = $this->_buildForm( 'core' );
$form->add( new \IPS\Helpers\Form\YesNo( 'developer_build_submit', (bool) \IPS\IPS_PASSWORD ) );
if ( $values = $form->values() )
{
@@ -548,8 +582,15 @@ class _applications extends \IPS\Node\Controller
\IPS\Output::i()->error( $e->getMessage(), '' );
}
}
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=applications&controller=applications' ), 'application_now_built' );
if ( $values['developer_build_submit'] )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=applications&controller=applications&do=submit' )->csrf() );
}
else
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=applications&controller=applications' ), 'application_now_built' );
}
}
\IPS\Output::i()->output = $form;
@@ -655,14 +696,24 @@ class _applications extends \IPS\Node\Controller
*/
public function upgrade()
{
\IPS\Session::i()->csrfCheck();
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('upgrading_application');
unset( \IPS\Data\Store::i()->syncCompleted );
$url = \IPS\Http\Url::internal( "app=core&module=applications&controller=applications&do=upgrade&appKey=" . \IPS\Request::i()->appKey )->csrf();
if ( isset( \IPS\Request::i()->marketplace ) )
{
$url = $url->setQueryString( 'marketplace', \IPS\Request::i()->marketplace );
}
\IPS\Output::i()->output = new \IPS\Helpers\MultipleRedirect(
\IPS\Http\Url::internal( "app=core&module=applications&controller=applications&do=upgrade&appKey=" . \IPS\Request::i()->appKey ),
$url,
function( $data )
{
/* On first cycle return data */
if ( !is_array( $data ) )
if ( !\is_array( $data ) )
{
/* Does this application exist in the database? */
try
@@ -717,9 +768,9 @@ class _applications extends \IPS\Node\Controller
$allVersions = \IPS\Application::load( $data['key'] )->getAllVersions();
$longVersions = array_keys( $allVersions );
$humanVersions = array_values( $allVersions );
$lastRan = ( isset( $data['extra']['_last'] ) ) ? intval( $data['extra']['_last'] ) : $currentVersion;
$lastRan = ( isset( $data['extra']['_last'] ) ) ? \intval( $data['extra']['_last'] ) : $currentVersion;
if( count($allVersions) )
if( \count($allVersions) )
{
$latestLVersion = array_pop( $longVersions );
$latestHVersion = array_pop( $humanVersions );
@@ -731,7 +782,7 @@ class _applications extends \IPS\Node\Controller
$upgradeSteps = \IPS\Application::load( $data['key'] )->getUpgradeSteps( $lastRan );
/* Did we find any? */
if( count( $upgradeSteps ) )
if( \count( $upgradeSteps ) )
{
/* Re-initialize $extra variable */
$extra = array();
@@ -739,7 +790,7 @@ class _applications extends \IPS\Node\Controller
/* Store a count of all the upgrade steps for later use */
if( !$lastRan )
{
$extra['_totalSteps'] = count($upgradeSteps);
$extra['_totalSteps'] = \count($upgradeSteps);
$data['extra']['_totalSteps'] = $extra['_totalSteps'];
}
else
@@ -760,7 +811,21 @@ class _applications extends \IPS\Node\Controller
$extra['_last'] = $_next;
/* What step in the upgrader file are we on? */
$upgradeStep = ( isset($data['extra']['_upgradeStep']) ) ? intval($data['extra']['_upgradeStep']) : 1;
$upgradeStep = ( isset($data['extra']['_upgradeStep']) ) ? \intval($data['extra']['_upgradeStep']) : 1;
/* Delete removed language strings */
if( file_exists( \IPS\ROOT_PATH . "/applications/{$data['key']}/setup/upg_{$_next}/lang.json" ) )
{
$langChanges = json_decode( \file_get_contents( \IPS\ROOT_PATH . "/applications/{$data['key']}/setup/upg_{$_next}/lang.json" ), TRUE );
if ( isset( $langChanges['normal']['removed'] ) and $langChanges['normal']['removed'] )
{
\IPS\Db::i()->delete( 'core_sys_lang_words', array( array( 'word_app=?', $data['key'] ), array( 'word_js=0' ), array( \IPS\Db::i()->in( 'word_key', $langChanges['normal']['removed'] ) ) ) );
}
if ( isset( $langChanges['js']['removed'] ) and $langChanges['js']['removed'] )
{
\IPS\Db::i()->delete( 'core_sys_lang_words', array( array( 'word_app=?', $data['key'] ), array( 'word_js=1' ), array( \IPS\Db::i()->in( 'word_key', $langChanges['js']['removed'] ) ) ) );
}
}
/* If we haven't run the raw queries yet, do so */
if( $upgradeStep == 1 AND !isset( $data['extra']['_upgradeData'] ) )
@@ -813,7 +878,7 @@ class _applications extends \IPS\Node\Controller
$laststep = 'start';
$language = $language ?: \IPS\Member::loggedIn()->language()->addToStack('appupdate_databasechanges', FALSE, array( 'sprintf' => $allVersions[ $_next ] ) );
$progress = round( ( 30 * ( $data['extra']['_totalSteps'] - count($upgradeSteps) ) ) / ( $data['extra']['_totalSteps'] ?: 1 ) );
$progress = round( ( 30 * ( $data['extra']['_totalSteps'] - \count($upgradeSteps) ) ) / ( $data['extra']['_totalSteps'] ?: 1 ) );
}
else
{
@@ -845,12 +910,34 @@ class _applications extends \IPS\Node\Controller
/* Insert email templates */
\IPS\Application::load( $data['key'] )->installEmailTemplates();
$laststep = 'emails';
$hasCmsTemplates = file_exists( \IPS\ROOT_PATH . '/applications/' . \IPS\Application::load( $data['key'] )->directory . '/data/cmsTemplates.xml' );
$laststep = $hasCmsTemplates ? 'emails' : 'cmstemplates';
$language = \IPS\Member::loggedIn()->language()->addToStack('appinstall_emails');
$progress = 75;
break;
case 'emails':
/* Get Templates and Insert */
$_SESSION['cmsConflictKey'] = '';
try
{
$result = \IPS\cms\Templates::importTemplatesFromXml( \IPS\ROOT_PATH . '/applications/' . \IPS\Application::load( $data['key'] )->directory . '/data/cmsTemplates.xml' );
if( $result instanceof \IPS\Http\Url\Internal )
{
$_SESSION['cmsConflictKey'] = $result->setQueryString( [ 'app' => \IPS\Application::load( $data['key'] )->directory, 'lang' => 'updated' ] );
}
}
catch( \Throwable $e )
{
\IPS\Log::log( $e, 'cms_template_app_install' );
}
$laststep = 'cmstemplates';
$language = \IPS\Member::loggedIn()->language()->addToStack('appinstall_cmstemplates');
$progress = 95;
break;
case 'cmstemplates':
/* Insert skin templates */
\IPS\Application::load( $data['key'] )->installSkins( TRUE );
\IPS\Application::load( $data['key'] )->installJavascript();
@@ -864,6 +951,11 @@ class _applications extends \IPS\Node\Controller
/* Return null to indicate we are done */
if( $laststep === NULL )
{
if( \IPS\Request::i()->marketplace AND !\IPS\Application::load( $data['key'] )->marketplace_id )
{
\IPS\Application::load( $data['key'] )->marketplace_id = (int) \IPS\Request::i()->marketplace;
\IPS\Application::load( $data['key'] )->save();
}
\IPS\Session::i()->log( 'acplog__application_updated', array( \IPS\Application::load( $data['key'] )->titleForLog() => FALSE, \IPS\Application::load( $data['key'] )->version => TRUE ) );
return NULL;
@@ -877,12 +969,30 @@ class _applications extends \IPS\Node\Controller
{
/* IPS Cloud Sync */
\IPS\IPS::resyncIPSCloud('Upgraded application in ACP');
$application = \IPS\Application::load( \IPS\Request::i()->appKey );
/* Invalidate disk templates */
\IPS\Theme::resetAllCacheKeys();
/* And redirect back to the overview screen */
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=applications&controller=applications' ), 'application_now_updated' );
/* If CMS Template install had conflicts, solve those now */
if( !empty( $_SESSION['cmsConflictKey'] ) )
{
if( $application->marketplace_id )
{
$_SESSION['cmsConflictKey'] = $_SESSION['cmsConflictKey']->setQueryString( 'marketplace', $application->marketplace_id );
}
\IPS\Output::i()->redirect( $_SESSION['cmsConflictKey'] );
}
/* And redirect back to the overview screen (or marketplace if it was installed from there) */
if( $application->marketplace_id )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=marketplace&controller=marketplace&do=viewFile&id=' . $application->marketplace_id ), 'application_now_updated' );
}
else
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=applications&controller=applications' ), 'application_now_updated' );
}
}
);
}
@@ -895,14 +1005,24 @@ class _applications extends \IPS\Node\Controller
*/
public function install()
{
\IPS\Session::i()->csrfCheck();
unset( \IPS\Data\Store::i()->syncCompleted );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('installing_application');
$url = \IPS\Http\Url::internal( "app=core&module=applications&controller=applications&do=install&appKey=" . \IPS\Request::i()->appKey )->csrf();
if ( isset( \IPS\Request::i()->marketplace ) )
{
$url = $url->setQueryString( 'marketplace', \IPS\Request::i()->marketplace );
}
\IPS\Output::i()->output = new \IPS\Helpers\MultipleRedirect(
\IPS\Http\Url::internal( "app=core&module=applications&controller=applications&do=install&appKey=" . \IPS\Request::i()->appKey ),
$url,
function( $data )
{
/* On first cycle return data */
if ( !is_array( $data ) )
if ( !\is_array( $data ) )
{
/* Does this application exist in the database? */
try
@@ -915,11 +1035,11 @@ class _applications extends \IPS\Node\Controller
}
}
catch( \OutOfRangeException $e ){} // We don't need to do anything if it hasn't loaded - that's good
/* Get the application data to insert the application record */
if( file_exists( \IPS\ROOT_PATH . '/applications/' . \IPS\Request::i()->appKey . '/data/application.json' ) )
if( file_exists( \IPS\Application::getRootPath( \IPS\Request::i()->appKey ) . '/applications/' . \IPS\Request::i()->appKey . '/data/application.json' ) )
{
$application = json_decode( file_get_contents( \IPS\ROOT_PATH . '/applications/' . \IPS\Request::i()->appKey . '/data/application.json' ), TRUE );
$application = json_decode( file_get_contents( \IPS\Application::getRootPath( \IPS\Request::i()->appKey ) . '/applications/' . \IPS\Request::i()->appKey . '/data/application.json' ), TRUE );
if( !$application['app_directory'] )
{
@@ -975,7 +1095,7 @@ class _applications extends \IPS\Node\Controller
case 'basics':
/* Insert lang data */
$offset = ( isset( $data['offset'] ) ) ? intval( $data['offset'] ) : 0;
$offset = ( isset( $data['offset'] ) ) ? \intval( $data['offset'] ) : 0;
$inserted = \IPS\Application::load( $data['key'] )->installLanguages( $offset, 250 );
@@ -1014,7 +1134,7 @@ class _applications extends \IPS\Node\Controller
case 'extensions':
/* Insert skin templates */
$offset = ( isset( $data['offset'] ) ) ? intval( $data['offset'] ) : 0;
$offset = ( isset( $data['offset'] ) ) ? \intval( $data['offset'] ) : 0;
if( !$offset )
{
@@ -1042,11 +1162,34 @@ class _applications extends \IPS\Node\Controller
case 'skins':
/* Insert skin templates */
\IPS\Application::load( $data['key'] )->installJavascript();
$hasCmsTemplates = file_exists( \IPS\Application::getRootPath( $data['key'] ) . '/applications/' . $data['key'] . '/data/cmsTemplates.xml' );
$laststep = $hasCmsTemplates ? 'cmstemplates' : 'javascript';
$language = $hasCmsTemplates ? \IPS\Member::loggedIn()->language()->addToStack('appinstall_cmstemplates') : \IPS\Member::loggedIn()->language()->addToStack('appinstall_javascript');
$progress = 87.5;
break;
case 'cmstemplates':
/* Get Templates and Insert */
$_SESSION['cmsConflictKey'] = '';
try
{
$result = \IPS\cms\Templates::importTemplatesFromXml( \IPS\Application::getRootPath( $data['key'] ) . '/applications/' . $data['key'] . '/data/cmsTemplates.xml' );
if( $result instanceof \IPS\Http\Url\Internal )
{
$_SESSION['cmsConflictKey'] = $result->setQueryString( [ 'app' => $data['key'], 'lang' => 'installed' ] );
}
}
catch( \Throwable $e )
{
\IPS\Log::log( $e, 'cms_template_app_install' );
}
$laststep = 'javascript';
$language = \IPS\Member::loggedIn()->language()->addToStack('appinstall_javascript');
$progress = 87.5;
$progress = 95;
break;
case 'javascript':
/* Insert other data */
\IPS\Application::load( $data['key'] )->installOther();
@@ -1060,6 +1203,12 @@ class _applications extends \IPS\Node\Controller
/* Return null to indicate we are done */
if( $laststep === NULL )
{
if( \IPS\Request::i()->marketplace )
{
\IPS\Application::load( $data['key'] )->marketplace_id = (int) \IPS\Request::i()->marketplace;
\IPS\Application::load( $data['key'] )->save();
}
\IPS\Session::i()->log( 'acplog__application_installed', array( "__app_" . $data['key'] => TRUE ) );
return NULL;
@@ -1087,9 +1236,26 @@ class _applications extends \IPS\Node\Controller
/* Invalidate disk templates */
\IPS\Theme::resetAllCacheKeys();
/* And redirect back to the overview screen */
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=applications&controller=applications' ), 'application_now_installed' );
/* If CMS Template install had conflicts, solve those now */
if( !empty( $_SESSION['cmsConflictKey'] ) )
{
if( $application->marketplace_id )
{
$_SESSION['cmsConflictKey'] = $_SESSION['cmsConflictKey']->setQueryString( 'marketplace', $application->marketplace_id );
}
\IPS\Output::i()->redirect( $_SESSION['cmsConflictKey'] );
}
/* And redirect back to the overview screen (or marketplace if it was installed from there) */
if( $application->marketplace_id )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=marketplace&controller=marketplace&do=viewFile&id=' . $application->marketplace_id ), 'application_now_installed' );
}
else
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=applications&controller=applications' ), 'application_now_installed' );
}
}
);
}
@@ -1102,6 +1268,8 @@ class _applications extends \IPS\Node\Controller
*/
protected function delete()
{
\IPS\Session::i()->csrfCheck();
/* Get node */
$nodeClass = $this->nodeClass;
if ( \IPS\Request::i()->subnode )
@@ -1174,11 +1342,6 @@ class _applications extends \IPS\Node\Controller
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=applications&controller=applications' ), 'deleted' );
}
/**
* Set a new default application if the current default app is being uninstalled
*
* @return void
*/
/**
* Set a new default application if the current default app is being uninstalled
@@ -1210,8 +1373,9 @@ class _applications extends \IPS\Node\Controller
if ( $values = $form->values() )
{
$values['new_default_app']->setAsDefault();
\IPS\Session::i()->log( 'acplog__application_set_default', array( $values['new_default_app']->titleForLog() => FALSE ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=applications&controller=applications&do=delete&id={$node->_id}&wasConfirmed=1" ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=applications&controller=applications&do=delete&id={$node->_id}&wasConfirmed=1" )->csrf() );
}
else
{
@@ -1236,7 +1400,7 @@ class _applications extends \IPS\Node\Controller
/* Get the application */
try
{
$application = call_user_func( "{$nodeClass}::load", \IPS\Request::i()->id );
$application = $nodeClass::load( \IPS\Request::i()->id );
}
catch ( \OutOfRangeException $e )
{
@@ -1245,7 +1409,8 @@ class _applications extends \IPS\Node\Controller
/* Work out tab */
$tab = \IPS\Request::i()->tab ?: 'details';
$activeTabContents = call_user_func( array( $this, '_show' . mb_ucfirst( $tab ) ), $application );
$tabFunction = array( $this, '_show' . mb_ucfirst( $tab ) );
$activeTabContents = $tabFunction( $application );
/* If this is an AJAX request, just return tab contents */
if( \IPS\Request::i()->isAjax() && \IPS\Request::i()->tab and !isset( \IPS\Request::i()->ajaxValidate ) )
@@ -1272,27 +1437,32 @@ class _applications extends \IPS\Node\Controller
*/
public function upload()
{
if ( \IPS\DEMO_MODE )
{
\IPS\Output::i()->error( 'demo_mode_function_blocked', '1C133/V', 403, '' );
}
if ( \IPS\NO_WRITES )
{
\IPS\Output::i()->error( 'no_writes', '1C133/B', 403, '' );
}
if( !is_writable( \IPS\ROOT_PATH . "/applications/" ) )
if( !\IPS\CIC2 AND !is_writable( \IPS\ROOT_PATH . "/applications/" ) )
{
\IPS\Output::i()->error( 'app_dir_not_write', '4C133/8', 500, '' );
}
if( !is_writable( \IPS\ROOT_PATH . "/plugins/" ) ) // necessary as we write the hooks.php file here
if( !\IPS\CIC2 AND !is_writable( \IPS\ROOT_PATH . "/plugins/" ) ) // necessary as we write the hooks.php file here
{
\IPS\Output::i()->error( 'plugin_dir_not_write', '4C133/L', 403, '' );
}
if ( file_exists( \IPS\ROOT_PATH . "/plugins/hooks.php") AND !is_writable( \IPS\ROOT_PATH . "/plugins/hooks.php" ) )
if ( !\IPS\CIC2 AND file_exists( \IPS\ROOT_PATH . "/plugins/hooks.php") AND !is_writable( \IPS\ROOT_PATH . "/plugins/hooks.php" ) )
{
\IPS\Output::i()->error( 'plugin_file_not_write', '4C133/U', 403, '' );
}
if ( !extension_loaded('phar') )
if ( !\extension_loaded('phar') )
{
\IPS\Output::i()->error( 'no_phar_extension', '1C133/P', 403, '' );
}
@@ -1323,10 +1493,13 @@ class _applications extends \IPS\Node\Controller
catch ( \UnexpectedValueException $e )
{
// Legacy 3.x app
if ( !is_dir( \IPS\ROOT_PATH . "/applications/" . \IPS\Request::i()->appKey ) )
if ( !\IPS\CIC2 )
{
mkdir( \IPS\ROOT_PATH . "/applications/" . \IPS\Request::i()->appKey );
chmod( \IPS\ROOT_PATH . "/applications/" . \IPS\Request::i()->appKey, \IPS\IPS_FOLDER_PERMISSION );
if ( !is_dir( \IPS\ROOT_PATH . "/applications/" . \IPS\Request::i()->appKey ) )
{
mkdir( \IPS\ROOT_PATH . "/applications/" . \IPS\Request::i()->appKey );
chmod( \IPS\ROOT_PATH . "/applications/" . \IPS\Request::i()->appKey, \IPS\IPS_FOLDER_PERMISSION );
}
}
}
catch( \OutOfRangeException $e )
@@ -1344,10 +1517,17 @@ class _applications extends \IPS\Node\Controller
\IPS\Output::i()->error( \IPS\Member::loggedIn()->language()->addToStack( "app_upload_designersmode", FALSE, array( 'sprintf' => \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=designersmode' ) ) ), '2C133/O', 403, '' );
}
/* Check Marketplace Status? */
if( $app->marketplace_id )
{
\IPS\Output::i()->error( 'app_upload_marketplace_only', '2C133/X', 403, '' );
}
$_type = 'upgrade';
}
$form = new \IPS\Helpers\Form( 'form', 'install' );
$form->addMessage('applications_manual_install_warning');
$form->add( new \IPS\Helpers\Form\Upload( 'application_file', NULL, TRUE, array( 'allowedFileTypes' => array( 'tar' ), 'temporary' => TRUE ) ) );
if ( $values = $form->values() )
@@ -1368,19 +1548,45 @@ class _applications extends \IPS\Node\Controller
/* Get app directory */
$appdata = json_decode( file_get_contents( "phar://" . $values['application_file'] . '/data/application.json' ), TRUE );
/* Make sure that the app data is valid */
if( !isset( $appdata['app_directory'] ) )
{
throw new \UnexpectedValueException;
}
$appDirectory = $appdata['app_directory'];
/* Extract */
$application->extractTo( \IPS\ROOT_PATH . "/applications/" . $appDirectory, NULL, TRUE );
$this->_checkChmod( \IPS\ROOT_PATH . '/applications/' . $appDirectory );
\IPS\IPS::resyncIPSCloud('Uploaded new application in ACP');
if ( \IPS\CIC2 )
{
unset( \IPS\Data\Store::i()->syncCompleted );
\IPS\Cicloud\file( "IPScustomapp_{$appdata['app_directory']}.tar", \file_get_contents( $values['application_file'] ) );
/* Check files are ready */
$i = 0;
do
{
if ( ( isset( \IPS\Data\Store::i()->syncCompleted ) AND \IPS\Data\Store::i()->syncCompleted ) OR $i >= 30 ) # 30 x 0.25 seconds
{
/* We need to wait for the backend to process the tar */
sleep(3);
break;
}
/* Pause slightly before checking the datastore again */
usleep( 250000 );
$i++;
}
while( TRUE );
}
else
{
/* Extract */
$application->extractTo( \IPS\ROOT_PATH . "/applications/" . $appDirectory, NULL, TRUE );
$this->_checkChmod( \IPS\ROOT_PATH . '/applications/' . $appDirectory );
\IPS\IPS::resyncIPSCloud('Uploaded new application in ACP');
}
}
catch( \PharException $e )
{
@@ -1392,7 +1598,7 @@ class _applications extends \IPS\Node\Controller
\IPS\Output::i()->error( 'application_notvalid', '1C133/K', 403, '' );
}
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=applications&controller=applications&do={$_type}&appKey={$appDirectory}" ), \IPS\Member::loggedIn()->language()->addToStack('installing_application') );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=applications&controller=applications&do={$_type}&appKey={$appDirectory}" )->csrf(), \IPS\Member::loggedIn()->language()->addToStack('installing_application') );
}
/* Display */
@@ -1406,7 +1612,7 @@ class _applications extends \IPS\Node\Controller
* @return void
* @see Ticket 956849
*/
protected function _checkChmod( $directory )
public static function _checkChmod( $directory )
{
if ( !is_dir( $directory ) )
{
@@ -1435,12 +1641,14 @@ class _applications extends \IPS\Node\Controller
*/
public function compilejs()
{
\IPS\Session::i()->csrfCheck();
\IPS\Output::i()->output = new \IPS\Helpers\MultipleRedirect(
\IPS\Http\Url::internal( 'app=core&module=applications&controller=applications&do=compilejs&appKey=' . \IPS\Request::i()->appKey ),
\IPS\Http\Url::internal( 'app=core&module=applications&controller=applications&do=compilejs&appKey=' . \IPS\Request::i()->appKey )->csrf(),
function( $data )
{
/* Is this the first cycle? */
if ( !is_array( $data ) )
if ( !\is_array( $data ) )
{
/* Start importing */
$data = array( 'toDo' => array( 'import', 'compile' ) );
@@ -1449,7 +1657,7 @@ class _applications extends \IPS\Node\Controller
}
/* Grab something to build */
if ( count( $data['toDo'] ) )
if ( \count( $data['toDo'] ) )
{
reset( $data['toDo'] );
$command = array_shift( $data['toDo'] );
@@ -1492,6 +1700,84 @@ class _applications extends \IPS\Node\Controller
}
);
}
/**
* Submit application details to IPS backend
*
* @return void
*/
public function submit()
{
\IPS\Session::i()->csrfCheck();
$url = \IPS\Http\Url::internal('app=core&module=applications&controller=applications&do=submit')->csrf();
\IPS\Output::i()->output = (string) new \IPS\Helpers\MultipleRedirect( $url, function( $data )
{
if ( !isset( $data['done'] ) )
{
$data['done'] = array();
}
foreach ( \IPS\IPS::$ipsApps as $app )
{
if ( \in_array( $app, $data['done'] ) )
{
continue;
}
try
{
$version = \IPS\Application::getAvailableVersion( $app );
$info = json_decode( file_get_contents( \IPS\ROOT_PATH . '/applications/' . $app . '/setup/upg_' . $version . '/data.json' ), TRUE );
$info['built'] = time();
$response = \IPS\Http\Url::ips('upgrade')->request()->login( 'dev', \IPS\IPS_PASSWORD )->post( array(
'version' => $version,
'app' => $app,
'type' => 'info',
'data' => json_encode( $info ),
'alpha' => (int) \IPS\IPS_ALPHA_BUILD,
) );
if ( $response->httpResponseCode != 200 )
{
\IPS\Output::i()->error( (string) $app . $response, '2C133/X', 403, '' );
}
foreach ( $info['steps'] as $k => $v )
{
if ( $v and !\in_array( $k, array( 'customOptions', 'customRoutines' ) ) )
{
$response = \IPS\Http\Url::ips('upgrade')->request()->login( 'dev', \IPS\IPS_PASSWORD )->post( array(
'version' => $version,
'app' => $app,
'type' => $k,
'alpha' => (int) \IPS\IPS_ALPHA_BUILD,
'data' => json_encode( json_decode( file_get_contents( \IPS\ROOT_PATH . '/applications/' . $app . '/setup/upg_' . $version . '/' . $k . '.json' ) ) )
) );
if ( $response->httpResponseCode != 200 )
{
\IPS\Output::i()->error( (string) $app . $response, '2C133/Z', 403, '' );
}
}
}
$data['done'][] = $app;
return array( $data, \IPS\Member::loggedIn()->language()->addToStack('processing') );
}
catch ( \Exception $e )
{
\IPS\Output::i()->error( $e->getMessage(), '2C133/Y', 500, '' );
}
}
return NULL;
},
function()
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=applications&controller=applications' ), 'application_now_built' );
} );
}
/**
* Get application details
@@ -1528,7 +1814,7 @@ class _applications extends \IPS\Node\Controller
$list[ (string) \IPS\DateTime::ts( $version['upgrade_date'] ) ] = \IPS\Member::loggedIn()->language()->addToStack('app_version_string', FALSE, array( 'sprintf' => array( $version['upgrade_version_human'], $version['upgrade_version_id'] ) ) );
}
return ( count( $upgrades ) ) ? \IPS\Theme::i()->getTemplate( 'global' )->definitionTable( $list ) : \IPS\Theme::i()->getTemplate( 'global' )->paddedBlock( \IPS\Member::loggedIn()->language()->addToStack('app_no_upgrade_history') );
return ( \count( $upgrades ) ) ? \IPS\Theme::i()->getTemplate( 'global' )->definitionTable( $list ) : \IPS\Theme::i()->getTemplate( 'global' )->paddedBlock( \IPS\Member::loggedIn()->language()->addToStack('app_no_upgrade_history') );
}
/**
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\applications;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _developer extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -67,6 +72,8 @@ class _developer extends \IPS\Dispatcher\Controller
*/
public function reorder()
{
\IPS\Session::i()->csrfCheck();
return $this->manage();
}
@@ -80,11 +87,13 @@ class _developer extends \IPS\Dispatcher\Controller
/* Work out output */
if ( $pos = mb_strpos( $this->activeTab, '-' ) )
{
$activeTabContents = call_user_func( array( $this, '_manage' . mb_ucfirst( mb_substr( $this->activeTab, 0, $pos ) ) ), mb_substr( $this->activeTab, $pos + 1 ) );
$methodToCall = '_manage' . mb_ucfirst( mb_substr( $this->activeTab, 0, $pos ) );
$activeTabContents = $this->$methodToCall( mb_substr( $this->activeTab, $pos + 1 ) );
}
else
{
$activeTabContents = call_user_func( array( $this, '_manage' . mb_ucfirst( $this->activeTab ) ) );
$methodToCall = '_manage' . mb_ucfirst( $this->activeTab );
$activeTabContents = $this->$methodToCall();
}
/* If this is an AJAX request, just return it */
@@ -107,6 +116,15 @@ class _developer extends \IPS\Dispatcher\Controller
$tabs['tasks'] = 'dev_tasks';
$tabs['versions'] = 'dev_versions';
$tabs['widgets'] = 'dev_widgets';
if ( \in_array( $this->application->directory, \IPS\IPS::$ipsApps ) )
{
$tabs['features'] = 'dev_features';
}
if( \IPS\Application::appIsEnabled('cms') )
{
$tabs['cmstemplates'] = 'dev_cms_templates';
}
/* Display */
if ( $activeTabContents )
@@ -144,6 +162,8 @@ class _developer extends \IPS\Dispatcher\Controller
/* Are we setting a default? */
if ( isset( \IPS\Request::i()->default ) )
{
\IPS\Session::i()->csrfCheck();
$modules[ $location ][ \IPS\Request::i()->root ]['default_controller'] = mb_substr( \IPS\Request::i()->default, 0, -4 );
$this->_writeModules( $modules );
@@ -155,6 +175,8 @@ class _developer extends \IPS\Dispatcher\Controller
/* Or deleting a controller? */
if ( isset( \IPS\Request::i()->delete ) )
{
\IPS\Session::i()->csrfCheck();
if ( @unlink( \IPS\ROOT_PATH . "/applications/{$appKey}/modules/{$location}/" . \IPS\Request::i()->root . '/' . \IPS\Request::i()->delete ) === FALSE )
{
\IPS\Output::i()->error( 'dev_could_not_write_controller', '1C103/I', 403, '' );
@@ -193,7 +215,7 @@ class _developer extends \IPS\Dispatcher\Controller
{
$rows = array();
if( !empty($modules[ $location ]) AND is_array($modules[ $location ]) )
if( !empty($modules[ $location ]) AND \is_array($modules[ $location ]) )
{
foreach ( $modules[ $location ] as $k => $module )
{
@@ -201,7 +223,7 @@ class _developer extends \IPS\Dispatcher\Controller
'default'=> array(
'icon' => ( array_key_exists( 'default', $module ) ) ? ( $module['default'] ? 'star' : 'star-o' ) : 'star-o',
'title' => 'make_default_module',
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=developer&appKey={$appKey}&do=setDefaultModule&id={$module['id']}&location={$location}" ),
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=developer&appKey={$appKey}&do=setDefaultModule&id={$module['id']}&location={$location}" )->csrf(),
),
'add' => array(
'icon' => 'plus-circle',
@@ -269,12 +291,12 @@ class _developer extends \IPS\Dispatcher\Controller
'default' => array(
'icon' => ( str_replace( '.php', '', $controller ) == $modules[ $location ][ $key ]['default_controller'] ) ? 'star' : 'star-o',
'title' => 'modules_make_default',
'link' => $url->setQueryString( array( 'root' => $key, 'default' => (string) $controller ) ),
'link' => $url->setQueryString( array( 'root' => $key, 'default' => (string) $controller ) )->csrf(),
),
'delete' => array(
'icon' => 'times-circle',
'title' => 'delete',
'link' => $url->setQueryString( array( 'root' => $key, 'delete' => (string) $controller ) ),
'link' => $url->setQueryString( array( 'root' => $key, 'delete' => (string) $controller ) )->csrf(),
'data' => array( 'delete' => '' )
)
), '', NULL, NULL, FALSE, NULL, NULL, ( $modules[ $location ][ $key ]['default_controller'] == $controller ? array( 'green', 'modules_default' ) : NULL ) );
@@ -307,10 +329,14 @@ class _developer extends \IPS\Dispatcher\Controller
*/
public function setDefaultModule()
{
\IPS\Session::i()->csrfCheck();
try
{
$module = \IPS\Application\Module::load( \IPS\Request::i()->id );
$module->setAsDefault();
$this->_writeModules( $this->_getModules() );
}
catch ( \OutOfRangeException $e )
{
@@ -401,6 +427,7 @@ class _developer extends \IPS\Dispatcher\Controller
$modules[ $location ][ $module->key ] = array(
'default_controller' => $module->default_controller,
'protected' => $module->protected,
'default' => $module->default
);
if( $current['module_key'] AND $current['module_key'] != $module->key )
@@ -476,6 +503,8 @@ class _developer extends \IPS\Dispatcher\Controller
/* Handle reordering */
if ( \IPS\Request::i()->do === 'reorder' )
{
\IPS\Session::i()->csrfCheck();
/* Normalise AJAX vs non-AJAX */
if( isset( \IPS\Request::i()->ajax_order ) )
{
@@ -645,7 +674,7 @@ class _developer extends \IPS\Dispatcher\Controller
}
/* And restrictions */
$restrictions = array_merge( array( '' => 'acpmenu_norestriction' ), $this->_getRestrictions( $module ) );
$restrictions = $this->_getRestrictions( $module );
/* And tabs */
$tabs = $this->_getAcpMenuTabs();
@@ -665,7 +694,7 @@ class _developer extends \IPS\Dispatcher\Controller
'maxItems' => 1,
) ) ) );
$form->add( new \IPS\Helpers\Form\Text( 'acpmenu_doaction', ( $current ? $current['do'] : NULL ), FALSE, array(), NULL, 'do=' ) );
$form->add( new \IPS\Helpers\Form\Select( 'acpmenu_restriction', ( $current ? $current['restriction'] : '' ), FALSE, array( 'options' => $restrictions ) ) );
$form->add( new \IPS\Helpers\Form\Select( 'acpmenu_restriction', ( $current ? explode( ',', $current['restriction'] ) : '' ), FALSE, array( 'options' => $restrictions, 'multiple' => true ) ) );
/* Handle submissions */
if ( $values = $form->values() )
@@ -674,7 +703,8 @@ class _developer extends \IPS\Dispatcher\Controller
'tab' => $values['acpmenu_tab'],
'controller' => $values['acpmenu_controller'],
'do' => $values['acpmenu_doaction'],
'restriction' => $values['acpmenu_restriction']
'restriction' => \count( $values['acpmenu_restriction'] ) ? implode( ',', $values['acpmenu_restriction'] ) : '',
'subcontrollers' => ""
);
$this->_writeJson( \IPS\ROOT_PATH . "/applications/{$this->application->directory}/data/acpmenu.json", $menu );
@@ -725,6 +755,8 @@ class _developer extends \IPS\Dispatcher\Controller
/* Handle reordering */
if ( \IPS\Request::i()->do === 'reorder' )
{
\IPS\Session::i()->csrfCheck();
/* Init */
$id = explode( '-', mb_substr( \IPS\Request::i()->root, 1 ) );
if( isset( \IPS\Request::i()->ajax_order ) )
@@ -885,10 +917,10 @@ class _developer extends \IPS\Dispatcher\Controller
* Get Group Row
*
* @param string $url URL
* @param string $k Key
* @param string $groupKey Key
* @param string $moduleKey Module Key
* @param array $rows Rows in this group
* @param bool $root As root?
* @param array $r Rows in this group
* @param bool $root As root?
* @return string
*/
protected function _restrictionsGroupRow( $url, $groupKey, $moduleKey, $r, $root )
@@ -1127,7 +1159,7 @@ class _developer extends \IPS\Dispatcher\Controller
);
/* If this isn't an IPS app, strip out our header */
if ( !in_array( $this->application->directory, \IPS\Application::$ipsApps ) )
if ( !\in_array( $this->application->directory, \IPS\IPS::$ipsApps ) )
{
$contents = preg_replace( '/(<\?php\s)\/*.+?\*\//s', '$1', $contents );
}
@@ -1148,7 +1180,8 @@ class _developer extends \IPS\Dispatcher\Controller
'tab' => $values['acpmenu_tab'],
'controller' => $values['filename'],
'do' => '',
'restriction' => $restriction
'restriction' => $restriction,
'subcontrollers' => ''
);
$this->_writeJson( \IPS\ROOT_PATH . "/applications/{$this->application->directory}/data/acpmenu.json", $menu );
@@ -1336,8 +1369,6 @@ class _developer extends \IPS\Dispatcher\Controller
'type' => 'BIGINT',
'length' => '20',
'unsigned' => TRUE,
'zerofill' => FALSE,
'binary' => FALSE,
'allow_null' => FALSE,
'default' => NULL,
'auto_increment' => TRUE,
@@ -1475,36 +1506,8 @@ class _developer extends \IPS\Dispatcher\Controller
{
\IPS\Output::i()->error( 'node_error', '2C103/A', 404, '' );
}
$definition = $schema[ \IPS\Request::i()->_name ];
/* Normalise it a little to stop unecessary conflict errors */
foreach ( $definition['columns'] as $k => $c )
{
foreach ( array( 'length', 'decimals' ) as $i )
{
if ( isset( $c[ $i ] ) )
{
$definition['columns'][ $k ][ $i ] = intval( $c[ $i ] );
}
else
{
$definition['columns'][ $k ][ $i ] = NULL;
}
}
if ( !isset( $c['values'] ) )
{
$definition['columns'][ $k ]['values'] = array();
}
if ( $c['type'] === 'BIT' )
{
$definition['columns'][ $k ]['default'] = ( is_null($c['default']) ) ? NULL : "b'{$c['default']}'";
}
ksort( $definition['columns'][ $k ] );
}
$definition = \IPS\Db::i()->normalizeDefinition( $schema[ \IPS\Request::i()->_name ] );
/* Init Output */
\IPS\Output::i()->title = $definition['name'];
\IPS\Output::i()->breadcrumb[] = array( \IPS\Http\Url::internal( "app=core&module=applications&controller=developer&appKey={$this->application->directory}&tab=schema" ), \IPS\Member::loggedIn()->language()->addToStack( 'database_tables' ) );
@@ -1514,8 +1517,6 @@ class _developer extends \IPS\Dispatcher\Controller
$_definition = $definition;
unset( $_definition['inserts'] );
unset( $_definition['comment'] );
unset( $_definition['engine'] );
unset( $_definition['collation'] );
unset( $_definition['reporting'] );
try
{
@@ -1526,9 +1527,11 @@ class _developer extends \IPS\Dispatcher\Controller
\IPS\Db::i()->createTable( $definition );
$localDefinition = $definition;
}
unset( $localDefinition['collation'] );
$localDefinition = \IPS\Db::i()->normalizeDefinition( $localDefinition );
unset( $localDefinition['comment'] );
unset( $localDefinition['engine'] );
if ( $_definition != $localDefinition )
{
$string1 = str_replace( array( '&lt;?php', '<br>', '<br />' ), "\n", highlight_string( "<?php\n" . var_export( $_definition, TRUE ), TRUE ) );
@@ -1626,7 +1629,7 @@ class _developer extends \IPS\Dispatcher\Controller
}
) );
if ( in_array( $this->application->directory, \IPS\Application::$ipsApps ) )
if ( \in_array( $this->application->directory, \IPS\IPS::$ipsApps ) )
{
$output->add( new \IPS\Helpers\Form\Select( 'database_table_reporting', isset( $definition['reporting'] ) ? $definition['reporting'] : 'none', FALSE, array( 'options' => array(
'none' => 'database_table_reporting_none',
@@ -1681,7 +1684,9 @@ class _developer extends \IPS\Dispatcher\Controller
/* Execute it */
try
{
call_user_func_array( array( \IPS\Db::i(), $query['method'] ), $query['params'] );
$method = $query['method'];
$params = $query['params'];
\IPS\Db::i()->$method( ...$params );
}
catch ( \IPS\Db\Exception $e )
{
@@ -1717,7 +1722,7 @@ class _developer extends \IPS\Dispatcher\Controller
{
$output = new \IPS\Helpers\Table\Custom( $definition['columns'], \IPS\Http\Url::internal( "app=core&module=applications&controller=developer&appKey={$this->application->directory}&do=editSchema&_name={$definition['name']}&existing=1&tab=columns" ) );
$output->langPrefix = 'database_column_';
$output->include = array( 'name', 'type', 'length', 'unsigned', 'zerofill', 'binary', 'allow_null', 'default', 'auto_increment', 'comment' );
$output->include = array( 'name', 'type', 'length', 'unsigned', 'allow_null', 'default', 'auto_increment', 'comment' );
$output->limit = 150;
$output->rootButtons = array(
'add' => array(
@@ -1742,7 +1747,7 @@ class _developer extends \IPS\Dispatcher\Controller
'delete' => array(
'icon' => 'times-circle',
'title' => 'delete',
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=developer&appKey={$appKey}&do=editSchemaDeleteColumn&_name={$definition['name']}&column={$row['name']}" ),
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=developer&appKey={$appKey}&do=editSchemaDeleteColumn&_name={$definition['name']}&column={$row['name']}" )->csrf(),
'data' => array( 'delete' => '' )
)
);
@@ -1755,19 +1760,17 @@ class _developer extends \IPS\Dispatcher\Controller
$output->parsers = array(
'length' => function( $val, $row )
{
if ( $row['decimals'] )
if ( isset( $row['decimals'] ) AND $row['decimals'] )
{
return "{$row['length']},{$row['decimals']}";
}
if ( $row['values'] )
if ( isset( $row['values'] ) AND $row['values'] )
{
return implode( '<br>', $row['values'] );
}
return $val;
},
'unsigned' => $boolParser,
'zerofill' => $boolParser,
'binary' => $boolParser,
'allow_null' => $boolParser,
'auto_increment'=> $boolParser,
);
@@ -1808,7 +1811,7 @@ class _developer extends \IPS\Dispatcher\Controller
'delete' => array(
'icon' => 'times-circle',
'title' => 'delete',
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=developer&appKey={$appKey}&do=editSchemaDeleteIndex&_name={$definition['name']}&index={$row['name']}" ),
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=developer&appKey={$appKey}&do=editSchemaDeleteIndex&_name={$definition['name']}&index={$row['name']}" )->csrf(),
'data' => array( 'delete' => '' )
)
);
@@ -1865,7 +1868,7 @@ class _developer extends \IPS\Dispatcher\Controller
'delete' => array(
'icon' => 'times-circle',
'title' => 'delete',
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=developer&appKey={$self->application->directory}&do=editSchemaDeleteInsert&_name={$definition['name']}&row={$k}" ),
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=developer&appKey={$self->application->directory}&do=editSchemaDeleteInsert&_name={$definition['name']}&row={$k}" )->csrf(),
'data' => array( 'delete' => '' )
)
);
@@ -1945,25 +1948,21 @@ class _developer extends \IPS\Dispatcher\Controller
$form->add( new \IPS\Helpers\Form\Select( 'database_column_type', $column ? $column['type'] : 'VARCHAR', TRUE, array(
'options' => \IPS\Db::$dataTypes,
'toggles' => array(
'TINYINT' => array( 'database_column_length', 'database_column_unsigned', 'database_column_zerofill', 'database_column_auto_increment', 'database_column_default' ),
'SMALLINT' => array( 'database_column_length', 'database_column_unsigned', 'database_column_zerofill', 'database_column_auto_increment', 'database_column_default' ),
'MEDIUMINT' => array( 'database_column_length', 'database_column_unsigned', 'database_column_zerofill', 'database_column_auto_increment', 'database_column_default' ),
'INT' => array( 'database_column_length', 'database_column_unsigned', 'database_column_zerofill', 'database_column_auto_increment', 'database_column_default' ),
'BIGINT' => array( 'database_column_length', 'database_column_unsigned', 'database_column_zerofill', 'database_column_auto_increment', 'database_column_default' ),
'DECIMAL' => array( 'database_column_length', 'database_column_unsigned', 'database_column_zerofill', 'database_column_decimals', 'database_column_default' ),
'FLOAT' => array( 'database_column_length', 'database_column_unsigned', 'database_column_zerofill', 'database_column_decimals', 'database_column_default' ),
'TINYINT' => array( 'database_column_unsigned', 'database_column_auto_increment', 'database_column_default' ),
'SMALLINT' => array( 'database_column_unsigned', 'database_column_auto_increment', 'database_column_default' ),
'MEDIUMINT' => array( 'database_column_unsigned', 'database_column_auto_increment', 'database_column_default' ),
'INT' => array( 'database_column_unsigned', 'database_column_auto_increment', 'database_column_default' ),
'BIGINT' => array( 'database_column_unsigned', 'database_column_auto_increment', 'database_column_default' ),
'DECIMAL' => array( 'database_column_length', 'database_column_decimals', 'database_column_default' ),
'FLOAT' => array( 'database_column_length', 'database_column_default' ),
'BIT' => array( 'database_column_length', 'database_column_default' ),
'DATE' => array( 'database_column_default' ),
'DATETIME' => array( 'database_column_default' ),
'TIMESTAMP' => array( 'database_column_default' ),
'TIME' => array( 'database_column_default' ),
'YEAR' => array( 'database_column_default' ),
'CHAR' => array( 'database_column_length', 'database_column_binary', 'database_column_default' ),
'VARCHAR' => array( 'database_column_length', 'database_column_binary', 'database_column_default' ),
'TINYTEXT' => array( 'database_column_binary' ),
'TEXT' => array( 'database_column_binary' ),
'MEDIUMTEXT'=> array( 'database_column_binary' ),
'LONGTEXT' => array( 'database_column_binary' ),
'CHAR' => array( 'database_column_length', 'database_column_default' ),
'VARCHAR' => array( 'database_column_length', 'database_column_default' ),
'BINARY' => array( 'database_column_length', 'database_column_default' ),
'VARBINARY' => array( 'database_column_length', 'database_column_default' ),
'TINYBLOB' => array( ),
@@ -1982,9 +1981,7 @@ class _developer extends \IPS\Dispatcher\Controller
$form->add( new \IPS\Helpers\Form\TextArea( 'database_column_default', $column ? $column['default'] : NULL, FALSE, array( 'nullLang' => 'NULL' ), NULL, NULL, NULL, 'database_column_default' ) );
$form->add( new \IPS\Helpers\Form\TextArea( 'database_column_comment', $column ? $column['comment'] : NULL, FALSE ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'database_column_unsigned', $column ? $column['unsigned'] : FALSE, FALSE, array(), NULL, NULL, NULL, 'database_column_unsigned' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'database_column_zerofill', $column ? $column['zerofill'] : FALSE, FALSE, array(), NULL, NULL, NULL, 'database_column_zerofill' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'database_column_auto_increment', $column ? $column['auto_increment'] : FALSE, FALSE, array(), NULL, NULL, NULL, 'database_column_auto_increment' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'database_column_binary', $column ? $column['binary'] : FALSE, FALSE, array(), NULL, NULL, NULL, 'database_column_binary' ) );
/* Handle submissions */
if ( $values = $form->values() )
@@ -2110,6 +2107,8 @@ class _developer extends \IPS\Dispatcher\Controller
*/
protected function editSchemaDeleteColumn()
{
\IPS\Session::i()->csrfCheck();
try
{
if ( $this->_schemaJsonIsWritable() !== true )
@@ -2124,7 +2123,7 @@ class _developer extends \IPS\Dispatcher\Controller
unset( $schema[ \IPS\Request::i()->_name ]['columns'][ \IPS\Request::i()->column ] );
/* Do any indexes use this column? */
if ( isset( $schema[ \IPS\Request::i()->_name ]['indexes'] ) and is_array( $schema[ \IPS\Request::i()->_name ]['indexes'] ) )
if ( isset( $schema[ \IPS\Request::i()->_name ]['indexes'] ) and \is_array( $schema[ \IPS\Request::i()->_name ]['indexes'] ) )
{
foreach( $schema[ \IPS\Request::i()->_name ]['indexes'] as $name => $definition )
{
@@ -2142,7 +2141,7 @@ class _developer extends \IPS\Dispatcher\Controller
}
/* Still have columns? */
if ( ! count( $definition['columns'] ) )
if ( ! \count( $definition['columns'] ) )
{
/* Remove the index from schema.json, MySQL will do this automatically */
unset( $schema[ \IPS\Request::i()->_name ]['indexes'][ $name ] );
@@ -2221,7 +2220,7 @@ class _developer extends \IPS\Dispatcher\Controller
{
if ( isset( $definition['columns'][ $field ] ) )
{
if ( ( \mb_substr( \mb_strtolower( $definition['columns'][ $field ]['type'] ), -4 ) === 'text' ) OR ( ! empty( $definition['columns'][ $field ]['length'] ) AND is_integer( $definition['columns'][ $field ]['length']) AND $definition['columns'][ $field ]['length'] > $maxIndexLength ) )
if ( ( \mb_substr( \mb_strtolower( $definition['columns'][ $field ]['type'] ), -4 ) === 'text' ) OR ( ! empty( $definition['columns'][ $field ]['length'] ) AND \is_integer( $definition['columns'][ $field ]['length']) AND $definition['columns'][ $field ]['length'] > $maxIndexLength ) )
{
$save['length'][ $id ] = $maxIndexLength;
}
@@ -2287,6 +2286,8 @@ class _developer extends \IPS\Dispatcher\Controller
*/
protected function editSchemaDeleteIndex()
{
\IPS\Session::i()->csrfCheck();
try
{
if ( $this->_schemaJsonIsWritable() !== true )
@@ -2395,11 +2396,11 @@ class _developer extends \IPS\Dispatcher\Controller
$element = new \IPS\Helpers\Form\Text( $column['name'], $value, FALSE, $options );
}
elseif ( in_array( $column['type'], array( 'CHAR', 'VARCHAR', 'BINARY', 'VARBINARY' ) ) )
elseif ( \in_array( $column['type'], array( 'CHAR', 'VARCHAR', 'BINARY', 'VARBINARY' ) ) )
{
$element = new \IPS\Helpers\Form\Text( $column['name'], ( $data AND isset( $data[ $column['name'] ] ) ) ? $data[ $column['name'] ] : $column['default'], FALSE, $column['allow_null'] ? array( 'nullLang' => 'NULL' ) : array() );
}
elseif ( in_array( $column['type'], array( 'TEXT', 'MEDIUMTEXT', 'BIGTEXT', 'TINYBLOB', 'BLOB', 'MEDIUMBLOB', 'BIGBLOB' ) ) )
elseif ( \in_array( $column['type'], array( 'TEXT', 'MEDIUMTEXT', 'BIGTEXT', 'TINYBLOB', 'BLOB', 'MEDIUMBLOB', 'BIGBLOB' ) ) )
{
$element = new \IPS\Helpers\Form\TextArea( $column['name'], ( $data AND isset( $data[ $column['name'] ] ) ) ? $data[ $column['name'] ] : $column['default'], FALSE, $column['allow_null'] ? array( 'nullLang' => 'NULL' ) : array() );
}
@@ -2471,6 +2472,8 @@ class _developer extends \IPS\Dispatcher\Controller
*/
protected function editSchemaDeleteInsert()
{
\IPS\Session::i()->csrfCheck();
try
{
if ( $this->_schemaJsonIsWritable() !== true )
@@ -2511,6 +2514,8 @@ class _developer extends \IPS\Dispatcher\Controller
*/
protected function resolveSchemaConflicts()
{
\IPS\Session::i()->csrfCheck();
/* Get table definitions */
$schema = $this->_getSchema();
if ( !isset( $schema[ \IPS\Request::i()->_name ] ) )
@@ -2527,7 +2532,7 @@ class _developer extends \IPS\Dispatcher\Controller
{
if ( $data['type'] == 'BIT' )
{
$localDefinition['columns'][ $i ]['default'] = intval( preg_replace( "/^b'(\d+?)'\$/", '$1', $data['default'] ) );
$localDefinition['columns'][ $i ]['default'] = \intval( preg_replace( "/^b'(\d+?)'\$/", '$1', $data['default'] ) );
}
}
$schema[ \IPS\Request::i()->_name ] = $localDefinition;
@@ -2600,12 +2605,11 @@ class _developer extends \IPS\Dispatcher\Controller
$table = \IPS\Request::i()->name;
/* Drop the table */
$query = array( 'method' => 'dropTable', 'params' => array( $table, TRUE ) );
call_user_func_array( array( \IPS\Db::i(), $query['method'] ), $query['params'] );
\IPS\Db::i()->dropTable( $table, TRUE );
/* Add the drop to the queries.json file */
$queries = $this->_getQueries( 'working' );
$queries = $this->_addQueryToJson( $queries, $query );
$queries = $this->_addQueryToJson( $queries, array( 'method' => 'dropTable', 'params' => array( $table, TRUE ) ) );
$this->_writeQueries( 'working', $queries );
/* Remove from schema.json */
@@ -2693,14 +2697,28 @@ class _developer extends \IPS\Dispatcher\Controller
if ( mb_substr( $file, 0, 1 ) !== '.' AND $file != 'index.html' )
{
$buttons = array();
if ( file_exists( \IPS\ROOT_PATH . "/applications/{$folder}/data/defaults/extensions/{$file}.txt" ) )
if ( $file->isDir() )
{
$buttons['add'] = array(
'icon' => 'plus-circle',
'title' => \IPS\Member::loggedIn()->language()->addToStack('dev_extensions_create', FALSE, array( 'sprintf' => array( $file ) ) ),
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=developer&appKey={$this->application->directory}&do=addExtension&type={$file}&extapp={$folder}" ),
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('dev_extensions_create', FALSE, array( 'sprintf' => array( $file ) ) ) )
if ( file_exists( \IPS\ROOT_PATH . "/applications/{$folder}/data/defaults/extensions/{$file}.txt" ) )
{
$buttons['add'] = array(
'icon' => 'plus-circle',
'title' => \IPS\Member::loggedIn()->language()->addToStack('dev_extensions_create', FALSE, array( 'sprintf' => array( $file ) ) ),
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=developer&appKey={$this->application->directory}&do=addExtension&type={$file}&extapp={$folder}" ),
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('dev_extensions_create', FALSE, array( 'sprintf' => array( $file ) ) ) )
);
}
}
else
{
$name = mb_substr( $file, 0, -4 );
$buttons['delete'] = array(
'icon' => 'times-circle',
'title' => \IPS\Member::loggedIn()->language()->addToStack('delete'),
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=developer&appKey={$this->application->directory}&do=removeExtension&file={$name}&ext={$folder}" )->csrf(),
'data' => array( 'delete' => '' )
);
}
@@ -2712,7 +2730,7 @@ class _developer extends \IPS\Dispatcher\Controller
}
$extensionApp = explode( '/', $folder );
if( count( $extensionApp ) == 1 )
if( \count( $extensionApp ) == 1 )
{
foreach ( new \DirectoryIterator( \IPS\ROOT_PATH . "/applications/{$extensionApp[0]}/data/defaults/extensions" ) as $file )
{
@@ -2741,6 +2759,7 @@ class _developer extends \IPS\Dispatcher\Controller
/**
* Extensions: Get parent ID
*
* @param string $folder Folder name
* @return string
*/
public function extGetRowParentId( $folder )
@@ -2785,12 +2804,28 @@ class _developer extends \IPS\Dispatcher\Controller
}
\file_put_contents( \IPS\ROOT_PATH . "/applications/{$this->application->directory}/extensions/" . \IPS\Request::i()->extapp . '/' . \IPS\Request::i()->type . "/{$values['dev_extensions_classname']}.php", $contents );
$this->_writeJson( \IPS\ROOT_PATH . "/applications/{$this->application->directory}/data/extensions.json", $this->application->buildExtensionsJson() );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=applications&controller=developer&appKey={$this->application->directory}&tab=extensions" ), 'file_created' );
}
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('global')->block( \IPS\Member::loggedIn()->language()->addToStack('dev_extensions_create', FALSE, array( 'sprintf' => array( \IPS\Request::i()->type ) ) ), $form, FALSE );
}
/**
* Remove Extension
*
* @return void
*/
protected function removeExtension()
{
\IPS\Session::i()->csrfCheck();
unlink( \IPS\ROOT_PATH . "/applications/{$this->application->directory}/extensions/" . \IPS\Request::i()->ext . "/" . \IPS\Request::i()->file . ".php" );
$this->_writeJson( \IPS\ROOT_PATH . "/applications/{$this->application->directory}/data/extensions.json", $this->application->buildExtensionsJson() );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=applications&controller=developer&appKey={$this->application->directory}&tab=extensions" ), 'deleted' );
}
/**
* Manage Settings
*
@@ -2806,7 +2841,7 @@ class _developer extends \IPS\Dispatcher\Controller
'key' => array( 'Text', NULL, TRUE ),
'default' => array( 'Text' ),
);
if ( in_array( $this->application->directory, \IPS\Application::$ipsApps ) )
if ( \in_array( $this->application->directory, \IPS\IPS::$ipsApps ) )
{
$form->columns['report'] = array( 'Select', 'none', FALSE, array( 'options' => array(
'none' => 'dev_settings_none',
@@ -2882,6 +2917,8 @@ class _developer extends \IPS\Dispatcher\Controller
/* Reorder if necessary */
if ( \IPS\Request::i()->do === 'reorder' )
{
\IPS\Session::i()->csrfCheck();
/* Normalise AJAX vs non-AJAX */
if( isset( \IPS\Request::i()->ajax_order ) )
{
@@ -3018,7 +3055,7 @@ class _developer extends \IPS\Dispatcher\Controller
$buttons['phpcode'] = array(
'icon' => 'code',
'title' => 'versions_code',
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=developer&appKey={$this->application->directory}&do=versionCode&id={$long}" ),
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=developer&appKey={$this->application->directory}&do=versionCode&id={$long}" )->csrf(),
);
}
@@ -3069,7 +3106,7 @@ class _developer extends \IPS\Dispatcher\Controller
foreach ( $queries as $qid => $data )
{
$params = array();
if ( isset( $data['params'] ) and is_array( $data['params'] ) )
if ( isset( $data['params'] ) and \is_array( $data['params'] ) )
{
foreach ( $data['params'] as $v )
{
@@ -3151,7 +3188,7 @@ class _developer extends \IPS\Dispatcher\Controller
/* If this was for the core, add it to all IPS apps */
if ( $this->application->directory === 'core' )
{
foreach ( \IPS\Application::$ipsApps as $_appKey )
foreach ( \IPS\IPS::$ipsApps as $_appKey )
{
$appJson = $this->_getJson( \IPS\ROOT_PATH . "/applications/{$_appKey}/data/versions.json" );
$appJson[ $values['versions_long'] ] = $values['versions_human'];
@@ -3236,9 +3273,9 @@ class _developer extends \IPS\Dispatcher\Controller
$versionsFile = \IPS\ROOT_PATH . "/applications/{$this->application->directory}/data/versions.json";
$json = $this->_getVersions();
if ( isset( $json[ intval( \IPS\Request::i()->id ) ] ) )
if ( isset( $json[ \intval( \IPS\Request::i()->id ) ] ) )
{
unset( $json[ intval( \IPS\Request::i()->id ) ] );
unset( $json[ \intval( \IPS\Request::i()->id ) ] );
}
$this->_writeVersions( $json );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=applications&controller=developer&appKey={$this->application->directory}&tab=versions" ) );
@@ -3348,7 +3385,7 @@ class _developer extends \IPS\Dispatcher\Controller
$version = ( \IPS\Request::i()->version == 'install' ? 'install' : \IPS\Request::i()->version );
$json = $this->_getQueries( $version );
unset( $json[ intval( \IPS\Request::i()->query ) ] );
unset( $json[ \intval( \IPS\Request::i()->query ) ] );
$this->_writeQueries( $version, $json );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=applications&controller=developer&appKey={$this->application->directory}&tab=versions&root={$version}" ) );
}
@@ -3360,10 +3397,12 @@ class _developer extends \IPS\Dispatcher\Controller
*/
protected function versionCode()
{
\IPS\Session::i()->csrfCheck();
/* Get version */
if ( \IPS\Request::i()->id !== 'working' )
{
$long = intval( \IPS\Request::i()->id );
$long = \intval( \IPS\Request::i()->id );
$json = $this->_getVersions();
if ( !isset( $json[ $long ] ) )
{
@@ -3402,7 +3441,7 @@ class _developer extends \IPS\Dispatcher\Controller
);
/* If this isn't an IPS app, strip out our header */
if ( !in_array( $this->application->directory, \IPS\Application::$ipsApps ) )
if ( !\in_array( $this->application->directory, \IPS\IPS::$ipsApps ) )
{
$contents = preg_replace( '/(<\?php\s)\/*.+?\*\//s', '$1', $contents );
}
@@ -3456,6 +3495,25 @@ class _developer extends \IPS\Dispatcher\Controller
$this->application->directory
);
}
/**
* Manage Features
*
* @return string
*/
protected function _manageFeatures()
{
if ( !\in_array( $this->application->directory, \IPS\IPS::$ipsApps ) )
{
\IPS\Output::i()->error( 'app_ips_only', '2C103/R', 403, '' );
}
return \IPS\Application\Feature::devTable(
\IPS\Http\Url::internal( "app=core&module=applications&controller=developer&appKey={$this->application->directory}&tab=features" ),
$this->application->directory
);
}
/**
* Manage Hooks
*
@@ -3473,6 +3531,7 @@ class _developer extends \IPS\Dispatcher\Controller
/**
* Edit Hook
*
* @csrfChecked uses $hook->editForm() 7 Oct 2019
* @return string
*/
protected function editHook()
@@ -3573,9 +3632,21 @@ class _developer extends \IPS\Dispatcher\Controller
$extra[ $row['sys_module_area'] ][ $row['sys_module_key'] ] = array( 'default' => $row['sys_module_default'], 'id' => $row['sys_module_id'], 'default_controller' => $row['sys_module_default_controller'], 'protected' => $row['sys_module_protected'] );
}
if ( is_array( $json ) AND count( $json ) )
if ( \is_array( $json ) AND \count( $json ) )
{
$modules = $json;
foreach( $db as $row )
{
if( $row['sys_module_default'] )
{
$modules[ $row['sys_module_area'] ][ $row['sys_module_key'] ]['default'] = true;
}
elseif( isset( $modules[ $row['sys_module_area'] ][ $row['sys_module_key'] ]['default'] ) )
{
$modules[ $row['sys_module_area'] ][ $row['sys_module_key'] ]['default'] = false;
}
}
}
else
{
@@ -3583,7 +3654,8 @@ class _developer extends \IPS\Dispatcher\Controller
{
$modules[ $row['sys_module_area'] ][ $row['sys_module_key'] ] = array(
'default_controller' => $row['sys_module_default_controller'],
'protected' => $row['sys_module_protected']
'protected' => $row['sys_module_protected'],
'default' => $row['sys_module_default']
);
}
}
@@ -3611,7 +3683,7 @@ class _developer extends \IPS\Dispatcher\Controller
{
foreach( $data as $k => $v )
{
if ( ! in_array( $k, array( 'protected', 'default_controller' ) ) )
if ( ! \in_array( $k, array( 'protected', 'default_controller', 'default' ) ) )
{
unset( $json[ $location ][ $name ][ $k ] );
}
@@ -3893,6 +3965,8 @@ class _developer extends \IPS\Dispatcher\Controller
*/
protected function build()
{
\IPS\Session::i()->csrfCheck();
try
{
$this->application->build();
@@ -3904,4 +3978,29 @@ class _developer extends \IPS\Dispatcher\Controller
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=applications&controller=applications" ), 'dev_built' );
}
/**
* Manage CMS Templates
*
* @return string
*/
protected function _manageCmstemplates(): string
{
$templateConfigFile = \IPS\ROOT_PATH . "/applications/{$this->application->directory}/dev/cmsTemplates.json";
$preSelectedTemplates = array( 'templates_database' => [], 'templates_block' => [], 'templates_page' => [] );
if( file_exists( $templateConfigFile ) )
{
$preSelectedTemplates = json_decode( file_get_contents( $templateConfigFile ), TRUE );
}
$form = \IPS\cms\Templates::exportForm( TRUE, $preSelectedTemplates );
if( $values = $form->values() )
{
$this->_writeJson( \IPS\ROOT_PATH . "/applications/{$this->application->directory}/dev/cmsTemplates.json", $values );
}
return $form;
}
}
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\applications;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _enhancements extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* @brief Enhancements plugins
*/
@@ -51,14 +56,17 @@ class _enhancements extends \IPS\Dispatcher\Controller
foreach( $this->enhancements as $key => $class )
{
$rows[ (int) $class->ips ][ $key ] = array(
'title' => "enhancements__{$key}",
'description' => "enhancements__{$key}_desc",
'app' => mb_substr( $key, 0, mb_strpos( $key, '_' ) ),
'icon' => $class->icon,
'enabled' => $class->enabled,
'config' => $class->hasOptions ? TRUE : FALSE
);
if ( !method_exists( $class, 'isAvailable' ) or $class::isAvailable() )
{
$rows[ (int) $class->ips ][ $key ] = array(
'title' => "enhancements__{$key}",
'description' => "enhancements__{$key}_desc",
'app' => mb_substr( $key, 0, mb_strpos( $key, '_' ) ),
'icon' => $class->icon,
'enabled' => $class->enabled,
'config' => $class->hasOptions ? TRUE : FALSE
);
}
}
/* Display */
@@ -70,11 +78,12 @@ class _enhancements extends \IPS\Dispatcher\Controller
/**
* Edit
*
* @csrfChecked Extension files use form helper 7 Oct 2019
* @return void
*/
public function edit()
{
if ( isset( $this->enhancements[ \IPS\Request::i()->id ] ) )
if ( isset( $this->enhancements[ \IPS\Request::i()->id ] ) and ( !method_exists( $this->enhancements[ \IPS\Request::i()->id ], 'isAvailable' ) or $this->enhancements[ \IPS\Request::i()->id ]::isAvailable() ) )
{
$langKey = 'enhancements__' . \IPS\Request::i()->id;
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( $langKey );
@@ -104,6 +113,8 @@ class _enhancements extends \IPS\Dispatcher\Controller
*/
public function enableToggle()
{
\IPS\Session::i()->csrfCheck();
try
{
$this->enhancements[ \IPS\Request::i()->id ]->toggle( \IPS\Request::i()->status );
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\applications;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _menu extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -47,7 +52,7 @@ class _menu extends \IPS\Dispatcher\Controller
/* Buttons */
\IPS\Output::i()->sidebar['actions']['restore'] = array(
'icon' => 'refresh',
'link' => \IPS\Http\Url::internal('app=core&module=applications&controller=menu&do=restore'),
'link' => \IPS\Http\Url::internal('app=core&module=applications&controller=menu&do=restore')->csrf(),
'title' => 'menu_manager_revert',
'data' => array(
'action' => 'restoreMenu'
@@ -66,7 +71,7 @@ class _menu extends \IPS\Dispatcher\Controller
$class = 'IPS\\' . $item['app'] . '\extensions\core\FrontNavigation\\' . $item['extension'];
if ( class_exists( $class ) )
{
$items[ intval( $item['parent'] ) ][ $item['id'] ] = new $class( json_decode( $item['config'], TRUE ), $item['id'], $item['permissions'] );
$items[ \intval( $item['parent'] ) ][ $item['id'] ] = new $class( json_decode( $item['config'], TRUE ), $item['id'], $item['permissions'] );
}
}
if ( $item['app'] == 'core' and $item['extension'] == 'Menu' )
@@ -83,7 +88,7 @@ class _menu extends \IPS\Dispatcher\Controller
/* Display */
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_applications_menu');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'applications' )->menuManager( $items, $menus );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'applications' )->menuManager( $items, $menus, \IPS\Http\Url::internal('app=core&module=applications&controller=menu') );
}
/**
@@ -93,6 +98,8 @@ class _menu extends \IPS\Dispatcher\Controller
*/
protected function publish()
{
\IPS\Session::i()->csrfCheck();
if ( \IPS\Db::i()->select( 'COUNT(*)', 'core_menu' )->first() )
{
unset( \IPS\Data\Store::i()->frontNavigation );
@@ -135,7 +142,7 @@ class _menu extends \IPS\Dispatcher\Controller
{
$existing = \IPS\Db::i()->select( '*', 'core_menu', array( 'id=?', \IPS\Request::i()->id ) )->first();
}
catch ( \OutOfRangeException $e ) { }
catch ( \UnderflowException $e ) { }
}
/* What options are available? */
@@ -216,7 +223,7 @@ class _menu extends \IPS\Dispatcher\Controller
{
foreach ( $values as $k => $v )
{
if ( in_array( $k, $fieldNames[ $values['menu_manager_extension'] ] ) )
if ( \in_array( $k, $fieldNames[ $values['menu_manager_extension'] ] ) )
{
$config[ $k ] = $v;
}
@@ -244,7 +251,7 @@ class _menu extends \IPS\Dispatcher\Controller
}
/* First we need to determine if the access type option was even shown */
$hasAccessType = ( in_array( 'menu_manager_access_type', $toggles[ $values['menu_manager_extension'] ] ) ) ? TRUE : FALSE;
$hasAccessType = ( \in_array( 'menu_manager_access_type', $toggles[ $values['menu_manager_extension'] ] ) ) ? TRUE : FALSE;
/* If we didn't have the access type field, then we should just check the permissions that were specified */
if ( $values['menu_manager_access_type'] OR !$hasAccessType )
@@ -304,7 +311,7 @@ class _menu extends \IPS\Dispatcher\Controller
{
$item = new $class( $_config, $id, $save['permissions'] );
$output = array(
'menu_item' => \IPS\Theme::i()->getTemplate( 'applications' )->menuItem( $item, $id ),
'menu_item' => \IPS\Theme::i()->getTemplate( 'applications' )->menuItem( $item, $id, \IPS\Http\Url::internal('app=core&module=applications&controller=menu') ),
'id' => $id
);
@@ -320,7 +327,7 @@ class _menu extends \IPS\Dispatcher\Controller
if ( $parent['app'] === 'core' and $parent['extension'] === 'Menu' )
{
$output['menu_item'] = \IPS\Theme::i()->getTemplate( 'applications' )->menuManagerDropdownItem( $item, $id );
$output['menu_item'] = \IPS\Theme::i()->getTemplate( 'applications' )->menuManagerDropdownItem( $item, $id, \IPS\Http\Url::internal('app=core&module=applications&controller=menu') );
}
}
catch ( \UnderflowException $e ) { }
@@ -329,7 +336,7 @@ class _menu extends \IPS\Dispatcher\Controller
/* If this is a dropdown item, we also need to return the HTML for the dropdown edit screen */
if ( $exploded[1] == 'Menu' )
{
$output['dropdown_menu'] = \IPS\Theme::i()->getTemplate( 'applications' )->menuManagerDropdown( $item, $id, ( isset( $parentItem ) ? $parentItem : 0 ) );
$output['dropdown_menu'] = \IPS\Theme::i()->getTemplate( 'applications' )->menuManagerDropdown( $item, $id, ( isset( $parentItem ) ? $parentItem : 0 ), \IPS\Http\Url::internal('app=core&module=applications&controller=menu') );
}
\IPS\Output::i()->json( $output );
@@ -341,7 +348,7 @@ class _menu extends \IPS\Dispatcher\Controller
}
/* Display */
\IPS\Output::i()->output = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'applications', 'core' ) ), 'menuManagerForm' ) );
\IPS\Output::i()->output = $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'applications', 'core' ), 'menuManagerForm' ) );
if ( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->sendOutput( \IPS\Output::i()->output );
@@ -355,7 +362,8 @@ class _menu extends \IPS\Dispatcher\Controller
*/
protected function remove()
{
static::_remove( intval( \IPS\Request::i()->id ) );
\IPS\Session::i()->csrfCheck();
static::_remove( \intval( \IPS\Request::i()->id ) );
\IPS\Output::i()->json('OK');
}
@@ -384,6 +392,8 @@ class _menu extends \IPS\Dispatcher\Controller
*/
protected function reorder()
{
\IPS\Session::i()->csrfCheck();
$positions = array();
foreach ( \IPS\Request::i()->menu_order as $id => $parentId )
@@ -425,6 +435,7 @@ class _menu extends \IPS\Dispatcher\Controller
*/
protected function restore()
{
\IPS\Session::i()->csrfCheck();
\IPS\core\FrontNavigation::buildDefaultFrontNavigation();
\IPS\Output::i()->redirect( \IPS\Http\Url::internal('app=core&module=applications&controller=menu'), 'menu_manager_reverted' );
}
@@ -12,7 +12,7 @@
namespace IPS\core\modules\admin\applications;
/* 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;
@@ -23,6 +23,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _oauth extends \IPS\Node\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Node Class
*/
@@ -41,7 +46,6 @@ class _oauth extends \IPS\Node\Controller
public function execute()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'oauth_manage' );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'system/api.css', 'core', 'admin' ) );
return parent::execute();
}
@@ -85,9 +89,16 @@ class _oauth extends \IPS\Node\Controller
\IPS\Output::i()->error( 'node_error', '2C362/1', 404, '' );
}
if ( $client->type === 'mobile' )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=mobile&controller=mobile" ) );
}
$secret = NULL;
if ( isset( \IPS\Request::i()->newSecret ) and \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'applications', 'oauth_secrets' ) )
{
\IPS\Session::i()->csrfCheck();
$secret = \IPS\Login::generateRandomString( 48 );
$client->client_secret = password_hash( $secret, PASSWORD_DEFAULT );
$client->brute_force = NULL;
@@ -118,14 +129,14 @@ class _oauth extends \IPS\Node\Controller
$return['ban'] = array(
'icon' => 'ban',
'title' => 'oauth_brute_force_ban',
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=oauth&do=bfRemove&ban=1&client_id={$client->client_id}" )->setQueryString( 'ip', $row['ip_address'] )
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=oauth&do=bfRemove&ban=1&client_id={$client->client_id}" )->setQueryString( 'ip', $row['ip_address'] )->csrf()
);
if ( $row['fails'] >= 3 )
{
$return['unlock'] = array(
'icon' => 'unlock',
'title' => 'oauth_brute_force_unlock',
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=oauth&do=bfRemove&client_id={$client->client_id}" )->setQueryString( 'ip', $row['ip_address'] )
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=oauth&do=bfRemove&client_id={$client->client_id}" )->setQueryString( 'ip', $row['ip_address'] )->csrf()
);
}
return $return;
@@ -145,6 +156,8 @@ class _oauth extends \IPS\Node\Controller
*/
protected function bfRemove()
{
\IPS\Session::i()->csrfCheck();
try
{
$client = \IPS\Api\OAuthClient::load( \IPS\Request::i()->client_id );
@@ -211,6 +224,53 @@ class _oauth extends \IPS\Node\Controller
\IPS\Output::i()->error( 'node_error', '2C362/3', 404, '' );
}
if ( $client )
{
$columns = array(
'access_token_expires' => (bool) $client->access_token_length,
'refresh_token_expires' => (bool) ( $client->use_refresh_tokens and $client->refresh_token_length ),
'scope' => (bool) ( $client->scopes and json_decode( $client->scopes ) ),
'auth_user_agent' => ( \in_array( 'authorization_code', explode( ',', $client->grant_types ) ) or \in_array( 'implicit', explode( ',', $client->grant_types ) ) ),
'issue_user_agent' => ( \in_array( 'authorization_code', explode( ',', $client->grant_types ) ) or \in_array( 'password', explode( ',', $client->grant_types ) ) or \in_array( 'client_credentials', explode( ',', $client->grant_types ) ) ),
);
}
else
{
$columns = array(
'access_token_expires' => FALSE,
'refresh_token_expires' => FALSE,
'scope' => FALSE,
'auth_user_agent' => FALSE,
'issue_user_agent' => FALSE,
);
$count = 0;
foreach ( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_oauth_clients', array( \IPS\Db::i()->findInSet( 'oauth_grant_types', array( 'authorization_code', 'implicit', 'password' ) ) ) ), 'IPS\Api\OAuthClient' ) as $_client )
{
$count++;
if ( $_client->access_token_length )
{
$columns['access_token_expires'] = TRUE;
}
if ( $_client->use_refresh_tokens and $_client->refresh_token_length )
{
$columns['refresh_token_expires'] = TRUE;
}
if ( $_client->scopes and json_decode( $_client->scopes ) )
{
$columns['scope'] = TRUE;
}
if ( \in_array( 'authorization_code', explode( ',', $_client->grant_types ) ) or \in_array( 'implicit', explode( ',', $_client->grant_types ) ) )
{
$columns['auth_user_agent'] = TRUE;
}
if ( \in_array( 'authorization_code', explode( ',', $_client->grant_types ) ) or \in_array( 'password', explode( ',', $_client->grant_types ) ) or \in_array( 'client_credentials', explode( ',', $_client->grant_types ) ) )
{
$columns['issue_user_agent'] = TRUE;
}
}
}
$table = new \IPS\Helpers\Table\Db( 'core_oauth_server_access_tokens', $baseUrl, array( $client ? array( 'client_id=?', $client->client_id ) : array( 'member_id=?', $member->member_id ) ) );
$table->langPrefix = 'oauth_authorization_';
$table->include = array();
@@ -220,27 +280,35 @@ class _oauth extends \IPS\Node\Controller
$table->include[] = 'member_id';
$table->advancedSearch['member_id'] = \IPS\Helpers\Table\SEARCH_MEMBER;
}
else
elseif ( $count > 1 )
{
$table->include[] = 'client_id';
}
$table->include[] = 'issued';
$table->include[] = 'status';
$table->advancedSearch['issued'] = \IPS\Helpers\Table\SEARCH_DATE_RANGE;
if ( !$client or $client->access_token_length )
if ( $columns['access_token_expires'] )
{
$table->include[] = 'access_token_expires';
$table->advancedSearch['access_token_expires'] = \IPS\Helpers\Table\SEARCH_DATE_RANGE;
}
if ( !$client or ( $client->use_refresh_tokens and $client->refresh_token_length ) )
if ( $columns['refresh_token_expires'] )
{
$table->include[] = 'refresh_token_expires';
$table->advancedSearch['refresh_token_expires'] = \IPS\Helpers\Table\SEARCH_DATE_RANGE;
}
if ( !$client or ( $client->scopes and json_decode( $client->scopes ) ) )
if ( $columns['scope'] )
{
$table->include[] = 'scope';
}
if ( $columns['auth_user_agent'] )
{
$table->include[] = 'auth_user_agent';
}
if ( $columns['issue_user_agent'] )
{
$table->include[] = 'issue_user_agent';
}
$table->noSort = array( 'status', 'scope' );
$table->sortBy = $table->sortBy ?: 'issued';
$table->parsers = array(
@@ -315,6 +383,31 @@ class _oauth extends \IPS\Node\Controller
{
return '';
}
},
'auth_user_agent' => function( $val, $row )
{
if ( $row['device_key'] )
{
try
{
$device = \IPS\Member\Device::load( $row['device_key'] );
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( \IPS\Http\Url::internal( "app=core&module=members&controller=devices&do=device&key={$row['device_key']}&member={$row['member_id']}" ), FALSE, (string) \IPS\Http\UserAgent::parse( $val ), FALSE );
}
catch ( \OutOfRangeException $e ) { }
}
return (string) \IPS\Http\UserAgent::parse( $val );
},
'issue_user_agent' => function( $val )
{
$agent = \IPS\Http\UserAgent::parse( $val );
if ( $agent->ipsApp )
{
return \IPS\Theme::i()->getTemplate( 'api', 'core', 'admin' )->clientDetails( $val, $agent );
}
else
{
return \IPS\Theme::i()->getTemplate( 'api', 'core', 'admin' )->clientDetails( $val );
}
}
);
$table->rowButtons = function( $row ) use ( $client ) {
@@ -322,12 +415,12 @@ class _oauth extends \IPS\Node\Controller
'revoke' => array(
'icon' => 'times-circle',
'title' => 'oauth_app_revoke',
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=oauth&do=revokeToken&client_id={$row['client_id']}&member_id={$row['member_id']}&token={$row['access_token']}" )->setQueryString( 'r', $client ? 'c' : 'm' ),
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=oauth&do=revokeToken&client_id={$row['client_id']}&member_id={$row['member_id']}&token={$row['access_token']}" )->setQueryString( 'r', $client ? 'c' : 'm' )->csrf(),
'data' => array( 'delete' => '' )
)
);
};
$revokeAllLink = $client ? \IPS\Http\Url::internal( "app=core&module=applications&controller=oauth&do=revokeAllTokens&client_id={$client->client_id}" ) : \IPS\Http\Url::internal( "app=core&module=applications&controller=oauth&do=revokeAllTokens&member_id={$member->member_id}" );
$revokeAllLink = $client ? \IPS\Http\Url::internal( "app=core&module=applications&controller=oauth&do=revokeAllTokens&client_id={$client->client_id}" )->csrf() : \IPS\Http\Url::internal( "app=core&module=applications&controller=oauth&do=revokeAllTokens&member_id={$member->member_id}" )->csrf();
$table->rootButtons = array(
'revoke' => array(
'icon' => 'times-circle',
@@ -341,15 +434,30 @@ class _oauth extends \IPS\Node\Controller
if ( $client )
{
\IPS\Output::i()->title = $client->_title;
\IPS\Output::i()->breadcrumb[] = array( \IPS\Http\Url::internal( "app=core&module=applications&controller=api&tab=oauth" ), 'oauth_clients' );
if ( $client->type !== 'mobile' )
{
\IPS\Output::i()->breadcrumb[] = array( \IPS\Http\Url::internal( "app=core&module=applications&controller=api&tab=oauth" ), 'oauth_clients' );
}
\IPS\Output::i()->breadcrumb[] = array( \IPS\Http\Url::internal( "app=core&module=applications&controller=oauth&do=view&client_id={$client->client_id}" ), $client->_title );
\IPS\Output::i()->breadcrumb[] = array( NULL, 'oauth_view_authorizations' );
}
else
{
\IPS\Output::i()->title = $member->name;
\IPS\Output::i()->breadcrumb[] = array( \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=edit&id={$member->member_id}" ), $member->name );
\IPS\Output::i()->breadcrumb[] = array( NULL, 'oauth_member_authorizations' );
\IPS\Output::i()->breadcrumb[] = array( \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=view&id={$member->member_id}" ), $member->name );
if ( $count > 1 )
{
\IPS\Output::i()->breadcrumb[] = array( NULL, 'oauth_member_authorizations' );
}
else
{
foreach ( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_oauth_clients', array( \IPS\Db::i()->findInSet( 'oauth_grant_types', array( 'authorization_code', 'implicit', 'password' ) ) ) ), 'IPS\Api\OAuthClient' ) as $_client )
{
\IPS\Output::i()->breadcrumb[] = array( NULL, $_client->_title );
break;
}
}
}
}
@@ -362,6 +470,7 @@ class _oauth extends \IPS\Node\Controller
protected function revokeToken()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'oauth_tokens' );
\IPS\Session::i()->csrfCheck();
\IPS\Db::i()->delete( 'core_oauth_server_access_tokens', array( 'client_id=? AND access_token=?', \IPS\Request::i()->client_id, \IPS\Request::i()->token ) );
\IPS\Session::i()->log( 'acplogs__oauth_revoke_token', array( 'core_oauth_client_' . \IPS\Request::i()->client_id => TRUE ) );
@@ -388,6 +497,7 @@ class _oauth extends \IPS\Node\Controller
protected function revokeAllTokens()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'oauth_tokens' );
\IPS\Session::i()->csrfCheck();
if ( \IPS\Request::i()->member_id )
{
@@ -398,7 +508,7 @@ class _oauth extends \IPS\Node\Controller
else
{
\IPS\Db::i()->delete( 'core_oauth_server_access_tokens', array( 'client_id=?', \IPS\Request::i()->client_id ) );
\IPS\Session::i()->log( 'acplogs__oauth_revoke_client', array( 'core_oauth_client_' . $client->client_id => TRUE ) );
\IPS\Session::i()->log( 'acplogs__oauth_revoke_client', array( 'core_oauth_client_' . \IPS\Request::i()->client_id => TRUE ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=applications&controller=oauth&do=tokens" )->setQueryString( 'client_id', \IPS\Request::i()->client_id ) );
}
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,341 @@
<?php
/**
* @brief Feeds
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage Forums
* @since 04 Feb 2014
*/
namespace IPS\core\modules\admin\applications;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* rss
*/
class _rss extends \IPS\Node\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Node Class
*/
protected $nodeClass = 'IPS\core\Rss\Import';
/**
* Execute
*
* @return void
*/
public function execute()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'rss_manage' );
parent::execute();
}
/**
* Get Root Rows
*
* @return array
*/
public function _getRoots()
{
$nodeClass = $this->nodeClass;
$rows = array();
$classes = array();
foreach( \IPS\Application::allExtensions( 'core', 'RssImport' ) as $key => $class )
{
foreach( $class->classes as $_class )
{
$classes[ $_class ] = $class;
}
}
foreach( $nodeClass::roots( NULL ) as $node )
{
/* Don't show user-added blog entries */
if ( isset( $classes[ $node->class ] ) and $classes[ $node->class ]->showInAdminCp( $node->class ) )
{
$rows[$node->_id] = $this->_getRow( $node );
}
}
return $rows;
}
/**
* Get Root Buttons
*
* @return array
*/
public function _getRootButtons()
{
$buttons = parent::_getRootButtons();
if ( isset( \IPS\Output::i()->sidebar['actions']['add'] ) )
{
\IPS\Output::i()->sidebar['actions']['add']['link'] = \IPS\Output::i()->sidebar['actions']['add']['link']->setQueryString( '_new', TRUE );
}
return $buttons;
}
/**
* Fetch any additional HTML for this row
*
* @param object $node Node returned from $nodeClass::load()
* @return NULL|string
*/
public function _getRowHtml( $node )
{
return \IPS\Theme::i()->getTemplate( 'feeds' )->importRow( $node );
}
/**
* Add/Edit Form
*
* @return void
*/
protected function form()
{
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global', 'core' )->message( \IPS\Member::loggedIn()->language()->addToStack('rss_import_untrusted_url'), 'warning' );
$nodeClass = $this->nodeClass;
if ( !\IPS\Request::i()->id and $nodeClass::canAddRoot() )
{
\IPS\Output::i()->output .= (string) new \IPS\Helpers\Wizard( array(
'rss_import_url' => function( $data )
{
$form = new \IPS\Helpers\Form( 'form', 'continue' );
$form->add( new \IPS\Helpers\Form\Url( 'rss_import_url', NULL, TRUE ) );
$form->add( new \IPS\Helpers\Form\Text( 'rss_import_auth_user' ) );
$form->add( new \IPS\Helpers\Form\Password( 'rss_import_auth_pass' ) );
if ( $values = $form->values() )
{
try
{
$request = $values['rss_import_url']->request();
if ( $values['rss_import_auth_user'] or $values['rss_import_auth_pass'] )
{
$request = $request->login( $values['rss_import_auth_user'], $values['rss_import_auth_pass'] );
}
$response = $request->get();
if ( $response->httpResponseCode == 401 )
{
$form->error = \IPS\Member::loggedIn()->language()->addToStack( 'rss_import_auth' );
return $form;
}
$response = $response->decodeXml();
if ( !( $response instanceof \IPS\Xml\Rss ) and !( $response instanceof \IPS\Xml\Atom ) )
{
$form->error = \IPS\Member::loggedIn()->language()->addToStack( 'rss_import_invalid' );
return $form;
}
}
catch ( \IPS\Http\Request\Exception $e )
{
$form->error = \IPS\Member::loggedIn()->language()->addToStack( 'form_url_bad' );
return $form;
}
catch ( \Exception $e )
{
$form->error = \IPS\Member::loggedIn()->language()->addToStack( 'rss_import_invalid' );
return $form;
}
return $values;
}
return $form;
},
'rss_import_preview' => function( $data )
{
if ( isset( \IPS\Request::i()->continue ) )
{
return $data;
}
$request = \IPS\Http\Url::external( $data['rss_import_url'] )->request();
if ( $data['rss_import_auth_user'] or $data['rss_import_auth_pass'] )
{
$request = $request->login( $data['rss_import_auth_user'], $data['rss_import_auth_pass'] );
}
$preview = array();
foreach( $request->get()->decodeXml()->articles() as $article )
{
if ( \count( $preview ) > 9 )
{
break;
}
$preview[] = $article;
}
return \IPS\Theme::i()->getTemplate( 'feeds' )->importPreview( $preview );
},
'rss_import_app' => function ( $data ) {
$form = new \IPS\Helpers\Form( 'form', 'continue' );
$options = array();
foreach( \IPS\Application::allExtensions( 'core', 'RssImport' ) as $key => $class )
{
$options = array_merge( $options, $class->availableOptions() );
}
$form->add( new \IPS\Helpers\Form\Radio( 'rss_import_app_areas', NULL, FALSE, array( 'options' => $options ) ) );
if ( $values = $form->values() )
{
return $values;
}
return $form;
},
'rss_import_details' => function ( $data )
{
$import = new \IPS\core\Rss\Import;
$rss_import_app_areas = isset( \IPS\Request::i()->rss_import_app_areas ) ? \IPS\Request::i()->rss_import_app_areas : $data['rss_import_app_areas'];
$import->class = $rss_import_app_areas;
$extension = $import->_extension;
$request = \IPS\Http\Url::external( $data['rss_import_url'] )->request();
if ( $data['rss_import_auth_user'] or $data['rss_import_auth_pass'] )
{
$request = $request->login( $data['rss_import_auth_user'], $data['rss_import_auth_pass'] );
}
foreach( $request->get()->decodeXml()->articles() as $article )
{
if ( isset( $article['enclosure'] ) and isset( $article['enclosure']['url'] ) )
{
$import->has_enclosures = true;
break;
}
}
$form = new \IPS\Helpers\Form;
if ( ! empty( $data['rss_import_app_areas'] ) )
{
$form->hiddenValues['rss_import_app_areas'] = $data['rss_import_app_areas'];
}
$form->add( new \IPS\Helpers\Form\Node( 'rss_import_node_id', NULL, TRUE, $extension->nodeSelectorOptions( $import ) ) );
$form->add( new \IPS\Helpers\Form\Member( 'rss_import_member', \IPS\Member::loggedIn(), TRUE, array(), function( $val ){
if( !\is_array( $val ) )
{
$val = array( $val );
}
foreach( $val as $member )
{
if( $member instanceof \IPS\Member )
{
if( !$member->member_id )
{
throw new \InvalidArgumentException( 'form_member_bad' );
}
}
else
{
$testMember = \IPS\Member::load( $member );
if( !$testMember->member_id )
{
throw new \InvalidArgumentException( 'form_member_bad' );
}
}
}
} ) );
$form->add( new \IPS\Helpers\Form\Text( 'rss_import_showlink', \IPS\Member::loggedIn()->language()->addToStack('rss_import_showlink_default') ) );
$form->add( new \IPS\Helpers\Form\Text( 'rss_import_topic_pre', NULL, FALSE, array( 'trim' => FALSE ) ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'rss_import_auto_follow', NULL, FALSE, array(), NULL, NULL, NULL, 'rss_import_auto_follow' ) );
$extension->form( $form, $import );
if ( $values = $form->values() )
{
$import->enabled = TRUE;
$import->title = $request->get()->decodeXml()->title();
$import->url = (string) $data['rss_import_url'];
$import->node_id = $values['rss_import_node_id']->_id;
$import->member = $values['rss_import_member']->member_id;
$import->showlink = $values['rss_import_showlink'];
$import->topic_pre = $values['rss_import_topic_pre'];
$import->auth_user = ( $data['rss_import_auth_user'] or $data['rss_import_auth_pass'] ) ? $data['rss_import_auth_user'] : NULL;
$import->auth_pass = ( $data['rss_import_auth_user'] or $data['rss_import_auth_pass'] ) ? $data['rss_import_auth_pass'] : NULL;
$import->auto_follow = $values['rss_import_auto_follow'];
if ( $settings = $extension->saveForm( $values, $import ) )
{
$import->settings = $settings;
}
$import->save();
try
{
$import->run();
}
catch( \Exception $e )
{
\IPS\Output::i()->error( 'rss_run_error', '3F181/3', 500, '' );
}
\IPS\Db::i()->update( 'core_tasks', array( 'enabled' => 1 ), array( '`key`=?', 'rssimport' ) );
\IPS\Session::i()->log( 'acplog__node_created', array( (string) $import->title => TRUE, (string) $import->title => FALSE ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal('app=core&module=applications&controller=rss' ) );
}
return $form;
}
), \IPS\Http\Url::internal('app=core&module=applications&controller=rss&do=form') );
}
else
{
return parent::form();
}
}
/**
* Run
*
* @return void
*/
public function run()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'rss_run' );
\IPS\Session::i()->csrfCheck();
try
{
$feed = \IPS\core\Rss\Import::load( \IPS\Request::i()->id );
$feed->run();
}
catch ( \OutOfRangeException $e )
{
\IPS\Output::i()->error( 'node_error', '2F181/1', 404, '' );
}
catch ( \Exception $e )
{
\IPS\Output::i()->error( 'rss_run_error', '3F181/2', 500, '' );
}
\IPS\Session::i()->log( 'acplog__rss_ran', array( $feed->title => FALSE ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal('app=core&module=applications&controller=rss' ) );
}
}
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\bulkmail;
/* 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;
@@ -21,7 +21,12 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
* Bulk mail management
*/
class _bulkmail extends \IPS\Dispatcher\Controller
{
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -117,18 +122,8 @@ class _bulkmail extends \IPS\Dispatcher\Controller
$return['cancel'] = array(
'icon' => 'minus-circle',
'title' => 'cancel',
'link' => \IPS\Http\Url::internal( 'app=core&module=bulkmail&controller=bulkmail&do=cancel&id=' ) . $row['mail_id'],
'link' => \IPS\Http\Url::internal( 'app=core&module=bulkmail&controller=bulkmail&do=cancel&id=' . $row['mail_id'] )->csrf(),
);
$classToUse = \IPS\Email::classToUse( \IPS\Email::TYPE_BULK );
if( !$classToUse::REQUIRES_TIME_BREAK )
{
$return['resend'] = array(
'icon' => 'share',
'title' => 'continue_sending',
'link' => \IPS\Http\Url::internal( 'app=core&module=bulkmail&controller=bulkmail&resetTask=1&do=sendImmediately&id=' ) . $row['mail_id'],
);
}
}
else
{
@@ -196,6 +191,8 @@ class _bulkmail extends \IPS\Dispatcher\Controller
*/
public function cancel()
{
\IPS\Session::i()->csrfCheck();
/* Retrieve the bulk mail details for the log */
try
{
@@ -302,13 +299,13 @@ class _bulkmail extends \IPS\Dispatcher\Controller
$content = $mail->content;
foreach ( $mail->returnTagValues( 0, \IPS\Member::loggedIn() ) as $k => $v )
{
$content = str_replace( $k, $v, $content );
$content = str_replace( array( $k, urlencode( $k ) ), $v, $content );
}
/* Display */
$email = \IPS\Email::buildFromContent( $mail->subject, \IPS\Email::staticParseTextForEmail( $content, \IPS\Member::loggedIn()->language() ), NULL, \IPS\Email::TYPE_BULK )->setUnsubscribe( 'core', 'unsubscribeBulk' );
\IPS\Output::i()->sendOutput( $email->compileContent( 'html', \IPS\Member::loggedIn() ) );
\IPS\Output::i()->sendOutput( $email->compileContent( 'html', \IPS\Member::loggedIn(), NULL, FALSE ) );
}
/**
@@ -346,6 +343,8 @@ class _bulkmail extends \IPS\Dispatcher\Controller
*/
public function send()
{
\IPS\Session::i()->csrfCheck();
/* Retrieve the bulk mail details */
try
{
@@ -365,72 +364,8 @@ class _bulkmail extends \IPS\Dispatcher\Controller
\IPS\Session::i()->log( 'acplogs__bulkmail_sent', array( $mail->subject => FALSE ) );
/* And redirect */
$classToUse = \IPS\Email::classToUse( \IPS\Email::TYPE_BULK );
if( !$classToUse::REQUIRES_TIME_BREAK )
{
return $this->sendImmediately();
}
else
{
\IPS\Task::queue( 'core', 'Bulkmail', array( 'mail_id' => $mail->id ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=bulkmail&controller=bulkmail' ), 'bm_initiated' );
}
}
/**
* Send bulk mails immediately
*
* @return void
*/
public function sendImmediately()
{
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('bm_initiated');
\IPS\Output::i()->output = new \IPS\Helpers\MultipleRedirect(
\IPS\Http\Url::internal( "app=core&module=bulkmail&controller=bulkmail&do=sendImmediately&id=" . intval( \IPS\Request::i()->id ) ),
function( $data )
{
/* Retrieve the bulk mail details */
try
{
$mail = \IPS\core\BulkMail\Bulkmailer::load( (int) \IPS\Request::i()->id, NULL, 'mail_active=1' );
}
catch( \OutOfRangeException $e )
{
return NULL;
}
/* On first cycle return data */
if ( !is_array( $data ) )
{
return array( array( 'total' => $mail->getQuery( \IPS\core\BulkMail\Bulkmailer::GET_COUNT_ONLY )->first() ),
\IPS\Member::loggedIn()->language()->addToStack('bulkmail_sent_sofar', FALSE, array( 'sprintf' => array( $mail->subject, \IPS\Member::loggedIn()->language()->addToStack( 'bm_users', FALSE, array( 'pluralize' => array( 0 ) ) ) ) ) ),
1
);
}
/* Send the bulk mail */
$result = $mail->send();
/* If response is NULL there were no recipients. If response is 0 no emails were sent. Either way we're done. */
if( $result === NULL OR $result === 0 )
{
return NULL;
}
else
{
return array( $data,
\IPS\Member::loggedIn()->language()->addToStack('bulkmail_sent_sofar', FALSE, array( 'sprintf' => array( $mail->subject, \IPS\Member::loggedIn()->language()->addToStack( 'bm_users', FALSE, array( 'pluralize' => array( $mail->sentto ) ) ) ) ) ),
min( ( 100 / $data['total'] * $mail->offset ), 100 )
);
}
},
function()
{
/* We don't have to do anything special because BulkMailer will have already updated the task and disabled the bulk mail */
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=bulkmail&controller=bulkmail' ), 'bm_sent_succesfull' );
}
);
\IPS\Task::queue( 'core', 'Bulkmail', array( 'mail_id' => $mail->id ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=bulkmail&controller=bulkmail' ), 'bm_initiated' );
}
/**
+21 -14
View File
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\clubs;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _clubs extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -119,7 +124,7 @@ class _clubs extends \IPS\Dispatcher\Controller
$return['approve'] = array(
'title' => 'approve',
'icon' => 'check',
'link' => \IPS\Http\Url::internal("app=core&module=clubs&controller=clubs&do=approve&id={$row['id']}")
'link' => \IPS\Http\Url::internal("app=core&module=clubs&controller=clubs&do=approve&id={$row['id']}")->csrf()
);
}
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'clubs', 'clubs_delete' ) )
@@ -183,11 +188,10 @@ class _clubs extends \IPS\Dispatcher\Controller
protected function enable()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'clubs_settings_manage' );
\IPS\Session::i()->csrfCheck();
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => TRUE ), array( 'conf_key=?', 'clubs' ) );
\IPS\Settings::i()->clubs = TRUE;
unset( \IPS\Data\Store::i()->settings );
\IPS\Settings::i()->changeValues( array( 'clubs' => true ) );
\IPS\Db::i()->update( 'core_tasks', array( 'enabled' => 1 ), array( '`key`=?', 'clubrebuild' ) );
\IPS\Session::i()->log( 'acplog__club_settings' );
@@ -198,6 +202,7 @@ class _clubs extends \IPS\Dispatcher\Controller
/**
* Edit
*
* @csrfChecked Uses node form() 7 Oct 2019
* @return void
*/
protected function edit()
@@ -284,6 +289,7 @@ class _clubs extends \IPS\Dispatcher\Controller
protected function approve()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'clubs_edit' );
\IPS\Session::i()->csrfCheck();
/* Load Club */
try
@@ -329,7 +335,7 @@ class _clubs extends \IPS\Dispatcher\Controller
/* Ask what to do with each node */
$nodes = $club->nodes();
if ( count( $nodes ) )
if ( \count( $nodes ) )
{
$form = new \IPS\Helpers\Form( 'form', 'delete' );
$form->addMessage( 'club_delete_blurb' );
@@ -368,15 +374,16 @@ class _clubs extends \IPS\Dispatcher\Controller
}
foreach ( $nodesToQueue as $_node )
{
{
$_values = array();
if ( $v )
{
\IPS\Task::queue( 'core', 'DeleteOrMoveContent', array( 'class' => $nodeClass, 'id' => $_node->_id, 'moveToClass' => $nodeClass, 'moveTo' => $v->_id, 'deleteWhenDone' => TRUE, 'additional' => array() ) );
}
else
{
\IPS\Task::queue( 'core', 'DeleteOrMoveContent', array( 'class' => $nodeClass, 'id' => $_node->_id, 'deleteWhenDone' => TRUE, 'additional' => array() ) );
$_values['node_move_children'] = $v;
$_values['node_move_content'] = $v;
}
$_node->deleteOrMoveFormSubmit( $_values );
}
}
catch ( \Exception $e ) {}
@@ -510,7 +517,7 @@ class _clubs extends \IPS\Dispatcher\Controller
/* Do we have any children or content? */
if ( $node->hasChildren( NULL, NULL, TRUE ) or $node->showDeleteOrMoveForm() )
{
$form = $node->deleteOrMoveForm( FALSE, TRUE );
$form = $node->deleteOrMoveForm( FALSE );
if ( $values = $form->values() )
{
\IPS\Db::i()->delete( 'core_clubs_node_map', array( 'club_id=? AND node_class=? AND node_id=?', $club->id, $nodeClass, $node->_id ) );
@@ -12,7 +12,7 @@
namespace IPS\core\modules\admin\clubs;
/* 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;
@@ -23,6 +23,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _fields extends \IPS\Node\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Node Class
*/
Whitespace-only changes.
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\clubs;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _settings extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -42,7 +47,7 @@ class _settings extends \IPS\Dispatcher\Controller
{
$form = new \IPS\Helpers\Form;
$form->addHeader( 'club_settings' );
$form->add( new \IPS\Helpers\Form\YesNo( 'clubs_enabled_setting', \IPS\Settings::i()->clubs, FALSE, array( 'togglesOn' => array( 'clubs_default_sort', 'clubs_header', 'clubs_locations', 'clubs_modperms', 'clubs_require_approval', 'form_header_club_display_settings', 'form_header_club_moderation', 'clubs_default_view', 'clubs_allow_view_change', 'club_nodes_in_apps', 'form_header_clubs_paid_settings', 'clubs_paid_on' ) ) ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'clubs_enabled_setting', \IPS\Settings::i()->clubs, FALSE, array( 'togglesOn' => array( 'clubs_default_sort', 'clubs_header', 'clubs_locations', 'clubs_modperms', 'clubs_require_approval', 'form_header_club_display_settings', 'form_header_club_moderation', 'clubs_default_view', 'clubs_allow_view_change', 'club_nodes_in_apps', 'form_header_clubs_paid_settings', 'clubs_paid_on', '_allow_club_moderators' ) ) ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'clubs_require_approval', \IPS\Settings::i()->clubs_require_approval, FALSE, array(), NULL, NULL, NULL, 'clubs_require_approval' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'clubs_locations', \IPS\Settings::i()->clubs_locations, FALSE, array(), NULL, NULL, NULL, 'clubs_locations' ) );
if ( \IPS\Application::appIsEnabled( 'nexus' ) )
@@ -76,7 +81,8 @@ class _settings extends \IPS\Dispatcher\Controller
'1' => 'club_nodes_in_apps_on',
) ), NULL, NULL, NULL, 'club_nodes_in_apps' ) );
$form->addHeader( 'club_moderation' );
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'clubs_modperms', explode( ',', \IPS\Settings::i()->clubs_modperms ), FALSE, array( 'options' => array(
$form->add( new \IPS\Helpers\Form\YesNo( '_allow_club_moderators', ( \IPS\Settings::i()->clubs_modperms != -1 ), FALSE, array( 'togglesOn' => array( 'clubs_modperms' ) ), NULL, NULL, NULL, '_allow_club_moderators' ) );
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'clubs_modperms', ( \IPS\Settings::i()->clubs_modperms != -1 ) ? explode( ',', \IPS\Settings::i()->clubs_modperms ) : array(), FALSE, array( 'options' => array(
'pin' => 'club_modperm_pin',
'unpin' => 'club_modperm_unpin',
'edit' => 'club_modperm_edit',
@@ -94,19 +100,29 @@ class _settings extends \IPS\Dispatcher\Controller
) ), NULL, NULL, NULL, 'clubs_modperms' ) );
if ( $values = $form->values() )
{
{
$values['clubs'] = $values['clubs_enabled_setting'];
unset( $values['clubs_enabled_setting'] );
$values['clubs_modperms'] = implode( ',', $values['clubs_modperms'] );
/* If this setting is set to "No" then we're going to wipe out moderator permissions */
if( !$values['_allow_club_moderators'] )
{
$values['clubs_modperms'] = array();
}
$values['clubs_modperms'] = ( \count( $values['clubs_modperms'] ) ) ? implode( ',', $values['clubs_modperms'] ) : -1;
/* Get rid of fake settings */
unset( $values['clubs_enabled_setting'], $values['_allow_club_moderators'] );
if ( \IPS\Application::appIsEnabled( 'nexus' ) )
{
$values['clubs_paid_tax'] = $values['clubs_paid_tax'] ? $values['clubs_paid_tax']->id : 0;
$values['clubs_paid_gateways'] = is_array( $values['clubs_paid_gateways'] ) ? implode( ',', array_keys( $values['clubs_paid_gateways'] ) ) : $values['clubs_paid_gateways'];
$values['clubs_paid_gateways'] = \is_array( $values['clubs_paid_gateways'] ) ? implode( ',', array_keys( $values['clubs_paid_gateways'] ) ) : $values['clubs_paid_gateways'];
}
$form->saveAsSettings( $values );
\IPS\Session::i()->log( 'acplog__club_settings' );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal('app=core&module=clubs&controller=settings') );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal('app=core&module=clubs&controller=settings'), 'saved' );
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_clubs_settings');
@@ -0,0 +1,475 @@
<?php
/**
* @brief Statistics
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 06 Dec 2019
*/
namespace IPS\core\modules\admin\clubs;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Statistics
*/
class _stats extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
* @return void
*/
public function execute()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'stats_manage' );
parent::execute();
}
/**
* Display the statistics
*
* @return void
*/
protected function manage()
{
/* If clubs are not enabled, stop now */
if ( !\IPS\Settings::i()->clubs )
{
$availableTypes = array();
foreach ( \IPS\Member\Club::availableNodeTypes( NULL ) as $class )
{
$availableTypes[] = \IPS\Member::loggedIn()->language()->addToStack( $class::clubAcpTitle() );
}
$availableTypes = \IPS\Member::loggedIn()->language()->formatList( $availableTypes );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'clubs' )->disabled( $availableTypes );
return;
}
/* Generate the tabs */
$tabs = array( 'overview' => 'overview', 'total' => 'stats_club_activity', 'byclub' => 'stats_club_club_activity' );
$activeTab = ( isset( \IPS\Request::i()->tab ) and array_key_exists( \IPS\Request::i()->tab, $tabs ) ) ? \IPS\Request::i()->tab : 'overview';
/* Get the HTML to output */
$method = '_' . $activeTab;
$output = $this->$method();
/* And then print it */
if ( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->output = (string) $output;
}
else
{
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_clubs_stats');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $activeTab, (string) $output, \IPS\Http\Url::internal( "app=core&module=clubs&controller=stats" ) );
}
}
/**
* Club activity overview
*
* @return string
*/
protected function _overview()
{
$clubTypePieChart = $this->_getClubTypePieChart();
$clubSignups = $this->_getClubSignups();
$clubCreations = $this->_getClubCreations();
return \IPS\Theme::i()->getTemplate( 'clubs', 'core' )->statsOverview( $clubTypePieChart, $clubSignups, $clubCreations );
}
/**
* Get line chart showing club signups
*
* @return \IPS\Helpers\Chart
*/
protected function _getClubSignups()
{
$chart = new \IPS\Helpers\Chart\Database( \IPS\Http\Url::internal( 'app=core&module=clubs&controller=stats&fetch=signups' ), 'core_clubs_memberships', 'joined', '', array(
'isStacked' => TRUE,
'backgroundColor' => '#ffffff',
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
'lineWidth' => 1,
'areaOpacity' => 0.4
), 'ColumnChart', 'monthly', array( 'start' => 0, 'end' => 0 ), array( 'club_id', 'name' ), 'signups' );
$chart->joins[] = array( 'core_clubs', 'core_clubs.id=core_clubs_memberships.club_id' );
$chart->groupBy = 'club_id';
foreach( \IPS\Member\Club::clubs( NULL, NULL, 'name' ) as $club )
{
$chart->addSeries( $club->name, 'number', 'COUNT(*)', TRUE, $club->id );
}
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_clubs_signups');
$chart->availableTypes = array( 'ColumnChart', 'BarChart' );
if( \IPS\Request::i()->fetch == 'signups' AND \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->sendOutput( (string) $chart, 200 );
}
return $chart;
}
/**
* Get line chart showing club creations
*
* @return \IPS\Helpers\Chart
*/
protected function _getClubCreations()
{
$chart = new \IPS\Helpers\Chart\Database( \IPS\Http\Url::internal( 'app=core&module=clubs&controller=stats&fetch=clubs' ), 'core_clubs', 'created', '', array(
'isStacked' => FALSE,
'backgroundColor' => '#ffffff',
'colors' => array( '#10967e' ),
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
'lineWidth' => 1,
'areaOpacity' => 0.4
), 'ColumnChart', 'monthly', array( 'start' => 0, 'end' => 0 ), array(), 'signups' );
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('stats_clubs_creations'), 'number', 'COUNT(*)', FALSE );
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_clubs_creations');
$chart->availableTypes = array( 'ColumnChart', 'BarChart' );
if( \IPS\Request::i()->fetch == 'clubs' AND \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->sendOutput( (string) $chart, 200 );
}
return $chart;
}
/**
* Get pie chart showing club types
*
* @return \IPS\Helpers\Chart
*/
protected function _getClubTypePieChart()
{
$percentages = array();
$counts = array();
$total = 0;
foreach( \IPS\Db::i()->select( 'type, COUNT(*) as total', 'core_clubs', array(), NULL, NULL, array( 'type' ) ) as $clubs )
{
$counts[ $clubs['type'] ] = $clubs['total'];
$total += $clubs['total'];
}
foreach( $counts as $type => $typeTotal )
{
$percentages[ $type ] = number_format( round( 100 / $total * $typeTotal, 2 ), 2 );
}
return \IPS\Theme::i()->getTemplate( 'clubs', 'core' )->clubTypesBar( $percentages, $counts );
}
/**
* Total club activity
*
* @return \IPS\Helpers\Chart\Dynamic
*/
protected function _total()
{
$chart = new \IPS\Helpers\Chart\Callback(
\IPS\Http\Url::internal( 'app=core&module=clubs&controller=stats&tab=total' ),
array( $this, 'getResults' ),
'',
array(
'isStacked' => TRUE,
'backgroundColor' => '#ffffff',
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
'lineWidth' => 1,
'areaOpacity' => 0.4
),
'ColumnChart',
'monthly',
array( 'start' => ( new \IPS\DateTime )->sub( new \DateInterval('P6M') ), 'end' => \IPS\DateTime::create() ),
'total'
);
foreach( \IPS\Member\Club::availableNodeTypes( NULL ) as $nodeType )
{
$contentClass = $nodeType::$contentItemClass;
$chart->addSeries( \IPS\Member::loggedIn()->language()->get( $contentClass::$title . '_pl' ), 'number', TRUE );
}
$chart->title = \IPS\Member::loggedIn()->language()->addToStack( 'stats_club_activity' );
$chart->availableTypes = array( 'AreaChart', 'ColumnChart', 'BarChart' );
return $chart;
}
/**
* Activity by club
*
* @return string
*/
protected function _byclub()
{
$chart = new \IPS\Helpers\Chart\Callback(
\IPS\Http\Url::internal( 'app=core&module=clubs&controller=stats&tab=byclub' ),
array( $this, 'getResults' ),
'',
array(
'isStacked' => TRUE,
'backgroundColor' => '#ffffff',
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
'lineWidth' => 1,
'areaOpacity' => 0.4
),
'ColumnChart',
'monthly',
array( 'start' => ( new \IPS\DateTime )->sub( new \DateInterval('P6M') ), 'end' => \IPS\DateTime::create() ),
'byclub'
);
foreach( \IPS\Member\Club::clubs( NULL, NULL, 'name' ) as $club )
{
$chart->addSeries( $club->name, 'number', TRUE );
}
$chart->title = \IPS\Member::loggedIn()->language()->addToStack( 'stats_club_club_activity' );
$chart->availableTypes = array( 'AreaChart', 'ColumnChart', 'BarChart' );
return $chart;
}
/**
* Fetch the results
*
* @param \IPS\Helpers\Chart\Callback $chart Chart object
* @return array
*/
public function getResults( $chart )
{
/* Get the info we need */
$nodeTypes = \IPS\Member\Club::availableNodeTypes( NULL );
$clubNodes = array();
$classes = array();
$classMap = array();
$results = array();
foreach( $nodeTypes as $nodeType )
{
$clubNodes[ $nodeType ] = $nodeType::clubNodes( NULL );
$itemClass = $nodeType::$contentItemClass;
$classes[] = $itemClass;
$classMap[ $itemClass ] = $nodeType;
if( isset( $itemClass::$commentClass ) )
{
$classes[] = $itemClass::$commentClass;
$classMap[ $itemClass::$commentClass ] = $nodeType;
}
if( isset( $itemClass::$reviewClass ) )
{
$classes[] = $itemClass::$reviewClass;
$classMap[ $itemClass::$reviewClass ] = $nodeType;
}
}
/* If we are fetching by club, we need to build a map of containers to clubs */
if( mb_strpos( $chart->identifier, 'byclub' ) !== FALSE )
{
$clubNodeMap = array();
foreach( \IPS\Db::i()->select( '*', 'core_clubs_node_map' ) as $nodeMap )
{
$nodeClass = $nodeMap['node_class'];
$nodeClass = $nodeClass::$contentItemClass;
$clubNodeMap[ $nodeClass::$databaseTable . '-' . $nodeMap['node_id'] ] = $nodeMap['club_id'];
if( isset( $nodeClass::$commentClass ) )
{
$commentClass = $nodeClass::$commentClass;
$clubNodeMap[ $commentClass::$databaseTable . '-' . $nodeMap['node_id'] ] = $nodeMap['club_id'];
}
if( isset( $nodeClass::$reviewClass ) )
{
$reviewClass = $nodeClass::$reviewClass;
$clubNodeMap[ $reviewClass::$databaseTable . '-' . $nodeMap['node_id'] ] = $nodeMap['club_id'];
}
}
}
else
{
$groupByContainer = FALSE;
}
/* Get results */
foreach ( $classes as $class )
{
$where = array();
$join = NULL;
if( is_subclass_of( $class, '\IPS\Content\Comment' ) )
{
/* We're going to need a subquery... */
$parentClass = $class::$itemClass;
$where[] = array(
$class::$databasePrefix . $class::$databaseColumnMap['item'] . " IN(" .
(string) \IPS\Db::i()->select( $parentClass::$databasePrefix . $parentClass::$databaseColumnId, $parentClass::$databaseTable, array( \IPS\Db::i()->in( $parentClass::$databasePrefix . $parentClass::$databaseColumnMap['container'], array_keys( $clubNodes[ $classMap[ $class ] ] ) ) ) )
. ")"
);
if( mb_strpos( $chart->identifier, 'byclub' ) !== FALSE )
{
$groupByContainer = $parentClass::$databaseTable . '.' . $parentClass::$databasePrefix . $parentClass::$databaseColumnMap['container'];
$join = array( $parentClass::$databaseTable, $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['item'] . '=' . $parentClass::$databaseTable . '.' . $parentClass::$databasePrefix . $parentClass::$databaseColumnId );
}
/* We need to account for topic + first post not counting as two posts */
if( $parentClass::$firstCommentRequired === TRUE AND isset( $class::$databaseColumnMap['first'] ) )
{
$where[] = array( $class::$databasePrefix . $class::$databaseColumnMap['first'] . '=?', 0 );
}
}
else
{
$where[] = array( \IPS\Db::i()->in( $class::$databasePrefix . $class::$databaseColumnMap['container'], array_keys( $clubNodes[ $classMap[ $class ] ] ) ) );
if( mb_strpos( $chart->identifier, 'byclub' ) !== FALSE )
{
$groupByContainer = $class::$databasePrefix . $class::$databaseColumnMap['container'];
}
}
$stmt = $this->getSqlResults(
$class::$databaseTable,
$class::$databasePrefix . ( isset( $class::$databaseColumnMap['updated'] ) ? $class::$databaseColumnMap['updated'] : $class::$databaseColumnMap['date'] ),
$class::$databasePrefix . $class::$databaseColumnMap['author'],
$chart,
$where,
$groupByContainer,
$join
);
foreach( $stmt as $row )
{
if( !isset( $results[ $row['time'] ] ) )
{
$results[ $row['time'] ] = array(
'time' => $row['time']
);
if( mb_strpos( $chart->identifier, 'byclub' ) !== FALSE )
{
foreach( \IPS\Member\Club::clubs( NULL, NULL, 'name' ) as $club )
{
$results[ $row['time'] ][ $club->name ] = 0;
}
}
else
{
foreach( \IPS\Member\Club::availableNodeTypes( NULL ) as $nodeType )
{
$contentClass = $nodeType::$contentItemClass;
$results[ $row['time'] ][ \IPS\Member::loggedIn()->language()->get( $contentClass::$title . '_pl' ) ] = 0;
}
}
}
$classType = $classMap[ $class ];
if( mb_strpos( $chart->identifier, 'byclub' ) !== FALSE )
{
$results[ $row['time'] ][ \IPS\Member\Club::load( $clubNodeMap[ $class::$databaseTable . '-' . $row['container'] ] )->name ] += $row['total'];
}
else
{
$contentClass = $classMap[ $class ]::$contentItemClass;
$results[ $row['time'] ][ \IPS\Member::loggedIn()->language()->get( $contentClass::$title . '_pl' ) ] += $row['total'];
}
}
}
return $results;
}
/**
* Get SQL query/results
*
* @note Consolidated to reduce duplicated code
* @param string $table Database table
* @param string $date Date column
* @param string $author Author column
* @param object $chart Chart
* @param array $where Where clause
* @param bool|string $groupByContainer If a string is provided, it must be a column name to group by in addition to time
* @param NULL|array $join Join data, if needed (only used when $groupByContainer is set)
* @return array
*/
protected function getSqlResults( $table, $date, $author, $chart, $where = array(), $groupByContainer = FALSE, $join = NULL )
{
/* What's our SQL time? */
switch ( $chart->timescale )
{
case 'daily':
$timescale = '%Y-%c-%e';
break;
case 'weekly':
$timescale = '%x-%v';
break;
case 'monthly':
$timescale = '%Y-%c';
break;
}
$where[] = array( "{$date}>?", 0 );
if ( $chart->start )
{
$where[] = array( "{$date}>?", $chart->start->getTimestamp() );
}
if ( $chart->end )
{
$where[] = array( "{$date}<?", $chart->end->getTimestamp() );
}
/* First we need to get search index activity */
$fromUnixTime = "FROM_UNIXTIME( IFNULL( {$date}, 0 ) )";
if ( !$chart->timezoneError and \IPS\Member::loggedIn()->timezone and \in_array( \IPS\Member::loggedIn()->timezone, \IPS\DateTime::getTimezoneIdentifiers() ) )
{
$fromUnixTime = "CONVERT_TZ( {$fromUnixTime}, @@session.time_zone, '" . \IPS\Db::i()->escape_string( \IPS\Member::loggedIn()->timezone ) . "' )";
}
if( $groupByContainer !== FALSE )
{
$stmt = \IPS\Db::i()->select( "DATE_FORMAT( {$fromUnixTime}, '{$timescale}' ) AS time, COUNT(*) as total, {$groupByContainer} as container", $table, $where, 'time ASC', NULL, array( 'time', 'container' ) );
if( $join !== NULL )
{
$stmt = $stmt->join( ...$join );
}
}
else
{
$stmt = \IPS\Db::i()->select( "DATE_FORMAT( {$fromUnixTime}, '{$timescale}' ) AS time, COUNT(*) as total", $table, $where, 'time ASC', NULL, array( 'time' ) );
}
return $stmt;
}
}
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\customization;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _ajax extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Return a CSS or HTML menu
*
@@ -189,7 +194,7 @@ class _ajax extends \IPS\Dispatcher\Controller
}
$where[] = array( \IPS\Db::i()->in( "MD5( CONCAT_WS( ',', template_app, template_location, template_group, template_name ) )", $uniqueIds ) );
}
if ( in_array( 'inherited', explode( ',', \IPS\Request::i()->filters ) ) )
if ( \in_array( 'inherited', explode( ',', \IPS\Request::i()->filters ) ) )
{
try
{
@@ -201,15 +206,15 @@ class _ajax extends \IPS\Dispatcher\Controller
catch( \OutOfRangeException $e ) { }
}
array_push( $parents, 0 );
if ( !in_array( 'unique', explode( ',', \IPS\Request::i()->filters ) ) )
if ( !\in_array( 'unique', explode( ',', \IPS\Request::i()->filters ) ) )
{
$where[] = array( 'template_user_added=0' );
}
if ( !in_array( 'unmodified', explode( ',', \IPS\Request::i()->filters ) ) )
if ( !\in_array( 'unmodified', explode( ',', \IPS\Request::i()->filters ) ) )
{
$where[] = array( 'template_user_edited>0 or template_added_to > 0' );
}
if ( !in_array( 'modified', explode( ',', \IPS\Request::i()->filters ) ) )
if ( !\in_array( 'modified', explode( ',', \IPS\Request::i()->filters ) ) )
{
$where[] = array( 'template_added_to=0' );
}
@@ -229,7 +234,7 @@ class _ajax extends \IPS\Dispatcher\Controller
{
$outOfDate = $result['template_user_edited'] < \IPS\Application::load( $result['template_app'] )->long_version;
if ( ( $outOfDate and !in_array( 'outofdate', explode( ',', \IPS\Request::i()->filters ) ) ) or ( !$outOfDate and !in_array( 'modified', explode( ',', \IPS\Request::i()->filters ) ) ) )
if ( ( $outOfDate and !\in_array( 'outofdate', explode( ',', \IPS\Request::i()->filters ) ) ) or ( !$outOfDate and !\in_array( 'modified', explode( ',', \IPS\Request::i()->filters ) ) ) )
{
unset( $return[ $result['template_app'] ][ $result['template_location'] ][ $result['template_group'] ][ $result['template_name'] ] );
continue;
@@ -276,7 +281,7 @@ class _ajax extends \IPS\Dispatcher\Controller
}
$where[] = array( \IPS\Db::i()->in( "MD5( CONCAT_WS( ',', css_app, css_location, css_path, css_name ) )", $uniqueIds ) );
}
if ( in_array( 'inherited', explode( ',', \IPS\Request::i()->filters ) ) )
if ( \in_array( 'inherited', explode( ',', \IPS\Request::i()->filters ) ) )
{
try
{
@@ -288,11 +293,11 @@ class _ajax extends \IPS\Dispatcher\Controller
catch( \OutOfRangeException $e ) { }
}
array_push( $parents, 0 );
if ( !in_array( 'unique', explode( ',', \IPS\Request::i()->filters ) ) )
if ( !\in_array( 'unique', explode( ',', \IPS\Request::i()->filters ) ) )
{
$where[] = array( 'css_added_to=?', \IPS\Request::i()->id );
}
if ( !in_array( 'unmodified', explode( ',', \IPS\Request::i()->filters ) ) )
if ( !\in_array( 'unmodified', explode( ',', \IPS\Request::i()->filters ) ) )
{
$where[] = array( 'css_user_edited>0' );
}
@@ -310,7 +315,7 @@ class _ajax extends \IPS\Dispatcher\Controller
{
$outOfDate = $result['css_user_edited'] < \IPS\Application::load( $result['css_app'] )->long_version;
if ( !$result['css_added_to'] and ( ( $outOfDate and !in_array( 'outofdate', explode( ',', \IPS\Request::i()->filters ) ) ) or ( !$outOfDate and !in_array( 'modified', explode( ',', \IPS\Request::i()->filters ) ) ) ) )
if ( !$result['css_added_to'] and ( ( $outOfDate and !\in_array( 'outofdate', explode( ',', \IPS\Request::i()->filters ) ) ) or ( !$outOfDate and !\in_array( 'modified', explode( ',', \IPS\Request::i()->filters ) ) ) ) )
{
unset( $return[ $result['css_app'] ][ $result['css_location'] ][ $result['css_path'] ][ $result['css_name'] ] );
continue;
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\customization;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _emails extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -60,7 +65,7 @@ class _emails extends \IPS\Dispatcher\Controller
$table->quickSearch = function( $val )
{
$matches = \IPS\Member::loggedIn()->language()->searchCustom( 'emailtpl_', $val, TRUE );
if ( count( $matches ) )
if ( \count( $matches ) )
{
return '(' . \IPS\Db::i()->in( '`template_name`', array_keys( $matches ) ) . " OR `template_name` LIKE '%{$val}%')";
}
@@ -98,8 +103,8 @@ class _emails extends \IPS\Dispatcher\Controller
$return['revert'] = array(
'icon' => 'undo',
'title' => 'revert',
'link' => \IPS\Http\Url::internal( 'app=core&module=customization&controller=emails&do=revert&key=' ) . $row['template_key'],
'data' => array( 'delete' => '', 'delete-warning' => \IPS\Member::loggedIn()->language()->addToStack('email_revert_confirm') )
'link' => \IPS\Http\Url::internal( 'app=core&module=customization&controller=emails&do=revert&key=' . $row['template_key'] )->csrf(),
'data' => array( 'confirm' => '', 'confirmMessage' => \IPS\Member::loggedIn()->language()->addToStack('email_revert_confirm') )
);
$return['export'] = array(
@@ -184,7 +189,7 @@ class _emails extends \IPS\Dispatcher\Controller
{
$xml = \IPS\Xml\SimpleXML::loadFile( $values['email_template_file'] );
if( !count($xml->template) )
if( !\count($xml->template) )
{
\IPS\Output::i()->error( 'email_template_badform', '1S128/4', 403, '' );
}
@@ -248,11 +253,11 @@ class _emails extends \IPS\Dispatcher\Controller
\IPS\Db::i()->update( 'core_email_templates', array( 'template_edited' => 0 ), array( 'template_id=?', $template['template_id'] ) );
/* Rebuild template */
$this->buildTemplate( $template );
$this->_buildTemplate( $template );
/* Log and redirect */
\IPS\Session::i()->log( 'acplog__emailtpl_reverted', array( $template['template_name'] => FALSE ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=emails' ), 'reverted' );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=emails' ), 'reverted_emailtpl' );
}
/**
@@ -317,7 +322,7 @@ class _emails extends \IPS\Dispatcher\Controller
\IPS\Db::i()->update( 'core_email_templates', array( 'template_edited' => 1 ), array( 'template_id=?', $template['template_id'] ) );
}
$this->buildTemplate( $save );
$this->_buildTemplate( $save );
\IPS\Session::i()->log( 'acplogs__emailtpl_edited', array( $template['template_name'] => FALSE ) );
@@ -326,7 +331,7 @@ class _emails extends \IPS\Dispatcher\Controller
/* Output */
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('emailtpl_edit');
\IPS\Output::i()->output = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'customization', 'core', 'admin' ) ), 'email' ) );;
\IPS\Output::i()->output = $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'customization', 'core', 'admin' ), 'email' ) );;
}
/**
@@ -335,7 +340,7 @@ class _emails extends \IPS\Dispatcher\Controller
* @param array $template Template data from core_email_templates
* @return void
*/
public function buildTemplate( $template )
protected function _buildTemplate( $template )
{
$htmlFunction = 'namespace IPS\Theme;' . "\n" . \IPS\Theme::compileTemplate( $template['template_content_html'], "email_html_{$template['template_app']}_{$template['template_name']}", $template['template_data'] );
$ptFunction = 'namespace IPS\Theme;' . "\n" . \IPS\Theme::compileTemplate( $template['template_content_plaintext'], "email_plaintext_{$template['template_app']}_{$template['template_name']}", $template['template_data'] );
@@ -0,0 +1,256 @@
<?php
/**
* @brief icons
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 25 Jul 2018
*/
namespace IPS\core\modules\admin\customization;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* icons
*/
class _icons extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
* @return void
*/
public function execute()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'theme_sets_manage' );
parent::execute();
}
/**
* Show the form
*
* @return void
*/
protected function manage()
{
$form = new \IPS\Helpers\Form;
/* 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' ) ) );
/* Sharer logos, allows multiple images to be uploaded */
$shareLogos = \IPS\Settings::i()->icons_sharer_logo ? json_decode( \IPS\Settings::i()->icons_sharer_logo, true ) : array();
$form->add( new \IPS\Helpers\Form\Upload( 'icons_sharer_logo', \count( $shareLogos ) ? array_map( function( $val ) { return \IPS\File::get( 'core_Icons', $val ); }, $shareLogos ) : array(), FALSE, array( 'image' => true, 'storageExtension' => 'core_Icons', 'multiple' => true ) ) );
/* 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' ) ) );
/* Safari pinned tabs icon and highlight color */
$form->add( new \IPS\Helpers\Form\Upload( 'icons_mask_icon', \IPS\Settings::i()->icons_mask_icon ? \IPS\File::get( 'core_Icons', \IPS\Settings::i()->icons_mask_icon ) : NULL, FALSE, array( 'allowedFileTypes' => array( 'svg' ), 'storageExtension' => 'core_Icons', 'obscure' => FALSE ) ) );
$form->add( new \IPS\Helpers\Form\Color( 'icons_mask_color', \IPS\Settings::i()->icons_mask_color, FALSE ) );
/* And finally, additional manifest and livetile details */
$manifestDetails = json_decode( \IPS\Settings::i()->manifest_details, TRUE );
$form->add( new \IPS\Helpers\Form\YesNo( 'configure_manifest', \count( $manifestDetails ) > 0, FALSE, array(
'togglesOn' => array( 'manifest_shortname', 'manifest_fullname', 'manifest_description', 'manifest_defaultapp', 'manifest_themecolor', 'manifest_bgcolor', 'manifest_display', 'manifest_custom_url_toggle' ),
) ) );
$form->add( new \IPS\Helpers\Form\Text( 'manifest_shortname', ( isset( $manifestDetails['short_name'] ) ) ? $manifestDetails['short_name'] : '', FALSE, array(), NULL, NULL, NULL, 'manifest_shortname' ) );
$form->add( new \IPS\Helpers\Form\Text( 'manifest_fullname', ( isset( $manifestDetails['name'] ) ) ? $manifestDetails['name'] : '', FALSE, array(), NULL, NULL, NULL, 'manifest_fullname' ) );
$form->add( new \IPS\Helpers\Form\TextArea( 'manifest_description', ( isset( $manifestDetails['description'] ) ) ? $manifestDetails['description'] : '', FALSE, array(), NULL, NULL, NULL, 'manifest_description' ) );
$formStartUrl = '';
if( isset( $manifestDetails['start_url'] ) )
{
$formStartUrl = str_replace( 'index.php?/', '', \IPS\Http\Url\Friendly::fixComponentPath( $manifestDetails['start_url'] ) );
}
$form->add( new \IPS\Helpers\Form\YesNo( 'manifest_custom_url_toggle', ( isset( $manifestDetails['start_url'] ) and empty( $formStartUrl ) ? FALSE : TRUE ), FALSE, array(
'togglesOn' => array( 'manifest_short_url' ),
), NULL, NULL, NULL, 'manifest_custom_url_toggle' ) );
$form->add( new \IPS\Helpers\Form\Text( 'manifest_short_url', $formStartUrl, FALSE, array(), function( $val )
{
if ( $val and \IPS\Request::i()->manifest_custom_url_toggle_checkbox )
{
if ( mb_substr( $val, -1 ) !== '/' )
{
$val .= '/';
}
$response = \IPS\Http\Url::external( \IPS\Http\Url::baseUrl() . ( \IPS\Settings::i()->htaccess_mod_rewrite ? $val : 'index.php?/' . $val ) )->request( NULL, NULL, FALSE )->get();
if ( $response->httpResponseCode != 200 and $response->httpResponseCode != 303 and ( \IPS\Settings::i()->site_online OR $response->httpResponseCode != 503 ) )
{
throw new \LogicException( 'pwa_start_url_incorrect' );
}
}
}, \IPS\Http\Url::baseUrl() . ( !\IPS\Settings::i()->htaccess_mod_rewrite ? 'index.php?/' : '' ), NULL, 'manifest_short_url' ) );
$form->add( new \IPS\Helpers\Form\Color( 'manifest_themecolor', ( isset( $manifestDetails['theme_color'] ) ) ? $manifestDetails['theme_color'] : NULL, FALSE, array(), NULL, NULL, NULL, 'manifest_themecolor' ) );
$form->add( new \IPS\Helpers\Form\Color( 'manifest_bgcolor', ( isset( $manifestDetails['background_color'] ) ) ? $manifestDetails['background_color'] : NULL, FALSE, array(), NULL, NULL, NULL, 'manifest_bgcolor' ) );
$form->add( new \IPS\Helpers\Form\Radio( 'manifest_display', ( isset( $manifestDetails['display'] ) ) ? $manifestDetails['display'] : 'standalone', FALSE, array( 'options' => array( 'fullscreen' => 'manifest_fullscreen', 'standalone' => 'manifest_standalone', 'minimal-ui' => 'manifest_minimalui', 'browser' => 'manifest_browser' ) ), NULL, NULL, NULL, 'manifest_display' ) );
/* We've submitted, check our values! */
if ( $values = $form->values() )
{
/* Favicon is easy, we just store the string value of the file object */
$values['icons_favicon'] = (string) $values['icons_favicon'];
/* Sharer logos are easy too, except it's an array of images instead of a single image */
if( \count( $values['icons_sharer_logo'] ) )
{
$values['icons_sharer_logo'] = json_encode( array_map( function( $val ){ return (string) $val; }, $values['icons_sharer_logo'] ) );
}
$path = \IPS\Http\Url::createFromString( \IPS\Http\Url::baseUrl() )->data[ \IPS\Http\Url::COMPONENT_PATH ];
$startUrl = $path ?? '';
if ( $values['manifest_custom_url_toggle'] !== FALSE and ! empty( $values['manifest_short_url'] ) )
{
$startUrl = '/' . trim( $values['manifest_short_url'], '/' ) . '/';
if( !empty( $path ) )
{
$startUrl = '/' . trim( $path . ( !\IPS\Settings::i()->htaccess_mod_rewrite ? 'index.php?/' : '' ) . ltrim( $values['manifest_short_url'], '/' ), '/' ) . '/';
}
}
/* Homescreen icons are the hardest part, as we need to generate different sizes.. */
$values = static::processApplicationIcon( $values, $homeScreen );
/* We need the string value of this uploaded file as well */
$values['icons_mask_icon'] = (string) $values['icons_mask_icon'];
/* Finally, handle the manifest details */
$values['manifest_details'] = array();
if( $values['configure_manifest'] )
{
$values['manifest_details']['short_name'] = $values['manifest_shortname'];
$values['manifest_details']['start_url'] = $startUrl;
$values['manifest_details']['name'] = $values['manifest_fullname'];
$values['manifest_details']['description'] = $values['manifest_description'];
$values['manifest_details']['theme_color'] = $values['manifest_themecolor'];
$values['manifest_details']['background_color'] = $values['manifest_bgcolor'];
$values['manifest_details']['display'] = $values['manifest_display'];
}
unset( $values['configure_manifest'], $values['manifest_shortname'], $values['manifest_fullname'], $values['manifest_description'], $values['manifest_display'], $values['manifest_bgcolor'], $values['manifest_themecolor'], $values['manifest_custom_url_toggle'], $values['manifest_short_url'] );
$values['manifest_details'] = json_encode( $values['manifest_details'] );
/* Save the settings */
$form->saveAsSettings( $values );
/* Clear guest page caches */
\IPS\Data\Cache::i()->clearAll();
/* Clear manifest and ie browser data stores */
unset( \IPS\Data\Store::i()->manifest, \IPS\Data\Store::i()->iebrowserconfig );
/* And log */
\IPS\Session::i()->log( 'acplogs__icons_and_logos' );
/* And Redirect */
\IPS\Output::i()->redirect( $this->url , 'saved' );
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_customization_icons');
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate( 'global' )->block( 'menu__core_customization_icons', $form );
}
/**
* Process application icon
*
* @param array $values Values from form submission
* @param array $homeScreen Existing values, if any
* @return array
*/
public static function processApplicationIcon( $values, $homeScreen = array() )
{
if( $values['icons_homescreen'] )
{
$sizes = array(
'android-chrome-36x36' => array( 36, 36 ),
'android-chrome-48x48' => array( 48, 48 ),
'android-chrome-72x72' => array( 72, 72 ),
'android-chrome-96x96' => array( 96, 96 ),
'android-chrome-144x144' => array( 144, 144 ),
'android-chrome-192x192' => array( 192, 192 ),
'android-chrome-256x256' => array( 256, 256 ),
'android-chrome-384x384' => array( 384, 384 ),
'android-chrome-512x512' => array( 512, 512 ),
'msapplication-square70x70logo' => array( 128, 128 ),
'msapplication-TileImage' => array( 144, 144 ),
'msapplication-square150x150logo' => array( 270, 270 ),
'msapplication-wide310x150logo' => array( 558, 558 ),
'msapplication-square310x310logo' => array( 558, 270 ),
'apple-touch-icon-57x57' => array( 57, 57 ),
'apple-touch-icon-60x60' => array( 60, 60 ),
'apple-touch-icon-72x72' => array( 72, 72 ),
'apple-touch-icon-76x76' => array( 76, 76 ),
'apple-touch-icon-114x114' => array( 114, 114 ),
'apple-touch-icon-120x120' => array( 120, 120 ),
'apple-touch-icon-144x144' => array( 144, 144 ),
'apple-touch-icon-152x152' => array( 152, 152 ),
'apple-touch-icon-180x180' => array( 180, 180 ),
);
$setting = array( 'original' => (string) $values['icons_homescreen'] );
foreach( $sizes as $filename => $_sizes )
{
try
{
$image = \IPS\Image::create( $values['icons_homescreen']->contents() );
if( $image::exifSupported() )
{
$image->setExifData( $values['icons_homescreen']->contents() );
}
$image->crop( $_sizes[0], $_sizes[1] );
$setting[ $filename ] = array(
'url' => (string) \IPS\File::create( 'core_Icons', $filename . '.png', (string) $image, NULL, TRUE, NULL, FALSE ),
'width' => $image->width,
'height' => $image->height
);
}
catch ( \Exception $e ) { }
}
$values['icons_homescreen'] = json_encode( $setting );
}
else
{
/* Delete any images that may already exist */
foreach( $homeScreen as $key => $image )
{
try
{
\IPS\File::get( 'core_Icons', ( $key == 'original' ) ? $image : $image['url'] )->delete();
}
catch( \Exception $e ){}
}
$values['icons_homescreen'] = '';
}
return $values;
}
}
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\customization;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _themes extends \IPS\Node\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Node Class
*/
@@ -31,6 +36,11 @@ class _themes extends \IPS\Node\Controller
* @brief If true, will prevent any item from being moved out of its current parent, only allowing them to be reordered within their current parent
*/
protected $lockParents = TRUE;
/**
* Title can contain HTML?
*/
public $_titleHtml = TRUE;
/**
* Description can contain HTML?
@@ -72,18 +82,30 @@ class _themes extends \IPS\Node\Controller
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('theme_designers_mode_title') )
);
}
\IPS\Output::i()->sidebar['actions']['add'] = array(
'primary' => true,
'icon' => 'plus',
'title' => 'theme_set_add_button',
'link' => \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=add' ),
'title' => 'add',
'link' => \IPS\Http\Url::internal( ( \IPS\DEMO_MODE ) ? 'app=core&module=customization&controller=themes&do=form&type=vse' : 'app=core&module=customization&controller=themes&do=add' ),
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('theme_set_add_button') )
);
parent::manage();
}
/**
* Allow overloading to change how the title is displayed in the tree
*
* @param $node \IPS\Node Node
* @return string
*/
protected static function nodeTitle( $node )
{
return \IPS\Theme::i()->getTemplate('customization')->themeRowTitle( $node );
}
/**
* Get Root Buttons
*
@@ -101,6 +123,8 @@ class _themes extends \IPS\Node\Controller
*/
public function revertThemeSetting()
{
\IPS\Session::i()->csrfCheck();
$theme = \IPS\Theme::load( \IPS\Request::i()->id );
$setting = NULL;
$value = NULL;
@@ -191,7 +215,7 @@ class _themes extends \IPS\Node\Controller
/* Switching on */
if ( isset( $values['theme_designers_mode'] ) and $values['theme_designers_mode'] )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=designersmodeon' ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=designersmodeon' )->csrf() );
}
else
{
@@ -200,7 +224,7 @@ class _themes extends \IPS\Node\Controller
\IPS\Data\Store::i()->designers_mode_theme_sync = ( $values['theme_designers_themes'] == 0 ) ? NULL : array_keys( $values['theme_designers_themes'] );
\IPS\Data\Store::i()->designers_mode_theme_wipe = $values['theme_designers_mode_wipe'];
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=designersmodeoff' ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=designersmodeoff' )->csrf() );
}
else
{
@@ -223,25 +247,32 @@ class _themes extends \IPS\Node\Controller
*/
public function designersmodeoff()
{
\IPS\Session::i()->csrfCheck();
if ( \IPS\NO_WRITES or \IPS\CIC )
{
\IPS\Output::i()->error( 'no_writes', '1C163/B', 403, '' );
}
\IPS\Output::i()->output = new \IPS\Helpers\MultipleRedirect(
\IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=designersmodeoff' ),
\IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=designersmodeoff' )->csrf(),
function( $data )
{
/* Is this the first cycle? */
if ( ! is_array( $data ) )
if ( ! \is_array( $data ) )
{
$toImport = array();
foreach( \IPS\Theme::themes() as $id => $theme )
{
if( !$theme->id )
{
continue;
}
if ( isset( \IPS\Data\Store::i()->designers_mode_theme_sync ) )
{
if ( \IPS\Data\Store::i()->designers_mode_theme_sync !== NULL and ! in_array( $id, \IPS\Data\Store::i()->designers_mode_theme_sync ) )
if ( \IPS\Data\Store::i()->designers_mode_theme_sync !== NULL and ! \in_array( $id, \IPS\Data\Store::i()->designers_mode_theme_sync ) )
{
continue;
}
@@ -257,11 +288,11 @@ class _themes extends \IPS\Node\Controller
$extensions = array();
foreach ( \IPS\Application::allExtensions( 'core', 'DesignersMode', NULL, NULL, NULL, TRUE ) as $class )
{
$extensions[] = get_class( $class );
$extensions[] = \get_class( $class );
}
/* Start importing */
$data = array( 'toImport' => count( $toImport ) ? true : false, 'extensions' => $extensions, 'extra' => array() );
$data = array( 'toImport' => \count( $toImport ) ? true : false, 'extensions' => $extensions, 'extra' => array() );
/* Could be too large for a simple URL */
$_SESSION['theme_dev_import'] = $toImport;
@@ -269,7 +300,7 @@ class _themes extends \IPS\Node\Controller
return array( $data, \IPS\Member::loggedIn()->language()->addToStack('processing') );
}
if ( count( $data['extensions'] ) )
if ( \count( $data['extensions'] ) )
{
/* Do this first */
$array = $data['extensions'];
@@ -298,11 +329,11 @@ class _themes extends \IPS\Node\Controller
reset( $toImport );
$themeId = key( $toImport );
$theme = \IPS\Theme::load( $themeId );
$theme = \IPS\Theme\Advanced\Theme::load( $themeId );
$app = key( $toImport[ $themeId ] );
$type = array_shift( $toImport[ $themeId ][ $app ] );
if( !count( $toImport[ $themeId ][ $app ] ) )
if( !\count( $toImport[ $themeId ][ $app ] ) )
{
unset( $toImport[ $themeId ][ $app ] );
}
@@ -353,22 +384,22 @@ class _themes extends \IPS\Node\Controller
}
break;
case 'html':
\IPS\Theme::i()->importDevHtml( $app, $themeId );
$theme->importDevHtml( $app, $themeId );
break;
case 'css':
\IPS\Theme::i()->importDevCss( $app, $themeId );
$theme->importDevCss( $app, $themeId );
break;
case 'resources':
\IPS\Theme::i()->importDevResources( $app, $themeId );
$theme->importDevResources( $app, $themeId );
break;
}
if ( ! count( $toImport[ $themeId ] ) )
if ( ! \count( $toImport[ $themeId ] ) )
{
unset( $toImport[ $themeId ] );
}
if ( ! count( $toImport ) )
if ( ! \count( $toImport ) )
{
$data['toImport'] = false;
}
@@ -394,7 +425,7 @@ class _themes extends \IPS\Node\Controller
{
\IPS\Theme\Advanced\Theme::removeThemeDirectory( \IPS\ROOT_PATH . "/themes" );
}
else if ( is_array( \IPS\Data\Store::i()->designers_mode_theme_sync ) )
else if ( \is_array( \IPS\Data\Store::i()->designers_mode_theme_sync ) )
{
foreach( \IPS\Data\Store::i()->designers_mode_theme_sync as $id )
{
@@ -426,6 +457,8 @@ class _themes extends \IPS\Node\Controller
*/
public function designersmodeon()
{
\IPS\Session::i()->csrfCheck();
if ( \IPS\NO_WRITES or \IPS\CIC )
{
\IPS\Output::i()->error( 'no_writes', '1C163/A', 403, '' );
@@ -463,11 +496,11 @@ class _themes extends \IPS\Node\Controller
}
\IPS\Output::i()->output = new \IPS\Helpers\MultipleRedirect(
\IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=designersmodeon' ),
\IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=designersmodeon' )->csrf(),
function( $data )
{
/* Is this the first cycle? */
if ( !is_array( $data ) )
if ( !\is_array( $data ) )
{
if ( ! is_dir( \IPS\ROOT_PATH . "/themes" ) )
{
@@ -493,6 +526,11 @@ class _themes extends \IPS\Node\Controller
{
foreach ( \IPS\Theme\Advanced\Theme::getToBuild() as $id )
{
if( !$id )
{
continue;
}
$toBuild[$id] = array( 'html' => 'html', 'css' => 'css', 'resources' => 'resources' );
}
}
@@ -503,21 +541,21 @@ class _themes extends \IPS\Node\Controller
{
if ( $class->toBuild() === TRUE )
{
$extensions[] = get_class( $class );
$extensions[] = \get_class( $class );
}
}
/* Start importing */
$data = array( 'buildingDesignersFiles' => true, 'toBuild' => $toBuild, 'extensions' => $extensions, 'totalItems' => count( array_keys( $toBuild ) ) + count( array_keys( $extensions ) ), 'totalDone' => 0, 'extra' => array() );
$data = array( 'buildingDesignersFiles' => true, 'toBuild' => $toBuild, 'extensions' => $extensions, 'totalItems' => \count( array_keys( $toBuild ) ) + \count( array_keys( $extensions ) ), 'totalDone' => 0, 'extra' => array() );
return array( $data, \IPS\Member::loggedIn()->language()->addToStack('theme_dm_building') );
}
/* Grab something to build */
$msg = NULL;
if ( count( $data['extensions'] ) or count( $data['toBuild'] ) )
if ( \count( $data['extensions'] ) or \count( $data['toBuild'] ) )
{
if ( count( $data['extensions'] ) )
if ( \count( $data['extensions'] ) )
{
/* Do this first */
$array = $data['extensions'];
@@ -541,14 +579,14 @@ class _themes extends \IPS\Node\Controller
$msg = \IPS\Member::loggedIn()->language()->addToStack( 'theme_dm_building_extensions' );
}
else if ( count( $data['toBuild'] ) )
else if ( \count( $data['toBuild'] ) )
{
reset( $data['toBuild'] );
$themeId = key( $data['toBuild'] );
$type = array_shift( $data['toBuild'][$themeId] );
if( !count( $data['toBuild'][ $themeId ] ) )
if( !\count( $data['toBuild'][ $themeId ] ) )
{
unset( $data['toBuild'][ $themeId ] );
}
@@ -577,7 +615,8 @@ class _themes extends \IPS\Node\Controller
$data['totalDone']++;
}
return array( $data, $msg, ( $data['totalDone'] / $data['totalItems'] * 100 ) );
$percent = ( $data['totalDone'] / $data['totalItems'] * 100 );
return array( $data, $msg, ( $percent <= 100 ? $percent : 100 ) );
}
else
{
@@ -603,7 +642,7 @@ class _themes extends \IPS\Node\Controller
*/
public function resources()
{
$setId = intval( \IPS\Request::i()->set_id );
$setId = \intval( \IPS\Request::i()->set_id );
try
{
@@ -625,7 +664,7 @@ class _themes extends \IPS\Node\Controller
$table->parsers = array(
'resource_data' => function( $val, $row ) use ($set)
{
if ( in_array( mb_substr( $row['resource_name'], mb_strrpos( $row['resource_name'], '.' ) + 1 ), \IPS\Image::$imageExtensions ) )
if ( \in_array( mb_substr( $row['resource_name'], mb_strrpos( $row['resource_name'], '.' ) + 1 ), array_merge( \IPS\Image::$imageExtensions, array( 'svg' ) ) ) )
{
$row['resource_filename'] = $set->resource( ltrim( $row['resource_path'], '/' ) . $row['resource_name'], $row['resource_app'], $row['resource_location'] );
}
@@ -634,7 +673,8 @@ class _themes extends \IPS\Node\Controller
},
'resource_name' => function( $val, $row ) use ($set)
{
if ( in_array( mb_substr( $row['resource_name'], mb_strrpos( $row['resource_name'], '.' ) + 1 ), \IPS\Image::$imageExtensions ) )
if ( \in_array( mb_substr( $row['resource_name'], mb_strrpos( $row['resource_name'], '.' ) + 1 ), array_merge( \IPS\Image::$imageExtensions, array( 'svg' ) ) ) )
{
$row['resource_filename'] = $set->resource( ltrim( $row['resource_path'], '/' ) . $row['resource_name'], $row['resource_app'], $row['resource_location'] );
}
@@ -701,7 +741,7 @@ class _themes extends \IPS\Node\Controller
/* Make sure the user confirmed the deletion */
\IPS\Request::i()->confirmedDelete();
$id = intval( \IPS\Request::i()->id );
$id = \intval( \IPS\Request::i()->id );
try
{
@@ -751,7 +791,7 @@ class _themes extends \IPS\Node\Controller
*/
public function resourceForm()
{
$id = intval( \IPS\Request::i()->id );
$id = \intval( \IPS\Request::i()->id );
try
{
@@ -906,6 +946,8 @@ class _themes extends \IPS\Node\Controller
*/
public function launchvse()
{
\IPS\Session::i()->csrfCheck();
$theme = \IPS\Theme::load( \IPS\Request::i()->id );
if ( $theme->by_skin_gen AND \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'customization', 'theme_easy_editor' ) )
@@ -929,6 +971,13 @@ class _themes extends \IPS\Node\Controller
*/
public function convertToCustom()
{
if ( \IPS\DEMO_MODE )
{
\IPS\Output::i()->error( 'demo_mode_function_blocked', '1C163/J', 403, '' );
}
\IPS\Session::i()->csrfCheck();
$theme = \IPS\Theme::load( \IPS\Request::i()->id );
$theme->by_skin_gen = 0;
$theme->save();
@@ -943,6 +992,53 @@ class _themes extends \IPS\Node\Controller
}
}
/**
* Manual Theme Upload
*
* @return void
*/
public function upload()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'theme_download_upload' );
$form = new \IPS\Helpers\Form( 'form', 'next' );
$form->addMessage('themes_manual_install_warning');
$form->add(
new \IPS\Helpers\Form\Upload(
'core_theme_set_new_import', NULL, FALSE, array(
'allowedFileTypes' => array( 'xml' ),
'temporary' => TRUE
), NULL, NULL, NULL, 'core_theme_set_new_import'
)
);
if ( $values = $form->values() )
{
/* Move it to a temporary location */
$tempFile = tempnam( \IPS\TEMP_DIRECTORY, 'IPS' );
move_uploaded_file( $values['core_theme_set_new_import'], $tempFile );
$max = \IPS\Db::i()->select( 'MAX(set_order)', 'core_themes' )->first();
/* Create a default theme */
$theme = new \IPS\Theme;
$theme->parent_array = '[]';
$theme->child_array = '[]';
$theme->parent_id = 0;
$theme->by_skin_gen = 0;
$theme->editor_skin = 'ips';
$theme->order = $max + 1;
$theme->save();
$theme->copyResourcesFromSet();
/* Initate a redirector */
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=import' )->setQueryString( array( 'file' => $tempFile, 'key' => md5_file( $tempFile ), 'id' => $theme->id ) )->csrf() );
}
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->block( 'theme_set_add_button', $form, FALSE );
}
/**
* Add a theme dialog
*
@@ -950,63 +1046,30 @@ class _themes extends \IPS\Node\Controller
*/
public function add()
{
$form = new \IPS\Helpers\Form( 'form', 'next' );
$form->addTab( 'core_theme_add_new' );
$form->add( new \IPS\Helpers\Form\Radio(
'core_theme_add_theme_type',
NULL,
FALSE,
array( 'options'=> array( 'vse' => 'core_theme_add_theme_vse', 'custom' => 'core_theme_add_theme_custom' ),
'descriptions' => array( 'vse' => 'core_theme_add_theme_vse_desc', 'custom' => 'core_theme_add_theme_custom_desc' ) ),
NULL,
NULL,
NULL,
'core_theme_add_theme_type'
) );
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'customization', 'theme_download_upload' ) )
if ( \IPS\DEMO_MODE )
{
$form->addTab( 'core_theme_add_upload' );
$form->add(
new \IPS\Helpers\Form\Upload(
'core_theme_set_new_import', NULL, FALSE, array(
'allowedFileTypes' => array( 'xml' ),
'temporary' => TRUE
), NULL, NULL, NULL, 'core_theme_set_new_import'
)
);
/* We only allow easy mode themes */
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=form&type=vse' ) );
}
$form = new \IPS\Helpers\Form( 'form', 'next' );
$form->addMessage('themes_create_blurb');
$form->add( new \IPS\Helpers\Form\Radio(
'core_theme_add_theme_type',
NULL,
FALSE,
array( 'options'=> array( 'vse' => 'core_theme_add_theme_vse', 'custom' => 'core_theme_add_theme_custom' ),
'descriptions' => array( 'vse' => 'core_theme_add_theme_vse_desc', 'custom' => 'core_theme_add_theme_custom_desc' ) ),
NULL,
NULL,
NULL,
'core_theme_add_theme_type'
) );
if ( $values = $form->values() )
{
if ( isset( $values['core_theme_set_new_import'] ) and $values['core_theme_set_new_import'] )
{
/* Move it to a temporary location */
$tempFile = tempnam( \IPS\TEMP_DIRECTORY, 'IPS' );
move_uploaded_file( $values['core_theme_set_new_import'], $tempFile );
$max = \IPS\Db::i()->select( 'MAX(set_order)', 'core_themes' )->first();
/* Create a default theme */
$theme = new \IPS\Theme;
$theme->parent_array = '[]';
$theme->child_array = '[]';
$theme->parent_id = 0;
$theme->by_skin_gen = 0;
$theme->editor_skin = 'ips';
$theme->order = $max + 1;
$theme->save();
$theme->copyResourcesFromSet();
/* Initate a redirector */
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=import' )->setQueryString( array( 'file' => $tempFile, 'key' => md5_file( $tempFile ), 'id' => $theme->id ) ) );
}
else
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=form&type=' . $values['core_theme_add_theme_type'] ) );
}
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=form&type=' . $values['core_theme_add_theme_type'] ) );
}
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->block( 'theme_set_add_button', $form, FALSE );
@@ -1019,13 +1082,15 @@ class _themes extends \IPS\Node\Controller
*/
public function import()
{
\IPS\Session::i()->csrfCheck();
if ( !file_exists( \IPS\Request::i()->file ) or md5_file( \IPS\Request::i()->file ) !== \IPS\Request::i()->key )
{
\IPS\Output::i()->error( 'generic_error', '3C130/1', 500, '' );
}
\IPS\Output::i()->output = new \IPS\Helpers\MultipleRedirect(
\IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=import' )->setQueryString( array( 'file' => \IPS\Request::i()->file, 'key' => \IPS\Request::i()->key, 'id' => \IPS\Request::i()->id ) ),
\IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=import' )->setQueryString( array( 'file' => \IPS\Request::i()->file, 'key' => \IPS\Request::i()->key, 'id' => \IPS\Request::i()->id ) )->csrf(),
function( $data )
{
$set = \IPS\Theme::load( \IPS\Request::i()->id );
@@ -1047,7 +1112,7 @@ class _themes extends \IPS\Node\Controller
}
/* Is this the first batch? */
if ( !is_array( $data ) )
if ( !\is_array( $data ) )
{
$_SESSION['theme_import'] = array( 'css' => array(), 'isNewSet' => false );
@@ -1206,7 +1271,7 @@ class _themes extends \IPS\Node\Controller
}
}
if ( count( $files ) )
if ( \count( $files ) )
{
$value = implode( ',', $files );
}
@@ -1312,7 +1377,7 @@ class _themes extends \IPS\Node\Controller
/* Theme resources should be raw binary data everywhere (filesystem and DB) except in the theme XML download where they are base64 encoded. */
$content = base64_decode( $xml->readString() );
$name = \IPS\Theme::makeBuiltTemplateLookupHash( $appKey, $xml->getAttribute('location'), $xml->getAttribute('path') ) . '_' . $xml->getAttribute('name');
$fileName = (string) \IPS\File::create( 'core_Theme', $name, $content, 'set_resources_' . \IPS\Request::i()->id, FALSE, NULL, FALSE );
$fileName = (string) \IPS\File::create( 'core_Theme', $name, $content, 'set_resources_' . \IPS\Request::i()->id, TRUE, NULL, FALSE );
try
{
@@ -1343,7 +1408,7 @@ class _themes extends \IPS\Node\Controller
'resource_data' => $content,
'resource_added' => time(),
'resource_filename' => $fileName,
'resource_user_edited' => intval( $xml->getAttribute('user_edited') )
'resource_user_edited' => \intval( $xml->getAttribute('user_edited') )
) );
break;
}
@@ -1414,26 +1479,22 @@ class _themes extends \IPS\Node\Controller
$type = $xml->getAttribute('type');
$name = $xml->getAttribute('name');
if( $set->logo[ $type ]['url'] !== null )
/* Sharer logo and favicons may be present from themes generated in 4.3 and earlier, but are no longer used */
if( $type != 'sharer' AND $type != 'favicon' )
{
\IPS\File::get( 'core_Theme', $set->logo[ $type ]['url'] )->delete();
}
if( $set->logo[ $type ]['url'] !== null )
{
\IPS\File::get( 'core_Theme', $set->logo[ $type ]['url'] )->delete();
}
$url = (string) \IPS\File::create( 'core_Theme', $name, base64_decode( $xml->readString() ) );
$url = (string) \IPS\File::create( 'core_Theme', $name, base64_decode( $xml->readString() ) );
if( $type != 'favicon' )
{
$image = \IPS\Image::create( base64_decode( $xml->readString() ) );
$width = $image->width;
$height = $image->height;
}
else
{
$width = 0;
$height = 0;
}
$set->saveSet( array( 'logo' => array( $type => array( 'url' => $url, 'width' => $width, 'height' => $height ) ) ) );
$set->saveSet( array( 'logo' => array( $type => array( 'url' => $url, 'width' => $width, 'height' => $height ) ) ) );
}
$xml->next();
}
@@ -1465,12 +1526,18 @@ class _themes extends \IPS\Node\Controller
}
\IPS\Theme::setThemeCustomized( $set->id );
$set->resetCacheKey();
/* Conflicts to fix? */
if ( \IPS\Db::i()->select( 'count(*)', 'core_theme_conflict', array( 'conflict_set_id=?', \IPS\Request::i()->id ) )->first() )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=conflicts&id=' . \IPS\Request::i()->id ) );
}
elseif( $set->marketplace_id )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=marketplace&controller=marketplace&do=viewFile&id=' . $set->marketplace_id ), 'theme_now_installed' );
}
else
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes' ) );
@@ -1489,7 +1556,7 @@ class _themes extends \IPS\Node\Controller
/* Check permission */
\IPS\Dispatcher::i()->checkAcpPermission( 'theme_templates_manage' );
$id = intval( \IPS\Request::i()->id );
$id = \intval( \IPS\Request::i()->id );
$form = new \IPS\Helpers\Form( 'form', 'theme_conflict_save' );
$themeSet = \IPS\Theme::load( $id );
$templates = array();
@@ -1503,7 +1570,7 @@ class _themes extends \IPS\Node\Controller
$conflicts[ $data['conflict_type'] ][ $key ] = $data;
}
if ( isset( $conflicts['template'] ) AND count( $conflicts['template'] ) )
if ( isset( $conflicts['template'] ) AND \count( $conflicts['template'] ) )
{
$templates = iterator_to_array( \IPS\Db::i()->select(
'*, MD5( CONCAT( template_app, \'.\', template_location, \'.\', template_group, \'.\', template_name ) ) as bit_key',
@@ -1516,7 +1583,7 @@ class _themes extends \IPS\Node\Controller
$conflicts['template'] = array();
}
if ( isset( $conflicts['css'] ) AND count( $conflicts['css'] ) )
if ( isset( $conflicts['css'] ) AND \count( $conflicts['css'] ) )
{
$css = iterator_to_array( \IPS\Db::i()->select(
'*, MD5( CONCAT( css_app, \'.\', css_location, \'.\', css_path, \'.\', css_name ) ) as bit_key',
@@ -1593,7 +1660,7 @@ class _themes extends \IPS\Node\Controller
}
}
if ( count( $conflictIds ) )
if ( \count( $conflictIds ) )
{
/* Get conflict data */
foreach( \IPS\Db::i()->select( '*', 'core_theme_conflict', \IPS\Db::i()->in( 'conflict_id', array_keys( $conflictIds ) ) )->setKeyField( 'conflict_id' ) as $cid => $data )
@@ -1604,7 +1671,7 @@ class _themes extends \IPS\Node\Controller
}
}
if ( isset( $conflicts['template'] ) AND count( $conflicts['template'] ) )
if ( isset( $conflicts['template'] ) AND \count( $conflicts['template'] ) )
{
$templates = iterator_to_array( \IPS\Db::i()->select(
'*, MD5( CONCAT( template_app, \'.\', template_location, \'.\', template_group, \'.\', template_name ) ) as bit_key',
@@ -1613,7 +1680,7 @@ class _themes extends \IPS\Node\Controller
)->setKeyField( 'bit_key' ) );
}
if ( isset( $conflicts['css'] ) AND count( $conflicts['css'] ) )
if ( isset( $conflicts['css'] ) AND \count( $conflicts['css'] ) )
{
$css = iterator_to_array( \IPS\Db::i()->select(
'*, MD5( CONCAT( css_app, \'.\', css_location, \'.\', css_path, \'.\', css_name ) ) as bit_key',
@@ -1660,7 +1727,7 @@ class _themes extends \IPS\Node\Controller
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes' ), 'completed' );
}
if ( count( $conflicts['css'] ) OR count( $conflicts['template'] ) )
if ( \count( $conflicts['css'] ) OR \count( $conflicts['template'] ) )
{
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'system/diff.css', 'core', 'admin' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'codemirror/codemirror.js', 'core', 'interface' ) );
@@ -1687,7 +1754,7 @@ class _themes extends \IPS\Node\Controller
/* Check permission */
\IPS\Dispatcher::i()->checkAcpPermission( 'theme_templates_manage' );
$id = intval( \IPS\Request::i()->id );
$id = \intval( \IPS\Request::i()->id );
/* Build form */
$form = new \IPS\Helpers\Form();
@@ -1758,7 +1825,7 @@ class _themes extends \IPS\Node\Controller
if ( $values = $form->values() )
{
$id = intval( \IPS\Request::i()->id );
$id = \intval( \IPS\Request::i()->id );
$save = array( 'app' => $values['theme_css_app'],
'location' => $values['theme_css_location'],
'path' => ( $values['theme_css_group_type'] == 'existing' ) ? $values['theme_css_group_existing'] : $values['theme_css_group_new'],
@@ -1798,13 +1865,18 @@ class _themes extends \IPS\Node\Controller
*/
public function importForm()
{
$id = intval( \IPS\Request::i()->id );
$id = \intval( \IPS\Request::i()->id );
/* Check permission */
\IPS\Dispatcher::i()->checkAcpPermission( 'theme_download_upload' );
$themeSet = \IPS\Theme::load( $id );
$apps = array();
if( $themeSet->marketplace_id )
{
\IPS\Output::i()->error( 'app_upload_marketplace_only', '2C163/K', 403, '' );
}
foreach( \IPS\Application::applications() as $key => $data )
{
@@ -1823,7 +1895,7 @@ class _themes extends \IPS\Node\Controller
move_uploaded_file( $values['core_theme_set_new_import'], $tempFile );
/* Initate a redirector */
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=import' )->setQueryString( array( 'file' => $tempFile, 'key' => md5_file( $tempFile ), 'id' => \IPS\Request::i()->id) ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=import' )->setQueryString( array( 'file' => $tempFile, 'key' => md5_file( $tempFile ), 'id' => \IPS\Request::i()->id) )->csrf() );
}
/* Display */
@@ -1837,7 +1909,7 @@ class _themes extends \IPS\Node\Controller
*/
public function exportForm()
{
$id = intval( \IPS\Request::i()->id );
$id = \intval( \IPS\Request::i()->id );
/* Check permission */
\IPS\Dispatcher::i()->checkAcpPermission( 'theme_download_upload' );
@@ -1853,7 +1925,7 @@ class _themes extends \IPS\Node\Controller
$form = new \IPS\Helpers\Form( 'form', 'theme_set_export_button' );
$storedAuthor = ( isset( \IPS\Data\Store::i()->theme_stored_author ) AND is_array( \IPS\Data\Store::i()->theme_stored_author ) ) ? \IPS\Data\Store::i()->theme_stored_author : null;
$storedAuthor = ( isset( \IPS\Data\Store::i()->theme_stored_author ) AND \is_array( \IPS\Data\Store::i()->theme_stored_author ) ) ? \IPS\Data\Store::i()->theme_stored_author : null;
$form->addHeader( \IPS\Member::loggedIn()->language()->addToStack('theme_set_export_title', FALSE, array( 'sprintf' => array( $themeSet->_title ) ) ) );
@@ -1861,7 +1933,7 @@ class _themes extends \IPS\Node\Controller
{
if ( ! isset( \IPS\Request::i()->rebuildDone ) )
{
$url = \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=devImportBeforeExport&id=' . $id );
$url = \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=devImportBeforeExport&id=' . $id )->csrf();
$form->addMessage( \IPS\Member::loggedIn()->language()->addToStack('theme_export_designer_mode_build_message', NULL, array( 'sprintf' => array( $url ) ) ), 'ipsMessage ipsMessage_info', FALSE );
}
@@ -1878,7 +1950,7 @@ class _themes extends \IPS\Node\Controller
$authorName = $values['theme_template_export_author_name'] ? $values['theme_template_export_author_name'] : $themeSet->author_name;
$authorUrl = $values['theme_template_export_author_url'] ? $values['theme_template_export_author_url'] : $themeSet->author_url;
$version = $values['theme_template_export_version'] ? $values['theme_template_export_version'] : $themeSet->version;
$longVersion = is_int( $values['theme_template_export_long_version'] ) ? $values['theme_template_export_long_version'] : $themeSet->long_version;
$longVersion = \is_int( $values['theme_template_export_long_version'] ) ? $values['theme_template_export_long_version'] : $themeSet->long_version;
$updateCheck = $values['theme_update_check'] ? $values['theme_update_check'] : $themeSet->update_check;
\IPS\Data\Store::i()->theme_stored_author = array(
@@ -1929,7 +2001,7 @@ class _themes extends \IPS\Node\Controller
{
$logos = json_decode( $themeSet->logo_data, TRUE );
if( is_array( $logos ) )
if( \is_array( $logos ) )
{
foreach ( $logos as $file => $data )
{
@@ -1981,7 +2053,7 @@ class _themes extends \IPS\Node\Controller
/* Templates */
$templates = $themeSet->getRawTemplates( $appDir, '', '', \IPS\Theme::RETURN_ALL );
if ( isset( $templates[ $appDir->directory ] ) AND count( $templates[ $appDir->directory ] ) )
if ( isset( $templates[ $appDir->directory ] ) AND \count( $templates[ $appDir->directory ] ) )
{
foreach( $templates[ $appDir->directory ] as $loc => $lv )
{
@@ -2006,7 +2078,7 @@ class _themes extends \IPS\Node\Controller
foreach( $templates[ $appDir->directory ][ $loc ][ $group ][ $name ] as $k => $v )
{
if ( in_array( \substr( $k, 9 ), array( 'location', 'group', 'name', 'data' ) ) )
if ( \in_array( \substr( $k, 9 ), array( 'location', 'group', 'name', 'data' ) ) )
{
$xml->startAttribute($k);
$xml->text( $v );
@@ -2050,7 +2122,7 @@ class _themes extends \IPS\Node\Controller
foreach( $css[ $appDir->directory ][ $loc ][ $path ][ $name ] as $k => $v )
{
if ( in_array( \substr( $k, 4 ), array( 'location', 'path', 'name', 'attributes' ) ) )
if ( \in_array( \substr( $k, 4 ), array( 'location', 'path', 'name', 'attributes' ) ) )
{
$xml->startAttribute($k);
$xml->text( $v );
@@ -2130,7 +2202,7 @@ class _themes extends \IPS\Node\Controller
/* Custom fields */
$settings = \IPS\Db::i()->select( '*', 'core_theme_settings_fields', array( 'sc_set_id=? AND sc_app=?', $themeSet->id, $appDir->directory ) )->join( 'core_theme_settings_values', 'sc_id=sv_id' );
if ( count( $settings ) )
if ( \count( $settings ) )
{
foreach ( $settings as $row )
{
@@ -2141,7 +2213,7 @@ class _themes extends \IPS\Node\Controller
foreach( $row as $k => $v )
{
if ( !in_array( $k, array( 'sc_updated', 'sv_value', 'sc_app', 'sv_id' ) ) )
if ( !\in_array( $k, array( 'sc_updated', 'sv_value', 'sc_app', 'sv_id' ) ) )
{
$xml->startAttribute( $k );
$xml->text( $v );
@@ -2269,10 +2341,12 @@ class _themes extends \IPS\Node\Controller
}
catch ( \OutOfRangeException $e ) {}
$parentResult = parent::delete();
/* Clear guest page caches */
\IPS\Data\Cache::i()->clearAll();
return parent::delete();
return $parentResult;
}
/**
@@ -2286,7 +2360,7 @@ class _themes extends \IPS\Node\Controller
/* Check permission */
\IPS\Dispatcher::i()->checkAcpPermission( 'theme_templates_manage' );
$id = intval( \IPS\Request::i()->id );
$id = \intval( \IPS\Request::i()->id );
$locations = array();
$groups = array();
@@ -2361,7 +2435,7 @@ class _themes extends \IPS\Node\Controller
if ( $values = $form->values() )
{
$id = intval( \IPS\Request::i()->id );
$id = \intval( \IPS\Request::i()->id );
$save = array( 'set_id' => $id,
'app' => $values['theme_template_app'],
'location' => $values['theme_template_location'],
@@ -2410,6 +2484,8 @@ class _themes extends \IPS\Node\Controller
*/
public function build()
{
\IPS\Session::i()->csrfCheck();
$set = \IPS\Theme::load( \IPS\Request::i()->id );
/* Resources has to come before CSS otherwise CSS url()s are out of date as resource build changes resource URL after CSS has been built */
@@ -2435,8 +2511,8 @@ class _themes extends \IPS\Node\Controller
\IPS\Request::i()->confirmedDelete();
$type = \IPS\Request::i()->t_type;
$id = intval( \IPS\Request::i()->id );
$item_id = intval( \IPS\Request::i()->t_item_id );
$id = \intval( \IPS\Request::i()->id );
$item_id = \intval( \IPS\Request::i()->t_item_id );
try
{
@@ -2503,6 +2579,8 @@ class _themes extends \IPS\Node\Controller
*/
public function saveTemplate()
{
\IPS\Session::i()->csrfCheck();
$type = \IPS\Request::i()->t_type;
$editorName = 'editor_' . \IPS\Request::i()->t_key;
$url = array();
@@ -2512,8 +2590,8 @@ class _themes extends \IPS\Node\Controller
'location' => \IPS\Request::i()->t_location,
'group' => \IPS\Request::i()->t_group,
'name' => \IPS\Request::i()->t_name,
'set_id' => intval( \IPS\Request::i()->id ),
'item_id' => intval( \IPS\Request::i()->t_item_id ),
'set_id' => \intval( \IPS\Request::i()->id ),
'item_id' => \intval( \IPS\Request::i()->t_item_id ),
'key' => \IPS\Request::i()->t_key,
'content' => \IPS\Request::i()->$editorName );
try
@@ -2561,7 +2639,7 @@ class _themes extends \IPS\Node\Controller
foreach( $save as $k => $v )
{
if ( in_array( $k, array( 'app', 'location', 'group', 'name' ) ) )
if ( \in_array( $k, array( 'app', 'location', 'group', 'name' ) ) )
{
$url[] = 't_' . $k . '=' . $v;
$log[ $k . ': ' . $v ] = FALSE;
@@ -2611,12 +2689,14 @@ class _themes extends \IPS\Node\Controller
*/
public function devImportMultipleRedirect( $finalUrl=NULL )
{
\IPS\Session::i()->csrfCheck();
\IPS\Output::i()->output = new \IPS\Helpers\MultipleRedirect(
\IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=devImportMultipleRedirect&id=' . \IPS\Request::i()->id . '&finalUrl=' . base64_encode( $finalUrl ) ),
\IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=devImportMultipleRedirect&id=' . \IPS\Request::i()->id . '&finalUrl=' . base64_encode( $finalUrl ) )->csrf(),
function( $data )
{
/* Is this the first cycle? */
if ( ! is_array( $data ) )
if ( ! \is_array( $data ) )
{
$toImport = array();
@@ -2632,7 +2712,7 @@ class _themes extends \IPS\Node\Controller
}
/* Grab something to build */
if ( count( $data['toImport'] ) )
if ( \count( $data['toImport'] ) )
{
reset( $data['toImport'] );
$app = key( $data['toImport'] );
@@ -2643,18 +2723,43 @@ class _themes extends \IPS\Node\Controller
switch( $type )
{
case 'html':
\IPS\Theme::i()->importDevHtml( $app, ( ( \IPS\Theme::designersModeEnabled() !== true ) ? 0 : \IPS\Request::i()->id ) );
\IPS\Theme::load( ( ( \IPS\IN_DEV AND ! \IPS\Request::i()->id ) ? 1 : \IPS\Request::i()->id ) )->compileTemplates( $app );
if( \IPS\Theme::designersModeEnabled() !== true )
{
\IPS\Theme::i()->importDevHtml( $app, 0 );
\IPS\Theme::load( 1 )->compileTemplates( $app );
}
else
{
\IPS\Theme\Advanced\Theme::load( \IPS\Request::i()->id )->importDevHtml( $app, \IPS\Request::i()->id );
\IPS\Theme::load( \IPS\Request::i()->id )->compileTemplates( $app );
}
unset( $data['toImport'][ $app ][0] );
break;
case 'css':
\IPS\Theme::i()->importDevCss( $app, ( ( \IPS\Theme::designersModeEnabled() !== true ) ? 0 : \IPS\Request::i()->id ) );
\IPS\Theme::load( ( ( \IPS\IN_DEV AND ! \IPS\Request::i()->id ) ? 1 : \IPS\Request::i()->id ) )->compileCss( $app );
if( \IPS\Theme::designersModeEnabled() !== true )
{
\IPS\Theme::i()->importDevCss( $app, 0 );
\IPS\Theme::load( 1 )->compileCss( $app );
}
else
{
\IPS\Theme\Advanced\Theme::load( \IPS\Request::i()->id )->importDevCss( $app, \IPS\Request::i()->id );
\IPS\Theme::load( \IPS\Request::i()->id )->compileCss( $app );
}
unset( $data['toImport'][ $app ][1] );
break;
case 'resources':
\IPS\Theme::i()->importDevResources( $app, ( ( \IPS\Theme::designersModeEnabled() !== true ) ? 1 : \IPS\Request::i()->id ) );
\IPS\Theme::load( ( ( \IPS\IN_DEV AND ! \IPS\Request::i()->id ) ? 1 : \IPS\Request::i()->id ) )->buildResourceMap( $app );
if( \IPS\Theme::designersModeEnabled() !== true )
{
\IPS\Theme::i()->importDevResources( $app, 0 );
\IPS\Theme::load( 1 )->buildResourceMap( $app );
}
else
{
\IPS\Theme\Advanced\Theme::load( \IPS\Request::i()->id )->importDevResources( $app, \IPS\Request::i()->id );
\IPS\Theme::load( \IPS\Request::i()->id )->buildResourceMap( $app );
}
/* All done */
unset( $data['toImport'][ $app ] );
break;
@@ -2688,7 +2793,7 @@ class _themes extends \IPS\Node\Controller
\IPS\Dispatcher::i()->checkAcpPermission( 'theme_templates_manage' );
/* Grab groups */
$id = intval( \IPS\Request::i()->id );
$id = \intval( \IPS\Request::i()->id );
if ( $id === 1 AND \IPS\IN_DEV )
{
@@ -2735,7 +2840,7 @@ class _themes extends \IPS\Node\Controller
\IPS\Dispatcher::i()->checkAcpPermission( 'theme_templates_manage' );
/* Grab groups */
$id = intval( \IPS\Request::i()->id );
$id = \intval( \IPS\Request::i()->id );
if ( $id === 1 AND \IPS\IN_DEV )
{
@@ -2789,7 +2894,7 @@ class _themes extends \IPS\Node\Controller
\IPS\Dispatcher::i()->checkAcpPermission( 'theme_templates_manage' );
/* Grab groups */
$id = intval( \IPS\Request::i()->id );
$id = \intval( \IPS\Request::i()->id );
$t_type = \IPS\Request::i()->t_type ?: 'templates';
$t_app = \IPS\Request::i()->t_app ?: 'core';
$t_location = \IPS\Request::i()->t_location ?: ( ( \IPS\Request::i()->t_app ) ? false : 'front' );
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\customization;
/* 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;
@@ -21,7 +21,12 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
* themesettings
*/
class _themesettings extends \IPS\Dispatcher\Controller
{
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -41,7 +46,7 @@ class _themesettings extends \IPS\Dispatcher\Controller
),
array(
NULL,
\IPS\Member::loggedIn()->language()->addToStack( 'core_theme_set_title_' . intval( \IPS\Request::i()->set_id ) )
\IPS\Member::loggedIn()->language()->addToStack( 'core_theme_set_title_' . \intval( \IPS\Request::i()->set_id ) )
)
);
}
@@ -97,14 +102,14 @@ class _themesettings extends \IPS\Dispatcher\Controller
$return = array();
foreach( \IPS\Db::i()->select( '*', 'core_theme_settings_fields', array( 'sc_set_id=? AND sc_tab_key=?', $theme->id, $activeTab ), 'sc_order' ) as $row )
{
$return[ $row['sc_id'] ] = call_user_func( array( $self, '_settingRow' ), $row );
$return[ $row['sc_id'] ] = $self->_settingRow( $row );
}
return $return;
},
// getRow
function( $id ) use ( $self )
{
return call_user_func( array( $self, '_settingRow' ), \IPS\Db::i()->select( '*', 'core_theme_settings_fields', array( 'sc_id=?', $id ) )->first() );
return $self->_settingRow( \IPS\Db::i()->select( '*', 'core_theme_settings_fields', array( 'sc_id=?', $id ) )->first() );
},
// getRowParentId
function()
@@ -202,6 +207,8 @@ class _themesettings extends \IPS\Dispatcher\Controller
*/
public function reorder()
{
\IPS\Session::i()->csrfCheck();
/* Normalise AJAX vs non-AJAX */
if( isset( \IPS\Request::i()->ajax_order ) )
{
@@ -250,8 +257,8 @@ class _themesettings extends \IPS\Dispatcher\Controller
public function form()
{
$current = NULL;
$setId = intval( \IPS\Request::i()->set_id );
$id = intval( \IPS\Request::i()->id );
$setId = \intval( \IPS\Request::i()->set_id );
$id = \intval( \IPS\Request::i()->id );
$set = \IPS\Theme::load( $setId );
if ( $id )
@@ -288,6 +295,12 @@ class _themesettings extends \IPS\Dispatcher\Controller
),
function( $val )
{
/* Check key format */
if( !preg_match( '/^[a-zA-Z0-9_]+$/', $val ) )
{
throw new \InvalidArgumentException('core_theme_settings_error_key_disallowed_characters');
}
/* Make sure key is unique */
try
{
@@ -313,7 +326,7 @@ class _themesettings extends \IPS\Dispatcher\Controller
return true;
} ) );
if ( count( $tabs ) )
if ( \count( $tabs ) )
{
$form->add( new \IPS\Helpers\Form\Radio( 'theme_custom_setting_tab_key_type', 'existing', FALSE, array(
'options' => array( 'existing' => 'theme_custom_setting_tab_key_o_existing',
@@ -504,7 +517,7 @@ class _themesettings extends \IPS\Dispatcher\Controller
*/
public function delete()
{
$setId = intval( \IPS\Request::i()->set_id );
$setId = \intval( \IPS\Request::i()->set_id );
\IPS\Dispatcher::i()->checkAcpPermission( 'theme_settings_remove' );
Whitespace-only changes.
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\discovery;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _rss extends \IPS\Node\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Node Class
*/
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\discovery;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _search extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -45,7 +50,7 @@ class _search extends \IPS\Dispatcher\Controller
'rebuildIndex' => array(
'title' => 'search_rebuild_index',
'icon' => 'undo',
'link' => \IPS\Http\Url::internal( 'app=core&module=discovery&controller=search&do=queueIndexRebuild' ),
'link' => \IPS\Http\Url::internal( 'app=core&module=discovery&controller=search&do=queueIndexRebuild' )->csrf(),
'data' => array( 'confirm' => '', 'confirmSubMessage' => \IPS\Member::loggedIn()->language()->get('search_rebuild_index_confirm') ),
),
);
@@ -74,7 +79,6 @@ class _search extends \IPS\Dispatcher\Controller
throw new \DomainException('form_url_error');
}
$requiredVersion = '5.5.0';
try
{
$response = $val->request()->get()->decodeJson();
@@ -87,48 +91,79 @@ class _search extends \IPS\Dispatcher\Controller
{
throw new \DomainException('search_elastic_server_no_version');
}
if ( version_compare( $response['version']['number'], $requiredVersion, '<' ) )
if ( version_compare( $response['version']['number'], \IPS\Content\Search\Elastic\Index::MINIMUM_VERSION, '<' ) )
{
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack('search_elastic_server_unsupported_version', FALSE, array( 'sprintf' => array( $requiredVersion, $response['version']['number'] ) ) ) );
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack('search_elastic_server_unsupported_version', FALSE, array( 'sprintf' => array( \IPS\Content\Search\Elastic\Index::MINIMUM_VERSION, $response['version']['number'] ) ) ) );
}
if ( version_compare( $response['version']['number'], \IPS\Content\Search\Elastic\Index::UNSUPPORTED_VERSION, '>=' ) )
{
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack('search_elastic_server_unsupported_version_high', FALSE, array( 'sprintf' => array( \IPS\Content\Search\Elastic\Index::UNSUPPORTED_VERSION, $response['version']['number'] ) ) ) );
}
}, NULL, NULL, 'search_elastic_server' ) );
\IPS\Member::loggedIn()->language()->words['search_elastic_server_desc'] = sprintf( \IPS\Member::loggedIn()->language()->get('search_elastic_server_desc'), \IPS\Content\Search\Elastic\Index::MINIMUM_VERSION, \IPS\Content\Search\Elastic\Index::UNSUPPORTED_VERSION );
$form->add( new \IPS\Helpers\Form\Text( 'search_elastic_index', \IPS\Settings::i()->search_elastic_index, NULL, array( 'regex' => '/^[A-Z0-9_]*$/i' ), NULL, NULL, NULL, 'search_elastic_index' ) );
$form->add( new \IPS\Helpers\Form\Select( 'search_elastic_analyzer', \IPS\Settings::i()->search_elastic_analyzer, FALSE, array( 'options' => array(
'standard' => 'elastic_analyzer_standard',
'arabic' => 'elastic_analyzer_arabic',
'armenian' => 'elastic_analyzer_armenian',
'basque' => 'elastic_analyzer_basque',
'brazilian' => 'elastic_analyzer_brazilian',
'bulgarian' => 'elastic_analyzer_bulgarian',
'catalan' => 'elastic_analyzer_catalan',
'cjk' => 'elastic_analyzer_cjk',
'czech' => 'elastic_analyzer_czech',
'danish' => 'elastic_analyzer_danish',
'dutch' => 'elastic_analyzer_dutch',
'english' => 'elastic_analyzer_english',
'finnish' => 'elastic_analyzer_finnish',
'french' => 'elastic_analyzer_french',
'galician' => 'elastic_analyzer_galician',
'german' => 'elastic_analyzer_german',
'greek' => 'elastic_analyzer_greek',
'hindi' => 'elastic_analyzer_hindi',
'hungarian' => 'elastic_analyzer_hungarian',
'indonesian' => 'elastic_analyzer_indonesian',
'irish' => 'elastic_analyzer_irish',
'italian' => 'elastic_analyzer_italian',
'latvian' => 'elastic_analyzer_latvian',
'lithuanian' => 'elastic_analyzer_lithuanian',
'norwegian' => 'elastic_analyzer_norwegian',
'persian' => 'elastic_analyzer_persian',
'portuguese' => 'elastic_analyzer_portuguese',
'romanian' => 'elastic_analyzer_romanian',
'russian' => 'elastic_analyzer_russian',
'sorani' => 'elastic_analyzer_sorani',
'spanish' => 'elastic_analyzer_spanish',
'swedish' => 'elastic_analyzer_swedish',
'turkish' => 'elastic_analyzer_turkish',
'thai' => 'elastic_analyzer_thai',
) ), NULL, NULL, NULL, 'search_elastic_analyzer' ) );
$form->add( new \IPS\Helpers\Form\Select( 'search_elastic_analyzer', \IPS\Settings::i()->search_elastic_analyzer, FALSE, array(
'options' => array(
'language' => array(
'arabic' => 'elastic_analyzer_arabic',
'armenian' => 'elastic_analyzer_armenian',
'basque' => 'elastic_analyzer_basque',
'brazilian' => 'elastic_analyzer_brazilian',
'bulgarian' => 'elastic_analyzer_bulgarian',
'catalan' => 'elastic_analyzer_catalan',
'cjk' => 'elastic_analyzer_cjk',
'czech' => 'elastic_analyzer_czech',
'danish' => 'elastic_analyzer_danish',
'dutch' => 'elastic_analyzer_dutch',
'english' => 'elastic_analyzer_english',
'finnish' => 'elastic_analyzer_finnish',
'french' => 'elastic_analyzer_french',
'galician' => 'elastic_analyzer_galician',
'german' => 'elastic_analyzer_german',
'greek' => 'elastic_analyzer_greek',
'hindi' => 'elastic_analyzer_hindi',
'hungarian' => 'elastic_analyzer_hungarian',
'indonesian' => 'elastic_analyzer_indonesian',
'irish' => 'elastic_analyzer_irish',
'italian' => 'elastic_analyzer_italian',
'latvian' => 'elastic_analyzer_latvian',
'lithuanian' => 'elastic_analyzer_lithuanian',
'norwegian' => 'elastic_analyzer_norwegian',
'persian' => 'elastic_analyzer_persian',
'portuguese' => 'elastic_analyzer_portuguese',
'romanian' => 'elastic_analyzer_romanian',
'russian' => 'elastic_analyzer_russian',
'sorani' => 'elastic_analyzer_sorani',
'spanish' => 'elastic_analyzer_spanish',
'swedish' => 'elastic_analyzer_swedish',
'turkish' => 'elastic_analyzer_turkish',
'thai' => 'elastic_analyzer_thai',
),
'other' => array(
'standard' => 'elastic_analyzer_standard',
'custom' => 'elastic_analyzer_custom',
)
),
'toggles' => array(
'custom' => array( 'search_elastic_custom_analyzer_row' )
)
), NULL, NULL, NULL, 'search_elastic_analyzer' ) );
$form->add( new \IPS\Helpers\Form\Codemirror(
'search_elastic_custom_analyzer',
\IPS\Settings::i()->search_elastic_custom_analyzer ?: "\t\"analyzer\": {\n\t\t\"my_custom_analyzer\": {\n\t\t\t\"type\": \"custom\",\n\t\t\t\"char_filter\": [\n\t\t\t\t\"emoticons\" \n\t\t\t],\n\t\t\t\"tokenizer\": \"punctuation\", \n\t\t\t\"filter\": [\n\t\t\t\t\"lowercase\",\n\t\t\t\t\"english_stop\" \n\t\t\t]\n\t\t}\n\t},\n\t\"tokenizer\": {\n\t\t\"punctuation\": { \n\t\t\t\"type\": \"pattern\",\n\t\t\t\"pattern\": \"[ .,!?]\"\n\t\t}\n\t},\n\t\"char_filter\": {\n\t\t\"emoticons\": { \n\t\t\t\"type\": \"mapping\",\n\t\t\t\"mappings\": [\n\t\t\t\t\":) => _happy_\",\n\t\t\t\t\":( => _sad_\"\n\t\t\t]\n\t\t}\n\t},\n\t\"filter\": {\n\t\t\"english_stop\": { \n\t\t\t\"type\": \"stop\",\n\t\t\t\"stopwords\": \"_english_\"\n\t\t}\n\t}",
NULL,
array( 'mode' => 'javascript' ),
function( $val ) {
$json = json_decode( '{' . $val . '}', TRUE );
if ( $json === NULL or !isset( $json['analyzer'] ) or \count( $json['analyzer'] ) !== 1 )
{
throw new \DomainException( 'search_elastic_custom_analyzer_error' );
}
},
'<code>"analysis": {</code>',
'<code>}</code>',
'search_elastic_custom_analyzer_row'
) );
$form->addHeader('search_options');
$form->add( new \IPS\Helpers\Form\Radio( 'search_default_operator', \IPS\Settings::i()->search_default_operator, FALSE, array( 'options' => array(
'or' => 'search_default_operator_or',
@@ -140,7 +175,7 @@ class _search extends \IPS\Dispatcher\Controller
return \IPS\Theme::i()->getTemplate( 'settings' )->searchDecay( isset( $field->value[0] ) ? $field->value[0] : 0, isset( $field->value[1] ) ? $field->value[1] : 0 );
}
), NULL, NULL, NULL, 'search_decay' ) );
$form->add( new \IPS\Helpers\Form\Number( 'search_elastic_self_boost', \IPS\Settings::i()->search_elastic_self_boost, FALSE, array( 'unlimited' => floatval( 0 ), 'unlimitedLang' => 'do_not_boost', 'decimals' => 1 ), NULL, \IPS\Member::loggedIn()->language()->addToStack('search_elastic_self_boost_prefix'), \IPS\Member::loggedIn()->language()->addToStack('search_elastic_self_boost_suffix'), 'search_elastic_self_boost' ) );
$form->add( new \IPS\Helpers\Form\Number( 'search_elastic_self_boost', \IPS\Settings::i()->search_elastic_self_boost, FALSE, array( 'unlimited' => \floatval( 0 ), 'unlimitedLang' => 'do_not_boost', 'decimals' => 1 ), NULL, \IPS\Member::loggedIn()->language()->addToStack('search_elastic_self_boost_prefix'), \IPS\Member::loggedIn()->language()->addToStack('search_elastic_self_boost_suffix'), 'search_elastic_self_boost' ) );
$form->add( new \IPS\Helpers\Form\Number( 'search_index_timeframe', \IPS\Settings::i()->search_index_timeframe, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'search_index_timeframe_unlimited' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('search_index_timeframe_prefix'), \IPS\Member::loggedIn()->language()->addToStack('search_index_timeframe_suffix'), 'search_index_timeframe' ) );
$form->add( new \IPS\Helpers\Form\Number( 'search_index_maxresults', \IPS\Settings::i()->search_index_maxresults, FALSE, array(), NULL, NULL, NULL, 'search_index_maxresults' ) );
@@ -149,6 +184,7 @@ class _search extends \IPS\Dispatcher\Controller
$engine = \IPS\Settings::i()->search_method;
$indexPrune = \IPS\Settings::i()->search_index_timeframe;
$analyzer = \IPS\Settings::i()->search_elastic_analyzer;
$customAnalyzer = \IPS\Settings::i()->search_elastic_custom_analyzer;
$maxResults = \IPS\Settings::i()->search_index_maxresults;
if ( isset( $values['search_decay'][2] ) )
@@ -180,7 +216,7 @@ class _search extends \IPS\Dispatcher\Controller
\IPS\Session::i()->log( 'acplogs__search_settings' );
/* And re-index if setting updated */
if( $engine != $values['search_method'] or ( $values['search_method'] == 'elastic' and ( $values['search_elastic_analyzer'] != $analyzer or $values['search_index_maxresults'] != $maxResults ) ) )
if( $engine != $values['search_method'] or ( $values['search_method'] == 'elastic' and ( $values['search_elastic_analyzer'] != $analyzer or ( $values['search_elastic_analyzer'] == 'custom' and $values['search_elastic_custom_analyzer'] != $customAnalyzer ) or $values['search_index_maxresults'] != $maxResults ) ) )
{
/* We pass TRUE to the i() method to ensure we get a new instance, otherwise the old instance cached from the previous prune call will be used */
\IPS\Content\Search\Index::i( TRUE )->init();
@@ -203,6 +239,8 @@ class _search extends \IPS\Dispatcher\Controller
*/
protected function queueIndexRebuild()
{
\IPS\Session::i()->csrfCheck();
/* Clear MySQL minimum word length cached value */
unset( \IPS\Data\Store::i()->mysqlMinWord );
unset( \IPS\Data\Store::i()->mysqlMaxWord );
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\discovery;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _streams extends \IPS\Node\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Node Class
*/
@@ -55,7 +60,8 @@ class _streams extends \IPS\Node\Controller
'rebuildDefault' => array(
'title' => 'restore_default_streams',
'icon' => 'cog',
'link' => \IPS\Http\Url::internal( 'app=core&module=discovery&controller=streams&do=restoreDefaultStreams' )
'link' => \IPS\Http\Url::internal( 'app=core&module=discovery&controller=streams&do=restoreDefaultStreams' )->csrf(),
'data' => array( 'confirm' => '', 'confirmMessage' => \IPS\Member::loggedIn()->language()->addToStack('restore_default_streams_confirm') )
),
);
@@ -69,6 +75,8 @@ class _streams extends \IPS\Node\Controller
*/
protected function restoreDefaultStreams()
{
\IPS\Session::i()->csrfCheck();
$schema = json_decode( file_get_contents( \IPS\ROOT_PATH . "/applications/core/data/schema.json" ), TRUE );
/* Get the default language strings */
@@ -159,7 +167,7 @@ class _streams extends \IPS\Node\Controller
$toSave = array();
foreach ( $types as $k )
{
$toSave[ "all_activity_{$k}" ] = intval( in_array( $k, $values['all_activity_extra_stream'] ) );
$toSave[ "all_activity_{$k}" ] = \intval( \in_array( $k, $values['all_activity_extra_stream'] ) );
}
$toSave[ 'activity_stream_rss' ] = $values['activity_stream_rss'];
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\editor;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _emoticons extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -56,7 +61,6 @@ class _emoticons extends \IPS\Dispatcher\Controller
'options' => array(
'native' => 'emoji_style_native',
'twemoji' => 'emoji_style_twemoji',
'emojione' => 'emoji_style_emojione',
'disabled' => 'emoji_style_disabled',
),
) ) );
@@ -75,7 +79,7 @@ class _emoticons extends \IPS\Dispatcher\Controller
}
else
{
$activeTabContents = \IPS\Theme::i()->getTemplate( 'global' )->message( 'emoji_utf8mb4_required', 'error', NULL, TRUE, TRUE );
$activeTabContents = \IPS\Theme::i()->getTemplate( 'global' )->message( \IPS\CIC ? 'emoji_utf8mb4_required_cic' : 'emoji_utf8mb4_required', 'error', NULL, TRUE, TRUE );
}
}
else
@@ -148,10 +152,9 @@ class _emoticons extends \IPS\Dispatcher\Controller
{
$setId = $values['emoticons_add_choosegroup'];
$position = \IPS\Db::i()->select( 'MAX(emo_position)', 'core_emoticons', array( 'emo_set=?', $setId ) )->first( );
$position = $position['pos'];
}
if ( !is_array( $values['emoticons_upload'] ) )
if ( !\is_array( $values['emoticons_upload'] ) )
{
$values['emoticons_upload'] = array( $values['emoticons_upload'] );
}
@@ -180,13 +183,13 @@ class _emoticons extends \IPS\Dispatcher\Controller
);
}
if( count( $inserts ) )
if( \count( $inserts ) )
{
\IPS\Db::i()->insert( 'core_emoticons', $inserts );
}
/* Add 2x */
if( count( $images2x ) )
if( \count( $images2x ) )
{
foreach( \IPS\Db::i()->select( '*', 'core_emoticons', array( 'emo_set=?', $setId ) ) as $emo )
{
@@ -218,8 +221,7 @@ class _emoticons extends \IPS\Dispatcher\Controller
}
}
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => time() ), array( 'conf_key=?', 'emoji_cache' ) );
unset( \IPS\Data\Store::i()->settings );
\IPS\Settings::i()->changeValues( array( 'emoji_cache' => time() ) );
/* Clear guest page caches */
\IPS\Data\Cache::i()->clearAll();
@@ -239,7 +241,8 @@ class _emoticons extends \IPS\Dispatcher\Controller
*/
protected function edit()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'emoticons_edit' );
\IPS\Dispatcher::i()->checkAcpPermission( 'emoticons_edit' );
\IPS\Session::i()->csrfCheck();
$position = 0;
$set = NULL;
@@ -264,7 +267,7 @@ class _emoticons extends \IPS\Dispatcher\Controller
foreach ( $emoticons as $group => $emos )
{
if( isset( \IPS\Request::i()->$group ) AND is_array( \IPS\Request::i()->$group ) )
if( isset( \IPS\Request::i()->$group ) AND \is_array( \IPS\Request::i()->$group ) )
{
foreach( \IPS\Request::i()->$group as $id )
{
@@ -277,8 +280,7 @@ class _emoticons extends \IPS\Dispatcher\Controller
}
}
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => time() ), array( 'conf_key=?', 'emoji_cache' ) );
unset( \IPS\Data\Store::i()->settings );
\IPS\Settings::i()->changeValues( array( 'emoji_cache' => time() ) );
\IPS\Output::i()->json( 'OK' );
return;
@@ -327,8 +329,7 @@ class _emoticons extends \IPS\Dispatcher\Controller
}
}
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => time() ), array( 'conf_key=?', 'emoji_cache' ) );
unset( \IPS\Data\Store::i()->settings );
\IPS\Settings::i()->changeValues( array( 'emoji_cache' => time() ) );
/* Clear guest page caches */
\IPS\Data\Cache::i()->clearAll();
@@ -371,8 +372,7 @@ class _emoticons extends \IPS\Dispatcher\Controller
\IPS\Session::i()->log( 'acplog__emoticon_deleted' );
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => time() ), array( 'conf_key=?', 'emoji_cache' ) );
unset( \IPS\Data\Store::i()->settings );
\IPS\Settings::i()->changeValues( array( 'emoji_cache' => time() ) );
/* Clear guest page caches */
\IPS\Data\Cache::i()->clearAll();
@@ -409,8 +409,7 @@ class _emoticons extends \IPS\Dispatcher\Controller
\IPS\Db::i()->delete( 'core_emoticons', array( 'emo_set=?', $set ) );
\IPS\Lang::deleteCustom( 'core', 'core_emoticon_group_'. $set );
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => time() ), array( 'conf_key=?', 'emoji_cache' ) );
unset( \IPS\Data\Store::i()->settings );
\IPS\Settings::i()->changeValues( array( 'emoji_cache' => time() ) );
/* Clear guest page caches */
\IPS\Data\Cache::i()->clearAll();
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\editor;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _settings extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -56,7 +61,8 @@ class _settings extends \IPS\Dispatcher\Controller
protected function manage()
{
/* Work out output */
$activeTabContents = call_user_func( array( $this, '_manage' . mb_ucfirst( $this->activeTab ) ) );
$methodToCall = '_manage' . mb_ucfirst( $this->activeTab );
$activeTabContents = $this->$methodToCall();
/* If this is an AJAX request, just return it */
if( \IPS\Request::i()->isAjax() )
@@ -87,6 +93,8 @@ class _settings extends \IPS\Dispatcher\Controller
0 => 'editor_paragraph_padding_off'
) ) ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'enable_bbcode', \IPS\Settings::i()->enable_bbcode, FALSE ) );
if ( $values = $form->values() )
{
$clearCss = ( $values['editor_paragraph_padding'] !== \IPS\Settings::i()->editor_paragraph_padding );
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\editor;
/* 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;
@@ -56,6 +56,7 @@ class _toolbar extends \IPS\Dispatcher\Controller
'ipsquote',
'ipssource',
'ipsspoiler',
'ipsgiphy',
'justify',
'lineutils',
'list',
@@ -71,6 +72,11 @@ class _toolbar extends \IPS\Dispatcher\Controller
'widget',
);
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -123,7 +129,7 @@ class _toolbar extends \IPS\Dispatcher\Controller
'revert' => array(
'icon' => 'refresh',
'title' => 'editor_revert',
'link' => \IPS\Http\Url::internal( "app=core&module=editor&controller=toolbar&do=revert" ),
'link' => \IPS\Http\Url::internal( "app=core&module=editor&controller=toolbar&do=revert" )->csrf(),
'data' => array( 'confirm' => '', 'confirmSubMessage' => \IPS\Member::loggedIn()->language()->addToStack('editor_revert_confirm' ) )
)
);
@@ -136,6 +142,8 @@ class _toolbar extends \IPS\Dispatcher\Controller
*/
protected function save()
{
\IPS\Session::i()->csrfCheck();
$save = array();
$toolbars = json_decode( \IPS\Request::i()->toolbars, TRUE );
@@ -222,7 +230,7 @@ class _toolbar extends \IPS\Dispatcher\Controller
{
$attributes[] = "{$k}='{$v}'";
}
$attributes = count( $attributes ) ? ( ' ' . implode( ' ', $attributes ) ) : '';
$attributes = \count( $attributes ) ? ( ' ' . implode( ' ', $attributes ) ) : '';
$content = ( $matches2[4] === 'true' ) ? '{content}' : '';
$matches2[3] = json_decode( $matches2[3] );
$matches2[5] = json_decode( $matches2[5] );
@@ -249,7 +257,7 @@ class _toolbar extends \IPS\Dispatcher\Controller
$form->add( new \IPS\Helpers\Form\Select( 'editor_permission_areas', isset( $current[ $button ] ) ? $current[ $button ]['areas'] : '*', TRUE, array( 'options' => $areas, 'multiple' => TRUE, 'unlimited' => '*', 'unlimitedLang' => 'everywhere' ), NULL, NULL, NULL, 'l' . $b64Button ) );
/* If this is a custom one, add a delete button */
if ( in_array( mb_strtolower( $button ), explode( ',', mb_strtolower( \IPS\Settings::i()->ckeditor_extraPlugins ) ) ) or preg_match( '/^custom-\w{32}$/i', $button ) )
if ( \in_array( mb_strtolower( $button ), explode( ',', mb_strtolower( \IPS\Settings::i()->ckeditor_extraPlugins ) ) ) or preg_match( '/^custom-\w{32}$/i', $button ) )
{
$form->addButton( 'delete', 'link', (string) \IPS\Http\Url::internal( "app=core&module=editor&controller=toolbar&do=deletePlugin&key={$button}" ), 'ipsButton ipsButton_negative' );
}
@@ -342,7 +350,7 @@ class _toolbar extends \IPS\Dispatcher\Controller
/* Build Form */
if ( class_exists( 'ZipArchive', FALSE ) )
{
if ( !is_writable( \IPS\ROOT_PATH . '/applications/core/interface/ckeditor/ckeditor/plugins' ) )
if ( !\IPS\CIC2 AND !is_writable( \IPS\ROOT_PATH . '/applications/core/interface/ckeditor/ckeditor/plugins' ) )
{
\IPS\Output::i()->error( 'editor_plugin_nowrite', '4C120/2', 403, '' );
}
@@ -352,9 +360,14 @@ class _toolbar extends \IPS\Dispatcher\Controller
else
{
$plugins = array();
foreach ( new \DirectoryIterator( \IPS\ROOT_PATH . '/applications/core/interface/ckeditor/ckeditor/plugins' ) as $f )
$path = \IPS\ROOT_PATH . '/applications/core/interface/ckeditor/ckeditor/plugins';
if ( \IPS\CIC2 )
{
if ( !$f->isDot() and $f->isDir() and $f !== 'index.html' and !in_array( (string) $f, array_merge( static::$defaultPlugins, explode( ',', \IPS\Settings::i()->ckeditor_extraPlugins ) ) ) )
$path = \IPS\SITE_FILES_PATH . '/ckeditor_custom';
}
foreach ( new \DirectoryIterator( $path ) as $f )
{
if ( !$f->isDot() and $f->isDir() and $f !== 'index.html' and !\in_array( (string) $f, array_merge( static::$defaultPlugins, explode( ',', \IPS\Settings::i()->ckeditor_extraPlugins ) ) ) )
{
$plugins[ (string) $f ] = (string) $f;
}
@@ -395,55 +408,125 @@ class _toolbar extends \IPS\Dispatcher\Controller
}
zip_close( $zip );
/* If the content of the plugin is in the root of the zip, then we need to manually create the folder and key */
$extractTo = \IPS\ROOT_PATH . '/applications/core/interface/ckeditor/ckeditor/plugins';
if ( $inRoot === TRUE )
/* Cloud2 handles extraction */
if ( \IPS\CIC2 )
{
/* Remove the .zip extension from the filename */
$name = explode( '_', $fileName );
$name = array_shift( $name );
$extractTo .= '/' . $name;
\IPS\Cicloud\file( "IPScustomcke_{$name}.zip", \file_get_contents( $tmpName ) );
do
{
if ( isset( \IPS\Data\Store::i()->syncCompleted ) AND \IPS\Data\Store::i()->syncCompleted )
{
break;
}
}
while( TRUE );
}
/* Check it isn't already installed */
if ( in_array( $name, array_merge( static::$defaultPlugins, explode( ',', \IPS\Settings::i()->ckeditor_extraPlugins ) ) ) )
else
{
$form->error = \IPS\Member::loggedIn()->language()->addToStack('editor_plugin_already_installed');
goto displayForm;
/* If the content of the plugin is in the root of the zip, then we need to manually create the folder and key */
$extractTo = \IPS\ROOT_PATH . '/applications/core/interface/ckeditor/ckeditor/plugins';
if ( $inRoot === TRUE )
{
/* Remove the .zip extension from the filename */
$name = explode( '_', $fileName );
$name = array_shift( $name );
$extractTo .= '/' . $name;
}
$pluginDirectory = \IPS\ROOT_PATH . '/applications/core/interface/ckeditor/ckeditor/plugins/' . $name;
if ( \IPS\CIC2 )
{
$pluginDirectory = \IPS\SITE_FILES_PATH . '/ckeditor_custom/' . $name;
}
/* Check it isn't already installed */
if ( \in_array( $name, array_merge( static::$defaultPlugins, explode( ',', \IPS\Settings::i()->ckeditor_extraPlugins ) ) ) )
{
$form->error = \IPS\Member::loggedIn()->language()->addToStack('editor_plugin_already_installed');
goto displayForm;
}
/* Extract it */
$zip = new \ZipArchive;
$zip->open( $tmpName );
$zip->extractTo( $extractTo );
$zip->close();
/* Delete the temp file */
unlink( $tmpName );
}
/* Extract it */
$zip = new \ZipArchive;
$zip->open( $tmpName );
$zip->extractTo( $extractTo );
$zip->close();
/* Delete the temp file */
unlink( $tmpName );
}
else
{
$name = $values['editor_plugin_folder'];
if ( !file_exists( \IPS\ROOT_PATH . '/applications/core/interface/ckeditor/ckeditor/plugins/' . $name . '/plugin.js' ) )
$path = \IPS\ROOT_PATH . '/applications/core/interface/ckeditor/ckeditor/plugins/' . $name . '/plugin.js';
if ( \IPS\CIC2 )
{
$path = \IPS\SITE_FILES_PATH . '/ckeditor_custom/' . $name . '/plugin.js';
}
if ( !file_exists( $path ) )
{
\IPS\Output::i()->error( 'editor_bad_plugin_directory', '1C120/9', 400, '' );
}
}
unset( \IPS\Data\Store::i()->syncCompleted );
/* See if we can sniff out requirements for this plugin. */
if ( !file_exists( \IPS\ROOT_PATH . '/applications/core/interface/ckeditor/ckeditor/plugins/' . $name . '/plugin.js' ) )
$path = \IPS\ROOT_PATH . '/applications/core/interface/ckeditor/ckeditor/plugins/' . $name . '/plugin.js';
if ( \IPS\CIC2 )
{
\IPS\Output::i()->error( 'editor_bad_plugin', '1C120/9', 400, '' );
$path = \IPS\SITE_FILES_PATH . '/ckeditor_custom/' . $name . '/plugin.js';
}
$file = \file_get_contents( \IPS\ROOT_PATH . '/applications/core/interface/ckeditor/ckeditor/plugins/' . $name . '/plugin.js' );
if ( !file_exists( $path ) )
{
/* If we just extracted some files remove them as the plugin was apparently not valid */
if ( isset( $values['editor_plugin_zip'] ) )
{
if( \is_dir( $pluginDirectory ) )
{
if ( \IPS\CIC2 )
{
\IPS\Cicloud\deleteFolder( 'ckeditor_custom/' . $name );
}
else
{
try
{
$iterator = new \RecursiveDirectoryIterator( $pluginDirectory, \FilesystemIterator::SKIP_DOTS );
foreach ( new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::CHILD_FIRST ) as $file )
{
if ( $file->isDir() )
{
@rmdir( $file->getPathname() );
}
else
{
@unlink( $file->getPathname() );
}
}
$handle = opendir( $pluginDirectory );
closedir ( $pluginDirectory );
@rmdir( $pluginDirectory );
}
catch( \UnexpectedValueException $e ){}
}
}
}
\IPS\Output::i()->error( 'editor_bad_plugin', '1C120/B', 400, '' );
}
$file = \file_get_contents( $path );
preg_match( "#requires: \'(.+?)\',#i", $file, $matches );
if ( isset( $matches[1] ) )
{
$required = explode( ',', $matches[1] );
$rewriteFileWithIpsContextMenuRequirement = FALSE;
if ( in_array( 'contextmenu', $required ) )
if ( \in_array( 'contextmenu', $required ) )
{
$required[ array_search( 'contextmenu', $required ) ] = 'ipscontextmenu';
$rewriteFileWithIpsContextMenuRequirement = TRUE;
@@ -451,9 +534,9 @@ class _toolbar extends \IPS\Dispatcher\Controller
$current = array_merge( static::$defaultPlugins, explode( ',', \IPS\Settings::i()->ckeditor_extraPlugins ) );
$missing = array_diff( $required, $current );
if ( count( $missing ) > 0 )
if ( \count( $missing ) > 0 )
{
\IPS\Output::i()->error( \IPS\Member::loggedIn()->language()->addToStack( 'editor_missing_requirements', FALSE, array( 'sprintf' => array( count( $missing ), implode( ', ', $missing ) ) ) ), '2C120/5', 404, '' );
\IPS\Output::i()->error( \IPS\Member::loggedIn()->language()->addToStack( 'editor_missing_requirements', FALSE, array( 'sprintf' => array( \count( $missing ), implode( ', ', $missing ) ) ) ), '2C120/5', 404, '' );
}
if ( $rewriteFileWithIpsContextMenuRequirement )
@@ -543,7 +626,7 @@ class _toolbar extends \IPS\Dispatcher\Controller
}
/* Automatically detect and allow custom css classes */
if( count( $cssClasses ) )
if( \count( $cssClasses ) )
{
$settingClasses = \IPS\Settings::i()->editor_allowed_classes ? explode( ',', \IPS\Settings::i()->editor_allowed_classes ) : array();
$settingClasses = array_unique( array_merge( $settingClasses, $cssClasses ) );
@@ -565,7 +648,7 @@ class _toolbar extends \IPS\Dispatcher\Controller
}
/* Automatically detect and allow custom css classes */
if( count( $dataControllers ) )
if( \count( $dataControllers ) )
{
$settingControllers = \IPS\Settings::i()->editor_allowed_datacontrollers ? explode( ',', \IPS\Settings::i()->editor_allowed_datacontrollers ) : array();
$settingControllers = array_unique( array_merge( $settingControllers, $dataControllers ) );
@@ -714,30 +797,46 @@ JS;
}
/* Write it */
$dir = \IPS\ROOT_PATH . '/applications/core/interface/ckeditor/ckeditor/plugins/' . $key;
if ( !is_dir( $dir ) )
if ( \IPS\CIC2 )
{
mkdir( $dir );
chmod( $dir, \IPS\IPS_FOLDER_PERMISSION );
}
\file_put_contents( $dir . '/plugin.js', $pluginFile );
if ( $values['editor_button_image'] )
{
if ( !is_dir( $dir . '/icons' ) )
\IPS\Cicloud\file( 'plugin.js', $pluginFile, "ckeditor_custom/{$key}" );
if ( $values['editor_button_image'] )
{
mkdir( $dir . '/icons' );
chmod( $dir . '/icons', \IPS\IPS_FOLDER_PERMISSION );
\IPS\Cicloud\file( "{$key}.png", \file_get_contents( $values['editor_button_image'] ), "ckeditor_custom/{$key}/icons" );
}
\file_put_contents( "{$dir}/icons/{$key}.png", file_get_contents( $values['editor_button_image'] ) );
}
if ( $dialogFile )
{
if ( !is_dir( $dir . '/dialogs' ) )
if ( $dialogFile )
{
mkdir( $dir . '/dialogs' );
chmod( $dir . '/dialogs', \IPS\IPS_FOLDER_PERMISSION );
\IPS\Cicloud\file( "{$key}.js", $dialogFile, "ckeditor_custom/{$key}/dialogs" );
}
}
else
{
$dir = \IPS\ROOT_PATH . '/applications/core/interface/ckeditor/ckeditor/plugins/' . $key;
if ( !is_dir( $dir ) )
{
mkdir( $dir );
chmod( $dir, \IPS\IPS_FOLDER_PERMISSION );
}
\file_put_contents( $dir . '/plugin.js', $pluginFile );
if ( $values['editor_button_image'] )
{
if ( !is_dir( $dir . '/icons' ) )
{
mkdir( $dir . '/icons' );
chmod( $dir . '/icons', \IPS\IPS_FOLDER_PERMISSION );
}
\file_put_contents( "{$dir}/icons/{$key}.png", file_get_contents( $values['editor_button_image'] ) );
}
if ( $dialogFile )
{
if ( !is_dir( $dir . '/dialogs' ) )
{
mkdir( $dir . '/dialogs' );
chmod( $dir . '/dialogs', \IPS\IPS_FOLDER_PERMISSION );
}
\file_put_contents( "{$dir}/dialogs/{$key}.js", $dialogFile );
}
\file_put_contents( "{$dir}/dialogs/{$key}.js", $dialogFile );
}
/* Save name */
@@ -758,7 +857,7 @@ JS;
}
if ( \IPS\NO_WRITES )
{
\IPS\Output::i()->error( 'no_writes', '1C120/3', 403, '' );
\IPS\Output::i()->error( 'no_writes', '1C120/C', 403, '' );
}
/* Make sure the user confirmed the deletion */
@@ -772,7 +871,7 @@ JS;
$jsFile = file_get_contents( \IPS\ROOT_PATH . '/applications/core/interface/ckeditor/ckeditor/plugins/' . $file . '/plugin.js' );
if ( preg_match( '/requires:\"(.+?)\"/i', $jsFile, $matches ) )
{
if ( in_array( mb_strtolower( \IPS\Request::i()->key ), explode( ',', mb_strtolower( $matches[1] ) ) ) )
if ( \in_array( mb_strtolower( \IPS\Request::i()->key ), explode( ',', mb_strtolower( $matches[1] ) ) ) )
{
\IPS\Output::i()->error( \IPS\Member::loggedIn()->language()->addToStack( 'editor_plugin_requirements', FALSE, array( 'sprintf' => array( $file ) ) ), '1C120/3', 403, '' );
}
@@ -842,16 +941,25 @@ JS;
* @return void
*/
public function revert()
{
{
\IPS\Session::i()->csrfCheck();
if ( !empty( \IPS\Settings::i()->ckeditor_extraPlugins ) and !\IPS\NO_WRITES )
{
$extraPlugins = explode( ',', \IPS\Settings::i()->ckeditor_extraPlugins );
$dir = \IPS\ROOT_PATH . '/applications/core/interface/ckeditor/ckeditor/plugins/';
foreach( $extraPlugins AS $key )
{
if ( is_dir( $dir . $key ) )
if ( \IPS\CIC2 )
{
$this->_recursiveRmDir( $dir . $key );
\IPS\Cicloud\deleteFolder( "ckeditor_custom/{$key}" );
}
else
{
if ( is_dir( $dir . $key ) )
{
$this->_recursiveRmDir( $dir . $key );
}
}
}
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\languages;
/* 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;
@@ -22,11 +22,26 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _languages extends \IPS\Node\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Node Class
*/
protected $nodeClass = 'IPS\Lang';
/**
* Title can contain HTML?
*/
public $_titleHtml = TRUE;
/**
* Description can contain HTML?
*/
public $_descriptionHtml = TRUE;
/**
* Execute
*
@@ -38,6 +53,29 @@ class _languages extends \IPS\Node\Controller
parent::execute();
}
/**
* Allow overloading to change how the title is displayed in the tree
*
* @param $node \IPS\Node Node
* @return string
*/
protected static function nodeTitle( $node )
{
return \IPS\Theme::i()->getTemplate('customization')->langRowTitle( $node );
}
/**
* Fetch any additional HTML for this row
*
* @param object $node Node returned from $nodeClass::load()
* @return NULL|string
*/
public function _getRowHtml( $node )
{
$hasBeenCustomized = \IPS\Db::i()->select( 'COUNT(*)', 'core_sys_lang_words', array( 'lang_id=? AND word_export=1 AND word_custom IS NOT NULL', $node->id ) )->first();
return \IPS\Theme::i()->getTemplate('customization' )->langRowAdditional( $node, $hasBeenCustomized );
}
/**
* Manage
*
@@ -55,11 +93,130 @@ class _languages extends \IPS\Node\Controller
'link' => \IPS\Http\Url::internal( 'app=core&module=languages&controller=languages&do=vle' ),
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('lang_vle') )
);
\IPS\Output::i()->sidebar['actions'][] = array(
'icon' => 'plus',
'title' => 'add_word',
'link' => \IPS\Http\Url::internal( "app=core&module=languages&controller=languages&do=addWord" ),
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack( 'add_word' ) )
);
/* @note If any more settings are added, then this condition needs to be moved. */
if ( \count( \IPS\Lang::languages() ) > 1 )
{
\IPS\Output::i()->sidebar['actions'][] = array(
'icon' => 'cogs',
'title' => 'settings',
'link' => \IPS\Http\Url::internal( 'app=core&module=languages&controller=languages&do=settings' ),
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('settings') )
);
}
}
parent::manage();
}
/**
* Add Word
*
* @return void
*/
protected function addWord(): void
{
$form = new \IPS\Helpers\Form( 'wordForm', 'save', NULL, array( 'data-role' => 'wordForm' ) );
\IPS\Lang::wordForm( $form );
if ( $values = $form->values() )
{
/* Save */
\IPS\Lang::saveCustom( 'core', $values['word_key'], $values['word_custom'] ?? NULL, FALSE, $values['word_default'] );
if ( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->json( 'OK' );
}
else
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=languages&controller=languages" ), 'saved' );
}
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'add_word' );
\IPS\Output::i()->output = (string) $form;
}
/**
* Delete Word
*
* @return void
*/
protected function deleteWord(): void
{
\IPS\Session::i()->csrfCheck();
/* Make sure this is actually a custom phrase */
try
{
$word = \IPS\Db::i()->select( '*', 'core_sys_lang_words', array( "word_key=? AND lang_id=?", \IPS\Request::i()->key, \IPS\Request::i()->langId ) )->first();
if ( !$word['word_is_custom'] )
{
\IPS\Output::i()->error( 'node_error', '1C126/9', 403, '' );
}
}
catch( \UnderflowException $e )
{
\IPS\Output::i()->error( 'node_error', '1C126/A', 403, '' );
}
\IPS\Db::i()->delete( 'core_sys_lang_words', array( "word_key=?", \IPS\Request::i()->key ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=languages&controller=languages&do=translate&id=" . \IPS\Request::i()->langId ), 'deleted' );
}
/**
* Manual Upload Form
*
* @return void
*/
protected function upload()
{
/* Build form */
$form = new \IPS\Helpers\Form;
$form->addMessage('languages_manual_install_warning');
$form->add( new \IPS\Helpers\Form\Upload( 'lang_upload', NULL, TRUE, array( 'allowedFileTypes' => array( 'xml' ), 'temporary' => TRUE ) ) );
\IPS\Lang::localeField( $form );
$activeTabContents = $form;
/* Handle submissions */
if ( $values = $form->values() )
{
/* Move it to a temporary location */
$tempFile = tempnam( \IPS\TEMP_DIRECTORY, 'IPS' );
move_uploaded_file( $values['lang_upload'], $tempFile );
/* Work out locale */
if( isset( $values['lang_short_custom'] ) )
{
if ( !isset($values['lang_short']) OR $values['lang_short'] === 'x' )
{
$locale = $values['lang_short_custom'];
}
else
{
$locale = $values['lang_short'];
}
}
/* Initate a redirector */
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=languages&controller=languages&do=import' )->setQueryString( array( 'file' => $tempFile, 'key' => md5_file( $tempFile ), 'locale' => $locale ) )->csrf() );
}
/* Display */
\IPS\Output::i()->output = $form;
}
/**
* Add/Edit Form
*
@@ -69,126 +226,80 @@ class _languages extends \IPS\Node\Controller
{
/* If we have no ID number, this is the add form, which is handled differently to the edit */
if ( !\IPS\Request::i()->id )
{
$tab = \IPS\Request::i()->tab ?: 'new';
{
/* CREATE NEW */
$max = \IPS\Db::i()->select( 'MAX(lang_order)', 'core_sys_lang' )->first();
if ( $tab === 'new' )
{
/* Build form */
$form = new \IPS\Helpers\Form;
$lang = new \IPS\Lang( array( 'lang_short' => 'en_US' ) );
$lang->form( $form );
$activeTabContents = $form;
/* Handle submissions */
if ( $values = $form->values() )
{
/* Find the correct locale */
if ( !isset($values['lang_short']) OR $values['lang_short'] === 'x' )
{
$values['lang_short'] = $values['lang_short_custom'];
}
unset( $values['lang_short_custom'] );
/* reset default language if we want this to be default */
if( isset( $values['lang_default'] ) and $values['lang_default'] )
{
\IPS\Db::i()->update( 'core_sys_lang', array( 'lang_default' => 0 ) );
}
/* Add "UTF8" if we can */
$currentLocale = setlocale( LC_ALL, '0' );
foreach ( array( "{$values['lang_short']}.UTF-8", "{$values['lang_short']}.UTF8" ) as $l )
{
$test = setlocale( LC_ALL, $l );
if ( $test !== FALSE )
{
$values['lang_short'] = $l;
break;
}
}
foreach( explode( ";", $currentLocale ) as $locale )
{
$parts = explode( "=", $locale );
if( in_array( $parts[0], array( 'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME' ) ) )
{
setlocale( constant( $parts[0] ), $parts[1] );
}
}
/* Insert the actual language */
$values['lang_order'] = ++$max;
$insertId = \IPS\Db::i()->insert( 'core_sys_lang', $values );
/* Copy over language strings */
$default = \IPS\Lang::defaultLanguage();
$prefix = \IPS\Db::i()->prefix;
$defaultStmt = \IPS\Db::i()->prepare( "INSERT INTO `{$prefix}core_sys_lang_words` ( `lang_id`, `word_app`, `word_key`, `word_default`, `word_custom`, `word_default_version`, `word_custom_version`, `word_js`, `word_export` ) SELECT {$insertId} AS `lang_id`, `word_app`, `word_key`, `word_default`, NULL AS `word_custom`, `word_default_version`, NULL AS `word_custom_version`, `word_js`, `word_export` FROM `{$prefix}core_sys_lang_words` WHERE `lang_id`={$default} AND `word_export`=1" );
$defaultStmt->execute();
$customStmt = \IPS\Db::i()->prepare( "INSERT INTO `{$prefix}core_sys_lang_words` ( `lang_id`, `word_app`, `word_key`, `word_default`, `word_custom`, `word_default_version`, `word_custom_version`, `word_js`, `word_export` ) SELECT {$insertId} AS `lang_id`, `word_app`, `word_key`, `word_default`, `word_custom`, `word_default_version`, `word_custom_version`, `word_js`, `word_export` FROM `{$prefix}core_sys_lang_words` WHERE `lang_id`={$default} AND `word_export`=0" );
$customStmt->execute();
unset( \IPS\Data\Store::i()->languages );
/* Clear guest page caches */
\IPS\Data\Cache::i()->clearAll();
/* Log */
\IPS\Session::i()->log( 'acplogs__lang_created', array( $values['lang_title'] => FALSE ) );
/* Redirect */
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=languages&controller=languages' ), 'saved' );
}
}
/* Build form */
$form = new \IPS\Helpers\Form;
$form->addMessage('languages_create_blurb');
$lang = new \IPS\Lang( array( 'lang_short' => 'en_US' ) );
$lang->form( $form );
/* UPLOAD */
else
/* Handle submissions */
if ( $values = $form->values() )
{
/* Build form */
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\Upload( 'lang_upload', NULL, TRUE, array( 'allowedFileTypes' => array( 'xml' ), 'temporary' => TRUE ) ) );
\IPS\Lang::localeField( $form );
$activeTabContents = $form;
/* Handle submissions */
if ( $values = $form->values() )
/* Find the correct locale */
if ( !isset($values['lang_short']) OR $values['lang_short'] === 'x' )
{
/* Move it to a temporary location */
$tempFile = tempnam( \IPS\TEMP_DIRECTORY, 'IPS' );
move_uploaded_file( $values['lang_upload'], $tempFile );
/* Work out locale */
if( isset( $values['lang_short_custom'] ) )
{
if ( !isset($values['lang_short']) OR $values['lang_short'] === 'x' )
{
$locale = $values['lang_short_custom'];
}
else
{
$locale = $values['lang_short'];
}
}
/* Initate a redirector */
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=languages&controller=languages&do=import' )->setQueryString( array( 'file' => $tempFile, 'key' => md5_file( $tempFile ), 'locale' => $locale ) ) );
$values['lang_short'] = $values['lang_short_custom'];
}
unset( $values['lang_short_custom'] );
/* reset default language if we want this to be default */
if( isset( $values['lang_default'] ) and $values['lang_default'] )
{
\IPS\Db::i()->update( 'core_sys_lang', array( 'lang_default' => 0 ) );
}
/* Add "UTF8" if we can */
$currentLocale = setlocale( LC_ALL, '0' );
foreach ( array( "{$values['lang_short']}.UTF-8", "{$values['lang_short']}.UTF8" ) as $l )
{
$test = setlocale( LC_ALL, $l );
if ( $test !== FALSE )
{
$values['lang_short'] = $l;
break;
}
}
foreach( explode( ";", $currentLocale ) as $locale )
{
$parts = explode( "=", $locale );
if( \in_array( $parts[0], array( 'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME' ) ) )
{
setlocale( \constant( $parts[0] ), $parts[1] );
}
}
/* Insert the actual language */
$values['lang_order'] = ++$max;
$insertId = \IPS\Db::i()->insert( 'core_sys_lang', $values );
/* Copy over language strings */
$default = \IPS\Lang::defaultLanguage();
$prefix = \IPS\Db::i()->prefix;
$defaultStmt = \IPS\Db::i()->prepare( "INSERT INTO `{$prefix}core_sys_lang_words` ( `lang_id`, `word_app`, `word_key`, `word_default`, `word_custom`, `word_default_version`, `word_custom_version`, `word_js`, `word_export` ) SELECT {$insertId} AS `lang_id`, `word_app`, `word_key`, `word_default`, NULL AS `word_custom`, `word_default_version`, NULL AS `word_custom_version`, `word_js`, `word_export` FROM `{$prefix}core_sys_lang_words` WHERE `lang_id`={$default} AND `word_export`=1" );
$defaultStmt->execute();
$customStmt = \IPS\Db::i()->prepare( "INSERT INTO `{$prefix}core_sys_lang_words` ( `lang_id`, `word_app`, `word_key`, `word_default`, `word_custom`, `word_default_version`, `word_custom_version`, `word_js`, `word_export`, `word_is_custom` ) SELECT {$insertId} AS `lang_id`, `word_app`, `word_key`, `word_default`, `word_custom`, `word_default_version`, `word_custom_version`, `word_js`, `word_export`, `word_is_custom` FROM `{$prefix}core_sys_lang_words` WHERE `lang_id`={$default} AND `word_export`=0" );
$customStmt->execute();
unset( \IPS\Data\Store::i()->languages );
/* Clear guest page caches */
\IPS\Data\Cache::i()->clearAll();
/* Log */
\IPS\Session::i()->log( 'acplogs__lang_created', array( $values['lang_title'] => FALSE ) );
/* Redirect */
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=languages&controller=languages' ), 'saved' );
}
/* Display */
if( \IPS\Request::i()->isAjax() and \IPS\Request::i()->existing and !isset( \IPS\Request::i()->ajaxValidate ) )
{
\IPS\Output::i()->output = $activeTabContents;
}
else
{
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->tabs( array( 'new' => 'add', 'upload' => 'upload' ), $tab, $activeTabContents, \IPS\Http\Url::internal( "app=core&module=languages&controller=languages&do=form&existing=1" ) );
}
\IPS\Output::i()->output = $form;
}
/* If it's an edit, we can just let the node controller handle it */
else
@@ -204,6 +315,8 @@ class _languages extends \IPS\Node\Controller
*/
protected function enableToggle()
{
\IPS\Session::i()->csrfCheck();
/* Load Language */
try
{
@@ -306,11 +419,11 @@ class _languages extends \IPS\Node\Controller
{
if ( $values[ 'lang_' . $k ] )
{
\IPS\Request::i()->setcookie( $k, 1 );
\IPS\Request::i()->setCookie( $k, 1 );
}
elseif ( isset( \IPS\Request::i()->cookie[ $k ] ) )
{
\IPS\Request::i()->setcookie( $k, 0 );
\IPS\Request::i()->setCookie( $k, 0 );
}
}
@@ -320,6 +433,25 @@ class _languages extends \IPS\Node\Controller
\IPS\Output::i()->output = $form;
}
/**
* Language Setting
*
* @return void
*/
protected function settings()
{
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\YesNo( 'lang_auto_detect', \IPS\Settings::i()->lang_auto_detect, TRUE ) );
if ( $values = $form->values() )
{
$form->saveAsSettings( $values );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=languages&controller=languages' ) );
}
\IPS\Output::i()->output = $form;
}
/**
* Translate
*
@@ -342,7 +474,7 @@ class _languages extends \IPS\Node\Controller
}
$where = array(
array( 'lang_id=? AND word_export=1', \IPS\Request::i()->id ),
array( 'lang_id=? AND (word_export=1 OR word_is_custom=1)', \IPS\Request::i()->id ),
);
$table = new \IPS\Helpers\Table\Db( 'core_sys_lang_words', \IPS\Http\Url::internal( 'app=core&module=languages&controller=languages&do=translate&id=' . \IPS\Request::i()->id ), $where );
@@ -408,15 +540,35 @@ class _languages extends \IPS\Node\Controller
'word_key' => \IPS\Helpers\Table\SEARCH_CONTAINS_TEXT,
'word_default' => \IPS\Helpers\Table\SEARCH_CONTAINS_TEXT,
'word_custom' => \IPS\Helpers\Table\SEARCH_CONTAINS_TEXT,
'word_app' => array( \IPS\Helpers\Table\SEARCH_NODE, array( 'class' => 'IPS\Application', 'subnodes' => FALSE ) ),
'word_plugin' => array( \IPS\Helpers\Table\SEARCH_NODE, array( 'class' => 'IPS\Plugin', 'subnodes' => FALSE ) )
);
$table->filters = array(
'lang_filter_translated' => 'word_custom IS NOT NULL',
'lang_filter_untranslated' => 'word_custom IS NULL',
'lang_filter_out_of_date' => 'word_custom IS NOT NULL AND word_custom_version<word_default_version'
'lang_filter_out_of_date' => 'word_custom IS NOT NULL AND word_custom_version<word_default_version',
'lang_filter_admin_custom' => 'word_is_custom!=0'
);
$table->widths = array( 'word_key' => 15, 'word_default' => 35, 'word_custom' => 50 );
$table->rowButtons = function( $row ) {
if ( $row['word_is_custom'] )
{
return array(
'delete' => array(
'icon' => 'times',
'title' => 'delete',
'link' => \IPS\Http\Url::internal( "app=core&module=languages&controller=languages&do=deleteWord&key={$row['word_key']}&langId=" . \IPS\Request::i()->id )->csrf(),
'data' => array( 'confirm' => '', 'confirmMessage' => \IPS\Member::loggedIn()->language()->addToStack('delete_word_all_languages') )
)
);
}
else
{
return array();
}
};
\IPS\Member::loggedIn()->language()->words['lang_word_custom'] = $lang->title;
@@ -445,7 +597,7 @@ class _languages extends \IPS\Node\Controller
$word = \IPS\Db::i()->select( '*', 'core_sys_lang_words', array( 'lang_id=? AND word_key=? AND word_js=?', \IPS\Request::i()->lang, \IPS\Request::i()->key, (int) \IPS\Request::i()->js ) )->first();
$form = new \IPS\Helpers\Form;
$form->addDummy( 'lang_word_default', $word['word_default'] );
$form->addDummy( 'lang_word_default', htmlspecialchars( $word['word_default'],ENT_QUOTES | ENT_DISALLOWED, 'UTF-8', FALSE ) );
$form->add( new \IPS\Helpers\Form\Text( 'lang_word_custom', $word['word_custom'] ) );
if ( $values = $form->values() )
@@ -480,6 +632,11 @@ class _languages extends \IPS\Node\Controller
{
unset( \IPS\Data\Store::i()->listFormats );
}
if ( \substr( $word['word_key'], 0, 10 ) === 'num_short_' )
{
unset( \IPS\Data\Store::i()->shortFormats );
}
/* Clear guest page caches */
\IPS\Data\Cache::i()->clearAll();
@@ -505,17 +662,20 @@ class _languages extends \IPS\Node\Controller
*/
protected function copy()
{
\IPS\Session::i()->csrfCheck();
\IPS\Output::i()->output = new \IPS\Helpers\MultipleRedirect(
\IPS\Http\Url::internal( "app=core&module=languages&controller=languages&do=copy&id=" . intval( \IPS\Request::i()->id ) ),
\IPS\Http\Url::internal( "app=core&module=languages&controller=languages&do=copy&id=" . \intval( \IPS\Request::i()->id ) )->csrf(),
function( $data )
{
if ( !is_array( $data ) )
if ( !\is_array( $data ) )
{
$lang = \IPS\Db::i()->select( '*', 'core_sys_lang', array( 'lang_id=?', \IPS\Request::i()->id ) )->first();
unset( $lang['lang_id'] );
$lang['lang_title'] = $lang['lang_title'] . ' ' . \IPS\Member::loggedIn()->language()->get('copy_noun');
$lang['lang_default'] = FALSE;
$lang['lang_marketplace_id'] = NULL;
$insertId = \IPS\Db::i()->insert( 'core_sys_lang', $lang );
@@ -528,7 +688,7 @@ class _languages extends \IPS\Node\Controller
else
{
$words = \IPS\Db::i()->select( '*', 'core_sys_lang_words', array( 'lang_id=?', \IPS\Request::i()->id ), 'word_id', array( $data['done'], 100 ) );
if ( !count( $words ) )
if ( !\count( $words ) )
{
return NULL;
}
@@ -577,86 +737,125 @@ class _languages extends \IPS\Node\Controller
\IPS\Output::i()->error( 'node_error', '2C126/5', 404, '' );
}
$count = 0;
try
{
$count = \IPS\Db::i()->select( 'COUNT(word_id)', 'core_sys_lang_words', array( 'lang_id=? AND word_export=1 AND word_custom IS NOT NULL', $lang->id ), 'word_id', NULL, 'word_id' )->first();
}
catch ( \UnderflowException $e ) {}
$form = new \IPS\Helpers\Form( 'form', 'langauge_export_button' );
if ( $count < 1 )
$form->add( new \IPS\Helpers\Form\Text( 'language_export_author_name', $lang->author_name, false ) );
$form->add( new \IPS\Helpers\Form\Text( 'language_export_author_url', $lang->author_url, false ) );
$form->add( new \IPS\Helpers\Form\Text( 'language_export_update_check', $lang->update_url, false ) );
$form->add( new \IPS\Helpers\Form\Text( 'language_export_version', $lang->version, true, array( 'placeholder' => '1.0.0' ) ) );
$form->add( new \IPS\Helpers\Form\Number( 'language_export_long_version', $lang->version_long ?: 10000, true ) );
if ( $values = $form->values() )
{
\IPS\Output::i()->error( 'core_lang_download_empty', '1C126/7', 404, '' );
}
/* Init */
$xml = new \XMLWriter;
$xml->openMemory();
$xml->setIndent( TRUE );
$xml->startDocument( '1.0', 'UTF-8' );
/* Root tag */
$xml->startElement('language');
$xml->startAttribute('name');
$xml->text( $lang->title );
$xml->endAttribute();
$xml->startAttribute('rtl');
$xml->text( $lang->isrtl );
$xml->endAttribute();
/* Loop applications */
foreach ( \IPS\Application::applications() as $app )
{
/* Initiate the <app> tag */
$xml->startElement('app');
/* Set key */
$xml->startAttribute('key');
$xml->text( $app->directory );
$xml->endAttribute();
/* Set version */
$xml->startAttribute('version');
$xml->text( $app->long_version );
$xml->endAttribute();
/* Add words */
foreach ( \IPS\Db::i()->select( '*', 'core_sys_lang_words', array( 'lang_id=? AND word_app=? AND word_export=1 AND word_custom IS NOT NULL', $lang->id, $app->directory ), 'word_id' ) as $row )
$lang->author_name = $values['language_export_author_name'];
$lang->author_url = $values['language_export_author_url'];
$lang->update_url = $values['language_export_update_check'];
$lang->version = $values['language_export_version'];
$lang->version_long = (int) $values['language_export_long_version'];
$lang->save();
$count = 0;
try
{
/* Start */
$xml->startElement( 'word' );
/* Add key */
$xml->startAttribute('key');
$xml->text( $row['word_key'] );
$count = \IPS\Db::i()->select( 'COUNT(word_id)', 'core_sys_lang_words', array( 'lang_id=? AND word_export=1 AND word_custom IS NOT NULL', $lang->id ), 'word_id', NULL, 'word_id' )->first();
}
catch ( \UnderflowException $e ) {}
if ( $count < 1 )
{
\IPS\Output::i()->error( 'core_lang_download_empty', '1C126/7', 404, '' );
}
/* Init */
$xml = new \XMLWriter;
$xml->openMemory();
$xml->setIndent( TRUE );
$xml->startDocument( '1.0', 'UTF-8' );
/* Root tag */
$xml->startElement( 'language' );
$xml->startAttribute( 'name' );
$xml->text( $lang->title );
$xml->endAttribute();
$xml->startAttribute( 'rtl' );
$xml->text( $lang->isrtl );
$xml->endAttribute();
$xml->startAttribute( 'author_name' );
$xml->text( $lang->author_name );
$xml->endAttribute();
$xml->startAttribute( 'author_url' );
$xml->text( $lang->author_url );
$xml->endAttribute();
$xml->startAttribute( 'version' );
$xml->text( $lang->version );
$xml->endAttribute();
$xml->startAttribute( 'long_version' );
$xml->text( $lang->version_long );
$xml->endAttribute();
$xml->startAttribute( 'update_check' );
$xml->text( $lang->update_url );
$xml->endAttribute();
/* Loop applications */
foreach ( \IPS\Application::applications() as $app )
{
/* Initiate the <app> tag */
$xml->startElement( 'app' );
/* Set key */
$xml->startAttribute( 'key' );
$xml->text( $app->directory );
$xml->endAttribute();
/* Is this a javascript string? */
$xml->startAttribute('js');
$xml->text( $row['word_js'] );
/* Set version */
$xml->startAttribute( 'version' );
$xml->text( $app->version_long );
$xml->endAttribute();
/* Write value */
if ( preg_match( '/<|>|&/', $row['word_custom'] ) )
/* Add words */
foreach ( \IPS\Db::i()->select( '*', 'core_sys_lang_words', array( 'lang_id=? AND word_app=? AND word_export=1 AND word_custom IS NOT NULL', $lang->id, $app->directory ), 'word_id' ) as $row )
{
$xml->writeCData( str_replace( ']]>', ']]]]><![CDATA[>', $row['word_custom'] ) );
/* Start */
$xml->startElement( 'word' );
/* Add key */
$xml->startAttribute( 'key' );
$xml->text( $row['word_key'] );
$xml->endAttribute();
/* Is this a javascript string? */
$xml->startAttribute( 'js' );
$xml->text( $row['word_js'] );
$xml->endAttribute();
/* Write value */
if ( preg_match( '/<|>|&/', $row['word_custom'] ) )
{
$xml->writeCData( str_replace( ']]>', ']]]]><![CDATA[>', $row['word_custom'] ) );
}
else
{
$xml->text( $row['word_custom'] );
}
/* End */
$xml->endElement();
}
else
{
$xml->text( $row['word_custom'] );
}
/* End */
/* </app> */
$xml->endElement();
}
/* </app> */
$xml->endElement();
/* Finish */
$xml->endDocument();
\IPS\Output::i()->sendOutput( $xml->outputMemory(), 200, 'application/xml', array( 'Content-Disposition' => \IPS\Output::getContentDisposition( 'attachment', $lang->title . '.xml' ) ), FALSE, FALSE, FALSE );
}
/* Finish */
$xml->endDocument();
\IPS\Output::i()->sendOutput( $xml->outputMemory(), 200, 'application/xml', array( 'Content-Disposition' => \IPS\Output::getContentDisposition( 'attachment', $lang->title . '.xml' ) ), FALSE, FALSE, FALSE );
/* Display */
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->block( \IPS\Member::loggedIn()->language()->addToStack('language_export_title', FALSE, array( 'sprintf' => array( $lang->title ) ) ), $form, FALSE );
}
/**
@@ -667,6 +866,13 @@ class _languages extends \IPS\Node\Controller
public function uploadNewVersion()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'lang_words' );
$language = \IPS\Lang::load( \IPS\Request::i()->id );
if( $language->marketplace_id )
{
\IPS\Output::i()->error( 'app_upload_marketplace_only', '2C126/B', 403, '' );
}
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\Upload( 'lang_upload', NULL, TRUE, array( 'allowedFileTypes' => array( 'xml' ), 'temporary' => TRUE ) ) );
@@ -679,7 +885,7 @@ class _languages extends \IPS\Node\Controller
move_uploaded_file( $values['lang_upload'], $tempFile );
/* Initate a redirector */
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=languages&controller=languages&do=import' )->setQueryString( array( 'file' => $tempFile, 'key' => md5_file( $tempFile ), 'into' => \IPS\Request::i()->id ) ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=languages&controller=languages&do=import' )->setQueryString( array( 'file' => $tempFile, 'key' => md5_file( $tempFile ), 'into' => \IPS\Request::i()->id ) )->csrf() );
}
\IPS\Output::i()->output = $form;
@@ -690,18 +896,24 @@ class _languages extends \IPS\Node\Controller
*
* @return void
*/
protected function import()
public function import()
{
\IPS\Session::i()->csrfCheck();
if ( !file_exists( \IPS\Request::i()->file ) or md5_file( \IPS\Request::i()->file ) !== \IPS\Request::i()->key )
{
\IPS\Output::i()->error( 'generic_error', '3C126/6', 500, '' );
}
$url = \IPS\Http\Url::internal( 'app=core&module=languages&controller=languages&do=import' )->setQueryString( array( 'file' => \IPS\Request::i()->file, 'key' => \IPS\Request::i()->key, 'locale' => \IPS\Request::i()->locale ) );
$url = \IPS\Http\Url::internal( 'app=core&module=languages&controller=languages&do=import' )->setQueryString( array( 'file' => \IPS\Request::i()->file, 'key' => \IPS\Request::i()->key, 'locale' => \IPS\Request::i()->locale ) )->csrf();
if ( isset( \IPS\Request::i()->into ) )
{
$url = $url->setQueryString( 'into', \IPS\Request::i()->into );
}
if ( isset( \IPS\Request::i()->marketplace ) )
{
$url = $url->setQueryString( 'marketplace', \IPS\Request::i()->marketplace );
}
\IPS\Output::i()->output = new \IPS\Helpers\MultipleRedirect(
$url,
@@ -713,12 +925,23 @@ class _languages extends \IPS\Node\Controller
$xml->read();
/* If this is the first batch, create the language record */
if ( !is_array( $data ) )
if ( !\is_array( $data ) )
{
/* Create the record */
if ( isset( \IPS\Request::i()->into ) )
{
$insertId = \IPS\Request::i()->into;
\IPS\Db::i()->update( 'core_sys_lang', array(
'lang_title' => $xml->getAttribute('name'),
'lang_isrtl' => $xml->getAttribute('rtl'),
'lang_version' => $xml->getAttribute('version'),
'lang_version_long' => $xml->getAttribute('long_version'),
'lang_author_name' => $xml->getAttribute('author_name'),
'lang_author_url' => $xml->getAttribute('author_url'),
'lang_update_url' => $xml->getAttribute('update_check')
),
array( 'lang_id=?', $insertId) );
}
else
{
@@ -738,19 +961,25 @@ class _languages extends \IPS\Node\Controller
foreach( explode( ";", $currentLocale ) as $locale )
{
$parts = explode( "=", $locale );
if( in_array( $parts[0], array( 'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME' ) ) )
if( \in_array( $parts[0], array( 'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME' ) ) )
{
setlocale( constant( $parts[0] ), $parts[1] );
setlocale( \constant( $parts[0] ), $parts[1] );
}
}
/* Insert the language pack record */
$max = \IPS\Db::i()->select( 'MAX(lang_order)', 'core_sys_lang' )->first();
$insertId = \IPS\Db::i()->insert( 'core_sys_lang', array(
'lang_short' => \IPS\Request::i()->locale,
'lang_title' => $xml->getAttribute('name'),
'lang_isrtl' => $xml->getAttribute('rtl'),
'lang_order' => $max['max'] + 1
'lang_short' => \IPS\Request::i()->locale,
'lang_title' => $xml->getAttribute('name'),
'lang_isrtl' => $xml->getAttribute('rtl'),
'lang_order' => $max + 1,
'lang_version' => $xml->getAttribute('version'),
'lang_version_long' => $xml->getAttribute('long_version'),
'lang_author_name' => $xml->getAttribute('author_name'),
'lang_author_url' => $xml->getAttribute('author_url'),
'lang_update_url' => $xml->getAttribute('update_check'),
'lang_marketplace_id' => \IPS\Request::i()->marketplace ? (int) \IPS\Request::i()->marketplace : NULL
) );
/* Copy over default language strings */
@@ -780,7 +1009,7 @@ class _languages extends \IPS\Node\Controller
while ( $xml->read() )
{
$appKey = $xml->getAttribute('key');
if ( !array_key_exists( $appKey, $data['apps'] ) AND in_array( $appKey, $applications ) )
if ( !array_key_exists( $appKey, $data['apps'] ) AND \in_array( $appKey, $applications ) )
{
/* Get version */
$version = $xml->getAttribute('version');
@@ -816,13 +1045,20 @@ class _languages extends \IPS\Node\Controller
},
function()
{
unset( \IPS\Data\Store::i()->languages );
/* Clear language caches, including update counter */
unset( \IPS\Data\Store::i()->languages, \IPS\Data\Store::i()->listFormats, \IPS\Data\Store::i()->updatecount_languages );
/* Clear guest page caches */
\IPS\Data\Cache::i()->clearAll();
@unlink( \IPS\Request::i()->file );
/* Redirect back to marketplace if it was installed from there. */
if( \IPS\Request::i()->marketplace )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=marketplace&controller=marketplace&do=viewFile&id=' . \IPS\Request::i()->marketplace ), 'lang_installed' );
}
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=languages&controller=languages' ) );
}
);
@@ -835,11 +1071,13 @@ class _languages extends \IPS\Node\Controller
*/
protected function devimport()
{
\IPS\Session::i()->csrfCheck();
\IPS\Output::i()->output = new \IPS\Helpers\MultipleRedirect(
\IPS\Http\Url::internal( "app=core&module=languages&controller=languages&do=devimport&id=" . intval( \IPS\Request::i()->id ) ),
\IPS\Http\Url::internal( "app=core&module=languages&controller=languages&do=devimport&id=" . \intval( \IPS\Request::i()->id ) )->csrf(),
function ( $data )
{
if ( !is_array( $data ) )
if ( !\is_array( $data ) )
{
\IPS\Db::i()->delete( 'core_sys_lang_words', array( 'lang_id=? AND word_export=1', \IPS\Request::i()->id ) );
return array( array(), \IPS\Member::loggedIn()->language()->addToStack('lang_dev_importing'), 1 );
@@ -903,7 +1141,7 @@ class _languages extends \IPS\Node\Controller
return NULL;
}
return array( $data, \IPS\Member::loggedIn()->language()->addToStack('lang_dev_importing'), ( 100 / ( count( \IPS\Application::applications() ) * 2 ) * count( $data ) ) );
return array( $data, \IPS\Member::loggedIn()->language()->addToStack('lang_dev_importing'), ( 100 / ( \count( \IPS\Application::applications() ) * 2 ) * \count( $data ) ) );
},
function ()
{
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,236 @@
<?php
/**
* @brief Marketplace Onboarding Controller
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 1 May 2020
*/
namespace IPS\core\modules\admin\marketplace;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Marketplace Onboarding Controller
*/
class _onboard extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Run
*
* @return void
*/
public function manage(): void
{
$steps = [];
if ( array_filter( array_keys( \IPS\Application::applications() ), function( $k ) { return !\in_array( $k, \IPS\Application::$ipsApps ); } ) )
{
$steps['marketplace_onboard_applications'] = array( $this, '_applications' );
}
if ( \IPS\Plugin::plugins() )
{
$steps['marketplace_onboard_plugins'] = array( $this, '_plugins' );
}
if ( array_filter( \IPS\Theme::themes(), function( $t ) { return $t->isCustomized(); } ) )
{
$steps['marketplace_onboard_themes'] = array( $this, '_themes' );
}
if ( \IPS\Db::i()->select( 'COUNT(*)', 'core_sys_lang_words', array( 'word_custom IS NOT NULL AND word_export=1' ) )->first() )
{
$steps['marketplace_onboard_languages'] = array( $this, '_languages' );
}
$steps['marketplace_onboard_complete'] = array( $this, '_complete' );
if ( !$steps )
{
$this->_complete();
}
$wizard = new \IPS\Helpers\Wizard( $steps, \IPS\Http\Url::internal('app=core&module=marketplace&controller=onboard') );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('marketplace_onboard_title');
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'admin_marketplace.js', 'core', 'admin' ) );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'marketplace/marketplace.css', 'core', 'admin' ) );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'forms', 'core' )->blurb( \IPS\Member::loggedIn()->language()->addToStack( 'marketplace_onboard_blurb' ) ) . $wizard;
}
/**
* Applications
*
* @param mixed $data Wizard data
* @return mixed
*/
public function _applications( $data )
{
if ( \IPS\Request::i()->match_apps )
{
foreach ( \IPS\Application::applications() as $application )
{
if ( isset( \IPS\Request::i()->match_apps[ $application->directory ] ) and \IPS\Request::i()->match_apps[ $application->directory ] )
{
$application->marketplace_id = \IPS\Request::i()->match_apps[ $application->directory ];
$application->save();
}
}
return array();
}
$rows = [];
foreach ( \IPS\Application::applications() as $application )
{
if ( !\in_array( $application->directory, \IPS\Application::$ipsApps ) )
{
$rows[ $application->directory ] = \IPS\Theme::i()->getTemplate('marketplace')->onboardRow( $application->directory, $application->marketplace_id, 'apps', $application->_title, $application->version, $application->author, $application->website, 'apps' );
}
}
if ( $rows )
{
return \IPS\Theme::i()->getTemplate('marketplace')->onboardTemplate( $rows );
}
else
{
return array();
}
}
/**
* Plugins
*
* @param mixed $data Wizard data
* @return mixed
*/
public function _plugins( $data )
{
if ( \IPS\Request::i()->match_plugins )
{
foreach ( \IPS\Plugin::plugins() as $plugin )
{
if ( isset( \IPS\Request::i()->match_plugins[ $plugin->id ] ) and \IPS\Request::i()->match_plugins[ $plugin->id ] )
{
$plugin->marketplace_id = \IPS\Request::i()->match_plugins[ $plugin->id ];
$plugin->save();
}
}
return array();
}
$rows = [];
foreach ( \IPS\Plugin::plugins() as $plugin )
{
$rows[ $plugin->id ] = \IPS\Theme::i()->getTemplate('marketplace')->onboardRow( $plugin->id, $plugin->marketplace_id, 'apps', $plugin->name, $plugin->version_human, $plugin->author, $plugin->website, 'plugins' );
}
if ( $rows )
{
return \IPS\Theme::i()->getTemplate('marketplace')->onboardTemplate( $rows );
}
else
{
return array();
}
}
/**
* Themes
*
* @param mixed $data Wizard data
* @return mixed
*/
public function _themes( $data )
{
if ( \IPS\Request::i()->match_themes )
{
foreach ( \IPS\Theme::themes() as $theme )
{
if ( isset( \IPS\Request::i()->match_themes[ $theme->id ] ) and \IPS\Request::i()->match_themes[ $theme->id ] )
{
$theme->marketplace_id = \IPS\Request::i()->match_themes[ $theme->id ];
$theme->save();
}
}
return array();
}
$rows = [];
foreach ( \IPS\Theme::themes() as $theme )
{
if ( $theme->isCustomized() )
{
$rows[ $theme->id ] = \IPS\Theme::i()->getTemplate('marketplace')->onboardRow( $theme->id, $theme->marketplace_id, 'themes', $theme->_title, $theme->version, $theme->author_name, $theme->author_url, 'themes' );
}
}
if ( $rows )
{
return \IPS\Theme::i()->getTemplate('marketplace')->onboardTemplate( $rows );
}
else
{
return array();
}
}
/**
* Languages
*
* @param mixed $data Wizard data
* @return mixed
*/
public function _languages( $data )
{
if ( \IPS\Request::i()->match_languages )
{
foreach ( \IPS\Lang::languages() as $language )
{
if ( isset( \IPS\Request::i()->match_languages[ $language->id ] ) and \IPS\Request::i()->match_languages[ $language->id ] )
{
$language->marketplace_id = \IPS\Request::i()->match_languages[ $language->id ];
$language->save();
}
}
return array();
}
$rows = [];
foreach ( \IPS\Lang::languages() as $language )
{
if ( \IPS\Db::i()->select( 'COUNT(*)', 'core_sys_lang_words', array( 'lang_id=? AND word_custom IS NOT NULL AND word_export=1', $language->_id ) )->first() )
{
$rows[ $language->id ] = \IPS\Theme::i()->getTemplate('marketplace')->onboardRow( $language->id, $language->marketplace_id, 'languages', $language->_title, $language->version, $language->author_name, $language->author_url, 'languages' );
}
}
if ( $rows )
{
return \IPS\Theme::i()->getTemplate('marketplace')->onboardTemplate( $rows );
}
else
{
return array();
}
}
/**
* Complete
*
* @param mixed $data Wizard data
* @return mixed
*/
public function _complete()
{
\IPS\core\AdminNotification::remove( 'core', 'ConfigurationError', 'marketplaceSetup' );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal('app=core&module=marketplace&controller=marketplace') );
}
}
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\members;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _devices extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -71,7 +76,17 @@ class _devices extends \IPS\Dispatcher\Controller
if ( $activeTab === 'info' )
{
$activeTabContents = \IPS\Theme::i()->getTemplate( 'members' )->deviceInfo( $device );
$oauthClients = \IPS\Api\OAuthClient::roots();
$apps = array();
foreach ( \IPS\Db::i()->select( '*', 'core_oauth_server_access_tokens', array( 'member_id=? AND device_key=?', \IPS\Member::loggedIn()->member_id, $device->device_key ), 'issued DESC' ) as $accessToken )
{
if ( isset( $oauthClients[ $accessToken['client_id'] ] ) )
{
$apps[] = $accessToken;
}
}
$activeTabContents = \IPS\Theme::i()->getTemplate( 'members' )->deviceInfo( $device, $apps, $oauthClients );
}
elseif ( $activeTab === 'member' )
{
@@ -156,7 +171,7 @@ class _devices extends \IPS\Dispatcher\Controller
\IPS\Output::i()->sidebar['actions']['deauthorize'] = array(
'icon' => 'times',
'title' => 'sign_out',
'link' => \IPS\Http\Url::internal( "app=core&module=members&controller=devices&do=deauthorize" )->setQueryString( array( 'device' => $device->device_key, 'member' => $member->member_id, 'r' => 'device' ) )
'link' => \IPS\Http\Url::internal( "app=core&module=members&controller=devices&do=deauthorize" )->setQueryString( array( 'device' => $device->device_key, 'member' => $member->member_id, 'r' => 'device' ) )->csrf()
);
}
@@ -223,7 +238,7 @@ class _devices extends \IPS\Dispatcher\Controller
'link' => \IPS\Http\Url::internal( 'app=core&module=members&controller=devices&do=deauthorize' )->setQueryString( array(
'device' => $row['device_key'],
'member' => $row['member_id']
) ),
) )->csrf(),
);
}
@@ -244,7 +259,9 @@ class _devices extends \IPS\Dispatcher\Controller
* @return void
*/
protected function deauthorize()
{
{
\IPS\Session::i()->csrfCheck();
try
{
$member = \IPS\Member::load( \IPS\Request::i()->member );
@@ -252,6 +269,8 @@ class _devices extends \IPS\Dispatcher\Controller
$device->login_key = NULL;
$device->save();
\IPS\Db::i()->delete( 'core_oauth_server_access_tokens', array( 'member_id=? AND device_key=?', $device->member_id, $device->device_key ) );
$member->logHistory( 'core', 'login', array( 'type' => 'logout', 'device' => $device->device_key ) );
\IPS\Session::i()->log( 'acplog__members_disabled_autologin', array( $member->name => FALSE ) );
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\members;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _groups extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -130,7 +135,7 @@ class _groups extends \IPS\Dispatcher\Controller
$return['copy'] = array(
'icon' => 'files-o',
'title' => 'copy',
'link' => \IPS\Http\Url::internal( 'app=core&module=members&controller=groups&do=copy&id=' ) . $row['g_id'],
'link' => \IPS\Http\Url::internal( 'app=core&module=members&controller=groups&do=copy&id=' . $row['g_id'] )->csrf(),
);
}
@@ -143,7 +148,7 @@ class _groups extends \IPS\Dispatcher\Controller
);
}
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'groups_delete' ) AND !in_array( $row['g_id'], array( \IPS\Settings::i()->guest_group, \IPS\Settings::i()->member_group, \IPS\Settings::i()->admin_group ) ) )
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'groups_delete' ) AND !\in_array( $row['g_id'], array( \IPS\Settings::i()->guest_group, \IPS\Settings::i()->member_group, \IPS\Settings::i()->admin_group ) ) )
{
$return['delete'] = array(
'icon' => 'times-circle',
@@ -151,7 +156,7 @@ class _groups extends \IPS\Dispatcher\Controller
'link' => \IPS\Http\Url::internal( 'app=core&module=members&controller=groups&do=delete&id=' ) . $row['g_id'],
'data' => \IPS\Db::i()->select( 'COUNT(*)', 'core_members', array( 'member_group_id=?', $row['g_id'] ) )->first() ?
array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack( "core_group_{$row['g_id']}" ) ) :
array( 'delete' => '', 'delete-warning' => ( in_array( $row['g_id'], $warnGroups ) ) ? \IPS\Member::loggedIn()->language()->addToStack( "nexus_dont_delete_group", FALSE, array( 'sprintf' => array( \IPS\Member\Group::load( \IPS\Settings::i()->member_group )->name ) ) ) : '' ),
array( 'delete' => '', 'delete-warning' => ( \in_array( $row['g_id'], $warnGroups ) ) ? \IPS\Member::loggedIn()->language()->addToStack( "nexus_dont_delete_group", FALSE, array( 'sprintf' => array( \IPS\Member\Group::load( \IPS\Settings::i()->member_group )->name ) ) ) : '' ),
);
}
@@ -265,7 +270,7 @@ class _groups extends \IPS\Dispatcher\Controller
{
foreach ( $ext->getNodeClasses() as $class => $callback )
{
if( !is_callable( $callback ) )
if( !\is_callable( $callback ) )
{
continue;
}
@@ -293,15 +298,15 @@ class _groups extends \IPS\Dispatcher\Controller
{
$matrix->columns[ $class::$permissionLangPrefix . 'perm__' . $k ] = function( $key, $value, $data ) use ( $class )
{
return new \IPS\Helpers\Form\Checkbox( $class::$permApp . $key, $value, FALSE, array( 'disabled' => (bool) is_null( $value ) ) );
return new \IPS\Helpers\Form\Checkbox( $class::$permApp . $key, $value, FALSE, array( 'disabled' => \is_null( $value ) ) );
};
if( isset( $current[ $class::$permApp ][ $class::$permType ] ) AND is_array( $current[ $class::$permApp ][ $class::$permType ] ) )
if( isset( $current[ $class::$permApp ][ $class::$permType ] ) AND \is_array( $current[ $class::$permApp ][ $class::$permType ] ) )
{
foreach( $current[ $class::$permApp ][ $class::$permType ] AS $x => $y )
{
$matrix->checkAlls[ $class::$permissionLangPrefix . 'perm__' . $k ] = TRUE;
if ( $y["perm_{$v}"] !== '*' AND !in_array( $group->g_id, explode( ',', $y["perm_{$v}"] ) ) )
if ( $y["perm_{$v}"] !== '*' AND !\in_array( $group->g_id, explode( ',', $y["perm_{$v}"] ) ) )
{
$matrix->checkAlls[ $class::$permissionLangPrefix . 'perm__' . $k ] = FALSE;
break;
@@ -316,7 +321,7 @@ class _groups extends \IPS\Dispatcher\Controller
{
try
{
$matrix->rows = call_user_func( $callback, $current[ $class::$permApp ][ $class::$permType ], $group );
$matrix->rows = $callback( $current[ $class::$permApp ][ $class::$permType ], $group );
}
catch( \UnderflowException $e )
{
@@ -378,6 +383,7 @@ class _groups extends \IPS\Dispatcher\Controller
public function copy()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'groups_add' );
\IPS\Session::i()->csrfCheck();
/* Load group */
try
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\members;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _ip extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* @brief GeoLocation
*/
File diff suppressed because it is too large. Load diff
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\members;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _promotions extends \IPS\Node\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Node Class
*/
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\members;
/* 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;
@@ -21,7 +21,12 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
* Recount and Reset Tools
*/
class _reset extends \IPS\Dispatcher\Controller
{
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -40,6 +45,7 @@ class _reset extends \IPS\Dispatcher\Controller
*/
public function posts()
{
\IPS\Session::i()->csrfCheck();
\IPS\Task::queue( 'core', 'RecountMemberContent', array(), 4 );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=members&controller=members' ), \IPS\Member::loggedIn()->language()->addToStack( 'member_recount_content_process' ) );
}
@@ -51,6 +57,7 @@ class _reset extends \IPS\Dispatcher\Controller
*/
public function rep()
{
\IPS\Session::i()->csrfCheck();
\IPS\Task::queue( 'core', 'RecountMemberReputation', array(), 4 );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=members&controller=members' ), \IPS\Member::loggedIn()->language()->addToStack( 'member_recount_rep_process' ) );
}
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\membersettings;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _ban extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\membersettings;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _notifications extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -32,7 +37,7 @@ class _notifications extends \IPS\Dispatcher\Controller
\IPS\Dispatcher::i()->checkAcpPermission( 'notifications_manage' );
parent::execute();
}
/**
* Manage
*
@@ -40,136 +45,340 @@ class _notifications extends \IPS\Dispatcher\Controller
*/
protected function manage()
{
/* Init Matrix */
$matrix = new \IPS\Helpers\Form\Matrix;
$matrix->manageable = FALSE;
$matrix->langPrefix = 'notificationsettings_';
$matrix->classes = array( 'cMemberNotifications' );
/* Populate columns */
$matrix->columns = array(
'label' => function( $key, $value, $data )
{
if ( mb_substr( $key, 0, -7 ) === 'new_likes' )
{
if ( \IPS\Content\Reaction::isLikeMode() )
{
return 'notifications__new_likes_like';
}
else
{
return 'notifications__new_likes_rep';
}
}
return $value;
},
'default' => function( $key, $value, $data )
{
return new \IPS\Helpers\Form\CheckboxSet( $key, $value, FALSE, array( 'options' => array( 'email' => 'member_notifications_email', 'inline' => 'member_notifications_inline' ), 'multiple' => TRUE ) );
},
'disabled' => function( $key, $value, $data )
{
return new \IPS\Helpers\Form\CheckboxSet( $key, $value, FALSE, array( 'options' => array( 'email' => 'member_notifications_email', 'inline' => 'member_notifications_inline' ), 'multiple' => TRUE ) );
},
'editable' => function( $key, $value, $data )
{
return new \IPS\Helpers\Form\YesNo( $key, $value );
},
);
/* Populate rows */
$current = iterator_to_array( \IPS\Db::i()->select( '*', 'core_notification_defaults' )->setKeyField( 'notification_key' ) );
foreach( \IPS\Application::allExtensions( 'core', 'Notifications', FALSE ) as $group => $class )
$types = array();
foreach ( \IPS\Application::allExtensions( 'core', 'Notifications' ) as $k => $extension )
{
$configuration = $class->getConfiguration( NULL );
if ( !empty( $configuration ) )
{
$lang = "notifications__{$group}";
$header = \IPS\Member::loggedIn()->language()->addToStack( $lang );
$matrix->rows[] = $header;
foreach ( $configuration as $key => $data )
{
$matrix->rows[ $key ] = array(
'label' => "notifications__{$key}",
'default' => isset( $current[ $key ] ) ? explode( ',', $current[ $key ]['default'] ) : $data['default'],
'disabled' => isset( $current[ $key ] ) ? explode( ',', $current[ $key ]['disabled'] ) : $data['disabled'],
'editable' => isset( $current[ $key ] ) ? $current[ $key ]['editable'] : TRUE,
);
}
}
$types[ $k ] = array(
'title' => \IPS\Member::loggedIn()->language()->get('notifications__' . $k)
);
}
/* Handle submissions */
if ( $values = $matrix->values() )
$typeTable = new \IPS\Helpers\Table\Custom( $types, \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=notifications', 'admin' ) );
$typeTable->langPrefix = 'notificationsettings_';
$typeTable->rowButtons = function( $row, $k ) {
return array(
'edit' => array(
'title' => 'edit',
'link' => \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=notifications&do=edit', 'admin' )->setQueryString( 'id', $k ),
'icon' => 'pencil'
)
);
};
$methodTable = new \IPS\Helpers\Table\Custom( array( 'inline' => array( 'method' => \IPS\Member::loggedIn()->language()->addToStack('member_notifications_inline') ) ), \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=notifications', 'admin' ) );
$methodTable->langPrefix = 'notificationsettings_';
$methodTable->rowButtons = function( $row, $k ) {
return array(
'edit' => array(
'title' => 'edit',
'link' => \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=notifications&do=method', 'admin' )->setQueryString( 'id', $k ),
'icon' => 'pencil'
)
);
};
if( !\IPS\CIC )
{
\IPS\Db::i()->delete( 'core_notification_defaults' );
$inserts = array();
foreach ( $values as $k => $data )
{
$inserts[] = array(
'notification_key' => $k,
'default' => implode( ',', $data['default'] ),
'disabled' => implode( ',', $data['disabled'] ),
'editable' => $data['editable']
);
}
if( count( $inserts ) )
{
\IPS\Db::i()->insert( 'core_notification_defaults', $inserts );
}
\IPS\Session::i()->log( 'acplog__notifications_edited' );
\IPS\Output::i()->sidebar['actions'] = array(
'settings' => array(
'title' => 'prunesettings',
'icon' => 'cog',
'link' => \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=notifications&do=pruneSettings' ),
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('prunesettings') )
),
);
}
/* Add a button for settings */
\IPS\Output::i()->sidebar['actions'] = array(
'settings' => array(
'title' => 'notificationsettings',
'icon' => 'cog',
'link' => \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=notifications&do=settings' ),
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('notificationsettings') )
),
);
/* Display */
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'members/notifications.css', 'core', 'admin' ) );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('notifications');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->block( 'notifications', $matrix );
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'membersettings', 'profiles_manage' ) )
{
\IPS\Output::i()->sidebar['actions']['reset'] = array(
'icon' => 'undo',
'title' => 'notification_prefs_reset',
'link' => \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=notifications&do=updateMemberFollowPrefs' )->csrf(),
'data' => array( 'confirm' => '', 'confirmSubMessage' => \IPS\Member::loggedIn()->language()->addToStack('notification_prefs_reset_confirm') )
);
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('notifications');
\IPS\Output::i()->output = $typeTable . $methodTable;
}
/**
* Profile Settings
* Prune Settings
*
* @return void
*/
protected function settings()
protected function pruneSettings()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'profiles_manage' );
if( \IPS\CIC )
{
\IPS\Output::i()->error( 'node_error', '2C413/1', 403, '' );
}
$form = new \IPS\Helpers\Form;
$form->addHeader('auto_follow_preferences');
$form->addMessage( 'auto_follow_update_all' );
$form->add( new \IPS\Helpers\Form\YesNo( 'auto_follow_new_content', \IPS\Settings::i()->auto_follow_new_content, FALSE, array(), NULL, NULL, NULL, 'auto_follow_new_content' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'auto_follow_replied_to', \IPS\Settings::i()->auto_follow_replied_to, FALSE, array(), NULL, NULL, NULL, 'auto_follow_replied_to' ) );
$form->addHeader('notification_pruning');
$form->add( new \IPS\Helpers\Form\Number( 'subs_autoprune', \IPS\Settings::i()->subs_autoprune, FALSE, array(), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), \IPS\Member::loggedIn()->language()->addToStack('days') ) );
$form->add( new \IPS\Helpers\Form\Number( 'prune_notifications', \IPS\Settings::i()->prune_notifications, FALSE, array(), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), \IPS\Member::loggedIn()->language()->addToStack('months') ) );
$form->add( new \IPS\Helpers\Form\Interval( 'prune_follows', \IPS\Settings::i()->prune_follows, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'never' ), function( $val ) {
if( $val > 0 AND $val < 30 )
{
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack( 'form_interval_min_d', FALSE, array( 'pluralize' => array( 29 ) ) ) );
}
}, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL, 'prune_follows' ) );
$form->add( new \IPS\Helpers\Form\Interval( 'prune_notifications', \IPS\Settings::i()->prune_notifications, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'never' ), function( $val ) {
if( $val > 0 AND $val < 7 )
{
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack( 'form_interval_min_d', FALSE, array( 'pluralize' => array( 6 ) ) ) );
}
}, \IPS\Member::loggedIn()->language()->addToStack('after') ) );
if ( $values = $form->values() )
{
$form->saveAsSettings();
/* If we're enabling pruning on a potentially large table, handle that */
if( !\IPS\Settings::i()->prune_follows AND $values['prune_follows'] )
{
\IPS\Task::queue( 'core', 'PruneLargeTable', array(
'table' => 'core_follow',
'where' => array( 'follow_member_id IN(?)', \IPS\Db::i()->select( 'member_id', 'core_members', array( 'last_activity < ?', \IPS\DateTime::create()->sub( new \DateInterval( 'P' . $values['prune_follows'] . 'D' ) )->getTimestamp() ) ) ),
'deleteJoin' => array(
'column' => 'member_id',
'table' => 'core_members',
'where' => array( 'last_activity < ?', \IPS\DateTime::create()->sub( new \DateInterval( 'P' . $values['prune_follows'] . 'D' ) )->getTimestamp() ),
'outerColumn' => 'follow_member_id'
)
), 4 );
}
$form->saveAsSettings( $values );
\IPS\Session::i()->log( 'acplog__follow_settings' );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=notifications' ), 'saved' );
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('notification_pruning');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('global')->block( 'notification_pruning', $form, FALSE );
}
/**
* Edit
*
* @return void
*/
protected function edit()
{
$extensions = \IPS\Application::allExtensions( 'core', 'Notifications' );
if ( !isset( $extensions[ \IPS\Request::i()->id ] ) )
{
\IPS\Output::i()->error( 'node_error', '2C404/2', 404, '' );
}
$extension = $extensions[ \IPS\Request::i()->id ];
$defaultConfiguration = \IPS\Notification::defaultConfiguration();
$form = new \IPS\Helpers\Form;
foreach ( \IPS\Notification::availableOptions( NULL, $extension ) as $key => $option )
{
if ( $option['type'] === 'standard' )
{
$form->addHeader( $option['title'] );
if ( isset( $option['adminDescription'] ) )
{
$form->addMessage( $option['adminDescription'] );
}
elseif ( $option['description'] )
{
$form->addMessage( $option['description'] );
}
if ( !\in_array( 'inline', $option['disabled'] ) or ( \IPS\Settings::i()->mobile_app_id and !\in_array( 'push', $option['disabled'] ) ) or !\in_array( 'email', $option['disabled'] ) )
{
$canEditField = new \IPS\Helpers\Form\YesNo( 'notificationsettings_editable_' . $key, $defaultConfiguration[ $key ]['editable'], FALSE, array(
'togglesOn' => array( "member_notifications_{$key}_inline_editable", "member_notifications_{$key}_push", "member_notifications_{$key}_email_editable" ),
'togglesOff' => array( "member_notifications_{$key}_inline", "member_notifications_{$key}_email" )
) );
$canEditField->label = \IPS\Member::loggedIn()->language()->addToStack('notificationsettings_editable');
$form->add( $canEditField );
}
if ( isset( $option['extra'] ) )
{
foreach ( $option['extra'] as $k => $extra )
{
if ( isset( $extra['adminCanSetDefault'] ) and $extra['adminCanSetDefault'] )
{
$field = new \IPS\Helpers\Form\Radio( 'member_notifications_' . $key . '_' . $k, $extra['default'] ? 'default' : 'optional', FALSE, array( 'options' => array(
'default' => isset( $extra['admin_lang'] ) ? $extra['admin_lang']['default'] : 'admin_notification_pref_default',
'optional' => isset( $extra['admin_lang'] ) ? $extra['admin_lang']['optional'] : 'admin_notification_pref_optional',
) ), NULL, NULL, NULL, 'member_notifications_' . $k );
$field->label = \IPS\Member::loggedIn()->language()->addToStack( isset( $extra['admin_lang'] ) ? $extra['admin_lang']['title'] : $extra['title'] );
$form->add( $field );
}
}
}
foreach ( array( 'inline', 'push', 'email' ) as $k )
{
if ( !\in_array( $k, $option['disabled'] ) )
{
$editableFieldValue = \in_array( $k, $defaultConfiguration[ $key ]['default'] ) ? 'default' : 'optional';
if ( \in_array( $k, $defaultConfiguration[ $key ]['disabled'] ) )
{
$editableFieldValue = 'disabled';
}
if ( $k !== 'push' )
{
$editableField = new \IPS\Helpers\Form\Radio( 'member_notifications_' . $key . '_' . $k . '_editable', $editableFieldValue, FALSE, array(
'options' => array(
'default' => 'admin_notification_pref_default',
'optional' => 'admin_notification_pref_optional',
'disabled' => 'admin_notification_pref_disabled'
),
'toggles' => $k === 'inline' ? array(
'default' => array( 'member_notifications_' . $key . '_push' ),
'optional' => array( 'member_notifications_' . $key . '_push' ),
) : array()
), NULL, NULL, NULL, 'member_notifications_' . $key . '_' . $k . '_editable' );
$editableField->label = \IPS\Member::loggedIn()->language()->addToStack( 'member_notifications_' . $k );
$form->add( $editableField );
}
$nonEditableField = new \IPS\Helpers\Form\Radio( 'member_notifications_' . $key . '_' . $k, \in_array( $k, $defaultConfiguration[ $key ]['disabled'] ) ? 'disabled' : 'default', FALSE, array( 'options' => array(
'default' => $k === 'push' ? 'admin_notification_pref_available' : 'admin_notification_pref_force',
'disabled' => 'admin_notification_pref_disabled'
) ), NULL, NULL, NULL, 'member_notifications_' . $key . '_' . $k );
$nonEditableField->label = \IPS\Member::loggedIn()->language()->addToStack( 'member_notifications_' . $k );
$form->add( $nonEditableField );
}
}
}
elseif ( $option['type'] === 'custom' )
{
if ( ( isset( $option['adminCanSetDefault'] ) and $option['adminCanSetDefault'] ) or ( isset( $option['adminOnly'] ) and $option['adminOnly'] ) )
{
if ( isset( $option['admin_lang']['header'] ) and isset( $option['admin_lang'] ) )
{
$form->addHeader( $option['admin_lang']['header'] );
}
$form->add( $option['field'] );
}
}
}
if ( $values = $form->values() )
{
foreach ( \IPS\Notification::availableOptions( NULL, $extension ) as $key => $option )
{
if ( $option['type'] === 'standard' )
{
if ( isset( $option['extra'] ) )
{
foreach ( $option['extra'] as $k => $extra )
{
if ( isset( $extra['adminCanSetDefault'] ) and $extra['adminCanSetDefault'] )
{
$extension->saveExtra( NULL, $k, ( $values[ 'member_notifications_' . $key . '_' . $k ] === 'default' ) );
}
}
}
if ( !\in_array( 'inline', $option['disabled'] ) or ( \IPS\Settings::i()->mobile_app_id and !\in_array( 'push', $option['disabled'] ) ) or !\in_array( 'email', $option['disabled'] ) )
{
$row = array(
'notification_key' => $key,
'default' => array(),
'disabled' => array(),
'editable' => $values[ 'notificationsettings_editable_' . $key ]
);
foreach ( array( 'inline', 'push', 'email' ) as $k )
{
if ( !\in_array( $k, $option['disabled'] ) )
{
if ( $k === 'push' )
{
if ( !\IPS\Settings::i()->mobile_app_id )
{
continue;
}
$fieldToCheck = $values[ 'member_notifications_' . $key . '_push' ];
}
else
{
$fieldToCheck = $values[ 'notificationsettings_editable_' . $key ] ? $values[ 'member_notifications_' . $key . '_' . $k . '_editable' ] : $values[ 'member_notifications_' . $key . '_' . $k ];
}
if ( $fieldToCheck === 'default' )
{
$row['default'][] = $k;
}
elseif ( $fieldToCheck === 'disabled' )
{
$row['disabled'][] = $k;
}
}
}
$row['default'] = implode( ',', $row['default'] );
$row['disabled'] = implode( ',', $row['disabled'] );
\IPS\Db::i()->replace( 'core_notification_defaults', $row, array( 'notification_key=?', $key ) );
$extensionConfiguration = $extension->configurationOptions();
if ( isset( $extensionConfiguration[ $key ] ) and $extensionConfiguration[ $key ]['type'] === 'standard' )
{
foreach ( $extensionConfiguration[ $key ]['notificationTypes'] as $notificationType )
{
$row['notification_key'] = $notificationType;
\IPS\Db::i()->replace( 'core_notification_defaults', $row, array( 'notification_key=?', $notificationType ) );
}
}
}
}
elseif ( $option['type'] === 'custom' )
{
if ( ( isset( $option['adminCanSetDefault'] ) and $option['adminCanSetDefault'] ) or ( isset( $option['adminOnly'] ) and $option['adminOnly'] ) )
{
$extension->saveExtra( NULL, $key, $option['field']->value );
}
}
}
\IPS\Session::i()->log( 'acplog__notifications_edited' );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=notifications' ), 'saved' );
}
\IPS\Output::i()->breadcrumb[] = array( \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=notifications' ), \IPS\Member::loggedIn()->language()->addToStack( 'menu__core_membersettings_notifications' ) );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'notifications__' . \IPS\Request::i()->id );
\IPS\Output::i()->output = $form;
}
/**
* Notification List Settings
*
* @return void
*/
protected function method()
{
$form = new \IPS\Helpers\Form;
$form->addHeader('notification_preferences');
$form->add( new \IPS\Helpers\Form\Radio( 'notification_prefs_sound', \IPS\Settings::i()->notification_prefs_sound ? 'default' : 'optional', FALSE, array( 'options' => array(
'default' => 'admin_notification_pref_default',
'optional' => 'admin_notification_pref_optional',
) ), NULL, NULL, NULL, 'notification_prefs_sound' ) );
if ( $values = $form->values() )
{
$values['notification_prefs_sound'] = ( $values['notification_prefs_sound'] === 'default' );
$form->saveAsSettings( $values );
\IPS\Session::i()->log( 'acplog__notification_settings' );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=notifications' ), 'saved' );
}
\IPS\Output::i()->breadcrumb[] = array( \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=notifications' ), \IPS\Member::loggedIn()->language()->addToStack( 'menu__core_membersettings_notifications' ) );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('notificationsettings');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('global')->block( 'notificationsettings', $form, FALSE );
}
/**
* Member Auto Follow Preferences
*
@@ -178,14 +387,25 @@ class _notifications extends \IPS\Dispatcher\Controller
protected function updateMemberFollowPrefs()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'profiles_manage' );
\IPS\Session::i()->csrfCheck();
/* Do standard preferences */
\IPS\Db::i()->delete( 'core_notification_preferences' );
/* Do "extra" preferences */
foreach ( \IPS\Application::allExtensions( 'core', 'Notifications' ) as $k => $extension )
{
if ( method_exists( $extension, 'resetExtra' ) )
{
$extension->resetExtra();
}
}
/* Sounds is special */
\IPS\Db::i()->update( 'core_members', 'members_bitoptions2 = members_bitoptions2 ' . ( !\IPS\Settings::i()->notification_prefs_sound ? '|' : '&~' ) . \IPS\Member::$bitOptions['members_bitoptions']['members_bitoptions2']['disable_notification_sounds'] );
$autoTrack = json_encode( array(
'content' => \IPS\Settings::i()->auto_follow_new_content ? 1 : 0,
'comments' => \IPS\Settings::i()->auto_follow_replied_to ? 1 : 0,
'method' => 'immediate'
) );
\IPS\Db::i()->update( 'core_members', array( 'auto_track' => $autoTrack ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=notifications' ), 'saved' );
/* Log and redirect */
\IPS\Session::i()->log( 'acplog__notification_settings_existing' );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=notifications' ), 'reset' );
}
}
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\membersettings;
/* 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;
@@ -22,10 +22,20 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _profilecompletion extends \IPS\Node\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Node Class
*/
protected $nodeClass = '\IPS\Member\ProfileStep';
/**
* Show the "add" button in the page root rather than the table root
*/
protected $_addButtonInRoot = FALSE;
/**
* Get Root Buttons
@@ -78,6 +88,8 @@ class _profilecompletion extends \IPS\Node\Controller
*/
public function enableQuickRegister()
{
\IPS\Session::i()->csrfCheck();
\IPS\Settings::i()->changeValues( array( 'quick_register' => 1 ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=profiles&tab=profilecompletion' ), 'profile_complete_quick_register_off_enabled' );
@@ -114,16 +126,9 @@ class _profilecompletion extends \IPS\Node\Controller
$values = $step->formatFormValues( $values );
$step->extension = \IPS\Member\ProfileStep::findExtensionFromAction( $values['step_completion_act'] );
if ( isset( $values['step_required'] ) )
{
$step->required = $values['step_required'];
}
else
{
$step->required = FALSE;
}
$step->required = ( isset( $values['step_required'] ) ) ? $values['step_required'] : FALSE;
$step->completion_act = $values['step_completion_act'];
$step->subcompletion_act = $values['step_subcompletion_act'];
$step->subcompletion_act = isset( $values['step_subcompletion_act'] ) ? $values['step_subcompletion_act'] : NULL;
$step->save();
$step->postSaveForm( $values );
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\membersettings;
/* 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;
@@ -22,11 +22,21 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _profilefields extends \IPS\Node\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Node Class
*/
protected $nodeClass = '\IPS\core\ProfileFields\Group';
/**
* Show the "add" button in the page root rather than the table root
*/
protected $_addButtonInRoot = FALSE;
/**
* Execute
*
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\membersettings;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _profiles extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Call
*
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\membersettings;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _profilesettings extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -30,6 +35,9 @@ class _profilesettings extends \IPS\Dispatcher\Controller
public function execute()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'profiles_manage' );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'admin_members.js', 'core' ) );
parent::execute();
}
@@ -41,14 +49,9 @@ class _profilesettings extends \IPS\Dispatcher\Controller
public function manage()
{
$form = new \IPS\Helpers\Form;
$form->addHeader( 'photos' );
$form->add( new \IPS\Helpers\Form\YesNo( 'allow_gravatars', \IPS\Settings::i()->allow_gravatars ) );
if( \IPS\Image::canWriteText() )
{
$form->add( new \IPS\Helpers\Form\Radio( 'letter_photos', \IPS\Settings::i()->letter_photos, FALSE, array( 'options' => array( 'default' => 'letterphoto_default', 'letters' => 'letterphoto_letters' ) ) ) );
}
$form->addHeader( 'photos' );
$form->add( new \IPS\Helpers\Form\Radio( 'letter_photos', \IPS\Settings::i()->letter_photos, FALSE, array( 'options' => array( 'default' => 'letterphoto_default', 'letters' => 'letterphoto_letters' ) ) ) );
$form->addHeader( 'usernames' );
$form->add( new \IPS\Helpers\Form\Custom( 'user_name_length', array( \IPS\Settings::i()->min_user_name_length, \IPS\Settings::i()->max_user_name_length ), FALSE, array(
@@ -71,9 +74,30 @@ class _profilesettings extends \IPS\Dispatcher\Controller
throw new \DomainException('user_name_length_no_match');
}
} ) );
$form->add( new \IPS\Helpers\Form\Text( 'username_characters', \IPS\Settings::i()->username_characters, FALSE, array( 'max' => 255 ) ) );
$form->add( new \IPS\Helpers\Form\Custom( 'username_characters', \IPS\Settings::i()->username_characters, FALSE, array(
'getHtml' => function( $field ) {
$easy = NULL;
if ( preg_match( '/^\/\^\(\(\[(\\\p\{L\}\\\p\{M\}|A\-Za\-z)(\\\p\{N\}|0-9)?(.+)?\]\+\)( )?\?\)\+\$\/u$/', $field->value, $matches ) )
{
$easy['letters'] = ( $matches[1] == '\\p{L}\\p{M}' ) ? 'all' : 'latin';
if ( $matches[2] )
{
$easy['numbers'] = ( $matches[2] == '\\p{N}' ) ? 'all' : 'arabic';
}
else
{
$easy['numbers'] = 'none';
}
$easy['spaces'] = (bool) isset( $matches[4] );
$easy['extra'] = stripslashes( $matches[3] );
}
return \IPS\Theme::i()->getTemplate('members')->usernameRegexSetting( $field->name, $field->value, $easy );
}
) ) );
$form->addHeader( 'signatures' );
$form->add( new \IPS\Helpers\Form\YesNo( 'signatures_enabled', \IPS\Settings::i()->signatures_enabled ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'signatures_enabled', \IPS\Settings::i()->signatures_enabled, FALSE, array( 'togglesOn' => array( 'signatures_mobile', 'signatures_guests' ) ) ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'signatures_mobile', \IPS\Settings::i()->signatures_mobile, FALSE, array(), NULL, NULL, NULL, 'signatures_mobile' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'signatures_guests', \IPS\Settings::i()->signatures_guests, FALSE, array(), NULL, NULL, NULL, 'signatures_guests' ) );
/* Status updates */
$options = array(
@@ -93,22 +117,135 @@ class _profilesettings extends \IPS\Dispatcher\Controller
'options' => array( 'public' => 'profile_birthday_type_public', 'private' => 'profile_birthday_type_private', 'none' => 'profile_birthday_type_none' )
), NULL, NULL, NULL, 'profile_birthday_type' ) );
if( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'profiles', 'member_history_prune' ) )
if( !\IPS\CIC AND \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'profiles', 'member_history_prune' ) )
{
$form->addHeader( 'profile_member_history' );
$form->add( new \IPS\Helpers\Form\Number( 'prune_member_history', \IPS\Settings::i()->prune_member_history, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), \IPS\Member::loggedIn()->language()->addToStack('days'), 'prune_member_history' ) );
$form->add( new \IPS\Helpers\Form\Interval( 'prune_member_history', \IPS\Settings::i()->prune_member_history, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL, 'prune_member_history' ) );
$form->add( new \IPS\Helpers\Form\Interval( 'prune_known_ips', \IPS\Settings::i()->prune_known_ips, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'never' ), function( $val ) {
if( $val > 0 AND $val < 7 )
{
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack( 'form_interval_min_d', FALSE, array( 'pluralize' => array( 6 ) ) ) );
}
}, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL, 'prune_known_ips' ) );
$form->add( new \IPS\Helpers\Form\Interval( 'prune_known_devices', \IPS\Settings::i()->prune_known_devices, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'never' ), function( $val ) {
if( $val > 0 AND $val < 30 )
{
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack( 'form_interval_min_d', FALSE, array( 'pluralize' => array( 29 ) ) ) );
}
}, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL, 'prune_known_devices' ) );
$form->add( new \IPS\Helpers\Form\Interval( 'prune_item_markers', \IPS\Settings::i()->prune_item_markers, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'never' ), function( $val ) {
if( $val > 0 AND $val < 7 )
{
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack( 'form_interval_min_d', FALSE, array( 'pluralize' => array( 6 ) ) ) );
}
}, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL, 'prune_item_markers' ) );
}
$form->addHeader( 'profile_settings_ignore' );
$form->add( new \IPS\Helpers\Form\YesNo( 'ignore_system_on', \IPS\Settings::i()->ignore_system_on, FALSE, array(), NULL, NULL, NULL, 'ignore_system_on' ) );
$form->addHeader( 'profile_settings_display' );
$form->add( new \IPS\Helpers\Form\Radio( 'group_formatting_type', \IPS\Settings::i()->group_formatting, FALSE, array( 'options' => array(
'legacy' => 'group_formatting_type_legacy',
'global' => 'group_formatting_type_global'
) ) ) );
$form->add( new \IPS\Helpers\Form\Radio( 'link_default', \IPS\Settings::i()->link_default, FALSE, array( 'options' => array(
'unread' => 'profile_settings_cvb_unread',
'last' => 'profile_settings_cvb_last',
'first' => 'profile_settings_cvb_first'
) ) ) );
if ( $values = $form->values() )
{
if ( $values['username_characters']['easy'] )
{
$regex = '/^(([';
if ( $values['username_characters']['letters'] == 'all' )
{
$regex .= '\p{L}\p{M}';
}
else
{
$regex .= 'A-Za-z';
}
if ( $values['username_characters']['numbers'] == 'all' )
{
$regex .= '\p{N}';
}
elseif ( $values['username_characters']['numbers'] == 'arabic' )
{
$regex .= '0-9';
}
if ( isset( $values['username_characters']['extra_enabled'] ) )
{
$regex .= preg_quote( $values['username_characters']['extra'], '/' );
}
$regex .= ']+)';
if ( isset( $values['username_characters']['spaces'] ) )
{
$regex .= ' ';
}
$regex .= '?)+$/u';
$values['username_characters'] = $regex;
}
else
{
$values['username_characters'] = $values['username_characters']['regex'];
}
$values['group_formatting'] = $values['group_formatting_type'];
unset( $values['group_formatting_type'] );
$values['min_user_name_length'] = $values['user_name_length'][0];
$values['max_user_name_length'] = $values['user_name_length'][1];
unset( $values['user_name_length'] );
$values['status_updates_mem_enable'] = ( $values['profile_comments'] == 2 ) ? 1 : 0;
$values['profile_comments'] = ( $values['profile_comments'] > 0 ) ? 1 : 0;
/* If we're enabling pruning on a potentially large table, handle that */
if( !\IPS\Settings::i()->prune_member_history AND $values['prune_member_history'] )
{
\IPS\Task::queue( 'core', 'PruneLargeTable', array(
'table' => 'core_member_history',
'where' => array( 'log_date < ?', \IPS\DateTime::create()->sub( new \DateInterval( 'P' . $values['prune_member_history'] . 'D' ) )->getTimestamp() ),
), 4 );
}
if( !\IPS\Settings::i()->prune_known_ips AND $values['prune_known_ips'] )
{
\IPS\Task::queue( 'core', 'PruneLargeTable', array(
'table' => 'core_members_known_ip_addresses',
'where' => array( 'last_seen < ?', \IPS\DateTime::create()->sub( new \DateInterval( 'P' . $values['prune_known_ips'] . 'D' ) )->getTimestamp() ),
), 4 );
}
if( !\IPS\Settings::i()->prune_known_devices AND $values['prune_known_devices'] )
{
\IPS\Task::queue( 'core', 'PruneLargeTable', array(
'table' => 'core_members_known_devices',
'where' => array( 'last_seen < ?', \IPS\DateTime::create()->sub( new \DateInterval( 'P' . $values['prune_known_devices'] . 'D' ) )->getTimestamp() ),
), 4 );
}
if( !\IPS\Settings::i()->prune_item_markers AND $values['prune_item_markers'] )
{
\IPS\Task::queue( 'core', 'PruneLargeTable', array(
'table' => 'core_item_markers',
'where' => array( 'item_member_id IN(?)', \IPS\Db::i()->select( 'member_id', 'core_members', array( 'last_activity < ?', \IPS\DateTime::create()->sub( new \DateInterval( 'P' . $values['prune_item_markers'] . 'D' ) )->getTimestamp() ) ) ),
'deleteJoin' => array(
'column' => 'member_id',
'table' => 'core_members',
'where' => array( 'last_activity < ?', \IPS\DateTime::create()->sub( new \DateInterval( 'P' . $values['prune_item_markers'] . 'D' ) )->getTimestamp() ),
'outerColumn' => 'item_member_id'
)
), 4 );
}
$form->saveAsSettings( $values );
\IPS\Session::i()->log( 'acplog__profile_settings' );
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\membersettings;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _ranks extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -62,7 +67,7 @@ class _ranks extends \IPS\Dispatcher\Controller
}
else
{
return str_repeat("<span class='ipsPip'></span>", intval($val) );
return str_repeat("<span class='ipsPip'></span>", \intval($val) );
}
}
);
@@ -71,44 +76,34 @@ class _ranks extends \IPS\Dispatcher\Controller
$table->sortDirection = $table->sortDirection ?: 'asc';
/* Root Buttons */
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'membersettings', 'ranks_manage' ) )
{
\IPS\Output::i()->sidebar['actions'] = array(
'add' => array(
'primary' => TRUE,
'icon' => 'plus',
'title' => 'member_ranks_add',
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('member_ranks_add') ),
'link' => \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=ranks&do=form' ),
)
);
}
\IPS\Output::i()->sidebar['actions'] = array(
'add' => array(
'primary' => TRUE,
'icon' => 'plus',
'title' => 'member_ranks_add',
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('member_ranks_add') ),
'link' => \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=ranks&do=form' ),
)
);
/* Row buttons */
$table->rowButtons = function( $row )
{
$return = array();
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'membersettings', 'ranks_manage' ) )
{
$return['edit'] = array(
'icon' => 'pencil',
'title' => 'edit',
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('edit') ),
'link' => \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=ranks&do=form&id=' ) . $row['id'],
);
}
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'membersettings', 'ranks_manage' ) )
{
$return['delete'] = array(
'icon' => 'times-circle',
'title' => 'delete',
'link' => \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=ranks&do=delete&id=' ) . $row['id'],
'data' => array( 'delete' => '' ),
);
}
$return['edit'] = array(
'icon' => 'pencil',
'title' => 'edit',
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('edit') ),
'link' => \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=ranks&do=form&id=' ) . $row['id'],
);
$return['delete'] = array(
'icon' => 'times-circle',
'title' => 'delete',
'link' => \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=ranks&do=delete&id=' ) . $row['id'],
'data' => array( 'delete' => '' ),
);
return $return;
};
@@ -142,6 +137,8 @@ class _ranks extends \IPS\Dispatcher\Controller
/**
* Add/Edit Rank
*
* @return void
*/
public function form()
{
@@ -158,7 +155,7 @@ class _ranks extends \IPS\Dispatcher\Controller
$form->add( new \IPS\Helpers\Form\Translatable( 'member_ranks_word_custom', NULL, TRUE, array( 'app' => 'core', 'key' => ( $current ? "core_member_rank_{$current['id']}" : NULL ) ) ) );
$form->add( new \IPS\Helpers\Form\Number( 'member_ranks_posts', $current ? $current['posts'] : 0, TRUE, array( 'min' => 0 ) ) );
$form->add( new \IPS\Helpers\Form\Radio( 'member_use_icon', $current['use_icon'], FALSE, array(
$form->add( new \IPS\Helpers\Form\Radio( 'member_use_icon', $current ? $current['use_icon'] : NULL, FALSE, array(
'parse' => 'raw',
'options' => array( 0 => \IPS\Member::loggedIn()->language()->addToStack('member_ranks_pip_number', FALSE, array( 'htmlsprintf' => array( "<span class='ipsPip'></span>" ) ) ), 1 => \IPS\Member::loggedIn()->language()->addToStack('member_ranks_pip_icon') ),
'toggles' => array( 0 => array('member_ranks_pips'), 1 => array('member_ranks_icon') )
@@ -166,7 +163,7 @@ class _ranks extends \IPS\Dispatcher\Controller
/* Pip Icon */
$pip = NULL;
if ( $current['icon'] )
if ( $current AND $current['icon'] )
{
$pip = \IPS\File::get( 'core_Theme', $current['icon'] );
}
@@ -176,9 +173,9 @@ class _ranks extends \IPS\Dispatcher\Controller
if ( $values = $form->values() )
{
$save = array(
'posts' => intval( $values['member_ranks_posts'] ),
'pips' => intval( $values['member_ranks_num_pips'] ),
'use_icon' => intval( $values['member_use_icon'] ),
'posts' => \intval( $values['member_ranks_posts'] ),
'pips' => \intval( $values['member_ranks_num_pips'] ),
'use_icon' => \intval( $values['member_use_icon'] ),
'icon' => '',
);
@@ -0,0 +1,78 @@
<?php
/**
* @brief referralbanners
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage Core
* @since 7 Aug 2019
*/
namespace IPS\core\modules\admin\membersettings;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* referralbanners
*/
class _referralbanners extends \IPS\Node\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Node Class
*/
protected $nodeClass = 'IPS\core\ReferralBanner';
/**
* Show the "add" button in the page root rather than the table root
*/
protected $_addButtonInRoot = FALSE;
/**
* Title can contain HTML?
*/
public $_titleHtml = TRUE;
/**
* Execute
*
* @return void
*/
public function execute()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'core', 'membersettings', 'referrals_manage' );
parent::execute();
}
/**
* Manage
*
* @return void
*/
public function manage()
{
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate( 'forms', 'core' )->blurb( 'referral_banners_blurb', TRUE, TRUE );
return parent::manage();
}
/**
* Form
*
* @return void
*/
public function form()
{
parent::form();
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('referral_banners');
}
}
@@ -0,0 +1,95 @@
<?php
/**
* @brief referralcommission
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 30 Sep 2019
*/
namespace IPS\core\modules\admin\membersettings;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* referralcommission
*/
class _referralcommission extends \IPS\Node\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Node Class
*/
protected $nodeClass = 'IPS\nexus\CommissionRule';
/**
* Show the "add" button in the page root rather than the table root
*/
protected $_addButtonInRoot = FALSE;
/**
* Execute
*
* @return void
*/
public function execute()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'commission_rules_manage' );
parent::execute();
}
/**
* Manage
*
* @return void
*/
public function manage()
{
\IPS\Output::i()->sidebar['actions'][] = array(
'icon' => 'cog',
'title' => 'commission_settings',
'link' => \IPS\Http\Url::internal( "app=nexus&module=customers&controller=referrals&do=settings" )
);
parent::manage();
}
/**
* Manage
*
* @return void
*/
public function settings()
{
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\YesNo( 'nexus_com_rules', \IPS\Settings::i()->nexus_com_rules, FALSE, array( 'togglesOff' => array( 'nexus_com_rules_alt' ) ), NULL, NULL, NULL, 'nexus_com_rules' ) );
$form->add( new \IPS\Helpers\Form\Translatable( 'nexus_com_rules_alt', NULL, FALSE, array( 'app' => 'nexus', 'key' => 'nexus_com_rules_val', 'editor' => array( 'app' => 'core', 'key' => 'Admin', 'autoSaveKey' => 'nexus_com_rules_alt', 'attachIds' => array( NULL, NULL, 'nexus_com_rules_alt' ) ) ), NULL, NULL, NULL, 'nexus_com_rules_alt' ) );
if ( $values = $form->values() )
{
\IPS\Lang::saveCustom( 'nexus', 'nexus_com_rules_val', $values['nexus_com_rules_alt'] );
unset( $values['nexus_com_rules_alt'] );
$form->saveAsSettings( $values );
/* Clear guest page caches */
\IPS\Data\Cache::i()->clearAll();
\IPS\Output::i()->redirect( \IPS\Http\Url::internal('app=nexus&module=customers&controller=referrals'), 'saved' );
}
\IPS\Output::i()->output = $form;
}
}
@@ -0,0 +1,164 @@
<?php
/**
* @brief referrals
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 07 Aug 2019
*/
namespace IPS\core\modules\admin\membersettings;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Referrals
*/
class _referrals extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Call
*
* @param string $method Method name
* @param mixed $args Method arguments
* @return void
*/
public function __call( $method, $args )
{
$tabs = array();
if( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'membersettings', 'referrals_manage' ) )
{
$tabs['refersettings'] = 'settings';
}
if( \IPS\Settings::i()->ref_on and \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'membersettings', 'referrals_manage' ) )
{
$tabs['referralbanners'] = 'referral_banners';
}
if( \IPS\Settings::i()->ref_on and \IPS\Application::appIsEnabled( 'nexus' ) )
{
$tabs['referralcommission'] = 'referral_commission';
}
if ( isset( \IPS\Request::i()->tab ) and isset( $tabs[ \IPS\Request::i()->tab ] ) )
{
$activeTab = \IPS\Request::i()->tab;
}
else
{
$_tabs = array_keys( $tabs ) ;
$activeTab = array_shift( $_tabs );
}
$classname = 'IPS\core\modules\admin\membersettings\\' . $activeTab;
$class = new $classname;
$class->url = \IPS\Http\Url::internal("app=core&module=membersettings&controller=referrals&tab={$activeTab}");
$class->execute();
if ( $method !== 'manage' or \IPS\Request::i()->isAjax() )
{
return;
}
\IPS\Output::i()->sidebar['actions'] = array(
'history' => array(
'title' => 'referral_history',
'icon' => 'clock-o',
'link' => \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=referrals&do=history' ),
),
);
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_membersettings_referrals');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $activeTab, \IPS\Output::i()->output, \IPS\Http\Url::internal( "app=core&module=membersettings&controller=referrals" ) );
}
/**
* Referral History
*
* @return void
*/
protected function history()
{
$table = new \IPS\Helpers\Table\Db( 'core_referrals', \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=referrals&do=history' ) );
$table->langPrefix = 'referrals_';
/* Columns we need */
$table->include = array( 'photo', 'member_id', 'referred_by', 'joined' );
if( \IPS\Application::appIsEnabled( 'nexus' ) )
{
$table->include[] = 'amount';
}
$table->sortBy = $table->sortBy ?: 'joined';
$table->sortDirection = $table->sortDirection ?: 'DESC';
$table->noSort = array( 'photo', 'member_id', 'referred_by' );
$table->joins = array(
array( 'select' => 'm.*', 'from' => array( 'core_members', 'm' ), 'where' => "core_referrals.member_id=m.member_id" )
);
$table->parsers = array(
'photo' => function( $val, $row )
{
return \IPS\Theme::i()->getTemplate( 'global', 'core' )->userPhoto( \IPS\Member::constructFromData( $row ), 'tiny' );
},
'member_id' => function( $val, $row )
{
if ( $val )
{
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'admin' )->userLink( \IPS\Member::constructFromData( $row ) );
}
else
{
return \IPS\Theme::i()->getTemplate( 'members', 'core', 'admin' )->memberReserved( \IPS\Member::load( $val ) );
}
},
'referred_by' => function( $val, $row )
{
if ( $val )
{
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'admin' )->userLink( \IPS\Member::load( $val ) );
}
else
{
return \IPS\Theme::i()->getTemplate( 'members', 'core', 'admin' )->memberReserved( \IPS\Member::load( $val ) );
}
},
'joined' => function( $val, $row )
{
return \IPS\DateTime::ts( $val )->localeDate();
},
'amount' => function ( $val, $row )
{
$return = array();
if ( $val )
{
foreach ( json_decode( $val, TRUE ) as $currency => $amount )
{
$return[] = new \IPS\nexus\Money( $amount, $currency );
}
}
else
{
$return[] = \IPS\Member::loggedIn()->language()->addToStack('none');
}
return implode( '<br>', $return );
}
);
/* Display */
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('referrals');
\IPS\Output::i()->output = (string) $table;
}
}
@@ -0,0 +1,48 @@
<?php
/**
* @brief Referral Settings
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage Core
* @since 7 Aug 2019
*/
namespace IPS\core\modules\admin\membersettings;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Referral Settings
*/
class _refersettings extends \IPS\Dispatcher\Controller
{
/**
* Manage
*
* @return void
*/
public function execute()
{
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\YesNo( 'ref_on', \IPS\Settings::i()->ref_on, FALSE ) );
if ( $values = $form->values() )
{
$form->saveAsSettings( $values );
/* Clear guest page caches */
\IPS\Data\Cache::i()->clearAll();
\IPS\Output::i()->redirect( \IPS\Http\Url::internal('app=core&module=membersettings&controller=referrals') );
}
\IPS\Output::i()->output = $form;
}
}
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\membersettings;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _reputation extends \IPS\Node\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Node Class
*/
@@ -32,6 +37,11 @@ class _reputation extends \IPS\Node\Controller
*/
public $_titleHtml = TRUE;
/**
* Show the "add" button in the page root rather than the table root
*/
protected $_addButtonInRoot = FALSE;
/**
* Execute
*
@@ -130,7 +140,7 @@ class _reputation extends \IPS\Node\Controller
break;
}
}
if ( count( $names ) == 3 )
if ( \count( $names ) == 3 )
{
$names[] = \IPS\Member::loggedIn()->language()->addToStack( 'like_blurb_others', FALSE, array( 'pluralize' => array( 2 ) ) );
}
@@ -140,7 +150,7 @@ class _reputation extends \IPS\Node\Controller
}
else
{
$blurb = \IPS\Member::loggedIn()->language()->addToStack( 'like_blurb', FALSE, array( 'htmlsprintf' => array( \IPS\Member::loggedIn()->language()->formatList( $names ) ), 'pluralize' => array( count( $names ) ) ) );
$blurb = \IPS\Member::loggedIn()->language()->addToStack( 'like_blurb', FALSE, array( 'htmlsprintf' => array( \IPS\Member::loggedIn()->language()->formatList( $names ) ), 'pluralize' => array( \count( $names ) ) ) );
}
/* Build Form */
@@ -174,9 +184,9 @@ class _reputation extends \IPS\Node\Controller
/* Build Form */
$form = new \IPS\Helpers\Form();
$form->add( new \IPS\Helpers\Form\YesNo( 'reputation_leaderboard_on', \IPS\Settings::i()->reputation_leaderboard_on, FALSE, array( 'togglesOn' => array( 'reputation_leaderboard_default_tab', 'form_header_leaderboard_tabs_leaderboard', 'reputation_show_days_won_trophy', 'reputation_timezone', 'form_header_leaderboard_tabs_members', 'reputation_top_members_overview', 'reputation_overview_max_members', 'reputation_top_members_filters', 'reputation_max_members' ) ), NULL, NULL, NULL, 'reputation_leaderboard_on' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'reputation_leaderboard_on', \IPS\Settings::i()->reputation_leaderboard_on, FALSE, array( 'togglesOn' => array( 'reputation_leaderboard_default_tab', 'form_header_leaderboard_tabs_leaderboard', 'reputation_show_days_won_trophy', 'reputation_timezone', 'form_header_leaderboard_tabs_members', 'reputation_top_members_overview', 'reputation_overview_max_members', 'reputation_top_members_filters', 'reputation_max_members', 'leaderboard_excluded_groups' ) ), NULL, NULL, NULL, 'reputation_leaderboard_on' ) );
$form->add( new \IPS\Helpers\Form\Radio( 'reputation_leaderboard_default_tab', \IPS\Settings::i()->reputation_leaderboard_default_tab, FALSE, array( 'options' => array( 'leaderboard' => 'leaderboard_tabs_leaderboard', 'history' => 'leaderboard_tabs_history', 'members' => 'leaderboard_tabs_members' ) ), NULL, NULL, NULL, 'reputation_leaderboard_default_tab' ) );
$form->add( new \IPS\Helpers\Form\Select( 'leaderboard_excluded_groups', \IPS\Settings::i()->leaderboard_excluded_groups, FALSE, array( 'options' => \IPS\Member\Group::groups( TRUE, FALSE ), 'parse' => 'normal', 'multiple' => TRUE ), NULL, NULL, NULL, 'leaderboard_excluded_groups' ) );
$form->add( new \IPS\Helpers\Form\Select( 'leaderboard_excluded_groups', explode( ',', \IPS\Settings::i()->leaderboard_excluded_groups ), FALSE, array( 'options' => \IPS\Member\Group::groups( TRUE, FALSE ), 'parse' => 'normal', 'multiple' => TRUE ), NULL, NULL, NULL, 'leaderboard_excluded_groups' ) );
$form->addHeader('leaderboard_tabs_leaderboard');
$form->add( new \IPS\Helpers\Form\YesNo( 'reputation_show_days_won_trophy', \IPS\Settings::i()->reputation_show_days_won_trophy, FALSE, array( 'togglesOn' => array() ), NULL, NULL, NULL, 'reputation_show_days_won_trophy' ) );
$form->add( new \IPS\Helpers\Form\Timezone( 'reputation_timezone', \IPS\Settings::i()->reputation_timezone, FALSE, array(), NULL, NULL, NULL, 'reputation_timezone' ) );
@@ -191,6 +201,7 @@ class _reputation extends \IPS\Node\Controller
{
/* Save */
$values['reputation_top_members_overview'] = implode( ',', $values['reputation_top_members_overview'] );
$values['leaderboard_excluded_groups'] = implode( ',', $values['leaderboard_excluded_groups'] );
$values['reputation_top_members_filters'] = $values['reputation_top_members_filters'] == array_keys( $topMemberFilters ) ? '*' : implode( ',', $values['reputation_top_members_filters'] );
$form->saveAsSettings( $values );
@@ -374,6 +385,7 @@ class _reputation extends \IPS\Node\Controller
}
else
{
\IPS\Session::i()->csrfCheck();
\IPS\Task::queue( 'core', 'RebuildReputationLeaderboard', array(), 4 );
\IPS\Db::i()->delete('core_reputation_leaderboard_history');
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=reputation&tab=leaderboard' ), 'reputation_leaderboard_rebuilding' );
@@ -436,7 +448,7 @@ class _reputation extends \IPS\Node\Controller
try
{
$node = call_user_func( "{$nodeClass}::load", \IPS\Request::i()->id );
$node = $nodeClass::load( \IPS\Request::i()->id );
}
catch ( \OutOfRangeException $e )
{
@@ -483,7 +495,7 @@ class _reputation extends \IPS\Node\Controller
}
else
{
\IPS\Output::i()->redirect( $this->url->setQueryString( array( 'root' => ( $node->parent() ? $node->parent()->_id : '' ) ) ), 'deleted' );
\IPS\Output::i()->redirect( $this->url->setQueryString( 'tab', 'reactions' )->setQueryString( array( 'root' => ( $node->parent() ? $node->parent()->_id : '' ) ) ), 'deleted' );
}
}
@@ -12,7 +12,7 @@
namespace IPS\core\modules\admin\membersettings;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
@@ -24,6 +24,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _spam extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
Whitespace-only changes.
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\messengerstats;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _pmstats extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Manage Members
*
@@ -41,10 +46,10 @@ class _pmstats extends \IPS\Dispatcher\Controller
'areaOpacity' => 0.4
) );
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('new_conversations'), 'number', 'SUM(msg_is_first_post)' );
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('mt_replies'), 'number', '( COUNT(*) - SUM(msg_is_first_post) )' );
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('mt_replies'), 'number', '( count(*) - SUM(msg_is_first_post) )' );
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_messages_title');
$chart->availableTypes = array( 'AreaChart', 'ColumnChart', 'BarChart' );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_messengerstats_pmstats');
\IPS\Output::i()->output = (string) $chart;
}
@@ -0,0 +1,308 @@
<?php
/**
* @brief menu
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 12 Jun 2019
*/
namespace IPS\core\modules\admin\mobile;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* menu
*/
class _menu extends \IPS\Dispatcher\Controller
{
/**
* Execute
*
* @return void
*/
public function execute()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'menu_manage' );
\IPS\Output::i()->responsive = FALSE;
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'system/menumanager.css', 'core', 'admin' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'admin_system.js', 'core', 'admin' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'jquery/jquery.nestedSortable.js', 'core', 'interface' ) );
parent::execute();
}
/**
* Manage Menu
*
* @return void
*/
protected function manage()
{
/* Get items from the database */
$items = array();
foreach ( \IPS\Db::i()->select( '*', 'core_mobileapp_menu', NULL, 'position' ) as $item )
{
if ( \IPS\Application::appIsEnabled( $item['app'] ) )
{
$class = 'IPS\\' . $item['app'] . '\extensions\core\MobileNavigation\\' . $item['extension'];
if ( class_exists( $class ) )
{
$items[ $item['id'] ] = new $class( json_decode( $item['config'], TRUE ), $item['id'], $item['permissions'] );
}
}
}
/* Display */
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_mobile_menu');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'applications' )->menuManager( array( 0 => $items ), array(), \IPS\Http\Url::internal('app=core&module=mobile&controller=menu'), FALSE, FALSE );
}
/**
* Add Menu Item
*
* @return void
*/
protected function form()
{
/* What menu items have we already configured? */
$current = array();
foreach ( \IPS\Db::i()->select( '*', 'core_mobileapp_menu', NULL, 'position' ) as $item )
{
if ( !isset( $current[ $item['app'] ][ $item['extension'] ] ) )
{
$current[ $item['app'] ][ $item['extension'] ] = 0;
}
$current[ $item['app'] ][ $item['extension'] ]++;
}
/* Are we editing an existing item? */
$existing = NULL;
if ( \IPS\Request::i()->id )
{
try
{
$existing = \IPS\Db::i()->select( '*', 'core_mobileapp_menu', array( 'id=?', \IPS\Request::i()->id ) )->first();
}
catch ( \OutOfRangeException $e ) { }
}
/* What options are available? */
$options = array();
$disabled = array();
$toggles = array();
$fieldNames = array();
$extraFields = array();
$haveInheritable = FALSE;
foreach ( \IPS\Application::allExtensions( 'core', 'MobileNavigation', FALSE, 'core', NULL, FALSE ) as $key => $class )
{
if ( method_exists( $class, 'typeTitle' ) )
{
$exploded = explode( '_', $key );
if ( $class::allowMultiple() or !isset( $current[ $exploded[0] ][ $exploded[1] ] ) or ( $existing and $existing['app'] == $exploded[0] and $existing['extension'] == $exploded[1] ) )
{
$options[ $key ] = $class::typeTitle();
foreach ( $class::configuration( $existing ? json_decode( $existing['config'], TRUE ) : array(), $existing ? $existing['id'] : NULL ) as $field )
{
if ( !$field->htmlId )
{
$field->htmlId = md5( mt_rand() );
}
$toggles[ $key ][ $field->name ] = $field->htmlId;
$fieldNames[ $key ][] = $field->name;
$extraFields[] = $field;
}
if ( $class::permissionsCanInherit() )
{
$haveInheritable = TRUE;
$toggles[ $key ][] = 'menu_manager_access_type';
}
else
{
$toggles[ $key ][] = 'menu_manager_access';
}
}
if ( !$class::isEnabled() )
{
$disabled[] = $key;
}
}
}
/* Create the form */
$form = new \IPS\Helpers\Form( 'menu_item', 'save_menu_item' );
$form->class = 'ipsForm_vertical';
$form->add( new \IPS\Helpers\Form\Radio( 'menu_manager_extension', $existing ? "{$existing['app']}_{$existing['extension']}" : NULL, TRUE, array( 'options' => $options, 'toggles' => $toggles, 'disabled' => $disabled ) ) );
foreach ( $extraFields as $field )
{
$form->add( $field );
}
$groups = array();
foreach ( \IPS\Member\Group::groups() as $group )
{
$groups[ $group->g_id ] = $group->name;
}
if ( $haveInheritable )
{
$form->add( new \IPS\Helpers\Form\Radio( 'menu_manager_access_type', ( $existing and $existing['permissions'] !== NULL ) ? 1 : 0, TRUE, array(
'options' => array( 0 => 'menu_manager_access_type_inherit', 1 => 'menu_manager_access_type_override' ),
'toggles' => array( 1 => array( 'menu_manager_access' ) )
), NULL, NULL, NULL, 'menu_manager_access_type' ) );
}
$form->add( new \IPS\Helpers\Form\Select( 'menu_manager_access', $existing ? ( $existing['permissions'] == '*' ? '*' : explode( ',', $existing['permissions'] ) ) : '*', NULL, array( 'multiple' => TRUE, 'options' => $groups, 'unlimited' => '*', 'unlimitedLang' => 'everyone' ), NULL, NULL, NULL, 'menu_manager_access' ) );
if( !$existing ){
$form->hiddenValues['newItem'] = TRUE;
}
/* Handle submissions */
if ( $values = $form->values() )
{
$exploded = explode( '_', $values['menu_manager_extension'] );
$class = 'IPS\\' . $exploded[0] . '\extensions\core\MobileNavigation\\' . $exploded[1];
$config = array();
if ( isset( $fieldNames[ $values['menu_manager_extension'] ] ) )
{
foreach ( $values as $k => $v )
{
if ( \in_array( $k, $fieldNames[ $values['menu_manager_extension'] ] ) )
{
$config[ $k ] = $v;
}
}
}
$save = array(
'app' => $exploded[0],
'extension' => $exploded[1],
'config' => json_encode( $config ),
);
/* First we need to determine if the access type option was even shown */
$hasAccessType = ( \in_array( 'menu_manager_access_type', $toggles[ $values['menu_manager_extension'] ] ) ) ? TRUE : FALSE;
/* If we didn't have the access type field, then we should just check the permissions that were specified */
if ( !$hasAccessType or $values['menu_manager_access_type'] )
{
$save['permissions'] = $values['menu_manager_access'] == '*' ? '*' : implode( ',', $values['menu_manager_access'] );
}
else
{
$save['permissions'] = $class::permissionsCanInherit() ? NULL : '';
}
if ( $existing )
{
$id = $existing['id'];
$_config = $class::parseConfiguration( $config, $id );
if ( $_config != $config )
{
$save['config'] = json_encode( $_config );
}
\IPS\Db::i()->update( 'core_mobileapp_menu', $save, array( 'id=?', $id ) );
}
else
{
try
{
$save['position'] = \IPS\Db::i()->select( 'MAX(position)', 'core_mobileapp_menu' )->first() + 1;
}
catch ( \UnderflowException $e )
{
$save['position'] = 1;
}
$id = \IPS\Db::i()->insert( 'core_mobileapp_menu', $save );
$_config = $class::parseConfiguration( $config, $id );
if ( $_config != $config )
{
\IPS\Db::i()->update( 'core_mobileapp_menu', array( 'config' => json_encode( $_config ) ), array( 'id=?', $id ) );
}
}
if( \IPS\Request::i()->isAjax() )
{
$item = new $class( $_config, $id, $save['permissions'] );
$output = array(
'menu_item' => \IPS\Theme::i()->getTemplate( 'applications' )->menuItem( $item, $id, \IPS\Http\Url::internal('app=core&module=mobile&controller=menu') ),
'id' => $id
);
\IPS\Output::i()->json( $output );
}
else
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal('app=core&module=mobile&controller=menu') );
}
}
/* Display */
\IPS\Output::i()->output = $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'applications', 'core' ), 'menuManagerForm' ) );
if ( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->sendOutput( \IPS\Output::i()->output );
}
}
/**
* Remove an item
*
* @return void
*/
protected function remove()
{
\IPS\Session::i()->csrfCheck();
static::_remove( \intval( \IPS\Request::i()->id ) );
\IPS\Output::i()->json('OK');
}
/**
* Remove a menu item
*
* @param int $id ID of item to remove
* @return void
*/
protected static function _remove( $id )
{
\IPS\Db::i()->delete( 'core_mobileapp_menu', array( 'id=?', $id ) );
/* remove the title language strings */
\IPS\Lang::deleteCustom( 'core', "mobilemenu_item_{$id}" );
}
/**
* Reorder Items
*
* @return void
*/
protected function reorder()
{
\IPS\Session::i()->csrfCheck();
$position = 1;
foreach ( \IPS\Request::i()->menu_order as $id )
{
\IPS\Db::i()->update( 'core_mobileapp_menu', array(
'position' => $position++,
), array( 'id=?', $id ) );
}
\IPS\Output::i()->json('OK');
}
}
@@ -0,0 +1,647 @@
<?php
/**
* @brief Mobile App Settings
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 18 Mar 2019
*/
namespace IPS\core\modules\admin\mobile;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Mobile App Settings
*/
class _mobile extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
* @return void
*/
public function execute()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'mobile_manage' );
parent::execute();
}
/**
* Mobile App Page
*
* @return void
*/
protected function manage()
{
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'menu__core_mobile' );
/* If the mobile app has been set up, show the details page... */
if ( \IPS\Settings::i()->mobile_app_id )
{
try
{
/* Get details from directory */
$details = \IPS\Http\Url::ips('mobile/directory')->setQueryString( array( 'id' => \IPS\Settings::i()->mobile_app_id ) )->request()->login( \IPS\Settings::i()->mobile_app_id, \IPS\Settings::i()->mobile_app_secret )->get()->decodeJson();
if ( \count( $details ) !== 1 )
{
if ( \count( $details ) === 0 )
{
\IPS\Settings::i()->changeValues( array( 'mobile_app_id' => '', 'mobile_app_secret' => '' ) );
return $this->_notSetup();
}
else
{
throw new \DomainException;
}
}
$details = $details[0];
$langaugeOptions = \IPS\Http\Url::ips('mobile/languages')->request()->get()->decodeJson();
$languages = [];
foreach ( $details['languages'] as $langCode )
{
if ( array_key_exists( $langCode, $langaugeOptions ) )
{
$langauges[] = $langaugeOptions[ $langCode ];
}
}
$details['languages'] = $langauges;
/* If it says the status is other than what we expect, refresh our license key cache */
if ( $licenseKey = \IPS\IPS::licenseKey() and ( ( $details['status'] === 'active' and !$licenseKey['active'] ) or ( $details['status'] === 'expired' and !$licenseKey['active'] ) ) )
{
$licenseKey = \IPS\IPS::licenseKey( TRUE );
}
/* Unless we're suspended, show a button to turn off mobile app integration */
if ( $details['category'] === 'unlisted' or \in_array( $details['status'], array( 'pending', 'active', 'expired' ) ) )
{
\IPS\Output::i()->sidebar['actions']['deregister'] = array(
'icon' => 'times',
'title' => 'disable',
'link' => \IPS\Http\Url::internal('app=core&module=mobile&controller=mobile&do=deregister')->csrf(),
'data' => array(
'confirm' => '',
'confirmSubMessage' => $details['category'] === 'unlisted' ? '' : \IPS\Member::loggedIn()->language()->addToStack('mobile_app_deregister_confirm')
)
);
}
/* Build the settings form */
$settings = NULL;
if ( \in_array( $details['status'], array( 'active' ) ) )
{
\IPS\Output::i()->sidebar['actions']['tokens'] = array(
'icon' => 'key',
'title' => 'oauth_view_authorizations',
'link' => \IPS\Http\Url::internal('app=core&module=applications&controller=api&tab=oauth')->setQueryString( array( 'do' => 'tokens', 'client_id' => $this->_getClient()->client_id ) )
);
$settings = new \IPS\Helpers\Form;
$settings->add( new \IPS\Helpers\Form\Sort( 'mobile_app_home_blocks_order', json_decode( \IPS\Settings::i()->mobile_app_home_blocks_order, TRUE ), FALSE, array( 'checkboxes' => 'mobile_app_home_blocks_' ) ) );
if ( $details['category'] === 'unlisted' )
{
$settings->add( new \IPS\Helpers\Form\Text( 'mobile_app_redirect_scheme', \IPS\Settings::i()->mobile_app_redirect_scheme, FALSE, array( 'placeholder' => 'your-app-schema' ), NULL, NULL, '://', 'mobile_app_redirect_scheme' ) );
}
$settings->add( new \IPS\Helpers\Form\YesNo( 'mobile_app_prompt', \IPS\Settings::i()->mobile_app_prompt, FALSE, array( 'togglesOn' => array( 'mobile_app_itunes_id', 'mobile_app_itunes_url', 'mobile_app_play_id', 'mobile_app_play_url', 'itunes_affiliate_string' ) ) ) );
if ( $details['category'] === 'unlisted' )
{
$settings->add( new \IPS\Helpers\Form\Text( 'mobile_app_itunes_id', \IPS\Settings::i()->mobile_app_itunes_id, FALSE, array( 'placeholder' => '123456789' ), NULL, NULL, NULL, 'mobile_app_itunes_id' ) );
$settings->add( new \IPS\Helpers\Form\Url( 'mobile_app_itunes_url', \IPS\Settings::i()->mobile_app_itunes_url, FALSE, array( 'placeholder' => 'https://itunes.apple.com/us/app/my-app/123456789' ), NULL, NULL, NULL, 'mobile_app_itunes_url' ) );
$settings->add( new \IPS\Helpers\Form\Text( 'mobile_app_play_id', \IPS\Settings::i()->mobile_app_play_id, FALSE, array( 'placeholder' => 'com.company.appname' ), NULL, NULL, NULL, 'mobile_app_play_id' ) );
$settings->add( new \IPS\Helpers\Form\Url( 'mobile_app_play_url', \IPS\Settings::i()->mobile_app_play_url, FALSE, array( 'placeholder' => 'https://play.google.com/store/apps/details?id=com.company.appname' ), NULL, NULL, NULL, 'mobile_app_play_url' ) );
}
$settings->add( new \IPS\Helpers\Form\Text( 'itunes_affiliate_string', \IPS\Settings::i()->itunes_affiliate_string, FALSE, array(), NULL, NULL, NULL, 'itunes_affiliate_string' ) );
if ( $values = $settings->values() )
{
$values['mobile_app_home_blocks_order'] = json_encode( $values['mobile_app_home_blocks_order'] );
if ( $details['category'] !== 'unlisted' )
{
$values['mobile_app_redirect_scheme'] = '';
$values['mobile_app_itunes_id'] = '';
$values['mobile_app_itunes_url'] = '';
$values['mobile_app_play_id'] = '';
$values['mobile_app_play_url'] = '';
}
$settings->saveAsSettings( $values );
if ( $details['category'] == 'unlisted' and $values['mobile_app_redirect_scheme'] )
{
$this->_updateClient( $this->_getClient() );
}
unset( \IPS\Data\Store::i()->manifest );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal('app=core&module=mobile&controller=mobile') );
}
}
elseif ( \IPS\Settings::i()->mobile_app_prompt )
{
\IPS\Settings::i()->changeValues( array( 'mobile_app_prompt' => 0 ) );
unset( \IPS\Data\Store::i()->manifest );
}
/* Display */
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'mobile' )->details( $details, $settings );
}
catch ( \Exception $e )
{
\IPS\Output::i()->error( 'mobile_app_registration_error', '4C397/7', 500, NULL, array(), \IPS\IPS::getExceptionDetails( $e ) );
}
}
/* Otherwise show the splash screen */
else
{
return $this->_notSetup();
}
}
/**
* Mobile App: Not Setup
*
* @return void
*/
protected function _notSetup()
{
\IPS\Output::i()->sidebar['actions']['manual'] = array(
'icon' => 'cog',
'title' => 'mobile_app_splash_manual',
'link' => \IPS\Http\Url::internal('app=core&module=mobile&controller=mobile&do=manual'),
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('mobile_app_splash_manual') )
);
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'mobile' )->splash();
}
/**
* Mobile App: Submit to Multi Community
*
* @return void
*/
protected function submit()
{
/* Before we do anything, check the license key */
if ( $licenseKey = \IPS\IPS::licenseKey( TRUE ) )
{
if ( !$licenseKey['active'] )
{
\IPS\Output::i()->error( 'mobile_app_requires_active_license', '1C397/8', 403 );
}
}
else
{
\IPS\Output::i()->error( 'mobile_app_requires_license', '1C397/9', 403 );
}
/* Create the form */
$form = new \IPS\Helpers\Form( 'mobile_app_submit', 'submit' );
$existing = NULL;
if ( \IPS\Settings::i()->mobile_app_id )
{
try
{
$details = \IPS\Http\Url::ips('mobile/directory')->setQueryString( array( 'id' => \IPS\Settings::i()->mobile_app_id ) )->request()->login( \IPS\Settings::i()->mobile_app_id, \IPS\Settings::i()->mobile_app_secret )->get()->decodeJson();
if ( \count( $details ) !== 1 )
{
throw new \DomainException;
}
$existing = $details[0];
}
catch ( \Exception $e )
{
\IPS\Output::i()->error( 'mobile_app_registration_error', '4C397/6', 500, NULL, array(), \IPS\IPS::getExceptionDetails( $e ) );
}
$form->addMessage('mobile_app_listing_update_info');
}
$form->add( new \IPS\Helpers\Form\Text( 'mobile_app_listing_name', $existing ? ( $existing['pending_name'] ?: $existing['name'] ) : \IPS\Settings::i()->board_name, TRUE ) );
$form->add( new \IPS\Helpers\Form\TextArea( 'mobile_app_listing_desc', $existing ? ( $existing['pending_description'] ?: $existing['description'] ) : NULL, TRUE, array( 'maxLength' => 125 ) ) );
if ( $existing )
{
$form->add( new \IPS\Helpers\Form\Radio( 'mobile_app_listing_logo_choose', 'keep', TRUE, array(
'options' => array(
'keep' => 'mobile_app_listing_logo_keep',
'change' => 'mobile_app_listing_logo_change',
),
'toggles' => array(
'change' => array( 'mobile_app_listing_logo_upload' )
)
) ) );
}
$homeScreen = json_decode( \IPS\Settings::i()->icons_homescreen, TRUE ) ?? array();
$form->add( new \IPS\Helpers\Form\Upload( 'mobile_app_listing_logo', $existing ? NULL : ( ( isset( $homeScreen['original'] ) ) ? \IPS\File::get( 'core_Icons', $homeScreen['original'] ) : NULL ), TRUE, array( 'image' => TRUE, 'storageExtension' => 'core_Icons' ), function ( $file ) {
if ( $file )
{
$image = \IPS\Image::create( $file->contents() );
if ( $image->width !== $image->height )
{
throw new \DomainException('mobile_app_listing_logo_square');
}
if ( $image->width < 512 )
{
throw new \DomainException('mobile_app_listing_logo_dims');
}
}
}, NULL, NULL, 'mobile_app_listing_logo_upload' ) );
try
{
$categoryData = \IPS\Http\Url::ips('mobile/categories')->request()->get()->decodeJson();
$categories = array();
foreach( $categoryData as $_key => $_cat )
{
$categories[ $_key ] = $_cat['title'];
}
$form->add( new \IPS\Helpers\Form\Select( 'mobile_app_listing_category', $existing ? $existing['category'] : 'general', TRUE, array( 'options' => $categories ) ) );
$langaugeOptions = \IPS\Http\Url::ips('mobile/languages')->request()->get()->decodeJson();
if ( $existing )
{
$langauges = explode( ',', $existing['languages'] );
}
else
{
$langauges = [];
foreach ( \IPS\Lang::languages() as $lang )
{
$code = mb_substr( $lang->bcp47(), 0, 2 );
if ( array_key_exists( $code, $langaugeOptions ) )
{
$langauges[] = $code;
}
}
if ( !$langauges )
{
$langauges = ['en'];
}
}
$form->add( new \IPS\Helpers\Form\Select( 'mobile_app_listing_languages', $existing ? explode( ',', $existing['languages'] ): ['en'], TRUE, array( 'multiple' => TRUE, 'options' => $langaugeOptions ) ) );
}
catch ( \Exception $e )
{
\IPS\Output::i()->error( 'mobile_app_registration_error', '4C397/3', 500, NULL, array(), \IPS\IPS::getExceptionDetails( $e ) );
}
/* Handle submissions */
if ( $values = $form->values() )
{
/* If we haven't set application icons for our manifest, use the icon provided */
if ( !isset( $homeScreen['original'] ) )
{
\IPS\Settings::i()->changeValues( \IPS\core\modules\admin\customization\icons::processApplicationIcon( array( 'icons_homescreen' => $values['mobile_app_listing_logo'] ), $homeScreen ) );
}
try
{
/* Work out logo */
$logo = NULL;
$logoExt = NULL;
$logoMime = NULL;
if ( !$existing or $values['mobile_app_listing_logo_choose'] === 'change' )
{
$logo = $values['mobile_app_listing_logo']->contents();
$logoExt = mb_substr( $values['mobile_app_listing_logo']->originalFilename, mb_strrpos( $values['mobile_app_listing_logo']->originalFilename, '.' ) + 1 );
$logoMime = \IPS\File::getMimeType( $values['mobile_app_listing_logo']->originalFilename );
}
/* Submit to IPS */
$response = $this->_updateRegistration( array(
'name' => $values['mobile_app_listing_name'],
'description' => $values['mobile_app_listing_desc'],
'category' => $values['mobile_app_listing_category'],
'logo' => $logo,
'logo_type' => $logoExt,
'logo_mime' => $logoMime,
'languages' => implode( ',', $values['mobile_app_listing_languages'] )
), $existing ? \IPS\Settings::i()->mobile_app_id : NULL, $existing ? \IPS\Settings::i()->mobile_app_secret : NULL );
/* If we already had an icon for our manifest, delete the logo we just uploaded */
if ( isset( $homeScreen['original'] ) and $values['mobile_app_listing_logo'] and (string) $homeScreen['original'] != (string) $values['mobile_app_listing_logo'] )
{
$values['mobile_app_listing_logo']->delete();
}
}
catch ( \DomainException $e )
{
\IPS\Output::i()->error( $e->getMessage(), '2C397/4', 500 );
}
catch ( \Exception $e )
{
\IPS\Output::i()->error( 'mobile_app_registration_error', '4C397/5', 500, NULL, array(), \IPS\IPS::getExceptionDetails( $e ) );
}
if ( $existing )
{
\IPS\Session::i()->log( 'acplogs__mobile_app_updated' );
}
else
{
$this->_doEnable( $response['id'], $response['secret'] );
\IPS\Session::i()->log( 'acplogs__mobile_app_registered' );
}
\IPS\Output::i()->redirect( \IPS\Http\Url::internal('app=core&module=mobile&controller=mobile') );
}
/* Display */
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'menu__core_mobile_mobile' );
\IPS\Output::i()->output = $form;
}
/**
* Mobile App: Activate Manually
*
* @return void
*/
protected function manual()
{
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'mobile_app_splash_manual' );
$form = new \IPS\Helpers\Form;
$form->addDummy( 'oauth_client_id', $this->_getClient( TRUE )->client_id );
$form->add( new \IPS\Helpers\Form\Text( 'mobile_app_splash_manual_id', NULL, TRUE, array(), function( $val ) {
try
{
$response = \IPS\Http\Url::ips('mobile/directory')->setQueryString( array( 'id' => $val ) )->request()->get()->decodeJson();
}
catch ( \Exception $e )
{
throw new \DomainException('mobile_app_registration_error');
}
if ( \count( $response ) !== 1 )
{
throw new \DomainException('mobile_app_splash_manual_error');
}
if ( $response[0]['url'] !== \IPS\Settings::i()->base_url )
{
throw new \DomainException('mobile_app_splash_manual_error_url');
}
} ) );
$form->add( new \IPS\Helpers\Form\Text( 'mobile_app_splash_manual_secret', NULL, TRUE, array(), function( $val ) {
try
{
$response = \IPS\Http\Url::ips('mobile/notify')->request()->login( \IPS\Request::i()->mobile_app_splash_manual_id, $val )->post( array( 'data' => array() ) );
}
catch ( \Exception $e )
{
throw new \DomainException('mobile_app_registration_error');
}
if ( $response->httpResponseCode != 200 )
{
throw new \DomainException('mobile_app_splash_manual_error');
}
} ) );
if ( $values = $form->values() )
{
try
{
$this->_updateRegistration( array( ), $values['mobile_app_splash_manual_id'], $values['mobile_app_splash_manual_secret'] );
}
catch ( \DomainException $e )
{
\IPS\Output::i()->error( $e->getMessage(), '2C397/1', 500 );
}
catch ( \Exception $e )
{
\IPS\Output::i()->error( 'mobile_app_registration_error', '4C397/2', 500, NULL, array(), \IPS\IPS::getExceptionDetails( $e ) );
}
$this->_doEnable( $values['mobile_app_splash_manual_id'], $values['mobile_app_splash_manual_secret'] );
\IPS\Session::i()->log( 'acplogs__mobile_app_activated' );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal('app=core&module=mobile&controller=mobile') );
}
\IPS\Output::i()->output = $form;
}
/**
* Mobile App: Deregister
*
* @return void
*/
protected function deregister()
{
\IPS\Session::i()->csrfCheck();
if ( \IPS\Settings::i()->mobile_app_id )
{
try
{
\IPS\Http\Url::ips('mobile/unregister')->request()->login( \IPS\Settings::i()->mobile_app_id, \IPS\Settings::i()->mobile_app_secret )->post( array(
'lkey' => trim( \IPS\Settings::i()->ipb_reg_number ),
) );
}
catch ( \Exception $e )
{
// It doesn't really matter if this failed (which could happen if the stores secret was somehow wrong)
// It might just require tech support to go in and manually remove them before they can register again
}
\IPS\Settings::i()->changeValues( array(
'mobile_app_id' => '',
'mobile_app_secret' => '',
) );
}
if ( $client = $this->_getClient() )
{
$client->delete();
}
\IPS\Session::i()->log( 'acplogs__mobile_app_unregistered' );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=mobile&controller=mobile' ) );
}
/**
* Get OAuth Client if there is one
*
* @bool bool $createIfNot If there isn't one, create one
* @return \IPS\Api\OAuthClient|NULL
*/
protected function _getClient( $createIfNot = FALSE )
{
try
{
return \IPS\Api\OAuthClient::constructFromData( \IPS\Db::i()->select( '*', 'core_oauth_clients', array( 'oauth_type=?', 'mobile' ) )->first() );
}
catch ( \UnderflowException $e )
{
if ( $createIfNot )
{
$client = $this->_createClient();
$this->_updateClient( $client );
return $client;
}
else
{
return NULL;
}
}
}
/**
* Create an OAuth Client
*
* @return \IPS\Api\OAuthClient
*/
protected function _createClient()
{
$client = new \IPS\Api\OAuthClient;
do
{
$client->client_id = \IPS\Login::generateRandomString( 32 );
}
while ( \IPS\Db::i()->select( 'COUNT(*)', 'core_oauth_clients', array( 'oauth_client_id=?', $client->client_id ) )->first() );
$client->enabled = TRUE;
$client->prompt = 'none';
$client->ucp = FALSE;
\IPS\Lang::copyCustom( 'core', 'mobile_app_default_client_name', "core_oauth_client_{$client->client_id}" );
return $client;
}
/**
* Update OAuth Client with appropriate settings
*
* @param \IPS\Api\OAuthClient $client The client
* @return void
*/
protected function _updateClient( \IPS\Api\OAuthClient $client )
{
$client->client_secret = NULL;
foreach ( \IPS\Api\OAuthClient::mobileAppValues() as $k => $v )
{
if ( $k === 'redirect_uris' or $k === 'scopes' )
{
$client->$k = json_encode( $v );
}
else
{
$client->$k = \is_array( $v ) ? implode( ',', $v ) : $v;
}
}
$client->save();
}
/**
* Update registration details on remoteservices
*
* @param array $details Details to update (name, description, etc)
* @param string|NULL $appId If updating, the ID
* @param string|NULL $appSecret If updating, the secret
* @return array
*/
protected function _updateRegistration( $details, $appId = NULL, $appSecret = NULL )
{
/* First of all, create or update our OAuth Client */
$client = $this->_getClient();
if ( !$client )
{
$client = $this->_createClient();
}
$this->_updateClient( $client );
/* Set details to send */
$details['lkey'] = trim( \IPS\Settings::i()->ipb_reg_number );
$details['url'] = \IPS\Settings::i()->base_url;
$details['client_id'] = $client->client_id;
/* Register or update remoteservices */
if ( $appId )
{
$response = \IPS\Http\Url::ips('mobile/register')->request()->login( $appId, $appSecret )->post( $details );
if ( $response->httpResponseCode != 200 )
{
throw new \DomainException( ( (string) $response ) ?: 'mobile_app_registration_error' );
}
}
else
{
$response = \IPS\Http\Url::ips('mobile/register')->request()->post( $details );
if ( $response->httpResponseCode != 201 )
{
throw new \DomainException( ( (string) $response ) ?: 'mobile_app_registration_error' );
}
}
return $response->decodeJson();
}
/**
* Enable mobile app
*
* @param string $appId If updating, the ID
* @param string $appSecret If updating, the secret
* @return array
*/
protected function _doEnable( $appId, $appSecret )
{
\IPS\Settings::i()->changeValues( array(
'mobile_app_id' => $appId,
'mobile_app_secret' => $appSecret,
) );
/* Add it to our notification defaults */
if ( \IPS\Db::i()->select( 'COUNT(*)', 'core_notification_defaults' )->first() )
{
$keysToAddPushTo = array();
foreach( \IPS\Application::allExtensions( 'core', 'Notifications', FALSE ) as $group => $class )
{
if ( method_exists( $class, 'configurationOptions' ) )
{
foreach ( $class->configurationOptions( NULL ) as $key => $data )
{
if ( $data['type'] == 'standard' and \in_array( 'push', $data['default'] ) )
{
$keysToAddPushTo[] = $key;
foreach ( $data['notificationTypes'] as $type )
{
$keysToAddPushTo[] = $type;
}
}
}
}
elseif ( method_exists( $class, 'getConfiguration' ) )
{
$configuration = $class->getConfiguration( NULL );
if ( !empty( $configuration ) )
{
foreach ( $configuration as $key => $data )
{
if ( \in_array( 'push', $data['default'] ) )
{
$keysToAddPushTo[] = $key;
}
}
}
}
}
if ( \count( $keysToAddPushTo ) )
{
\IPS\Db::i()->update( 'core_notification_defaults', "`default` = IF( `default` = '', 'push', CONCAT_WS( ',', `default`, 'push' ) )", \IPS\Db::i()->in( 'notification_key', $keysToAddPushTo ) );
}
}
}
}
Whitespace-only changes.
@@ -12,7 +12,7 @@
namespace IPS\core\modules\admin\moderation;
/* 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;
@@ -23,6 +23,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _reportedContent extends \IPS\Node\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Node Class
*/
@@ -12,7 +12,7 @@
namespace IPS\core\modules\admin\moderation;
/* 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;
@@ -23,6 +23,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _reportedContentTypes extends \IPS\Node\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Node Class
*/
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\moderation;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _spam extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -44,7 +49,8 @@ class _spam extends \IPS\Dispatcher\Controller
protected function manage()
{
/* Work out output */
$activeTabContents = call_user_func( array( $this, '_manage' . mb_ucfirst( $this->activeTab ) ) );
$methodFunction = '_manage' . mb_ucfirst( $this->activeTab );
$activeTabContents = $this->$methodFunction();
/* If this is an AJAX request, just return it */
if( \IPS\Request::i()->isAjax() )
@@ -59,7 +65,7 @@ class _spam extends \IPS\Dispatcher\Controller
$tabs['flagging'] = 'spamprevention_flagging';
$tabs['service'] = 'enhancements__core_SpamMonitoring';
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'moderation', 'qanda_manage' ) and in_array( \IPS\Login::registrationType(), array( 'normal', 'full' ) ) )
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'moderation', 'qanda_manage' ) and \in_array( \IPS\Login::registrationType(), array( 'normal', 'full' ) ) )
{
$tabs['qanda'] = 'qanda_settings';
}
@@ -170,7 +176,6 @@ class _spam extends \IPS\Dispatcher\Controller
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'spm_option', explode( ',', \IPS\Settings::i()->spm_option ), FALSE, array(
'options' => array( 'disable' => 'spm_option_disable', 'unapprove' => 'spm_option_unapprove', 'delete' => 'spm_option_delete', 'ban' => 'spm_option_ban' ),
) ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'spm_notify', \IPS\Settings::i()->spm_notify ) );
/* Save values */
if ( $form->values() )
@@ -210,8 +215,8 @@ class _spam extends \IPS\Dispatcher\Controller
}
}
$form->add( new \IPS\Helpers\Form\YesNo( 'spam_service_enabled', \IPS\Settings::i()->spam_service_enabled, FALSE, array( 'disabled' => $disabled, 'togglesOn' => array( 'spam_service_send_to_ips', 'spam_service_action_0', 'spam_service_action_1', 'spam_service_action_2', 'spam_service_action_3', 'spam_service_action_4' ) ) ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'spam_service_send_to_ips', \IPS\Settings::i()->spam_service_send_to_ips, FALSE, array( 'disabled' => $disabled ), NULL, NULL, NULL, 'spam_service_send_to_ips' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'spam_service_enabled', 0, FALSE, array( 'disabled' => $disabled, 'togglesOn' => array( 'spam_service_send_to_ips', 'spam_service_action_0', 'spam_service_action_1', 'spam_service_action_2', 'spam_service_action_3', 'spam_service_action_4' ) ) ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'spam_service_send_to_ips', 0, FALSE, array( 'disabled' => $disabled ), NULL, NULL, NULL, 'spam_service_send_to_ips' ) );
$form->add( new \IPS\Helpers\Form\Select( 'spam_service_action_1', \IPS\Settings::i()->spam_service_action_1, FALSE, array( 'disabled' => $disabled, 'options' => $actions ), NULL, NULL, NULL, 'spam_service_action_1' ) );
$form->add( new \IPS\Helpers\Form\Select( 'spam_service_action_2', \IPS\Settings::i()->spam_service_action_2, FALSE, array( 'disabled' => $disabled, 'options' => $actions ), NULL, NULL, NULL, 'spam_service_action_2' ) );
$form->add( new \IPS\Helpers\Form\Select( 'spam_service_action_3', \IPS\Settings::i()->spam_service_action_3, FALSE, array( 'disabled' => $disabled, 'options' => $actions ), NULL, NULL, NULL, 'spam_service_action_3' ) );
@@ -309,7 +314,7 @@ class _spam extends \IPS\Dispatcher\Controller
/* Load question */
try
{
$id = intval( \IPS\Request::i()->id );
$id = \intval( \IPS\Request::i()->id );
$form->hiddenValues['id'] = $id;
$question = \IPS\Db::i()->select( '*', 'core_question_and_answer', array( 'qa_id=?', $id ) )->first();
@@ -360,7 +365,7 @@ class _spam extends \IPS\Dispatcher\Controller
*/
protected function delete()
{
$id = intval( \IPS\Request::i()->id );
$id = \intval( \IPS\Request::i()->id );
\IPS\Dispatcher::i()->checkAcpPermission( 'qanda_delete' );
/* Make sure the user confirmed the deletion */
@@ -438,7 +443,7 @@ class _spam extends \IPS\Dispatcher\Controller
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\Number( 'prune_log_spam', \IPS\Settings::i()->prune_log_spam, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), \IPS\Member::loggedIn()->language()->addToStack('days'), 'prune_log_spam' ) );
$form->add( new \IPS\Helpers\Form\Interval( 'prune_log_spam', \IPS\Settings::i()->prune_log_spam, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL, 'prune_log_spam' ) );
if ( $values = $form->values() )
{
@@ -621,7 +626,7 @@ class _spam extends \IPS\Dispatcher\Controller
/**
* Delete whitelist entry
*
* return @void
* @return void
*/
protected function whitelistDelete()
{
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\moderation;
/* 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;
@@ -22,11 +22,21 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _warnings extends \IPS\Node\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Node Class
*/
protected $nodeClass = '\IPS\core\Warnings\Reason';
/**
* Show the "add" button in the page root rather than the table root
*/
protected $_addButtonInRoot = FALSE;
/**
* Execute
*
@@ -226,7 +236,7 @@ class _warnings extends \IPS\Node\Controller
$save = array( 'wa_points' => $values['wa_points'], 'wa_override' => $values['wa_override'] );
foreach ( array( 'mq', 'rpa', 'suspend' ) as $k )
{
$save[ 'wa_' . $k ] = intval( $values[ 'wa_' . $k ][0] );
$save[ 'wa_' . $k ] = \intval( $values[ 'wa_' . $k ][0] );
$save[ 'wa_' . $k . '_unit' ] = $values[ 'wa_' . $k ][1];
}
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\overview;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _dashboard extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Show the ACP dashboard
*
@@ -54,7 +59,7 @@ class _dashboard extends \IPS\Dispatcher\Controller
{
foreach( $toShow as $row )
{
if( in_array( $key, $row ) )
if( \in_array( $key, $row ) )
{
$blocks[ $key ] = $extension->getBlock();
break;
@@ -64,352 +69,10 @@ class _dashboard extends \IPS\Dispatcher\Controller
}
}
/* ACP Bulletin */
$bulletin = isset( \IPS\Data\Store::i()->acpBulletin ) ? \IPS\Data\Store::i()->acpBulletin : NULL;
if ( !$bulletin or $bulletin['time'] < ( time() - 86400 ) )
{
try
{
$bulletins = \IPS\Http\Url::ips('bulletin')->request()->get()->decodeJson();
\IPS\Data\Store::i()->acpBulletin = array(
'time' => time(),
'content' => $bulletins ?: array()
);
}
catch( \RuntimeException $e )
{
$bulletins = array();
}
}
else
{
$bulletins = $bulletin['content'];
}
if( !empty( $bulletins ) )
{
foreach ( $bulletins as $k => $data )
{
if ( count( $data['files'] ) )
{
$skip = TRUE;
foreach ( $data['files'] as $file )
{
if ( filemtime( \IPS\ROOT_PATH . '/' . $file ) < $data['timestamp'] )
{
$skip = FALSE;
}
}
if ( $skip )
{
unset( $bulletins[ $k ] );
}
}
}
}
/* Warnings */
$warnings = array();
$functionsToDisable = array( 'exec', 'system', 'passhtru', 'pcntl_exec', 'popen', 'proc_open', 'shell_exec' );
$showingFunctionWarning = FALSE;
foreach ( $functionsToDisable as $k => $function )
{
if ( function_exists( $function ) )
{
$showingFunctionWarning = TRUE;
}
else
{
unset( $functionsToDisable[ $k ] );
}
}
if ( $showingFunctionWarning )
{
$warnings[] = array(
'key' => 'disable_functions',
'title' => \IPS\Member::loggedIn()->language()->addToStack('disable_functions_title'),
'description' => \IPS\Member::loggedIn()->language()->addToStack('disable_functions_desc', FALSE, array( 'sprintf' => array( implode( ', ', $functionsToDisable ) ) ) ),
);
}
if ( !\IPS\IN_DEV and ( (bool) ini_get( 'display_errors' ) ) !== FALSE AND ini_get( 'display_errors' ) !== 'Off' )
{
$warnings[] = array(
'key' => 'display_errors',
'title' => \IPS\Member::loggedIn()->language()->addToStack('display_errors_title'),
'description' => \IPS\Member::loggedIn()->language()->addToStack('display_errors_desc'),
'risk' => "medium",
);
}
$tasks = \IPS\Db::i()->select( '*', 'core_tasks', 'lock_count >= 3 AND enabled=1' );
$keys = array();
foreach( $tasks as $task )
{
$keys[] = $task['key'];
}
if ( !empty( $keys ) )
{
$warnings[] = array(
'key' => 'dashboard_tasks_broken',
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'dashboard_tasks_broken' ),
'description' => \IPS\Member::loggedIn()->language()->addToStack( 'dashboard_tasks_broken_desc', TRUE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->formatList( $keys ) ) ) )
);
}
if( isset( \IPS\Data\Store::i()->failedMailCount ) AND \IPS\Data\Store::i()->failedMailCount >= 3 )
{
$warnings[] = array(
'key' => 'dashboard_email_broken',
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'dashboard_email_broken' ),
'description' => \IPS\Member::loggedIn()->language()->addToStack( 'dashboard_email_broken_desc', TRUE )
);
}
$supportAccount = \IPS\Member::load( 'nobody@invisionpower.com', 'email' );
if ( $supportAccount->member_id )
{
$warnings[] = array(
'key' => 'dashboard_support_account',
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'dashboard_support_account' ),
'description' => \IPS\Member::loggedIn()->language()->addToStack( 'dashboard_support_account_desc', TRUE, array( 'sprintf' => array( $supportAccount->acpUrl() ) ) )
);
}
if( !\IPS\Settings::i()->orig_tables_checked )
{
/* Check if we have any orig_* tables */
$tables = \IPS\Db::i()->getTables( 'orig_' . \IPS\Db::i()->prefix );
/* If we don't have any, we're good. Set a flag so we don't check this every dashboard load. */
if( !count( $tables ) )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 1 ), array( 'conf_key=?', 'orig_tables_checked' ) );
unset( \IPS\Data\Store::i()->settings );
}
else
{
/* Determine if the background queue task has already been launched */
try
{
\IPS\Db::i()->select( '*', 'core_queue', array( "`key`=?", 'CleanupOrigTables' ) )->first();
$inProgress = TRUE;
}
catch( \UnderflowException $e )
{
$inProgress = FALSE;
}
/* Launch the task if we clicked to do so */
if( !$inProgress AND isset( \IPS\Request::i()->cleanupOrigTables ) AND \IPS\Request::i()->cleanupOrigTables )
{
\IPS\Task::queue( 'core', 'CleanupOrigTables', array( 'originalCount' => count( $tables ) ), 5 );
$inProgress = TRUE;
\IPS\Output::i()->inlineMessage = \IPS\Member::loggedIn()->language()->addToStack('orig_tables_rebuilding');
}
if( !$inProgress )
{
$warnings[] = array(
'key' => 'block_core_OrigTables',
'title' => \IPS\Member::loggedIn()->language()->addToStack('block_core_OrigTables'),
'description' => \IPS\Member::loggedIn()->language()->addToStack('orig_cleanup_suggested')
);
}
}
}
/* Check Tasks */
try
{
$task = \IPS\DateTime::ts( \IPS\Db::i()->select( 'next_run', 'core_tasks', array( 'core_tasks.enabled=1 AND (core_plugins.plugin_enabled=1 OR core_applications.app_enabled=1)' ), 'next_run ASC' )
->join( 'core_applications', array( 'core_applications.app_directory=core_tasks.app' ) )
->join( 'core_plugins', array( 'core_plugins.plugin_id=core_tasks.plugin' ) )
->first() );
$today = new \IPS\DateTime;
$difference = $today->diff( $task )->h + ( $today->diff( $task )->days * 24 );
if ( $difference >= 36 )
{
if( \IPS\Settings::i()->task_use_cron == 'cron' )
{
$warnings[] = array(
'key' => 'dashboard_tasksrun_broken',
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'dashboard_tasksrun_broken' ),
'description' => \IPS\Member::loggedIn()->language()->addToStack( \IPS\CIC ? 'dashboard_tasks_cron_broken_desc_cic' : 'dashboard_tasks_cron_broken_desc' )
);
}
elseif( \IPS\Settings::i()->task_use_cron == 'web' )
{
$warnings[] = array(
'key' => 'dashboard_tasksrun_broken',
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'dashboard_tasksrun_broken' ),
'description' => \IPS\Member::loggedIn()->language()->addToStack( 'dashboard_tasks_web_broken_desc' )
);
}
else
{
$warnings[] = array(
'key' => 'dashboard_tasksrun_broken',
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'dashboard_tasksrun_broken' ),
'description' => \IPS\Member::loggedIn()->language()->addToStack( 'dashboard_tasks_not_enough_desc' )
);
}
}
}
catch ( \UnderflowException $e ) { }
if ( !\IPS\Settings::i()->site_online AND \IPS\Settings::i()->task_use_cron == 'normal' AND !\IPS\CIC )
{
$warnings[] = array(
'key' => 'dasbhoard_tasks_site_offline',
'title' => \IPS\Member::loggedIn()->language()->addToStack('dasbhoard_tasks_site_offline'),
'description' => \IPS\Member::loggedIn()->language()->addToStack('dasbhoard_tasks_site_offline_desc')
);
}
/* Check MySQL and PHP min versions */
$phpVersion = PHP_VERSION;
$mysqlVersion = \IPS\Db::i()->server_info;
$requirements = json_decode( file_get_contents( \IPS\ROOT_PATH . '/applications/core/data/requirements.json' ), TRUE );
if ( version_compare( $phpVersion, $requirements['php']['recommended'] ) < 0 )
{
$warnings[] = array(
'key' => 'dashboard_recommended_php_version',
'title' => \IPS\Member::loggedIn()->language()->addToStack('dashboard_recommended_php_version'),
'description' => \IPS\Member::loggedIn()->language()->addToStack( 'requirements_php_version_advice', FALSE, array( 'sprintf' => array( $phpVersion, $requirements['php']['recommended'] ) ) )
);
}
if ( version_compare( $mysqlVersion, $requirements['mysql']['recommended'] ) < 0 AND !mb_strpos( $mysqlVersion, "MariaDB" ) )
{
$warnings[] = array(
'key' => 'dasbhoard_required_mysql_version',
'title' => \IPS\Member::loggedIn()->language()->addToStack('dasbhoard_required_mysql_version'),
'description' => \IPS\Member::loggedIn()->language()->addToStack( 'requirements_mysql_version_advice', FALSE, array( 'sprintf' => array( $mysqlVersion, $requirements['mysql']['recommended'] ) ) )
);
}
/* Don't do this for IN_DEV on localhost */
//
/* If there have been more than 10 datastore failures in the last hour, or if an instant test fails, show a message */
if( !\IPS\Data\Store::i()->test() OR \IPS\Db::i()->select( 'COUNT(*)', 'core_log', array( '`category`=? AND `time`>?', 'datastore', \IPS\DateTime::create()->sub( new \DateInterval( 'PT1H' ) )->getTimestamp() ) )->first() >= 10 )
{
/* Have we just recently updated the configuration? If so, ignore this warning for 24 hours */
if( \IPS\Settings::i()->last_data_store_update < \IPS\DateTime::create()->sub( new \DateInterval( 'PT24H' ) )->getTimestamp() )
{
$warnings[] = array(
'key' => 'dashboard_datastore_broken',
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'dashboard_datastore_broken' ),
'description' => \IPS\Member::loggedIn()->language()->addToStack( 'dashboard_datastore_broken_desc' )
);
}
}
if ( \IPS\CIC )
{
try
{
$cicEmails = \IPS\Data\Cache::i()->getWithExpire( 'cicEmailUsage', TRUE );
}
catch( \OutOfRangeException $e )
{
preg_match( '/^\/var\/www\/html\/(.+?)(?:\/|$)/i', \IPS\ROOT_PATH, $matches );
try
{
$cicEmails = \IPS\Http\Url::external( "http://ips-cic-email.invisioncic.com/blocked.php?account={$matches[1]}" )->request()->get()->decodeJson();
}
catch( \Exception $e )
{
/* Request failed, so assume okay and try again in an hour */
$cicEmails = array( 'status' => 'OKAY', 'time' => time() );
}
\IPS\Data\Cache::i()->storeWithExpire( 'cicEmailUsage', $cicEmails, \IPS\DateTime::create()->add( new \DateInterval( 'PT1H' ) ), true );
}
if ( isset( $cicEmails['status'] ) AND $cicEmails['status'] == 'BLOCKED' )
{
$warnings[] = array(
'key' => 'dashboard_cic_email_quota',
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'dashboard_cic_email_quota' ),
'description' => \IPS\Member::loggedIn()->language()->addToStack( 'dashboard_cic_email_quota_desc' )
);
}
}
/* Cache Check */
if ( \IPS\CACHE_METHOD AND \IPS\CACHE_METHOD != 'None' AND \IPS\Data\Cache::i() instanceof \IPS\Data\Cache\None )
{
$warnings[] = array(
'key' => 'dashboard_invalid_cachesetup',
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'dashboard_invalid_cachesetup' ),
'description' => \IPS\Member::loggedIn()->language()->addToStack( 'dashboard_invalid_cachesetup_desc' )
);
}
/* Temporary thing for to show a warning if Facebook login is enabled but site doesn't use https */
if ( \IPS\Login\Handler::findMethod( 'IPS\Login\Handler\OAuth2\Facebook' ) and mb_substr( \IPS\Settings::i()->base_url, 0, 8 ) !== 'https://' )
{
$warnings[] = array(
'key' => 'facebook_https',
'title' => \IPS\Member::loggedIn()->language()->addToStack( time() < mktime( 0, 0, 0, 10, 6, 2018 ) ? 'facebook_https_warning_title' : 'facebook_https_warning_title2' ),
'description' => \IPS\Member::loggedIn()->language()->addToStack( \IPS\CIC ? 'facebook_https_warning_desc_cic' : 'facebook_https_warning_desc' )
);
}
/* Get new core update available data */
$update = \IPS\Application::load( 'core' )->availableUpgrade( TRUE );
/* Determine if there are any new features to show */
$latestFeatureId = \IPS\Application::load( 'core' )->newFeature();
$features = array();
try
{
$latestSeenFeature = \IPS\Db::i()->select( 'feature_id', 'core_members_feature_seen', array( 'member_id=?', \IPS\Member::loggedIn()->member_id ) )->first();
}
catch( \UnderflowException $e )
{
$latestSeenFeature = 0;
}
if( $latestFeatureId AND ( !$latestSeenFeature OR $latestSeenFeature < $latestFeatureId ) )
{
try
{
$features = json_encode( \IPS\Http\Url::ips('newFeatures')->setQueryString( array( 'since' => (int) $latestSeenFeature ) )->request()->get()->decodeJson() );
/* Reset our last feature ID information so this doesn't show on subsequent page loads */
\IPS\Db::i()->replace( 'core_members_feature_seen', array( 'member_id' => \IPS\Member::loggedIn()->member_id, 'feature_id' => $latestFeatureId ) );
}
catch( \RuntimeException $e ){}
}
/* Don't show warnings we've hidden */
if ( isset( \IPS\Request::i()->cookie['acpWarnings_mute'] ) )
{
$keys = explode( ',', \IPS\Request::i()->cookie['acpWarnings_mute'] );
foreach( $warnings as $index => $data )
{
if ( isset( $data['key'] ) and in_array( $data['key'], $keys ) )
{
unset( $warnings[ $index ] );
}
}
}
/* 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( $update, $features, $toShow, $blocks, $info, $bulletins, $warnings );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'dashboard' )->dashboard( $toShow, $blocks, $info );
}
/**
@@ -419,9 +82,21 @@ class _dashboard extends \IPS\Dispatcher\Controller
*/
public function whatsNew()
{
\IPS\Session::i()->csrfCheck();
\IPS\Member::loggedIn()->members_bitoptions['has_new_acp_features'] = TRUE;
\IPS\Member::loggedIn()->save();
\IPS\Db::i()->delete( 'core_members_feature_seen', array( 'member_id=?', \IPS\Member::loggedIn()->member_id ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=overview&controller=dashboard" ) );
if( \IPS\Member::loggedIn()->hasAcpRestriction( \IPS\Application::load( 'core' ), \IPS\Application\Module::get( 'core', 'overview' ), 'view_dashboard' ) )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=overview&controller=dashboard" ) );
}
else
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "" ) );
}
}
/**
@@ -503,6 +178,8 @@ class _dashboard extends \IPS\Dispatcher\Controller
*/
public function update()
{
\IPS\Session::i()->csrfCheck();
if( \IPS\Settings::i()->acp_dashboard_blocks )
{
$blocks = json_decode( \IPS\Settings::i()->acp_dashboard_blocks, TRUE );
+133 -117
View File
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\overview;
/* 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;
@@ -23,14 +23,9 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
class _files extends \IPS\Dispatcher\Controller
{
/**
* Execute
*
* @return void
* @brief Has been CSRF-protected
*/
public function execute()
{
parent::execute();
}
public static $csrfProtected = TRUE;
/**
* Manage Attachment Types
@@ -93,14 +88,14 @@ class _files extends \IPS\Dispatcher\Controller
{
if ( $row['attach_is_image'] and $table->filter === 'images' )
{
$url = \IPS\Http\Url::external( \IPS\File::get( 'core_Attachment', $row['attach_location'] )->url )->makeSafeForAcp( TRUE );
$url = \IPS\Http\Url::external( \IPS\File::get( 'core_Attachment', $row['attach_location'] )->url );
return "<a href='{$url}' target='_blank' rel='noopener'><img src='{$url}' style='max-height:200px'></a>";
}
else
{
$url = \IPS\Http\Url::external( \IPS\Settings::i()->base_url . "applications/core/interface/file/attachment.php" )->setQueryString( 'id', $row['attach_id'] )->makeSafeForAcp( FALSE );
$url = \IPS\Http\Url::external( \IPS\Settings::i()->base_url . "applications/core/interface/file/attachment.php" )->setQueryString( 'id', $row['attach_id'] );
$val = htmlentities( $val, ENT_DISALLOWED, 'UTF-8', FALSE );
return "<a href='{$url}' target='_blank' rel='noopener'>{$val}</a>";
return "<a href='{$url}' target='_blank' rel='noreferrer'>{$val}</a>";
}
},
'attach_filesize' => function( $val )
@@ -138,7 +133,7 @@ class _files extends \IPS\Dispatcher\Controller
}
else
{
return "<a href='" . \IPS\Http\Url::internal( 'app=core&module=members&controller=members&do=edit&id=' . $val ) . "'>" . htmlentities( \IPS\Member::load( $val )->name, ENT_DISALLOWED, 'UTF-8', FALSE ) . '</a>';
return "<a href='" . \IPS\Http\Url::internal( 'app=core&module=members&controller=members&do=view&id=' . $val ) . "'>" . htmlentities( \IPS\Member::load( $val )->name, ENT_DISALLOWED, 'UTF-8', FALSE ) . '</a>';
}
}
@@ -191,7 +186,7 @@ class _files extends \IPS\Dispatcher\Controller
$loadedExtensions = array();
$locations = array();
foreach ( \IPS\Db::i()->select( '*', 'core_attachments_map', array( 'attachment_id=?', intval( \IPS\Request::i()->id ) ) ) as $map )
foreach ( \IPS\Db::i()->select( '*', 'core_attachments_map', array( 'attachment_id=?', \intval( \IPS\Request::i()->id ) ) ) as $map )
{
if ( !isset( $loadedExtensions[ $map['location_key'] ] ) )
{
@@ -207,7 +202,7 @@ class _files extends \IPS\Dispatcher\Controller
catch ( \OutOfRangeException $e ){ }
}
if ( isset( $loadedExtensions[ $map['location_key'] ] ) )
if ( isset( $loadedExtensions[ $map['location_key'] ] ) AND method_exists( $loadedExtensions[ $map['location_key'] ], 'attachmentLookup' ) )
{
try
{
@@ -311,7 +306,8 @@ class _files extends \IPS\Dispatcher\Controller
$configurations = array();
foreach ( \IPS\Db::i()->select( '*', 'core_file_storage' ) as $row )
{
$classname = 'IPS\File\\' . $row['method'];
$handlers = \IPS\File::storageHandlers( $row );
$classname = $handlers[ $row['method'] ];
$configurations[ $row['id'] ] = $classname::displayName( json_decode( $row['configuration'], TRUE ) );
}
@@ -319,12 +315,12 @@ class _files extends \IPS\Dispatcher\Controller
$form->addMessage( 'filestorage_move_info' );
foreach ( \IPS\Application::allExtensions( 'core', 'FileStorage', FALSE, NULL, NULL, TRUE ) as $name => $obj )
{
$disabled = ( isset( $settings[ "filestorage__{$name}" ] ) and is_array( $settings[ "filestorage__{$name}" ] ) ) ? TRUE : FALSE;
$disabled = ( isset( $settings[ "filestorage__{$name}" ] ) and \is_array( $settings[ "filestorage__{$name}" ] ) ) ? TRUE : FALSE;
$value = NULL;
if ( isset( $settings[ "filestorage__{$name}" ] ) )
{
if ( is_array( $settings[ "filestorage__{$name}" ] ) )
if ( \is_array( $settings[ "filestorage__{$name}" ] ) )
{
$copyOfSettings = $settings[ "filestorage__{$name}" ];
$value = array_shift( $copyOfSettings );
@@ -335,73 +331,91 @@ class _files extends \IPS\Dispatcher\Controller
}
}
$form->add( new \IPS\Helpers\Form\Select( 'filestorage__' . $name, (int) $value, TRUE, array( 'options' => $configurations, 'disabled' => $disabled ) ) );
$toggles = array();
foreach( $configurations as $id => $title )
{
if( $id != $value )
{
$toggles[ $id ] = array( 'filestorage_move' );
}
}
$form->add( new \IPS\Helpers\Form\Select( 'filestorage__' . $name, (int) $value, TRUE, array( 'options' => $configurations, 'disabled' => $disabled, 'toggles' => $toggles ) ) );
if ( $disabled )
{
\IPS\Member::loggedIn()->language()->words[ 'filestorage__' . $name . '_warning' ] = \IPS\Member::loggedIn()->language()->addToStack( 'file_storage_move_in_progress' );
}
}
$form->add( new \IPS\Helpers\Form\YesNo( 'filestorage_move', TRUE, FALSE, array(), NULL, NULL, NULL, 'filestorage_move' ) );
if ( $values = $form->values() )
{
$rebuild = FALSE;
/* Queue theme first */
if( isset( $values['filestorage__core_Theme'] ) and $settings['filestorage__core_Theme'] != $values['filestorage__core_Theme'] )
{
$rebuild = TRUE;
$extension = new \IPS\core\extensions\core\FileStorage\Theme;
\IPS\Task::queue( 'core', 'MoveFiles', array( 'storageExtension' => 'filestorage__core_Theme', 'oldConfiguration' => $settings[ 'filestorage__core_Theme' ], 'newConfiguration' => $values[ 'filestorage__core_Theme' ], 'count' => $extension->count() ), 1 );
/* Add to allowed storage methods so when moving files, we can accept old config or new config if move is in progress. Important: order is array(x, y) x is the new location (pos 0), y is the old location (pos 1)*/
$values['filestorage__core_Theme'] = array( $values['filestorage__core_Theme'], $settings['filestorage__core_Theme'] );
}
foreach ( $values as $k => $v )
if( isset( $values['filestorage_move'] ) AND $values['filestorage_move'] )
{
if ( isset( $settings[$k] ) AND !is_array( $settings[$k] ) )
{
if ( $settings[ $k ] != $v and $k != 'filestorage__core_Theme' )
{
/* Do we need to move files at all? */
$configurations = \IPS\File::getConfigurations();
$exploded = explode( '_', $k );
$classname = "IPS\\{$exploded[2]}\\extensions\\core\\FileStorage\\{$exploded[3]}";
$extension = new $classname;
$currentClass = \IPS\File::getClass( intval( $settings[ $k ] ) );
$newClass = \IPS\File::getClass( intval( $v ) );
if ( ( isset( $configurations[ $v ] ) and isset( $configurations[ $settings[ $k ] ] ) ) and ( get_class( $currentClass ) == get_class( $newClass ) ) and ! $newClass::moveCheck( $newClass->configuration, $currentClass->configuration ) )
{
$rebuild = FALSE;
}
else
{
$rebuild = TRUE;
}
$rebuild = FALSE;
if ( $rebuild )
/* Queue theme first */
if( isset( $values['filestorage__core_Theme'] ) and $settings['filestorage__core_Theme'] != $values['filestorage__core_Theme'] )
{
$rebuild = TRUE;
$extension = new \IPS\core\extensions\core\FileStorage\Theme;
\IPS\Task::queue( 'core', 'MoveFiles', array( 'storageExtension' => 'filestorage__core_Theme', 'oldConfiguration' => $settings[ 'filestorage__core_Theme' ], 'newConfiguration' => $values[ 'filestorage__core_Theme' ], 'count' => $extension->count() ), 1 );
/* Add to allowed storage methods so when moving files, we can accept old config or new config if move is in progress. Important: order is array(x, y) x is the new location (pos 0), y is the old location (pos 1)*/
$values['filestorage__core_Theme'] = array( $values['filestorage__core_Theme'], $settings['filestorage__core_Theme'] );
}
$totalCount = 0;
foreach ( $values as $k => $v )
{
if ( isset( $settings[$k] ) AND !\is_array( $settings[$k] ) )
{
if ( $settings[ $k ] != $v and $k != 'filestorage__core_Theme' AND $k != 'filestorage_move' )
{
$count = $extension->count();
/* Do we need to move files at all? */
$configurations = \IPS\File::getConfigurations();
$exploded = explode( '_', $k );
$classname = "IPS\\{$exploded[2]}\\extensions\\core\\FileStorage\\{$exploded[3]}";
$extension = new $classname;
$currentClass = \IPS\File::getClass( \intval( $settings[ $k ] ) );
$newClass = \IPS\File::getClass( \intval( $v ) );
if ( $count )
if ( ( isset( $configurations[ $v ] ) and isset( $configurations[ $settings[ $k ] ] ) ) and ( \get_class( $currentClass ) == \get_class( $newClass ) ) and ! $newClass::moveCheck( $newClass->configuration, $currentClass->configuration ) )
{
\IPS\Task::queue( 'core', 'MoveFiles', array( 'storageExtension' => $k, 'oldConfiguration' => $settings[ $k ], 'newConfiguration' => $values[ $k ], 'count' => $count ), 2 );
$rebuild = FALSE;
}
else
{
$rebuild = TRUE;
}
if ( $rebuild )
{
$count = $extension->count();
/* Add to allowed storage methods so when moving files, we can accept old config or new config if move is in progress. Important: order is array(x, y) x is the new location (pos 0), y is the old location (pos 1)*/
$values[ $k ] = array( $v, $settings[ $k ] );
if ( $count )
{
$totalCount += $count;
\IPS\Task::queue( 'core', 'MoveFiles', array( 'storageExtension' => $k, 'oldConfiguration' => $settings[ $k ], 'newConfiguration' => $values[ $k ], 'count' => $count ), 2 );
/* Add to allowed storage methods so when moving files, we can accept old config or new config if move is in progress. Important: order is array(x, y) x is the new location (pos 0), y is the old location (pos 1)*/
$values[ $k ] = array( $v, $settings[ $k ] );
}
}
}
}
}
if( $rebuild )
{
\IPS\Task::queue( 'core', 'DeleteMovedFiles', array( 'delete' => true, 'count' => $totalCount ), 5, array( 'delete' ) ); /* We use a key in the data array just to trigger the code that deletes duplicate tasks */
}
}
if( $rebuild )
{
\IPS\Task::queue( 'core', 'DeleteMovedFiles', array( 'delete' => true ), 5, array( 'delete' ) ); /* We use a key in the data array just to trigger the code that deletes duplicate tasks */
}
/* Update the settings */
\IPS\Settings::i()->changeValues( array( 'upload_settings' => json_encode( $values ) ) );
/* Clear guest page caches */
@@ -422,7 +436,8 @@ class _files extends \IPS\Dispatcher\Controller
$table->noSort = array( 'filestorage_method' );
$table->parsers = array( 'filestorage_method' => function( $val, $row )
{
$classname = 'IPS\File\\' . $row['method'];
$handlers = \IPS\File::storageHandlers( $row );
$classname = $handlers[ $row['method'] ];
return $classname::displayName( json_decode( $row['configuration'], TRUE ) );
} );
@@ -448,7 +463,7 @@ class _files extends \IPS\Dispatcher\Controller
'delete' => array(
'icon' => 'times-circle',
'title' => 'delete',
'link' => \IPS\Http\Url::internal( "app=core&module=overview&controller=files&do=configurationForm&id={$row['id']}&delete=1" )
'link' => \IPS\Http\Url::internal( "app=core&module=overview&controller=files&do=configurationForm&id={$row['id']}&delete=1" )->csrf()
),
);
};
@@ -487,7 +502,7 @@ class _files extends \IPS\Dispatcher\Controller
protected function fileLogSettings()
{
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\Number( 'file_log_pruning', \IPS\Settings::i()->file_log_pruning, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), \IPS\Member::loggedIn()->language()->addToStack('days'), 'file_log_pruning' ) );
$form->add( new \IPS\Helpers\Form\Interval( 'file_log_pruning', \IPS\Settings::i()->file_log_pruning, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL, 'file_log_pruning' ) );
if ( $values = $form->values() )
{
@@ -515,10 +530,10 @@ class _files extends \IPS\Dispatcher\Controller
{
try
{
$current = \IPS\Db::i()->select( '*', 'core_file_storage', array( 'id=?', intval( \IPS\Request::i()->id ) ) )->first();
$current = \IPS\Db::i()->select( '*', 'core_file_storage', array( 'id=?', \intval( \IPS\Request::i()->id ) ) )->first();
$currentHandlerSettings = json_decode( $current['configuration'], TRUE );
if ( in_array( intval( \IPS\Request::i()->id ), json_decode( \IPS\Settings::i()->upload_settings, TRUE ) ) )
if ( \in_array( \intval( \IPS\Request::i()->id ), array_filter( json_decode( \IPS\Settings::i()->upload_settings, TRUE ), function( $key ){ return $key != 'filestorage_move'; }, ARRAY_FILTER_USE_KEY ) ) )
{
if ( isset( \IPS\Request::i()->delete ) )
{
@@ -546,7 +561,9 @@ class _files extends \IPS\Dispatcher\Controller
if ( isset( \IPS\Request::i()->delete ) )
{
\IPS\Db::i()->delete( 'core_file_storage', array( 'id=?', intval( \IPS\Request::i()->id ) ) );
\IPS\Session::i()->csrfCheck();
\IPS\Db::i()->delete( 'core_file_storage', array( 'id=?', \intval( \IPS\Request::i()->id ) ) );
unset( \IPS\Data\Store::i()->storageConfigurations );
\IPS\Session::i()->log( 'acplogs__files_config_removed', array() );
@@ -564,54 +581,36 @@ class _files extends \IPS\Dispatcher\Controller
$handlers = array();
$handlerSettings = array();
$toggles = array();
foreach ( new \DirectoryIterator( \IPS\ROOT_PATH . '/system/File' ) as $f )
foreach ( \IPS\File::storageHandlers( $current ) as $key => $class )
{
if ( !$f->isDot() and mb_substr( $f, -4 ) === '.php' and $f != 'File.php' and $f != 'Iterator.php' and $f != 'Exception.php' )
{
$key = mb_substr( $f, 0, -4 );
if ( \IPS\CIC and $key === 'FileSystem' )
$handlers[ $key ] = 'filehandler__' . $key;
foreach ( $class::settings( $currentHandlerSettings ) as $k => $v )
{
if ( \is_array( $v ) )
{
continue;
}
/* Hide FTP option from form unless it's already in use */
if ( ( $key === 'Ftp' AND isset( $current['method'] ) AND $current['method'] !== 'Ftp' ) OR ( $key === 'Ftp' AND !isset( $current['method'] ) ) )
{
continue;
}
$handlers[ $key ] = 'filehandler__' . $key;
$class = 'IPS\File\\' . $key;
foreach ( $class::settings( $currentHandlerSettings ) as $k => $v )
{
if ( is_array( $v ) )
$settingClass = '\IPS\Helpers\Form\\' . $v['type'];
$default = isset( $currentHandlerSettings[ $k ] ) ? str_replace( '{root}', \IPS\ROOT_PATH, $currentHandlerSettings[ $k ] ) : NULL;
if ( isset( $v['default'] ) and !$default )
{
$settingClass = '\IPS\Helpers\Form\\' . $v['type'];
$default = isset( $currentHandlerSettings[ $k ] ) ? str_replace( '{root}', \IPS\ROOT_PATH, $currentHandlerSettings[ $k ] ) : NULL;
if ( isset( $v['default'] ) and !$default )
{
$default = str_replace( '{root}', \IPS\ROOT_PATH, $v['default'] );
}
$handlerSettings[ $key ][ $k ] = new $settingClass( "filehandler__{$key}_{$k}", $default, FALSE, isset( $v['options'] ) ? $v['options'] : array(), isset( $v['validate'] ) ? $v['validate'] : NULL, isset( $v['prefix'] ) ? $v['prefix'] : NULL, isset( $v['suffix'] ) ? $v['suffix'] : NULL, "{$key}_{$k}" );
$default = str_replace( '{root}', \IPS\ROOT_PATH, $v['default'] );
}
else
{
$settingClass = '\IPS\Helpers\Form\\' . $v;
$handlerSettings[ $key ][ $k ] = new $settingClass( "filehandler__{$key}_{$k}", isset( $currentHandlerSettings[ $k ] ) ? $currentHandlerSettings[ $k ] : NULL, FALSE, array(), NULL, NULL, NULL, "{$key}_{$k}" );
}
$toggles[ $key ][ $k ] = "{$key}_{$k}";
$handlerSettings[ $key ][ $k ] = new $settingClass( "filehandler__{$key}_{$k}", $default, FALSE, isset( $v['options'] ) ? $v['options'] : array(), isset( $v['validate'] ) ? $v['validate'] : NULL, isset( $v['prefix'] ) ? $v['prefix'] : NULL, isset( $v['suffix'] ) ? $v['suffix'] : NULL, "{$key}_{$k}" );
}
else
{
$settingClass = '\IPS\Helpers\Form\\' . $v;
$handlerSettings[ $key ][ $k ] = new $settingClass( "filehandler__{$key}_{$k}", isset( $currentHandlerSettings[ $k ] ) ? $currentHandlerSettings[ $k ] : NULL, FALSE, array(), NULL, NULL, NULL, "{$key}_{$k}" );
}
$toggles[ $key ][ $k ] = "{$key}_{$k}";
}
}
/* Build form */
$form = new \IPS\Helpers\Form;
$form->hiddenValues['configurationId'] = isset( \IPS\Request::i()->id ) ? \IPS\Request::i()->id : 0;
if ( isset( \IPS\Request::i()->id ) AND in_array( intval( \IPS\Request::i()->id ), json_decode( \IPS\Settings::i()->upload_settings, TRUE ) ) and $current['method'] !== 'FileSystem')
if ( isset( \IPS\Request::i()->id ) AND \in_array( \intval( \IPS\Request::i()->id ), json_decode( \IPS\Settings::i()->upload_settings, TRUE ) ) and $current['method'] !== 'FileSystem')
{
$form->addMessage( 'files_edit_existing_and_used', 'ipsMessage ipsMessage_info' );
}
@@ -643,7 +642,7 @@ class _files extends \IPS\Dispatcher\Controller
}
}
$classname = 'IPS\File\\' . $values['filestorage_method'];
$classname = \IPS\File::storageHandlers( $current )[ $values['filestorage_method'] ];
if ( method_exists( $classname, 'testSettings' ) )
{
$classname::testSettings( $currentHandlerSettings );
@@ -695,9 +694,13 @@ class _files extends \IPS\Dispatcher\Controller
) );
unset( \IPS\Data\Store::i()->storageConfigurations );
/* Log the storage addition */
\IPS\Session::i()->log( 'acplogs__files_config_added' );
if ( $createNewAndMove )
{
$settings = json_decode( \IPS\Settings::i()->upload_settings, TRUE );
$totalCount = 0;
foreach ( $settings as $k => $v )
{
if ( $v == $current['id'] )
@@ -712,8 +715,9 @@ class _files extends \IPS\Dispatcher\Controller
if( \IPS\Application::appIsEnabled( $exploded[2] ) AND class_exists( $classname ) )
{
$extension = new $classname;
\IPS\Task::queue( 'core', 'MoveFiles', array( 'storageExtension' => $k, 'oldConfiguration' => $v, 'newConfiguration' => $insertId, 'count' => $extension->count() ), 2 );
$count = $extension->count();
$totalCount += $count;
\IPS\Task::queue( 'core', 'MoveFiles', array( 'storageExtension' => $k, 'oldConfiguration' => $v, 'newConfiguration' => $insertId, 'count' => $count ), 2 );
}
$settings[ $k ] = $insertId;
@@ -731,7 +735,7 @@ class _files extends \IPS\Dispatcher\Controller
if ( $values['filestorage_move'] )
{
\IPS\Task::queue( 'core', 'DeleteMovedFiles', array( 'delete' => true ), 5, array( 'delete' ) ); /* We use a key in the data array just to trigger the code that deletes duplicate tasks */
\IPS\Task::queue( 'core', 'DeleteMovedFiles', array( 'delete' => true, 'count' => $totalCount ), 5, array( 'delete' ) ); /* We use a key in the data array just to trigger the code that deletes duplicate tasks */
}
}
}
@@ -743,6 +747,9 @@ class _files extends \IPS\Dispatcher\Controller
if ( $current !== NULL and ! $createNewAndMove )
{
/* Log the storage change */
\IPS\Session::i()->log( 'acplogs__files_config_changed' );
$classname::getClass( $current['id'] )->settingsUpdated();
}
@@ -772,7 +779,7 @@ class _files extends \IPS\Dispatcher\Controller
{
try
{
$current = \IPS\Db::i()->select( '*', 'core_file_storage', array( 'id=?', intval( \IPS\Request::i()->id ) ) )->first();
$current = \IPS\Db::i()->select( '*', 'core_file_storage', array( 'id=?', \intval( \IPS\Request::i()->id ) ) )->first();
}
catch( \UnderflowException $e )
{
@@ -782,7 +789,7 @@ class _files extends \IPS\Dispatcher\Controller
$currentConf = json_decode( $current['configuration'], TRUE );
$needsMoving = FALSE;
if ( in_array( intval( \IPS\Request::i()->id ), json_decode( \IPS\Settings::i()->upload_settings, TRUE ) ) )
if ( \in_array( \intval( \IPS\Request::i()->id ), json_decode( \IPS\Settings::i()->upload_settings, TRUE ) ) )
{
foreach ( $currentConf as $k => $v )
{
@@ -794,7 +801,8 @@ class _files extends \IPS\Dispatcher\Controller
}
}
$classname = 'IPS\File\\' . $current['method'];
$handlers = \IPS\File::storageHandlers( $current );
$classname = $handlers[ $current['method'] ];
/* Do we really need to create and move? */
$needsMoving = $classname::moveCheck( $currentHandlerSettings, $currentConf );
@@ -810,7 +818,7 @@ class _files extends \IPS\Dispatcher\Controller
*/
protected function configurationLog()
{
$method = \IPS\Db::i()->select( '*', 'core_file_storage', array( 'id=?', intval( \IPS\Request::i()->id ) ) )->first();
$method = \IPS\Db::i()->select( '*', 'core_file_storage', array( 'id=?', \intval( \IPS\Request::i()->id ) ) )->first();
$title = \IPS\Member::loggedIn()->language()->addToStack( 'file_config_log', NULL, array( 'sprintf' => array( $method['method'] ) ) );
/* Create the table */
@@ -859,6 +867,8 @@ class _files extends \IPS\Dispatcher\Controller
*/
protected function orphaned()
{
\IPS\Session::i()->csrfCheck();
foreach( \IPS\Db::i()->select( '*', 'core_file_storage', NULL, 'id' ) as $row )
{
\IPS\Task::queue( 'core', 'FindOrphanedFiles', array( 'configurationId' => $row['id'] ), 4, array( 'configurationId' ) );
@@ -878,7 +888,7 @@ class _files extends \IPS\Dispatcher\Controller
\IPS\Session::i()->csrfCheck();
if( !isset( \IPS\Request::i()->multimod ) OR !is_array( \IPS\Request::i()->multimod ) OR !count( \IPS\Request::i()->multimod ) )
if( !isset( \IPS\Request::i()->multimod ) OR !\is_array( \IPS\Request::i()->multimod ) OR !\count( \IPS\Request::i()->multimod ) )
{
\IPS\Output::i()->error( 'nothing_mm_selected', '2C158/6', 403, '' );
}
@@ -897,6 +907,12 @@ class _files extends \IPS\Dispatcher\Controller
\IPS\Db::i()->delete( 'core_attachments', \IPS\Db::i()->in( 'attach_id', array_keys( \IPS\Request::i()->multimod ) ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=overview&controller=files" ), 'deleted' );
$url = \IPS\Http\Url::internal( "app=core&module=overview&controller=files" );
if( \IPS\Request::i()->listResort )
{
$url = $url->setQueryString( array( 'listResort' => 1, 'sortby' => \IPS\Request::i()->sortby, 'sortdirection' => \IPS\Request::i()->sortdirection ) )->csrf();
}
\IPS\Output::i()->redirect( $url, 'deleted' );
}
}
@@ -0,0 +1,221 @@
<?php
/**
* @brief ACP Notification Center
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 21 June 2018
*/
namespace IPS\core\modules\admin\overview;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* ACP Notification Center
*/
class _notifications extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Show notifications
*
* @return void
*/
protected function manage()
{
$notifications = \IPS\core\AdminNotification::notifications();
if ( \IPS\Request::i()->isAjax() and !isset( \IPS\Request::i()->_table ) )
{
\IPS\Output::i()->json( array( 'data' => \IPS\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 );
\IPS\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' ),
),
);
}
}
/**
* Dismiss a notification
*
* @return void
*/
protected function dismiss()
{
\IPS\Session::i()->csrfCheck();
\IPS\core\AdminNotification::dismissNotification( \IPS\Request::i()->id );
if ( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->json( array( 'status' => 'OK' ) );
}
else
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=overview&controller=notifications' ) );
}
}
/**
* Notification Settings
*
* @return void
*/
protected function settings()
{
$preferences = iterator_to_array( \IPS\Db::i()->select( '*', 'core_acp_notifications_preferences', array( '`member`=?', \IPS\Member::loggedIn()->member_id ) )->setKeyField('type') );
$matrix = new \IPS\Helpers\Form\Matrix;
$matrix->langPrefix = 'acp_notifications_';
$matrix->manageable = FALSE;
$matrix->columns = array(
'name' => function( $key, $value, $data )
{
return $value;
},
'view' => function( $key, $value, $data )
{
return new \IPS\Helpers\Form\YesNo( $key, $data['maybeOptional'] ? $value : TRUE, FALSE, array( 'disabled' => !$data['maybeOptional'] ) );
},
'email' => function( $key, $value, $data )
{
if ( $data['customEmail'] )
{
return $data['customEmail'];
}
else
{
$options = $data['mayRecur'] ? array(
'never' => 'acp_notifications_email_never',
'once' => 'acp_notifications_email_once',
'always' => 'acp_notifications_email_always',
) : array(
'never' => 'acp_notifications_email_never',
'once' => 'acp_notifications_email_yes',
);
return new \IPS\Helpers\Form\Select( $key, $value, FALSE, array( 'options' => $options, 'class' => 'ipsField_medium' ) );
}
},
);
$rows = array();
foreach ( \IPS\Application::allExtensions( 'core', 'AdminNotifications', TRUE, NULL, NULL, FALSE ) as $ext )
{
if ( $ext::permissionCheck( \IPS\Member::loggedIn() ) )
{
$exploded = explode( '\\', $ext );
$key = "{$exploded[1]}_{$exploded[5]}";
$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',
'maybeOptional' => $ext::mayBeOptional(),
'mayRecur' => $ext::mayRecur(),
'customEmail' => $ext::customEmailConfigurationSetting( "{$ext}[email]", isset( $preferences[ $key ]['email'] ) ? $preferences[ $key ]['email'] : NULL ),
'priority' => $ext::$itemPriority,
);
}
}
uasort( $rows, function( $a, $b ) {
return $a['priority'] - $b['priority'];
});
foreach ( $rows as $group => $data )
{
$matrix->rows[] = \IPS\Member::loggedIn()->language()->addToStack("acp_notification_group_{$group}");
uasort( $data['rows'], function( $a, $b ) {
return $a['priority'] - $b['priority'];
});
foreach ( $data['rows'] as $ext => $row )
{
$matrix->rows[ $ext ] = $row;
}
}
if ( $values = $matrix->values() )
{
foreach ( $values as $ext => $_values )
{
$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,
'type' => $key,
'view' => $ext::mayBeOptional() ? $_values['view'] : TRUE,
'email' => isset( $v['email'] ) ? $v['email'] : 'never',
), TRUE );
}
if( isset( \IPS\Data\Store::i()->acpNotificationIds ) )
{
$notificationCache = \IPS\Data\Store::i()->acpNotificationIds;
unset( $notificationCache[ \IPS\Member::loggedIn()->member_id ] );
\IPS\Data\Store::i()->acpNotificationIds = $notificationCache;
}
\IPS\Output::i()->redirect( \IPS\Http\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;
}
/**
* Recheck configuration errors
*
* @return void
*/
protected function configurationErrorChecks()
{
\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' ) );
}
/**
* Delete orig_ database tables
*
* @return void
*/
protected function removeOrigTables()
{
\IPS\Session::i()->csrfCheck();
$tables = \IPS\Db::i()->getTables( 'orig_' . \IPS\Db::i()->prefix );
\IPS\Task::queue( 'core', 'CleanupOrigTables', array( 'originalCount' => \count( $tables ) ), 5 );
\IPS\core\AdminNotification::remove( 'core', 'ConfigurationError', 'origTables' );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=overview&controller=notifications' ) );
}
}
@@ -0,0 +1,246 @@
<?php
/**
* @brief Initial installation onboarding
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 12 Feb 2020
*/
namespace IPS\core\modules\admin\overview;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Initial installation onboarding
*/
class _onboard extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
* @return void
*/
public function execute()
{
\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' ) );
return parent::execute();
}
/**
* Welcome page
*
* @return void
*/
public function initial()
{
\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
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('onboard_pagetitle');
\IPS\Output::i()->showTitle = FALSE;
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate( 'dashboard' )->onboardWelcome();
}
/**
* Dismiss
*
* @return void
*/
public function dismiss()
{
\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' ) );
}
/**
* Remind me later
*
* @return void
*/
public function remind()
{
\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' );
}
/**
* Show a form allowing the admin to configure some common settings after an initial installation
*
* @return void
*/
protected function manage()
{
/* Create the form and add our message to it */
$form = new \IPS\Helpers\Form;
$form->class = "ipsForm_vertical";
$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->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 )
{
if( $theme->logo_front )
{
$hasCustomLogo = TRUE;
break;
}
}
if( !\IPS\Settings::i()->email_logo AND !$hasCustomLogo )
{
$form->add( new \IPS\Helpers\Form\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' ) ) );
/* 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' ) ) );
$form->add( new \IPS\Helpers\Form\Color( 'email_color', \IPS\Settings::i()->email_color, TRUE ) );
/* These options are only for non-cic */
if( !\IPS\CIC )
{
/* Get the task setting */
$form->addTab( 'onboard_tab_tasks' );
\IPS\core\modules\admin\settings\advanced::taskSetting( $form );
/* Get the htaccess mod_Rewrite setting */
$form->addTab( 'onboard_tab_furls' );
\IPS\core\modules\admin\promotion\seo::htaccessSetting( $form );
}
/* Add the terms/privacy policy/etc. */
$form->addTab( 'onboard_tab_privacyterms' );
\IPS\core\modules\admin\settings\terms::buildForm( $form );
/* Add dismiss and remind me later buttons */
if( \IPS\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 ) );
}
if ( $values = $form->values() )
{
/* If we dismissed, do that but also send an email */
if( isset( \IPS\Request::i()->dismiss ) AND \IPS\Request::i()->dismiss == 1 )
{
\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' ) );
}
/* If we said to remind us later, set the flag and redirect */
if( isset( \IPS\Request::i()->remind ) AND \IPS\Request::i()->remind == 1 )
{
\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' );
}
array_walk( $values['site_social_profiles'], function( &$value ){
$value['key'] = (string) $value['key'];
});
$values['site_social_profiles'] = json_encode( array_filter( $values['site_social_profiles'], function( $value ) {
return (bool) $value['key'];
} ) );
$values['site_address'] = json_encode( $values['site_address'] );
$values['icons_favicon'] = (string) $values['icons_favicon'];
$values = \IPS\core\modules\admin\customization\icons::processApplicationIcon( $values );
$values = \IPS\core\modules\admin\settings\terms::processForm( $values );
/* If a logo was uploaded, do stuff with it... */
if( isset( $values['initial_logo'] ) )
{
$values['email_logo'] = (string) $values['initial_logo'];
/* Do this once for efficiency */
$image = \IPS\Image::create( $values['initial_logo']->contents() );
$width = $image->width;
$height = $image->height;
unset( $image );
foreach( \IPS\Theme::themes() as $theme )
{
$logoUrl = (string) \IPS\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 ) ) ) );
}
unset( $values['initial_logo'] );
}
elseif( array_key_exists( 'initial_logo', $values ) )
{
unset( $values['initial_logo'] );
}
$form->saveAsSettings( $values );
/* 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
);
\IPS\Session::i()->log( 'acplogs__onboard_settings' );
\IPS\Output::i()->redirect( \IPS\Http\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' ) );
}
/**
* Show a confirmation screen with "next steps"
*
* @return void
*/
protected function next()
{
\IPS\Output::i()->showTitle = FALSE;
\IPS\Output::i()->title = \IPS\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'] .= ' 🎉';
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'dashboard' )->onboard();
}
}
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\promotion;
/* 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;
@@ -22,6 +22,16 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _advertisements extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* @brief Active tab
*/
protected $activeTab = '';
/**
* Execute
*
@@ -30,22 +40,96 @@ class _advertisements extends \IPS\Dispatcher\Controller
public function execute()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'advertisements_manage' );
/* Get tab content */
$this->activeTab = \IPS\Request::i()->tab ?: 'site';
parent::execute();
}
/**
* Manage
*
* @return void
*/
protected function manage()
{
/* Work out output */
$thisUrl = \IPS\Http\Url::internal( 'app=core&module=promotion&controller=advertisements' );
$activeTabContents = (string) static::table( $thisUrl, ( $this->activeTab == 'emails' ) );
/* If this is an AJAX request, just return it */
if( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->output = $activeTabContents;
return;
}
/* Action Buttons */
\IPS\Output::i()->sidebar['actions']['settings'] = array(
'title' => 'ad_settings',
'icon' => 'cog',
'link' => \IPS\Http\Url::internal( 'app=core&module=promotion&controller=advertisements&do=settings' ),
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('ad_settings') )
);
/* If Nexus is not installed, show message */
if( !\IPS\Application::appIsEnabled( 'nexus' ) AND !\IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'promotion' )->advertisementMessage( );
}
/* Display */
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'promotion/advertisements.css', 'core', 'admin' ) );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('advertisements');
/* Build tab list */
$tabs = array( 'site' => 'advertisements_site', 'emails' => 'advertisements_emails' );
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate( 'global' )->tabs( $tabs, $this->activeTab, $activeTabContents, $thisUrl );
}
/**
* Get table
*
* @param \IPS\Http\Url $url The URL the table will be shown on
* @param bool $email Email advertisements (true) or website (false)
* @return void
*/
public static function table( \IPS\Http\Url $url, $email=FALSE )
{
/* Create the table */
$table = new \IPS\Helpers\Table\Db( 'core_advertisements', \IPS\Http\Url::internal( 'app=core&module=promotion&controller=advertisements' ) );
$where = NULL;
if( $email === TRUE )
{
$where = array( array( 'ad_type=?', \IPS\core\Advertisement::AD_EMAIL ) );
$url = $url->setQueryString( 'tab', 'emails' );
\IPS\Member::loggedIn()->language()->words['ads_ad_impressions'] = \IPS\Member::loggedIn()->language()->addToStack('ads_ad_sends');
}
else
{
$where = array( array( 'ad_type!=?', \IPS\core\Advertisement::AD_EMAIL ) );
$url = $url->setQueryString( 'tab', 'site' );
}
$table = new \IPS\Helpers\Table\Db( 'core_advertisements', $url, $where );
$table->langPrefix = 'ads_';
/* Columns we need */
$table->include = array( 'word_custom', 'ad_html', 'ad_impressions', 'ad_clicks', 'ad_active' );
$table->include = array( 'word_custom', 'ad_html', 'ad_impressions' );
if( $email === TRUE )
{
$table->include[] = 'ad_email_views';
$table->include[] = 'ad_clicks';
$table->include[] = 'ad_active';
}
else
{
$table->include[] = 'ad_clicks';
$table->include[] = 'ad_active';
}
$table->mainColumn = 'word_custom';
$table->noSort = array( 'ad_images', 'ad_html' );
$table->joins = array(
@@ -67,12 +151,12 @@ class _advertisements extends \IPS\Dispatcher\Controller
/* Filters */
$table->filters = array(
'ad_filters_active' => array('ad_active=1 AND ( ad_end=0 OR ad_end > ? )', time() ),
'ad_filters_inactive' => array('ad_active=0 OR ad_end<? ', time() ),
'ad_filters_active' => array( 'ad_active=1 AND ( ad_end=0 OR ad_end > ? )', time() ),
'ad_filters_inactive' => array( '(ad_active=0 OR (ad_end>0 AND ad_end<?))', time() ),
);
/* If Nexus is installed, we get the pending filter too */
if( \IPS\Application::appIsEnabled( 'nexus' ) )
if( \IPS\Application::appIsEnabled( 'nexus' ) AND $email === FALSE )
{
$table->filters['ad_filters_pending'] = 'ad_active=-1';
}
@@ -85,7 +169,7 @@ class _advertisements extends \IPS\Dispatcher\Controller
},
'ad_html' => function( $val, $row )
{
if( $row['ad_html'] )
if( $row['ad_type'] == \IPS\core\Advertisement::AD_HTML )
{
$preview = \IPS\Theme::i()->getTemplate( 'promotion' )->advertisementIframePreview( $row['ad_id'] );
}
@@ -93,7 +177,7 @@ class _advertisements extends \IPS\Dispatcher\Controller
{
$advert = \IPS\core\Advertisement::constructFromData( $row );
if( !count( $advert->_images ) )
if( !\count( $advert->_images ) )
{
return '';
}
@@ -110,17 +194,26 @@ class _advertisements extends \IPS\Dispatcher\Controller
'ad_clicks' => function( $val, $row )
{
return $row['ad_html'] ? \IPS\Theme::i()->getTemplate( 'global' )->shortMessage( 'unavailable', array( 'ipsType_light' ) ) : $val;
}
},
'ad_impressions' => function( $val, $row )
{
return $val;
},
'ad_email_views' => function( $val, $row )
{
return \intval( $val );
},
);
/* Specify the buttons */
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'promotion', 'advertisements_add' ) )
{
\IPS\Output::i()->sidebar['actions']['add'] = array(
'primary' => TRUE,
'icon' => 'plus',
'title' => 'add',
'link' => \IPS\Http\Url::internal( 'app=core&module=promotion&controller=advertisements&do=form' )
$table->rootButtons = array(
'add' => array(
'icon' => 'plus',
'title' => 'add',
'link' => \IPS\Http\Url::internal( 'app=core&module=promotion&controller=advertisements&do=form&type=' . ( ( $email === TRUE ) ? 'emails' : 'site' ) )
)
);
}
@@ -135,7 +228,7 @@ class _advertisements extends \IPS\Dispatcher\Controller
$return['approve'] = array(
'icon' => 'check',
'title' => 'approve',
'link' => \IPS\Http\Url::internal( 'app=core&module=promotion&controller=advertisements&do=toggle&status=1&id=' . $row['ad_id'] ),
'link' => \IPS\Http\Url::internal( 'app=core&module=promotion&controller=advertisements&do=toggle&status=1&id=' . $row['ad_id'] )->csrf(),
'hotkey' => 'a',
);
}
@@ -161,24 +254,8 @@ class _advertisements extends \IPS\Dispatcher\Controller
return $return;
};
/* Action Buttons */
\IPS\Output::i()->sidebar['actions']['settings'] = array(
'title' => 'ad_settings',
'icon' => 'cog',
'link' => \IPS\Http\Url::internal( 'app=core&module=promotion&controller=advertisements&do=settings' ),
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('ad_settings') )
);
/* If Nexus is not installed, show message */
if( !\IPS\Application::appIsEnabled( 'nexus' ) AND !\IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'promotion' )->advertisementMessage( );
}
/* Display */
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'promotion/advertisements.css', 'core', 'admin' ) );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('advertisements');
\IPS\Output::i()->output .= (string) $table;
/* Return */
return $table;
}
/**
@@ -251,6 +328,8 @@ class _advertisements extends \IPS\Dispatcher\Controller
*/
protected function toggle()
{
\IPS\Session::i()->csrfCheck();
/* Get our record */
try
{
@@ -308,6 +387,16 @@ class _advertisements extends \IPS\Dispatcher\Controller
try
{
$record = \IPS\core\Advertisement::load( \IPS\Request::i()->id );
if( $record->type == \IPS\core\Advertisement::AD_EMAIL )
{
\IPS\Request::i()->type = 'emails';
\IPS\Member::loggedIn()->language()->words['ad_impressions'] = \IPS\Member::loggedIn()->language()->addToStack('ad_sends_so_far');
}
else
{
\IPS\Request::i()->type = 'site';
}
}
catch( \OutOfRangeException $e )
{
@@ -325,80 +414,134 @@ class _advertisements extends \IPS\Dispatcher\Controller
/* Start the form */
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\Translatable( 'ad_title', NULL, TRUE, array( 'app' => 'core', 'key' => ( !$record->id ) ? NULL : "core_advert_{$record->id}" ) ) );
$form->add( new \IPS\Helpers\Form\Radio( 'ad_type', ( $record->id ) ? ( $record->html ? 'html' : 'image' ) : 'html', TRUE, array( 'options' => array( 'html' => 'ad_type_html', 'image' => 'ad_type_image' ), 'toggles' => array( 'html' => array( 'ad_html', 'ad_html_specify_https', 'ad_maximums_html' ), 'image' => array( 'ad_url', 'ad_new_window', 'ad_image', 'ad_image_more', 'ad_clicks', 'ad_maximums_image' ) ) ), NULL, NULL, NULL, 'ad_type' ) );
/* Show the fields for an HTML advertisement */
$form->add( new \IPS\Helpers\Form\Codemirror( 'ad_html', ( $record->id ) ? $record->html : NULL, FALSE, array(), NULL, NULL, NULL, 'ad_html' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'ad_html_specify_https', ( $record->id ) ? $record->html_https_set : FALSE, FALSE, array( 'togglesOn' => array( 'ad_html_https' ) ), NULL, NULL, NULL, 'ad_html_specify_https' ) );
$form->add( new \IPS\Helpers\Form\Codemirror( 'ad_html_https', ( $record->id ) ? $record->html_https : NULL, FALSE, array(), NULL, NULL, NULL, 'ad_html_https' ) );
if( \IPS\Request::i()->type != 'emails' )
{
$form->add( new \IPS\Helpers\Form\Radio( 'ad_type', ( $record->id ) ? $record->type : \IPS\core\Advertisement::AD_HTML, TRUE, array( 'options' => array( 1 => 'ad_type_html', 2 => 'ad_type_image' ), 'toggles' => array( \IPS\core\Advertisement::AD_HTML => array( 'ad_html', 'ad_html_specify_https', 'ad_maximums_html', 'ad_location', 'ad_exempt' ), \IPS\core\Advertisement::AD_IMAGES => array( 'ad_url', 'ad_image_alt', 'ad_new_window', 'ad_image', 'ad_image_more', 'ad_clicks', 'ad_maximums_image', 'ad_location', 'ad_exempt' ) ) ), NULL, NULL, NULL, 'ad_type' ) );
/* Show the fields for an image advertisement */
/* Show the fields for an HTML advertisement */
$form->add( new \IPS\Helpers\Form\Codemirror( 'ad_html', ( $record->id ) ? $record->html : NULL, NULL, array(), function( $val ) {
if( \IPS\Request::i()->ad_type == 1 AND !$val ) {
throw new \DomainException('form_required');
}
}, NULL, NULL, 'ad_html' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'ad_html_specify_https', ( $record->id ) ? $record->html_https_set : FALSE, FALSE, array( 'togglesOn' => array( 'ad_html_https' ) ), NULL, NULL, NULL, 'ad_html_specify_https' ) );
$form->add( new \IPS\Helpers\Form\Codemirror( 'ad_html_https', ( $record->id ) ? $record->html_https : NULL, FALSE, array(), NULL, NULL, NULL, 'ad_html_https' ) );
}
/* Show the fields for an image advertisement (and most of the email ad settings as well) */
$form->add( new \IPS\Helpers\Form\Url( 'ad_url', ( $record->id ) ? $record->link : NULL, FALSE, array(), NULL, NULL, NULL, 'ad_url' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'ad_new_window', ( $record->id ) ? $record->new_window : FALSE, FALSE, array(), NULL, NULL, NULL, 'ad_new_window' ) );
$form->add( new \IPS\Helpers\Form\Upload( 'ad_image', ( $record->id ) ? ( ( isset( $record->_images['large'] ) and $record->_images['large'] ) ? \IPS\File::get( 'core_Advertisements', $record->_images['large'] ) : NULL ) : NULL, FALSE, array( 'image' => TRUE, 'storageExtension' => 'core_Advertisements' ), NULL, NULL, NULL, 'ad_image' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'ad_image_more', ( $record->id ) ? ( ( ( isset( $record->_images['medium'] ) and $record->_images['medium'] ) OR ( ( isset( $record->_images['small'] ) and $record->_images['small'] ) ) ) ? TRUE : FALSE ) : FALSE, FALSE, array( 'togglesOn' => array( 'ad_image_small', 'ad_image_medium' ) ), NULL, NULL, NULL, 'ad_image_more' ) );
$form->add( new \IPS\Helpers\Form\Upload( 'ad_image_small', ( $record->id ) ? ( ( isset( $record->_images['small'] ) and $record->_images['small'] ) ? \IPS\File::get( 'core_Advertisements', $record->_images['small'] ) : NULL ) : NULL, FALSE, array( 'image' => TRUE, 'storageExtension' => 'core_Advertisements' ), NULL, NULL, NULL, 'ad_image_small' ) );
$form->add( new \IPS\Helpers\Form\Upload( 'ad_image_medium', ( $record->id ) ? ( ( isset( $record->_images['medium'] ) and $record->_images['medium'] ) ? \IPS\File::get( 'core_Advertisements', $record->_images['medium'] ) : NULL ) : NULL, FALSE, array( 'image' => TRUE, 'storageExtension' => 'core_Advertisements' ), NULL, NULL, NULL, 'ad_image_medium' ) );
/* Add the location fields, remember to call extensions for additional locations.
Array format: location => array of toggle fields to show */
$defaultLocations = array(
'ad_global_header' => array(),
'ad_global_footer' => array(),
'ad_sidebar' => array(),
);
$currentValues = ( $record->id ) ? json_decode( $record->additional_settings, TRUE ) : array();
/* Now grab ad location extensions */
foreach ( \IPS\Application::allExtensions( 'core', 'AdvertisementLocations', FALSE, 'core' ) as $key => $extension )
if( \IPS\Request::i()->type != 'emails' )
{
$result = $extension->getSettings( $currentValues );
$form->add( new \IPS\Helpers\Form\YesNo( 'ad_new_window', ( $record->id ) ? $record->new_window : FALSE, FALSE, array(), NULL, NULL, NULL, 'ad_new_window' ) );
}
$defaultLocations = array_merge( $defaultLocations, $result['locations'] );
$form->add( new \IPS\Helpers\Form\Upload( 'ad_image', ( $record->id ) ? ( ( isset( $record->_images['large'] ) and $record->_images['large'] ) ? \IPS\File::get( 'core_Advertisements', $record->_images['large'] ) : NULL ) : NULL, FALSE, array( 'image' => TRUE, 'storageExtension' => 'core_Advertisements' ), NULL, NULL, NULL, 'ad_image' ) );
if( isset( $result['settings'] ) )
if( \IPS\Request::i()->type != 'emails' )
{
$form->add( new \IPS\Helpers\Form\YesNo( 'ad_image_more', ( $record->id ) ? ( ( ( isset( $record->_images['medium'] ) and $record->_images['medium'] ) OR ( ( isset( $record->_images['small'] ) and $record->_images['small'] ) ) ) ? TRUE : FALSE ) : FALSE, FALSE, array( 'togglesOn' => array( 'ad_image_small', 'ad_image_medium' ) ), NULL, NULL, NULL, 'ad_image_more' ) );
$form->add( new \IPS\Helpers\Form\Upload( 'ad_image_small', ( $record->id ) ? ( ( isset( $record->_images['small'] ) and $record->_images['small'] ) ? \IPS\File::get( 'core_Advertisements', $record->_images['small'] ) : NULL ) : NULL, FALSE, array( 'image' => TRUE, 'storageExtension' => 'core_Advertisements' ), NULL, NULL, NULL, 'ad_image_small' ) );
$form->add( new \IPS\Helpers\Form\Upload( 'ad_image_medium', ( $record->id ) ? ( ( isset( $record->_images['medium'] ) and $record->_images['medium'] ) ? \IPS\File::get( 'core_Advertisements', $record->_images['medium'] ) : NULL ) : NULL, FALSE, array( 'image' => TRUE, 'storageExtension' => 'core_Advertisements' ), NULL, NULL, NULL, 'ad_image_medium' ) );
}
$form->add( new \IPS\Helpers\Form\Text( 'ad_image_alt', ( $record->id ) ? $record->image_alt : NULL, FALSE, array(), NULL, NULL, NULL, 'ad_image_alt' ) );
if( \IPS\Request::i()->type != 'emails' )
{
/* Add the location fields, remember to call extensions for additional locations.
Array format: location => array of toggle fields to show */
$defaultLocations = array(
'ad_global_header' => array(),
'ad_global_footer' => array(),
'ad_sidebar' => array(),
);
$currentValues = ( $record->id ) ? json_decode( $record->additional_settings, TRUE ) : array();
/* Now grab ad location extensions */
foreach ( \IPS\Application::allExtensions( 'core', 'AdvertisementLocations', FALSE, 'core' ) as $key => $extension )
{
foreach( $result['settings'] as $setting )
$result = $extension->getSettings( $currentValues );
$defaultLocations = array_merge( $defaultLocations, $result['locations'] );
if( isset( $result['settings'] ) )
{
$form->add( $setting );
foreach( $result['settings'] as $setting )
{
$form->add( $setting );
}
}
}
$defaultLocations['_ad_custom_'] = array('ad_location_custom');
/* Add the locations to the form, and make sure the toggles get set properly */
$locations = array();
$customLocations = array();
if ( $record->id )
{
$locations = explode( ',', $record->location );
$customLocations = array_diff( $locations, array_keys( $defaultLocations ) );
if ( !empty( $customLocations ) )
{
$locations[] = '_ad_custom_';
}
$locations = array_intersect( $locations, array_keys( $defaultLocations ) );
}
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'ad_location',
$locations,
TRUE,
array(
'options' => array_combine( array_keys( $defaultLocations ), array_keys( $defaultLocations ) ),
'toggles' => $defaultLocations,
),
NULL,
NULL,
NULL,
'ad_location'
) );
$form->add( new \IPS\Helpers\Form\Stack( 'ad_location_custom', $customLocations, FALSE, array(), NULL, NULL, NULL, 'ad_location_custom' ) );
/* Generic fields available for both html and image ads */
$form->add( new \IPS\Helpers\Form\Select( 'ad_exempt', ( $record->id ) ? ( ( $record->exempt == '*' ) ? '*' : json_decode( $record->exempt, TRUE ) ) : '*', FALSE, array( 'options' => \IPS\Member\Group::groups(), 'parse' => 'normal', 'multiple' => TRUE, 'unlimited' => '*', 'unlimitedLang' => 'everyone' ), NULL, NULL, NULL, 'ad_exempt' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'ad_nocontent_page_output', ( $record->id ) ? $record->nocontent_page_output : FALSE ) );
}
else
{
/* Container restrictions for email ads */
$containerClasses = array();
$containerToggles = array();
foreach( \IPS\Content::routedClasses( FALSE, FALSE, TRUE ) as $contentItemClass )
{
if( isset( $contentItemClass::$containerNodeClass ) AND $contentItemClass::$containerNodeClass AND !isset( $containerClasses[ $contentItemClass::$containerNodeClass ] ) )
{
$containerClass = $contentItemClass::$containerNodeClass;
$containerClasses[ $containerClass ] = $contentItemClass::$title . '_pl';
$containerToggles[ $containerClass ] = array( 'node_' . md5( $containerClass ) );
}
}
if( \count( $containerClasses ) )
{
$form->add( new \IPS\Helpers\Form\Select( '_ad_email_container', ( $record->id AND isset( $record->_additional_settings['email_container'] ) ) ? $record->_additional_settings['email_container'] : '*', FALSE, array( 'options' => $containerClasses, 'multiple' => FALSE, 'unlimited' => '*', 'unlimitedLang' => 'unrestricted', 'toggles' => $containerToggles ), NULL, NULL, NULL, '_ad_email_container' ) );
foreach( $containerClasses as $classname => $lang )
{
$form->add( new \IPS\Helpers\Form\Node( 'node_' . md5( $classname ), ( $record->id ) ? ( isset( $record->_additional_settings['email_node'] ) ? $record->_additional_settings['email_node'] : 0 ) : 0, FALSE, array( 'class' => $classname, 'zeroVal' => 'any', 'multiple' => TRUE, 'forceOwner' => FALSE ), NULL, NULL, NULL, 'node_' . md5( $classname ) ) );
\IPS\Member::loggedIn()->language()->words[ 'node_' . md5( $classname ) ] = \IPS\Member::loggedIn()->language()->addToStack( $classname::$nodeTitle );
}
}
}
$defaultLocations['_ad_custom_'] = array('ad_location_custom');
/* Add the locations to the form, and make sure the toggles get set properly */
$locations = array();
$customLocations = array();
if ( $record->id )
{
$locations = explode( ',', $record->location );
$customLocations = array_diff( $locations, array_keys( $defaultLocations ) );
if ( !empty( $customLocations ) )
{
$locations[] = '_ad_custom_';
}
$locations = array_intersect( $locations, array_keys( $defaultLocations ) );
}
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'ad_location',
$locations,
TRUE,
array(
'options' => array_combine( array_keys( $defaultLocations ), array_keys( $defaultLocations ) ),
'toggles' => $defaultLocations,
),
NULL,
NULL,
NULL,
'ad_location'
) );
$form->add( new \IPS\Helpers\Form\Stack( 'ad_location_custom', $customLocations, FALSE, array(), NULL, NULL, NULL, 'ad_location_custom' ) );
/* Generic fields universally available for both types of ads */
$form->add( new \IPS\Helpers\Form\Select( 'ad_exempt', ( $record->id ) ? ( ( $record->exempt == '*' ) ? '*' : json_decode( $record->exempt, TRUE ) ) : '*', FALSE, array( 'options' => \IPS\Member\Group::groups(), 'parse' => 'normal', 'multiple' => TRUE, 'unlimited' => '*', 'unlimitedLang' => 'everyone' ) ) );
/* Generic fields for all ad types */
$form->add( new \IPS\Helpers\Form\Date( 'ad_start', ( $record->id ) ? \IPS\DateTime::ts( $record->start ) : new \IPS\DateTime, TRUE ) );
$form->add( new \IPS\Helpers\Form\Date( 'ad_end', ( $record->id ) ? ( $record->end ? \IPS\DateTime::ts( $record->end ) : 0 ) : 0, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'indefinitely' ) ) );
@@ -407,10 +550,19 @@ class _advertisements extends \IPS\Dispatcher\Controller
{
$form->add( new \IPS\Helpers\Form\Number( 'ad_impressions', ( $record->id ) ? $record->impressions : 0, FALSE, array(), NULL, NULL, NULL, 'ad_impressions' ) );
$form->add( new \IPS\Helpers\Form\Number( 'ad_clicks', ( $record->id ) ? $record->clicks : 0, FALSE, array(), NULL, NULL, NULL, 'ad_clicks' ) );
if( \IPS\Request::i()->type == 'emails' )
{
$form->add( new \IPS\Helpers\Form\Number( 'ad_email_views', ( $record->id ) ? $record->email_views : 0, FALSE, array(), NULL, NULL, NULL, 'ad_email_views' ) );
}
}
/* Click/impression maximum cutoffs, toggled depending upon HTML or image type ad */
$form->add( new \IPS\Helpers\Form\Number( 'ad_maximums_html', ( $record->id ) ? $record->maximum_value : -1, FALSE, array( 'unlimited' => -1 ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('ad_max_impressions'), 'ad_maximums_html' ) );
if( \IPS\Request::i()->type != 'emails' )
{
$form->add( new \IPS\Helpers\Form\Number( 'ad_maximums_html', ( $record->id ) ? $record->maximum_value : -1, FALSE, array( 'unlimited' => -1 ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('ad_max_impressions'), 'ad_maximums_html' ) );
}
$form->add( new \IPS\Helpers\Form\Custom( 'ad_maximums_image', array( 'value' => ( $record->id ) ? $record->maximum_value : -1, 'type' => ( $record->id ) ? $record->maximum_unit : 'i' ), FALSE, array(
'getHtml' => function( $element )
{
@@ -418,7 +570,7 @@ class _advertisements extends \IPS\Dispatcher\Controller
},
'formatValue' => function( $element )
{
if( !is_array( $element->value ) AND $element->value == -1 )
if( !\is_array( $element->value ) AND $element->value == -1 )
{
return array( 'value' => -1, 'type' => 'i' );
}
@@ -430,36 +582,48 @@ class _advertisements extends \IPS\Dispatcher\Controller
/* Handle submissions */
if ( $values = $form->values() )
{
$locations = $values['ad_location'];
$customKey = array_search( '_ad_custom_', $locations );
if ( $customKey !== FALSE )
if( \IPS\Request::i()->type != 'emails' )
{
unset( $locations[ $customKey ] );
$locations = array_merge( $locations, $values['ad_location_custom'] );
$locations = $values['ad_location'];
$customKey = array_search( '_ad_custom_', $locations );
if ( $customKey !== FALSE )
{
unset( $locations[ $customKey ] );
$locations = array_merge( $locations, $values['ad_location_custom'] );
}
}
else
{
$locations = array();
$values['ad_type'] = \IPS\core\Advertisement::AD_EMAIL;
}
/* Let us start with the easy stuff... */
$record->type = $values['ad_type'];
$record->location = implode( ',', $locations );
$record->html = ( $values['ad_type'] == 'html' ) ? $values['ad_html'] : NULL;
$record->link = ( $values['ad_type'] == 'image' ) ? $values['ad_url'] : NULL;
$record->new_window = ( $values['ad_type'] == 'image' ) ? $values['ad_new_window'] : 0;
$record->html = ( $values['ad_type'] == \IPS\core\Advertisement::AD_HTML ) ? $values['ad_html'] : NULL;
$record->link = ( $values['ad_type'] != \IPS\core\Advertisement::AD_HTML ) ? $values['ad_url'] : NULL;
$record->image_alt = ( $values['ad_type'] != \IPS\core\Advertisement::AD_HTML ) ? $values['ad_image_alt'] : NULL;
$record->new_window = ( $values['ad_type'] == \IPS\core\Advertisement::AD_IMAGES ) ? $values['ad_new_window'] : 0;
$record->impressions = ( isset( $values['ad_impressions'] ) ) ? $values['ad_impressions'] : 0;
$record->clicks = ( $values['ad_type'] == 'image' AND isset( $values['ad_clicks'] ) ) ? $values['ad_clicks'] : 0;
$record->clicks = ( $values['ad_type'] != \IPS\core\Advertisement::AD_HTML AND isset( $values['ad_clicks'] ) ) ? $values['ad_clicks'] : 0;
$record->email_views = ( $values['ad_type'] == \IPS\core\Advertisement::AD_EMAIL AND isset( $values['ad_email_views'] ) ) ? $values['ad_email_views'] : NULL;
$record->active = ( $record->id ) ? $record->active : 1;
$record->html_https = ( $values['ad_type'] == 'html' ) ? $values['ad_html_https'] : NULL;
$record->html_https = ( $values['ad_type'] == \IPS\core\Advertisement::AD_HTML ) ? $values['ad_html_https'] : NULL;
$record->start = $values['ad_start'] ? $values['ad_start']->getTimestamp() : 0;
$record->end = $values['ad_end'] ? $values['ad_end']->getTimestamp() : 0;
$record->exempt = ( $values['ad_exempt'] == '*' ) ? '*' : json_encode( $values['ad_exempt'] );
$record->exempt = ( $values['ad_type'] != \IPS\core\Advertisement::AD_EMAIL ) ? ( $values['ad_exempt'] == '*' ) ? '*' : json_encode( $values['ad_exempt'] ) : NULL;
$record->images = NULL;
$record->maximum_value = ( $values['ad_type'] == 'html' ) ? $values['ad_maximums_html'] : $values['ad_maximums_image']['value'];
$record->maximum_unit = ( $values['ad_type'] == 'html' ) ? 'i' : $values['ad_maximums_image']['type'];
$record->maximum_value = ( $values['ad_type'] == \IPS\core\Advertisement::AD_HTML ) ? $values['ad_maximums_html'] : $values['ad_maximums_image']['value'];
$record->maximum_unit = ( $values['ad_type'] == \IPS\core\Advertisement::AD_HTML ) ? 'i' : $values['ad_maximums_image']['type'];
$record->additional_settings = NULL;
$record->html_https_set = ( $values['ad_type'] == 'html' ) ? $values['ad_html_specify_https'] : 0;
$record->html_https_set = ( $values['ad_type'] == \IPS\core\Advertisement::AD_HTML ) ? $values['ad_html_specify_https'] : 0;
$record->nocontent_page_output = isset( $values['ad_nocontent_page_output'] ) ? $values['ad_nocontent_page_output'] : FALSE;
/* Figure out the ad_images */
$images = array();
if( $values['ad_type'] == 'image' )
if( $values['ad_type'] == \IPS\core\Advertisement::AD_IMAGES )
{
$images = array( 'large' => (string) $values['ad_image'] );
@@ -484,7 +648,22 @@ class _advertisements extends \IPS\Dispatcher\Controller
$values['ad_image_medium']->delete();
}
}
else
elseif( $values['ad_type'] == \IPS\core\Advertisement::AD_EMAIL )
{
$images = array( 'large' => (string) $values['ad_image'] );
/* Make sure we don't retain any small/medium copies if they switched to images then to email */
if( isset( $values['ad_image_small'] ) AND $values['ad_image_small'] )
{
$values['ad_image_small']->delete();
}
if( isset( $values['ad_image_medium'] ) AND $values['ad_image_medium'] )
{
$values['ad_image_medium']->delete();
}
}
elseif( $values['ad_type'] == \IPS\core\Advertisement::AD_HTML )
{
/* Did they upload images and then switch back to an html type ad, by chance? */
if( isset( $values['ad_image'] ) AND $values['ad_image'] )
@@ -503,10 +682,13 @@ class _advertisements extends \IPS\Dispatcher\Controller
}
}
/* If we are editing, and we changed from image -> html, clean up old images */
if( $record->id AND count( $record->_images ) AND $values['ad_type'] == 'html' )
/* If we are editing, and we changed from image/email -> html/email, clean up old images */
if( $record->id AND \count( $record->_images ) AND ( $values['ad_type'] == \IPS\core\Advertisement::AD_HTML OR $values['ad_type'] == \IPS\core\Advertisement::AD_EMAIL ) )
{
\IPS\File::get( 'core_Advertisements', $record->_images['large'] )->delete();
if( $values['ad_type'] == \IPS\core\Advertisement::AD_HTML )
{
\IPS\File::get( 'core_Advertisements', $record->_images['large'] )->delete();
}
if( isset( $record->_images['small'] ) )
{
@@ -524,11 +706,19 @@ class _advertisements extends \IPS\Dispatcher\Controller
/* Any additional settings to save? */
$additionalSettings = array();
foreach ( \IPS\Application::allExtensions( 'core', 'AdvertisementLocations', FALSE, 'core' ) as $key => $extension )
if( $values['ad_type'] == \IPS\core\Advertisement::AD_EMAIL )
{
$settings = $extension->parseSettings( $values );
$additionalSettings['email_container'] = $values['_ad_email_container'];
$additionalSettings['email_node'] = ( $values['_ad_email_container'] != '*' ) ? ( ( $values['node_' . md5( $values['_ad_email_container'] ) ] == 0 ) ? 0 : array_keys( $values['node_' . md5( $values['_ad_email_container'] ) ] ) ) : 0;
}
else
{
foreach ( \IPS\Application::allExtensions( 'core', 'AdvertisementLocations', FALSE, 'core' ) as $key => $extension )
{
$settings = $extension->parseSettings( $values );
$additionalSettings = array_merge( $additionalSettings, $settings );
$additionalSettings = array_merge( $additionalSettings, $settings );
}
}
$record->additional_settings = json_encode( $additionalSettings );
@@ -545,7 +735,8 @@ class _advertisements extends \IPS\Dispatcher\Controller
$record->save();
/* Save if any exist */
$adsExist = (bool) \IPS\Db::i()->select( 'COUNT(*)', 'core_advertisements', 'ad_active=1' )->first();
$adsExist = $record->active ? TRUE : (bool) \IPS\Db::i()->select( 'COUNT(*)', 'core_advertisements', 'ad_active=1' )->first();
if ( $adsExist != \IPS\Settings::i()->ads_exist )
{
\IPS\Settings::i()->changeValues( array( 'ads_exist' => $adsExist ) );
@@ -558,7 +749,7 @@ class _advertisements extends \IPS\Dispatcher\Controller
\IPS\Lang::saveCustom( 'core', 'core_advert_' . $record->id, $values[ 'ad_title' ] );
/* Redirect */
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=promotion&controller=advertisements' ), 'saved' );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=promotion&controller=advertisements&tab=' . \IPS\Request::i()->type ), 'saved' );
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( ( !isset( \IPS\Request::i()->id ) ) ? 'add_advertisement' : 'edit_advertisement' );
@@ -589,6 +780,8 @@ class _advertisements extends \IPS\Dispatcher\Controller
\IPS\Output::i()->error( 'node_error', '2C157/7', 404, '' );
}
$preview = '';
if( $record->html )
{
if( \IPS\Request::i()->isSecure() AND $record->html_https_set )
@@ -600,11 +793,6 @@ class _advertisements extends \IPS\Dispatcher\Controller
$preview = $record->html;
}
}
else
{
$advert = \IPS\core\Advertisement::constructFromData( $record );
$preview = \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->advertisementImage( $advert );
}
$preview = preg_replace( "/<script(?:[^>]*?)?>.*<\/script>/ims", \IPS\Theme::i()->getTemplate( 'global' )->shortMessage( 'ad_script_disabled', array( 'ipsType_light' ) ), $preview );
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\promotion;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _analytics extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -85,6 +90,11 @@ class _analytics extends \IPS\Dispatcher\Controller
$values['ipseo_ga'] = $values['analytics_' . $values['ipbseo_ga_provider'] ];
}
foreach( $providers as $key => $provider )
{
unset( $values['analytics_' . $key ] );
}
$form->saveAsSettings( $values );
\IPS\Session::i()->log( 'acplog__analytics_edited' );
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\promotion;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _promote extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* @brief Active tab
*/
@@ -50,7 +55,8 @@ class _promote extends \IPS\Dispatcher\Controller
protected function manage()
{
/* Work out output */
$activeTabContents = call_user_func( array( $this, '_manage' . mb_ucfirst( $this->activeTab ) ) );
$methodFunction = '_manage' . mb_ucfirst( $this->activeTab );
$activeTabContents = $this->$methodFunction();
/* If this is an AJAX request, just return it */
if( \IPS\Request::i()->isAjax() )
@@ -269,6 +275,8 @@ class _promote extends \IPS\Dispatcher\Controller
if ( isset( \IPS\Request::i()->clear ) and \IPS\Request::i()->clear === 'true' )
{
\IPS\Session::i()->csrfCheck();
/* We want to clear settings */
$account->enabled = FALSE;
$account->saveSettings( array(
@@ -315,7 +323,7 @@ class _promote extends \IPS\Dispatcher\Controller
/* Are we saving? */
if ( $values = $form->values() )
{
if ( is_array( $values['promote_twitter_tags'] ) )
if ( \is_array( $values['promote_twitter_tags'] ) )
{
array_walk( $values['promote_twitter_tags'], function( &$value )
{
@@ -349,6 +357,28 @@ class _promote extends \IPS\Dispatcher\Controller
$facebook = \IPS\Login\Handler::findMethod('IPS\Login\Handler\Oauth2\Facebook');
$account = \IPS\core\Promote::getPromoter('Facebook')->setMember( \IPS\Member::loggedIn() );
if ( isset( \IPS\Request::i()->clear ) and \IPS\Request::i()->clear === 'true' )
{
\IPS\Session::i()->csrfCheck();
/* We want to clear settings */
$account->enabled = FALSE;
$account->saveSettings( array(
'token' => NULL,
'owner' => NULL,
'page_name' => NULL,
'enabled' => NULL,
'page' => NULL,
'group' => NULL,
'member_token' => NULL,
'shareable' => NULL
) );
\IPS\Settings::i()->changeValues( array( 'promote_facebook_auth' => '' ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=promotion&controller=promote&tab=facebook" ) );
}
/* We are using the default app here */
if ( \is_numeric( \IPS\Settings::i()->promote_facebook_auth ) )
{
@@ -426,27 +456,7 @@ class _promote extends \IPS\Dispatcher\Controller
}
$form = new \IPS\Helpers\Form;
if ( isset( \IPS\Request::i()->clear ) and \IPS\Request::i()->clear === 'true' )
{
/* We want to clear settings */
$account->enabled = FALSE;
$account->saveSettings( array(
'token' => NULL,
'owner' => NULL,
'page_name' => NULL,
'enabled' => NULL,
'page' => NULL,
'group' => NULL,
'member_token' => NULL,
'shareable' => NULL
) );
\IPS\Settings::i()->changeValues( array( 'promote_facebook_auth' => '' ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=promotion&controller=promote&tab=facebook" ) );
}
/* We're all set, so show the form! */
$pages = $facebook->getPages( \IPS\Member::loggedIn() );
$options = array();
@@ -517,7 +527,7 @@ class _promote extends \IPS\Dispatcher\Controller
/* Are we saving? */
if ( $values = $form->values() )
{
if ( is_array( $values['promote_facebook_tags'] ) )
if ( \is_array( $values['promote_facebook_tags'] ) )
{
array_walk( $values['promote_facebook_tags'], function( &$value )
{
@@ -646,6 +656,8 @@ class _promote extends \IPS\Dispatcher\Controller
*/
protected function reschedule()
{
\IPS\Session::i()->csrfCheck();
\IPS\core\Promote::rescheduleQueue();
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=promotion&controller=promote&tab=schedule" ), 'promote_rescheduled_done' );
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\promotion;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _seo extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* @brief Active tab
*/
@@ -40,7 +45,7 @@ class _seo extends \IPS\Dispatcher\Controller
$this->activeTab = \IPS\Request::i()->tab ?: 'urls';
\IPS\Output::i()->sidebar['actions']['rebuildsitemap'] = array(
'link' => \IPS\Http\Url::internal( 'app=core&module=promotion&controller=seo&do=rebuildSitemap' ),
'link' => \IPS\Http\Url::internal( 'app=core&module=promotion&controller=seo&do=rebuildSitemap' )->csrf(),
'title' => 'rebuild_sitemap',
'icon' => 'history'
);
@@ -56,7 +61,8 @@ class _seo extends \IPS\Dispatcher\Controller
protected function manage()
{
/* Work out output */
$activeTabContents = call_user_func( array( $this, '_manage' . mb_ucfirst( $this->activeTab ) ) );
$methodFunction = '_manage' . mb_ucfirst( $this->activeTab );
$activeTabContents = $this->$methodFunction();
/* If this is an AJAX request, just return it */
if( \IPS\Request::i()->isAjax() )
@@ -83,16 +89,13 @@ class _seo extends \IPS\Dispatcher\Controller
}
/**
* Manage FURL definitions
* Get setting to enable htaccess friendly URLs
*
* @return string
* @param \IPS\Form $form Form to add the setting to
* @return void
*/
protected function _manageUrls()
public static function htaccessSetting( $form )
{
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\YesNo( 'use_friendly_urls', \IPS\Settings::i()->use_friendly_urls, TRUE, array( 'togglesOn' => array( 'htaccess_mod_rewrite', 'seo_r_on' ), 'togglesOff' => array( 'use_friendly_urls_warning' ) ), NULL, NULL, NULL, 'use_friendly_urls' ) );
$isApache = mb_stripos( $_SERVER['SERVER_SOFTWARE'], 'apache' ) !== FALSE;
$form->add( new \IPS\Helpers\Form\YesNo( 'htaccess_mod_rewrite', \IPS\Settings::i()->htaccess_mod_rewrite, TRUE, array(), NULL, NULL, NULL, 'htaccess_mod_rewrite' ) );
if ( !$isApache )
@@ -105,7 +108,7 @@ class _seo extends \IPS\Dispatcher\Controller
{
$furlDefinition = \IPS\Http\Url::furlDefinition();
$response = \IPS\Http\Url::external( \IPS\Settings::i()->base_url . $furlDefinition['login']['friendly'] )->request( NULL, NULL, FALSE )->get();
if ( !in_array( mb_substr( $response->httpResponseCode, 0, 1 ), array( '2', '3' ) ) and ( \IPS\Settings::i()->site_online OR $response->httpResponseCode != 503 ) )
if ( !\in_array( mb_substr( $response->httpResponseCode, 0, 1 ), array( '2', '3' ) ) and ( \IPS\Settings::i()->site_online OR $response->httpResponseCode != 503 ) )
{
\IPS\Member::loggedIn()->language()->words['htaccess_mod_rewrite_warning'] = \IPS\Member::loggedIn()->language()->get( $isApache ? 'htaccess_mod_rewrite_err' : 'htaccess_mod_rewrite_err_na' );
}
@@ -115,6 +118,20 @@ class _seo extends \IPS\Dispatcher\Controller
\IPS\Member::loggedIn()->language()->words['htaccess_mod_rewrite_warning'] = \IPS\Member::loggedIn()->language()->get( $isApache ? 'htaccess_mod_rewrite_err' : 'htaccess_mod_rewrite_err_na' );
}
}
}
/**
* Manage FURL definitions
*
* @return string
*/
protected function _manageUrls()
{
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\YesNo( 'use_friendly_urls', \IPS\Settings::i()->use_friendly_urls, TRUE, array( 'togglesOn' => array( 'htaccess_mod_rewrite', 'seo_r_on' ), 'togglesOff' => array( 'use_friendly_urls_warning' ) ), NULL, NULL, NULL, 'use_friendly_urls' ) );
static::htaccessSetting( $form );
$form->add( new \IPS\Helpers\Form\YesNo( 'seo_r_on', \IPS\Settings::i()->seo_r_on, TRUE, array( 'togglesOff' => array( 'seo_r_on_warning' ) ), NULL, NULL, NULL, 'seo_r_on' ) );
@@ -280,7 +297,9 @@ class _seo extends \IPS\Dispatcher\Controller
foreach ( \IPS\Db::i()->select( '*', 'core_seo_meta' ) as $row )
{
$rows[ $row['meta_url'] ] = \IPS\Theme::i()->getTemplate( 'trees' )->row( $url, $row['meta_id'], $row['meta_url'], TRUE, array(
$urlToDisplay = \IPS\Theme::i()->getTemplate( 'promotion' )->metaTagUrl( trim( $row['meta_url'], '/' ) );
$rows[ $row['meta_url'] ] = \IPS\Theme::i()->getTemplate( 'trees' )->row( $url, $row['meta_id'], $urlToDisplay, TRUE, array(
'edit' => array(
'icon' => 'pencil',
'title' => 'seo_meta_manage',
@@ -293,7 +312,7 @@ class _seo extends \IPS\Dispatcher\Controller
'link' => \IPS\Http\Url::internal( "app=core&module=promotion&controller=seo&tab=metatags&delete=" . $row['meta_id'] ),
'data' => array( 'delete' => '' )
)
), "", NULL, NULL );
), "", NULL, NULL, FALSE, NULL, NULL, NULL, TRUE );
}
return $rows;
@@ -330,7 +349,7 @@ class _seo extends \IPS\Dispatcher\Controller
$tags = json_decode( $meta['meta_tags'], TRUE );
$rows = array();
if( is_array( $tags ) )
if( \is_array( $tags ) )
{
foreach ( $tags as $name => $content )
{
@@ -341,7 +360,7 @@ class _seo extends \IPS\Dispatcher\Controller
'link' => \IPS\Http\Url::internal( $url . "&root={$key}&delete={$name}" ),
'data' => array( 'delete' => '' )
)
), $content );
), $content ?? \IPS\Member::loggedIn()->language()->addToStack('meta_tag_acp_deleted') );
}
}
@@ -413,7 +432,7 @@ class _seo extends \IPS\Dispatcher\Controller
'name' => function( $key, $value, $data )
{
return new \IPS\Helpers\Form\Select( $key,
$data['name'],
$data ? $data['name'] : '',
TRUE,
array( 'options' => array( 'keywords' => 'meta_keywords', 'description' => 'meta_description', 'robots' => 'meta_robots', 'other' => 'meta_other' ), 'toggles' => array( 'other' => array( 'other_' . preg_replace( "/[^a-zA-Z0-9\-_]/", "_", $key ) ) ), 'userSuppliedInput' => 'other' ),
NULL,
@@ -424,12 +443,12 @@ class _seo extends \IPS\Dispatcher\Controller
},
'content' => function( $key, $value, $data )
{
return new \IPS\Helpers\Form\TextArea( $key, $data['content'], FALSE );
return new \IPS\Helpers\Form\TextArea( $key, $data ? $data['content'] : '', FALSE, array( 'nullLang' => 'meta_tag_null_acp_form' ) );
},
);
/* Add rows */
if( count( $tags ) )
if( \count( $tags ) )
{
foreach( $tags as $tagName => $tagValue )
{
@@ -448,7 +467,7 @@ class _seo extends \IPS\Dispatcher\Controller
foreach( $values['metatag_tags'] as $index => $data )
{
if( !$data['name'] OR !$data['content'] )
if( !$data['name'] OR $data['content'] === '' )
{
continue;
}
@@ -459,7 +478,7 @@ class _seo extends \IPS\Dispatcher\Controller
\IPS\Db::i()->delete( 'core_seo_meta', array( 'meta_url=?', $url ) );
\IPS\Db::i()->delete( 'core_seo_meta', array( 'meta_url=?', \IPS\Request::i()->original_url ) );
if( $title or count( $tags ) )
if( $title or \count( $tags ) )
{
\IPS\Db::i()->insert( 'core_seo_meta', array( 'meta_url' => $url, 'meta_title' => $title, 'meta_tags' => json_encode( $tags ) ) );
}
@@ -474,6 +493,7 @@ class _seo extends \IPS\Dispatcher\Controller
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=promotion&controller=seo&tab=metatags" ) );
}
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'promotion/meta.css', 'core', 'admin' ) );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('seo_meta_add');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->block( 'seo_meta_add', $form );
}
@@ -516,6 +536,8 @@ FILE;
*/
protected function rebuildSitemap()
{
\IPS\Session::i()->csrfCheck();
/* truncate sitemap */
\IPS\Db::i()->delete( 'core_sitemap' );
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\promotion;
/* 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;
@@ -21,7 +21,12 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
* Share Link Services
*/
class _sharelinks extends \IPS\Node\Controller
{
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Node Class
*/
@@ -41,9 +46,11 @@ class _sharelinks extends \IPS\Node\Controller
/* First, see if any are missing */
$nodeClass = $this->nodeClass;
$shareServices = \IPS\Content\ShareServices::services();
foreach( $nodeClass::roots() as $node )
{
if( !file_exists( \IPS\ROOT_PATH . '/system/Content/ShareServices/' . ucwords( $node->key ) . '.php' ) )
if( !isset( $shareServices[ ucwords( $node->key ) ] ) )
{
$node->delete();
$reloadRoots = TRUE;
@@ -51,26 +58,19 @@ class _sharelinks extends \IPS\Node\Controller
}
/* Now see if there are any new classes */
foreach( new \DirectoryIterator( \IPS\ROOT_PATH . '/system/Content/ShareServices/' ) as $file )
foreach( \IPS\Content\ShareServices::services() as $id => $className )
{
if( $file->isDir() OR $file->isDot() OR $file->getExtension() !== 'php' )
{
continue;
}
$className = str_replace( '.php', '', $file->getFilename() );
try
{
$nodeClass::load( mb_strtolower( $className ), 'share_key' );
$nodeClass::load( mb_strtolower( $id ), 'share_key' );
}
catch( \OutOfRangeException $e )
{
/* Class does not exist - let's add it */
$newService = new \IPS\core\ShareLinks\Service;
$newService->key = mb_strtolower( $className );
$newService->key = mb_strtolower( $id );
$newService->groups = '*';
$newService->title = $className;
$newService->title = $id;
$newService->enabled = 0;
$newService->save();
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\settings;
/* 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;
@@ -22,6 +22,10 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _advanced extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
@@ -45,11 +49,13 @@ class _advanced extends \IPS\Dispatcher\Controller
\IPS\Request::i()->tab = isset( \IPS\Request::i()->tab ) ? \IPS\Request::i()->tab : 'settings';
if ( $pos = mb_strpos( \IPS\Request::i()->tab, '-' ) )
{
$activeTabContents = call_user_func( array( $this, '_manage' . mb_ucfirst( mb_substr( \IPS\Request::i()->tab, 0, $pos ) ) ), mb_substr( \IPS\Request::i()->tab, $pos + 1 ) );
$tabMethod = '_manage' . mb_ucfirst( mb_substr( \IPS\Request::i()->tab, 0, $pos ) );
$activeTabContents = $this->$tabMethod( mb_substr( \IPS\Request::i()->tab, $pos + 1 ) );
}
else
{
$activeTabContents = call_user_func( array( $this, '_manage' . mb_ucfirst( \IPS\Request::i()->tab ) ) );
$tabMethod = '_manage' . mb_ucfirst( \IPS\Request::i()->tab );
$activeTabContents = $this->$tabMethod();
}
/* If this is an AJAX request, just return it */
@@ -85,12 +91,19 @@ class _advanced extends \IPS\Dispatcher\Controller
{
/* Are we just checking the constants? */
if ( isset( \IPS\Request::i()->checkConstants ) )
{
{
$cacheConfig = \IPS\CACHE_CONFIG;
if ( \IPS\Request::i()->store_method === 'Redis' or \IPS\Request::i()->cache_method === 'Redis' )
{
$cacheConfig = \IPS\REDIS_CONFIG;
}
/* If we've changed anything, explain to the admin they have to update */
if ( \IPS\Request::i()->store_method !== \IPS\STORE_METHOD or \IPS\Request::i()->store_config !== \IPS\STORE_CONFIG or \IPS\Request::i()->cache_method !== \IPS\CACHE_METHOD or \IPS\Request::i()->cache_config !== \IPS\CACHE_CONFIG or \IPS\Request::i()->cache_guest_page != \IPS\CACHE_PAGE_TIMEOUT )
if ( \IPS\Request::i()->store_method !== \IPS\STORE_METHOD or \IPS\Request::i()->store_config !== \IPS\STORE_CONFIG or \IPS\Request::i()->cache_method !== \IPS\CACHE_METHOD or \IPS\Request::i()->cache_config !== $cacheConfig or \IPS\Request::i()->cache_guest_page != \IPS\CACHE_PAGE_TIMEOUT )
{
$downloadUrl = \IPS\Http\Url::internal( 'app=core&module=settings&controller=advanced&do=downloadDatastoreConstants' )->setQueryString( array( 'store_method' => \IPS\Request::i()->store_method, 'store_config' => \IPS\Request::i()->store_config, 'cache_method' => \IPS\Request::i()->cache_method, 'cache_config' => \IPS\Request::i()->cache_config, 'cache_guest_page' => \IPS\Request::i()->cache_guest_page ) );
$checkUrl = \IPS\Http\Url::internal( 'app=core&module=settings&controller=advanced&tab=datastore&checkConstants=1' )->setQueryString( array( 'store_method' => \IPS\Request::i()->store_method, 'store_config' => \IPS\Request::i()->store_config, 'cache_method' => \IPS\Request::i()->cache_method, 'cache_config' => \IPS\Request::i()->cache_config, 'cache_guest_page' => \IPS\Request::i()->cache_guest_page ) );
$checkUrl = \IPS\Http\Url::internal( 'app=core&module=settings&controller=advanced&tab=datastore&checkConstants=1' )->setQueryString( array( 'store_method' => \IPS\Request::i()->store_method, 'store_config' => \IPS\Request::i()->store_config, 'cache_method' => \IPS\Request::i()->cache_method, 'cache_config' => \IPS\Request::i()->cache_config, 'cache_guest_page' => \IPS\Request::i()->cache_guest_page ) )->csrf();
return \IPS\Theme::i()->getTemplate( 'settings' )->dataStoreChange( $downloadUrl, $checkUrl, TRUE );
}
/* Otherwise just log and redirect */
@@ -111,9 +124,10 @@ class _advanced extends \IPS\Dispatcher\Controller
/* Init */
$form = new \IPS\Helpers\Form;
$form->attributes['data-controller'] = 'core.admin.system.settings';
/* If the datastore isn't working properly, show a message */
if( !\IPS\Data\Store::i()->test() OR \IPS\Db::i()->select( 'COUNT(*)', 'core_log', array( '`category`=? AND `time`>?', 'datastore', \IPS\DateTime::create()->sub( new \DateInterval( 'PT1H' ) )->getTimestamp() ) )->first() >= 10 )
if( !\IPS\Data\Store::testStore() OR \IPS\Db::i()->select( 'COUNT(*)', 'core_log', array( '`category`=? AND `time`>?', 'datastore', \IPS\DateTime::create()->sub( new \DateInterval( 'PT1H' ) )->getTimestamp() ) )->first() >= 10 )
{
/* Have we just recently updated the configuration? If so, ignore this warning for 24 hours */
if( \IPS\Settings::i()->last_data_store_update < \IPS\DateTime::create()->sub( new \DateInterval( 'PT24H' ) )->getTimestamp() )
@@ -147,7 +161,7 @@ class _advanced extends \IPS\Dispatcher\Controller
{
if ( !$input->htmlId )
{
$input->htmlId = md5( mt_rand() );
$input->htmlId = 'id_' . $input->name;
}
$extra[] = $input;
@@ -188,7 +202,26 @@ class _advanced extends \IPS\Dispatcher\Controller
{
$options['Test'] = 'datastore_method_Test';
}
$existingConfiguration = json_decode( \IPS\CACHE_CONFIG, TRUE );
$cacheConfig = \IPS\CACHE_CONFIG;
if ( \defined( '\IPS\REDIS_CONFIG' ) and ( \IPS\STORE_METHOD == 'Redis' OR \IPS\CACHE_METHOD == 'Redis' ) )
{
$cacheConfig = \IPS\REDIS_CONFIG;
}
$existingConfiguration = json_decode( $cacheConfig, TRUE );
/* Remove deprecated options if they're not in use @todo 4.6 */
$deprecatedCacheMethods = array( 'Apc', 'Memcache', 'Wincache', 'Xcache' );
foreach( $deprecatedCacheMethods as $cache )
{
if( $cache !== \IPS\CACHE_METHOD )
{
unset( $options[ $cache ] );
}
}
foreach ( $options as $k => $v )
{
$class = 'IPS\Data\Cache\\' . $k;
@@ -203,13 +236,19 @@ class _advanced extends \IPS\Dispatcher\Controller
{
if ( !$input->htmlId )
{
$input->htmlId = md5( mt_rand() );
$input->htmlId = 'id_' . $input->name;
}
$extra[] = $input;
$toggles[ $k ][] = $input->htmlId;
$cacheConfigurationFields[ $k ][ $inputKey ] = $input->name;
}
/* Deprecated Warnings */
if( \in_array( $k, $deprecatedCacheMethods ) )
{
\IPS\Member::loggedIn()->language()->words["datastore_method_{$k}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'datastore_method_deprecated', FALSE, array( 'sprintf' => array( $k ) ) );
}
}
}
$form->add( new \IPS\Helpers\Form\Radio( 'cache_method', \IPS\CACHE_METHOD, TRUE, array(
@@ -224,7 +263,7 @@ class _advanced extends \IPS\Dispatcher\Controller
$form->add( new \IPS\Helpers\Form\YesNo( 'redis_enabled', \IPS\REDIS_ENABLED, FALSE, array(), NULL, NULL, NULL, 'redis_enabled' ) );
$form->add( new \IPS\Helpers\Form\Number( 'cache_guest_page', \IPS\CACHE_PAGE_TIMEOUT, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'disable' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('for'), \IPS\Member::loggedIn()->language()->addToStack('seconds'), 'cache_guest_page' ) );
$form->add( new \IPS\Helpers\Form\Interval( 'cache_guest_page', \IPS\CACHE_PAGE_TIMEOUT, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::SECONDS, 'unlimited' => 0, 'unlimitedLang' => 'disable' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('for'), NULL, 'cache_guest_page' ) );
/* Handle submissions */
if ( $values = $form->values() )
@@ -274,10 +313,11 @@ class _advanced extends \IPS\Dispatcher\Controller
/* Reset the last update flag for data store */
\IPS\Settings::i()->changeValues( array( 'last_data_store_update' => time() ) );
\IPS\core\AdminNotification::remove( 'core', 'ConfigurationError', 'dataStorageBroken' );
/* Display */
$downloadUrl = \IPS\Http\Url::internal( 'app=core&module=settings&controller=advanced&do=downloadDatastoreConstants' )->setQueryString( array( 'store_method' => $values['datastore_method'], 'store_config' => str_replace( '\\/', '/', json_encode( $storeConfiguration ) ), 'cache_method' => $values['cache_method'], 'cache_config' => json_encode( $cacheConfiguration ), 'cache_guest_page' => $values['cache_guest_page'], 'redis_enabled' => $values['redis_enabled'] ) );
$checkUrl = \IPS\Http\Url::internal( 'app=core&module=settings&controller=advanced&tab=datastore&checkConstants=1' )->setQueryString( array( 'store_method' => $values['datastore_method'], 'store_config' => str_replace( '\\/', '/', json_encode( $storeConfiguration ) ), 'cache_method' => $values['cache_method'], 'cache_config' => json_encode( $cacheConfiguration ), 'cache_guest_page' => $values['cache_guest_page'], 'redis_enabled' => $values['redis_enabled'] ) );
$checkUrl = \IPS\Http\Url::internal( 'app=core&module=settings&controller=advanced&tab=datastore&checkConstants=1' )->setQueryString( array( 'store_method' => $values['datastore_method'], 'store_config' => str_replace( '\\/', '/', json_encode( $storeConfiguration ) ), 'cache_method' => $values['cache_method'], 'cache_config' => json_encode( $cacheConfiguration ), 'cache_guest_page' => $values['cache_guest_page'], 'redis_enabled' => $values['redis_enabled'] ) )->csrf();
return \IPS\Theme::i()->getTemplate( 'settings' )->dataStoreChange( $downloadUrl, $checkUrl );
}
/* Otherwise just log and redirect */
@@ -287,7 +327,9 @@ class _advanced extends \IPS\Dispatcher\Controller
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=settings&controller=advanced&tab=datastore' ), 'saved' );
}
}
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js('admin_system.js', 'core', 'admin') );
return $form;
}
@@ -303,11 +345,11 @@ class _advanced extends \IPS\Dispatcher\Controller
$output = "<?php\n\n";
foreach ( \IPS\IPS::defaultConstants() as $k => $v )
{
$val = constant( 'IPS\\' . $k );
$val = \constant( 'IPS\\' . $k );
if ( $val !== $v and !in_array( $k, array( 'STORE_METHOD', 'STORE_CONFIG', 'CACHE_METHOD', 'CACHE_CONFIG', 'CACHE_PAGE_TIMEOUT', 'SUITE_UNIQUE_KEY', 'READ_WRITE_SEPARATION', 'REDIS_ENABLED', 'REPORT_EXCEPTIONS' ) ) )
if ( $val !== $v and !\in_array( $k, array( 'STORE_METHOD', 'STORE_CONFIG', 'CACHE_METHOD', 'CACHE_CONFIG', 'CACHE_PAGE_TIMEOUT', 'SUITE_UNIQUE_KEY', 'READ_WRITE_SEPARATION', 'REDIS_ENABLED', 'REDIS_CONFIG', 'REPORT_EXCEPTIONS' ) ) )
{
$output .= "define( '{$k}', " . var_export( $val, TRUE ) . " );\n";
$output .= "\\define( '{$k}', " . var_export( $val, TRUE ) . " );\n";
}
}
@@ -323,33 +365,41 @@ class _advanced extends \IPS\Dispatcher\Controller
if( isset( $matches[1] ) )
{
$output .= "define( 'READ_WRITE_SEPARATION', " . $matches[1] . " );\n";
$output .= "\\define( 'READ_WRITE_SEPARATION', " . $matches[1] . " );\n";
}
}
}
$output .= "\n";
$output .= "define( 'REDIS_ENABLED', " . var_export( (boolean) \IPS\Request::i()->redis_enabled, TRUE ) . " );\n";
$output .= "define( 'STORE_METHOD', " . var_export( \IPS\Request::i()->store_method, TRUE ) . " );\n";
$output .= "define( 'STORE_CONFIG', " . var_export( \IPS\Request::i()->store_config, TRUE ) . " );\n";
$output .= "define( 'CACHE_METHOD', " . var_export( \IPS\Request::i()->cache_method, TRUE ) . " );\n";
$output .= "define( 'CACHE_CONFIG', " . var_export( \IPS\Request::i()->cache_config, TRUE ) . " );\n";
$output .= "define( 'CACHE_PAGE_TIMEOUT', " . var_export( (int) \IPS\Request::i()->cache_guest_page, TRUE ) . " );\n";
$output .= "define( 'SUITE_UNIQUE_KEY', " . var_export( mb_substr( md5( mt_rand() ), 10, 10 ), TRUE ) . " );\n"; // Regenerate the unique key so there's no conflicts
$output .= "\\define( 'REDIS_ENABLED', " . var_export( (boolean) \IPS\Request::i()->redis_enabled, TRUE ) . " );\n";
$output .= "\\define( 'STORE_METHOD', " . var_export( \IPS\Request::i()->store_method, TRUE ) . " );\n";
$output .= "\\define( 'STORE_CONFIG', " . var_export( \IPS\Request::i()->store_config, TRUE ) . " );\n";
$output .= "\\define( 'CACHE_METHOD', " . var_export( \IPS\Request::i()->cache_method, TRUE ) . " );\n";
if ( \IPS\Request::i()->store_method === 'Redis' or \IPS\Request::i()->cache_method === 'Redis' )
{
$output .= "\\define( 'REDIS_CONFIG', " . var_export( \IPS\Request::i()->cache_config, TRUE ) . " );\n";
}
else
{
$output .= "\\define( 'CACHE_CONFIG', " . var_export( \IPS\Request::i()->cache_config, TRUE ) . " );\n";
}
$output .= "\\define( 'CACHE_PAGE_TIMEOUT', " . var_export( (int) \IPS\Request::i()->cache_guest_page, TRUE ) . " );\n";
$output .= "\\define( 'SUITE_UNIQUE_KEY', " . var_export( mb_substr( md5( mt_rand() ), 10, 10 ), TRUE ) . " );\n"; // Regenerate the unique key so there's no conflicts
$output .= "\n\n\n";
\IPS\Output::i()->sendOutput( $output, 200, 'text/x-php', array( 'Content-Disposition' => 'attachment; filename=constants.php' ) );
}
/**
* Settings
* Get setting to configure tasks
*
* @return string
* @param \IPS\Form $form Form to add the setting to
* @return void
*/
protected function _manageSettings()
public static function taskSetting( $form )
{
\IPS\Dispatcher::i()->checkAcpPermission( 'advanced_manage_server' );
/* Generate a cron key if we don't have one */
if ( !\IPS\Settings::i()->task_cron_key )
{
@@ -398,9 +448,6 @@ class _advanced extends \IPS\Dispatcher\Controller
);
}
/* Build and show form */
$form = new \IPS\Helpers\Form;
$form->addHeader('task_manager');
$form->add( new \IPS\Helpers\Form\Radio( 'task_use_cron', \IPS\CIC ? 'ips' : \IPS\Settings::i()->task_use_cron, FALSE, $options, function ( $val )
{
$cronFile = \IPS\ROOT_PATH . '/applications/core/interface/task/task.php';
@@ -409,6 +456,26 @@ class _advanced extends \IPS\Dispatcher\Controller
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack('task_use_cron_executable', FALSE, array( 'sprintf' => array( $cronFile ) ) ) );
}
}, NULL, NULL, 'task_use_cron' ) );
}
/**
* Settings
*
* @return string
*/
protected function _manageSettings()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'advanced_manage_server' );
/* Build and show form */
$form = new \IPS\Helpers\Form;
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'admin_settings.js', 'core', 'admin' ) );
$form->attributes['data-controller'] = 'core.admin.settings.advanced';
$form->hiddenValues['rebuildPosts'] = \IPS\Request::i()->rebuildPosts ?: 0;
$form->addHeader('task_manager');
static::taskSetting( $form );
$form->addHeader( 'security_header_ips' );
if ( !\IPS\CIC )
{
@@ -419,8 +486,20 @@ class _advanced extends \IPS\Dispatcher\Controller
{
\IPS\Member::loggedIn()->language()->words['match_ipaddress_warning'] = \IPS\Member::loggedIn()->language()->addToStack('ip_override_warn');
}
$form->add( new \IPS\Helpers\Form\Radio( 'clickjackprevention', \IPS\Settings::i()->clickjackprevention, FALSE, array(
'options' => array(
'xframe' => 'clickjackprevention_xframe',
'csp' => 'clickjackprevention_csp',
'none' => 'clickjackprevention_none',
),
'toggles' => array(
'csp' => array( 'csp_header' )
)
) ) );
$form->add( new \IPS\Helpers\Form\Text( 'csp_header', \IPS\Settings::i()->csp_header, FALSE, array( 'placeholder' => "default-src *; frame-ancestors 'self' *.example.com" ), NULL, NULL, NULL, 'csp_header' ) );
$form->addHeader('performance_settings');
$form->add( new \IPS\Helpers\Form\Number( 'widget_cache_ttl', ( isset( \IPS\Settings::i()->widget_cache_ttl ) ) ? \IPS\Settings::i()->widget_cache_ttl : 60, FALSE, array( 'min' => 60 ), NULL, \IPS\Member::loggedIn()->language()->addToStack('for'), \IPS\Member::loggedIn()->language()->addToStack('seconds') ) );
$form->add( new \IPS\Helpers\Form\Interval( 'widget_cache_ttl', ( isset( \IPS\Settings::i()->widget_cache_ttl ) ) ? \IPS\Settings::i()->widget_cache_ttl : 60, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::SECONDS, 'min' => 60 ), NULL, \IPS\Member::loggedIn()->language()->addToStack('for'), NULL ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'lazy_load_enabled', \IPS\Settings::i()->lazy_load_enabled, FALSE ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'auto_polling_enabled', \IPS\Settings::i()->auto_polling_enabled, FALSE ) );
if ( ! \IPS\CIC )
@@ -440,7 +519,40 @@ class _advanced extends \IPS\Dispatcher\Controller
if ( $values = $form->values() )
{
$form->saveAsSettings();
/* Run the rebuild posts routine */
if( $values['rebuildPosts'] )
{
/* Remove any existing rebuilds */
\IPS\Db::i()->delete( 'core_queue', \IPS\Db::i()->in( '`key`', array( 'RebuildLazyLoad', 'RebuildLazyLoadNonContent' ) ) );
/* Unset task datastore */
unset( \IPS\Data\Store::i()->currentLazyLoadRebuild );
$enableDisable = $values['lazy_load_enabled'] ? TRUE : FALSE;
foreach ( \IPS\Content::routedClasses( FALSE, TRUE ) as $class )
{
if( isset( $class::$databaseColumnMap['content'] ) )
{
try
{
\IPS\Task::queue( 'core', 'RebuildLazyLoad', array( 'class' => $class, 'status' => $enableDisable ), 4 );
}
catch( \OutOfRangeException $ex ) { }
}
}
foreach( \IPS\Application::allExtensions( 'core', 'EditorLocations', FALSE, NULL, NULL, TRUE, TRUE ) as $_key => $extension )
{
if( method_exists( $extension, 'rebuildLazyLoad' ) )
{
\IPS\Task::queue( 'core', 'RebuildLazyLoadNonContent', array( 'extension' => $_key, 'status' => $enableDisable ), 4 );
}
}
}
unset( $values['rebuildPosts'] );
$form->saveAsSettings( $values );
\IPS\Session::i()->log( 'acplogs__advanced_server_edited' );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=settings&controller=advanced&tab=settings' ), 'saved' );
@@ -475,7 +587,7 @@ class _advanced extends \IPS\Dispatcher\Controller
$table->quickSearch = function( $val )
{
$matches = \IPS\Member::loggedIn()->language()->searchCustom( 'task__', $val, TRUE );
if ( count( $matches ) )
if ( \count( $matches ) )
{
return array( '(' . \IPS\Db::i()->in( '`key`', array_keys( $matches ) ) . " OR `key` LIKE '%{$val}%')" );
}
@@ -552,7 +664,7 @@ class _advanced extends \IPS\Dispatcher\Controller
$return = array( 'unlock' => array(
'icon' => 'unlock',
'title' => 'task_manager_unlock',
'link' => \IPS\Http\Url::internal( "app=core&module=settings&controller=advanced&do=unlockTask&id={$row['id']}" )
'link' => \IPS\Http\Url::internal( "app=core&module=settings&controller=advanced&do=unlockTask&id={$row['id']}" )->csrf()
) );
}
else
@@ -560,7 +672,7 @@ class _advanced extends \IPS\Dispatcher\Controller
$return = array( 'run' => array(
'icon' => 'play-circle',
'title' => 'task_manager_run',
'link' => \IPS\Http\Url::internal( "app=core&module=settings&controller=advanced&do=runTask&id={$row['id']}" )
'link' => \IPS\Http\Url::internal( "app=core&module=settings&controller=advanced&do=runTask&id={$row['id']}" )->csrf()
) );
}
$return['logs'] = array(
@@ -595,7 +707,7 @@ class _advanced extends \IPS\Dispatcher\Controller
\IPS\Dispatcher::i()->checkAcpPermission( 'advanced_manage_tasks' );
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\Number( 'prune_log_tasks', \IPS\Settings::i()->prune_log_tasks, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), \IPS\Member::loggedIn()->language()->addToStack('days'), 'prune_log_tasks' ) );
$form->add( new \IPS\Helpers\Form\Interval( 'prune_log_tasks', \IPS\Settings::i()->prune_log_tasks, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL, 'prune_log_tasks' ) );
if ( $values = $form->values() )
{
@@ -616,6 +728,7 @@ class _advanced extends \IPS\Dispatcher\Controller
protected function runTask()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'advanced_manage_tasks' );
\IPS\Session::i()->csrfCheck();
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('task_manager');
@@ -635,11 +748,11 @@ class _advanced extends \IPS\Dispatcher\Controller
}
else
{
if ( is_array( $output ) )
if ( \is_array( $output ) )
{
$output = implode( "\n", array_map( array( \IPS\Member::loggedIn()->language(), 'addToStack' ), $output ) );
}
elseif ( !is_string( $output ) and !is_numeric( $output ) )
elseif ( !\is_string( $output ) and !\is_numeric( $output ) )
{
$output = var_export( $output, TRUE );
}
@@ -655,6 +768,10 @@ class _advanced extends \IPS\Dispatcher\Controller
{
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'advancedsettings' )->taskResult( FALSE, \IPS\Member::loggedIn()->language()->addToStack( $e->getMessage(), FALSE ), $task->id );
}
catch ( \RuntimeException $e )
{
\IPS\Output::i()->error( 'task_running_error', '2C124/7', 404, '', array(), \IPS\IPS::getExceptionDetails( $e ) );
}
catch ( \OutOfRangeException $e )
{
\IPS\Output::i()->error( 'task_class_not_found', '2C124/1', 404, '' );
@@ -662,7 +779,7 @@ class _advanced extends \IPS\Dispatcher\Controller
catch( \Exception $e )
{
\IPS\Log::log( $e, 'uncaught_exception' );
\IPS\Output::i()->error( $e->getMessage(), '4C124/6', 404, '' );
\IPS\Output::i()->error( $e->getMessage() ?: 'task_running_error', '4C124/6', 404, '' );
}
}
@@ -674,6 +791,7 @@ class _advanced extends \IPS\Dispatcher\Controller
protected function unlockTask()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'advanced_manage_tasks' );
\IPS\Session::i()->csrfCheck();
try
{
@@ -716,11 +834,11 @@ class _advanced extends \IPS\Dispatcher\Controller
'log' => function ( $val, $row )
{
$val = json_decode( $val );
if ( is_array( $val ) )
if ( \is_array( $val ) )
{
$val = implode( "\n", array_map( array( \IPS\Member::loggedIn()->language(), 'addToStack' ), $val ) );
}
elseif ( !is_string( $val ) and !is_numeric( $val ) )
elseif ( !\is_string( $val ) and !\is_numeric( $val ) )
{
$val = var_export( $val, TRUE );
}
@@ -766,7 +884,8 @@ class _advanced extends \IPS\Dispatcher\Controller
}
$definition = \IPS\Http\Url\Friendly::furlDefinition();
$customConfiguration = \IPS\Settings::i()->furl_configuration ? json_decode( \IPS\Settings::i()->furl_configuration, TRUE ) : array();
$table = new \IPS\Helpers\Table\Custom( $definition, \IPS\Http\Url::internal( 'app=core&module=settings&controller=advanced&tab=furl' ) );
$table->include = array( 'friendly', 'real' );
$table->limit = 100;
@@ -809,17 +928,17 @@ class _advanced extends \IPS\Dispatcher\Controller
)
);
if( \IPS\Settings::i()->furl_configuration AND $config = json_decode( \IPS\Settings::i()->furl_configuration, TRUE ) AND count( $config ) )
if( $customConfiguration AND \count( $customConfiguration ) )
{
$table->rootButtons['revert'] = array(
'icon' => 'undo',
'title' => 'furl_revert',
'link' => \IPS\Http\Url::internal( 'app=core&module=settings&controller=advanced&do=furlRevert' ),
'link' => \IPS\Http\Url::internal( 'app=core&module=settings&controller=advanced&do=furlRevert' )->csrf(),
'data' => array( 'confirm' => '' )
);
}
$table->rowButtons = function( $row, $k ) use ( $definition )
$table->rowButtons = function( $row, $k ) use ( $definition, $customConfiguration )
{
$return = array(
'edit' => array(
@@ -830,12 +949,12 @@ class _advanced extends \IPS\Dispatcher\Controller
)
);
if( isset( $definition[ $k ]['custom'] ) )
if( isset( $definition[ $k ]['custom'] ) OR isset( $customConfiguration[ $k ] ) )
{
$return['revert'] = array(
'icon' => 'undo',
'title' => 'revert',
'link' => \IPS\Http\Url::internal( "app=core&module=settings&controller=advanced&do=furlDelete&key={$k}" ),
'link' => \IPS\Http\Url::internal( "app=core&module=settings&controller=advanced&do=furlDelete&key={$k}" )->csrf(),
'data' => array( 'confirm' => '', 'confirmMessage' => \IPS\Member::loggedIn()->language()->addToStack('revert_confirm') )
);
}
@@ -882,6 +1001,7 @@ class _advanced extends \IPS\Dispatcher\Controller
$appIsDefault = FALSE;
$alias = NULL;
$verify = NULL;
$seoPagination = NULL;
$friendly = $values['furl_friendly'];
if ( \IPS\Request::i()->key )
{
@@ -894,7 +1014,12 @@ class _advanced extends \IPS\Dispatcher\Controller
{
$verify = $currentDefinition[ \IPS\Request::i()->key ]['verify'];
}
if ( isset( $currentDefinition[ \IPS\Request::i()->key ]['seoPagination'] ) )
{
$seoPagination = $currentDefinition[ \IPS\Request::i()->key ]['seoPagination'];
}
if ( isset( $currentDefinition[ \IPS\Request::i()->key ]['with_top_level'] ) )
{
$appIsDefault = TRUE;
@@ -911,8 +1036,8 @@ class _advanced extends \IPS\Dispatcher\Controller
}
}
}
$save = \IPS\Http\Url\Friendly::buildFurlDefinition( $friendly, $values['furl_real'], $appTopLevel, $appIsDefault, $alias, TRUE, $verify );
$save = \IPS\Http\Url\Friendly::buildFurlDefinition( $friendly, $values['furl_real'], $appTopLevel, $appIsDefault, $alias, TRUE, $verify, NULL, $seoPagination );
if ( \IPS\Request::i()->key )
{
@@ -955,6 +1080,7 @@ class _advanced extends \IPS\Dispatcher\Controller
protected function furlDelete()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'advanced_manage_furls' );
\IPS\Session::i()->csrfCheck();
/* Make sure the user confirmed the deletion */
\IPS\Request::i()->confirmedDelete();
@@ -983,6 +1109,7 @@ class _advanced extends \IPS\Dispatcher\Controller
protected function furlRevert()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'advanced_manage_furls' );
\IPS\Session::i()->csrfCheck();
\IPS\Settings::i()->changeValues( array( 'furl_configuration' => NULL ) );
unset( \IPS\Data\Store::i()->furl_configuration );
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\settings;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _contactus extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\settings;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _email extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -55,47 +60,53 @@ class _email extends \IPS\Dispatcher\Controller
{
$settingsForm->add( new \IPS\Helpers\Form\YesNo( 'our_picks_in_email', \IPS\Settings::i()->our_picks_in_email, FALSE, array(), NULL, NULL, NULL, 'our_picks_in_email' ) );
}
$settingsForm->addHeader( 'advanced_settings' );
$method = \IPS\Settings::i()->mail_method;
$disabled = array();
if ( \IPS\Settings::i()->sendgrid_api_key )
$settingsForm->add( new \IPS\Helpers\Form\YesNo( 'email_truncate', ( \IPS\Settings::i()->email_truncate ), FALSE, array(), NULL, NULL, NULL, 'email_truncate' ) );
$settingsForm->add( new \IPS\Helpers\Form\YesNo( 'email_log_do', ( \IPS\Settings::i()->prune_log_emailstats != 0 ), FALSE, array( 'togglesOn' => array( 'prune_log_emailstats' ) ), NULL, NULL, NULL, 'email_log_do' ) );
$settingsForm->add( new \IPS\Helpers\Form\Interval( 'prune_log_emailstats', \IPS\Settings::i()->prune_log_emailstats ?: 60, FALSE, array( 'valueAs' => 'd', 'unlimited' => '-1', 'unlimitedLang' => 'never' ), NULL, NULL, NULL, 'prune_log_emailstats' ) );
if ( !\IPS\DEMO_MODE )
{
if ( \IPS\Settings::i()->sendgrid_use_for == 2 )
$settingsForm->addHeader( 'advanced_settings' );
$method = \IPS\Settings::i()->mail_method;
$disabled = array();
if ( \IPS\Settings::i()->sendgrid_api_key )
{
$method = 'sendgrid';
if ( \IPS\Settings::i()->sendgrid_use_for == 2 )
{
$method = 'sendgrid';
}
}
else
{
$disabled[] = 'sendgrid';
}
/* We previously renamed this to 'php' */
if( $method == 'mail' )
{
$method = 'php';
}
$settingsForm->add( new \IPS\Helpers\Form\Radio( 'mail_method', $method, TRUE, array(
'options' => array( 'php' => ( \IPS\CIC ? 'mail_method_cic' : 'mail_method_php' ), 'smtp' => 'mail_method_smtp', 'sendgrid' => 'mail_method_sendgrid' ),
'disabled' => $disabled,
'toggles' => array(
'php' => array( 'php_mail_extra' ),
'smtp' => array( 'smtp_host', 'smtp_port', 'smtp_user', 'smtp_pass', 'smtp_helo', 'smtp_protocol' ),
)
) ) );
$options = array( 'plain' => 'smtp_plaintext', 'ssl' => 'smtp_ssl', 'tls' => 'smtp_tls' );
$settingsForm->add( new \IPS\Helpers\Form\Text( 'smtp_host', \IPS\Settings::i()->smtp_host, FALSE, array(), NULL, NULL, NULL, 'smtp_host' ) );
$settingsForm->add( new \IPS\Helpers\Form\Select( 'smtp_protocol', \IPS\Settings::i()->smtp_protocol, FALSE, array( 'options' => $options ), NULL, NULL, NULL, 'smtp_protocol' ) );
$settingsForm->add( new \IPS\Helpers\Form\Number( 'smtp_port', \IPS\Settings::i()->smtp_port, FALSE, array(), NULL, NULL, NULL, 'smtp_port' ) );
$settingsForm->add( new \IPS\Helpers\Form\Text( 'smtp_user', \IPS\Settings::i()->smtp_user, FALSE, array(), NULL, NULL, NULL, 'smtp_user' ) );
$settingsForm->add( new \IPS\Helpers\Form\Password( 'smtp_pass', \IPS\Settings::i()->smtp_pass, FALSE, array(), NULL, NULL, NULL, 'smtp_pass' ) );
if ( !\IPS\CIC )
{
$settingsForm->add( new \IPS\Helpers\Form\Text( 'php_mail_extra', \IPS\Settings::i()->php_mail_extra, FALSE, array(), NULL, NULL, NULL, 'php_mail_extra' ) );
}
}
else
{
$disabled[] = 'sendgrid';
}
/* We previously renamed this to 'php' */
if( $method == 'mail' )
{
$method = 'php';
}
$settingsForm->add( new \IPS\Helpers\Form\Radio( 'mail_method', $method, TRUE, array(
'options' => array( 'php' => ( \IPS\CIC ? 'mail_method_cic' : 'mail_method_php' ), 'smtp' => 'mail_method_smtp', 'sendgrid' => 'mail_method_sendgrid' ),
'disabled' => $disabled,
'toggles' => array(
'php' => array( 'php_mail_extra' ),
'smtp' => array( 'smtp_host', 'smtp_port', 'smtp_user', 'smtp_pass', 'smtp_helo', 'smtp_protocol' ),
)
) ) );
$options = array( 'plain' => 'smtp_plaintext', 'ssl' => 'smtp_ssl', 'tls' => 'smtp_tls' );
$settingsForm->add( new \IPS\Helpers\Form\Text( 'smtp_host', \IPS\Settings::i()->smtp_host, FALSE, array(), NULL, NULL, NULL, 'smtp_host' ) );
$settingsForm->add( new \IPS\Helpers\Form\Select( 'smtp_protocol', \IPS\Settings::i()->smtp_protocol, FALSE, array( 'options' => $options ), NULL, NULL, NULL, 'smtp_protocol' ) );
$settingsForm->add( new \IPS\Helpers\Form\Number( 'smtp_port', \IPS\Settings::i()->smtp_port, FALSE, array(), NULL, NULL, NULL, 'smtp_port' ) );
$settingsForm->add( new \IPS\Helpers\Form\Text( 'smtp_user', \IPS\Settings::i()->smtp_user, FALSE, array(), NULL, NULL, NULL, 'smtp_user' ) );
$settingsForm->add( new \IPS\Helpers\Form\Password( 'smtp_pass', \IPS\Settings::i()->smtp_pass, FALSE, array(), NULL, NULL, NULL, 'smtp_pass' ) );
if ( !\IPS\CIC )
{
$settingsForm->add( new \IPS\Helpers\Form\Text( 'php_mail_extra', \IPS\Settings::i()->php_mail_extra, FALSE, array(), NULL, NULL, NULL, 'php_mail_extra' ) );
}
if ( $values = $settingsForm->values() )
{
@@ -108,6 +119,13 @@ class _email extends \IPS\Dispatcher\Controller
$sendTestEmail = TRUE;
}
}
if( !$values['email_log_do'] )
{
$values['prune_log_emailstats'] = 0;
}
unset( $values['email_log_do'] );
if ( $values['mail_method'] != 'sendgrid' and \IPS\Settings::i()->sendgrid_use_for == 2 )
{
@@ -134,6 +152,9 @@ class _email extends \IPS\Dispatcher\Controller
{
$result = $email->_send( \IPS\Member::loggedIn(), array(), array(), \IPS\Settings::i()->email_out );
$messageHtml = \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->message( 'email_test_okay_auto', 'success' );
/* Sent successfully, remove notification */
\IPS\core\AdminNotification::remove( 'core', 'ConfigurationError', 'failedMail' );
}
catch ( \Exception $e )
{
@@ -165,11 +186,15 @@ class _email extends \IPS\Dispatcher\Controller
{
$email = \IPS\Email::buildFromContent( $values['subject'], $values['message'], $values['message'], \IPS\Email::TYPE_TRANSACTIONAL );
$result = $email->_send( $values['to'], array(), array(), $values['from'] );
/* Sent successfully, remove notification */
\IPS\core\AdminNotification::remove( 'core', 'ConfigurationError', 'failedMail' );
$messageHtml = \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->message( 'email_test_okay', 'success' );
}
catch ( \Exception $exception )
{
$messageHtml = \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->message( get_class( $exception ) . ": " . $exception->getMessage() . " (" . $exception->getCode() . ")", 'error' );
$messageHtml = \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->message( \get_class( $exception ) . ": " . $exception->getMessage() . " (" . $exception->getCode() . ")", 'error' );
}
}
@@ -202,13 +227,39 @@ class _email extends \IPS\Dispatcher\Controller
{
\IPS\Dispatcher::i()->checkAcpPermission( 'email_errorlog' );
/* Add a button for settings */
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'settings', 'email_errorlog_prune' ) )
{
\IPS\Output::i()->sidebar['actions'] = array(
'settings' => array(
'title' => 'prunesettings',
'icon' => 'cog',
'link' => \IPS\Http\Url::internal( 'app=core&module=settings&controller=email&do=errorLogSettings' ),
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('prunesettings') )
),
);
}
/* Display */
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('emailerrorlogs');
\IPS\Output::i()->output = (string) static::emailErrorLogTable( \IPS\Http\Url::internal( 'app=core&module=settings&controller=email&do=errorLog' ) );
}
/**
* Get email error log table
*
* @param \IPS\Http\Url $url Base URL for table
* @param array $where Where Array
* @return \IPS\Helpers\Table\Db
*/
public static function emailErrorLogTable( \IPS\Http\Url $url, array $where=array() ): \IPS\Helpers\Table\Db
{
/* Create the table */
$table = new \IPS\Helpers\Table\Db( 'core_mail_error_logs', \IPS\Http\Url::internal( 'app=core&module=settings&controller=email&do=errorLog' ) );
$table = new \IPS\Helpers\Table\Db( 'core_mail_error_logs', $url, $where );
$table->langPrefix = 'emailerrorlogs_';
/* Columns we need */
$table->include = array( 'mlog_to', 'mlog_subject', 'mlog_date', 'mlog_msg' );
$table->widths = array( 'mlog_date' => '15', 'mlog_to' => '20', 'mlog_subject' => '20', 'mlog_msg' => '35' );
$table->rowClasses = array( 'mlog_subject' => array( 'ipsTable_wrap' ), 'mlog_msg' => array( 'ipsTable_wrap' ) );
$table->sortBy = $table->sortBy ?: 'mlog_date';
@@ -275,28 +326,14 @@ class _email extends \IPS\Dispatcher\Controller
$return['resend'] = array(
'icon' => 'refresh',
'title' => 'resend_email_error',
'link' => \IPS\Http\Url::internal( 'app=core&module=settings&controller=email&do=errorLogResend&id=' ) . $row['mlog_id'],
'link' => \IPS\Http\Url::internal( 'app=core&module=settings&controller=email&do=errorLogResend&id=' . $row['mlog_id'] )->csrf(),
);
return $return;
};
/* Add a button for settings */
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'settings', 'email_errorlog_prune' ) )
{
\IPS\Output::i()->sidebar['actions'] = array(
'settings' => array(
'title' => 'prunesettings',
'icon' => 'cog',
'link' => \IPS\Http\Url::internal( 'app=core&module=settings&controller=email&do=errorLogSettings' ),
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('prunesettings') )
),
);
}
/* Display */
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('emailerrorlogs');
\IPS\Output::i()->output = (string) $table;
/* Return */
return $table;
}
/**
@@ -307,6 +344,7 @@ class _email extends \IPS\Dispatcher\Controller
public function errorLogResend()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'email_errorlog' );
\IPS\Session::i()->csrfCheck();
$id = (int) \IPS\Request::i()->id;
$log = \IPS\Db::i()->select( '*', 'core_mail_error_logs', array( 'mlog_id=?', $id ) )->first();
@@ -342,6 +380,12 @@ class _email extends \IPS\Dispatcher\Controller
\IPS\Db::i()->delete( 'core_mail_error_logs', array( 'mlog_id=?', $id ) );
/* Remove notification since this was re-sent with success and it is below the recent threshold */
if( \IPS\Email::countFailedMail( \IPS\DateTime::create()->sub( new \DateInterval( 'P3D' ), FALSE ), TRUE ) <= 3 )
{
\IPS\core\AdminNotification::remove( 'core', 'ConfigurationError', 'failedMail' );
}
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=settings&controller=email&do=errorLog" ), 'emailerror_resent' );
}
@@ -370,7 +414,7 @@ class _email extends \IPS\Dispatcher\Controller
\IPS\Dispatcher::i()->checkAcpPermission( 'email_errorlog_prune' );
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\Number( 'prune_log_email_error', \IPS\Settings::i()->prune_log_email_error, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), \IPS\Member::loggedIn()->language()->addToStack('days'), 'prune_log_email_error' ) );
$form->add( new \IPS\Helpers\Form\Interval( 'prune_log_email_error', \IPS\Settings::i()->prune_log_email_error, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL, 'prune_log_email_error' ) );
if ( $values = $form->values() )
{
@@ -382,4 +426,4 @@ class _email extends \IPS\Dispatcher\Controller
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('emailerrorlogssettings');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('global')->block( 'emailerrorlogssettings', $form, FALSE );
}
}
}
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\settings;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _general extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -42,6 +47,7 @@ class _general extends \IPS\Dispatcher\Controller
{
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\Text( 'board_name', \IPS\Settings::i()->board_name, TRUE ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'site_online', \IPS\Settings::i()->site_online, FALSE, array(
'togglesOff' => array( 'site_offline_message_id' ),
) ) );
@@ -50,7 +56,6 @@ class _general extends \IPS\Dispatcher\Controller
$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 \IPS\Helpers\Form\Text( 'site_twitter_id', \IPS\Settings::i()->site_twitter_id, FALSE, array( 'placeholder' => \IPS\Member::loggedIn()->language()->addToStack('site_twitter_id_placeholder'), 'size' => 20 ) ) );
$form->add( new \IPS\Helpers\Form\Translatable( 'copyright_line', NULL, FALSE, array( 'app' => 'core', 'key' => 'copyright_line_value', 'placeholder' => \IPS\Member::loggedIn()->language()->addToStack('copyright_line_placeholder') ) ) );
$form->add( new \IPS\Helpers\Form\Stack( 'upgrade_email', explode( ',', \IPS\Settings::i()->upgrade_email ), FALSE, array( 'stackFieldType' => 'Email', 'maxItems' => 5 ) ) );
/* $form->add( new \IPS\Helpers\Form\CheckboxSet( 'diagnostics_reporting', array( 'usage' => ( (bool) \IPS\Settings::i()->usage_reporting ), 'diagnostics' => ( (bool) \IPS\Settings::i()->diagnostics_reporting ) ), FALSE, array( 'options' => array(
'usage' => 'diagnostics_reporting_usage',
'diagnostics' => 'diagnostics_reporting_diagnostics',
@@ -69,19 +74,30 @@ class _general extends \IPS\Dispatcher\Controller
} ) );
$values['site_address'] = json_encode( $values['site_address'] );
$values['upgrade_email'] = implode( ',', $values['upgrade_email'] );
// $values['usage_reporting'] = in_array( 'usage', $values['diagnostics_reporting'] );
// $values['diagnostics_reporting'] = in_array( 'diagnostics', $values['diagnostics_reporting'] );
// \IPS\Db::i()->update( 'core_tasks', array( 'enabled' => intval( $values['usage_reporting'] ) ), array( '`key`=?', 'usagereporting' ) );
//$values['usage_reporting'] = \in_array( 'usage', $values['diagnostics_reporting'] );
//$values['diagnostics_reporting'] = \in_array( 'diagnostics', $values['diagnostics_reporting'] );
//\IPS\Db::i()->update( 'core_tasks', array( 'enabled' => \intval( $values['usage_reporting'] ) ), array( '`key`=?', 'usagereporting' ) );
$form->saveAsSettings( $values );
if ( $values['site_online'] )
{
\IPS\core\AdminNotification::remove( 'core', 'ConfigurationError', 'siteOffline' );
}
else
{
\IPS\core\AdminNotification::send( 'core', 'ConfigurationError', 'siteOffline', FALSE, NULL, \IPS\Member::loggedIn() );
}
/* Clear guest page caches */
\IPS\Data\Cache::i()->clearAll();
/* Clear manifest and ie browser data stores */
unset( \IPS\Data\Store::i()->manifest, \IPS\Data\Store::i()->iebrowserconfig );
\IPS\Session::i()->log( 'acplogs__general_settings' );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=settings&controller=general' ), 'saved' );
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_settings_general');
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\settings;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _licensekey extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* @brief Data about the license key from the store
*/
@@ -59,7 +64,7 @@ class _licensekey extends \IPS\Dispatcher\Controller
\IPS\Output::i()->sidebar['actions'] = array(
'refresh' => array(
'icon' => 'refresh',
'link' => \IPS\Http\Url::internal( 'app=core&module=settings&controller=licensekey&do=refresh' ),
'link' => \IPS\Http\Url::internal( 'app=core&module=settings&controller=licensekey&do=refresh' )->csrf(),
'title' => 'license_refresh',
),
'remove' => array(
@@ -89,6 +94,8 @@ class _licensekey extends \IPS\Dispatcher\Controller
*/
protected function refresh()
{
\IPS\Session::i()->csrfCheck();
/* Fetch the license key data and update our local storage */
unset( \IPS\Data\Store::i()->license_data );
@@ -142,6 +149,8 @@ class _licensekey extends \IPS\Dispatcher\Controller
/* Refresh the locally stored license info */
unset( \IPS\Data\Store::i()->license_data );
\IPS\core\AdminNotification::remove( 'core', 'License', 'missing' );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=settings&controller=licensekey' ), 'saved' );
}
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\settings;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _login extends \IPS\Node\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Node Class
*/
@@ -129,7 +134,7 @@ class _login extends \IPS\Node\Controller
'forcelogout' => array(
'title' => 'force_all_logout',
'icon' => 'lock',
'link' => \IPS\Http\Url::internal( 'app=core&module=settings&controller=login&do=forceLogout' ),
'link' => \IPS\Http\Url::internal( 'app=core&module=settings&controller=login&do=forceLogout' )->csrf(),
'data' => array( 'confirm' => '' ),
),
);
@@ -150,7 +155,7 @@ class _login extends \IPS\Node\Controller
}
else
{
\IPS\Output::i()->output = new \IPS\Helpers\Wizard( array(
\IPS\Output::i()->output = (string) new \IPS\Helpers\Wizard( array(
'login_handler' => function( $data )
{
$options = array();
@@ -216,6 +221,8 @@ class _login extends \IPS\Node\Controller
*/
protected function enableToggle()
{
\IPS\Session::i()->csrfCheck();
$loginMethod = \IPS\Login\Handler::load( \IPS\Request::i()->id );
if ( \IPS\Request::i()->status )
@@ -292,17 +299,14 @@ class _login extends \IPS\Node\Controller
$form = new \IPS\Helpers\Form();
$form->addHeader( 'ipb_bruteforce_attempts' );
$form->add( new \IPS\Helpers\Form\Number( 'ipb_bruteforce_attempts', \IPS\Settings::i()->ipb_bruteforce_attempts, FALSE, array( 'min' => 0, 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), \IPS\Member::loggedIn()->language()->addToStack('failed_logins'), 'ipb_bruteforce_attempts' ) );
$form->add( new \IPS\Helpers\Form\Number( 'ipb_bruteforce_period', \IPS\Settings::i()->ipb_bruteforce_period, FALSE, array( 'min' => 0, 'max' => 10000, 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), \IPS\Member::loggedIn()->language()->addToStack('minutes'), 'ipb_bruteforce_period' ) );
$form->add( new \IPS\Helpers\Form\Interval( 'ipb_bruteforce_period', \IPS\Settings::i()->ipb_bruteforce_period, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::MINUTES, 'min' => 0, 'max' => 10000, 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL, 'ipb_bruteforce_period' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'ipb_bruteforce_unlock', \IPS\Settings::i()->ipb_bruteforce_unlock, FALSE, array(), NULL, NULL, NULL, 'ipb_bruteforce_unlock' ) );
$form->addHeader( 'login_settings' );
$form->add( new \IPS\Helpers\Form\YesNo( 'new_device_email', \IPS\Settings::i()->new_device_email, FALSE ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'disable_anonymous', !\IPS\Settings::i()->disable_anonymous, FALSE, array(), NULL, NULL, NULL, 'disable_anonymous' ) );
/* Save */
if ( $values = $form->values() )
{
$values['disable_anonymous'] = $values['disable_anonymous'] ? FALSE : TRUE;
/* unlock all locked members if we have disabled the locking */
if ( \IPS\Settings::i()->ipb_bruteforce_attempts > 0 AND $values['ipb_bruteforce_attempts'] == 0 )
{
@@ -399,27 +403,23 @@ class _login extends \IPS\Node\Controller
'options' => array( 'user' => 'reg_auth_type_user', 'admin' => 'reg_auth_type_admin', 'admin_user' => 'reg_auth_type_admin_user', 'none' => 'reg_auth_type_none' ),
'toggles' => array( 'user' => array( 'validate_day_prune' ), 'admin_user' => array( 'validate_day_prune' ) )
), NULL, NULL, NULL, 'reg_auth_type' ) );
$form->add( new \IPS\Helpers\Form\Number( 'validate_day_prune', \IPS\Settings::i()->validate_day_prune, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), \IPS\Member::loggedIn()->language()->addToStack('days'), 'validate_day_prune' ) );
$form->add( new \IPS\Helpers\Form\Interval( 'validate_day_prune', \IPS\Settings::i()->validate_day_prune, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL, 'validate_day_prune' ) );
$form->add( new \IPS\Helpers\Form\Stack( 'allowed_reg_email', explode( ',', \IPS\Settings::i()->allowed_reg_email ), FALSE, array( 'placeholder' => 'mycompany.com' ), function( $value ) {
if( isset( $value[0] ) AND mb_stripos( $value[0], '@' ) )
{
throw new \DomainException( 'allowed_reg_email_email_detected' );
}
}, NULL, NULL ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'new_reg_notify', \IPS\Settings::i()->new_reg_notify, FALSE, array(), NULL, NULL, NULL, 'new_reg_notify' ) );
$form->add( new \IPS\Helpers\Form\Radio( 'force_reg_terms', \IPS\Settings::i()->force_reg_terms, FALSE, array( 'options' => array(
'0' => 'force_reg_terms_0',
'1' => 'force_reg_terms_1',
) ) ) );
$form->add( new \IPS\Helpers\Form\Radio( 'updates_consent_default', \IPS\Settings::i()->updates_consent_default, FALSE, array(
'options' => array( 'enabled' => 'updates_consent_enabled', 'disabled' => 'updates_consent_disabled' ),
), NULL, NULL, NULL, 'updates_consent_default' ) );
/* Save */
if ( $values = $form->values() )
{
/* Save */
if ( isset( $values['allowed_reg_email'] ) and is_array( $values['allowed_reg_email'] ) )
if ( isset( $values['allowed_reg_email'] ) and \is_array( $values['allowed_reg_email'] ) )
{
$values['allowed_reg_email'] = implode( ',', $values['allowed_reg_email'] );
}
@@ -535,6 +535,8 @@ class _login extends \IPS\Node\Controller
*/
protected function forceLogout()
{
\IPS\Session::i()->csrfCheck();
\IPS\Db::i()->update( 'core_members_known_devices', array( 'login_key' => NULL ) );
\IPS\Db::i()->delete( 'core_sessions' );
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\settings;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _mfa extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -122,6 +127,8 @@ class _mfa extends \IPS\Dispatcher\Controller
*/
protected function enableToggle()
{
\IPS\Session::i()->csrfCheck();
$key = \IPS\Request::i()->id;
$handlers = \IPS\MFA\MFAHandler::handlers();
if ( !isset( $handlers[ $key ] ) )
@@ -217,7 +224,7 @@ class _mfa extends \IPS\Dispatcher\Controller
$form->addHeader('mfa_header_authentication');
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'security_questions_areas', \IPS\Settings::i()->security_questions_areas ? explode( ',', \IPS\Settings::i()->security_questions_areas ) : array_keys( \IPS\MFA\MFAHandler::areas() ), FALSE, array( 'options' => \IPS\MFA\MFAHandler::areas() ), NULL, NULL, NULL, 'security_questions_areas' ) );
$form->add( new \IPS\Helpers\Form\Number( 'security_questions_timer', \IPS\Settings::i()->security_questions_timer, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'security_questions_timer_session' ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('minutes') ) );
$form->add( new \IPS\Helpers\Form\Interval( 'security_questions_timer', \IPS\Settings::i()->security_questions_timer, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::MINUTES, 'unlimited' => 0, 'unlimitedLang' => 'security_questions_timer_session' ) ) );
$form->addHeader('mfa_header_recovery');
$form->add( new \IPS\Helpers\Form\Number( 'security_questions_tries', \IPS\Settings::i()->security_questions_tries, FALSE, array( 'min' => 1 ) ) );
@@ -231,7 +238,7 @@ class _mfa extends \IPS\Dispatcher\Controller
'lock' => array( 'mfa_lockout_time' )
)
) ) );
$form->add( new \IPS\Helpers\Form\Number( 'mfa_lockout_time', \IPS\Settings::i()->mfa_lockout_time, FALSE, array( 'min' => 1 ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('minutes'), 'mfa_lockout_time' ) );
$form->add( new \IPS\Helpers\Form\Interval( 'mfa_lockout_time', \IPS\Settings::i()->mfa_lockout_time, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::MINUTES, 'min' => 1 ), NULL, NULL, NULL, 'mfa_lockout_time' ) );
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'mfa_forgot_behaviour', explode( ',', \IPS\Settings::i()->mfa_forgot_behaviour ), FALSE, array(
'options' => array(
'email' => 'mfa_forgot_behaviour_email',
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\settings;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _posting extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -37,7 +42,7 @@ class _posting extends \IPS\Dispatcher\Controller
}
if( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'settings', 'posting_manage_acronym' ) )
{
$this->tabs['acronymExpansion'] = 'acronym_expansion';
$this->tabs['acronymExpansion'] = 'word_expansion';
}
if( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'settings', 'posting_manage_polls' ) )
{
@@ -79,7 +84,8 @@ class _posting extends \IPS\Dispatcher\Controller
protected function manage()
{
/* Work out output */
$activeTabContents = call_user_func( array( $this, '_manage' . mb_ucfirst( $this->activeTab ) ) );
$methodFunction = '_manage' . mb_ucfirst( $this->activeTab );
$activeTabContents = $this->$methodFunction();
/* If this is an AJAX request, just return it */
if( \IPS\Request::i()->isAjax() )
@@ -106,7 +112,6 @@ class _posting extends \IPS\Dispatcher\Controller
$form = new \IPS\Helpers\Form;
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'admin_settings.js', 'core', 'admin' ) );
$form->attributes['data-controller'] = 'core.admin.settings.posting';
$form->hiddenValues['rebuildImageProxy'] = \IPS\Request::i()->rebuildImageProxy ?: 0;
$form->addHeader('posting_attachments');
$form->add( new \IPS\Helpers\Form\Radio( 'attach_allowed_types', \IPS\Settings::i()->attach_allowed_types, FALSE, array(
'options' => array( 'all' => 'attach_allowed_types_all', 'media' => 'attach_allowed_types_media', 'images' => 'attach_allowed_types_images', 'none' => 'attach_allowed_types_none' ),
@@ -117,16 +122,23 @@ class _posting extends \IPS\Dispatcher\Controller
$form->add( new \IPS\Helpers\Form\WidthHeight( 'attachment_resample_size', \IPS\Settings::i()->attachment_resample_size ? explode( 'x', \IPS\Settings::i()->attachment_resample_size ) : array( 0, 0 ), FALSE, array( 'resizableDiv' => FALSE, 'unlimited' => array( 0, 0 ) ), NULL, NULL, NULL, 'attachment_resample_size' ) );
$current = ( isset( \IPS\Settings::i()->attachment_image_size ) ) ? explode( 'x', \IPS\Settings::i()->attachment_image_size ) : array( 1000, 750 );
$form->add( new \IPS\Helpers\Form\WidthHeight( 'attachment_image_size', $current, FALSE, array( 'resizableDiv' => FALSE ), NULL, NULL, NULL, 'attachment_image_size' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'editor_embeds', \IPS\Settings::i()->editor_embeds, FALSE, array( 'togglesOn' => array( 'max_video_width' ) ) ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'editor_embeds', \IPS\Settings::i()->editor_embeds, FALSE, array( 'togglesOn' => array( 'max_video_width', 'max_internalembed_width' ) ) ) );
$form->add( new \IPS\Helpers\Form\Number( 'max_video_width', \IPS\Settings::i()->max_video_width, FALSE, array( 'unlimited' => 0 ), NULL, NULL, 'px', 'max_video_width' ) );
$form->add( new \IPS\Helpers\Form\Number( 'max_internalembed_width', \IPS\Settings::i()->max_internalembed_width, FALSE, array( 'unlimited' => 0 ), NULL, NULL, 'px', 'max_internalembed_width' ) );
$form->addHeader('remote_images');
$form->add( new \IPS\Helpers\Form\YesNo( 'allow_remote_images', \IPS\Settings::i()->allow_remote_images, FALSE, array( 'togglesOn' => array( 'remote_image_proxy' ) ) ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'remote_image_proxy', \IPS\Settings::i()->remote_image_proxy, FALSE, array( 'togglesOn' => array( 'remote_image_proxy_cache' ) ), NULL, NULL, NULL, 'remote_image_proxy' ) );
$form->add( new \IPS\Helpers\Form\Number( 'image_proxy_cache_period', \IPS\Settings::i()->image_proxy_cache_period, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'indefinitely' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('for'), \IPS\Member::loggedIn()->language()->addToStack('days'), 'remote_image_proxy_cache' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'allow_remote_images', \IPS\Settings::i()->allow_remote_images, FALSE, array( 'togglesOn' => array( 'allow_only_https_remote_images' ) ) ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'allow_only_https_remote_images', \IPS\Settings::i()->allow_only_https_remote_images, FALSE, array(), NULL, NULL, NULL, 'allow_only_https_remote_images' ) );
$form->addHeader('posting_content');
if ( \IPS\Login::registrationType() != 'disabled' )
{
$form->add( new \IPS\Helpers\Form\Radio( 'post_before_registering', \IPS\Settings::i()->post_before_registering, FALSE, array( 'options' => array( 1 => 'post_before_registering_on', 0 => 'post_before_registering_off' ) ), NULL, NULL, NULL, 'post_before_registering' ) );
}
\IPS\Member::loggedIn()->language()->words['post_before_registering_on'] = sprintf( \IPS\Member::loggedIn()->language()->get('post_before_registering_on'), \IPS\Member::loggedIn()->language()->addToStack( 'core_group_' . \IPS\Settings::i()->member_group ) );
$form->add( new \IPS\Helpers\Form\Number( 'max_title_length', \IPS\Settings::i()->max_title_length, FALSE, array( 'max' => 255 ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('max_title_length_suffix') ) );
$form->add( new \IPS\Helpers\Form\Number( 'merge_concurrent_posts', \IPS\Settings::i()->merge_concurrent_posts, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('minutes') ) );
$form->add( new \IPS\Helpers\Form\Number( 'flood_control', \IPS\Settings::i()->flood_control, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'none' ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('seconds') ) );
$form->add( new \IPS\Helpers\Form\Interval( 'merge_concurrent_posts', \intval( \IPS\Settings::i()->merge_concurrent_posts ), FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::MINUTES, 'unlimited' => 0, 'unlimitedLang' => 'never' ) ) );
$form->add( new \IPS\Helpers\Form\Interval( 'flood_control', \IPS\Settings::i()->flood_control, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::SECONDS, 'unlimited' => 0, 'unlimitedLang' => 'none' ) ) );
$form->addHeader('edit_log');
$form->add( new \IPS\Helpers\Form\Radio( 'edit_log', \IPS\Settings::i()->edit_log, FALSE, array(
'options' => array(
@@ -139,10 +151,10 @@ class _posting extends \IPS\Dispatcher\Controller
)
) ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'edit_log_public', \IPS\Settings::i()->edit_log_public, FALSE, array(), NULL, NULL, NULL, 'edit_log_public' ) );
$form->add( new \IPS\Helpers\Form\Number( 'edit_log_prune', \IPS\Settings::i()->edit_log_prune, FALSE, array( 'unlimited' => -1, 'unlimitedLang' => 'never' ), function( $value ){ if( $value < 1 AND $value != -1 ) { throw new \InvalidArgumentException('form_required'); } }, NULL, \IPS\Member::loggedIn()->language()->addToStack('days'), 'edit_log_prune' ) );
$form->add( new \IPS\Helpers\Form\Interval( 'edit_log_prune', \IPS\Settings::i()->edit_log_prune, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => -1, 'unlimitedLang' => 'never' ), function( $value ){ if( $value < 1 AND $value != -1 ) { throw new \InvalidArgumentException('form_required'); } }, NULL, NULL, 'edit_log_prune' ) );
$form->addHeader('posting_items');
$form->add( new \IPS\Helpers\Form\Number( 'topic_redirect_prune', \IPS\Settings::i()->topic_redirect_prune, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), \IPS\Member::loggedIn()->language()->addToStack('days') ) );
$form->add( new \IPS\Helpers\Form\Interval( 'topic_redirect_prune', \IPS\Settings::i()->topic_redirect_prune, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL ) );
$hasReviewableApps = false;
@@ -172,61 +184,23 @@ class _posting extends \IPS\Dispatcher\Controller
$form->add( new \IPS\Helpers\Form\Number( 'dellog_retention_period', \IPS\Settings::i()->dellog_retention_period, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'immediately', 'max' => PHP_INT_MAX ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('dellog_days_after_deleted') ) );
$form->add( new \IPS\Helpers\Form\Number( 'ip_address_prune', \IPS\Settings::i()->ip_address_prune, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'never', 'max' => PHP_INT_MAX ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('days') ) );
$form->add( new \IPS\Helpers\Form\Interval( 'ip_address_prune', \IPS\Settings::i()->ip_address_prune, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'never', 'max' => PHP_INT_MAX ) ) );
/* Save values */
if ( $values = $form->values() )
{
\IPS\Db::i()->update( 'core_tasks', array( 'enabled' => (int) $values['allow_remote_images'] ), array( '`key`=?', 'imageproxy' ) );
{
\IPS\Db::i()->update( 'core_tasks', array( 'enabled' => (int) $values['post_before_registering'] ), array( '`key`=?', 'postBeforeRegisterFollowup' ) );
$clearCss = ( $values['max_video_width'] !== \IPS\Settings::i()->max_video_width );
$values['attachment_resample_size'] = implode( 'x', $values['attachment_resample_size'] );
$values['attachment_image_size'] = implode( 'x', $values['attachment_image_size'] );
$values['attach_allowed_extensions'] = is_array( $values['attach_allowed_extensions'] ) ? implode( ',', array_unique( array_map( function( $val )
$values['attach_allowed_extensions'] = \is_array( $values['attach_allowed_extensions'] ) ? implode( ',', array_unique( array_map( function( $val )
{
return ltrim( $val, '.' );
}, $values['attach_allowed_extensions'] ) ) ) : '';
$values['max_video_width_css'] = ( $values['max_video_width'] ) ? $values['max_video_width'] . 'px' : 'none';
/* Run the image proxy rebuild tasks */
if( $values['rebuildImageProxy'] )
{
/* Remove any existing rebuilds */
\IPS\Db::i()->delete( 'core_queue', \IPS\Db::i()->in( '`key`', array( 'RebuildImageProxy', 'RebuildImageProxyNonContent', 'DeleteImageProxyFiles' ) ) );
/* Unset task datastore */
unset( \IPS\Data\Store::i()->currentImageProxyRebuild );
$enableDisable = $values['remote_image_proxy'] ? TRUE : FALSE;
foreach ( \IPS\Content::routedClasses( FALSE, TRUE ) as $class )
{
if( isset( $class::$databaseColumnMap['content'] ) )
{
try
{
\IPS\Task::queue( 'core', 'RebuildImageProxy', array( 'class' => $class, 'status' => $enableDisable ), 4 );
}
catch( \OutOfRangeException $ex ) { }
}
}
foreach( \IPS\Application::allExtensions( 'core', 'EditorLocations', FALSE, NULL, NULL, TRUE, TRUE ) as $_key => $extension )
{
if( method_exists( $extension, 'rebuildImageProxy' ) )
{
\IPS\Task::queue( 'core', 'RebuildImageProxyNonContent', array( 'extension' => $_key, 'status' => $enableDisable ), 4 );
}
}
/* If we're disabling the image proxy, also delete the files */
if( !$enableDisable )
{
\IPS\Task::queue( 'core', 'DeleteImageProxyFiles', array( 'status' => $enableDisable ), 5 );
}
}
unset( $values['rebuildImageProxy'] );
$values['max_internalembed_width_css'] = ( $values['max_internalembed_width'] ) ? $values['max_internalembed_width'] . 'px' : 'none';
$form->saveAsSettings( $values );
@@ -261,7 +235,7 @@ class _posting extends \IPS\Dispatcher\Controller
/* So people don't set up filters, test with their admin account and think it's not working */
$groupsThatBypass = new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_groups', array( 'g_bypass_badwords=1' ) ), 'IPS\Member\Group' );
if ( count( $groupsThatBypass ) )
if ( \count( $groupsThatBypass ) )
{
$names = array();
foreach ( $groupsThatBypass as $group )
@@ -336,7 +310,18 @@ class _posting extends \IPS\Dispatcher\Controller
{
$values['ipb_url_whitelist'] = implode( ",", $values['ipb_url_whitelist'] );
$values['ipb_url_blacklist'] = implode( ",", $values['ipb_url_blacklist'] );
$values['posts_add_nofollow_exclude'] = json_encode( $values['posts_add_nofollow_exclude'] );
$noFollowExclude = array();
if( \is_array( $values['posts_add_nofollow_exclude'] ) )
{
foreach( $values['posts_add_nofollow_exclude'] as $url )
{
$noFollowExclude[] = preg_replace( "/^http(s)?:\/\//", '', $url );
}
}
$values['posts_add_nofollow_exclude'] = json_encode( $noFollowExclude );
$form->saveAsSettings( $values );
/* Clear guest page caches */
@@ -351,7 +336,7 @@ class _posting extends \IPS\Dispatcher\Controller
}
/**
* Acronym expansion
* Word expansion
*
* @return string HTML to display
*/
@@ -361,7 +346,7 @@ class _posting extends \IPS\Dispatcher\Controller
/* Create the table */
$table = new \IPS\Helpers\Table\Db( 'core_acronyms', \IPS\Http\Url::internal( 'app=core&module=settings&controller=posting&tab=acronymExpansion' ) );
$table->langPrefix = 'acronym_';
$table->langPrefix = 'word_';
$table->mainColumn = 'a_short';
$table->rowClasses = array( 'a_long' => array( 'ipsTable_wrap' ) );
@@ -387,9 +372,9 @@ class _posting extends \IPS\Dispatcher\Controller
$table->rootButtons = array(
'add' => array(
'icon' => 'plus',
'title' => 'acronym_add',
'title' => 'word_add',
'link' => \IPS\Http\Url::internal( 'app=core&module=settings&controller=posting&do=acronym' ),
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('acronym_add') )
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('word_add') )
)
);
@@ -433,7 +418,7 @@ class _posting extends \IPS\Dispatcher\Controller
$form->add( new \IPS\Helpers\Form\YesNo( 'ipb_poll_only', \IPS\Settings::i()->ipb_poll_only ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'allow_poll_creation_after', ( \IPS\Settings::i()->startpoll_cutoff ? TRUE : FALSE ), FALSE, array( 'togglesOn' => array( 'startpoll_cutoff' ) ) ) );
$form->add( new \IPS\Helpers\Form\Number( 'startpoll_cutoff', \IPS\Settings::i()->startpoll_cutoff, FALSE, array( 'unlimited' => -1, 'unlimitedLang' => 'always', 'max' => 43800 ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('hours'), 'startpoll_cutoff' ) ); #max is roughly 5 years
$form->add( new \IPS\Helpers\Form\Interval( 'startpoll_cutoff', \IPS\Settings::i()->startpoll_cutoff, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::HOURS, 'unlimited' => -1, 'unlimitedLang' => 'always', 'max' => 43800 ), NULL, NULL, NULL, 'startpoll_cutoff' ) ); #max is roughly 5 years
$form->addHeader('poll_voting');
$form->add( new \IPS\Helpers\Form\YesNo( 'allow_creator_vote', \IPS\Settings::i()->allow_creator_vote ) );
@@ -458,7 +443,7 @@ class _posting extends \IPS\Dispatcher\Controller
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=settings&controller=posting&tab=polls' ), 'saved' );
}
return \IPS\Theme::i()->getTemplate( 'forms' )->blurb( 'polls_blurb' ) . $form;
return \IPS\Theme::i()->getTemplate( 'forms' )->blurb( 'polls_blurb', TRUE, TRUE ) . $form;
}
/**
@@ -536,7 +521,7 @@ class _posting extends \IPS\Dispatcher\Controller
move_uploaded_file( $values['profanity_upload'], $tempFile );
/* Initate a redirector */
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=settings&controller=posting&do=importProfanity' )->setQueryString( array( 'file' => $tempFile, 'key' => md5_file( $tempFile ) ) ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=settings&controller=posting&do=importProfanity' )->setQueryString( array( 'file' => $tempFile, 'key' => md5_file( $tempFile ) ) )->csrf() );
}
/* Normal */
@@ -631,17 +616,19 @@ class _posting extends \IPS\Dispatcher\Controller
*/
protected function importProfanity()
{
\IPS\Session::i()->csrfCheck();
if ( !file_exists( \IPS\Request::i()->file ) or md5_file( \IPS\Request::i()->file ) !== \IPS\Request::i()->key )
{
\IPS\Output::i()->error( 'generic_error', '3C256/1', 500, '' );
}
$url = \IPS\Http\Url::internal( 'app=core&module=settings&controller=posting&do=importProfanity' )->setQueryString( array( 'file' => \IPS\Request::i()->file, 'key' => \IPS\Request::i()->key ) );
$url = \IPS\Http\Url::internal( 'app=core&module=settings&controller=posting&do=importProfanity' )->setQueryString( array( 'file' => \IPS\Request::i()->file, 'key' => \IPS\Request::i()->key ) )->csrf();
\IPS\Output::i()->output = new \IPS\Helpers\MultipleRedirect(
$url,
function( $data )
{
$data = intval( $data );
$data = \intval( $data );
/* Open XML file */
$xml = new \IPS\Xml\XMLReader;
@@ -733,7 +720,7 @@ class _posting extends \IPS\Dispatcher\Controller
}
/* Display */
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('acronym_a_short');
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('word_expansion');
\IPS\Output::i()->breadcrumb[] = array( NULL, \IPS\Output::i()->title );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->block( \IPS\Output::i()->title, $form, FALSE );
}
@@ -787,6 +774,8 @@ class _posting extends \IPS\Dispatcher\Controller
*/
public function rebuildThumbnails()
{
\IPS\Session::i()->csrfCheck();
\IPS\Task::queue( 'core', 'RebuildAttachmentThumbnails', array(), 4 );
/* And redirect */
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\settings;
/* 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;
@@ -22,11 +22,21 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _securityquestions extends \IPS\Node\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Node Class
*/
protected $nodeClass = '\IPS\MFA\SecurityQuestions\Question';
/**
* Show the "add" button in the page root rather than the table root
*/
protected $_addButtonInRoot = FALSE;
/**
* Form
*
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\settings;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _terms extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -41,6 +46,76 @@ class _terms extends \IPS\Dispatcher\Controller
protected function manage()
{
$form = new \IPS\Helpers\Form;
static::buildForm( $form );
if ( $values = $form->values() )
{
/* What were our previous values? */
$existingPrivacyPolicy = iterator_to_array( \IPS\Db::i()->select( 'word_custom', 'core_sys_lang_words', array( 'word_key=?', 'privacy_text_value' ) ) );
$existingRegistrationTerms = iterator_to_array( \IPS\Db::i()->select( 'word_custom', 'core_sys_lang_words', array( 'word_key=?', 'reg_rules_value' ) ) );
/* Save */
$values = static::processForm( $values );
$form->saveAsSettings( $values );
/* Clear guest page caches */
\IPS\Data\Cache::i()->clearAll();
/* Log */
\IPS\Session::i()->log( 'acplogs__terms_edited' );
/* Do we need to ask the admin if they want to ask members to reconfirm? */
$changedPrivacyPolicy = $existingPrivacyPolicy != iterator_to_array( \IPS\Db::i()->select( 'word_custom', 'core_sys_lang_words', array( 'word_key=?', 'privacy_text_value' ) ) );
$changedRegistrationTerms = $existingRegistrationTerms != iterator_to_array( \IPS\Db::i()->select( 'word_custom', 'core_sys_lang_words', array( 'word_key=?', 'reg_rules_value' ) ) );
if ( $changedPrivacyPolicy or $changedRegistrationTerms )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal('app=core&module=settings&controller=terms&do=reconfirm')->setQueryString( array(
'privacy' => \intval( $changedPrivacyPolicy ),
'reg' => \intval( $changedRegistrationTerms )
) ) );
}
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_settings_terms');
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate( 'global' )->block( 'menu__core_settings_terms', $form );
}
/**
* Process the text storage from submitting the form
*
* @param array $values Values from form
* @return array
*/
public static function processForm( $values )
{
foreach ( array( 'gl_guidelines' => 'guidelines_value', 'privacy_text' => 'privacy_text_value', 'reg_rules' => 'reg_rules_value', 'guest_terms_bar_text' => 'guest_terms_bar_text_value' ) as $k => $v )
{
/* Guest terms bar text has special replacements that need to be swapped to sprintf parameters */
if ( $k == 'guest_terms_bar_text' and $values['guest_terms_bar'] )
{
foreach( $values[ $k ] AS $lang_id => $text )
{
$values[ $k ][ $lang_id ] = str_replace( array( '{terms}', '{privacy}', '{guidelines}', '{cookies}' ), array( '%1$s', '%2$s', '%3$s', '%4$s' ), $text );
}
}
\IPS\Lang::saveCustom( 'core', $v, $values[ $k ] );
unset( $values[ $k ] );
}
return $values;
}
/**
* Build the form
*
* @param \IPS\Form $form Form to add settings to
* @return void
*/
public static function buildForm( $form )
{
$form->addHeader( 'terms_guidelines' );
$form->add( new \IPS\Helpers\Form\Radio( 'gl_type', \IPS\Settings::i()->gl_type, FALSE, array(
'options' => array(
@@ -82,50 +157,6 @@ class _terms extends \IPS\Dispatcher\Controller
$form->addHeader( 'guest_terms_options' );
$form->add( new \IPS\Helpers\Form\YesNo( 'guest_terms_bar', \IPS\Settings::i()->guest_terms_bar, FALSE, array( 'togglesOn' => array( 'guest_terms_bar_text' ) ) ) );
$form->add( new \IPS\Helpers\Form\Translatable( 'guest_terms_bar_text', NULL, FALSE, array( 'app' => 'core', 'key' => 'guest_terms_bar_text_value', 'sprintf' => array( '{terms}', '{privacy}', '{guidelines}', '{cookies}' ) ), NULL, NULL, NULL, 'guest_terms_bar_text' ) );
if ( $values = $form->values() )
{
/* What were our previous values? */
$existingPrivacyPolicy = iterator_to_array( \IPS\Db::i()->select( 'word_custom', 'core_sys_lang_words', array( 'word_key=?', 'privacy_text_value' ) ) );
$existingRegistrationTerms = iterator_to_array( \IPS\Db::i()->select( 'word_custom', 'core_sys_lang_words', array( 'word_key=?', 'reg_rules_value' ) ) );
/* Save */
foreach ( array( 'gl_guidelines' => 'guidelines_value', 'privacy_text' => 'privacy_text_value', 'reg_rules' => 'reg_rules_value', 'guest_terms_bar_text' => 'guest_terms_bar_text_value' ) as $k => $v )
{
/* Guest terms bar text has special replacements that need to be swapped to sprintf parameters */
if ( $k == 'guest_terms_bar_text' and $values['guest_terms_bar'] )
{
foreach( $values[ $k ] AS $lang_id => $text )
{
$values[ $k ][ $lang_id ] = str_replace( array( '{terms}', '{privacy}', '{guidelines}', '{cookies}' ), array( '%1$s', '%2$s', '%3$s', '%4$s' ), $text );
}
}
\IPS\Lang::saveCustom( 'core', $v, $values[ $k ] );
unset( $values[ $k ] );
}
$form->saveAsSettings( $values );
/* Clear guest page caches */
\IPS\Data\Cache::i()->clearAll();
/* Log */
\IPS\Session::i()->log( 'acplogs__terms_edited' );
/* Do we need to ask the admin if they want to ask members to reconfirm? */
$changedPrivacyPolicy = $existingPrivacyPolicy != iterator_to_array( \IPS\Db::i()->select( 'word_custom', 'core_sys_lang_words', array( 'word_key=?', 'privacy_text_value' ) ) );
$changedRegistrationTerms = $existingRegistrationTerms != iterator_to_array( \IPS\Db::i()->select( 'word_custom', 'core_sys_lang_words', array( 'word_key=?', 'reg_rules_value' ) ) );
if ( $changedPrivacyPolicy or $changedRegistrationTerms )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal('app=core&module=settings&controller=terms&do=reconfirm')->setQueryString( array(
'privacy' => intval( $changedPrivacyPolicy ),
'reg' => intval( $changedRegistrationTerms )
) ) );
}
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_settings_terms');
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate( 'global' )->block( 'menu__core_settings_terms', $form );
}
/**
+33 -12
View File
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\staff;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _admin extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -231,7 +236,7 @@ class _admin extends \IPS\Dispatcher\Controller
elseif ( $values['acprestrictions_member'] )
{
\IPS\Dispatcher::i()->checkAcpPermission( 'restrictions_add_member' );
if ( $values['acprestrictions_member'] === \IPS\Member::loggedIn() )
if ( $values['acprestrictions_member']->member_id === \IPS\Member::loggedIn()->member_id )
{
$form->error = \IPS\Member::loggedIn()->language()->addToStack('acprestrictions_noself');
}
@@ -245,7 +250,7 @@ class _admin extends \IPS\Dispatcher\Controller
{
$current = \IPS\Db::i()->select( '*', 'core_admin_permission_rows', array( "row_id=? AND row_id_type=?", $rowId, $values['acprestrictions_type'] ) );
if ( !count( $current ) )
if ( !\count( $current ) )
{
$current = array(
'row_id' => $rowId,
@@ -274,6 +279,7 @@ class _admin extends \IPS\Dispatcher\Controller
/**
* Edit
*
* @csrfChecked Uses form helper 7 Oct 2019
* @return void
*/
protected function edit()
@@ -281,7 +287,7 @@ class _admin extends \IPS\Dispatcher\Controller
try
{
/* Get record */
$current = \IPS\Db::i()->select( '*', 'core_admin_permission_rows', array( "row_id=? AND row_id_type=?", intval( \IPS\Request::i()->id ), \IPS\Request::i()->type ) )->first();
$current = \IPS\Db::i()->select( '*', 'core_admin_permission_rows', array( "row_id=? AND row_id_type=?", \intval( \IPS\Request::i()->id ), \IPS\Request::i()->type ) )->first();
}
catch ( \UnderflowException $e )
{
@@ -346,17 +352,19 @@ class _admin extends \IPS\Dispatcher\Controller
*/
protected function save()
{
\IPS\Session::i()->csrfCheck();
/* Get record */
$current = \IPS\Db::i()->select( '*', 'core_admin_permission_rows', array( "row_id=? AND row_id_type=?", intval( \IPS\Request::i()->id ), \IPS\Request::i()->type ) )->first();
$current = \IPS\Db::i()->select( '*', 'core_admin_permission_rows', array( "row_id=? AND row_id_type=?", \intval( \IPS\Request::i()->id ), \IPS\Request::i()->type ) )->first();
if ( !$current )
{
\IPS\Output::i()->error( 'node_error', '2C113/2', 404, '' );
}
$permissions = ( \IPS\Request::i()->admin_use_restrictions == 'no' ) ? '*' : json_encode( ( is_array( \IPS\Request::i()->r ) ) ? \IPS\Request::i()->r : array() );
$permissions = ( \IPS\Request::i()->admin_use_restrictions == 'no' ) ? '*' : json_encode( ( \is_array( \IPS\Request::i()->r ) ) ? \IPS\Request::i()->r : array() );
/* Save */
\IPS\Db::i()->update( 'core_admin_permission_rows', array( 'row_perm_cache' => $permissions, 'row_updated' => time() ), array( "row_id=? AND row_id_type=?", intval( \IPS\Request::i()->id ), \IPS\Request::i()->type ) );
\IPS\Db::i()->update( 'core_admin_permission_rows', array( 'row_perm_cache' => $permissions, 'row_updated' => time() ), array( "row_id=? AND row_id_type=?", \intval( \IPS\Request::i()->id ), \IPS\Request::i()->type ) );
unset( \IPS\Data\Store::i()->administrators );
@@ -374,7 +382,7 @@ class _admin extends \IPS\Dispatcher\Controller
*/
protected function delete()
{
$current = \IPS\Db::i()->select( '*', 'core_admin_permission_rows', array( "row_id=? AND row_id_type=?", intval( \IPS\Request::i()->id ), \IPS\Request::i()->type ) )->first();
$current = \IPS\Db::i()->select( '*', 'core_admin_permission_rows', array( "row_id=? AND row_id_type=?", \intval( \IPS\Request::i()->id ), \IPS\Request::i()->type ) )->first();
if ( $current['row_id_type'] === 'member' )
{
@@ -414,7 +422,7 @@ class _admin extends \IPS\Dispatcher\Controller
}
\IPS\Session::i()->log( 'acplog__acpr_deleted', array( $name => FALSE ) );
\IPS\Db::i()->delete( 'core_admin_permission_rows', array( 'row_id=? AND row_id_type=?', intval( \IPS\Request::i()->id ), \IPS\Request::i()->type ) );
\IPS\Db::i()->delete( 'core_admin_permission_rows', array( 'row_id=? AND row_id_type=?', \intval( \IPS\Request::i()->id ), \IPS\Request::i()->type ) );
unset ( \IPS\Data\Store::i()->administrators );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=staff&controller=admin' ) );
@@ -489,13 +497,26 @@ class _admin extends \IPS\Dispatcher\Controller
$table->sortDirection = $table->sortDirection ?: 'desc';
/* Search */
$table->quickSearch = array( array( 'note', 'word_custom', 'word_default' ), 'note' );
$table->advancedSearch = array(
'member_id' => \IPS\Helpers\Table\SEARCH_MEMBER,
'ip_address' => \IPS\Helpers\Table\SEARCH_CONTAINS_TEXT,
'ctime' => \IPS\Helpers\Table\SEARCH_DATE_RANGE,
);
/* Custom quick search function to search unicode entities in JSON encoded data */
$table->quickSearch = function( $val )
{
$searchTerm = mb_strtolower( trim( \IPS\Request::i()->quicksearch ) );
$jsonSearchTerm = str_replace( '\\', '\\\\\\', trim( json_encode( trim( \IPS\Request::i()->quicksearch ) ), '"' ) );
return array(
"(`note` LIKE CONCAT( '%', ?, '%' ) OR LOWER(`word_custom`) LIKE CONCAT( '%', ?, '%' ) OR LOWER(`word_default`) LIKE CONCAT( '%', ?, '%' ))",
$jsonSearchTerm,
$searchTerm,
$searchTerm
);
};
$table->joins = array(
array( 'from' => array( 'core_sys_lang_words', 'w' ), 'where' => "w.word_key=lang_key AND w.lang_id=" . \IPS\Member::loggedIn()->language()->id )
);
@@ -529,7 +550,7 @@ class _admin extends \IPS\Dispatcher\Controller
\IPS\Dispatcher::i()->checkAcpPermission( 'restrictions_adminlogs_prune' );
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\Number( 'prune_log_admin', \IPS\Settings::i()->prune_log_admin, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), \IPS\Member::loggedIn()->language()->addToStack('days'), 'prune_log_admin' ) );
$form->add( new \IPS\Helpers\Form\Interval( 'prune_log_admin', \IPS\Settings::i()->prune_log_admin, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL, 'prune_log_admin' ) );
if ( $values = $form->values() )
{
@@ -612,7 +633,7 @@ class _admin extends \IPS\Dispatcher\Controller
\IPS\Dispatcher::i()->checkAcpPermission( 'restrictions_acploginlogs_prune' );
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\Number( 'prune_log_adminlogin', \IPS\Settings::i()->prune_log_adminlogin, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), \IPS\Member::loggedIn()->language()->addToStack('days'), 'prune_log_adminlogin' ) );
$form->add( new \IPS\Helpers\Form\Interval( 'prune_log_adminlogin', \IPS\Settings::i()->prune_log_adminlogin, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL, 'prune_log_adminlogin' ) );
if ( $values = $form->values() )
{
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\staff;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _directory extends \IPS\Node\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Node Class
*/
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\staff;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _moderators extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -169,11 +174,11 @@ class _moderators extends \IPS\Dispatcher\Controller
{
$form->add( new \IPS\Helpers\Form\Radio( 'moderators_type', NULL, TRUE, array( 'options' => array( 'g' => 'group', 'm' => 'member' ), 'toggles' => array( 'g' => array( 'moderators_group' ), 'm' => array( 'moderators_member' ) ) ) ) );
}
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'staff', 'moderators_add_member' ) )
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'staff', 'moderators_add_group' ) )
{
$form->add( new \IPS\Helpers\Form\Select( 'moderators_group', NULL, FALSE, array( 'options' => \IPS\Member\Group::groups( TRUE, FALSE ), 'parse' => 'normal' ), NULL, NULL, NULL, 'moderators_group' ) );
}
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'staff', 'moderators_add_group' ) )
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'staff', 'moderators_add_member' ) )
{
$form->add( new \IPS\Helpers\Form\Member( 'moderators_member', NULL, ( \IPS\Request::i()->moderators_type === 'member' ), array(), NULL, NULL, NULL, 'moderators_member' ) );
}
@@ -204,7 +209,7 @@ class _moderators extends \IPS\Dispatcher\Controller
$current = array();
}
if ( !count( $current ) )
if ( !\count( $current ) )
{
$current = array(
'id' => $rowId,
@@ -242,13 +247,23 @@ class _moderators extends \IPS\Dispatcher\Controller
{
try
{
$current = \IPS\Db::i()->select( '*', 'core_moderators', array( "id=? AND type=?", intval( \IPS\Request::i()->id ), \IPS\Request::i()->type ) )->first();
$current = \IPS\Db::i()->select( '*', 'core_moderators', array( "id=? AND type=?", \intval( \IPS\Request::i()->id ), \IPS\Request::i()->type ) )->first();
}
catch ( \UnderflowException $e )
{
\IPS\Output::i()->error( 'node_error', '2C118/2', 404, '' );
}
/* Check acp restrictions */
if ( $current['type'] === 'm' )
{
\IPS\Dispatcher::i()->checkAcpPermission( 'moderators_edit_member' );
}
else
{
\IPS\Dispatcher::i()->checkAcpPermission( 'moderators_edit_group' );
}
/* Load */
try
{
@@ -262,7 +277,7 @@ class _moderators extends \IPS\Dispatcher\Controller
$currentPermissions = ( $current['perms'] === '*' ) ? '*' : ( $current['perms'] ? json_decode( $current['perms'], TRUE ) : array() );
/* Define content field toggles */
$toggles = array( 'view_future' => array(), 'future_publish' => array(), 'pin' => array(), 'unpin' => array(), 'feature' => array(), 'unfeature' => array(), 'edit' => array(), 'hide' => array(), 'unhide' => array(), 'view_hidden' => array(), 'move' => array(), 'lock' => array(), 'unlock' => array(), 'reply_to_locked' => array(), 'delete' => array(), 'split_merge' => array(), 'feature_comments' => array(), 'unfeature_comments' => array(), 'add_item_message' => array(), 'edit_item_message' => array(), 'delete_item_message' => array() );
$toggles = array( 'view_future' => array(), 'future_publish' => array(), 'pin' => array(), 'unpin' => array(), 'feature' => array(), 'unfeature' => array(), 'edit' => array(), 'hide' => array(), 'unhide' => array(), 'view_hidden' => array(), 'move' => array(), 'lock' => array(), 'unlock' => array(), 'reply_to_locked' => array(), 'delete' => array(), 'split_merge' => array(), 'feature_comments' => array(), 'unfeature_comments' => array(), 'add_item_message' => array(), 'edit_item_message' => array(), 'delete_item_message' => array(), 'toggle_item_moderation' => array() );
foreach ( \IPS\Application::allExtensions( 'core', 'ModeratorPermissions', FALSE ) as $k => $ext )
{
if ( $ext instanceof \IPS\Content\ModeratorPermissions )
@@ -303,7 +318,9 @@ class _moderators extends \IPS\Dispatcher\Controller
$form->add(
new \IPS\Helpers\Form\Radio( 'mod_use_restrictions', ( $currentPermissions === '*' ) ? 'no' : 'yes', TRUE, array( 'options' => array( 'no' => 'mod_all_permissions', 'yes' => 'mod_restricted' ), 'toggles' => array( 'yes' => array( 'permission_form_wrapper' ) ) ), NULL, NULL, NULL, 'use_restrictions_id' )
);
$form->add( new \IPS\Helpers\Form\YesNo( 'mod_show_badge', ( $current ) ? $current['show_badge'] : TRUE, TRUE ) );
$extensions = array();
foreach ( \IPS\Application::allExtensions( 'core', 'ModeratorPermissions', FALSE, 'core' ) as $k => $ext )
{
@@ -324,7 +341,7 @@ class _moderators extends \IPS\Dispatcher\Controller
foreach ( $ext->getPermissions( $toggles ) as $name => $data )
{
/* Class */
$type = is_array( $data ) ? $data[0] : $data;
$type = \is_array( $data ) ? $data[0] : $data;
$class = '\IPS\Helpers\Form\\' . ( $type );
/* Remember 'nodes' */
@@ -363,7 +380,7 @@ class _moderators extends \IPS\Dispatcher\Controller
}
/* Options */
$options = is_array( $data ) ? $data[1] : array();
$options = \is_array( $data ) ? $data[1] : array();
if ( $type === 'Number' )
{
$options['unlimited'] = -1;
@@ -372,7 +389,7 @@ class _moderators extends \IPS\Dispatcher\Controller
/* Prefix/Suffix */
$prefix = NULL;
$suffix = NULL;
if ( is_array( $data ) )
if ( \is_array( $data ) )
{
if ( isset( $data[2] ) )
{
@@ -392,6 +409,15 @@ class _moderators extends \IPS\Dispatcher\Controller
/* Handle submissions */
if ( $values = $form->values() )
{
/* Allow extensions an opportunity to inspect the values and make adjustments */
foreach ( \IPS\Application::allExtensions( 'core', 'ModeratorPermissions', FALSE ) as $k => $ext )
{
if( method_exists( $ext, 'preSave' ) )
{
$ext->preSave( $values );
}
}
if( $values['mod_use_restrictions'] == 'no' )
{
$permissions = '*';
@@ -405,7 +431,7 @@ class _moderators extends \IPS\Dispatcher\Controller
foreach ( $values as $k => $v )
{
/* For node fields, if the value is 0 translate it to -1 so mod permissions can merge properly */
if( in_array( $k, $nodeFields ) )
if( \in_array( $k, $nodeFields ) )
{
/* If nothing is checked we have '', but if 'all' is checked then the value is 0 */
if( $v === 0 )
@@ -415,7 +441,7 @@ class _moderators extends \IPS\Dispatcher\Controller
}
}
if ( is_array( $v ) )
if ( \is_array( $v ) )
{
foreach ( $v as $l => $w )
{
@@ -446,7 +472,7 @@ class _moderators extends \IPS\Dispatcher\Controller
$permissions = json_encode( $values );
}
\IPS\Db::i()->update( 'core_moderators', array( 'perms' => $permissions, 'updated' => time() ), array( array( "id=? AND type=?", $current['id'], $current['type'] ) ) );
\IPS\Db::i()->update( 'core_moderators', array( 'perms' => $permissions, 'updated' => time(), 'show_badge' => $values['mod_show_badge'] ), array( array( "id=? AND type=?", $current['id'], $current['type'] ) ) );
if( !( $currentPermissions == '*' AND $changed == '*' ) )
{
@@ -467,7 +493,7 @@ class _moderators extends \IPS\Dispatcher\Controller
/* Display */
\IPS\Output::i()->title = $_name;
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'admin_members.js', 'core', 'admin' ) );
\IPS\Output::i()->output .= $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'members' ) ), 'moderatorPermissions' ) );
\IPS\Output::i()->output .= $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'members' ), 'moderatorPermissions' ) );
}
/**
@@ -480,7 +506,7 @@ class _moderators extends \IPS\Dispatcher\Controller
/* Load */
try
{
$current = \IPS\Db::i()->select( '*', 'core_moderators', array( "id=? AND type=?", intval( \IPS\Request::i()->id ), \IPS\Request::i()->type ) )->first();
$current = \IPS\Db::i()->select( '*', 'core_moderators', array( "id=? AND type=?", \intval( \IPS\Request::i()->id ), \IPS\Request::i()->type ) )->first();
}
catch( \UnderflowException $e )
{
@@ -584,13 +610,26 @@ class _moderators extends \IPS\Dispatcher\Controller
$table->sortDirection = $table->sortDirection ?: 'desc';
/* Search */
$table->quickSearch = array( array( 'note', 'word_custom', 'word_default' ), 'action' );
$table->advancedSearch = array(
'member_id' => \IPS\Helpers\Table\SEARCH_MEMBER,
'ip_address' => \IPS\Helpers\Table\SEARCH_CONTAINS_TEXT,
'ctime' => \IPS\Helpers\Table\SEARCH_DATE_RANGE
);
/* Custom quick search function to search unicode entities in JSON encoded data */
$table->quickSearch = function( $val )
{
$searchTerm = mb_strtolower( trim( \IPS\Request::i()->quicksearch ) );
$jsonSearchTerm = str_replace( '\\', '\\\\\\', trim( json_encode( trim( \IPS\Request::i()->quicksearch ) ), '"' ) );
return array(
"(`note` LIKE CONCAT( '%', ?, '%' ) OR LOWER(`word_custom`) LIKE CONCAT( '%', ?, '%' ) OR LOWER(`word_default`) LIKE CONCAT( '%', ?, '%' ))",
$jsonSearchTerm,
$searchTerm,
$searchTerm
);
};
$table->joins = array(
array( 'from' => array( 'core_sys_lang_words', 'w' ), 'where' => "w.word_key=lang_key AND w.lang_id=" . \IPS\Member::loggedIn()->language()->id )
);
@@ -624,7 +663,7 @@ class _moderators extends \IPS\Dispatcher\Controller
\IPS\Dispatcher::i()->checkAcpPermission( 'restrictions_moderatorlogs_prune' );
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\Number( 'prune_log_moderator', \IPS\Settings::i()->prune_log_moderator, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), \IPS\Member::loggedIn()->language()->addToStack('days'), 'prune_log_moderator' ) );
$form->add( new \IPS\Helpers\Form\Interval( 'prune_log_moderator', \IPS\Settings::i()->prune_log_moderator, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL, 'prune_log_moderator' ) );
if ( $values = $form->values() )
{
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\stats;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _deviceusage extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -66,7 +71,7 @@ class _deviceusage extends \IPS\Dispatcher\Controller
$minimumDate = \IPS\DateTime::ts( $oldestLog );
}
}
catch( \OutOfRangeException $e )
catch( \UnderflowException $e )
{
/* We have nothing tracked, set minimum date to today */
$minimumDate = \IPS\DateTime::create();
@@ -103,7 +108,7 @@ class _deviceusage extends \IPS\Dispatcher\Controller
protected function settings()
{
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\Number( 'stats_device_usage_prune', \IPS\Settings::i()->stats_device_usage_prune, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), \IPS\Member::loggedIn()->language()->addToStack('days'), 'prune_log_moderator' ) );
$form->add( new \IPS\Helpers\Form\Interval( 'stats_device_usage_prune', \IPS\Settings::i()->stats_device_usage_prune, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL, 'prune_log_moderator' ) );
if ( $values = $form->values() )
{
Whitespace-only changes.
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\stats;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _membersactivity extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -76,7 +81,7 @@ class _membersactivity extends \IPS\Dispatcher\Controller
$start = $values['date']['start']->html();
$end = $values['date']['end']->html();
$groups = ( count( array_diff( array_keys( $groupOptions ), $values['groups'] ) ) ) ? $values['groups'] : NULL;
$groups = ( \count( array_diff( array_keys( $groupOptions ), $values['groups'] ) ) ) ? $values['groups'] : NULL;
}
else
{
@@ -87,7 +92,7 @@ class _membersactivity extends \IPS\Dispatcher\Controller
$start = $defaults['start']->html();
$end = $defaults['end']->html();
$groups = ( count( array_diff( array_keys( $groupOptions ), $defaultGroups ) ) ) ? $defaultGroups : NULL;
$groups = ( \count( array_diff( array_keys( $groupOptions ), $defaultGroups ) ) ) ? $defaultGroups : NULL;
}
/* Do we have our date ranges? */
@@ -105,7 +110,7 @@ class _membersactivity extends \IPS\Dispatcher\Controller
$count = \IPS\Db::i()->select( 'COUNT(*)', 'core_members', $where )->first();
/* And now build the table */
$table = new \IPS\Helpers\Table\Db( 'core_members', \IPS\Request::i()->url()->setQueryString( array( 'postDateStart' => $startTime, 'postDateEnd' => $endTime, 'postGroups' => is_array( $groups ) ? implode( ',', $groups ) : NULL ) ), $where );
$table = new \IPS\Helpers\Table\Db( 'core_members', \IPS\Request::i()->url()->setQueryString( array( 'postDateStart' => $startTime, 'postDateEnd' => $endTime, 'postGroups' => \is_array( $groups ) ? implode( ',', $groups ) : NULL ) ), $where );
$table->include = array( 'name', 'email', 'member_posts', 'member_last_post', 'group_name', 'ip_address' );
$table->mainColumn = 'name';
@@ -162,7 +167,7 @@ class _membersactivity extends \IPS\Dispatcher\Controller
$table->extraHtml = \IPS\Theme::i()->getTemplate( 'stats' )->tableheader( $start, $end, $count, "member_activity_results" );
}
$formHtml = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'stats' ) ), 'filtersFormTemplate' ) );
$formHtml = $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'stats' ), 'filtersFormTemplate' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'admin_stats.js', 'core' ) );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_stats_membersactivity');
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\stats;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _membervisits extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -76,7 +81,7 @@ class _membervisits extends \IPS\Dispatcher\Controller
$start = $values['date']['start']->html();
$end = $values['date']['end']->html();
$groups = ( count( array_diff( array_keys( $groupOptions ), $values['groups'] ) ) ) ? $values['groups'] : NULL;
$groups = ( \count( array_diff( array_keys( $groupOptions ), $values['groups'] ) ) ) ? $values['groups'] : NULL;
}
else
{
@@ -87,7 +92,7 @@ class _membervisits extends \IPS\Dispatcher\Controller
$start = $defaults['start']->html();
$end = $defaults['end']->html();
$groups = ( count( array_diff( array_keys( $groupOptions ), $defaultGroups ) ) ) ? $defaultGroups : NULL;
$groups = ( \count( array_diff( array_keys( $groupOptions ), $defaultGroups ) ) ) ? $defaultGroups : NULL;
}
/* Do we have our date ranges? */
@@ -105,7 +110,7 @@ class _membervisits extends \IPS\Dispatcher\Controller
$count = \IPS\Db::i()->select( 'COUNT(*)', 'core_members', $where )->first();
/* And now build the table */
$table = new \IPS\Helpers\Table\Db( 'core_members', \IPS\Request::i()->url()->setQueryString( array( 'visitDateStart' => $startTime, 'visitDateEnd' => $endTime, 'visitGroups' => is_array( $groups ) ? implode( ',', $groups ) : NULL ) ), $where );
$table = new \IPS\Helpers\Table\Db( 'core_members', \IPS\Request::i()->url()->setQueryString( array( 'visitDateStart' => $startTime, 'visitDateEnd' => $endTime, 'visitGroups' => \is_array( $groups ) ? implode( ',', $groups ) : NULL ) ), $where );
$table->include = array( 'name', 'email', 'last_visit', 'group_name', 'ip_address' );
$table->mainColumn = 'name';
@@ -161,7 +166,7 @@ class _membervisits extends \IPS\Dispatcher\Controller
$table->extraHtml = \IPS\Theme::i()->getTemplate( 'stats' )->tableheader( $start, $end, $count, "member_visits_results" );
}
$formHtml = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'stats' ) ), 'filtersFormTemplate' ) );
$formHtml = $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'stats' ), 'filtersFormTemplate' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'admin_stats.js', 'core' ) );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_stats_membervisits');
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\stats;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _onlineusers extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -66,7 +71,7 @@ class _onlineusers extends \IPS\Dispatcher\Controller
$minimumDate = \IPS\DateTime::ts( $oldestLog );
}
}
catch( \OutOfRangeException $e )
catch( \UnderflowException $e )
{
/* We have nothing tracked, set minimum date to today */
$minimumDate = \IPS\DateTime::create();
@@ -110,7 +115,7 @@ class _onlineusers extends \IPS\Dispatcher\Controller
protected function settings()
{
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\Number( 'stats_online_users_prune', \IPS\Settings::i()->stats_online_users_prune, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), \IPS\Member::loggedIn()->language()->addToStack('days'), 'prune_log_moderator' ) );
$form->add( new \IPS\Helpers\Form\Interval( 'stats_online_users_prune', \IPS\Settings::i()->stats_online_users_prune, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL, 'prune_log_moderator' ) );
if ( $values = $form->values() )
{
@@ -0,0 +1,126 @@
<?php
/**
* @brief User activity statistics overview
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 09 Jan 2020
*/
namespace IPS\core\modules\admin\stats;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* User activity statistics overview
*/
class _overview extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* @brief Date range to restrict to, or NULL for no restriction
*/
protected $dateRange = '7';
/**
* @brief Form object
*/
protected $form = NULL;
/**
* @brief Template group to use to output
*/
protected $templateGroup = 'stats';
/**
* Execute
*
* @return void
*/
public function execute()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'overview_manage' );
$options = array(
'7' => 'last_week',
'30' => 'last_month',
'90' => 'last_three_months',
'180' => 'last_six_months',
'365' => 'last_year',
'0' => 'alltime',
'-1' => 'custom'
);
$this->form = new \IPS\Helpers\Form( 'posts', 'update' );
$this->form->add( new \IPS\Helpers\Form\Select( 'predate', '7', FALSE, array( 'options' => $options, 'toggles' => array( '-1' => array( 'dateFilterInputs' ) ) ) ) );
$this->form->add( new \IPS\Helpers\Form\DateRange( 'date', NULL, FALSE, array(), NULL, NULL, NULL, 'dateFilterInputs' ) );
parent::execute();
}
/**
* Create the general page layout, but we will load the individual cells via AJAX to ensure there are no performance concerns loading the page
*
* @return void
*/
protected function manage()
{
$formHtml = $this->form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'stats' ), 'filtersOverviewForm' ) );
$blocks = \IPS\Application::allExtensions( 'core', 'OverviewStatistics', TRUE, 'core', 'Registrations' );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'admin_stats.js', 'core' ) );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'system/statistics.css', 'core', 'admin' ) );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_stats_overview');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( $this->templateGroup )->overview( $formHtml, $blocks );
}
/**
* Load an individual block and output its HTML
*
* @return void
*/
protected function loadBlock()
{
$blocks = \IPS\Application::allExtensions( 'core', 'OverviewStatistics', TRUE, 'core', 'Registrations' );
if( !isset( $blocks[ \IPS\Request::i()->blockKey ] ) )
{
\IPS\Output::i()->error( 'stats_overview_block_not_found', '2C412/1', 404, '' );
}
$dateFilters = NULL;
if( \IPS\Request::i()->range )
{
$dateFilters = \IPS\Request::i()->range;
}
elseif( \IPS\Request::i()->start )
{
try
{
$timezone = \IPS\Member::loggedIn()->timezone ? new \DateTimeZone( \IPS\Member::loggedIn()->timezone ) : NULL;
}
catch ( \Exception $e )
{
$timezone = NULL;
}
$dateFilters = array(
'start' => new \IPS\DateTime( \IPS\Helpers\Form\Date::_convertDateFormat( \IPS\Request::i()->start ), $timezone ),
'end' => new \IPS\DateTime( \IPS\Helpers\Form\Date::_convertDateFormat( \IPS\Request::i()->end ), $timezone )
);
}
\IPS\Output::i()->sendOutput( $blocks[ \IPS\Request::i()->blockKey ]->getBlock( $dateFilters, \IPS\Request::i()->subblock ) );
}
}
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\stats;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _registrationstats extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Manage Members
*
@@ -45,7 +50,7 @@ class _registrationstats extends \IPS\Dispatcher\Controller
$chart->availableTypes = array( 'AreaChart', 'ColumnChart', 'BarChart' );
/* fetch only successful registered members ; if this needs to be changed, please review the other areas where we have the name<>? AND email<>? condition */
$chart->where[] = array( '( name<>? AND email<>? )', '', '' );
$chart->where[] = array( 'completed=?', true );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_stats_registrationstats');
\IPS\Output::i()->output = (string) $chart;
@@ -11,7 +11,7 @@
namespace IPS\core\modules\admin\support;
/* 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;
@@ -22,6 +22,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _errorLogs extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -32,16 +37,16 @@ class _errorLogs extends \IPS\Dispatcher\Controller
\IPS\Dispatcher::i()->checkAcpPermission( 'system_logs_view' );
parent::execute();
}
/**
* Manage Error Logs
* Get table
*
* @return void
* @param \IPS\Http\Url $url The URL where the table will be displayed
* @return \IPS\Helpers\Table\Db
*/
protected function manage()
public static function table( \IPS\Http\Url $url )
{
/* Create the table */
$table = new \IPS\Helpers\Table\Db( 'core_error_logs', \IPS\Http\Url::internal( 'app=core&module=support&controller=errorLogs' ) );
$table = new \IPS\Helpers\Table\Db( 'core_error_logs', $url );
$table->langPrefix = 'errorlogs_';
$table->include = array( 'log_error_code', 'log_error', 'log_ip_address', 'log_request_uri', 'log_member', 'log_date' );
$table->mainColumn = 'log_error_code';
@@ -85,7 +90,6 @@ class _errorLogs extends \IPS\Dispatcher\Controller
$table->sortBy = $table->sortBy ?: 'log_date';
$table->sortDirection = $table->sortDirection ?: 'desc';
/* Search */
$table->advancedSearch = array(
'log_member' => \IPS\Helpers\Table\SEARCH_MEMBER,
'log_ip_address' => \IPS\Helpers\Table\SEARCH_CONTAINS_TEXT,
@@ -96,7 +100,16 @@ class _errorLogs extends \IPS\Dispatcher\Controller
);
$table->quickSearch = 'log_error';
/* Add a button for settings */
return $table;
}
/**
* Manage Error Logs
*
* @return void
*/
protected function manage()
{
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'support', 'diagnostic_log_settings' ) )
{
\IPS\Output::i()->sidebar['actions'] = array(
@@ -109,9 +122,8 @@ class _errorLogs extends \IPS\Dispatcher\Controller
);
}
/* Display */
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('errorlogs');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->block( 'errorlogs', (string) $table );
\IPS\Output::i()->output = (string) static::table( \IPS\Http\Url::internal( 'app=core&module=support&controller=errorLogs' ) );
}
/**
@@ -134,12 +146,9 @@ class _errorLogs extends \IPS\Dispatcher\Controller
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\Radio( 'error_log_level', \IPS\Settings::i()->error_log_level, FALSE, array( 'options' => $levelOptions ) ) );
$form->add( new \IPS\Helpers\Form\Number( 'error_notify_level', \IPS\Settings::i()->error_notify_level, FALSE, array( 'min' => 1, 'max' => 5, 'unlimited' => 0, 'unlimitedLang' => 'none' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('error_log_level_prefix'), \IPS\Member::loggedIn()->language()->addToStack('error_log_level_suffix') ) );
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'staff', 'error_prune' ) )
{
$form->add( new \IPS\Helpers\Form\Number( 'prune_log_error', \IPS\Settings::i()->prune_log_error, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), \IPS\Member::loggedIn()->language()->addToStack('days'), 'prune_log_error' ) );
$form->add( new \IPS\Helpers\Form\Interval( 'prune_log_error', \IPS\Settings::i()->prune_log_error, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL, 'prune_log_error' ) );
}
if ( $values = $form->values() )
@@ -12,7 +12,7 @@
namespace IPS\core\modules\admin\support;
/* 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;
@@ -23,6 +23,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _hookedFiles extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -12,7 +12,7 @@
namespace IPS\core\modules\admin\support;
/* 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;
@@ -23,6 +23,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _recovery extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Recover
*
@@ -41,11 +46,13 @@ class _recovery extends \IPS\Dispatcher\Controller
\IPS\Output::i()->error( 'no_writes', '1C342/2', 403, '' );
}
\IPS\Session::i()->csrfCheck();
/* We are, let's set up a multi-redirect to disable things. At the end of the process, we'll list everything we did. */
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'recovery_mode' );
\IPS\Output::i()->output = new \IPS\Helpers\MultipleRedirect( \IPS\Http\Url::internal( 'app=core&module=support&controller=recovery' ), function( $step )
\IPS\Output::i()->output = new \IPS\Helpers\MultipleRedirect( \IPS\Http\Url::internal( 'app=core&module=support&controller=recovery' )->csrf(), function( $step )
{
$step = intval( $step );
$step = \intval( $step );
switch( $step )
{
@@ -55,7 +62,7 @@ class _recovery extends \IPS\Dispatcher\Controller
/* Disable All non-IPS Applications */
foreach( \IPS\Application::applications() AS $app )
{
if ( !in_array( $app->directory, \IPS\Application::$ipsApps ) )
if ( !\in_array( $app->directory, \IPS\IPS::$ipsApps ) )
{
$app->_enabled = FALSE;
$appDisabled[] = $app->_id;
@@ -99,7 +106,6 @@ class _recovery extends \IPS\Dispatcher\Controller
/* Set this account to use that theme */
\IPS\Member::loggedIn()->skin = $theme->id;
\IPS\Member::loggedIn()->acp_skin = $theme->id;
\IPS\Member::loggedIn()->save();
$themeReset = TRUE;
Loaded 100 of 3791 files, more files were not shown because too many files have changed in this diff. Show more