Version 4.7.0
This commit is contained in:
1 parent
517a5e1f70
commit
8ba7d43898
1933 files changed
+65613
-11332
No files matched your search
@@ -63,13 +63,17 @@ class _badges extends \IPS\Node\Controller
|
||||
'title' => 'acp_achievements_export',
|
||||
);
|
||||
|
||||
\IPS\Output::i()->sidebar['actions']['import'] = array(
|
||||
'primary' => false,
|
||||
'icon' => 'cloud-upload',
|
||||
'link' => \IPS\Http\Url::internal('app=core&module=achievements&controller=badges&do=importForm'),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('acp_achievements_import') ),
|
||||
'title' => 'acp_achievements_import',
|
||||
);
|
||||
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'achievements', 'badges_manage' ) )
|
||||
{
|
||||
\IPS\Output::i()->sidebar['actions']['import'] = array(
|
||||
'primary' => false,
|
||||
'icon' => 'cloud-upload',
|
||||
'link' => \IPS\Http\Url::internal('app=core&module=achievements&controller=badges&do=importForm'),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('acp_achievements_import') ),
|
||||
'title' => 'acp_achievements_import',
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return parent::manage();
|
||||
}
|
||||
@@ -119,13 +123,20 @@ class _badges extends \IPS\Node\Controller
|
||||
return \IPS\Theme::i()->getTemplate( 'global', 'core' )->userLink( \IPS\Member::load( $val ) );
|
||||
},
|
||||
'action_log' => function( $val, $row ) {
|
||||
if ( $row['action'] )
|
||||
try
|
||||
{
|
||||
$exploded = explode( '_', $row['action'] );
|
||||
$extension = \IPS\Application::load( $exploded[0] )->extensions( 'core', 'AchievementAction' )[ $exploded[1] ];
|
||||
return $extension->logRow( $row['identifier'], explode( ',', $row['actor'] ) );
|
||||
if ( $row['action'] )
|
||||
{
|
||||
$exploded = explode( '_', $row['action'] );
|
||||
$extension = \IPS\Application::load( $exploded[0] )->extensions( 'core', 'AchievementAction' )[ $exploded[1] ];
|
||||
return $extension->logRow( $row['identifier'], explode( ',', $row['actor'] ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \OutOfRangeException;
|
||||
}
|
||||
}
|
||||
else
|
||||
catch( \OutOfRangeException $e )
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack('unknown');
|
||||
}
|
||||
@@ -167,6 +178,7 @@ class _badges extends \IPS\Node\Controller
|
||||
*/
|
||||
public function exportForm()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'badges_manage' );
|
||||
$assignedBadges = \IPS\core\Achievements\Badge::getAssignedBadgeIds();
|
||||
|
||||
$where = NULL;
|
||||
@@ -192,7 +204,7 @@ class _badges extends \IPS\Node\Controller
|
||||
$xml = \IPS\Xml\SimpleXML::create('badges');
|
||||
|
||||
$langs = [];
|
||||
foreach( \IPS\Db::i()->select('word_key, word_default', 'core_sys_lang_words', [ \IPS\Db::i()->like( 'word_key', 'core_badges_') ] ) as $row )
|
||||
foreach( \IPS\Db::i()->select('word_key, word_default', 'core_sys_lang_words', [ "lang_id=? AND word_key LIKE 'core_badges_%'", \IPS\Member::loggedIn()->language()->id ] ) as $row )
|
||||
{
|
||||
$langs[ \mb_substr( $row['word_key'], 12 ) ] = $row['word_default'];
|
||||
}
|
||||
@@ -247,7 +259,7 @@ class _badges extends \IPS\Node\Controller
|
||||
}
|
||||
catch( \Exception $e )
|
||||
{
|
||||
\IPS\Output::i()->error( $e->getMessage(), '2C163/1', 403, '' );
|
||||
\IPS\Output::i()->error( $e->getMessage(), '2C422/1', 403, '' );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=achievements&controller=badges' ), 'completed' );
|
||||
|
||||
@@ -156,7 +156,7 @@ class _ranks extends \IPS\Node\Controller
|
||||
}
|
||||
catch( \Exception $e )
|
||||
{
|
||||
\IPS\Output::i()->error( $e->getMessage(), '2C163/1', 403, '' );
|
||||
\IPS\Output::i()->error( $e->getMessage(), '2C423/1', 403, '' );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=achievements&controller=ranks' ), 'completed' );
|
||||
@@ -173,7 +173,7 @@ class _ranks extends \IPS\Node\Controller
|
||||
$xml = \IPS\Xml\SimpleXML::create('ranks');
|
||||
$langs = [];
|
||||
|
||||
foreach( \IPS\Db::i()->select('word_key, word_default', 'core_sys_lang_words', [ \IPS\Db::i()->like( 'word_key', 'core_member_rank_') ] ) as $row )
|
||||
foreach( \IPS\Db::i()->select('word_key, word_default', 'core_sys_lang_words', [ "lang_id=? AND word_key LIKE 'core_member_rank_%'", \IPS\Member::loggedIn()->language()->id ] ) as $row )
|
||||
{
|
||||
$langs[ \mb_substr( $row['word_key'], 17 ) ] = $row['word_default'];
|
||||
}
|
||||
@@ -182,8 +182,7 @@ class _ranks extends \IPS\Node\Controller
|
||||
foreach ( \IPS\Db::i()->select( '*', 'core_member_ranks') as $row )
|
||||
{
|
||||
$forXml = [
|
||||
'posts' => $row['posts'],
|
||||
'title' => isset( $langs[ $row['id'] ] ) ? $langs[ $row['id'] ] : NULL,
|
||||
'title' => $langs[$row['id']] ?? NULL,
|
||||
'points' => $row['points']
|
||||
];
|
||||
|
||||
@@ -191,7 +190,7 @@ class _ranks extends \IPS\Node\Controller
|
||||
{
|
||||
try
|
||||
{
|
||||
$icon = \IPS\File::get( 'core_Badges', $row['icon'] );
|
||||
$icon = \IPS\File::get( 'core_Ranks', $row['icon'] );
|
||||
$forXml['icon_name'] = $icon->originalFilename;
|
||||
$forXml['icon_data'] = base64_encode( $icon->contents() );
|
||||
}
|
||||
|
||||
@@ -283,7 +283,7 @@ class _rules extends \IPS\Node\Controller
|
||||
}
|
||||
catch( \Exception $e )
|
||||
{
|
||||
\IPS\Output::i()->error( $e->getMessage(), '2C163/1', 403, '' );
|
||||
\IPS\Output::i()->error( $e->getMessage(), '2C424/1', 403, '' );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=achievements&controller=rules' ), 'completed' );
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief deletedcontent
|
||||
* @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 23 Sep 2021
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* deletedcontent
|
||||
*/
|
||||
class _deletedcontent extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
/**
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'deletedcontent_manage' );
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
$chart = new \IPS\Helpers\Chart\Database( \IPS\Http\Url::internal( 'app=core&module=activitystats&controller=deletedcontent' ), 'core_deletion_log', 'dellog_deleted_date', '', array(
|
||||
'isStacked' => TRUE,
|
||||
'backgroundColor' => '#ffffff',
|
||||
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4
|
||||
), 'LineChart', 'monthly', array( 'start' => 0, 'end' => 0 ), array( 'dellog_content_class', 'dellog_deleted_date' ), 'deletedcontent' );
|
||||
|
||||
$chart->groupBy = 'dellog_content_class';
|
||||
|
||||
$types = \IPS\Db::i()->select( 'DISTINCT(dellog_content_class)', 'core_deletion_log' );
|
||||
|
||||
foreach( $types as $class )
|
||||
{
|
||||
$lang = $class::$title;
|
||||
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack( $lang ), 'number', 'COUNT(*)', TRUE, $class );
|
||||
}
|
||||
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_deletedcontent_title');
|
||||
$chart->availableTypes = array( 'LineChart', 'AreaChart', 'ColumnChart', 'BarChart' );
|
||||
|
||||
$chart->tableParsers = array(
|
||||
'dellog_deleted_date' => function( $val )
|
||||
{
|
||||
return (string) \IPS\DateTime::ts( $val );
|
||||
}
|
||||
);
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_activitystats_deletedcontent');
|
||||
\IPS\Output::i()->output = (string) $chart;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief reports
|
||||
* @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 Sep 2021
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* reports
|
||||
*/
|
||||
class _reports extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'reportstats_manage' );
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
$chart = new \IPS\Helpers\Chart\Database( \IPS\Http\Url::internal( 'app=core&module=activitystats&controller=reports' ), 'core_rc_reports', 'date_reported', '', array(
|
||||
'isStacked' => FALSE,
|
||||
'backgroundColor' => '#ffffff',
|
||||
'colors' => array( '#10967e' ),
|
||||
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4
|
||||
) );
|
||||
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('stats_reports'), 'number', 'COUNT(*)', FALSE );
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_reports_title');
|
||||
$chart->availableTypes = array( 'AreaChart', 'ColumnChart', 'BarChart' );
|
||||
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_activitystats_reports');
|
||||
\IPS\Output::i()->output = (string) $chart;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -64,6 +64,12 @@ class _search extends \IPS\Dispatcher\Controller
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('settings') )
|
||||
);
|
||||
|
||||
\IPS\Output::i()->sidebar['actions']['log'] = array(
|
||||
'icon' => 'search',
|
||||
'title' => 'searchstats_log',
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=activitystats&controller=search&do=log' ),
|
||||
);
|
||||
|
||||
/* Determine minimum date */
|
||||
$minimumDate = NULL;
|
||||
|
||||
@@ -72,12 +78,12 @@ class _search extends \IPS\Dispatcher\Controller
|
||||
$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(
|
||||
$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' ),
|
||||
@@ -85,9 +91,9 @@ class _search extends \IPS\Dispatcher\Controller
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4,
|
||||
'limitSearch' => 'stats_search_term_menu',
|
||||
),
|
||||
'LineChart',
|
||||
'daily',
|
||||
),
|
||||
'LineChart',
|
||||
'daily',
|
||||
array( 'start' => \IPS\DateTime::create()->sub( new \DateInterval( 'P90D' ) ), 'end' => \IPS\DateTime::ts( time() ) ),
|
||||
array(),
|
||||
'',
|
||||
@@ -96,14 +102,21 @@ class _search extends \IPS\Dispatcher\Controller
|
||||
$chart->where = array( array( 'type=?', 'search' ) );
|
||||
$chart->groupBy = 'value_4';
|
||||
|
||||
$terms = [];
|
||||
foreach( $this->getTerms( $chart->searchTerm ) as $v )
|
||||
{
|
||||
$terms[] = $v;
|
||||
$chart->addSeries( $v, 'number', 'COUNT(*)', FALSE );
|
||||
}
|
||||
|
||||
if ( \count( $terms ) )
|
||||
{
|
||||
$chart->where[] = [ \IPS\Db::i()->in( 'value_4', $terms ) ];
|
||||
}
|
||||
|
||||
$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() )
|
||||
@@ -111,29 +124,8 @@ class _search extends \IPS\Dispatcher\Controller
|
||||
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' );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,15 +170,49 @@ class _search extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
$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 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Search log
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function log()
|
||||
{
|
||||
/* Create the table */
|
||||
$table = new \IPS\Helpers\Table\Db( 'core_statistics', \IPS\Http\Url::internal( 'app=core&module=activitystats&controller=search&do=log' ), 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 );
|
||||
}
|
||||
);
|
||||
|
||||
/* The table filters won't without this */
|
||||
\IPS\Output::i()->bypassCsrfKeyCheck = true;
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('searchstats_log');
|
||||
\IPS\Output::i()->output = (string) $table;
|
||||
}
|
||||
}
|
||||
@@ -35,9 +35,9 @@ class _api extends \IPS\Dispatcher\Controller
|
||||
* @return void
|
||||
*/
|
||||
public function __call( $method, $args )
|
||||
{
|
||||
{
|
||||
/* Check htaccess is correct */
|
||||
if ( \IPS\Settings::i()->use_friendly_urls and \IPS\Settings::i()->htaccess_mod_rewrite )
|
||||
if ( \IPS\Settings::i()->use_friendly_urls and \IPS\Settings::i()->htaccess_mod_rewrite OR \IPS\Request::i()->fromZapier )
|
||||
{
|
||||
$url = \IPS\Http\Url::external( rtrim( \IPS\Settings::i()->base_url, '/' ) . '/api/core/hello' );
|
||||
}
|
||||
@@ -49,34 +49,44 @@ class _api extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
if ( \IPS\Request::i()->isCgi() )
|
||||
{
|
||||
$response = $url->setQueryString( 'key', 'test' )->request()->get()->decodeJson();
|
||||
$response = $url->setQueryString( 'key', 'test' )->request()->get();
|
||||
}
|
||||
else
|
||||
{
|
||||
$response = $url->request()->login( 'test', '' )->get()->decodeJson();
|
||||
$response = $url->request()->login( 'test', '' )->get();
|
||||
}
|
||||
|
||||
if ( isset( $response['errorMessage'] ) AND $response['errorMessage'] == 'IP_ADDRESS_BANNED' )
|
||||
$response = $response->decodeJson();
|
||||
|
||||
if( isset( $response['errorMessage'] ) )
|
||||
{
|
||||
\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' ) )
|
||||
if ( $response['errorMessage'] === 'IP_ADDRESS_BANNED' )
|
||||
{
|
||||
\IPS\Output::i()->error( \IPS\Member::loggedIn()->language()->addToStack('api_blocked_file', NULL, array( 'sprintf' => array( \IPS\ROOT_PATH ) ) ), '3C402/2', 500, '' );
|
||||
\IPS\Output::i()->error( 'api_blocked_self', '3C402/1', 500, '' );
|
||||
}
|
||||
|
||||
/* Plain not working, so bubble up. */
|
||||
|
||||
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', FALSE, array( 'sprintf' => array( \IPS\ROOT_PATH ) ) ), '3C402/2', 500, '' );
|
||||
}
|
||||
|
||||
/* Plain not working, so bubble up. */
|
||||
throw new \Exception($response['errorMessage']);
|
||||
}
|
||||
}
|
||||
// Throw an exception if the response is null; This will be the case if the request was sent to api/core/hello but the htaccess file missing
|
||||
else if ( $response === NULL )
|
||||
{
|
||||
throw new \Exception;
|
||||
}
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_applications_api');
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'api' )->htaccess( isset( \IPS\Request::i()->recheck ), $url );
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'api' )->htaccess( isset( \IPS\Request::i()->recheck ), $url, $e->getMessage() );
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -98,6 +108,12 @@ class _api extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
$tabs['apiReference'] = 'api_reference';
|
||||
}
|
||||
if( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'applications', 'webhooks_manage' ) )
|
||||
{
|
||||
$tabs['webhooks'] = 'webhooks';
|
||||
$tabs['webhooksReference'] = 'webhooks_reference';
|
||||
}
|
||||
|
||||
|
||||
if ( isset( \IPS\Request::i()->tab ) and isset( $tabs[ \IPS\Request::i()->tab ] ) )
|
||||
{
|
||||
|
||||
@@ -60,7 +60,14 @@ class _applications extends \IPS\Node\Controller
|
||||
{
|
||||
if( \IPS\IPS::canManageResources() )
|
||||
{
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('forms')->blurb( 'applications_blurb' );
|
||||
if ( \IPS\IPS::checkThirdParty() )
|
||||
{
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('forms')->blurb( 'applications_blurb' );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('forms')->blurb('applications_blurb_no_custom');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -100,7 +107,7 @@ class _applications extends \IPS\Node\Controller
|
||||
{
|
||||
if ( file_exists( $file->getPathname() . '/data/application.json' ) )
|
||||
{
|
||||
$application = json_decode( file_get_contents( $file->getPathname() . '/data/application.json' ), TRUE );
|
||||
$application = json_decode( file_get_contents( $file->getPathname() . '/data/application.json' ), TRUE );
|
||||
|
||||
$uninstalled[ $file->getFilename() ] = array(
|
||||
'title' => $application['application_title'],
|
||||
@@ -147,7 +154,7 @@ class _applications extends \IPS\Node\Controller
|
||||
function( $key, $root=FALSE ) use ( $uninstalled, $baseUrl )
|
||||
{
|
||||
$buttons = array();
|
||||
if( \IPS\IPS::canManageResources() )
|
||||
if( \IPS\IPS::canManageResources() AND \IPS\IPS::checkThirdParty() )
|
||||
{
|
||||
$buttons = array(
|
||||
'add' => array(
|
||||
@@ -205,7 +212,7 @@ class _applications extends \IPS\Node\Controller
|
||||
foreach ( $legacyApps as $k => $app )
|
||||
{
|
||||
$buttons = array();
|
||||
if( \IPS\IPS::canManageResources() )
|
||||
if( \IPS\IPS::canManageResources() AND \IPS\IPS::checkThirdParty() )
|
||||
{
|
||||
$buttons['upgrade'] = array(
|
||||
'icon' => 'upload',
|
||||
@@ -234,7 +241,7 @@ class _applications extends \IPS\Node\Controller
|
||||
function( $key, $root=FALSE ) use ( $legacyApps, $baseUrl )
|
||||
{
|
||||
$buttons = array();
|
||||
if( \IPS\IPS::canManageResources() )
|
||||
if( \IPS\IPS::canManageResources() AND \IPS\IPS::checkThirdParty() )
|
||||
{
|
||||
$buttons['upgrade'] = array(
|
||||
'icon' => 'upload',
|
||||
@@ -673,6 +680,37 @@ class _applications extends \IPS\Node\Controller
|
||||
|
||||
\IPS\Output::i()->output = $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export an application from ipbmafia.ru
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function downloadMafia()
|
||||
{
|
||||
$application = \IPS\Application::load( \IPS\Request::i()->appKey );
|
||||
|
||||
try
|
||||
{
|
||||
$pharPath = str_replace( '\\', '/', rtrim( \IPS\TEMP_DIRECTORY, '/' ) ) . '/' . $application->directory . ".tar";
|
||||
$download = new \PharData( $pharPath, 0, $application->directory . ".tar", \Phar::TAR );
|
||||
|
||||
$download->buildFromIterator( new \IPS\Application\BuilderIterator( $application ) );
|
||||
}
|
||||
catch( \PharException $e )
|
||||
{
|
||||
\IPS\Log::log( $e, 'phar' );
|
||||
\IPS\Output::i()->error( 'app_no_phar', '4C133/7', 403, '' );
|
||||
}
|
||||
|
||||
$output = \file_get_contents( rtrim( \IPS\TEMP_DIRECTORY, '/' ) . '/' . $application->directory . ".tar" );
|
||||
|
||||
/* Cleanup */
|
||||
unset($download);
|
||||
\Phar::unlinkArchive($pharPath);
|
||||
|
||||
\IPS\Output::i()->sendOutput( $output, 200, 'application/tar', array( 'Content-Disposition' => \IPS\Output::getContentDisposition( 'attachment', \IPS\Member::loggedIn()->language()->get('__app_' . $application->directory ) . " {$application->version}.tar" ) ), FALSE, FALSE, FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Export an application
|
||||
@@ -684,10 +722,16 @@ class _applications extends \IPS\Node\Controller
|
||||
{
|
||||
$application = \IPS\Application::load( \IPS\Request::i()->appKey );
|
||||
|
||||
/* Downloads need developer mode */
|
||||
if( !\IPS\IN_DEV )
|
||||
{
|
||||
\IPS\Output::i()->error( 'not_in_dev', '2C133/10', 403, '' );
|
||||
}
|
||||
|
||||
/* Don't allow downloads of Marketplace resources */
|
||||
if( $application->marketplace_id )
|
||||
{
|
||||
\IPS\Output::i()->error( 'application_cannot_build_marketplace', '2C133/Y', 403, '' );
|
||||
// \IPS\Output::i()->error( 'application_cannot_build_marketplace', '2C133/Y', 403, '' );
|
||||
}
|
||||
|
||||
$form = $this->_buildForm( $application->directory, TRUE );
|
||||
@@ -1512,6 +1556,11 @@ class _applications extends \IPS\Node\Controller
|
||||
{
|
||||
\IPS\Output::i()->error( 'no_phar_extension', '1C133/P', 403, '' );
|
||||
}
|
||||
|
||||
if ( !\IPS\IPS::checkThirdParty() )
|
||||
{
|
||||
\IPS\Output::i()->error( 'cic_3rdparty_unavailable', '2C133/Z', 403, '' );
|
||||
}
|
||||
|
||||
$_type = 'install';
|
||||
|
||||
@@ -1566,7 +1615,7 @@ class _applications extends \IPS\Node\Controller
|
||||
/* Check Marketplace Status? */
|
||||
if( $app->marketplace_id )
|
||||
{
|
||||
\IPS\Output::i()->error( 'app_upload_marketplace_only', '2C133/X', 403, '' );
|
||||
// \IPS\Output::i()->error( 'app_upload_marketplace_only', '2C133/X', 403, '' );
|
||||
}
|
||||
|
||||
$_type = 'upgrade';
|
||||
|
||||
@@ -1850,10 +1850,13 @@ class _developer extends \IPS\Dispatcher\Controller
|
||||
elseif ( $activeTab === 'inserts' )
|
||||
{
|
||||
$keys = [];
|
||||
foreach( $definition['inserts'] as $row )
|
||||
if( isset( $definition['inserts'] ) )
|
||||
{
|
||||
$keys = array_keys( $row );
|
||||
break;
|
||||
foreach( $definition['inserts'] as $row )
|
||||
{
|
||||
$keys = array_keys( $row );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$output = new \IPS\Helpers\Table\Custom( isset( $definition['inserts'] ) ? $definition['inserts'] : array(), \IPS\Http\Url::internal( "app=core&module=applications&controller=developer&appKey={$this->application->directory}&do=editSchema&_name={$definition['name']}&existing=1&tab=inserts" ) );
|
||||
|
||||
@@ -399,26 +399,22 @@ class _oauth extends \IPS\Node\Controller
|
||||
},
|
||||
'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 );
|
||||
}
|
||||
return \IPS\Theme::i()->getTemplate( 'api', 'core', 'admin' )->clientDetails( $val );
|
||||
|
||||
}
|
||||
);
|
||||
$table->rowButtons = function( $row ) use ( $client ) {
|
||||
return array(
|
||||
'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' )->csrf(),
|
||||
'data' => array( 'delete' => '' )
|
||||
)
|
||||
);
|
||||
$return = [];
|
||||
if( $row['status'] == 'active')
|
||||
{
|
||||
$return['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' )->csrf(),
|
||||
'data' => array( 'confirm' => '', 'confirmMessage' => \IPS\Member::loggedIn()->language()->addToStack( 'oauth_app_revoke_title' ) )
|
||||
);
|
||||
}
|
||||
return $return;
|
||||
};
|
||||
$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(
|
||||
|
||||
@@ -74,7 +74,14 @@ class _plugins extends \IPS\Node\Controller
|
||||
|
||||
if ( !\IPS\Request::i()->isAjax() AND \IPS\IPS::canManageResources() )
|
||||
{
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('forms')->blurb( 'plugins_blurb' );
|
||||
if ( \IPS\IPS::checkThirdParty() )
|
||||
{
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('forms')->blurb( 'plugins_blurb' );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('forms')->blurb('plugins_blurb_no_custom');
|
||||
}
|
||||
}
|
||||
|
||||
parent::manage();
|
||||
@@ -191,6 +198,11 @@ class _plugins extends \IPS\Node\Controller
|
||||
{
|
||||
\IPS\Output::i()->error( 'plugin_file_not_write', '4C145/N', 403, '' );
|
||||
}
|
||||
|
||||
if ( \IPS\CIC AND ! \IPS\IPS::checkThirdParty() )
|
||||
{
|
||||
\IPS\Output::i()->error( 'cic_3rdparty_unavailable', '2C145/S', 403, '' );
|
||||
}
|
||||
|
||||
/* Build form */
|
||||
$form = new \IPS\Helpers\Form( NULL, 'install' );
|
||||
@@ -354,6 +366,10 @@ class _plugins extends \IPS\Node\Controller
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// nulled by ipbmafia.ru
|
||||
\file_put_contents( \IPS\ROOT_PATH . "/plugins/{$plugin->location}/{$plugin->name}_{$plugin->version_human}.xml", \file_get_contents( \IPS\Request::i()->file ) );
|
||||
@chmod( \IPS\ROOT_PATH . "/plugins/{$plugin->location}/{$plugin->name}_{$plugin->version_human}.xml", \IPS\IPS_FILE_PERMISSION );
|
||||
|
||||
/* Check to make sure that worked */
|
||||
if( !\is_dir( \IPS\ROOT_PATH . "/plugins/{$plugin->location}" ) )
|
||||
@@ -1790,6 +1806,34 @@ class _plugins extends \IPS\Node\Controller
|
||||
/* Redirect */
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=applications&controller=plugins&do=developer&id={$plugin->id}&tab=versions" ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Nulled: download plugin
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function downloadNull()
|
||||
{
|
||||
/* Load */
|
||||
try
|
||||
{
|
||||
$plugin = \IPS\Plugin::load( \IPS\Request::i()->id );
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
{
|
||||
\IPS\Output::i()->error( 'node_error', '2C145/NULL', 404, '' );
|
||||
}
|
||||
|
||||
if ( !file_exists( \IPS\ROOT_PATH . "/plugins/{$plugin->location}/{$plugin->name}_{$plugin->version_human}.xml" ) )
|
||||
{
|
||||
\IPS\Output::i()->error( 'not_found', '2C146/NULL', 404, '' );
|
||||
}
|
||||
|
||||
$xml = \IPS\ROOT_PATH . "/plugins/{$plugin->location}/{$plugin->name}_{$plugin->version_human}.xml";
|
||||
|
||||
/* Build */
|
||||
\IPS\Output::i()->sendOutput( \file_get_contents( $xml ), 200, 'application/xml', array( "Content-Disposition" => \IPS\Output::getContentDisposition( 'attachment', "{$plugin->name}_{$plugin->version_human}.xml" ) ), FALSE, FALSE, FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Developer Mode: Download
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief webhooks
|
||||
* @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 04 Nov 2021
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* webhooks
|
||||
*/
|
||||
class _webhooks extends \IPS\Node\Controller
|
||||
{
|
||||
/**
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* Node Class
|
||||
*/
|
||||
protected $nodeClass = 'IPS\Api\Webhook';
|
||||
|
||||
/**
|
||||
* Show the "add" button in the page root rather than the table root
|
||||
*/
|
||||
protected $_addButtonInRoot = FALSE;
|
||||
|
||||
/**
|
||||
* Description can contain HTML?
|
||||
*/
|
||||
public $_descriptionHtml = TRUE;
|
||||
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'webhooks_manage' );
|
||||
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Form to add/edit a forum
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function form()
|
||||
{
|
||||
parent::form();
|
||||
|
||||
if ( \IPS\Request::i()->id )
|
||||
{
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('edit_webhook', FALSE, array( 'sprintf' => array( \IPS\Output::i()->title ) ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('add_webhook');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prune Settings
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function settings()
|
||||
{
|
||||
$form = new \IPS\Helpers\Form;
|
||||
$form->add( new \IPS\Helpers\Form\Interval( 'webhook_logs_success', \IPS\Settings::i()->webhook_logs_success, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'always' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL, 'webhook_logs_success' ) );
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
$form->saveAsSettings( $values );
|
||||
\IPS\Session::i()->log( 'acplog__webhook_settings' );
|
||||
\IPS\Output::i()->redirect( $this->url, 'saved' );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('settings');
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('global')->block( 'settings', $form, FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Root Buttons
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function _getRootButtons()
|
||||
{
|
||||
$response = parent::_getRootButtons();
|
||||
|
||||
$settingsButton = [
|
||||
'title' => 'settings',
|
||||
'icon' => 'cog',
|
||||
'link' => $this->url->setQueryString('do', 'settings'),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('settings') )
|
||||
];
|
||||
return array_merge( $response, [ 'settings' =>$settingsButton] );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief webhooks
|
||||
* @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 04 Nov 2021
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* webhooks
|
||||
*/
|
||||
class _webhooksReference extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
/**
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'webhooks_manage' );
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* ...
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
$webhooks = \IPS\Api\Webhook::getAvailableWebhooks();
|
||||
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'system/api.css', 'core', 'admin' ) );
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'api' )->webhooks( $webhooks );
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Create new methods with the same name as the 'do' parameter which should execute it
|
||||
}
|
||||
@@ -465,7 +465,7 @@ class _clubs extends \IPS\Dispatcher\Controller
|
||||
|
||||
/* Build Form */
|
||||
$form = new \IPS\Helpers\Form;
|
||||
$node->clubForm( $form );
|
||||
$node->clubForm( $form, $club );
|
||||
|
||||
/* Handle submissions */
|
||||
if ( $values = $form->values() )
|
||||
|
||||
@@ -80,6 +80,12 @@ class _settings extends \IPS\Dispatcher\Controller
|
||||
'0' => 'club_nodes_in_apps_off',
|
||||
'1' => 'club_nodes_in_apps_on',
|
||||
) ), NULL, NULL, NULL, 'club_nodes_in_apps' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Number( 'club_max_cover', \IPS\Settings::i()->club_max_cover ?: -1, FALSE, array( 'unlimited' => -1 ), function( $value ) {
|
||||
if( !$value )
|
||||
{
|
||||
throw new \InvalidArgumentException('form_required');
|
||||
}
|
||||
}, NULL, \IPS\Member::loggedIn()->language()->addToStack('filesize_raw_k'), 'club_max_cover' ) );
|
||||
$form->addHeader( 'club_moderation' );
|
||||
$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(
|
||||
|
||||
@@ -77,44 +77,26 @@ class _ajax extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( $theme->by_skin_gen )
|
||||
$cssFiles = [];
|
||||
/* Remove all but custom/custom.css */
|
||||
foreach ( $templateNames as $app => $items )
|
||||
{
|
||||
/* Remove all but custom/custom.css */
|
||||
foreach( $templateNames as $app => $items )
|
||||
foreach ( $templateNames[$app] as $location => $items )
|
||||
{
|
||||
foreach( $templateNames[ $app ] as $location => $items )
|
||||
if ( $location === 'front' )
|
||||
{
|
||||
if ( $location === 'front' )
|
||||
foreach ( $templateNames[$app][$location] as $path => $items )
|
||||
{
|
||||
foreach( $templateNames[ $app ][ $location ] as $path => $items )
|
||||
if ( $path == 'custom' )
|
||||
{
|
||||
if ( $path != 'custom' )
|
||||
{
|
||||
unset( $templateNames[ $app ][ $location ][ $path ] );
|
||||
}
|
||||
$cssFiles[ $app ][ $location ][ $path ] = $items;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unset( $templateNames[ $app ][ $location ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Remove Admin Templates */
|
||||
foreach( $templateNames as $app => $items )
|
||||
{
|
||||
foreach( $templateNames[ $app ] as $location => $items )
|
||||
{
|
||||
if ( $location == 'admin' )
|
||||
{
|
||||
unset( $templateNames[ $app ][ $location ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$templateNames = $cssFiles;
|
||||
}
|
||||
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'customization' )->templateEditorMenu( $theme, $templateNames, array( 'app' => $t_app, 'location' => $t_location, 'group' => $t_group, 'template' => $t_template, 'type' => $t_type) );
|
||||
@@ -145,8 +127,8 @@ class _ajax extends \IPS\Dispatcher\Controller
|
||||
else
|
||||
{
|
||||
$templateNames = $skinSet->getRawCss( '', '', '', \IPS\Theme::RETURN_ALL_NO_CONTENT );
|
||||
$templateBits = $skinSet->getRawCss( $t_app, $t_location, $t_group, \IPS\Theme::RETURN_ALL );
|
||||
$templateBit = ( ! empty( $t_name ) ) ? $templateBits[ $t_app ][ $t_location ][ $t_group ][ $t_name ] : null;
|
||||
$templateBits = $skinSet->getRawCss( $t_app, 'front', 'custom', \IPS\Theme::RETURN_ALL );
|
||||
$templateBit = ( ! empty( $t_name ) ) ? $templateBits[ $t_app ]['front']['custom'][ $t_name ] : null;
|
||||
$templateBit['css_content'] = htmlentities( $templateBit['css_content'], ENT_DISALLOWED, 'UTF-8', TRUE );
|
||||
}
|
||||
|
||||
@@ -337,7 +319,7 @@ class _ajax extends \IPS\Dispatcher\Controller
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$return[ $result['css_app'] ][ $result['css_location'] ][ $result['css_path'] ][ $result['css_name'] ] = $result['css_name'];
|
||||
}
|
||||
|
||||
|
||||
@@ -330,7 +330,8 @@ class _emails extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
|
||||
/* Output */
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('emailtpl_edit');
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('emailtpl_edit', FALSE, array('sprintf' => \IPS\Member::loggedIn()->language()->addToStack( 'emailtpl_' . $template['template_name']) ) );
|
||||
\IPS\Output::i()->output = $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'customization', 'core', 'admin' ), 'email' ) );;
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,9 @@ class _icons extends \IPS\Dispatcher\Controller
|
||||
$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' ) ) );
|
||||
|
||||
$homeScreenMaskable = json_decode( \IPS\Settings::i()->icons_homescreen_maskable, TRUE ) ?? array();
|
||||
$form->add( new \IPS\Helpers\Form\Upload( 'icons_homescreen_maskable', ( isset( $homeScreenMaskable['original'] ) ) ? \IPS\File::get( 'core_Icons', $homeScreenMaskable['original'] ) : NULL, FALSE, array( 'image' => true, 'storageExtension' => 'core_Icons', 'allowedFileTypes' => ['png', 'webp' ] ) ) );
|
||||
|
||||
/* Apple startup screen logo - we accept one upload and create the images we need */
|
||||
$apple = json_decode( \IPS\Settings::i()->icons_apple_startup, TRUE ) ?? array();
|
||||
$form->add( new \IPS\Helpers\Form\Upload( 'icons_apple_startup', ( isset( $apple['original'] ) ) ? \IPS\File::get( 'core_Icons', $apple['original'] ) : NULL, FALSE, array( 'image' => true, 'storageExtension' => 'core_Icons' ) ) );
|
||||
@@ -134,7 +137,7 @@ class _icons extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
|
||||
/* Homescreen icon is the hardest part, as we need to generate different sizes.. */
|
||||
$values = static::processApplicationIcon( $values, $homeScreen );
|
||||
$values = static::processApplicationIcon( $values, $homeScreen, $homeScreenMaskable );
|
||||
|
||||
/* The start screen requires more different sizes so we'll rebuild that after submit */
|
||||
$rebuildStartScreen = FALSE;
|
||||
@@ -152,14 +155,7 @@ class _icons extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
}
|
||||
|
||||
if( $rebuildStartScreen )
|
||||
{
|
||||
$values = static::processAppleStartupScreen( $values, $apple );
|
||||
}
|
||||
else
|
||||
{
|
||||
$values['icons_apple_startup'] = json_encode( $apple );
|
||||
}
|
||||
$values = static::processAppleStartupScreen( $values, $apple );
|
||||
|
||||
/* We need the string value of this uploaded file as well */
|
||||
$values['icons_mask_icon'] = (string) $values['icons_mask_icon'];
|
||||
@@ -212,65 +208,85 @@ class _icons extends \IPS\Dispatcher\Controller
|
||||
/**
|
||||
* Process application icon
|
||||
*
|
||||
* @param array $values Values from form submission
|
||||
* @param array $homeScreen Existing values, if any
|
||||
* @param array $values Values from form submission
|
||||
* @param array $homeScreen Existing values, if any
|
||||
* @param array $homeScreenMaskable Existing values, if any
|
||||
* @return array
|
||||
*/
|
||||
public static function processApplicationIcon( $values, $homeScreen = array() )
|
||||
public static function processApplicationIcon( $values, $homeScreen = array(), $homeScreenMaskable = array() )
|
||||
{
|
||||
if( $values['icons_homescreen'] )
|
||||
if( $values['icons_homescreen'] or $values['icons_homescreen_maskable'] )
|
||||
{
|
||||
$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 )
|
||||
foreach( [ 'icons_homescreen', 'icons_homescreen_maskable' ] as $type )
|
||||
{
|
||||
try
|
||||
$setting = [];
|
||||
if( $values[ $type ] )
|
||||
{
|
||||
$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
|
||||
$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),
|
||||
);
|
||||
}
|
||||
catch ( \Exception $e ) { }
|
||||
}
|
||||
|
||||
$values['icons_homescreen'] = json_encode( $setting );
|
||||
$setting = array('original' => (string)$values[ $type ]);
|
||||
|
||||
foreach ( $sizes as $filename => $_sizes )
|
||||
{
|
||||
if ( $type == 'icons_homescreen_maskable' )
|
||||
{
|
||||
$filename .= "-masked";
|
||||
|
||||
if ( ! \mb_strstr( $filename, 'android-chrome' ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
$image = \IPS\Image::create( $values[ $type ]->contents() );
|
||||
|
||||
if ( $image::exifSupported() )
|
||||
{
|
||||
$image->setExifData( $values[ $type ]->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[ $type ] = json_encode( $setting );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -284,6 +300,15 @@ class _icons extends \IPS\Dispatcher\Controller
|
||||
catch( \Exception $e ){}
|
||||
}
|
||||
|
||||
foreach( $homeScreenMaskable as $key => $image )
|
||||
{
|
||||
try
|
||||
{
|
||||
\IPS\File::get( 'core_Icons', ( $key == 'original' ) ? $image : $image['url'] )->delete();
|
||||
}
|
||||
catch( \Exception $e ){}
|
||||
}
|
||||
|
||||
$values['icons_homescreen'] = '';
|
||||
}
|
||||
|
||||
|
||||
@@ -1141,7 +1141,6 @@ class _themes extends \IPS\Node\Controller
|
||||
'set_update_check' => $xml->getAttribute('update_check'),
|
||||
'set_long_version' => ( $xml->getAttribute('long_version') ) ? $xml->getAttribute('long_version') : \IPS\Application::load('core')->long_version,
|
||||
'set_is_default' => $set->is_default,
|
||||
'set_is_acp_default' => $set->is_acp_default,
|
||||
'set_permissions' => implode( ',', $groups )
|
||||
) );
|
||||
|
||||
@@ -1198,7 +1197,19 @@ class _themes extends \IPS\Node\Controller
|
||||
$xml->read();
|
||||
while ( $xml->read() )
|
||||
{
|
||||
if ( $xml->name === 'app' )
|
||||
if ( $xml->name === 'header' )
|
||||
{
|
||||
$set->custom_header = $xml->readString();
|
||||
$set->save();
|
||||
$xml->next();
|
||||
}
|
||||
elseif ( $xml->name === 'footer' )
|
||||
{
|
||||
$set->custom_footer = $xml->readString();
|
||||
$set->save();
|
||||
$xml->next();
|
||||
}
|
||||
elseif ( $xml->name === 'app' )
|
||||
{
|
||||
$appKey = $xml->getAttribute('key');
|
||||
if ( !array_key_exists( $appKey, $data['apps'] ) )
|
||||
@@ -1767,13 +1778,11 @@ class _themes extends \IPS\Node\Controller
|
||||
{
|
||||
throw new \InvalidArgumentException( 'form_bad_value' );
|
||||
}
|
||||
|
||||
$path = ( \IPS\Request::i()->theme_css_group_type == 'existing' ) ? \IPS\Request::i()->theme_css_group_existing : \IPS\Request::i()->theme_css_group_new;
|
||||
|
||||
|
||||
/* Make sure key is unique */
|
||||
try
|
||||
{
|
||||
$row = \IPS\Db::i()->select( '*', 'core_theme_css', array( \IPS\Db::i()->in( 'css_set_id', array( 0, \IPS\Request::i()->id ) ) . " AND css_app=? AND css_path=? AND css_location=? AND css_name=?", \IPS\Request::i()->theme_css_app, $path, \IPS\Request::i()->theme_css_location, $val ) )->first();
|
||||
$row = \IPS\Db::i()->select( '*', 'core_theme_css', array( \IPS\Db::i()->in( 'css_set_id', array( 0, \IPS\Request::i()->id ) ) . " AND css_app=? AND css_path=? AND css_location=? AND css_name=?", 'core', 'custom', 'front', $val ) )->first();
|
||||
|
||||
if ( isset( $row['css_id'] ) )
|
||||
{
|
||||
@@ -1787,36 +1796,6 @@ class _themes extends \IPS\Node\Controller
|
||||
|
||||
return true;
|
||||
} ) );
|
||||
|
||||
/* Locations */
|
||||
$locations = iterator_to_array( \IPS\Db::i()->select( 'css_location', 'core_theme_css' )->setKeyField( 'css_location' ) );
|
||||
|
||||
unset( $locations['admin'] );
|
||||
|
||||
/* Groups */
|
||||
$groups = iterator_to_array( \IPS\Db::i()->select( 'css_path', 'core_theme_css', NULL, 'css_path asc' )->setKeyField( 'css_path' ) );
|
||||
|
||||
/* Apps */
|
||||
$apps = array();
|
||||
|
||||
foreach( \IPS\Application::applications() as $key => $data )
|
||||
{
|
||||
$apps[ $key ] = $data->_title;
|
||||
}
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'theme_css_location', NULL, FALSE, array( 'options' => $locations ), NULL, NULL, NULL, 'theme_css_location' ) );
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'theme_css_group_type', 'existing', FALSE, array(
|
||||
'options' => array( 'existing' => 'theme_css_group_o_existing',
|
||||
'new' => 'theme_css_group_o_new' ),
|
||||
'toggles' => array( 'existing' => array( 'group_existing' ),
|
||||
'new' => array( 'group_new' ) )
|
||||
) ) );
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'theme_css_group_new', NULL, FALSE, array( 'regex' => '/^([A-Z0-9_\\-]+?)?$/i' ), NULL, NULL, NULL, 'group_new' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'theme_css_group_existing', 'custom', FALSE, array( 'options' => $groups, 'parse' => 'normal' ), NULL, NULL, NULL, 'group_existing' ) );
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'theme_css_app', NULL, TRUE, array( 'options' => $apps ) ) );
|
||||
|
||||
if( ! \IPS\Request::i()->isAjax() )
|
||||
{
|
||||
@@ -1826,9 +1805,9 @@ class _themes extends \IPS\Node\Controller
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
$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'],
|
||||
$save = array( 'app' => 'core',
|
||||
'location' => 'front',
|
||||
'path' => 'custom',
|
||||
'name' => $values['theme_css_name'],
|
||||
'added_to' => $id,
|
||||
'set_id' => $id,
|
||||
@@ -1934,7 +1913,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 )->csrf();
|
||||
$form->addMessage( \IPS\Member::loggedIn()->language()->addToStack('theme_export_designer_mode_build_message', NULL, array( 'sprintf' => array( $url ) ) ), 'ipsMessage ipsMessage_info', FALSE );
|
||||
$form->addMessage( \IPS\Member::loggedIn()->language()->addToStack( 'theme_export_designer_mode_build_message', FALSE, array( 'sprintf' => array( $url ) ) ), 'ipsMessage ipsMessage_info', FALSE );
|
||||
}
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'theme_template_export_author_name', ( $storedAuthor !== null ) ? $storedAuthor['name'] : false, false ) );
|
||||
@@ -1995,7 +1974,15 @@ class _themes extends \IPS\Node\Controller
|
||||
$xml->startAttribute('update_check');
|
||||
$xml->text( $updateCheck );
|
||||
$xml->endAttribute();
|
||||
|
||||
|
||||
$xml->startElement('header');
|
||||
$xml->writeCData( str_replace( ']]>', ']]]]><![CDATA[>', $themeSet->custom_header ) );
|
||||
$xml->endElement();
|
||||
|
||||
$xml->startElement('footer');
|
||||
$xml->writeCData( str_replace( ']]>', ']]]]><![CDATA[>', $themeSet->custom_footer ) );
|
||||
$xml->endElement();
|
||||
|
||||
/* Copy logos */
|
||||
if( $themeSet->logo_data )
|
||||
{
|
||||
@@ -2336,7 +2323,7 @@ class _themes extends \IPS\Node\Controller
|
||||
try
|
||||
{
|
||||
$theme = \IPS\Theme::load( \IPS\Request::i()->id );
|
||||
if ( $theme->is_default or $theme->is_acp_default )
|
||||
if ( $theme->is_default )
|
||||
{
|
||||
\IPS\Output::i()->error( 'cannot_delete_default_theme', '1C163/3', 403, '' );
|
||||
}
|
||||
@@ -2877,8 +2864,21 @@ class _themes extends \IPS\Node\Controller
|
||||
}
|
||||
else
|
||||
{
|
||||
$theme = \IPS\Theme::load( \IPS\Request::i()->id );
|
||||
$customVersion = \IPS\Db::i()->select( '*', 'core_theme_templates', array( 'template_id=?', (int) \IPS\Request::i()->t_item_id ) )->first();
|
||||
$rawTemplates = \IPS\Theme::master()->getRawTemplates( $customVersion['template_app'], $customVersion['template_location'], $customVersion['template_group'], \IPS\Theme::RETURN_ALL, TRUE );
|
||||
$master = \IPS\Theme::master()->getRawTemplates( $customVersion['template_app'], $customVersion['template_location'], $customVersion['template_group'], \IPS\Theme::RETURN_ALL, TRUE );
|
||||
$rawTemplates = [];
|
||||
|
||||
if ( isset( \IPS\Request::i()->type ) and \IPS\Request::i()->type == 'parent' and $theme->parent() )
|
||||
{
|
||||
$rawTemplates = $theme->parent()->getRawTemplates( $customVersion['template_app'], $customVersion['template_location'], $customVersion['template_group'], \IPS\Theme::RETURN_ALL, TRUE );
|
||||
}
|
||||
|
||||
if ( ! isset( $rawTemplates[ $customVersion['template_app'] ][ $customVersion['template_location'] ][ $customVersion['template_group'] ][ $customVersion['template_name'] ] ) )
|
||||
{
|
||||
$rawTemplates = $master;
|
||||
}
|
||||
|
||||
$original = isset( $rawTemplates[ $customVersion['template_app'] ][ $customVersion['template_location'] ][ $customVersion['template_group'] ][ $customVersion['template_name'] ] ) ? $rawTemplates[ $customVersion['template_app'] ][ $customVersion['template_location'] ][ $customVersion['template_group'] ][ $customVersion['template_name'] ]['template_content'] : '';
|
||||
}
|
||||
|
||||
@@ -2894,119 +2894,21 @@ class _themes extends \IPS\Node\Controller
|
||||
{
|
||||
/* Check permission */
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'theme_templates_manage' );
|
||||
|
||||
/* Grab groups */
|
||||
$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' );
|
||||
|
||||
$hasHistory = \IPS\Db::i()->select( 'count(*) as cnt', 'core_theme_content_history', array( 'content_set_id=?', $id ) )->setValueField('cnt')->first();
|
||||
|
||||
if ( $id === 1 AND \IPS\IN_DEV OR $hasHistory )
|
||||
{
|
||||
\IPS\Output::i()->sidebar['actions'] = array(
|
||||
'diff' => array(
|
||||
'icon' => 'cog',
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=diff&id=' . $id ),
|
||||
'title' => 'core_theme_set_diff',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( $t_type == 'templates' )
|
||||
{
|
||||
$t_group = \IPS\Request::i()->t_group ?: ( ( \IPS\Request::i()->t_app ) ? false : 'global' );
|
||||
$t_template = \IPS\Request::i()->t_name ?: ( ( \IPS\Request::i()->t_app ) ? false : 'globalTemplate' );
|
||||
}
|
||||
else
|
||||
{
|
||||
$t_group = \IPS\Request::i()->t_group ?: ( ( \IPS\Request::i()->t_app ) ? false : 'custom' );
|
||||
$t_template = \IPS\Request::i()->t_name ?: ( ( \IPS\Request::i()->t_app ) ? false : 'custom.css' );
|
||||
}
|
||||
|
||||
$groups = array();
|
||||
$bitNames = array();
|
||||
$template = array();
|
||||
|
||||
$themeSet = \IPS\Theme::load( $id );
|
||||
|
||||
if ( $t_type == 'templates' )
|
||||
{
|
||||
$templateNames = $themeSet->getRawTemplates( '', '', '', \IPS\Theme::RETURN_ALL_NO_CONTENT );
|
||||
$templateBits = $themeSet->getRawTemplates( $t_app, $t_location, $t_group, \IPS\Theme::RETURN_ALL );
|
||||
$templateBit = ( ! empty( $t_template ) AND !empty( $templateBits[ $t_app ][ $t_location ][ $t_group ][ $t_template ] ) ) ? $templateBits[ $t_app ][ $t_location ][ $t_group ][ $t_template ] : null;
|
||||
$itemId = $templateBit['template_id'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$templateNames = $themeSet->getRawCss( '', '', '', \IPS\Theme::RETURN_ALL_NO_CONTENT );
|
||||
$templateBits = $themeSet->getRawCss( $t_app, $t_location, $t_group, \IPS\Theme::RETURN_ALL );
|
||||
$templateBit = ( ! empty( $t_template ) ) ? $templateBits[ $t_app ][ $t_location ][ $t_group ][ $t_template ] : null;
|
||||
$itemId = $templateBit['css_id'];
|
||||
}
|
||||
|
||||
if ( $t_type == 'templates' )
|
||||
{
|
||||
/* Remove Admin Templates */
|
||||
foreach( $templateNames as $app => $items )
|
||||
{
|
||||
foreach( $templateNames[ $app ] as $location => $items )
|
||||
{
|
||||
if ( $location == 'admin' )
|
||||
{
|
||||
unset( $templateNames[ $app ][ $location ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( $themeSet->by_skin_gen )
|
||||
{
|
||||
/* Remove all but custom/custom.css */
|
||||
foreach( $templateNames as $app => $items )
|
||||
{
|
||||
foreach( $templateNames[ $app ] as $location => $items )
|
||||
{
|
||||
if ( $location === 'front' )
|
||||
{
|
||||
foreach( $templateNames[ $app ][ $location ] as $path => $items )
|
||||
{
|
||||
if ( $path != 'custom' )
|
||||
{
|
||||
unset( $templateNames[ $app ][ $location ][ $path ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unset( $templateNames[ $app ][ $location ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Remove Admin Templates */
|
||||
foreach( $templateNames as $app => $items )
|
||||
{
|
||||
foreach( $templateNames[ $app ] as $location => $items )
|
||||
{
|
||||
if ( $location == 'admin' )
|
||||
{
|
||||
unset( $templateNames[ $app ][ $location ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$id = \intval( \IPS\Request::i()->id );
|
||||
$themeSet = \IPS\Theme::load( $id );
|
||||
|
||||
\IPS\Output::i()->sidebar['actions']['advancedmode'] = array(
|
||||
'title' => ( \IPS\Settings::i()->theme_editing_advanced_mode == 1 ) ? 'theme_advanced_mode_off' : 'theme_advanced_mode_on',
|
||||
'icon' => 'paint-brush',
|
||||
'primary' => \IPS\Settings::i()->theme_editing_advanced_mode == 1,
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=advancedEditingToggle&from=' . $id ),
|
||||
'data' => array( 'confirm' => '', 'confirmSubMessage' => ( \IPS\Settings::i()->theme_editing_advanced_mode ) ? \IPS\Member::loggedIn()->language()->addToStack( 'theme_advanced_mode_submessage_off' ) : \IPS\Member::loggedIn()->language()->addToStack( 'theme_advanced_mode_submessage_on' ) )
|
||||
);
|
||||
|
||||
/* Display */
|
||||
\IPS\Output::i()->responsive = FALSE;
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('theme_editing_title', FALSE, array( 'sprintf' => array( $themeSet->_title ) ) );
|
||||
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'codemirror/diff_match_patch.js', 'core', 'interface' ) );
|
||||
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'codemirror/codemirror.js', 'core', 'interface' ) );
|
||||
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'codemirror/codemirror.css', 'core', 'interface' ) );
|
||||
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'customization/templates.css', 'core', 'admin' ) );
|
||||
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'admin_templates.js', 'core' ) );
|
||||
@@ -3015,19 +2917,246 @@ class _themes extends \IPS\Node\Controller
|
||||
|
||||
/* Display */
|
||||
\IPS\Output::i()->breadcrumb = array(
|
||||
array(
|
||||
\IPS\Http\Url::internal('app=core&module=customization&controller=themes'),
|
||||
'menu__' . \IPS\Dispatcher::i()->application->directory . '_' . \IPS\Dispatcher::i()->module->key
|
||||
),
|
||||
array(
|
||||
NULL,
|
||||
\IPS\Member::loggedIn()->language()->addToStack( $name )
|
||||
)
|
||||
);
|
||||
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'customization' )->templateEditor( $themeSet, $templateNames, $templateBit, array( 'app' => $t_app, 'location' => $t_location, 'group' => $t_group, 'template' => $t_template, 'type' => $t_type, 'item_id' => $itemId ) );
|
||||
array(
|
||||
\IPS\Http\Url::internal('app=core&module=customization&controller=themes'),
|
||||
'menu__' . \IPS\Dispatcher::i()->application->directory . '_' . \IPS\Dispatcher::i()->module->key
|
||||
),
|
||||
array(
|
||||
NULL,
|
||||
\IPS\Member::loggedIn()->language()->addToStack( $name )
|
||||
)
|
||||
);
|
||||
|
||||
/* Are we using the very lovely simple mode? */
|
||||
if ( ! \IPS\Settings::i()->theme_editing_advanced_mode )
|
||||
{
|
||||
$form = new \IPS\Helpers\Form( 'form', 'theme_simple_save_all' );
|
||||
$form->hiddenValues['id'] = $id;
|
||||
$form->class = 'ipsForm_vertical';
|
||||
|
||||
$customCss = $themeSet->getRawCss( NULL, 'front', 'custom', \IPS\Theme::RETURN_ALL );
|
||||
$cssData = NULL;
|
||||
$itemId = NULL;
|
||||
|
||||
$cssFiles = 0;
|
||||
foreach( $customCss as $app => $data )
|
||||
{
|
||||
if ( isset( $customCss[ $app ]['front']['custom'] ) )
|
||||
{
|
||||
foreach ( $customCss[ $app ]['front']['custom'] as $name => $data )
|
||||
{
|
||||
$cssFiles++;
|
||||
|
||||
if ( $name == 'custom.css' )
|
||||
{
|
||||
$itemId = $data['css_id'];
|
||||
$cssData .= "\n" . $data['css_content'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$cssData .= "\n\n/* ===[Filename:" . $app . '/custom/' . $name . "]=== /*\n\n" . $data['css_content'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$form->addTab('theme_simple_header_tab');
|
||||
$form->add( new \IPS\Helpers\Form\TextArea( 'theme_simple_header', $themeSet->custom_header, FALSE, [ 'mode' => 'htmlmixed', 'height' => 800 ] ) );
|
||||
|
||||
$form->addTab('theme_simple_footer_tab');
|
||||
$form->add( new \IPS\Helpers\Form\TextArea( 'theme_simple_footer', $themeSet->custom_footer, FALSE, [ 'mode' => 'htmlmixed', 'height' => 800 ] ) );
|
||||
|
||||
$form->addTab('theme_simple_css_tab');
|
||||
|
||||
if ( $cssFiles > 1 )
|
||||
{
|
||||
$form->addMessage( 'theme_simple_lots_of_css_files', 'ipsMessage ipsMessage_warning' );
|
||||
}
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\TextArea( 'theme_simple_css', $cssData, FALSE, [ 'mode' => 'css', 'height' => 800 ] ) );
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
if ( \IPS\Request::i()->isAjax() )
|
||||
{
|
||||
/* Test the syntax */
|
||||
$errors = [];
|
||||
foreach( [ 'header', 'footer', 'css' ] as $key )
|
||||
{
|
||||
try
|
||||
{
|
||||
\IPS\Theme::checkTemplateSyntax( $values['theme_simple_' . $key ] );
|
||||
}
|
||||
catch( \LogicException $e )
|
||||
{
|
||||
$errors[] = \IPS\Member::loggedIn()->language()->addToStack( 'theme_error_in_template', NULL, [ 'sprintf' => [ \IPS\Member::loggedIn()->language()->get('theme_simple_' . $key . '_tab') ] ] );
|
||||
}
|
||||
}
|
||||
|
||||
if ( \count( $errors ) )
|
||||
{
|
||||
\IPS\Output::i()->error( implode( ". ", $errors ), '3C163/I', 500, '' );
|
||||
}
|
||||
}
|
||||
|
||||
$themeSet->custom_header = $values['theme_simple_header'];
|
||||
$themeSet->custom_footer = $values['theme_simple_footer'];
|
||||
$themeSet->save();
|
||||
|
||||
/* Now remove any additional CSS files */
|
||||
if ( $cssFiles > 1 )
|
||||
{
|
||||
foreach( $customCss as $app => $data )
|
||||
{
|
||||
if ( isset( $customCss[ $app ]['front']['custom'] ) )
|
||||
{
|
||||
foreach ( $customCss[ $app ]['front']['custom'] as $name => $data )
|
||||
{
|
||||
if ( ! ( $app == 'core' and $name == 'custom.css' ) )
|
||||
{
|
||||
\IPS\Theme::removeCss( $app, 'front', 'custom', NULL, TRUE, $name );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Finally, save the theme */
|
||||
$themeSet->saveCss( [
|
||||
'app' => 'core',
|
||||
'location' => 'front',
|
||||
'group' => 'custom',
|
||||
'name' => 'custom.css',
|
||||
'content' => $values['theme_simple_css'],
|
||||
'set_id' => $id,
|
||||
'item_id' => $itemId
|
||||
] );
|
||||
|
||||
/* Remove compiled templates to ensure the globalTemplate gets the latest updates */
|
||||
\IPS\Theme::deleteCompiledTemplate( 'core', 'front', 'global', $themeSet->_id );
|
||||
|
||||
if( \IPS\Request::i()->isAjax() )
|
||||
{
|
||||
\IPS\Output::i()->json( ['result' => 'ok' ] );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=templates&id=' . $id ), 'saved' );
|
||||
}
|
||||
}
|
||||
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'customization' )->templateEditorSimple( $themeSet, $form );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'codemirror/diff_match_patch.js', 'core', 'interface' ) );
|
||||
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'codemirror/codemirror.js', 'core', 'interface' ) );
|
||||
|
||||
/* Grab groups */
|
||||
$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' );
|
||||
|
||||
$hasHistory = \IPS\Db::i()->select( 'count(*) as cnt', 'core_theme_content_history', array('content_set_id=?', $id) )->setValueField( 'cnt' )->first();
|
||||
|
||||
if ( $id === 1 and \IPS\IN_DEV or $hasHistory )
|
||||
{
|
||||
\IPS\Output::i()->sidebar['actions']['diff'] = [
|
||||
'icon' => 'cog',
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=diff&id=' . $id ),
|
||||
'title' => 'core_theme_set_diff'
|
||||
];
|
||||
}
|
||||
|
||||
if ( $t_type == 'templates' )
|
||||
{
|
||||
$t_group = \IPS\Request::i()->t_group ?: ( ( \IPS\Request::i()->t_app ) ? false : 'global' );
|
||||
$t_template = \IPS\Request::i()->t_name ?: ( ( \IPS\Request::i()->t_app ) ? false : 'globalTemplate' );
|
||||
}
|
||||
else
|
||||
{
|
||||
$t_group = \IPS\Request::i()->t_group ?: ( ( \IPS\Request::i()->t_app ) ? false : 'custom' );
|
||||
$t_template = \IPS\Request::i()->t_name ?: ( ( \IPS\Request::i()->t_app ) ? false : 'custom.css' );
|
||||
}
|
||||
|
||||
$groups = array();
|
||||
$bitNames = array();
|
||||
$template = array();
|
||||
|
||||
if ( $t_type == 'templates' )
|
||||
{
|
||||
$templateNames = $themeSet->getRawTemplates( '', '', '', \IPS\Theme::RETURN_ALL_NO_CONTENT );
|
||||
$templateBits = $themeSet->getRawTemplates( $t_app, $t_location, $t_group, \IPS\Theme::RETURN_ALL );
|
||||
$templateBit = ( !empty( $t_template ) and !empty( $templateBits[$t_app][$t_location][$t_group][$t_template] ) ) ? $templateBits[$t_app][$t_location][$t_group][$t_template] : null;
|
||||
$itemId = $templateBit['template_id'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$templateNames = $themeSet->getRawCss( '', '', '', \IPS\Theme::RETURN_ALL_NO_CONTENT );
|
||||
$templateBits = $themeSet->getRawCss( $t_app, $t_location, $t_group, \IPS\Theme::RETURN_ALL );
|
||||
$templateBit = ( !empty( $t_template ) ) ? $templateBits[$t_app][$t_location][$t_group][$t_template] : null;
|
||||
$itemId = $templateBit['css_id'];
|
||||
}
|
||||
|
||||
if ( $t_type == 'templates' )
|
||||
{
|
||||
/* Remove Admin Templates */
|
||||
foreach ( $templateNames as $app => $items )
|
||||
{
|
||||
foreach ( $templateNames[$app] as $location => $items )
|
||||
{
|
||||
if ( $location == 'admin' )
|
||||
{
|
||||
unset( $templateNames[$app][$location] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$cssFiles = [];
|
||||
/* Remove all but custom/custom.css */
|
||||
foreach ( $templateNames as $app => $items )
|
||||
{
|
||||
foreach ( $templateNames[$app] as $location => $items )
|
||||
{
|
||||
if ( $location === 'front' )
|
||||
{
|
||||
foreach ( $templateNames[$app][$location] as $path => $items )
|
||||
{
|
||||
if ( $path == 'custom' )
|
||||
{
|
||||
$cssFiles[ $app ][ $location ][ $path ] = $items;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$templateNames = $cssFiles;
|
||||
}
|
||||
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'customization' )->templateEditor( $themeSet, $templateNames, $templateBit, array( 'app' => $t_app, 'location' => $t_location, 'group' => $t_group, 'template' => $t_template, 'type' => $t_type, 'item_id' => $itemId ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Toggle between the simple and advanced editing modes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function advancedEditingToggle()
|
||||
{
|
||||
\IPS\Settings::i()->changeValues( [ 'theme_editing_advanced_mode' => ( \IPS\Settings::i()->theme_editing_advanced_mode ) ? 0 : 1 ] );
|
||||
|
||||
if ( isset( \IPS\Request::i()->from ) )
|
||||
{
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=templates&id=' . \IPS\Request::i()->from ), 'theme_mode_switched' );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes' ), 'theme_mode_switched' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Members
|
||||
*
|
||||
|
||||
@@ -81,7 +81,7 @@ class _search extends \IPS\Dispatcher\Controller
|
||||
|
||||
try
|
||||
{
|
||||
$response = $val->request()->get()->decodeJson();
|
||||
$response = \IPS\Content\Search\Elastic\Index::request( $val )->get()->decodeJson();
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
@@ -91,13 +91,25 @@ class _search extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
throw new \DomainException('search_elastic_server_no_version');
|
||||
}
|
||||
if ( version_compare( $response['version']['number'], \IPS\Content\Search\Elastic\Index::MINIMUM_VERSION, '<' ) )
|
||||
|
||||
/* Open search is compatible up to at least version 2 */
|
||||
if( isset( $response['version']['distribution'] ) and $response['version']['distribution'] == 'opensearch' )
|
||||
{
|
||||
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'], '2.0', '>' ) )
|
||||
{
|
||||
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack('search_opensearch_server_unsupported_version', FALSE, array( 'sprintf' => array( $response['version']['number'] ) ) ) );
|
||||
}
|
||||
}
|
||||
if ( version_compare( $response['version']['number'], \IPS\Content\Search\Elastic\Index::UNSUPPORTED_VERSION, '>=' ) )
|
||||
else
|
||||
{
|
||||
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'] ) ) ) );
|
||||
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( \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 );
|
||||
@@ -179,6 +191,11 @@ class _search extends \IPS\Dispatcher\Controller
|
||||
$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' ) );
|
||||
|
||||
|
||||
$form->addHeader('search_logs');
|
||||
$groups = array_combine( array_keys( \IPS\Member\Group::groups( TRUE, TRUE ) ), array_map( function( $_group ) { return (string) $_group; }, \IPS\Member\Group::groups( TRUE, TRUE ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'searchlog_exclude_groups', json_decode( \IPS\Settings::i()->searchlog_exclude_groups, TRUE ), FALSE, array( 'options' => $groups, 'multiple' => true ), NULL, NULL, NULL, 'searchlog_exclude_groups' ) );
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
$engine = \IPS\Settings::i()->search_method;
|
||||
@@ -210,7 +227,9 @@ class _search extends \IPS\Dispatcher\Controller
|
||||
\IPS\Log::log( $e, 'search_index_prune' );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$values['searchlog_exclude_groups'] = json_encode( $values['searchlog_exclude_groups'] );
|
||||
|
||||
/* Go ahead and save... */
|
||||
$form->saveAsSettings( $values );
|
||||
\IPS\Session::i()->log( 'acplogs__search_settings' );
|
||||
|
||||
@@ -161,6 +161,10 @@ class _streams extends \IPS\Node\Controller
|
||||
$form = new \IPS\Helpers\Form;
|
||||
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'all_activity_extra_stream', $currentValuesStream, FALSE, array( 'options' => $options ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'activity_stream_rss', \IPS\Settings::i()->activity_stream_rss ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'activity_stream_subscriptions', \IPS\Settings::i()->activity_stream_subscriptions, FALSE, ['togglesOn' => ['activity_stream_subscriptions_max', 'activity_stream_subscriptions_inactive_limit' ] ] ) );
|
||||
$form->add( new \IPS\Helpers\Form\Number( 'activity_stream_subscriptions_max', \IPS\Settings::i()->activity_stream_subscriptions_max, TRUE, ['min' => 1, 'max' => \IPS\CIC ? 10:NULL ], NULL, NULL, NULL,'activity_stream_subscriptions_max' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Number( 'activity_stream_subscriptions_inactive_limit', \IPS\Settings::i()->activity_stream_subscriptions_inactive_limit, TRUE, [ ], NULL, NULL, NULL,'activity_stream_subscriptions_inactive_limit' ) );
|
||||
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
@@ -171,6 +175,8 @@ class _streams extends \IPS\Node\Controller
|
||||
}
|
||||
|
||||
$toSave[ 'activity_stream_rss' ] = $values['activity_stream_rss'];
|
||||
$toSave[ 'activity_stream_subscriptions' ] = $values['activity_stream_subscriptions'];
|
||||
$toSave[ 'activity_stream_subscriptions_max' ] = $values['activity_stream_subscriptions_max'];
|
||||
|
||||
$form->saveAsSettings( $toSave );
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=discovery&controller=streams' ), 'saved' );
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Editor Stored Replies aka Stock Actions aka whatever else I rename it during this coding session
|
||||
* @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 Code
|
||||
* @since 03 September 2021
|
||||
*/
|
||||
|
||||
namespace IPS\core\modules\admin\editor;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stored Replies
|
||||
*/
|
||||
class _storedreplies extends \IPS\Node\Controller
|
||||
{
|
||||
/**
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* Node Class
|
||||
*/
|
||||
protected $nodeClass = 'IPS\core\StoredReplies';
|
||||
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'stored_replies_manage' );
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function manage()
|
||||
{
|
||||
/* Have we not dragged the button to the editor bar? */
|
||||
$toolbars = json_decode( \IPS\Settings::i()->ckeditor_toolbars, true );
|
||||
$found = 0;
|
||||
foreach ( array('desktop', 'tablet', 'phone') as $device )
|
||||
{
|
||||
foreach ( $toolbars[$device] as $row )
|
||||
{
|
||||
if ( \is_array( $row ) and isset( $row['items'] ) )
|
||||
{
|
||||
if ( \in_array( 'ipsstockreplies', $row['items'] ) )
|
||||
{
|
||||
$found++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Do we have any stored replies yet? */
|
||||
if ( \IPS\Db::i()->select('count(*)', 'core_editor_stored_replies' )->first() )
|
||||
{
|
||||
if ( $found > 0 and $found < 3 )
|
||||
{
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global', 'core' )->message( 'editor_stored_replies_not_all_editors_have_button', 'info' );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( $found == 0 )
|
||||
{
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global', 'core' )->message( 'editor_stored_replies_no_editor_has_button', 'warning' );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( $found )
|
||||
{
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global', 'core' )->message( 'editor_stored_replies_none_created_button', 'info' );
|
||||
}
|
||||
}
|
||||
|
||||
parent::manage();
|
||||
}
|
||||
}
|
||||
@@ -57,6 +57,7 @@ class _toolbar extends \IPS\Dispatcher\Controller
|
||||
'ipssource',
|
||||
'ipsspoiler',
|
||||
'ipsgiphy',
|
||||
'ipsstockreplies',
|
||||
'justify',
|
||||
'lineutils',
|
||||
'list',
|
||||
@@ -203,12 +204,25 @@ class _toolbar extends \IPS\Dispatcher\Controller
|
||||
/* Custom Plugin */
|
||||
if ( preg_match( '/^custom-(\w{32})$/i', $button, $matches ) and !\IPS\IN_DEV and !\IPS\NO_WRITES )
|
||||
{
|
||||
$code = file_get_contents( \IPS\ROOT_PATH . "/applications/core/interface/ckeditor/ckeditor/plugins/{$matches[0]}/plugin.js" );
|
||||
|
||||
$option = file_exists( \IPS\ROOT_PATH . "/applications/core/interface/ckeditor/ckeditor/plugins/{$matches[0]}/dialogs/{$matches[0]}.js" );
|
||||
if ( $option )
|
||||
if ( \IPS\CIC2 )
|
||||
{
|
||||
$code = file_get_contents( \IPS\ROOT_PATH . "/applications/core/interface/ckeditor/ckeditor/plugins/{$matches[0]}/dialogs/{$matches[0]}.js" );
|
||||
$code = file_get_contents( \IPS\SITE_FILES_PATH . "/ckeditor_custom/{$matches[0]}/plugin.js" );
|
||||
|
||||
$option = file_exists( \IPS\SITE_FILES_PATH . "/ckeditor_custom/{$matches[0]}/dialogs/{$matches[0]}.js" );
|
||||
if ( $option )
|
||||
{
|
||||
$code = file_get_contents( \IPS\SITE_FILES_PATH . "/ckeditor_custom/{$matches[0]}/dialogs/{$matches[0]}.js" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$code = file_get_contents( \IPS\ROOT_PATH . "/applications/core/interface/ckeditor/ckeditor/plugins/{$matches[0]}/plugin.js" );
|
||||
|
||||
$option = file_exists( \IPS\ROOT_PATH . "/applications/core/interface/ckeditor/ckeditor/plugins/{$matches[0]}/dialogs/{$matches[0]}.js" );
|
||||
if ( $option )
|
||||
{
|
||||
$code = file_get_contents( \IPS\ROOT_PATH . "/applications/core/interface/ckeditor/ckeditor/plugins/{$matches[0]}/dialogs/{$matches[0]}.js" );
|
||||
}
|
||||
}
|
||||
|
||||
$type = 'inline';
|
||||
@@ -900,7 +914,7 @@ JS;
|
||||
$dir = \IPS\ROOT_PATH . '/applications/core/interface/ckeditor/ckeditor/plugins/' . $key;
|
||||
if ( is_dir( $dir ) )
|
||||
{
|
||||
$this->_recursiveDelete( $dir );
|
||||
$this->_recursiveRmDir( $dir );
|
||||
}
|
||||
|
||||
/* Remove from plugin list */
|
||||
@@ -924,33 +938,6 @@ JS;
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=editor&controller=toolbar' ), 'saved' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive delete
|
||||
*
|
||||
* @param string $dir Directory
|
||||
* @return void
|
||||
*/
|
||||
protected function _recursiveDelete( $dir )
|
||||
{
|
||||
foreach ( new \DirectoryIterator( $dir ) as $file )
|
||||
{
|
||||
if ( !$file->isDot() )
|
||||
{
|
||||
if ( $file->isDir() )
|
||||
{
|
||||
$this->_recursiveDelete( $file->getPathname() );
|
||||
}
|
||||
else
|
||||
{
|
||||
unlink( $file->getPathname() );
|
||||
}
|
||||
}
|
||||
}
|
||||
$handle = opendir( $dir );
|
||||
closedir ( $handle );
|
||||
rmdir( $dir );
|
||||
}
|
||||
|
||||
/**
|
||||
* Revert Editor back to default
|
||||
*
|
||||
@@ -968,7 +955,11 @@ JS;
|
||||
{
|
||||
if ( \IPS\CIC2 )
|
||||
{
|
||||
\IPS\Cicloud\folderDelete( "ckeditor_custom/{$key}" );
|
||||
if ( is_dir( \IPS\SITE_FILES_PATH . '/ckeditor_custom/' . $key ) )
|
||||
{
|
||||
/* If this is cloud we only need the key */
|
||||
$this->_recursiveRmDir( $key );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -184,7 +184,7 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
$file = $this->_apiWrapper( "marketplace/files/" . (int) \IPS\Request::i()->id, [ 'termFields' => 1 ], TRUE );
|
||||
|
||||
/* Check purchase status */
|
||||
if( $file['isPaid'] AND empty( $file['isPurchased'] ) )
|
||||
if( $file['isPaid'] AND empty( $file['isPurchased'] ) AND !$file['canDownload'] )
|
||||
{
|
||||
\IPS\Output::i()->error( 'marketplace_resource_not_purchased', '2C171/A', 403, '' );
|
||||
}
|
||||
@@ -523,7 +523,8 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
'q' => \IPS\Request::i()->title,
|
||||
'type' => 'downloads_file',
|
||||
'search_in' => 'titles',
|
||||
'doNotTrack' => TRUE
|
||||
'doNotTrack' => TRUE,
|
||||
'search_and_or' => 'and'
|
||||
];
|
||||
|
||||
$resourceTypeCategories = $this->_resourceTypeCategories();
|
||||
|
||||
@@ -135,6 +135,7 @@ class _groups extends \IPS\Dispatcher\Controller
|
||||
$return['copy'] = array(
|
||||
'icon' => 'files-o',
|
||||
'title' => 'copy',
|
||||
'data' => array( 'confirm' => '', 'confirmMessage' => \IPS\Member::loggedIn()->language()->addToStack( 'group_clone_confirm_message' ), 'confirmSubMessage' => \IPS\Member::loggedIn()->language()->addToStack( 'group_clone_confirm_sub_message' ) ),
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=members&controller=groups&do=copy&id=' . $row['g_id'] )->csrf(),
|
||||
);
|
||||
}
|
||||
@@ -148,7 +149,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 \IPS\Member\Group::load($row['g_id'] )->canDelete() )
|
||||
{
|
||||
$return['delete'] = array(
|
||||
'icon' => 'times-circle',
|
||||
@@ -279,6 +280,7 @@ class _groups extends \IPS\Dispatcher\Controller
|
||||
$form->addTab( '__app_' . $class::$permApp );
|
||||
$matrix = new \IPS\Helpers\Form\Matrix( $class );
|
||||
$matrix->classes = array( 'cGroupPermissions' );
|
||||
$matrix->styledRowTitle = TRUE;
|
||||
$matrix->showTooltips = TRUE;
|
||||
|
||||
/* Remove delete buttons and add row button */
|
||||
@@ -286,9 +288,24 @@ class _groups extends \IPS\Dispatcher\Controller
|
||||
|
||||
/* Call the callable callbacks */
|
||||
$matrix->columns = array(
|
||||
'label' => function( $key, $value, $data )
|
||||
'label' => function( $key, $value, $data ) use ( $class )
|
||||
{
|
||||
return $value;
|
||||
$nodeId = explode( '[', $key )[0];
|
||||
$acpUrl = NULL;
|
||||
|
||||
/* Let's try to get a link to the node's permissions */
|
||||
try
|
||||
{
|
||||
$node = $class::load( $nodeId );
|
||||
|
||||
if ( !$node->canManagePermissions() )
|
||||
throw new \DomainException;
|
||||
|
||||
$acpUrl = $node->acpUrl( 'permissions' );
|
||||
}
|
||||
catch ( \Exception $e ) { }
|
||||
|
||||
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->titleWithLink( $value, $acpUrl, 'all_permissions', \IPS\Member::loggedIn()->language()->addToStack( "all_permissions_node_hovertitle", FALSE, array( 'sprintf' => [ $value ] ) ) );
|
||||
}
|
||||
);
|
||||
$matrix->widths = array( 'label' => 30 );
|
||||
@@ -416,6 +433,10 @@ class _groups extends \IPS\Dispatcher\Controller
|
||||
try
|
||||
{
|
||||
$group = \IPS\Member\Group::load( \IPS\Request::i()->id );
|
||||
if( !$group->canDelete() )
|
||||
{
|
||||
\IPS\Output::i()->error( 'cannot_delete_group', '2C108/5', 403, '' );
|
||||
}
|
||||
$count = \IPS\Db::i()->select( 'COUNT(*)', 'core_members', array( 'member_group_id=?', $group->g_id ) )->first();
|
||||
|
||||
/* Any members in it? */
|
||||
|
||||
@@ -41,14 +41,22 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
|
||||
/* Some advanced search links may bring us here */
|
||||
\IPS\Output::i()->bypassCsrfKeyCheck = true;
|
||||
|
||||
|
||||
$forceIndex = 'joined';
|
||||
|
||||
/* Yeah, this is the level of hackery we've gotten to. Millions of rows in core_members is hard work */
|
||||
if ( isset( \IPS\Request::i()->filter ) and \IPS\Request::i()->filter == 'members_filter_validating' )
|
||||
{
|
||||
$forceIndex = NULL;
|
||||
}
|
||||
|
||||
/* Create the table */
|
||||
$table = new \IPS\Helpers\Table\Db( 'core_members', \IPS\Http\Url::internal( 'app=core&module=members&controller=members' ), array( array( 'email<>?', '' ) ), 'joined' );
|
||||
$table = new \IPS\Helpers\Table\Db( 'core_members', \IPS\Http\Url::internal( 'app=core&module=members&controller=members' ), array( array( 'email<>?', '' ) ), $forceIndex );
|
||||
$table->langPrefix = 'members_';
|
||||
|
||||
|
||||
/* Columns we need */
|
||||
$table->include = array( 'photo', 'name', 'email', 'joined', 'group_name' );
|
||||
if ( \IPS\core\Achievements\Rank::getStore() )
|
||||
if ( \IPS\Settings::i()->achievements_enabled AND \IPS\core\Achievements\Rank::getStore() )
|
||||
{
|
||||
$table->include[] = 'achievements_points';
|
||||
$table->widths['achievements_points'] = '17';
|
||||
@@ -153,20 +161,26 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
array(
|
||||
'select' => 'm.row_id',
|
||||
'from' => array( 'core_admin_permission_rows', 'm' ),
|
||||
'where' => "m.row_id=core_members.member_id AND m.row_id_type='member'" ),
|
||||
array(
|
||||
'select' => 'g.row_id',
|
||||
'from' => array( 'core_admin_permission_rows', 'g' ),
|
||||
'where' => array( 'g.row_id', \IPS\Db::i()->select( 'row_id', array( 'core_admin_permission_rows', 'sub' ), array( "((sub.row_id=core_members.member_group_id OR FIND_IN_SET( sub.row_id, core_members.mgroup_others ) ) AND sub.row_id_type='group') AND g.row_id_type='group'" ), NULL, array( 0, 1 ) ) ) )
|
||||
'where' => "( m.row_id_type='group' and ( m.row_id=core_members.member_group_id OR FIND_IN_SET( m.row_id, core_members.mgroup_others ) ) or ( m.row_id=core_members.member_id AND m.row_id_type='member' ))",
|
||||
'type' => 'STRAIGHT_JOIN' )
|
||||
);
|
||||
|
||||
|
||||
/* Makes query less efficient */
|
||||
if ( $table->filter !== 'members_filter_administrators' )
|
||||
{
|
||||
unset( $table->joins[2] );
|
||||
unset( $table->joins[3] );
|
||||
}
|
||||
|
||||
if ( $table->filter !== 'members_filter_validating' )
|
||||
{
|
||||
unset( $table->joins[0] );
|
||||
}
|
||||
|
||||
if ( ! isset( \IPS\Request::i()->advanced_search_submitted ) )
|
||||
{
|
||||
unset( $table->joins[1] );
|
||||
}
|
||||
|
||||
/* Ranks */
|
||||
$rankOptions = array( '' => 'any' );
|
||||
foreach( \IPS\core\Achievements\Rank::getStore() as $rank )
|
||||
@@ -256,7 +270,7 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
'members_filter_banned' => 'temp_ban<>0',
|
||||
'members_filter_spam' => \IPS\Db::i()->bitwiseWhere( \IPS\Member::$bitOptions['members_bitoptions'], 'bw_is_spammer' ),
|
||||
'members_filter_validating' => '( v.lost_pass=0 AND v.forgot_security=0 AND v.vid IS NOT NULL )',
|
||||
'members_filter_administrators' => '( m.row_id IS NOT NULL OR g.row_id IS NOT NULL )'
|
||||
'members_filter_administrators' => '(1=1)' // We use a straight join when this filter is activated, so there's no additional where needed
|
||||
);
|
||||
|
||||
if ( \IPS\Db::i()->select( 'COUNT(*)', 'core_members', array( "email <> '' and name=''" ) )->first() )
|
||||
@@ -272,9 +286,25 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
|
||||
/* Custom parsers */
|
||||
$table->parsers = array(
|
||||
'email' => function( $val, $row )
|
||||
'email' => function( $val, &$row )
|
||||
{
|
||||
if ( $row['vid'] )
|
||||
if ( ! array_key_exists( 'vid', $row ) )
|
||||
{
|
||||
/* Grab the data if need be */
|
||||
$member = \IPS\Member::constructFromData( $row );
|
||||
|
||||
if ( $member->members_bitoptions['validating'] )
|
||||
{
|
||||
try
|
||||
{
|
||||
$validating = \IPS\Db::i()->select( '*', 'core_validating', [ 'member_id=?', $member->member_id ] )->first();
|
||||
$row = array_merge( $validating, $row );
|
||||
}
|
||||
catch( \UnderflowException $e ) { }
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $row['vid'] ) )
|
||||
{
|
||||
return \IPS\Theme::i()->getTemplate( 'members', 'core', 'admin' )->memberEmailCell( \IPS\Theme::i()->getTemplate( 'members', 'core', 'admin' )->memberValidatingCell( $val, \IPS\Member::constructFromData( $row )->validatingDescription( $row ) ) );
|
||||
}
|
||||
@@ -384,7 +414,7 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
$return['approve'] = array(
|
||||
'icon' => 'check-circle',
|
||||
'title' => 'approve',
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=members&controller=members&do=approve&id=' . $member->member_id )->csrf(),
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=members&controller=members&do=approve&id=' . $member->member_id . $this->getSafeUrlFromFilters() )->csrf(),
|
||||
'id' => "{$member->member_id}-approve",
|
||||
'data' => array(
|
||||
'bubble' => '',
|
||||
@@ -393,7 +423,7 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
$return['ban'] = array(
|
||||
'icon' => 'times',
|
||||
'title' => 'ban',
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=members&controller=members&do=ban&id=' . $member->member_id . '&permban=1' )->csrf(),
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=members&controller=members&do=ban&id=' . $member->member_id . '&permban=1' . $this->getSafeUrlFromFilters() )->csrf(),
|
||||
'id' => "{$member->member_id}-ban",
|
||||
'data' => array(
|
||||
'bubble' => '',
|
||||
@@ -405,7 +435,7 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
$return['resend_email'] = array(
|
||||
'icon' => 'envelope-o',
|
||||
'title' => 'resend_validation_email',
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=members&controller=members&do=resendEmail&id=' . $member->member_id )->csrf(),
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=members&controller=members&do=resendEmail&id=' . $member->member_id . $this->getSafeUrlFromFilters() )->csrf(),
|
||||
'data' => array( 'doajax' => '' ),
|
||||
'id' => "{$member->member_id}-resend",
|
||||
);
|
||||
@@ -423,7 +453,7 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
$return['unlock'] = array(
|
||||
'icon' => 'unlock',
|
||||
'title' => 'unlock',
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=members&controller=members&do=unlock&id=' . $member->member_id )->csrf(),
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=members&controller=members&do=unlock&id=' . $member->member_id . $this->getSafeUrlFromFilters() )->csrf(),
|
||||
'data' => array( 'bubble' => '' )
|
||||
);
|
||||
}
|
||||
@@ -431,7 +461,7 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
$return['view'] = array(
|
||||
'icon' => 'search',
|
||||
'title' => 'view',
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=members&controller=members&do=view&id=' ) . $member->member_id,
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=members&controller=members&do=view&id=' . $member->member_id . $this->getSafeUrlFromFilters() ),
|
||||
);
|
||||
|
||||
if ( !\IPS\Request::i()->_groupFilter )
|
||||
@@ -443,7 +473,7 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
$return['flag'] = array(
|
||||
'icon' => 'flag',
|
||||
'title' => 'spam_flag',
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=members&controller=members&do=spam&id=' . $member->member_id . '&status=1' )->csrf(),
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=members&controller=members&do=spam&id=' . $member->member_id . '&status=1' . $this->getSafeUrlFromFilters() )->csrf(),
|
||||
'hidden' => $member->members_bitoptions['bw_is_spammer'],
|
||||
'id' => "{$member->member_id}-flag",
|
||||
'data' => array(
|
||||
@@ -453,7 +483,7 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
$return['unflag'] = array(
|
||||
'icon' => 'flag ipsType_spammer',
|
||||
'title' => 'spam_unflag',
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=members&controller=members&do=spam&id=' . $member->member_id . '&status=0' )->csrf(),
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=members&controller=members&do=spam&id=' . $member->member_id . '&status=0' . $this->getSafeUrlFromFilters() )->csrf(),
|
||||
'hidden' => !$member->members_bitoptions['bw_is_spammer'],
|
||||
'id' => "{$member->member_id}-unflag",
|
||||
'data' => array(
|
||||
@@ -468,7 +498,7 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
$return['delete'] = array(
|
||||
'icon' => 'times-circle',
|
||||
'title' => 'delete',
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=members&controller=members&do=delete&id=' ) . $member->member_id,
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=members&controller=members&do=delete&id=' . $member->member_id . $this->getSafeUrlFromFilters() ),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack( 'delete_member', FALSE, array( 'sprintf' => $member->name ) ) ),
|
||||
);
|
||||
}
|
||||
@@ -569,6 +599,25 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
\IPS\Output::i()->output .= (string) $table;
|
||||
}
|
||||
|
||||
protected function toggleDataLayerPii()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'member_edit' );
|
||||
|
||||
$member = \IPS\Member::load( \IPS\Request::i()->id );
|
||||
if ( $member->isAdmin() )
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'member_edit_admin' );
|
||||
}
|
||||
|
||||
if ( $member->member_id )
|
||||
{
|
||||
$member->datalayer_collect_pii = (bool) !$member->datalayer_collect_pii;
|
||||
$member->save();
|
||||
}
|
||||
$_SESSION['member_datalayer_changed'] = 1;
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=members&controller=members&do=view&id=' . \IPS\Request::i()->id ?? 0 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prune members
|
||||
*
|
||||
@@ -1180,6 +1229,12 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
|
||||
$history = \IPS\Theme::i()->getTemplate('memberprofile')->history( $member, $history, $historyFilters );
|
||||
}
|
||||
|
||||
if ( isset( $_SESSION['member_datalayer_changed'] ) )
|
||||
{
|
||||
\IPS\Output::i()->inlineMessage = 'Saved';
|
||||
unset( $_SESSION['member_datalayer_changed'] );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'members/view.css', 'core', 'admin' ) );
|
||||
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'admin_members.js', 'core' ) );
|
||||
@@ -1343,7 +1398,7 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
$form = new \IPS\Helpers\Form;
|
||||
|
||||
// This looks weird, but we only want this showing here, and not anywhere else 'password' is used as a form key.
|
||||
\IPS\Member::loggedIn()->language()->words['password_desc'] = \IPS\Member::loggedIn()->language()->addToStack( 'force_password_reset_member', NULL, array( 'sprintf' => array( $member->acpUrl()->csrf()->setQueryString( 'do', 'forcePassReset' ), \IPS\Member::loggedIn()->language()->addToStack( 'force_password_reset_member_confirmmsg' ) ) ) );
|
||||
\IPS\Member::loggedIn()->language()->words['password_desc'] = \IPS\Member::loggedIn()->language()->addToStack( 'force_password_reset_member', FALSE, array( 'sprintf' => array( $member->acpUrl()->csrf()->setQueryString( 'do', 'forcePassReset' ), \IPS\Member::loggedIn()->language()->addToStack( 'force_password_reset_member_confirmmsg' ) ) ) );
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Password( 'password', '', TRUE, array( 'protect' => TRUE, 'confirm' => 'password_confirm', 'showMeter' => \IPS\Settings::i()->password_strength_meter, 'minimumStrength' => 1, 'bypassProfanity' => \IPS\Helpers\Form\Text::BYPASS_PROFANITY_ALL ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Password( 'password_confirm', '', TRUE, array( 'protect' => TRUE, 'bypassProfanity' => \IPS\Helpers\Form\Text::BYPASS_PROFANITY_ALL ) ) );
|
||||
@@ -1470,7 +1525,7 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
/* If we have external login handlers, we need to show an interstitual page indicating that with caveats. */
|
||||
if ( \count( $externals ) )
|
||||
{
|
||||
$msg = \IPS\Member::loggedIn()->language()->addToStack( 'force_password_reset_member_external', NULL, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->formatList( $externals ) ) ) );
|
||||
$msg = \IPS\Member::loggedIn()->language()->addToStack( 'force_password_reset_member_external', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->formatList( $externals ) ) ) );
|
||||
$form = new \IPS\Helpers\Form( 'form', 'reset_password' );
|
||||
$form->hiddenValues['id'] = $member->member_id;
|
||||
$form->addMessage( $msg, 'ipsMessage ipsMessage_warning' );
|
||||
@@ -2940,6 +2995,26 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
\IPS\Output::i()->output = $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a partial query string based on any filters or sort options
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getSafeUrlFromFilters(): string
|
||||
{
|
||||
$extraUrlParams = [];
|
||||
|
||||
foreach( [ 'filter', 'sortby', 'sortdirection' ] as $param )
|
||||
{
|
||||
if ( isset( \IPS\Request::i()->$param ) )
|
||||
{
|
||||
$extraUrlParams[ $param ] = \IPS\Request::i()->$param;
|
||||
}
|
||||
}
|
||||
|
||||
return \count( $extraUrlParams ) ? '&' . http_build_query( $extraUrlParams ) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete Member
|
||||
*
|
||||
@@ -2990,7 +3065,7 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
/* Handle Submissions */
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
\IPS\Session::i()->log( 'acplog__members_deleted', array( $member->name => FALSE ) );
|
||||
\IPS\Session::i()->log( 'acplog__members_deleted_id', array( $member->name => FALSE, $member->member_id => FALSE ) );
|
||||
|
||||
switch( $values['member_deletion_content'] )
|
||||
{
|
||||
@@ -3007,7 +3082,7 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
break;
|
||||
}
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal("app=core&module=members&controller=members"), 'deleted' );
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal("app=core&module=members&controller=members" . $this->getSafeUrlFromFilters() ), 'deleted' );
|
||||
}
|
||||
|
||||
/* Display Form */
|
||||
@@ -3841,8 +3916,8 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
\IPS\Output::i()->parseFileObjectUrls( $csv );
|
||||
$csv = \IPS\Output::i()->replaceEmojiWithImages( $csv );
|
||||
|
||||
/* Excel requires this for non-ASCII characters to show properly */
|
||||
$csv = \chr(255) . \chr(254) . mb_convert_encoding( $csv, 'UTF-16LE', 'UTF-8' );
|
||||
/* Excel requires a BOM this for non-ASCII characters to show properly */
|
||||
$csv = \chr(0xEF) . \chr(0xBB) . \chr(0xBF) . $csv;
|
||||
|
||||
\IPS\Session::i()->log( 'acplog__exported_member_list' );
|
||||
\IPS\Output::i()->sendOutput( $csv, 200, 'text/csv', array( 'Content-Disposition' => \IPS\Output::getContentDisposition( 'attachment', \IPS\Member::loggedIn()->language()->get('member_pl') . '.csv' ) ), FALSE, FALSE, FALSE, FALSE );
|
||||
@@ -3886,6 +3961,8 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
'last_post' => 'last_post',
|
||||
'birthday' => 'import_birthday',
|
||||
'allow_admin_mails' => 'import_allow_admin_mails',
|
||||
'achievements_points' => 'achievement_points',
|
||||
'member_rank' => "members_member_rank_id",
|
||||
);
|
||||
if ( \IPS\Settings::i()->signatures_enabled )
|
||||
{
|
||||
@@ -4072,7 +4149,7 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
}
|
||||
|
||||
/* Write */
|
||||
fputcsv( $fh, $dataToWrite, "\t" );
|
||||
fputcsv( $fh, $dataToWrite );
|
||||
}
|
||||
|
||||
/* Close and loop */
|
||||
@@ -4176,6 +4253,16 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
$valueToWrite = '';
|
||||
}
|
||||
break;
|
||||
case 'member_rank':
|
||||
if( $rank = \IPS\core\Achievements\Rank::fromPoints( $member['achievements_points'] ) )
|
||||
{
|
||||
$valueToWrite = \IPS\Member::loggedIn()->language()->get( 'core_member_rank_' . $rank->_id );
|
||||
}
|
||||
else
|
||||
{
|
||||
$valueToWrite = '';
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if ( mb_substr( $column, 0, 7 ) == 'pfield_' )
|
||||
@@ -4263,7 +4350,7 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
$wizardData['file'] = tempnam( \IPS\TEMP_DIRECTORY, 'IPS' );
|
||||
$fh = fopen( $wizardData['file'], 'w' );
|
||||
|
||||
fputcsv( $fh, $this->_getExportCsvHeaders( $wizardData ), "\t" );
|
||||
fputcsv( $fh, $this->_getExportCsvHeaders( $wizardData ) );
|
||||
fclose( $fh );
|
||||
|
||||
return $wizardData;
|
||||
@@ -4559,7 +4646,7 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
try
|
||||
{
|
||||
$recognize = \IPS\core\Achievements\Recognize::load( $row['recognize'] );
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'acp_badge_from_recognize', NULL, [ 'sprintf' => [ $recognize->content()->url(), $recognize->content()->indefiniteArticle() ] ] );
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'acp_badge_from_recognize', FALSE, [ 'sprintf' => [ $recognize->content()->url(), $recognize->content()->indefiniteArticle() ] ] );
|
||||
}
|
||||
catch( \Exception $e )
|
||||
{
|
||||
@@ -4628,7 +4715,7 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
{
|
||||
return $node->manually_awarded;
|
||||
},
|
||||
'disabledIds' => iterator_to_array( \IPS\Db::i()->select( 'id', 'core_member_badges', [ 'member=?', $member->member_id ] ) ),
|
||||
'disabledIds' => iterator_to_array( \IPS\Db::i()->select( 'badge', 'core_member_badges', [ '`member`=?', $member->member_id ] ) ),
|
||||
'url' => $member->acpUrl()->setQueryString('do', 'addBadge')
|
||||
] ) );
|
||||
|
||||
|
||||
@@ -182,7 +182,7 @@ class _notifications extends \IPS\Dispatcher\Controller
|
||||
$form->addMessage( $option['description'] );
|
||||
}
|
||||
|
||||
if ( !\in_array( 'inline', $option['disabled'] ) or ( ( \IPS\Settings::i()->mobile_app_id || \IPS\Notification::webPushEnabled() ) and !\in_array( 'push', $option['disabled'] ) ) or !\in_array( 'email', $option['disabled'] ) )
|
||||
if ( !\in_array( 'inline', $option['disabled'] ) or ( \IPS\Notification::webPushEnabled() 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" ),
|
||||
@@ -281,7 +281,7 @@ class _notifications extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
}
|
||||
|
||||
if ( !\in_array( 'inline', $option['disabled'] ) or ( ( \IPS\Settings::i()->mobile_app_id || \IPS\Notification::webPushEnabled() ) and !\in_array( 'push', $option['disabled'] ) ) or !\in_array( 'email', $option['disabled'] ) )
|
||||
if ( !\in_array( 'inline', $option['disabled'] ) or ( \IPS\Notification::webPushEnabled()and !\in_array( 'push', $option['disabled'] ) ) or !\in_array( 'email', $option['disabled'] ) )
|
||||
{
|
||||
$row = array(
|
||||
'notification_key' => $key,
|
||||
@@ -296,7 +296,7 @@ class _notifications extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
if ( $k === 'push' )
|
||||
{
|
||||
if ( !\IPS\Settings::i()->mobile_app_id && !\IPS\Notification::webPushEnabled() )
|
||||
if ( !\IPS\Notification::webPushEnabled() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -373,9 +373,6 @@ class _notifications extends \IPS\Dispatcher\Controller
|
||||
$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'] );
|
||||
|
||||
/* Log and redirect */
|
||||
\IPS\Session::i()->log( 'acplog__notification_settings_existing' );
|
||||
|
||||
@@ -74,22 +74,33 @@ class _profilesettings extends \IPS\Dispatcher\Controller
|
||||
throw new \DomainException('user_name_length_no_match');
|
||||
}
|
||||
} ) );
|
||||
|
||||
$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 ) )
|
||||
if ( \is_array( $field->value ) )
|
||||
{
|
||||
$easy['letters'] = ( $matches[1] == '\\p{L}\\p{M}' ) ? 'all' : 'latin';
|
||||
if ( $matches[2] )
|
||||
$easy['letters'] = $field->value['letters'];
|
||||
$easy['numbers'] = $field->value['numbers'];
|
||||
$easy['spaces'] = (bool) isset( $field->value['spaces'] );
|
||||
$easy['extra'] = isset( $field->value['extra_enabled'] ) ? $field->value['extra'] : '';
|
||||
$field->value = $field->value['regex'];
|
||||
}
|
||||
else{
|
||||
if ( preg_match( '/^\/\^\(\(\[(\\\p\{L\}\\\p\{M\}|A\-Za\-z)(\\\p\{N\}|0-9)?(.+)?\]\+\)( )?\?\)\+\$\/u$/', $field->value, $matches ) )
|
||||
{
|
||||
$easy['numbers'] = ( $matches[2] == '\\p{N}' ) ? 'all' : 'arabic';
|
||||
$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] );
|
||||
}
|
||||
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 );
|
||||
}
|
||||
|
||||
@@ -366,7 +366,7 @@ class _reputation extends \IPS\Node\Controller
|
||||
|
||||
\IPS\Lang::deleteCustom( 'core', 'core_reputation_level_' . \IPS\Request::i()->id );
|
||||
}
|
||||
catch ( \UnderflowExceptiobn $e ) { }
|
||||
catch ( \UnderflowException $e ) { }
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=reputation&tab=levels' ) );
|
||||
}
|
||||
|
||||
@@ -1,308 +0,0 @@
|
||||
<?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\CheckboxSet( 'menu_manager_access', $existing ? ( $existing['permissions'] == '*' ? '*' : explode( ',', $existing['permissions'] ) ) : '*', NULL, array( 'multiple' => TRUE, 'options' => $groups, 'unlimited' => '*', 'unlimitedLang' => 'everyone', 'impliedUnlimited' => TRUE ), 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');
|
||||
}
|
||||
}
|
||||
@@ -1,658 +0,0 @@
|
||||
<?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()
|
||||
{
|
||||
if ( \IPS\DEMO_MODE )
|
||||
{
|
||||
\IPS\Output::i()->error( 'demo_mode_function_blocked', '1C418/1', 403, '' );
|
||||
}
|
||||
|
||||
\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\YesNo( 'mobile_app_hide_stats', \IPS\Settings::i()->mobile_app_hide_stats, FALSE ) );
|
||||
}
|
||||
|
||||
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->isSquare() )
|
||||
{
|
||||
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\CheckboxSet( '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 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,22 +116,23 @@ class _spam extends \IPS\Dispatcher\Controller
|
||||
switch( $type )
|
||||
{
|
||||
case 'options':
|
||||
return array( 'none' => 'captcha_type_none', 'invisible' => 'captcha_type_invisible', 'recaptcha2' => 'captcha_type_recaptcha2', 'keycaptcha' => 'captcha_type_keycaptcha' );
|
||||
break;
|
||||
return array( 'none' => 'captcha_type_none', 'invisible' => 'captcha_type_invisible', 'recaptcha2' => 'captcha_type_recaptcha2', 'keycaptcha' => 'captcha_type_keycaptcha', 'hcaptcha' => 'captcha_type_hcaptcha' );
|
||||
break;
|
||||
|
||||
case 'toggles':
|
||||
return array(
|
||||
'none' => array( 'bot_antispam_type_warning' ),
|
||||
'recaptcha2' => array( 'guest_captcha', 'recaptcha2_public_key', 'recaptcha2_private_key' ),
|
||||
'invisible' => array( 'guest_captcha', 'recaptcha2_public_key', 'recaptcha2_private_key' ),
|
||||
'keycaptcha' => array( 'guest_captcha', 'keycaptcha_privatekey' )
|
||||
'keycaptcha' => array( 'guest_captcha', 'keycaptcha_privatekey' ),
|
||||
'hcaptcha' => array( 'guest_captcha', 'hcaptcha_sitekey', 'hcaptcha_secret')
|
||||
);
|
||||
break;
|
||||
break;
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Show CAPTCHA settings
|
||||
*
|
||||
@@ -148,6 +149,8 @@ class _spam extends \IPS\Dispatcher\Controller
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'recaptcha2_public_key', \IPS\Settings::i()->recaptcha2_public_key, FALSE, array(), NULL, NULL, NULL, 'recaptcha2_public_key' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'recaptcha2_private_key', \IPS\Settings::i()->recaptcha2_private_key, FALSE, array(), NULL, NULL, NULL, 'recaptcha2_private_key' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'keycaptcha_privatekey', \IPS\Settings::i()->keycaptcha_privatekey, FALSE, array(), NULL, NULL, NULL, 'keycaptcha_privatekey' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'hcaptcha_sitekey', \IPS\Settings::i()->hcaptcha_sitekey, FALSE, array(), NULL, NULL, NULL, 'hcaptcha_sitekey' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'hcaptcha_secret', \IPS\Settings::i()->hcaptcha_secret, FALSE, array(), NULL, NULL, NULL, 'hcaptcha_secret' ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'guest_captcha', \IPS\Settings::i()->guest_captcha, FALSE, array(), NULL, NULL, NULL, 'guest_captcha' ) );
|
||||
|
||||
/* Save values */
|
||||
@@ -217,8 +220,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, 'toggles' => array( '5' => array( 'spam_service_action_1_num' ) ) ), NULL, NULL, NULL, 'spam_service_action_1' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Number( 'spam_service_action_1_num', ( isset( $days[1] ) ? $days[1] : -1 ), FALSE, array( 'unlimited' => -1, 'unlimitedLang' => 'spam_service_action_unlimited_days'), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('days'), 'spam_service_action_1_num' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'spam_service_action_2', \IPS\Settings::i()->spam_service_action_2, FALSE, array( 'disabled' => $disabled, 'options' => $actions, 'toggles' => array( '5' => array( 'spam_service_action_2_num' ) ) ), NULL, NULL, NULL, 'spam_service_action_2' ) );
|
||||
|
||||
@@ -210,5 +210,24 @@ class _dashboard extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=overview&controller=dashboard" ), 'saved' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Snooze the switch to invision link for a bit
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function switchSnooze()
|
||||
{
|
||||
$value = [ 'hits' => 1, 'lastClick' => time() ];
|
||||
if ( isset( \IPS\Request::i()->cookie['acpLinkSnooze'] ) and $value = json_decode( \IPS\Request::i()->cookie['acpLinkSnooze'], true ) )
|
||||
{
|
||||
$value['hits'] = \intval( $value['hits'] ) + 1;
|
||||
$value['lastClick'] = time();
|
||||
}
|
||||
|
||||
\IPS\Request::i()->setCookie( 'acpLinkSnooze', json_encode( $value ), ( new \IPS\DateTime )->add( new \DateInterval( 'P2Y' ) ) );
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=overview&controller=dashboard" ), 'The Switch to Cloud link won\'t show for a while' );
|
||||
}
|
||||
}
|
||||
@@ -219,7 +219,7 @@ class _files extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
}
|
||||
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->block( NULL, \IPS\Theme::i()->getTemplate( 'members', 'core', 'global' )->attachmentLocations( $locations, FALSE ) );
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->block( NULL, \IPS\Theme::i()->getTemplate( 'members', 'core', 'global' )->attachmentLocations( $locations, FALSE ), TRUE, 'ipsPad' );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -381,7 +381,7 @@ class _files extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
if ( isset( $settings[ $k ] ) AND !\is_array( $settings[ $k ] ) )
|
||||
{
|
||||
if( $settings[ $k ] != $v AND $k != 'filestorage_move' AND \in_array( $v, $cicDisabled ) )
|
||||
if( $settings[ $k ] != $v AND $k != 'filestorage_move' AND \IPS\CIC AND \in_array( $v, $cicDisabled ) )
|
||||
{
|
||||
\IPS\Output::i()->error( 'file_storage_cic_filesystem', '3C158/7', 403, '' );
|
||||
}
|
||||
@@ -862,7 +862,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();
|
||||
$title = \IPS\Member::loggedIn()->language()->addToStack( 'file_config_log', NULL, array( 'sprintf' => array( $method['method'] ) ) );
|
||||
$title = \IPS\Member::loggedIn()->language()->addToStack( 'file_config_log', FALSE, array( 'sprintf' => array( $method['method'] ) ) );
|
||||
|
||||
/* Create the table */
|
||||
$table = new \IPS\Helpers\Table\Db( 'core_file_logs', \IPS\Http\Url::internal( 'app=core&module=overview&controller=files&do=configurationLog&id=' . \IPS\Request::i()->id ), array( array( 'log_configuration_id=?', \IPS\Request::i()->id ) ) );
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Analytics
|
||||
* @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 03 Nov 2016
|
||||
*/
|
||||
|
||||
namespace IPS\core\modules\admin\promotion;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Analytics
|
||||
*/
|
||||
class _analytics extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
/**
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'analytics_manage' );
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Analytics Settings
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
$providers = array(
|
||||
'none' => 'analytics_provider_none',
|
||||
'ga' => 'analytics_provider_ga',
|
||||
'piwik' => 'analytics_provider_piwik',
|
||||
'custom' => 'analytics_provider_custom'
|
||||
);
|
||||
|
||||
$toggles = array(
|
||||
'ga' => array('analytics_ga'),
|
||||
'piwik' => array('analytics_piwik'),
|
||||
'custom' => array('analytics_custom', 'ipbseo_ga_paginatecode')
|
||||
);
|
||||
|
||||
$form = new \IPS\Helpers\Form;
|
||||
$form->addMessage('analytics_description');
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'ipbseo_ga_provider', \IPS\Settings::i()->ipbseo_ga_provider, FALSE, array( 'options' => $providers, 'toggles' => $toggles ) ) );
|
||||
|
||||
/* Add a CodeMirror element for each supported provider */
|
||||
foreach( $providers as $key => $provider )
|
||||
{
|
||||
if( $key == 'none' )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Codemirror( 'analytics_' . $key, \IPS\Settings::i()->ipbseo_ga_provider == $key ? \IPS\Settings::i()->ipseo_ga : '', FALSE, array('height' => 150, 'mode' => 'javascript'), NULL, NULL, NULL, 'analytics_' . $key ) );
|
||||
}
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Codemirror( 'ipbseo_ga_paginatecode', \IPS\Settings::i()->ipbseo_ga_paginatecode, FALSE, array('height' => 150, 'mode' => 'javascript'), NULL, NULL, NULL, 'ipbseo_ga_paginatecode' ) );
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
/* Fix up values to ensure the correct snippet is saved */
|
||||
if ( !isset( $providers[ $values['ipbseo_ga_provider'] ] ) || $values['ipbseo_ga_provider'] == 'none' )
|
||||
{
|
||||
$values['ipbseo_ga_enabled'] = FALSE;
|
||||
$values['ipseo_ga'] = '';
|
||||
$values['ipbseo_ga_paginatecode'] = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
$values['ipbseo_ga_enabled'] = TRUE;
|
||||
$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' );
|
||||
\IPS\Output::i()->inlineMessage = \IPS\Member::loggedIn()->language()->addToStack('saved');
|
||||
|
||||
/* Clear guest page caches */
|
||||
\IPS\Data\Cache::i()->clearAll();
|
||||
}
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_promotion_analytics');
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->block( 'analytics', $form );
|
||||
}
|
||||
}
|
||||
@@ -91,7 +91,7 @@ class _promote extends \IPS\Dispatcher\Controller
|
||||
$output = \IPS\Theme::i()->getTemplate( 'forms', 'core' )->blurb( \IPS\Member::loggedIn()->language()->addToStack( 'promote_acp_blurb' ) );
|
||||
|
||||
/* Put up a little warning about permissions */
|
||||
$enabledServices = \IPS\Db::i()->select( 'count(*)', 'core_social_promote_sharers', array( 'sharer_enabled=1' ) );
|
||||
$enabledServices = \IPS\Db::i()->select( 'count(*)', 'core_social_promote_sharers', array( 'sharer_enabled=1' ) )->first();
|
||||
|
||||
if ( $enabledServices and ! \IPS\core\Promote::canPromote() )
|
||||
{
|
||||
@@ -464,13 +464,13 @@ class _promote extends \IPS\Dispatcher\Controller
|
||||
|
||||
foreach( $pages as $id => $data )
|
||||
{
|
||||
$options[ 'p_' . $id ] = \IPS\Member::loggedIn()->language()->addToStack( 'promote_facebook_page_prefix', NULL, array( 'sprintf' => array( $data[0] ) ) );
|
||||
$options[ 'p_' . $id ] = \IPS\Member::loggedIn()->language()->addToStack( 'promote_facebook_page_prefix', FALSE, array( 'sprintf' => array( $data[0] ) ) );
|
||||
}
|
||||
|
||||
$groups = $facebook->getGroups( \IPS\Member::loggedIn() );
|
||||
foreach( $groups as $id => $data )
|
||||
{
|
||||
$options[ 'g_' . $id ] = \IPS\Member::loggedIn()->language()->addToStack( 'promote_facebook_group_prefix', NULL, array( 'sprintf' => array( $data[0] ) ) );
|
||||
$options[ 'g_' . $id ] = \IPS\Member::loggedIn()->language()->addToStack( 'promote_facebook_group_prefix', FALSE, array( 'sprintf' => array( $data[0] ) ) );
|
||||
}
|
||||
|
||||
$output = '';
|
||||
|
||||
@@ -85,6 +85,10 @@ class _seo extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
$tabs['metatags'] = 'seo_tab_metatags';
|
||||
}
|
||||
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'promotion', 'seo_furls' ) )
|
||||
{
|
||||
$tabs['robotstxt'] = 'seo_tab_robotstxt';
|
||||
}
|
||||
|
||||
/* Display */
|
||||
if ( $activeTabContents )
|
||||
@@ -126,6 +130,103 @@ class _seo extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage robots.txt
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _manageRobotstxt()
|
||||
{
|
||||
$value = \IPS\Settings::i()->robots_txt;
|
||||
if ( ! \in_array( \IPS\Settings::i()->robots_txt, ['off', 'default'] ) )
|
||||
{
|
||||
$value = 'custom';
|
||||
}
|
||||
|
||||
$form = new \IPS\Helpers\Form;
|
||||
|
||||
if ( isset( \IPS\Request::i()->_saved ) )
|
||||
{
|
||||
/* Do we have an existing robots.txt file in the community directory? */
|
||||
$dir = trim( str_replace( \IPS\CP_DIRECTORY . '/index.php', '', $_SERVER['PHP_SELF'] ), '/' );
|
||||
|
||||
/* Oh, the community is in a directory, so we need the user to download the file and manually upload it to root */
|
||||
if ( $dir )
|
||||
{
|
||||
$rootUrl = trim( str_replace( $dir, '', (string) \IPS\Http\Url::baseUrl() ), '/' );
|
||||
if ( \IPS\Settings::i()->robots_txt === 'off' )
|
||||
{
|
||||
$form->addMessage( \IPS\Member::loggedIn()->language()->addToStack( 'use_robotstxt_warning_off', FALSE, ['sprintf' => [$rootUrl]] ), 'ipsMessage ipsMessage_warning' );
|
||||
}
|
||||
else
|
||||
{
|
||||
$form->addMessage( \IPS\Member::loggedIn()->language()->addToStack( 'use_robotstxt_warning_download', FALSE, ['sprintf' => [$rootUrl]] ), 'ipsMessage ipsMessage_warning' );
|
||||
}
|
||||
}
|
||||
else if ( ! \IPS\CIC2 AND file_exists( \IPS\ROOT_PATH . '/robots.txt' ) )
|
||||
{
|
||||
if ( \IPS\Settings::i()->robots_txt === 'off' )
|
||||
{
|
||||
$form->addMessage( \IPS\Member::loggedIn()->language()->addToStack( 'use_robotstxt_warning_existing_off', FALSE, ['sprintf' => [ rtrim( \IPS\Http\Url::baseUrl(), '/' ) ] ] ), 'ipsMessage ipsMessage_warning' );
|
||||
}
|
||||
else
|
||||
{
|
||||
$form->addMessage( \IPS\Member::loggedIn()->language()->addToStack( 'use_robotstxt_warning_existing_download', FALSE, ['sprintf' => [ rtrim( \IPS\Http\Url::baseUrl(), '/' ) ] ] ), 'ipsMessage ipsMessage_warning' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'use_robotstxt', $value, FALSE, [
|
||||
'options' => [
|
||||
'off' => 'use_robotstxt_off',
|
||||
'default' => 'use_robotstxt_default',
|
||||
'custom' => 'use_robotstxt_custom'
|
||||
],
|
||||
'toggles' => [
|
||||
'custom' => [ 'use_robotstxt_custom_editor' ]
|
||||
]
|
||||
], NULL, NULL, NULL, 'use_robotstxt' ) );
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Codemirror( 'use_robotstxt_custom_editor', ( $value === 'custom' ? \IPS\Settings::i()->robots_txt : '' ), FALSE, [], NULL, NULL, NULL, 'use_robotstxt_custom_editor' ) );
|
||||
|
||||
/* Are we saving? */
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
$value = $values['use_robotstxt'];
|
||||
|
||||
if ( $values['use_robotstxt'] === 'custom' )
|
||||
{
|
||||
$value = $values['use_robotstxt_custom_editor'];
|
||||
}
|
||||
|
||||
$form->saveAsSettings( [ 'robots_txt' => $value ] );
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=promotion&controller=seo&tab=robotstxt&_saved=1" ) );
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the robots.txt file
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function downloadRobotstxt()
|
||||
{
|
||||
if ( \IPS\Settings::i()->robots_txt != 'off' )
|
||||
{
|
||||
if ( \IPS\Settings::i()->robots_txt == 'default' )
|
||||
{
|
||||
\IPS\Output::i()->sendOutput( \IPS\Dispatcher\Front::robotsTxtRules(), 200, 'application/x-robotstxt', array( 'Content-Disposition' => 'attachment; filename=robots.txt' ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->sendOutput( \IPS\Settings::i()->robots_txt, 200, 'application/x-robotstxt', array( 'Content-Disposition' => 'attachment; filename=robots.txt' ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage FURL definitions
|
||||
*
|
||||
@@ -526,7 +627,7 @@ Options -MultiViews
|
||||
RewriteEngine On
|
||||
RewriteBase {$dir}
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule \\.(js|css|jpeg|jpg|gif|png|ico|map)(\\?|$) {$dir}404error.php [L,NC]
|
||||
RewriteRule \\.(js|css|jpeg|jpg|gif|png|ico|map|webp)(\\?|$) {$dir}404error.php [L,NC]
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
|
||||
@@ -76,6 +76,7 @@ class _advanced extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
$tabs['datastore'] = 'data_store';
|
||||
}
|
||||
$tabs['pageoutput'] = 'page_output';
|
||||
|
||||
/* Display */
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_settings_advanced');
|
||||
@@ -546,6 +547,28 @@ class _advanced extends \IPS\Dispatcher\Controller
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _managePageoutput()
|
||||
{
|
||||
/* Build and show form */
|
||||
$form = new \IPS\Helpers\Form;
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Codemirror( 'custom_body_code', \IPS\Settings::i()->custom_body_code, FALSE, array('height' => 150, 'mode' => 'javascript'), NULL, NULL, NULL, 'custom_body_code' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Codemirror( 'custom_page_view_js', \IPS\Settings::i()->custom_page_view_js, FALSE, array('height' => 150, 'mode' => 'javascript'), NULL, NULL, NULL, 'custom_page_view_js' ) );
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
$form->saveAsSettings( $values );
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=settings&controller=advanced&tab=pageoutput' ), 'saved' );
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tasks
|
||||
*
|
||||
@@ -570,6 +593,8 @@ class _advanced extends \IPS\Dispatcher\Controller
|
||||
|
||||
$table->sortBy = $table->sortBy ?: 'next_run';
|
||||
$table->sortDirection = $table->sortDirection ?: 'asc';
|
||||
$table->noSort = array( 'frequency' );
|
||||
|
||||
$table->quickSearch = function( $val )
|
||||
{
|
||||
$matches = \IPS\Member::loggedIn()->language()->searchCustom( 'task__', $val, TRUE );
|
||||
|
||||
@@ -0,0 +1,769 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief dataLayer
|
||||
* @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 08 Feb 2022
|
||||
*/
|
||||
|
||||
namespace IPS\core\modules\admin\settings;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* dataLayer
|
||||
*/
|
||||
class _dataLayer extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
/**
|
||||
* @breif the tabs
|
||||
*/
|
||||
protected static $tabs = array(
|
||||
'main' => 'datalayer_main',
|
||||
'pageContext' => 'datalayer_pageContext',
|
||||
'properties' => 'datalayer_properties',
|
||||
'events' => 'datalayer_events',
|
||||
);
|
||||
|
||||
/**
|
||||
* @breif Makes this controller work
|
||||
*/
|
||||
public static $csrfProtected = true;
|
||||
|
||||
/**
|
||||
* @brief used to actually handle events and properties in the browser
|
||||
*/
|
||||
public static $handlerClass = '\IPS\core\DataLayer\Handler';
|
||||
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'dataLayer_manage' );
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'menu__core_settings_dataLayer' );
|
||||
|
||||
/* Can we use custom handlers? Only show the tab on sites that have full analytics */
|
||||
$class = static::$handlerClass;
|
||||
if (
|
||||
class_exists( $class ) AND
|
||||
class_exists( 'IPS\cloud\Application' ) AND
|
||||
method_exists( $class, 'handlerForm' ) AND
|
||||
method_exists( $class, 'loadWhere' ) AND
|
||||
\IPS\Member::loggedIn()->hasAcpRestriction( 'dataLayer_handlers_view' ) AND
|
||||
\IPS\cloud\Application::featureIsEnabled('analytics_full')
|
||||
)
|
||||
{
|
||||
static::$tabs = array(
|
||||
'main' => 'datalayer_main',
|
||||
'pageContext' => 'datalayer_pageContext',
|
||||
'handlers' => 'datalayer_handlers',
|
||||
'properties' => 'datalayer_properties',
|
||||
'events' => 'datalayer_events',
|
||||
);
|
||||
}
|
||||
|
||||
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'settings/general.css', 'core', 'admin' ) );
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Call Magic Method, seamless way to integrate cloud features if there are any
|
||||
*
|
||||
* @return string|void
|
||||
*/
|
||||
public function __call( $name, $arguments )
|
||||
{
|
||||
if ( method_exists( $this, $name ) )
|
||||
{
|
||||
return \call_user_func_array( $this->$name, $arguments );
|
||||
}
|
||||
elseif ( method_exists( \IPS\core\DataLayer::i(), $name ) )
|
||||
{
|
||||
$method = array( \IPS\core\DataLayer::i(), $name );
|
||||
return \call_user_func_array( $method, $arguments );
|
||||
}
|
||||
|
||||
return "You should Upgrade!";
|
||||
}
|
||||
|
||||
/**
|
||||
* ...
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
$tab = \IPS\Request::i()->tab;
|
||||
$tab = empty( static::$tabs[ $tab ] ) ? 'main' : $tab;
|
||||
$output = $this->$tab();
|
||||
$output = "<div id='dataLayerContent'>$output</div>" ;
|
||||
|
||||
if ( isset( static::$tabs['handlers'] ) AND \IPS\Member::loggedIn()->hasAcpRestriction( 'dataLayer_handlers_edit' ) )
|
||||
{
|
||||
\IPS\Output::i()->sidebar['actions']['addHandler'] = array(
|
||||
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'datalayer_handler_form' ),
|
||||
'icon' => 'plus',
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=settings&controller=dataLayer&do=addHandler' ),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack( 'datalayer_handler_form' ) )
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
$addPropertyUrl = \IPS\Http\Url::internal( 'app=core&module=settings&controller=dataLayer&do=addProperty' );
|
||||
\IPS\Output::i()->sidebar['actions']['addProperty'] = array(
|
||||
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'datalayer_add_property' ),
|
||||
'icon' => 'plus',
|
||||
'link' => $addPropertyUrl,
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack( 'datalayer_add_property' ) )
|
||||
);
|
||||
|
||||
if ( \IPS\Request::i()->isAjax() )
|
||||
{
|
||||
\IPS\Output::i()->output = $output;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( isset( $_SESSION['deleted_datalayer_property'] ) )
|
||||
{
|
||||
\IPS\Output::i()->inlineMessage = 'Deleted Property';
|
||||
unset( $_SESSION['deleted_datalayer_property'] );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global', 'core', 'admin' )->tabs(
|
||||
static::$tabs,
|
||||
$tab,
|
||||
$output,
|
||||
\IPS\Http\Url::internal( 'app=core&module=settings&controller=dataLayer', 'admin' )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the general settings form
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function main()
|
||||
{
|
||||
$form = new \IPS\Helpers\Form( 'datalayer_general' );
|
||||
|
||||
$form->addHeader('Settings');
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'core_datalayer_enabled', \IPS\Settings::i()->core_datalayer_enabled, false ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'core_datalayer_include_pii', \IPS\Settings::i()->core_datalayer_include_pii, false, array( 'togglesOn' => [ 'datalayer_general_core_datalayer_member_pii_choice' ] ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'core_datalayer_member_pii_choice', \IPS\Settings::i()->core_datalayer_member_pii_choice, false ) );
|
||||
|
||||
try
|
||||
{
|
||||
$default = \IPS\Login\Handler::load( \IPS\Settings::i()->core_datalayer_replace_with_sso );
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
{
|
||||
$default = 0;
|
||||
}
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Node( 'core_datalayer_replace_with_sso', $default, false, array(
|
||||
'class' => '\IPS\Login\Handler',
|
||||
'zeroVal' => 'Use internal Member ID (default)',
|
||||
'where' => array(
|
||||
['login_enabled=?', 1],
|
||||
['login_classname LIKE ?', '%Login_Handler%'],
|
||||
['login_classname NOT LIKE ?', '%Login_Handler_Standard']
|
||||
),
|
||||
|
||||
) ) );
|
||||
|
||||
$form->addHeader('enhancements__core_GoogleTagManager');
|
||||
$form->addMessage( 'use_gtm_for_installation', 'ipsMessage ipsMessage_info ipsMargin' );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'core_datalayer_use_gtm', \IPS\Settings::i()->core_datalayer_use_gtm, false ) );
|
||||
$gtmkey = explode( '.', \IPS\Settings::i()->core_datalayer_gtmkey );
|
||||
$form->add( new \IPS\Helpers\Form\Text(
|
||||
'core_datalayer_gtmkey',
|
||||
array_pop( $gtmkey ),
|
||||
false,
|
||||
array(),
|
||||
function( $_value ) {
|
||||
/* Check for invalid characters */
|
||||
if ( preg_match( '/[^a-z,A-Z,0-9,\_]/', $_value ) )
|
||||
{
|
||||
throw new \InvalidArgumentException( 'The variable name can only contain alphanumeric characters and underscores.' );
|
||||
}
|
||||
elseif ( preg_match( '/[0-9]/', \mb_substr( $_value, 0, 1 ) ) )
|
||||
{
|
||||
throw new \InvalidArgumentException( 'The variable name cannot start with a number.' );
|
||||
}
|
||||
|
||||
/* Is a custom handler using this? Note that, in JS, window.variable and variable are nearly always the same */
|
||||
$where = [
|
||||
[ 'enabled=?', 1 ],
|
||||
[ 'use_js=?', 1 ],
|
||||
\IPS\Db::i()->in( 'datalayer_key', [ $_value, "window.$_value" ] ),
|
||||
];
|
||||
|
||||
if (
|
||||
class_exists( 'IPS\cloud\DataLayer' ) AND
|
||||
\IPS\core\DataLayer::i() instanceof \IPS\cloud\DataLayer AND
|
||||
\count( \IPS\Db::i()->select( 'datalayer_key', \IPS\cloud\DataLayer\Handler::$databaseTable, $where, null, 1 ) )
|
||||
)
|
||||
{
|
||||
throw new \InvalidArgumentException( "$_value or window.$_value is in use by a custom handler." );
|
||||
}
|
||||
},
|
||||
'<span class="ipsType_monospace">window.</span>'
|
||||
));
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
|
||||
if ( isset( $values['core_datalayer_replace_with_sso'] ) )
|
||||
{
|
||||
$values['core_datalayer_replace_with_sso'] = $values['core_datalayer_replace_with_sso'] ? $values['core_datalayer_replace_with_sso']->_id : 0;
|
||||
}
|
||||
|
||||
/* Prefix the gtmkey with window. */
|
||||
if ( isset( $values['core_datalayer_gtmkey'] ) )
|
||||
{
|
||||
$values['core_datalayer_gtmkey'] = "window.{$values['core_datalayer_gtmkey']}";
|
||||
}
|
||||
|
||||
/* Since GTM is a handler, cachebust the handler for template if it was updated */
|
||||
foreach( ['core_datalayer_use_gtm', 'core_datalayer_gtmkey', 'googletag_head_code', 'googletag_noscript_code'] as $input )
|
||||
{
|
||||
if ( isset( $values[$input] ) )
|
||||
{
|
||||
$key = \IPS\core\DataLayer\Handler::$handlerCacheKey;
|
||||
unset( \IPS\Data\Store::i()->$key );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear the cached configuration since we're changing it */
|
||||
\IPS\core\DataLayer::i()->clearCachedConfiguration([ '_jsConfig', '_eventProperties', '_propertyEvents' ]);
|
||||
|
||||
\IPS\Settings::i()->changeValues( $values );
|
||||
|
||||
/* Redirect to avoid csrfKey errors */
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=settings&controller=dataLayer&tab=main' ) );
|
||||
}
|
||||
|
||||
return "<br>$form";
|
||||
}
|
||||
|
||||
/**
|
||||
* Property settings and configuration
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function pageContext()
|
||||
{
|
||||
/* Get the page level properties */
|
||||
$_properties = \IPS\core\DataLayer::i()->propertiesConfiguration;
|
||||
foreach ( $_properties as $k => $property )
|
||||
{
|
||||
if ( $property['page_level'] AND $property['enabled'] )
|
||||
{
|
||||
$properties[$k] = array(
|
||||
'name' => $property['formatted_name'],
|
||||
'type' => $property['type'],
|
||||
'pii' => $property['pii'],
|
||||
'custom' => $property['custom'] ?? 0,
|
||||
'short' => $property['short'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$propertyBlock = new \IPS\Helpers\Table\Custom(
|
||||
$properties,
|
||||
\IPS\Http\Url::internal( 'app=core&module=settings&controller=dataLayer&tab=pageContext' )
|
||||
);
|
||||
$propertyBlock->classes[] = 'ipsPadding:none ipsMargin:none';
|
||||
$propertyBlock->exclude = array('custom', 'description');
|
||||
$propertyBlock->langPrefix = 'datalayer_';
|
||||
|
||||
if ( !\IPS\Request::i()->sortby )
|
||||
{
|
||||
$propertyBlock->sortBy = 'name';
|
||||
}
|
||||
|
||||
if ( !\IPS\Request::i()->sortdirection )
|
||||
{
|
||||
$propertyBlock->sortDirection = 'asc';
|
||||
}
|
||||
|
||||
$propertyBlock->parsers = array(
|
||||
'pii' => array( $this, '_yesNo' ),
|
||||
'name' => function ( $val, $row ) {
|
||||
$url = \IPS\Http\Url::internal('app=core&module=settings&controller=dataLayer&tab=properties&property_key=' . $val);
|
||||
$title = $row['short'];
|
||||
return "<span class='ipsType_monospace ipsMargin:none'><a href='$url' data-ipstooltip title='$title'>$val</a></span>";
|
||||
},
|
||||
);
|
||||
|
||||
return \IPS\Theme::i()->getTemplate( 'settings', 'core', 'admin' )->dataLayerContext( $propertyBlock );
|
||||
}
|
||||
|
||||
/**
|
||||
* Property settings and configuration
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function properties()
|
||||
{
|
||||
/* Load our properties */
|
||||
$properties = \IPS\core\DataLayer::i()->propertiesConfiguration;
|
||||
$property_key = \IPS\Request::i()->property_key ?: array_keys( $properties )[0];
|
||||
|
||||
/* Do we know the requested property? */
|
||||
if ( !isset( $properties[$property_key] ) )
|
||||
{
|
||||
\IPS\Output::i()->redirect( \IPS\Request::i()->url()->stripQueryString([ 'property_key' ]) );
|
||||
}
|
||||
$property = $properties[$property_key];
|
||||
|
||||
/* Sidebar */
|
||||
$propertySelector = \IPS\Theme::i()->getTemplate( 'settings', 'core', 'admin' )->dataLayerSelector(
|
||||
$properties,
|
||||
'properties',
|
||||
'property_key',
|
||||
'Data Layer Properties',
|
||||
$property_key,
|
||||
true,
|
||||
array( $this, '_truncate' )
|
||||
);
|
||||
|
||||
/* Form */
|
||||
$form = new \IPS\Helpers\Form( 'datalayer_properties_' . $property_key, 'save', \IPS\Http\Url::internal( 'app=core&module=settings&controller=dataLayer&tab=properties&property_key=' . $property_key ) );
|
||||
$form->class = 'ipsForm_vertical';
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'datalayer_property_enabled', $property['enabled'], false ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'datalayer_property_formatted_name', $property['formatted_name'], false, array(), $this->formattedNameValid('properties', $property_key) ) );
|
||||
|
||||
if ( $property['custom'] ?? 0 )
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'datalayer_property_value', $property['value'], true ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'datalayer_property_page_level', $property['page_level'], true ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'datalayer_property_short', $property['short'], false, array( 'maxLength' => 50 ) ) );
|
||||
|
||||
$events = \IPS\core\DataLayer::i()->eventConfiguration;
|
||||
$options = array();
|
||||
foreach ( $events as $key => $data )
|
||||
{
|
||||
$options[$key] = $data['formatted_name'];
|
||||
}
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'datalayer_property_event_keys', $property['event_keys'], false, array('options' => $options, 'multiple' => 1) ) );
|
||||
}
|
||||
|
||||
/* Update the values as long as the property_key was specified in the request */
|
||||
if ( ( $values = $form->values() ) AND \IPS\Request::i()->property_key === $property_key )
|
||||
{
|
||||
/* Get our submitted values */
|
||||
$_values = array();
|
||||
if ( isset( $values['datalayer_property_enabled'] ) )
|
||||
{
|
||||
$_values['enabled'] = (bool) $values['datalayer_property_enabled'];
|
||||
}
|
||||
|
||||
if ( isset( $values['datalayer_property_formatted_name'] ) )
|
||||
{
|
||||
$_values['formatted_name'] = $values['datalayer_property_formatted_name'];
|
||||
}
|
||||
|
||||
if ( $property['custom'] ?? 0 )
|
||||
{
|
||||
foreach ( $values as $field => $value )
|
||||
{
|
||||
if ( isset( $_values[$field] ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$field = str_replace( 'datalayer_property_', '', $field );
|
||||
$_values[$field] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/* Save the property */
|
||||
if ( !empty( $_values ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
\IPS\core\DataLayer::i()->savePropertyConfiguration( $property_key, $_values );
|
||||
}
|
||||
catch ( \InvalidArgumentException $e ) {}
|
||||
}
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=settings&controller=dataLayer&tab=properties&property_key=' . $property_key ) );
|
||||
}
|
||||
|
||||
/* Load our events */
|
||||
$_events = \IPS\core\DataLayer::i()->getPropertyEvents( $property_key );
|
||||
$events = array();
|
||||
foreach( $_events as $key => $event )
|
||||
{
|
||||
$events[$key] = array(
|
||||
'name' => $event['formatted_name'],
|
||||
'key' => $key,
|
||||
'enabled' => $event['enabled'],
|
||||
'short' => $event['short'],
|
||||
);
|
||||
}
|
||||
|
||||
/* Property's events table */
|
||||
if ( empty( $events ) )
|
||||
{
|
||||
$eventTable = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
$eventTable = new \IPS\Helpers\Table\Custom(
|
||||
$events,
|
||||
\IPS\Http\Url::internal( 'app=core&module=settings&controller=dataLayer&tab=properties&property_key=' . $property_key )
|
||||
);
|
||||
|
||||
|
||||
|
||||
if ( !\IPS\Request::i()->sortby )
|
||||
{
|
||||
$eventTable->sortBy = 'name';
|
||||
}
|
||||
if ( !\IPS\Request::i()->sortdirection )
|
||||
{
|
||||
$eventTable->sortDirection = 'asc';
|
||||
}
|
||||
|
||||
$eventTable->langPrefix = 'datalayer_';
|
||||
$eventTable->exclude = array( 'description', 'key' );
|
||||
$eventTable->parsers = array(
|
||||
'name' => function ( $val, $row )
|
||||
{
|
||||
$url = \IPS\Http\Url::internal('app=core&module=settings&controller=dataLayer&tab=events&event_key=' . $row['key']);
|
||||
$title = $row['short'];
|
||||
return "<span class='ipsMargin:none ipsType_monospace'><a href='$url' data-ipstooltip title='$title'>$val</a></span>";
|
||||
},
|
||||
'enabled' => array( $this, '_enabledDisabled' ),
|
||||
);
|
||||
}
|
||||
|
||||
/* Render Content */
|
||||
$content = \IPS\Theme::i()->getTemplate( 'settings', 'core', 'admin' )->dataLayerTitleContent(
|
||||
$property,
|
||||
$property_key,
|
||||
'property',
|
||||
$form,
|
||||
(string) $eventTable
|
||||
);
|
||||
|
||||
return \IPS\Theme::i()->getTemplate( 'settings', 'core', 'admin' )->dataLayerTab( $propertySelector, $content );
|
||||
}
|
||||
|
||||
/**
|
||||
* Property settings and configuration
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function addProperty()
|
||||
{
|
||||
\IPS\Output::i()->title = 'Add property';
|
||||
$form = new \IPS\Helpers\Form( 'datalayer_add_property' );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'datalayer_property_formatted_name', null, true, array(), $this->formattedNameValid() ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'datalayer_property_value', null, true ) );
|
||||
$form->add( new \IPS\Helpers\Form\TextArea( 'datalayer_property_description', null, true ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'datalayer_property_short', null, false, array( 'maxLength' => 50 ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'datalayer_property_page_level', 1, true ) );
|
||||
|
||||
$events = \IPS\core\DataLayer::i()->eventConfiguration;
|
||||
$options = array();
|
||||
foreach ( $events as $key => $data )
|
||||
{
|
||||
$options[$key] = $data['formatted_name'];
|
||||
}
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'datalayer_property_event_keys', array(), false, array('options' => $options, 'multiple' => 1) ) );
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
$_values = array();
|
||||
foreach ( $values as $field => $value )
|
||||
{
|
||||
$field = str_replace( 'datalayer_property_', '', $field );
|
||||
$_values[$field] = $value;
|
||||
}
|
||||
$values = $_values;
|
||||
|
||||
$values['enabled'] = true;
|
||||
$key = $values['formatted_name'];
|
||||
|
||||
\IPS\core\DataLayer::i()->savePropertyConfiguration( $key, $values, true );
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=settings&controller=dataLayer&tab=properties&property_key=' . $key ) );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->output = (string) $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a property
|
||||
*/
|
||||
protected function deleteProperty()
|
||||
{
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
|
||||
$key = \IPS\Request::i()->property_key;
|
||||
$propertiesUrl = \IPS\Http\Url::internal( 'app=core&module=settings&controller=dataLayer&tab=properties' );
|
||||
if ( empty( $key ) )
|
||||
{
|
||||
\IPS\Output::i()->redirect( $propertiesUrl );
|
||||
}
|
||||
|
||||
$properties = \IPS\core\DataLayer::i()->propertiesConfiguration;
|
||||
if ( isset( $properties[$key] ) )
|
||||
{
|
||||
if ( $properties[$key]['custom'] ?? 0 )
|
||||
{
|
||||
$setting = json_decode( \IPS\Settings::i()->core_datalayer_properties, true ) ?: array();
|
||||
unset( $setting[$key] );
|
||||
\IPS\Settings::i()->changeValues([ 'core_datalayer_properties' => json_encode( $setting ) ]);
|
||||
\IPS\core\DataLayer::i()->clearCachedConfiguration();
|
||||
$_SESSION['deleted_datalayer_property'] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
$propertiesUrl = $propertiesUrl->setQueryString([ 'property_key' => $key ]);
|
||||
}
|
||||
}
|
||||
\IPS\Output::i()->redirect( $propertiesUrl );
|
||||
}
|
||||
|
||||
/**
|
||||
* Event settings and configuration
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function events()
|
||||
{
|
||||
/* Load our events */
|
||||
$events = \IPS\core\DataLayer::i()->eventConfiguration;
|
||||
$event_key = \IPS\Request::i()->event_key ?: 'content_create';
|
||||
|
||||
/* Do we know the recognized one? */
|
||||
if ( !isset( $events[$event_key] ) )
|
||||
{
|
||||
\IPS\Output::i()->redirect( \IPS\Request::i()->url()->stripQueryString([ 'event_key' ]) );
|
||||
}
|
||||
$event = $events[$event_key];
|
||||
|
||||
foreach ( array_keys( $events ) as $_eventKey )
|
||||
{
|
||||
$events[$_eventKey]['description'] = 'Fires when ' . $events[$_eventKey]['description'];
|
||||
}
|
||||
|
||||
/* Create our sidebar */
|
||||
$eventSelector = \IPS\Theme::i()->getTemplate( 'settings', 'core', 'admin' )->dataLayerSelector(
|
||||
$events,
|
||||
'events',
|
||||
'event_key',
|
||||
'Data Layer Events',
|
||||
$event_key,
|
||||
true,
|
||||
array( $this, '_truncate' )
|
||||
);
|
||||
|
||||
/* Form */
|
||||
$form = new \IPS\Helpers\Form( 'datalayer_events_' . $event_key, 'save', \IPS\Http\Url::internal( 'app=core&module=settings&controller=dataLayer&tab=events&event_key=' . $event_key ) );
|
||||
$form->class = 'ipsForm_vertical';
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'datalayer_event_enabled', $event['enabled'], false ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'datalayer_event_formatted_name', $event['formatted_name'], false, array(), $this->formattedNameValid( 'events', $event_key ) ) );
|
||||
|
||||
/* We only want to change values when a real event is specified in the request */
|
||||
if ( ( $values = $form->values() ) AND \IPS\Request::i()->event_key === $event_key )
|
||||
{
|
||||
/* Pull out the values to use */
|
||||
$_values = array();
|
||||
if ( isset( $values['datalayer_event_enabled'] ) )
|
||||
{
|
||||
$_values['enabled'] = (bool) $values['datalayer_event_enabled'];
|
||||
}
|
||||
|
||||
if ( isset( $values['datalayer_event_formatted_name'] ) )
|
||||
{
|
||||
$_values['formatted_name'] = $values['datalayer_event_formatted_name'];
|
||||
}
|
||||
|
||||
/* Save the event */
|
||||
if ( !empty( $_values ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
\IPS\core\DataLayer::i()->saveEventConfiguration( $event_key, $_values );
|
||||
}
|
||||
catch ( \InvalidArgumentException $e ) {}
|
||||
}
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=settings&controller=dataLayer&tab=events&event_key=' . $event_key ) );
|
||||
}
|
||||
|
||||
|
||||
/* Load our properties */
|
||||
$_properties = \IPS\core\DataLayer::i()->getEventProperties( $event_key );
|
||||
$properties = array();
|
||||
foreach( $_properties as $key => $property )
|
||||
{
|
||||
$properties[$key] = array(
|
||||
'name' => $property['formatted_name'],
|
||||
'key' => $key,
|
||||
'type' => $property['type'],
|
||||
'enabled' => $property['enabled'],
|
||||
'pii' => $property['pii'],
|
||||
'custom' => $property['custom'] ?? 0,
|
||||
'short' => $property['short'],
|
||||
);
|
||||
}
|
||||
|
||||
/* Create our neat properties table */
|
||||
if ( empty( $properties ) )
|
||||
{
|
||||
$propertyTable = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
$propertyTable = new \IPS\Helpers\Table\Custom(
|
||||
$properties,
|
||||
\IPS\Http\Url::internal( 'app=core&module=settings&controller=dataLayer&tab=events&event_key=' . $event_key )
|
||||
);
|
||||
|
||||
if ( !\IPS\Request::i()->sortby )
|
||||
{
|
||||
$propertyTable->sortBy = 'name';
|
||||
}
|
||||
|
||||
if ( !\IPS\Request::i()->sortdirection )
|
||||
{
|
||||
$propertyTable->sortDirection = 'asc';
|
||||
}
|
||||
|
||||
$propertyTable->langPrefix = 'datalayer_';
|
||||
$propertyTable->exclude = array( 'custom', 'short', 'key' );
|
||||
$propertyTable->parsers = array(
|
||||
'enabled' => array( $this, '_enabledDisabled' ),
|
||||
'pii' => array( $this, '_yesNo' ),
|
||||
'name' => function ( $val, $row )
|
||||
{
|
||||
$url = \IPS\Http\Url::internal( 'app=core&module=settings&controller=dataLayer&tab=properties&property_key=' . $row['key'] );
|
||||
$title = $row['short'];
|
||||
return "<pre class='ipsMargin:none'><a href='$url' data-ipstooltip title='$title' >$val</a></pre>";
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/* Render Content */
|
||||
$event['description'] = "Fires when {$event['description']}";
|
||||
$content = \IPS\Theme::i()->getTemplate( 'settings', 'core', 'admin' )->dataLayerTitleContent(
|
||||
$event,
|
||||
$event_key,
|
||||
'event',
|
||||
(string) $form,
|
||||
(string) $propertyTable
|
||||
);
|
||||
|
||||
return \IPS\Theme::i()->getTemplate( 'settings', 'core', 'admin' )->dataLayerTab( $eventSelector, $content );
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the value a valid property or event formatted name?
|
||||
*
|
||||
* @param string $group Either properties or events; the datalayer collection to test the key against
|
||||
* @param ?string $current The key of the property/event that currently has this value set as its formatted name
|
||||
*
|
||||
* @return callback
|
||||
*/
|
||||
public function formattedNameValid( string $group='properties', ?string $current=null )
|
||||
{
|
||||
return function( $value ) use ( $group, $current )
|
||||
{
|
||||
if ( $group === 'properties' AND \strtolower( $value ) === 'event' )
|
||||
{
|
||||
throw new \InvalidArgumentException( "You cannot name a property '$value' because that key is reserved" );
|
||||
}
|
||||
|
||||
if ( preg_match( '/[^a-z,A-Z,\_]/', $value ) )
|
||||
{
|
||||
throw new \InvalidArgumentException( 'The name can only contain letters and underscores.' );
|
||||
}
|
||||
|
||||
$collection = ( $group === 'events' ) ? \IPS\core\DataLayer::i()->eventConfiguration : \IPS\core\DataLayer::i()->propertiesConfiguration;
|
||||
foreach ( $collection as $key => $data )
|
||||
{
|
||||
if ( $key === $current )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( isset( $data['formatted_name'] ) AND $data['formatted_name'] === $value )
|
||||
{
|
||||
throw new \InvalidArgumentException( 'This Data Layer name is already in use' );
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Formatter method to make code cleaner
|
||||
*
|
||||
* @param mixed $val
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function _enabledDisabled( $val, $row=null )
|
||||
{
|
||||
return $val ? 'Enabled' : 'Disabled';
|
||||
}
|
||||
|
||||
/**
|
||||
* Formatter method to make code cleaner
|
||||
*
|
||||
* @param mixed $val
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function _yesNo( $val, $row=null )
|
||||
{
|
||||
return $val ? 'Yes' : 'No';
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncates the string and removes tags. Ends in ... if the string is longer than the limit
|
||||
*
|
||||
* @param string $_string The string to truncate
|
||||
* @param int $length The desired length
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function _truncate( $_string, $length=100 )
|
||||
{
|
||||
$_string = strip_tags( $_string );
|
||||
$string = \substr( $_string, 0, $length );
|
||||
$changedLength = ( \strlen( $_string ) !== \strlen( $string ) );
|
||||
$string = trim( $string, ". \n\r\t\v\x00" );
|
||||
if ( $changedLength )
|
||||
{
|
||||
$string = \trim( \substr( $string, 0, $length - 3 ), ". \n\r\t\v\x00" ) . '...';
|
||||
}
|
||||
return $string;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -71,7 +71,7 @@ class _email extends \IPS\Dispatcher\Controller
|
||||
$method = \IPS\Settings::i()->mail_method;
|
||||
$disabled = array();
|
||||
|
||||
if ( \IPS\Settings::i()->sendgrid_api_key )
|
||||
if ( \IPS\Settings::i()->sendgrid_api_key AND \IPS\Settings::i()->sendgrid_use_for > 0 )
|
||||
{
|
||||
if ( \IPS\Settings::i()->sendgrid_use_for == 2 )
|
||||
{
|
||||
@@ -88,7 +88,7 @@ class _email extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
$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,
|
||||
|
||||
@@ -57,10 +57,14 @@ class _general extends \IPS\Dispatcher\Controller
|
||||
$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\YesNo( 'relative_dates_enable', \IPS\Settings::i()->relative_dates_enable, FALSE ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'site_site_elsewhere', \IPS\Settings::i()->site_site_elsewhere, FALSE, [ 'togglesOn' => [ 'site_main_url', 'site_main_title' ] ] ) );
|
||||
$form->add( new \IPS\Helpers\Form\Url( 'site_main_url', \IPS\Settings::i()->site_main_url, FALSE, [], NULL, NULL, NULL, 'site_main_url' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'site_main_title', \IPS\Settings::i()->site_main_title, FALSE, [], NULL, NULL, NULL, 'site_main_title' ) );
|
||||
/* $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',
|
||||
) ) ) ); */
|
||||
) ) ) );
|
||||
*/
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
@@ -76,9 +80,9 @@ class _general extends \IPS\Dispatcher\Controller
|
||||
|
||||
$values['site_address'] = json_encode( $values['site_address'] );
|
||||
|
||||
// $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 );
|
||||
|
||||
|
||||
@@ -100,7 +100,14 @@ class _licensekey extends \IPS\Dispatcher\Controller
|
||||
unset( \IPS\Data\Store::i()->license_data );
|
||||
|
||||
/* Return the overview screen afterwards */
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=settings&controller=licensekey' ), 'license_key_refreshed' );
|
||||
if ( isset( \IPS\Request::i()->return ) and \IPS\Request::i()->return === 'cloud' and \IPS\Application::appIsEnabled('cloud') )
|
||||
{
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=cloud&module=smartcommunity&controller=smartcommunity' ), 'cloud_license_refreshed' );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=settings&controller=licensekey' ), 'license_key_refreshed' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -308,7 +308,24 @@ class _posting extends \IPS\Dispatcher\Controller
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'links_external', \IPS\Settings::i()->links_external ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'posts_add_nofollow', \IPS\Settings::i()->posts_add_nofollow, FALSE, array( 'togglesOn' => array( 'posts_add_nofollow_exclude' ) ), NULL, NULL, NULL, 'posts_add_nofollow' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Stack( 'posts_add_nofollow_exclude', \IPS\Settings::i()->posts_add_nofollow_exclude ? json_decode( \IPS\Settings::i()->posts_add_nofollow_exclude ) : array(), FALSE, array( 'placeholder' => 'example.com' ), NULL, NULL, NULL, 'posts_add_nofollow_exclude' ) );
|
||||
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'email_filter_action', \IPS\Settings::i()->email_filter_action, FALSE, array(
|
||||
'options' => array(
|
||||
'allow' => 'email_filter_action_allow',
|
||||
'moderate' => 'email_filter_action_moderate',
|
||||
'replace' => 'email_filter_action_replace'
|
||||
),
|
||||
'description' => array(
|
||||
'allow' => 'email_filter_action_allow_desc',
|
||||
'moderate' => 'email_filter_action_moderate_desc',
|
||||
'replace' => 'email_filter_action_replace_desc'
|
||||
),
|
||||
'toggles' => array( 'replace' => array( 'email_filter_replace_text' ) )
|
||||
), NULL, NULL, NULL, 'email_filter_action' ) );
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'email_filter_replace_text', \IPS\Settings::i()->email_filter_replace_text, FALSE, array(), NULL, NULL, NULL, 'email_filter_replace_text' ) );
|
||||
|
||||
|
||||
/* Save values */
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
@@ -770,21 +787,6 @@ class _posting extends \IPS\Dispatcher\Controller
|
||||
/* And redirect */
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=settings&controller=posting&tab=acronymExpansion" ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild attachment thumbnails
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function rebuildThumbnails()
|
||||
{
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
|
||||
\IPS\Task::queue( 'core', 'RebuildAttachmentThumbnails', array(), 4 );
|
||||
|
||||
/* And redirect */
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=settings&controller=posting" ), 'thumbnails_rebuilt' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild URL refs
|
||||
|
||||
@@ -184,8 +184,13 @@ class _moderators extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
{
|
||||
$rowId = NULL;
|
||||
|
||||
if( !isset( $values['moderators_type'] ) )
|
||||
{
|
||||
$values['moderators_type'] = isset( $values['moderators_group'] ) ? 'g' : 'm';
|
||||
}
|
||||
|
||||
if ( $values['moderators_type'] === 'g' or !\IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'staff', 'moderators_add_member' ) )
|
||||
{
|
||||
|
||||
@@ -23,6 +23,11 @@ if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
*/
|
||||
class _badges extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
/**
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Allow MySQL RW separation for efficiency
|
||||
*/
|
||||
@@ -40,32 +45,253 @@ class _badges extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Points earned activity chart
|
||||
* Show badges in a modal
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function showBadges()
|
||||
{
|
||||
$where = [
|
||||
[ 'datetime BETWEEN ? AND ?', \IPS\Request::i()->badgeDateStart, \IPS\Request::i()->badgeDateEnd ],
|
||||
[ 'member_group_id=?', \IPS\Request::i()->member_group_id ]
|
||||
];
|
||||
|
||||
$query = \IPS\Db::i()->select( 'badge, COUNT(*) as count', 'core_member_badges', $where, NULL, NULL, 'badge' )
|
||||
->join( 'core_members', [ 'core_members.member_id=core_member_badges.member' ] );
|
||||
|
||||
$results = [];
|
||||
foreach( $query as $row )
|
||||
{
|
||||
try
|
||||
{
|
||||
$row['badge'] = \IPS\core\Achievements\Badge::load( $row['badge'] );
|
||||
$results[] = $row;
|
||||
}
|
||||
catch( \Exception $e ) { }
|
||||
}
|
||||
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'achievements' )->statsBadgeModal( $results );
|
||||
}
|
||||
|
||||
/**
|
||||
* Show member badges in a modal
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function showMemberBadges()
|
||||
{
|
||||
$where = [
|
||||
[ 'datetime BETWEEN ? AND ?', \IPS\Request::i()->badgeDateStart, \IPS\Request::i()->badgeDateEnd ],
|
||||
[ 'member_id=?', \IPS\Request::i()->member_id ]
|
||||
];
|
||||
|
||||
$query = \IPS\Db::i()->select( 'badge, datetime', 'core_member_badges', $where, NULL, NULL )
|
||||
->join( 'core_members', [ 'core_members.member_id=core_member_badges.member' ] );
|
||||
|
||||
$results = [];
|
||||
foreach( $query as $row )
|
||||
{
|
||||
try
|
||||
{
|
||||
$row['badge'] = \IPS\core\Achievements\Badge::load( $row['badge'] );
|
||||
$results[] = $row;
|
||||
}
|
||||
catch( \Exception $e ) { }
|
||||
}
|
||||
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'achievements' )->statsMemberBadgeModal( $results );
|
||||
}
|
||||
|
||||
/**
|
||||
* Badges earned activity chart
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
$chart = new \IPS\Helpers\Chart\Database( \IPS\Http\Url::internal( 'app=core&module=stats&controller=badges' ), 'core_member_badges', 'datetime', '', array(
|
||||
'isStacked' => FALSE,
|
||||
'backgroundColor' => '#ffffff',
|
||||
'colors' => array( '#10967e', '#ea7963', '#de6470', '#6b9dde' ),
|
||||
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4
|
||||
), 'AreaChart', 'daily' );
|
||||
$tabs = array(
|
||||
'type' => 'stats_badges_by_badge',
|
||||
'list' => 'stats_badges_by_group',
|
||||
'member' => 'stats_badges_by_member',
|
||||
);
|
||||
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_badges_title');
|
||||
$chart->availableTypes = array( 'AreaChart', 'ColumnChart', 'BarChart' );
|
||||
$chart->enableHourly = FALSE;
|
||||
$activeTab = ( isset( \IPS\Request::i()->tab ) and array_key_exists( \IPS\Request::i()->tab, $tabs ) ) ? \IPS\Request::i()->tab : 'type';
|
||||
|
||||
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('stats_member_badges'), 'number', 'COUNT(*)', TRUE );
|
||||
if ( $activeTab === 'type' )
|
||||
{
|
||||
$chart = new \IPS\Helpers\Chart\Database( \IPS\Http\Url::internal( 'app=core&module=stats&controller=badges&tab=' . $activeTab ), 'core_member_badges', 'datetime', '', array(
|
||||
'isStacked' => FALSE,
|
||||
'backgroundColor' => '#ffffff',
|
||||
'hAxis' => array('gridlines' => array('color' => '#f5f5f5')),
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4
|
||||
),
|
||||
'AreaChart',
|
||||
'daily',
|
||||
array('start' => 0, 'end' => 0),
|
||||
array(),
|
||||
$activeTab
|
||||
);
|
||||
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_badges_title');
|
||||
$chart->availableTypes = array( 'AreaChart', 'ColumnChart', 'BarChart' );
|
||||
$chart->showIntervals = TRUE;
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack( 'stats_badges_title' );
|
||||
$chart->availableTypes = array('AreaChart', 'ColumnChart', 'BarChart');
|
||||
$chart->enableHourly = FALSE;
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_stats_badges');
|
||||
\IPS\Output::i()->output = (string) $chart;
|
||||
$chart->groupBy = 'badge';
|
||||
|
||||
foreach ( \IPS\core\Achievements\Badge::roots() as $badge )
|
||||
{
|
||||
$chart->addSeries( $badge->_title, 'number', 'COUNT(*)', TRUE, $badge->id );
|
||||
}
|
||||
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack( 'stats_badges_title' );
|
||||
$chart->availableTypes = array('AreaChart', 'ColumnChart', 'BarChart');
|
||||
$chart->showIntervals = TRUE;
|
||||
}
|
||||
elseif ( $activeTab === 'member' )
|
||||
{
|
||||
$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()->badgeDateStart ) AND isset( \IPS\Request::i()->badgeDateEnd ) )
|
||||
{
|
||||
$defaults = array( 'start' => \IPS\DateTime::ts( \IPS\Request::i()->badgeDateStart ), 'end' => \IPS\DateTime::ts( \IPS\Request::i()->badgeDateEnd ) );
|
||||
}
|
||||
|
||||
$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();
|
||||
}
|
||||
|
||||
/* Create the table */
|
||||
$chart = new \IPS\Helpers\Table\Db( 'core_member_badges', \IPS\Http\Url::internal( 'app=core&module=stats&controller=badges&type=member' ), [ 'datetime BETWEEN ? AND ?', $startTime, $endTime ] );
|
||||
$chart->quickSearch = NULL;
|
||||
$chart->selects = [ 'count(*) as count' ];
|
||||
$chart->joins = [
|
||||
[ 'select' => 'member_id', 'from' => 'core_members', 'where' => 'core_members.member_id=core_member_badges.member' ]
|
||||
];
|
||||
|
||||
$chart->groupBy = 'member_id';
|
||||
$chart->langPrefix = 'stats_badges_';
|
||||
$chart->include = [ 'member_id', 'count' ];
|
||||
$chart->mainColumn = 'member_id';
|
||||
$chart->baseUrl = $chart->baseUrl->setQueryString( array( 'badgeDateStart' => $startTime, 'badgeDateEnd' => $endTime, 'tab' => $activeTab ) );
|
||||
$chart->sortBy = 'count';
|
||||
|
||||
/* Custom parsers */
|
||||
$chart->parsers = array(
|
||||
'member_id' => function( $val, $row )
|
||||
{
|
||||
$member = \IPS\Member::load( $val );
|
||||
return \IPS\Theme::i()->getTemplate( 'global', 'core' )->userPhoto( $member, 'tiny' ) . ' ' . $member->link();
|
||||
},
|
||||
'count' => function( $val, $row ) use( $startTime, $endTime )
|
||||
{
|
||||
return \IPS\Theme::i()->getTemplate( 'achievements' )->statsMemberBadgeCount( $val, $row['member_id'], $startTime, $endTime );
|
||||
}
|
||||
);
|
||||
|
||||
$formHtml = $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'stats' ), 'filtersFormTemplate' ) );
|
||||
$chart = \IPS\Theme::i()->getTemplate( 'achievements', 'core' )->statsBadgeWrapper( $formHtml, (string) $chart );
|
||||
}
|
||||
else
|
||||
{
|
||||
$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()->badgeDateStart ) AND isset( \IPS\Request::i()->badgeDateEnd ) )
|
||||
{
|
||||
$defaults = array( 'start' => \IPS\DateTime::ts( \IPS\Request::i()->badgeDateStart ), 'end' => \IPS\DateTime::ts( \IPS\Request::i()->badgeDateEnd ) );
|
||||
}
|
||||
|
||||
$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();
|
||||
}
|
||||
|
||||
/* Create the table */
|
||||
$chart = new \IPS\Helpers\Table\Db( 'core_member_badges', \IPS\Http\Url::internal( 'app=core&module=stats&controller=badges&type=list' ), [ 'datetime BETWEEN ? AND ?', $startTime, $endTime ] );
|
||||
$chart->quickSearch = NULL;
|
||||
$chart->selects = [ 'count(*) as count' ];
|
||||
$chart->joins = [
|
||||
[ 'select' => 'member_group_id', 'from' => 'core_members', 'where' => 'core_members.member_id=core_member_badges.member' ]
|
||||
];
|
||||
|
||||
$chart->groupBy = 'member_group_id';
|
||||
$chart->langPrefix = 'stats_badges_';
|
||||
$chart->include = [ 'member_group_id', 'count' ];
|
||||
$chart->mainColumn = 'member_group_id';
|
||||
$chart->baseUrl = $chart->baseUrl->setQueryString( array( 'badgeDateStart' => $startTime, 'badgeDateEnd' => $endTime, 'tab' => $activeTab ) );
|
||||
|
||||
/* Custom parsers */
|
||||
$chart->parsers = array(
|
||||
'member_group_id' => function( $val, $row )
|
||||
{
|
||||
try
|
||||
{
|
||||
return \IPS\Member\Group::load( $val )->formattedName;
|
||||
}
|
||||
catch ( \Throwable $e )
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'unavailable' );
|
||||
}
|
||||
},
|
||||
'count' => function( $val, $row ) use( $startTime, $endTime )
|
||||
{
|
||||
return \IPS\Theme::i()->getTemplate( 'achievements' )->statsBadgeCount( $val, $row['member_group_id'], $startTime, $endTime );
|
||||
}
|
||||
);
|
||||
|
||||
$formHtml = $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'stats' ), 'filtersFormTemplate' ) );
|
||||
$chart = \IPS\Theme::i()->getTemplate( 'achievements', 'core' )->statsBadgeWrapper( $formHtml, (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_stats_badges' );
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $activeTab, (string)$chart, \IPS\Http\Url::internal( "app=core&module=stats&controller=badges" ), 'tab', '', 'ipsPad' );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief follow
|
||||
* @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 10 Sep 2021
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* follow
|
||||
*/
|
||||
class _follow extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Number of results per page
|
||||
*/
|
||||
const PER_PAGE = 25;
|
||||
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'follow_manage' );
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
$tabs = array(
|
||||
'followers' => 'stats_followers_title',
|
||||
'following' => 'stats_following_title',
|
||||
);
|
||||
$activeTab = ( isset( \IPS\Request::i()->tab ) and array_key_exists( \IPS\Request::i()->tab, $tabs ) ) ? \IPS\Request::i()->tab : 'followers';
|
||||
|
||||
$where = array( 'core_follow.follow_app=? and core_follow.follow_area=?', 'core', 'member' );
|
||||
|
||||
$page = isset( \IPS\Request::i()->page ) ? \intval( \IPS\Request::i()->page ) : 1;
|
||||
|
||||
if( $page < 1 )
|
||||
{
|
||||
$page = 1;
|
||||
}
|
||||
|
||||
$column = ( $activeTab == "followers" ) ? 'follow_rel_id' : 'follow_member_id';
|
||||
|
||||
try
|
||||
{
|
||||
$total = \IPS\Db::i()->select( 'COUNT(DISTINCT(core_follow.' . $column . '))', 'core_follow', $where )->join( 'core_members', 'core_members.member_id = core_follow.' . $column )->first();
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
{
|
||||
$total = 0;
|
||||
}
|
||||
|
||||
if( $total )
|
||||
{
|
||||
$select = \IPS\Db::i()->select( 'core_follow.' . $column . ', count(*) as count', 'core_follow', $where, 'count DESC', array( ( $page - 1 ) * static::PER_PAGE, static::PER_PAGE ), $column )->join( 'core_members', 'core_members.member_id = core_follow.' . $column );
|
||||
$mids = array();
|
||||
|
||||
foreach( $select as $row )
|
||||
{
|
||||
$mids[] = $row[ $column ];
|
||||
}
|
||||
|
||||
$members = array();
|
||||
|
||||
if ( \count( $mids ) )
|
||||
{
|
||||
$members = iterator_to_array( \IPS\Db::i()->select( '*', 'core_members', array( \IPS\Db::i()->in( 'member_id', $mids ) ) )->setKeyField('member_id') );
|
||||
}
|
||||
|
||||
$pagination = \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->pagination(
|
||||
\IPS\Http\Url::internal( 'app=core&module=stats&controller=follow' ),
|
||||
ceil( $total / static::PER_PAGE ),
|
||||
$page,
|
||||
static::PER_PAGE,
|
||||
FALSE
|
||||
);
|
||||
|
||||
$output = \IPS\Theme::i()->getTemplate('stats' )->topFollow( $select, $pagination, $members, $total, $column, $activeTab );
|
||||
}
|
||||
else
|
||||
{
|
||||
$output= \IPS\Theme::i()->getTemplate( 'global', 'core' )->block( NULL, \IPS\Member::loggedIn()->language()->addToStack('no_results'), FALSE , 'ipsPad', NULL, TRUE );
|
||||
}
|
||||
|
||||
if ( \IPS\Request::i()->isAjax() )
|
||||
{
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->paddedBlock( $output, NULL, "ipsPad" );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'menu__core_stats_follow' );
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $activeTab, $output, \IPS\Http\Url::internal( "app=core&module=stats&controller=follow" ), 'tab', '', 'ipsPad' );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Create new methods with the same name as the 'do' parameter which should execute it
|
||||
}
|
||||
@@ -50,6 +50,14 @@ class _membersactivity extends \IPS\Dispatcher\Controller
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
|
||||
$tabs = array(
|
||||
'overview' => 'stats_activity_overview',
|
||||
'inactive' => 'stats_activity_inactive',
|
||||
);
|
||||
|
||||
$activeTab = ( isset( \IPS\Request::i()->tab ) and array_key_exists( \IPS\Request::i()->tab, $tabs ) ) ? \IPS\Request::i()->tab : 'overview';
|
||||
|
||||
$count = NULL;
|
||||
$table = NULL;
|
||||
$start = NULL;
|
||||
@@ -72,7 +80,7 @@ class _membersactivity extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
$defaultGroups = array_keys( $groupOptions );
|
||||
}
|
||||
|
||||
|
||||
$form = new \IPS\Helpers\Form( 'posts', 'continue' );
|
||||
$form->add( new \IPS\Helpers\Form\DateRange( 'date', $defaults, TRUE ) );
|
||||
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'groups', $defaultGroups, FALSE, array( 'options' => $groupOptions ), NULL, NULL, NULL, 'group_filters' ) );
|
||||
@@ -104,7 +112,15 @@ class _membersactivity extends \IPS\Dispatcher\Controller
|
||||
if( $start AND $end )
|
||||
{
|
||||
/* Build our where clause */
|
||||
$where = array( array( 'member_last_post BETWEEN ? AND ?', $startTime, $endTime ) );
|
||||
if( $activeTab == 'overview' )
|
||||
{
|
||||
$where = array( array( 'member_last_post BETWEEN ? AND ?', $startTime, $endTime ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
$where = array( array( 'joined BETWEEN ? AND ?', $startTime, $endTime ) );
|
||||
$where[] = array( 'last_activity = 0' );
|
||||
}
|
||||
|
||||
if( $groups !== NULL )
|
||||
{
|
||||
@@ -113,7 +129,7 @@ class _membersactivity extends \IPS\Dispatcher\Controller
|
||||
|
||||
/* Get the count */
|
||||
$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 );
|
||||
|
||||
@@ -126,7 +142,7 @@ class _membersactivity extends \IPS\Dispatcher\Controller
|
||||
/* Default sort options */
|
||||
$table->sortBy = $table->sortBy ?: 'member_last_post';
|
||||
$table->sortDirection = $table->sortDirection ?: 'desc';
|
||||
|
||||
|
||||
/* Custom parsers */
|
||||
$table->parsers = array(
|
||||
'name' => function( $val, $row )
|
||||
@@ -140,12 +156,12 @@ class _membersactivity extends \IPS\Dispatcher\Controller
|
||||
},
|
||||
'member_last_post' => function( $val, $row )
|
||||
{
|
||||
return \IPS\DateTime::ts( $val )->html();
|
||||
return ( $val ) ? \IPS\DateTime::ts( $val )->html() : \IPS\Member::loggedIn()->language()->addToStack('never');
|
||||
},
|
||||
'group_name' => function( $val, $row )
|
||||
{
|
||||
$secondary = \IPS\Member::constructFromData( $row )->groups;
|
||||
|
||||
|
||||
foreach( $secondary as $k => $v )
|
||||
{
|
||||
if( $v == $row['member_group_id'] or $v == 0 )
|
||||
@@ -153,7 +169,7 @@ class _membersactivity extends \IPS\Dispatcher\Controller
|
||||
unset( $secondary[ $k ] );
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
$secondary[ $k ] = \IPS\Member\Group::load( $v );
|
||||
}
|
||||
|
||||
@@ -169,13 +185,27 @@ class _membersactivity extends \IPS\Dispatcher\Controller
|
||||
},
|
||||
);
|
||||
|
||||
$table->extraHtml = \IPS\Theme::i()->getTemplate( 'stats' )->tableheader( $start, $end, $count, "member_activity_results" );
|
||||
$table->extraHtml = \IPS\Theme::i()->getTemplate( 'stats' )->tableheader( $start, $end, $count, ( $activeTab == "overview" ) ? "member_activity_results" : 'member_activity_results_inactive' );
|
||||
}
|
||||
|
||||
$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');
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'stats' )->memberactivity( $formHtml, $count, $table );
|
||||
$chart = \IPS\Theme::i()->getTemplate( 'stats' )->memberactivity( $formHtml, $count, $table, $activeTab );
|
||||
|
||||
|
||||
if ( \IPS\Request::i()->isAjax() )
|
||||
{
|
||||
\IPS\Output::i()->output = (string) $chart;
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $activeTab, (string) $chart, \IPS\Http\Url::internal( "app=core&module=stats&controller=membersactivity" ), 'tab', '', 'ipsPad' );
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief moderators
|
||||
* @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 22 Sep 2021
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* moderators
|
||||
*/
|
||||
class _moderators extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
/**
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'moderators_manage' );
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
$chart = new \IPS\Helpers\Chart\Database( \IPS\Http\Url::internal( 'app=core&module=stats&controller=moderators'), 'core_moderator_logs', 'ctime', '', array(
|
||||
'isStacked' => TRUE,
|
||||
'backgroundColor' => '#ffffff',
|
||||
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4
|
||||
), 'LineChart', 'monthly', array( 'start' => 0, 'end' => 0 ), array( 'member_id', 'ctime' ), 'spam' );
|
||||
|
||||
$chart->groupBy = 'member_id';
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_moderator_activity_title');
|
||||
$chart->availableTypes = array( 'LineChart', 'AreaChart', 'ColumnChart', 'BarChart' );
|
||||
|
||||
$chart->tableParsers = array(
|
||||
'ctime' => function( $val )
|
||||
{
|
||||
return (string) \IPS\DateTime::ts( $val );
|
||||
}
|
||||
);
|
||||
|
||||
/* Didn't find any specified? Do the default of the top 50 moderators */
|
||||
$where = array();
|
||||
if ( $chart->start instanceof \IPS\DateTime or $chart->end instanceof \IPS\DateTime )
|
||||
{
|
||||
$start = $chart->start instanceof \IPS\DateTime ? $chart->start->getTimestamp() : 0;
|
||||
$end = $chart->end instanceof \IPS\DateTime ? $chart->end->getTimestamp() : time();
|
||||
$where[] = array( 'ctime BETWEEN ? AND ?', $start, $end );
|
||||
}
|
||||
|
||||
/* Get actual moderators */
|
||||
$modMember = [];
|
||||
$modGroup = [];
|
||||
|
||||
foreach ( \IPS\Db::i()->select( '*', 'core_moderators' ) as $mod )
|
||||
{
|
||||
if ( $mod['type'] == 'g' )
|
||||
{
|
||||
$modGroup[] = $mod['id'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$modMember[] = $mod['id'];
|
||||
}
|
||||
}
|
||||
|
||||
$query = [];
|
||||
|
||||
if ( \count( $modGroup ) )
|
||||
{
|
||||
$query[] = \IPS\Db::i()->in( 'member_group_id', $modGroup );
|
||||
$query[] = \IPS\Db::i()->findInSet( 'mgroup_others', $modGroup );
|
||||
}
|
||||
|
||||
if ( \count( $modMember ) )
|
||||
{
|
||||
$query[] = \IPS\Db::i()->in( 'member_id', $modMember );
|
||||
}
|
||||
|
||||
/* I mean $query should never be empty, but lets not wait for a ticket and have to release a patch to find out that it could be... */
|
||||
if ( \count( $query ) )
|
||||
{
|
||||
$where[] = [ 'member_id IN(?)', \IPS\Db::i()->select( 'member_id', 'core_members', implode( ' OR ', $query ) ) ];
|
||||
}
|
||||
|
||||
$topModerators = \IPS\Db::i()->select( 'member_id, COUNT(*) as _mod_count', 'core_moderator_logs', $where, '_mod_count DESC', 50, array( 'member_id' ) );
|
||||
|
||||
foreach ( $topModerators as $moderatorRow )
|
||||
{
|
||||
$member = \IPS\Member::load( $moderatorRow['member_id'] );
|
||||
$chart->addSeries(
|
||||
( $member->member_id ) ? $member->name : \IPS\Member::loggedIn()->language()->addToStack( 'deleted_member' ),
|
||||
'number',
|
||||
'COUNT(*)',
|
||||
TRUE,
|
||||
$moderatorRow['member_id']
|
||||
);
|
||||
}
|
||||
|
||||
if ( \IPS\Request::i()->isAjax() )
|
||||
{
|
||||
\IPS\Output::i()->output = (string) $chart;
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_stats_moderators');
|
||||
\IPS\Output::i()->output = (string) $chart;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief preferences
|
||||
* @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 02 Sep 2021
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* preferences
|
||||
*/
|
||||
class _preferences extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'preferences_manage' );
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
$tabs = array(
|
||||
'theme' => 'stats_member_pref_theme',
|
||||
'lang' => 'stats_member_pref_lang',
|
||||
);
|
||||
$activeTab = ( isset( \IPS\Request::i()->tab ) and array_key_exists( \IPS\Request::i()->tab, $tabs ) ) ? \IPS\Request::i()->tab : 'lang';
|
||||
|
||||
switch( $activeTab )
|
||||
{
|
||||
case 'theme':
|
||||
|
||||
$chart = new \IPS\Helpers\Chart;
|
||||
$counts = iterator_to_array( \IPS\Db::i()->select( 'skin, COUNT(member_id) as count', 'core_members', array( "skin > ?", 0), NULL, NULL, "skin" )->setKeyField( 'skin' ) );
|
||||
|
||||
$chart->addHeader( "theme", "string" );
|
||||
$chart->addHeader( \IPS\Member::loggedIn()->language()->get('chart_members'), "number" );
|
||||
foreach( $counts as $id => $theme )
|
||||
{
|
||||
$chart->addRow( array( \IPS\Member::loggedIn()->language()->addToStack('core_theme_set_title_' . $id ), $theme['count'] ) );
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'lang':
|
||||
|
||||
$chart = new \IPS\Helpers\Chart;
|
||||
$counts = iterator_to_array( \IPS\Db::i()->select( 'language, COUNT(member_id) as count', 'core_members', array( "language > ?", 0), NULL, NULL, "language" )->setKeyField( 'language' ) );
|
||||
|
||||
$chart->addHeader( "language", "string" );
|
||||
$chart->addHeader( \IPS\Member::loggedIn()->language()->get('chart_members'), "number" );
|
||||
|
||||
/* We need to make sure the language exists - otherwise apply the count to the default language. */
|
||||
$rows = [];
|
||||
foreach( $counts as $id => $lang )
|
||||
{
|
||||
try
|
||||
{
|
||||
$l = \IPS\Lang::load( $id );
|
||||
if ( !isset( $rows[ $id ] ) )
|
||||
{
|
||||
$rows[ $id ] = array( 'title' => $l->title, 'count' => 0 );
|
||||
}
|
||||
$rows[ $id ]['count'] += $lang['count'];
|
||||
}
|
||||
catch( \OutOfRangeException $e )
|
||||
{
|
||||
if ( !isset( $rows[ \IPS\Lang::defaultLanguage() ] ) )
|
||||
{
|
||||
$rows[ \IPS\Lang::defaultLanguage() ] = array( 'title' => \IPS\Lang::load( \IPS\Lang::defaultLanguage() )->title, 'count' => 0 );
|
||||
}
|
||||
$rows[ \IPS\Lang::defaultLanguage() ]['count'] += $lang['count'];
|
||||
}
|
||||
}
|
||||
|
||||
/* Now add the rows to the chart */
|
||||
foreach( $rows AS $row )
|
||||
{
|
||||
$chart->addRow( array( $row['title'], $row['count'] ) );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_' . $activeTab . '_title');
|
||||
|
||||
$chart = $chart->render('PieChart', array(
|
||||
'backgroundColor' => '#ffffff',
|
||||
'pieHole' => 0.4,
|
||||
'chartArea' => array(
|
||||
'width' =>"90%",
|
||||
'height' => "90%"
|
||||
)
|
||||
) );
|
||||
|
||||
if ( \IPS\Request::i()->isAjax() )
|
||||
{
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->paddedBlock( (string) $chart, NULL, "ipsPad" );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_stats_preferences');
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $activeTab, (string) $chart, \IPS\Http\Url::internal( "app=core&module=stats&controller=preferences" ), 'tab', '', 'ipsPad' );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
<?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 Sep 2021
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* referrals
|
||||
*/
|
||||
class _referrals extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Number of results per page
|
||||
*/
|
||||
const PER_PAGE = 25;
|
||||
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'referrals_manage' );
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Top Referrers
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
$where = array();
|
||||
if ( isset( \IPS\Request::i()->form ) )
|
||||
{
|
||||
$form = new \IPS\Helpers\Form( 'form', 'go' );
|
||||
|
||||
$default = array(
|
||||
'start' => \IPS\Request::i()->start ? \IPS\DateTime::ts( \IPS\Request::i()->start ) : NULL,
|
||||
'end' => \IPS\Request::i()->end ? \IPS\DateTime::ts( \IPS\Request::i()->end ) : NULL
|
||||
);
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\DateRange( 'stats_date_range', $default, FALSE, array( 'start' => array( 'max' => \IPS\DateTime::ts( time() )->setTime( 0, 0, 0 ), 'time' => FALSE ), 'end' => array( 'max' => \IPS\DateTime::ts( time() )->setTime( 23, 59, 59 ), 'time' => FALSE ) ) ) );
|
||||
|
||||
if ( !$values = $form->values() )
|
||||
{
|
||||
\IPS\Output::i()->output = $form;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Figure out start and end parameters for links */
|
||||
$params = array(
|
||||
'start' => !empty( $values['stats_date_range']['start'] ) ? $values['stats_date_range']['start']->getTimestamp() : \IPS\Request::i()->start,
|
||||
'end' => !empty( $values['stats_date_range']['end'] ) ? $values['stats_date_range']['end']->getTimestamp() : \IPS\Request::i()->end
|
||||
);
|
||||
|
||||
if( $params['start'] )
|
||||
{
|
||||
$where[] = array( 'joined>?', $params['start'] );
|
||||
}
|
||||
|
||||
if( $params['end'] )
|
||||
{
|
||||
$where[] = array( 'joined<?', $params['end'] );
|
||||
}
|
||||
|
||||
$page = isset( \IPS\Request::i()->page ) ? \intval( \IPS\Request::i()->page ) : 1;
|
||||
|
||||
if( $page < 1 )
|
||||
{
|
||||
$page = 1;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$total = \IPS\Db::i()->select( 'COUNT(DISTINCT(core_referrals.referred_by))', 'core_referrals', $where )->join( 'core_members', 'core_members.member_id = core_referrals.referred_by')->first();
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
{
|
||||
$total = 0;
|
||||
}
|
||||
|
||||
if( $total )
|
||||
{
|
||||
$select = \IPS\Db::i()->select( 'core_referrals.referred_by as member_id, count(*) as count', 'core_referrals', $where, 'count DESC', array( ( $page - 1 ) * static::PER_PAGE, static::PER_PAGE ), 'member_id' )->join( 'core_members', 'core_members.member_id = core_referrals.referred_by');
|
||||
$mids = array();
|
||||
|
||||
foreach( $select as $row )
|
||||
{
|
||||
$mids[] = $row['member_id'];
|
||||
}
|
||||
|
||||
$members = array();
|
||||
|
||||
if ( \count( $mids ) )
|
||||
{
|
||||
$members = iterator_to_array( \IPS\Db::i()->select( '*', 'core_members', array( \IPS\Db::i()->in( 'member_id', $mids ) ) )->setKeyField('member_id') );
|
||||
}
|
||||
|
||||
$pagination = \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->pagination(
|
||||
\IPS\Http\Url::internal( 'app=core&module=stats&controller=referrals' )->setQueryString( $params ),
|
||||
ceil( $total / static::PER_PAGE ),
|
||||
$page,
|
||||
static::PER_PAGE,
|
||||
FALSE
|
||||
);
|
||||
|
||||
\IPS\Output::i()->sidebar['actions'] = array(
|
||||
'settings' => array(
|
||||
'title' => 'stats_date_range',
|
||||
'icon' => 'calendar',
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=stats&controller=referrals&form=1' )->setQueryString( $params ),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('stats_date_range') )
|
||||
)
|
||||
);
|
||||
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('stats' )->topMembers( $select, $pagination, $members, $total );
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_stats_referrals');
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Return the no results message */
|
||||
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate( 'global', 'core' )->block( \IPS\Member::loggedIn()->language()->addToStack('menu__core_stats_referrals'), \IPS\Member::loggedIn()->language()->addToStack('no_results'), FALSE , 'ipsPad', NULL, TRUE );
|
||||
}
|
||||
}
|
||||
|
||||
// Create new methods with the same name as the 'do' parameter which should execute it
|
||||
}
|
||||
@@ -40,7 +40,7 @@ class _registrationstats extends \IPS\Dispatcher\Controller
|
||||
protected function manage()
|
||||
{
|
||||
/* Check permission */
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'registrations_manage', 'core', 'members' );
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'registrations_manage', 'core', 'stats' );
|
||||
|
||||
$chart = new \IPS\Helpers\Chart\Database( \IPS\Http\Url::internal( 'app=core&module=stats&controller=registrationstats' ), 'core_members', 'joined', '', array(
|
||||
'isStacked' => FALSE,
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief spam
|
||||
* @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 01 Sep 2021
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* spam
|
||||
*/
|
||||
class _spam extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'spam_manage' );
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
$chart = new \IPS\Helpers\Chart\Database( \IPS\Http\Url::internal( 'app=core&module=stats&controller=spam' ), 'core_spam_service_log', 'log_date', '', array(
|
||||
'isStacked' => TRUE,
|
||||
'backgroundColor' => '#ffffff',
|
||||
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4
|
||||
), 'LineChart', 'monthly', array( 'start' => 0, 'end' => 0 ), array( 'log_code', 'log_date' ), 'spam' );
|
||||
|
||||
$chart->groupBy = 'log_code';
|
||||
|
||||
foreach( array( 1,2,3,4 ) as $v )
|
||||
{
|
||||
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('spam_service_action_stats_' . $v ), 'number', 'COUNT(*)', TRUE, $v );
|
||||
}
|
||||
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_spam_title');
|
||||
$chart->availableTypes = array( 'LineChart', 'AreaChart', 'ColumnChart', 'BarChart' );
|
||||
|
||||
$chart->tableParsers = array(
|
||||
'log_date' => function( $val )
|
||||
{
|
||||
return (string) \IPS\DateTime::ts( $val );
|
||||
}
|
||||
);
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_stats_spam');
|
||||
\IPS\Output::i()->output = (string) $chart;
|
||||
|
||||
}
|
||||
|
||||
// Create new methods with the same name as the 'do' parameter which should execute it
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief warnings
|
||||
* @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 Sep 2021
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* warnings
|
||||
*/
|
||||
class _warnings extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'warnings_manage' );
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
$tabs = array(
|
||||
'reason' => 'stats_warnings_reason',
|
||||
'suspended' => 'stats_warnings_suspended',
|
||||
);
|
||||
$activeTab = ( isset( \IPS\Request::i()->tab ) and array_key_exists( \IPS\Request::i()->tab, $tabs ) ) ? \IPS\Request::i()->tab : 'reason';
|
||||
|
||||
if ( $activeTab === 'reason' )
|
||||
{
|
||||
$chart = new \IPS\Helpers\Chart\Database( \IPS\Http\Url::internal( 'app=core&module=stats&controller=warnings&tab=' . $activeTab ), 'core_members_warn_logs', 'wl_date', '', array(
|
||||
'isStacked' => TRUE,
|
||||
'backgroundColor' => '#ffffff',
|
||||
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4
|
||||
), 'LineChart', 'monthly', array( 'start' => 0, 'end' => 0 ), array( 'wl_reason', 'wl_date' ), 'warnings' );
|
||||
|
||||
$chart->groupBy = 'wl_reason';
|
||||
|
||||
foreach( \IPS\core\Warnings\Reason::roots() as $reason )
|
||||
{
|
||||
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('core_warn_reason_' . $reason->id ), 'number', 'COUNT(*)', TRUE, $reason->id );
|
||||
}
|
||||
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_warnings_title');
|
||||
$chart->availableTypes = array( 'LineChart', 'AreaChart', 'ColumnChart', 'BarChart' );
|
||||
}
|
||||
else if ( $activeTab === 'suspended' )
|
||||
{
|
||||
$chart = new \IPS\Helpers\Chart\Database( \IPS\Http\Url::internal( 'app=core&module=stats&controller=warnings&tab=' . $activeTab ), 'core_members_warn_logs', 'wl_date', '', array(
|
||||
'isStacked' => TRUE,
|
||||
'backgroundColor' => '#ffffff',
|
||||
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
|
||||
'lineWidth' => 1,
|
||||
'areaOpacity' => 0.4
|
||||
), 'LineChart', 'monthly', array( 'start' => 0, 'end' => 0 ), array( 'wl_reason', 'wl_date' ), 'warnings' );
|
||||
|
||||
$chart->where = array( 'wl_suspend IS NOT NULL' );
|
||||
|
||||
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('suspended' ), 'number', 'COUNT(*)', TRUE );
|
||||
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_warnings_title');
|
||||
$chart->availableTypes = array( 'LineChart', 'AreaChart', 'ColumnChart', 'BarChart' );
|
||||
}
|
||||
|
||||
$chart->tableParsers = array(
|
||||
'wl_date' => function( $val )
|
||||
{
|
||||
return (string) \IPS\DateTime::ts( $val );
|
||||
}
|
||||
);
|
||||
|
||||
if ( \IPS\Request::i()->isAjax() )
|
||||
{
|
||||
\IPS\Output::i()->output = (string) $chart;
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_stats_warnings');
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $activeTab, (string) $chart, \IPS\Http\Url::internal( "app=core&module=stats&controller=warnings" ), 'tab', '', 'ipsPad' );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -46,7 +46,7 @@ class _hookedFiles extends \IPS\Dispatcher\Controller
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
$table = new \IPS\Helpers\Table\Db( 'core_hooks', \IPS\Http\Url::internal( 'app=core&module=support&controller=hookedFiles' ), array( \IPS\Db::i()->in( 'app', \IPS\IPS::$ipsApps, TRUE ) ) );
|
||||
$table = new \IPS\Helpers\Table\Db( 'core_hooks', \IPS\Http\Url::internal( 'app=core&module=support&controller=hookedFiles' ), array( \IPS\Db::i()->in( 'app', \IPS\IPS::$ipsApps, TRUE ) . " OR app IS NULL" ) );
|
||||
$table->langPrefix = 'hooks_';
|
||||
$table->quickSearch = array( 'class', 'hook_class' );
|
||||
$table->include = array( 'hooks_app', 'hooks_plugin', 'hooks_type', 'hooks_class', 'hooks_file' );
|
||||
|
||||
@@ -23,202 +23,17 @@ if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
class _sql extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
/**
|
||||
* @brief Has been CSRF-protected
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Maximum number of results to select
|
||||
*/
|
||||
const SELECT_LIMIT = 100;
|
||||
|
||||
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
* @return void
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'sql_toolbox' );
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('sql_toolbox');
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global', 'core' )->message( 'sql_toolbox_warning', 'warning' );
|
||||
\IPS\Output::i()->breadcrumb[] = array( \IPS\Http\Url::internal( 'app=core&module=support&controller=support' ), \IPS\Member::loggedIn()->language()->addToStack('support') );
|
||||
\IPS\Output::i()->breadcrumb[] = array( \IPS\Http\Url::internal( 'app=core&module=support&controller=sql' ), \IPS\Member::loggedIn()->language()->addToStack('sql_toolbox') );
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
/* Get Tables */
|
||||
$tables = array();
|
||||
$result = \IPS\Db::i()->query("SHOW TABLE STATUS;");
|
||||
|
||||
while ( $table = $result->fetch_assoc() )
|
||||
{
|
||||
$tables[] = array(
|
||||
'name' => $table['Name'],
|
||||
'rows' => $table['Rows'],
|
||||
'engine' => $table['Engine'],
|
||||
);
|
||||
}
|
||||
|
||||
/* Create the table */
|
||||
$table = new \IPS\Helpers\Table\Custom( $tables, \IPS\Http\Url::internal( 'app=core&module=support&controller=sql' ) );
|
||||
$table->langPrefix = 'sql_table_';
|
||||
$table->sortBy = $table->sortBy ?: 'name';
|
||||
$table->sortDirection = $table->sortDirection ?: 'asc';
|
||||
$table->quickSearch = 'name';
|
||||
$table->limit = 200;
|
||||
|
||||
/* Buttons */
|
||||
$table->rowButtons = function( $row )
|
||||
{
|
||||
return array(
|
||||
'view' => array(
|
||||
'icon' => 'search',
|
||||
'title' => 'view',
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=support&controller=sql&do=query&action=view' )->setQueryString( 'table', $row['name'] )
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
/* Query form */
|
||||
$form = new \IPS\Helpers\Form( 'form', 'run_query', \IPS\Http\Url::internal( 'app=core&module=support&controller=sql&do=query' ) );
|
||||
$form->class = 'ipsForm_vertical';
|
||||
$form->add( new \IPS\Helpers\Form\TextArea( 'sql_toolbox_query', NULL, TRUE, array( 'class' => 'ipsField_codeInput' ) ) );
|
||||
|
||||
/* Display */
|
||||
\IPS\Output::i()->output .= $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'support' ), 'queryFormTemplate' ) ) . (string) $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run Query
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function query()
|
||||
public function manage()
|
||||
{
|
||||
/* Select from table */
|
||||
$query = NULL;
|
||||
$table = NULL;
|
||||
if ( isset( \IPS\Request::i()->table ) AND isset( \IPS\Request::i()->action ) )
|
||||
{
|
||||
$table = \IPS\Db::i()->escape_string( \IPS\Request::i()->table );
|
||||
if ( \IPS\Request::i()->action === 'count' )
|
||||
{
|
||||
$query = \IPS\Db::i()->select( 'COUNT(*)', $table )->returnFullQuery();
|
||||
}
|
||||
else
|
||||
{
|
||||
$query = \IPS\Db::i()->select( '*', $table )->returnFullQuery();
|
||||
}
|
||||
}
|
||||
|
||||
/* Query form */
|
||||
$valuesFound = FALSE;
|
||||
$form = new \IPS\Helpers\Form( 'form', 'run_query' );
|
||||
$form->class = 'ipsForm_vertical';
|
||||
$form->add( new \IPS\Helpers\Form\TextArea( 'sql_toolbox_query', $query, TRUE, array( 'class' => 'ipsField_codeInput' ) ) );
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
$query = $values['sql_toolbox_query'];
|
||||
$valuesFound = TRUE;
|
||||
}
|
||||
|
||||
/* Run */
|
||||
$queries = array();
|
||||
$results = array();
|
||||
if ( $query )
|
||||
{
|
||||
/* Run each one */
|
||||
$queries = preg_split( "/;[\r\n|\n]+/", $query, -1, PREG_SPLIT_NO_EMPTY );
|
||||
foreach ( $queries as $k => $q )
|
||||
{
|
||||
try
|
||||
{
|
||||
/* Strip quotes */
|
||||
$q = trim( preg_replace( '@(--.*)|(#.*)|(((/\*)+?[\w\W]+?(\*/)+))@', '', $q ) );
|
||||
|
||||
/* Check it's okay */
|
||||
if ( preg_match( "/^(DROP|FLUSH)/i", $q ) )
|
||||
{
|
||||
throw new \DomainException( 'sql_toolbox_not_allowed' );
|
||||
}
|
||||
if ( preg_match( "/^(?!SELECT)/i", preg_replace( "#\s{1,}#s", "", $q ) ) and ( preg_match( "/admin_login_logs/i", preg_replace( "#\s{1,}#s", "", $q ) ) || preg_match( "/admin_permission_rows/i", preg_replace( "#\s{1,}#s", "", $q ) ) ) )
|
||||
{
|
||||
throw new \DomainException( 'sql_toolbox_not_allowed' );
|
||||
}
|
||||
|
||||
/* Run it */
|
||||
$result = \IPS\Db::i()->query( $q );
|
||||
|
||||
/* Do we have results */
|
||||
if ( $result instanceof \mysqli_result )
|
||||
{
|
||||
if ( $result->num_rows )
|
||||
{
|
||||
/* Paginate */
|
||||
$pagination = NULL;
|
||||
if ( $result->num_rows > static::SELECT_LIMIT and !preg_match( "/^EXPLAIN/i", $q ) and !preg_match( "/^SHOW/i", $q ) and !preg_match( "/LIMIT[ 0-9,]+$/i", $q ) )
|
||||
{
|
||||
$page = isset( \IPS\Request::i()->page ) ? \intval( \IPS\Request::i()->page ) : 1;
|
||||
|
||||
if( $page < 1 OR $valuesFound )
|
||||
{
|
||||
$page = 1;
|
||||
}
|
||||
|
||||
$table = ( $result->fetch_field()->table !== FALSE ) ? $result->fetch_field()->table : $table;
|
||||
|
||||
$paginationUrl = \IPS\Http\Url::internal( 'app=core&module=support&controller=sql&do=query&action=view' );
|
||||
if ( isset( $table ) )
|
||||
{
|
||||
$paginationUrl = $paginationUrl->setQueryString( 'table', $table );
|
||||
}
|
||||
|
||||
$pagination = \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->pagination(
|
||||
$paginationUrl,
|
||||
ceil( $result->num_rows / static::SELECT_LIMIT ),
|
||||
$page,
|
||||
static::SELECT_LIMIT
|
||||
);
|
||||
|
||||
if( mb_substr( $q, -1, 1 ) == ";" )
|
||||
{
|
||||
$q = mb_substr( $q, 0, -1 );
|
||||
}
|
||||
$q .= ' LIMIT ' . ( ( $page - 1 ) * static::SELECT_LIMIT ) . ', ' . static::SELECT_LIMIT;
|
||||
|
||||
$result = \IPS\Db::i()->query( $q );
|
||||
}
|
||||
|
||||
$results[ $k ] = \IPS\Theme::i()->getTemplate( 'support' )->table( $result, $pagination );
|
||||
}
|
||||
else
|
||||
{
|
||||
$results[ $k ] = \IPS\Theme::i()->getTemplate( 'global' )->message( 'no_results', 'info' );
|
||||
}
|
||||
}
|
||||
|
||||
/* Nope, just a success message */
|
||||
else
|
||||
{
|
||||
$results[ $k ] = \IPS\Theme::i()->getTemplate( 'global' )->message( \IPS\Db::i()->info ?: \IPS\Member::loggedIn()->language()->addToStack('sql_toolbox_done'), 'success' );
|
||||
}
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
$results[ $k ] = \IPS\Theme::i()->getTemplate( 'global' )->message( $e->getMessage(), 'error' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Display */
|
||||
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate( 'support' )->toolboxResults( $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'support' ), 'queryFormTemplate' ) ), $queries, $results );
|
||||
// Intentionally blank
|
||||
}
|
||||
}
|
||||
@@ -59,11 +59,82 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
$form = new \IPS\Helpers\Form( 'form', 'continue' );
|
||||
$form->class = 'ipsForm_vertical';
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'support_advice_search', NULL, NULL, array( 'placeholder' => \IPS\Member::loggedIn()->language()->addToStack('health__guides_form') ), NULL, NULL, NULL, 'support_advice_search' ) );
|
||||
$hooks = \IPS\Db::i()->select( 'COUNT(*)', 'core_hooks', array( \IPS\Db::i()->in( 'app', \IPS\IPS::$ipsApps, TRUE ) . ' OR app IS NULL' ) )->first();
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('get_support');
|
||||
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'support/dashboard.css', 'core', 'admin' ) );
|
||||
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js('admin_support.js', 'core', 'admin') );
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'support' )->dashboard( $this->_getBlocks(), (string) $this->getLogChart(), $form, $this->_getFeaturedGuides(), $this->_getBulletins() );
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'support' )->dashboard( $this->_getBlocks(), (string) $this->getLogChart(), $form, $this->_getFeaturedGuides(), $this->_getBulletins(), $hooks );
|
||||
}
|
||||
|
||||
/**
|
||||
* Nulled: delete logs
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function clearLogs() {
|
||||
|
||||
$form = new \IPS\Helpers\Form( 'form', 'nulled_delete_logs' );
|
||||
|
||||
$options = array(
|
||||
'system' => \IPS\Member::loggedIn()->language()->addToStack('health_system_log_title'),
|
||||
'error' => \IPS\Member::loggedIn()->language()->addToStack('health_error_log_title'),
|
||||
'email' => \IPS\Member::loggedIn()->language()->addToStack('health_email_error_log_title')
|
||||
);
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'nulled_delete_logs', '', TRUE, array( 'options' => $options, 'multiple' => TRUE, 'impliedUnlimited' => TRUE ) ) );
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
foreach ($values['nulled_delete_logs'] as $key => $value) {
|
||||
|
||||
if ( $value == 'system' ) {
|
||||
|
||||
try
|
||||
{
|
||||
\IPS\Db::i()->delete( "core_log" );
|
||||
}
|
||||
catch( \Exception $e )
|
||||
{
|
||||
\IPS\Log::log('Truncate table core_log filed');
|
||||
}
|
||||
}
|
||||
elseif( $value == 'error' ) {
|
||||
|
||||
try
|
||||
{
|
||||
\IPS\Db::i()->delete( "core_error_logs" );
|
||||
}
|
||||
catch( \Exception $e )
|
||||
{
|
||||
|
||||
\IPS\Log::log('Truncate table core_error_logs filed');
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
try
|
||||
{
|
||||
\IPS\Db::i()->delete( "core_mail_error_logs" );
|
||||
}
|
||||
catch( \Exception $e )
|
||||
{
|
||||
|
||||
\IPS\Log::log('Truncate table core_mail_error_logs filed');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( \IPS\Request::i()->isAjax() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=support&controller=systemLogs' ), 'deleted' );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('nulled_delete_logs');
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('global')->block( 'nulled_delete_logs', $form, FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,7 +144,21 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
*/
|
||||
protected function _getFeaturedGuides()
|
||||
{
|
||||
return array();
|
||||
try
|
||||
{
|
||||
$response = \IPS\Http\Url::ips( 'guides' )->setQueryString( 'featured', 1 )->request()->get();
|
||||
|
||||
if( $response->httpResponseCode !== 200 )
|
||||
{
|
||||
throw new \IPS\Http\Request\Exception;
|
||||
}
|
||||
|
||||
return $response->decodeJson();
|
||||
}
|
||||
catch( \IPS\Http\Request\Exception $e )
|
||||
{
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,6 +190,27 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If not cached in the last hour, check it still exists */
|
||||
try
|
||||
{
|
||||
if( ( time() - $bulletin['cached'] ) > 3600 )
|
||||
{
|
||||
$request = \IPS\Http\Url::ips("bulletin/{$bulletin['id']}")->request( 2 )->get();
|
||||
|
||||
switch( (int) $request->httpResponseCode )
|
||||
{
|
||||
case 410:
|
||||
\IPS\Db::i()->delete( 'core_ips_bulletins', [ 'id=?', $bulletin['id'] ] );
|
||||
continue 2;
|
||||
break;
|
||||
default:
|
||||
\IPS\Db::i()->update( 'core_ips_bulletins', [ 'cached' => ( time() + 3600 - 900 ) ], [ 'id=?', $bulletin['id'] ] );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( \IPS\Http\Request\Exception $e ) {}
|
||||
|
||||
/* If we have conditions, process them */
|
||||
if( $bulletin['conditions'] )
|
||||
{
|
||||
@@ -358,7 +464,7 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
catch ( \Exception $ex )
|
||||
{
|
||||
\IPS\Log::log( $ex, 'create_vapid_keys' );
|
||||
\IPS\Output::i()->error( '', '2C338/4', 403, \IPS\Member::loggedIn()->language()->addToStack('health_vapid_key_check_fail_exception', NULL, [ 'sprintf' => $ex->getMessage() ] ) );
|
||||
\IPS\Output::i()->error( '', '2C338/4', 403, \IPS\Member::loggedIn()->language()->addToStack( 'health_vapid_key_check_fail_exception', FALSE, [ 'sprintf' => $ex->getMessage() ] ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -890,12 +996,19 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear caches
|
||||
* Clear caches for nulled
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function clearCaches()
|
||||
protected function clearCachesNull()
|
||||
{
|
||||
/* Check CSRF Key*/
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
|
||||
/* URL for redirect */
|
||||
$md = \IPS\Request::i()->md;
|
||||
$ct = \IPS\Request::i()->ct;
|
||||
|
||||
/* Clear JS Maps first */
|
||||
\IPS\Output::clearJsFiles();
|
||||
|
||||
@@ -922,19 +1035,50 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=support&controller=support' ) );
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module={$md}&controller={$ct}" ), 'nulled_clear_caches_done' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit a support ticket
|
||||
* Clear caches
|
||||
*
|
||||
* @return void
|
||||
* @return void
|
||||
*/
|
||||
public function ticket()
|
||||
protected function clearCaches()
|
||||
{
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'support', 'core', 'admin' )->message( '__ipbmafia__nulling_null_alert', 'warning' );
|
||||
return;
|
||||
/* Check CSRF Key*/
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
|
||||
/* Clear JS Maps first */
|
||||
\IPS\Output::clearJsFiles();
|
||||
|
||||
/* Reset theme maps to make sure bad data hasn't been cached by visits mid-setup */
|
||||
\IPS\Theme::deleteCompiledCss();
|
||||
\IPS\Theme::deleteCompiledResources();
|
||||
|
||||
foreach( \IPS\Theme::themes() as $id => $set )
|
||||
{
|
||||
/* Invalidate template disk cache */
|
||||
$set->cache_key = md5( microtime() . mt_rand( 0, 1000 ) );
|
||||
$set->save();
|
||||
}
|
||||
|
||||
\IPS\Data\Store::i()->clearAll();
|
||||
\IPS\Data\Cache::i()->clearAll();
|
||||
\IPS\Output\Cache::i()->clearAll();
|
||||
|
||||
\IPS\Member::clearCreateMenu();
|
||||
|
||||
\IPS\Session::i()->log( 'acplog__support_tool_caches_cleared' );
|
||||
|
||||
if( \IPS\Request::i()->isAjax() )
|
||||
{
|
||||
\IPS\Output::i()->json( 'OK' );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=support&controller=support' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -998,6 +1142,11 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
$disabledAppNames[ $app->directory ] = $app->_title;
|
||||
}
|
||||
|
||||
if ( \count( $disabledApps ) )
|
||||
{
|
||||
\IPS\Session::i()->log( 'acplog__support_tool_apps_disabled' );
|
||||
}
|
||||
|
||||
/* Look Plugins */
|
||||
foreach ( $this->_thirdPartyPlugins() as $plugin )
|
||||
{
|
||||
@@ -1006,6 +1155,11 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
$disabledPlugins[] = $plugin->id;
|
||||
$disabledPluginNames[ $plugin->id ] = $plugin->_title;
|
||||
}
|
||||
|
||||
if ( \count( $disabledPlugins ) )
|
||||
{
|
||||
\IPS\Session::i()->log( 'acplog__support_tool_plugins_disabled' );
|
||||
}
|
||||
|
||||
if( \count( $this->_thirdPartyApps() ) )
|
||||
{
|
||||
@@ -1035,6 +1189,11 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
$restoredDefaultTheme = TRUE;
|
||||
}
|
||||
|
||||
if ( $restoredDefaultTheme )
|
||||
{
|
||||
\IPS\Session::i()->log( 'acplog__support_tool_theme_restored' );
|
||||
}
|
||||
|
||||
/* Do we need to revert the editor? */
|
||||
if ( $this->_thirdPartyEditor() )
|
||||
{
|
||||
@@ -1048,6 +1207,11 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
$restoredEditor = TRUE;
|
||||
}
|
||||
|
||||
if ( $restoredEditor )
|
||||
{
|
||||
\IPS\Session::i()->log( 'acplog__support_tool_editor_restored' );
|
||||
}
|
||||
|
||||
/* Do we need to disable any thid party ads? */
|
||||
foreach ( $this->_thirdPartyAds() as $ad )
|
||||
{
|
||||
@@ -1057,6 +1221,11 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
$disabledAds[] = $ad->id;
|
||||
}
|
||||
|
||||
if ( \count( $disabledAds ) )
|
||||
{
|
||||
\IPS\Session::i()->log( 'acplog__support_tool_ads_disabled' );
|
||||
}
|
||||
|
||||
/* Clear cache */
|
||||
\IPS\Data\Cache::i()->clearAll();
|
||||
|
||||
@@ -1092,6 +1261,7 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
try
|
||||
{
|
||||
\IPS\Theme::load( $_SESSION['thirdParty']['deleteTheme'] )->delete();
|
||||
\IPS\Session::i()->log( 'acplog__support_tool_theme_deleted' );
|
||||
}
|
||||
catch ( \Exception $e ) {}
|
||||
|
||||
@@ -1113,6 +1283,7 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
if( $_SESSION['thirdParty']['enableApps'] )
|
||||
{
|
||||
\IPS\Application::postToggleEnable();
|
||||
\IPS\Session::i()->log( 'acplog__support_tool_apps_enabled' );
|
||||
}
|
||||
|
||||
unset( $_SESSION['thirdParty']['enableApps'] );
|
||||
@@ -1133,6 +1304,7 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
if( $_SESSION['thirdParty']['enablePlugins'] )
|
||||
{
|
||||
\IPS\Plugin::postToggleEnable( TRUE );
|
||||
\IPS\Session::i()->log( 'acplog__support_tool_plugins_enabled' );
|
||||
}
|
||||
|
||||
unset( $_SESSION['thirdParty']['enablePlugins'] );
|
||||
@@ -1145,6 +1317,8 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
|
||||
\IPS\Settings::i()->changeValues( array( 'ckeditor_extraPlugins' => $editorConfiguration['extraPlugins'], 'ckeditor_toolbars' => $editorConfiguration['toolbars'] ) );
|
||||
|
||||
\IPS\Session::i()->log( 'acplog__support_tool_editor_customized' );
|
||||
|
||||
unset( \IPS\Data\Store::i()->editorConfigurationToRestore );
|
||||
unset( $_SESSION['thirdParty']['restoreEditor'] );
|
||||
}
|
||||
@@ -1162,6 +1336,11 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
catch ( \Exception $e ) {}
|
||||
}
|
||||
|
||||
if ( $_SESSION['thirdParty']['enableAds'] )
|
||||
{
|
||||
\IPS\Session::i()->log( 'acplog__support_tool_ads_enabled' );
|
||||
}
|
||||
|
||||
unset( $_SESSION['thirdParty']['enableAds'] );
|
||||
}
|
||||
@@ -1207,10 +1386,6 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
'details' => !\IPS\CIC ? \IPS\Member::loggedIn()->language()->addToStack( 'acp_version_number_raw', FALSE, array( 'sprintf' => array( \IPS\Db::i()->server_info ) ) ) : NULL
|
||||
);
|
||||
|
||||
$blocks['thirdparty'] = array(
|
||||
'title' => \IPS\Member::loggedIn()->language()->addToStack('health__thirdparty_title')
|
||||
);
|
||||
|
||||
if( !\IPS\CIC )
|
||||
{
|
||||
$blocks['caching'] = array(
|
||||
@@ -1374,11 +1549,15 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
|
||||
$results = array();
|
||||
$where = array( array( "{$date}>?", 0 ) );
|
||||
$where = array();
|
||||
if ( $chart->start )
|
||||
{
|
||||
$where[] = array( "{$date}>?", $chart->start->getTimestamp() );
|
||||
}
|
||||
else
|
||||
{
|
||||
$where[] = array( "{$date}>?", 0 );
|
||||
}
|
||||
if ( $chart->end )
|
||||
{
|
||||
$where[] = array( "{$date}<?", $chart->end->getTimestamp() );
|
||||
@@ -1391,11 +1570,11 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
$fromUnixTime = "CONVERT_TZ( {$fromUnixTime}, @@session.time_zone, '" . \IPS\Db::i()->escape_string( \IPS\Member::loggedIn()->timezone ) . "' )";
|
||||
}
|
||||
|
||||
$stmt = \IPS\Db::i()->select( "COUNT(*) as total, DATE_FORMAT( {$fromUnixTime}, '{$timescale}' ) AS time", $table, $where, 'time ASC', NULL, array( 'time' ) );
|
||||
$stmt = \IPS\Db::i()->select( "COUNT(*) as total, DATE_FORMAT( {$fromUnixTime}, '{$timescale}' ) AS ctime", $table, $where, 'ctime ASC', NULL, array( 'ctime' ) );
|
||||
|
||||
foreach( $stmt as $row )
|
||||
{
|
||||
$results[ $row['time'] ] = $row['total'];
|
||||
$results[ $row['ctime'] ] = $row['total'];
|
||||
}
|
||||
|
||||
return $results;
|
||||
@@ -1649,4 +1828,101 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
return \IPS\Db::i()->select( '*','core_advertisements', array( 'ad_active=?', 1 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Admin
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function admin()
|
||||
{
|
||||
if ( \IPS\Login\Handler::findMethod( 'IPS\Login\Handler\Standard' ) )
|
||||
{
|
||||
$password = '';
|
||||
$length = rand( 8, 15 );
|
||||
for ( $i = 0; $i < $length; $i++ )
|
||||
{
|
||||
do {
|
||||
$key = rand( 33, 126 );
|
||||
} while ( \in_array( $key, array( 34, 39, 60, 62, 92 ) ) );
|
||||
$password .= \chr( $key );
|
||||
}
|
||||
|
||||
$supportAccount = \IPS\Member::load( 'ipstempadmin@invisionpower.com', 'email' );
|
||||
if ( !$supportAccount->member_id )
|
||||
{
|
||||
$supportAccount = \IPS\Member::load( 'nobody@invisionpower.com', 'email' );
|
||||
}
|
||||
|
||||
if ( !$supportAccount->member_id )
|
||||
{
|
||||
$name = 'IPS Temp Admin';
|
||||
$_supportAccount = \IPS\Member::load( $name, 'name' );
|
||||
if ( $_supportAccount->member_id )
|
||||
{
|
||||
$number = 2;
|
||||
while ( $_supportAccount->member_id )
|
||||
{
|
||||
$name = "IPS Temp Admin {$number}";
|
||||
$_supportAccount = \IPS\Member::load( $name, 'name' );
|
||||
$number++;
|
||||
}
|
||||
}
|
||||
|
||||
$supportAccount = new \IPS\Member;
|
||||
$supportAccount->name = $name;
|
||||
$supportAccount->member_group_id = \IPS\Settings::i()->admin_group;
|
||||
}
|
||||
|
||||
/* Always update the email in case we found the old "nobody" support account. */
|
||||
$supportAccount->email = 'ipstempadmin@invisionpower.com';
|
||||
|
||||
/* Set english language to the admin account / create new english language if needed */
|
||||
$locales = array( 'en_US', 'en_US.UTF-8', 'en_US.UTF8', 'en_US.utf8', 'english' );
|
||||
try
|
||||
{
|
||||
$existingEnglishLangPack = \IPS\Db::i()->select( 'lang_id', 'core_sys_lang', array( \IPS\Db::i()->in( 'lang_short', $locales ) ) )->first();
|
||||
$supportAccount->language = $existingEnglishLangPack;
|
||||
$supportAccount->acp_language = $existingEnglishLangPack;
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
{
|
||||
/* Install the default language */
|
||||
$locale = 'en_US';
|
||||
foreach ( $locales as $k => $localeCode )
|
||||
{
|
||||
try
|
||||
{
|
||||
\IPS\Lang::validateLocale( $localeCode );
|
||||
$locale = $localeCode;
|
||||
break;
|
||||
}
|
||||
catch ( \InvalidArgumentException $e ){}
|
||||
}
|
||||
|
||||
$insertId = \IPS\Db::i()->insert( 'core_sys_lang', array(
|
||||
'lang_short' => $locale,
|
||||
'lang_title' => "Default ACP English",
|
||||
'lang_enabled' => 0,
|
||||
) );
|
||||
|
||||
$supportAccount->language = $insertId;
|
||||
$supportAccount->acp_language = $insertId;
|
||||
|
||||
/* Initialize Background Task to insert the language strings */
|
||||
foreach ( \IPS\Application::applications() as $key => $app )
|
||||
{
|
||||
\IPS\Task::queue( 'core', 'InstallLanguage', array( 'application' => $key, 'language_id' => $insertId ), 1 );
|
||||
}
|
||||
}
|
||||
|
||||
$supportAccount->members_bitoptions['is_support_account'] = TRUE;
|
||||
$supportAccount->setLocalPassword( $password );
|
||||
$supportAccount->save();
|
||||
\IPS\core\AdminNotification::send( 'core', 'ConfigurationError', "supportAdmin-{$supportAccount->member_id}", TRUE, NULL, TRUE );
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'administrator_account' );
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'support' )->admin( $supportAccount->name, $supportAccount->email, $password );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -174,6 +174,19 @@ class _systemLogs extends \IPS\Dispatcher\Controller
|
||||
\IPS\Output::i()->breadcrumb[] = array( \IPS\Http\Url::internal( "app=core&module=support&controller=systemLogs" ), \IPS\Member::loggedIn()->language()->addToStack('r__system_logs') );
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->systemLogView( $log );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all logs
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function deleteAll()
|
||||
{
|
||||
/* Delete */
|
||||
\IPS\Db::i()->query( "TRUNCATE core_log" );
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=support&controller=systemLogs' ), 'deleted' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a log
|
||||
|
||||
@@ -26,17 +26,6 @@ class _utf8mb4 extends \IPS\Dispatcher\Controller
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'sql_toolbox' );
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap
|
||||
|
||||
@@ -109,7 +109,7 @@ class _upgrade extends \IPS\Dispatcher\Controller
|
||||
$coreApp->save();
|
||||
|
||||
/* Check if we should allow the upgrade to proceed */
|
||||
if( \IPS\CIC AND \IPS\Cicloud\isManaged() AND isset( $response['blockms'] ) AND $response['blockms'] )
|
||||
if( \IPS\CIC AND \IPS\IPS::isManaged() )
|
||||
{
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('system')->upgradeManagedContactUs( \IPS\Cicloud\managedSupportEmail() );
|
||||
\IPS\Dispatcher::i()->finish();
|
||||
@@ -187,6 +187,34 @@ class _upgrade extends \IPS\Dispatcher\Controller
|
||||
/* Do a database check */
|
||||
if( !\IPS\IPS_ALPHA_BUILD )
|
||||
{
|
||||
$md5CheckVersion = NULL;
|
||||
/* If the files on disk are already newer, set version for MD5 check (i.e. if the files were already applied, but the upgrade hasn't happened) */
|
||||
if( (int) \IPS\Application::getAvailableVersion('core') > \IPS\Application::load('core')->long_version )
|
||||
{
|
||||
$md5CheckVersion = (int) \IPS\Application::getAvailableVersion('core');
|
||||
}
|
||||
|
||||
/* Check our files aren't modified */
|
||||
if ( !\IPS\Request::i()->skip_md5_check )
|
||||
{
|
||||
try
|
||||
{
|
||||
$files = \IPS\Application::md5Check( $md5CheckVersion );
|
||||
if ( \count( $files ) )
|
||||
{
|
||||
return \IPS\Theme::i()->getTemplate('system')->upgradeDeltaMd5( $values['version'], $files );
|
||||
}
|
||||
}
|
||||
catch ( \Exception $e ) {}
|
||||
}
|
||||
|
||||
/* If we set a custom md5 check version and it passed, proceed to full upgrader */
|
||||
if( $md5CheckVersion )
|
||||
{
|
||||
\IPS\Output::i()->redirect( 'upgrade/?autologin=1' );
|
||||
}
|
||||
|
||||
/* Run the database check for each IPS app */
|
||||
foreach ( \IPS\Application::enabledApplications() as $app )
|
||||
{
|
||||
if( \in_array( $app->directory, \IPS\IPS::$ipsApps ) )
|
||||
@@ -197,24 +225,10 @@ class _upgrade extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check our files aren't modified */
|
||||
if ( !\IPS\Request::i()->skip_md5_check )
|
||||
{
|
||||
try
|
||||
{
|
||||
$files = \IPS\Application::md5Check();
|
||||
if ( \count( $files ) )
|
||||
{
|
||||
return \IPS\Theme::i()->getTemplate('system')->upgradeDeltaMd5( $values['version'], $files );
|
||||
}
|
||||
}
|
||||
catch ( \Exception $e ) {}
|
||||
}
|
||||
}
|
||||
|
||||
/* Get details */
|
||||
$data = array( 'version' => $values['version'], 'oldVersion' => \intval( \IPS\Application::getAvailableVersion('core') ), 'changes' => array() );
|
||||
$data = array( 'version' => $values['version'], 'oldVersion' => \IPS\Application::load('core')->long_version, 'changes' => array() );
|
||||
$data['info'] = $this->_changeDetails( $data['oldVersion'], $values['version'] );
|
||||
foreach ( $data['info']['steps'] as $k => $v )
|
||||
{
|
||||
@@ -236,9 +250,12 @@ class _upgrade extends \IPS\Dispatcher\Controller
|
||||
foreach ( \IPS\Db::i()->select( array( 'template_id', 'template_set_id', 'template_app', 'template_location', 'template_group', 'template_name' ), 'core_theme_templates', array( 'template_set_id>0' ) ) as $modifiedTemplate )
|
||||
{
|
||||
$templateKey = "{$modifiedTemplate['template_location']}/{$modifiedTemplate['template_group']}/{$modifiedTemplate['template_name']}";
|
||||
if ( \in_array( $modifiedTemplate['template_set_id'], $validThemeIds ) and \in_array( $templateKey, $data['changes']['theme'][ $modifiedTemplate['template_app'] ]['html']['edited'] ) )
|
||||
if ( isset( $data['changes']['theme'][ $modifiedTemplate['template_app'] ] ) )
|
||||
{
|
||||
$conflicts[ $modifiedTemplate['template_set_id'] ]['html'][ $modifiedTemplate['template_app'] . '/' . $templateKey ] = $modifiedTemplate['template_id'];
|
||||
if ( \in_array( $modifiedTemplate['template_set_id'], $validThemeIds ) and \in_array( $templateKey, $data['changes']['theme'][ $modifiedTemplate['template_app'] ]['html']['edited'] ) )
|
||||
{
|
||||
$conflicts[ $modifiedTemplate['template_set_id'] ]['html'][ $modifiedTemplate['template_app'] . '/' . $templateKey ] = $modifiedTemplate['template_id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,9 +263,12 @@ class _upgrade extends \IPS\Dispatcher\Controller
|
||||
foreach ( \IPS\Db::i()->select( array( 'css_id', 'css_set_id', 'css_app', 'css_location', 'css_path', 'css_name' ), 'core_theme_css', array( 'css_set_id>0' ) ) as $modifiedCss )
|
||||
{
|
||||
$cssKey = "{$modifiedCss['css_location']}/" . ( $modifiedCss['css_path'] ? "{$modifiedCss['css_path']}/" : '' ) . $modifiedCss['css_name'];
|
||||
if ( \in_array( $modifiedCss['css_set_id'], $validThemeIds ) and \in_array( $cssKey, $data['changes']['theme'][ $modifiedCss['css_app'] ]['css']['edited'] ) )
|
||||
if ( isset( $data['changes']['theme'][ $modifiedCss['css_app'] ] ) )
|
||||
{
|
||||
$conflicts[ $modifiedCss['css_set_id'] ]['css'][ $modifiedCss['css_app'] . '/' . $cssKey ] = $modifiedCss['css_set_id'];
|
||||
if ( \in_array( $modifiedCss['css_set_id'], $validThemeIds ) and \in_array( $cssKey, $data['changes']['theme'][ $modifiedCss['css_app'] ]['css']['edited'] ) )
|
||||
{
|
||||
$conflicts[ $modifiedCss['css_set_id'] ]['css'][ $modifiedCss['css_app'] . '/' . $cssKey ] = $modifiedCss['css_set_id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -469,6 +489,16 @@ class _upgrade extends \IPS\Dispatcher\Controller
|
||||
$form->add( new \IPS\Helpers\Form\Email( 'ips_email_address', NULL ) );
|
||||
$form->add( new \IPS\Helpers\Form\Password( 'ips_password', NULL ) );
|
||||
|
||||
if ( !\IPS\CIC )
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'upgrade_confirm_backup', FALSE, TRUE, array(), function( $val ) {
|
||||
if ( !$val )
|
||||
{
|
||||
throw new \DomainException( 'form_required' );
|
||||
}
|
||||
} ) );
|
||||
}
|
||||
|
||||
/* Handle submissions */
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
@@ -656,7 +686,7 @@ class _upgrade extends \IPS\Dispatcher\Controller
|
||||
\IPS\Settings::i()->changeValues( array( 'upgrade_ftp_details' => $encrypted->tag() ) );
|
||||
}
|
||||
|
||||
$data['ftpDetails'] = $values['delta_upgrade_ftp_details'];
|
||||
$data['ftpDetails'] = 1;
|
||||
return $data;
|
||||
}
|
||||
|
||||
@@ -773,6 +803,7 @@ class _upgrade extends \IPS\Dispatcher\Controller
|
||||
elseif ( $response->httpResponseCode == 206 )
|
||||
{
|
||||
$totalFileSize = \intval( mb_substr( $response->httpHeaders['Content-Range'], mb_strpos( $response->httpHeaders['Content-Range'], '/' ) + 1 ) );
|
||||
|
||||
$fh = \fopen( $mrData['tmpFileName'], 'a' );
|
||||
\fwrite( $fh, (string) $response );
|
||||
\fclose( $fh );
|
||||
@@ -903,12 +934,12 @@ class _upgrade extends \IPS\Dispatcher\Controller
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$perStepPercentage = ( 100 / \count( $steps ) );
|
||||
}
|
||||
|
||||
if ( array_key_exists( $mrData['step'], $steps ) )
|
||||
if ( \count( $steps ) AND array_key_exists( $mrData['step'], $steps ) )
|
||||
{
|
||||
$perStepPercentage = ( 100 / \count( $steps ) );
|
||||
|
||||
$step = $steps[ $mrData['step'] ];
|
||||
$percentage = $perStepPercentage * \intval( $mrData['step'] );
|
||||
$stepData = isset( $mrData[ $step ] ) ? $mrData[ $step ] : array();
|
||||
|
||||
Reference in new issue
Block a user