Version 4.5.0
This commit is contained in:
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( '<?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' );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
Reference in new issue
Block a user