Version 4.6.0
This commit is contained in:
1 parent
f79dcf067a
commit
517a5e1f70
2036 files changed
+110041
-26162
No files matched your search
@@ -0,0 +1,281 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Achievement badges
|
||||
* @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 Feb 2021
|
||||
*/
|
||||
|
||||
namespace IPS\core\modules\admin\achievements;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Achievement badges
|
||||
*/
|
||||
class _badges extends \IPS\Node\Controller
|
||||
{
|
||||
/**
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* Node Class
|
||||
*/
|
||||
protected $nodeClass = 'IPS\core\Achievements\Badge';
|
||||
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'badges_manage' );
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
if( $data = \IPS\core\Achievements\Rule::getRebuildProgress() )
|
||||
{
|
||||
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate( 'achievements' )->rebuildProgress( $data, TRUE );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->sidebar['actions']['export'] = array(
|
||||
'primary' => false,
|
||||
'icon' => 'cloud-download',
|
||||
'link' => \IPS\Http\Url::internal('app=core&module=achievements&controller=badges&do=exportForm'),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('acp_achievements_export') ),
|
||||
'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',
|
||||
);
|
||||
|
||||
return parent::manage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch any additional HTML for this row
|
||||
*
|
||||
* @param object $node Node returned from $nodeClass::load()
|
||||
* @return NULL|string
|
||||
*/
|
||||
public function _getRowHtml( $node )
|
||||
{
|
||||
$count = \IPS\Db::i()->select( 'COUNT(*)', 'core_member_badges', [ 'badge=?', $node->id ] )->first();
|
||||
|
||||
$url = \IPS\Http\Url::internal("app=core&module=achievements&controller=badges&do=view&id={$node->id}");
|
||||
|
||||
return \IPS\Theme::i()->getTemplate('achievements')->memberCount( $url, $count );
|
||||
}
|
||||
|
||||
/**
|
||||
* View who has earned a badge
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function view()
|
||||
{
|
||||
$badge = \IPS\core\Achievements\Badge::load( \IPS\Request::i()->id );
|
||||
|
||||
$table = new \IPS\Helpers\Table\Db( 'core_member_badges', \IPS\Http\Url::internal("app=core&module=achievements&controller=badges&do=view&id={$badge->id}"), [ 'badge=?', $badge->id ] );
|
||||
$table->joins[] = [
|
||||
'select' => 'action,identifier',
|
||||
'from' => 'core_achievements_log',
|
||||
'where' => 'core_achievements_log.id=core_member_badges.action_log',
|
||||
'type' => 'LEFT'
|
||||
];
|
||||
$table->langPrefix = 'badge_log_';
|
||||
$table->include = [ 'member', 'action_log', 'rule', 'datetime' ];
|
||||
$table->sortBy = $table->sortBy ?: 'datetime';
|
||||
|
||||
$table->advancedSearch = array(
|
||||
'datetime' => \IPS\Helpers\Table\SEARCH_DATE_RANGE,
|
||||
);
|
||||
|
||||
$table->parsers = array(
|
||||
'member' => function ( $val, $row )
|
||||
{
|
||||
return \IPS\Theme::i()->getTemplate( 'global', 'core' )->userLink( \IPS\Member::load( $val ) );
|
||||
},
|
||||
'action_log' => function( $val, $row ) {
|
||||
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
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack('unknown');
|
||||
}
|
||||
},
|
||||
'rule' => function( $val, $row ) {
|
||||
$rule = NULL;
|
||||
if ( $val )
|
||||
{
|
||||
try
|
||||
{
|
||||
$rule = \IPS\core\Achievements\Rule::load( $val );
|
||||
}
|
||||
catch ( \OutOfRangeException $e ) { }
|
||||
}
|
||||
|
||||
if ( $rule )
|
||||
{
|
||||
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( \IPS\Http\Url::internal("app=core&module=achievements&controller=rules&do=form&id={$rule->id}"), FALSE, $rule->extension()->ruleDescription( $rule ), TRUE, FALSE, FALSE, TRUE );
|
||||
}
|
||||
else
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack('unknown');
|
||||
}
|
||||
},
|
||||
'datetime' => function( $val )
|
||||
{
|
||||
return \IPS\DateTime::ts( $val );
|
||||
}
|
||||
);
|
||||
|
||||
\IPS\Output::i()->title = $badge->_title;
|
||||
\IPS\Output::i()->output = $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Import dialog
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function exportForm()
|
||||
{
|
||||
$assignedBadges = \IPS\core\Achievements\Badge::getAssignedBadgeIds();
|
||||
|
||||
$where = NULL;
|
||||
if ( \count( $assignedBadges ) )
|
||||
{
|
||||
$where = [ \IPS\Db::i()->in( '`id`', $assignedBadges, TRUE ) ];
|
||||
}
|
||||
|
||||
$exportableBadges = \IPS\Db::i()->select( 'COUNT(*)', 'core_badges', $where )->first();
|
||||
|
||||
/* Display */
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'achievements' )->badgeExport( $exportableBadges );
|
||||
}
|
||||
|
||||
/**
|
||||
* Export ranks with images as an XML file (XML is better at potentially large values from raw image data)
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function export()
|
||||
{
|
||||
$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 )
|
||||
{
|
||||
$langs[ \mb_substr( $row['word_key'], 12 ) ] = $row['word_default'];
|
||||
}
|
||||
|
||||
$assignedBadges = \IPS\core\Achievements\Badge::getAssignedBadgeIds();
|
||||
|
||||
$where = NULL;
|
||||
if ( \count( $assignedBadges ) )
|
||||
{
|
||||
$where = [ \IPS\Db::i()->in( '`id`', $assignedBadges, TRUE ) ];
|
||||
}
|
||||
|
||||
/* Ranks */
|
||||
foreach ( \IPS\Db::i()->select( '*', 'core_badges', $where ) as $badge )
|
||||
{
|
||||
try
|
||||
{
|
||||
$icon = \IPS\File::get( 'core_Badges', $badge['image'] );
|
||||
|
||||
$forXml = [
|
||||
'manually_awarded' => $badge['manually_awarded'],
|
||||
'title' => $langs[ $badge['id'] ],
|
||||
'icon_name' => $icon->originalFilename,
|
||||
'icon_data' => base64_encode( $icon->contents() )
|
||||
];
|
||||
|
||||
$xml->addChild( 'badge', $forXml );
|
||||
}
|
||||
catch( \Exception $e ) { }
|
||||
}
|
||||
|
||||
\IPS\Output::i()->sendOutput( $xml->asXML(), 200, 'application/xml', array( 'Content-Disposition' => \IPS\Output::getContentDisposition( 'attachment', "Achievement_Badges.xml" ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Import from upload
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function import()
|
||||
{
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
|
||||
if ( !file_exists( \IPS\Request::i()->file ) or md5_file( \IPS\Request::i()->file ) !== \IPS\Request::i()->key )
|
||||
{
|
||||
\IPS\Output::i()->error( 'generic_error', '3C130/1', 500, '' );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
\IPS\core\Achievements\Badge::importXml( \IPS\Request::i()->file, ( ! empty( \IPS\Request::i()->wipe ) ) );
|
||||
}
|
||||
catch( \Exception $e )
|
||||
{
|
||||
\IPS\Output::i()->error( $e->getMessage(), '2C163/1', 403, '' );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=achievements&controller=badges' ), 'completed' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Import dialog
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function importForm()
|
||||
{
|
||||
$form = new \IPS\Helpers\Form( 'form', 'acp_achievements_import' );
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'acp_achievements_import_option_badge_wipe', 0, FALSE, [], NULL, NULL, NULL, 'acp_achievements_import_option_rule_wipe' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Upload( 'acp_achievements_import_xml', NULL, FALSE, [ 'allowedFileTypes' => array( 'xml' ), 'temporary' => TRUE ], NULL, NULL, NULL, 'acp_achievements_import_xml' ) );
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
/* Move it to a temporary location */
|
||||
$tempFile = tempnam( \IPS\TEMP_DIRECTORY, 'IPS' );
|
||||
move_uploaded_file( $values['acp_achievements_import_xml'], $tempFile );
|
||||
|
||||
/* Initate a redirector */
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=achievements&controller=badges&do=import' )->setQueryString( array('wipe' => $values['acp_achievements_import_option_badge_wipe'], 'file' => $tempFile, 'key' => md5_file( $tempFile )) )->csrf() );
|
||||
}
|
||||
|
||||
/* Display */
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->block( \IPS\Member::loggedIn()->language()->addToStack('acp_achievements_import'), $form, FALSE );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Achievement ranks
|
||||
* @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 Apr 2013
|
||||
*/
|
||||
|
||||
namespace IPS\core\modules\admin\achievements;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Achievement ranks
|
||||
*/
|
||||
class _ranks extends \IPS\Node\Controller
|
||||
{
|
||||
/**
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Cached counts
|
||||
*/
|
||||
public static $counts = [];
|
||||
|
||||
/**
|
||||
* Node Class
|
||||
*/
|
||||
protected $nodeClass = 'IPS\core\Achievements\Rank';
|
||||
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'ranks_manage' );
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
static::$counts = [];
|
||||
$previousCount = NULL;
|
||||
|
||||
$allRanks = array_values( \IPS\core\Achievements\Rank::getStore() );
|
||||
$totalRanks = \count( $allRanks );
|
||||
for ( $i = 0; $i < $totalRanks; $i++ )
|
||||
{
|
||||
$where = [];
|
||||
$where[] = [ 'achievements_points>=' . \intval( $allRanks[ $i ]->points ) ];
|
||||
if ( isset( $allRanks[ $i + 1 ] ) )
|
||||
{
|
||||
$where[] = [ 'achievements_points<' . \intval( $allRanks[ $i + 1 ]->points ) ];
|
||||
}
|
||||
static::$counts[ $allRanks[ $i ]->id ] = \IPS\Db::i()->select( 'COUNT(*)', 'core_members', $where )->first();
|
||||
}
|
||||
|
||||
if( $data = \IPS\core\Achievements\Rule::getRebuildProgress() )
|
||||
{
|
||||
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate( 'achievements' )->rebuildProgress( $data, TRUE );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->sidebar['actions']['export'] = array(
|
||||
'primary' => false,
|
||||
'icon' => 'cloud-download',
|
||||
'link' => \IPS\Http\Url::internal('app=core&module=achievements&controller=ranks&do=export'),
|
||||
'data' => [],
|
||||
'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=ranks&do=importForm'),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('acp_achievements_import') ),
|
||||
'title' => 'acp_achievements_import',
|
||||
);
|
||||
|
||||
return parent::manage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Import dialog
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function importForm()
|
||||
{
|
||||
$form = new \IPS\Helpers\Form( 'form', 'acp_achievements_import' );
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'acp_achievements_import_option', 'replace', FALSE, [ 'options' =>
|
||||
[
|
||||
'wipe' => 'acp_achievements_import_option_wipe',
|
||||
'replace' => 'acp_achievements_import_option_replace'
|
||||
],
|
||||
'toggles' => [ 'wipe' => [ 'acp_achievements_import_option_wipe_sure' ] ]
|
||||
] ) );
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'acp_achievements_import_option_wipe_sure', 0, FALSE, [], NULL, NULL, NULL, 'acp_achievements_import_option_wipe_sure' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Upload( 'acp_achievements_import_xml', NULL, FALSE, [ 'allowedFileTypes' => array( 'xml' ), 'temporary' => TRUE ], NULL, NULL, NULL, 'acp_achievements_import_xml' ) );
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
if ( $values['acp_achievements_import_option'] == 'wipe' and empty( $values['acp_achievements_import_option_wipe_sure'] ) )
|
||||
{
|
||||
$form->error = \IPS\Member::loggedIn()->language()->addToStack( 'acp_achievements_import_error' );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Move it to a temporary location */
|
||||
$tempFile = tempnam( \IPS\TEMP_DIRECTORY, 'IPS' );
|
||||
move_uploaded_file( $values['acp_achievements_import_xml'], $tempFile );
|
||||
|
||||
/* Initate a redirector */
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=achievements&controller=ranks&do=import' )->setQueryString( array('option' => $values['acp_achievements_import_option'], 'file' => $tempFile, 'key' => md5_file( $tempFile )) )->csrf() );
|
||||
}
|
||||
}
|
||||
|
||||
/* Display */
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->block( \IPS\Member::loggedIn()->language()->addToStack('acp_achievements_import'), $form, FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Import from upload
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function import()
|
||||
{
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
|
||||
if ( !file_exists( \IPS\Request::i()->file ) or md5_file( \IPS\Request::i()->file ) !== \IPS\Request::i()->key )
|
||||
{
|
||||
\IPS\Output::i()->error( 'generic_error', '3C130/1', 500, '' );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
\IPS\core\Achievements\Rank::importXml( \IPS\Request::i()->file, \IPS\Request::i()->option );
|
||||
}
|
||||
catch( \Exception $e )
|
||||
{
|
||||
\IPS\Output::i()->error( $e->getMessage(), '2C163/1', 403, '' );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=achievements&controller=ranks' ), 'completed' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Export ranks with images as an XML file (XML is better at potentially large values from raw image data)
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function export()
|
||||
{
|
||||
$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 )
|
||||
{
|
||||
$langs[ \mb_substr( $row['word_key'], 17 ) ] = $row['word_default'];
|
||||
}
|
||||
|
||||
/* Ranks */
|
||||
foreach ( \IPS\Db::i()->select( '*', 'core_member_ranks') as $row )
|
||||
{
|
||||
$forXml = [
|
||||
'posts' => $row['posts'],
|
||||
'title' => isset( $langs[ $row['id'] ] ) ? $langs[ $row['id'] ] : NULL,
|
||||
'points' => $row['points']
|
||||
];
|
||||
|
||||
if ( $row['icon'] )
|
||||
{
|
||||
try
|
||||
{
|
||||
$icon = \IPS\File::get( 'core_Badges', $row['icon'] );
|
||||
$forXml['icon_name'] = $icon->originalFilename;
|
||||
$forXml['icon_data'] = base64_encode( $icon->contents() );
|
||||
}
|
||||
catch( \Exception $e ) { }
|
||||
}
|
||||
|
||||
$xml->addChild( 'rank', $forXml );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->sendOutput( $xml->asXML(), 200, 'application/xml', array( 'Content-Disposition' => \IPS\Output::getContentDisposition( 'attachment', "Achievement_Ranks.xml" ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch any additional HTML for this row
|
||||
*
|
||||
* @param object $node Node returned from $nodeClass::load()
|
||||
* @return NULL|string
|
||||
*/
|
||||
public function _getRowHtml( $node )
|
||||
{
|
||||
$url = \IPS\Http\Url::internal("app=core&module=members&controller=members&advanced_search_submitted=1&members_achievements_points={$node->id}")->csrf();
|
||||
|
||||
return \IPS\Theme::i()->getTemplate('achievements')->memberCount( $url, static::$counts[ $node->id ] );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Achievement rules
|
||||
* @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 Feb 2021
|
||||
*/
|
||||
|
||||
namespace IPS\core\modules\admin\achievements;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Achievement rules
|
||||
*/
|
||||
class _rules extends \IPS\Node\Controller
|
||||
{
|
||||
/**
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* Node Class
|
||||
*/
|
||||
protected $nodeClass = 'IPS\core\Achievements\Rule';
|
||||
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'rules_manage' );
|
||||
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'achievements/achievements.css', 'core', 'admin' ) );
|
||||
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'admin_members.js', 'core' ) );
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
$baseUrl = \IPS\Http\Url::internal('app=core&module=achievements&controller=rules');
|
||||
$perPage = 25;
|
||||
$totalCount = \IPS\Db::i()->select( 'COUNT(*)', 'core_achievements_rules' )->first();
|
||||
$totalPages = ceil( $totalCount / $perPage );
|
||||
$page = \IPS\Request::i()->page ?? 1;
|
||||
|
||||
$query = \IPS\Db::i()->select( '*', 'core_achievements_rules', NULL, 'action, milestone ASC', [ ( $perPage * ( $page - 1 ) ), $perPage ] );
|
||||
$rules = new \IPS\Patterns\ActiveRecordIterator( $query, 'IPS\core\Achievements\Rule' );
|
||||
$pagination = $totalPages > 1 ? \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->pagination( $baseUrl, $totalPages, $page, $perPage ) : '';
|
||||
|
||||
if( \IPS\Request::i()->isAjax() )
|
||||
{
|
||||
\IPS\Output::i()->json([
|
||||
'rows' => \IPS\Theme::i()->getTemplate( 'achievements' )->rulesListRows( $rules ),
|
||||
'pagination' => $pagination
|
||||
]);
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->sidebar['actions']['export'] = array(
|
||||
'primary' => false,
|
||||
'icon' => 'cloud-download',
|
||||
'link' => \IPS\Http\Url::internal('app=core&module=achievements&controller=rules&do=export'),
|
||||
'data' => [],
|
||||
'title' => 'acp_achievements_export',
|
||||
);
|
||||
|
||||
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'achievements', 'rules_add' ) )
|
||||
{
|
||||
\IPS\Output::i()->sidebar['actions']['import'] = array(
|
||||
'primary' => false,
|
||||
'icon' => 'cloud-upload',
|
||||
'link' => \IPS\Http\Url::internal('app=core&module=achievements&controller=rules&do=importForm'),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('acp_achievements_import') ),
|
||||
'title' => 'acp_achievements_import',
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'achievements' )->rulesList( $rules, $pagination, $this->_getRootButtons() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get form
|
||||
*
|
||||
* @param \IPS\Helpers\Form $form The form as returned by _addEditForm()
|
||||
* @return string
|
||||
*/
|
||||
protected function _showForm( \IPS\Helpers\Form $form )
|
||||
{
|
||||
\IPS\Output::i()->hiddenElements = array('acpHeader');
|
||||
|
||||
if ( \IPS\Request::i()->id )
|
||||
{
|
||||
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'rules_edit' );
|
||||
try
|
||||
{
|
||||
$form->hiddenValues['rule_enabled'] = \IPS\core\Achievements\Rule::load( \IPS\Request::i()->id )->enabled;
|
||||
}
|
||||
catch( \Exception $e ) { }
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'rules_add' );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_achievements_rules');
|
||||
|
||||
return $form->customTemplate( [ \IPS\Theme::i()->getTemplate( 'achievements' ), 'rulesForm' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle enabled status
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function toggleEnabled()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'rules_edit' );
|
||||
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
|
||||
try
|
||||
{
|
||||
$rule = \IPS\core\Achievements\Rule::load( \IPS\Request::i()->id );
|
||||
$rule->enabled = (int) \IPS\Request::i()->enable;
|
||||
$rule->save();
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=achievements&controller=rules' ), 'updated' );
|
||||
}
|
||||
catch( \Exception $e )
|
||||
{
|
||||
\IPS\Output::i()->error( 'node_error', '2T353/3', 404, '' ); /* @todo error code */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Export rules with badges as an XML file (XML is better at potentially large values from raw image data)
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function export()
|
||||
{
|
||||
$xml = \IPS\Xml\SimpleXML::create('rules');
|
||||
$badgeMap = [];
|
||||
|
||||
$langs = [];
|
||||
foreach( \IPS\Db::i()->select( 'word_key, word_default', 'core_sys_lang_words', [ 'lang_id=? AND (word_key LIKE \'core_award_subject_badge_%\' OR word_key LIKE \'core_award_other_badge_%\')', \IPS\Member::loggedIn()->language()->id ] ) as $row )
|
||||
{
|
||||
$langs[ $row['word_key'] ] = $row['word_default'];
|
||||
}
|
||||
|
||||
/* Rules */
|
||||
foreach ( \IPS\Db::i()->select( '*', 'core_achievements_rules') as $row )
|
||||
{
|
||||
$forXml = [];
|
||||
foreach( $row as $k => $v )
|
||||
{
|
||||
if ( $k !== 'id' )
|
||||
{
|
||||
if ( $k == 'badge_subject' and $v )
|
||||
{
|
||||
if ( !isset( $badgeMap[$row['badge_subject']] ) )
|
||||
{
|
||||
$badgeMap[$row['badge_subject']] = uniqid();
|
||||
}
|
||||
|
||||
$v = $badgeMap[$row['badge_subject']];
|
||||
}
|
||||
if ( $k == 'badge_other' and $v )
|
||||
{
|
||||
if ( !isset( $badgeMap[$row['badge_other']] ) )
|
||||
{
|
||||
$badgeMap[$row['badge_other']] = uniqid();
|
||||
}
|
||||
|
||||
$v = $badgeMap[$row['badge_other']];
|
||||
}
|
||||
|
||||
$forXml[$k] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
$forXml['award_subject_lang'] = $langs['core_award_subject_badge_' . $row['id']] ?? NULL;
|
||||
$forXml['award_other_lang'] = $langs['core_award_other_badge_' . $row['id']] ?? NULL;
|
||||
|
||||
$xml->addChild( 'rule', $forXml );
|
||||
}
|
||||
|
||||
/* Badges */
|
||||
if ( \count( $badgeMap ) )
|
||||
{
|
||||
$langs = [];
|
||||
foreach( \IPS\Db::i()->select('word_key, word_default', 'core_sys_lang_words', [ \IPS\Db::i()->like( 'word_key', 'core_badges_') ] ) as $row )
|
||||
{
|
||||
$langs[ \mb_substr( $row['word_key'], 12 ) ] = $row['word_default'];
|
||||
}
|
||||
|
||||
foreach( \IPS\Db::i()->select('*', 'core_badges', [ \IPS\Db::i()->in( '`id`', array_keys( $badgeMap ) ) ] ) as $badge )
|
||||
{
|
||||
try
|
||||
{
|
||||
$icon = \IPS\File::get( 'core_Badges', $badge['image'] );
|
||||
|
||||
$xml->addChild( 'badge', [
|
||||
'manually_awarded' => $badge['manually_awarded'],
|
||||
'id' => $badgeMap[ $badge['id'] ],
|
||||
'title' => $langs[ $badge['id'] ],
|
||||
'icon_name' => $icon->originalFilename,
|
||||
'icon_data' => base64_encode( $icon->contents() )
|
||||
] );
|
||||
}
|
||||
catch( \Exception $e ) { }
|
||||
}
|
||||
}
|
||||
|
||||
\IPS\Output::i()->sendOutput( $xml->asXML(), 200, 'application/xml', array( 'Content-Disposition' => \IPS\Output::getContentDisposition( 'attachment', "Achievement_Rules.xml" ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Import dialog
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function importForm()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'rules_add' );
|
||||
$form = new \IPS\Helpers\Form( 'form', 'acp_achievements_import' );
|
||||
|
||||
$form->addMessage( \IPS\Member::loggedIn()->language()->addToStack( 'acp_achievements_import_rules_message'), 'ipsMessage ipsMessage_info');
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'acp_achievements_import_option_rule_wipe', 0, FALSE, [], NULL, NULL, NULL, 'acp_achievements_import_option_rule_wipe' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Upload( 'acp_achievements_import_xml', NULL, FALSE, [ 'allowedFileTypes' => array( 'xml' ), 'temporary' => TRUE ], NULL, NULL, NULL, 'acp_achievements_import_xml' ) );
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
/* Move it to a temporary location */
|
||||
$tempFile = tempnam( \IPS\TEMP_DIRECTORY, 'IPS' );
|
||||
move_uploaded_file( $values['acp_achievements_import_xml'], $tempFile );
|
||||
|
||||
/* Initate a redirector */
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=achievements&controller=rules&do=import' )->setQueryString( array('wipe' => $values['acp_achievements_import_option_rule_wipe'], 'file' => $tempFile, 'key' => md5_file( $tempFile )) )->csrf() );
|
||||
}
|
||||
|
||||
/* Display */
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->block( \IPS\Member::loggedIn()->language()->addToStack('acp_achievements_import'), $form, FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Import from upload
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function import()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'rules_add' );
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
|
||||
if ( !file_exists( \IPS\Request::i()->file ) or md5_file( \IPS\Request::i()->file ) !== \IPS\Request::i()->key )
|
||||
{
|
||||
\IPS\Output::i()->error( 'generic_error', '3C130/1', 500, '' );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
\IPS\core\Achievements\Rule::importXml( \IPS\Request::i()->file, ( ! empty( \IPS\Request::i()->wipe ) ) );
|
||||
}
|
||||
catch( \Exception $e )
|
||||
{
|
||||
\IPS\Output::i()->error( $e->getMessage(), '2C163/1', 403, '' );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=achievements&controller=rules' ), 'completed' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Achievement 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 01 Mar 2021
|
||||
*/
|
||||
|
||||
namespace IPS\core\modules\admin\achievements;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Achievement settings
|
||||
*/
|
||||
class _settings extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'settings_manage' );
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage Settings
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
$form = new \IPS\Helpers\Form;
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'achievements_enabled', \IPS\Settings::i()->achievements_enabled, FALSE, [ 'togglesOn' => [ 'rare_badge_percent', 'prune_points_log', 'rules_exclude_groups', 'achievements_recognize_max_per_user_day' ] ] ) );
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Number( 'rare_badge_percent', \IPS\Settings::i()->rare_badge_percent, FALSE, [ 'decimals' => 1, 'unlimited' => 0, 'unlimitedLang' => 'never' ], NULL, \IPS\Member::loggedIn()->language()->addToStack('rare_badge_percent_prefix'), \IPS\Member::loggedIn()->language()->addToStack('rare_badge_percent_suffix'), 'rare_badge_percent' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Interval( 'prune_points_log', \IPS\Settings::i()->prune_points_log, FALSE, [ 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'never' ], NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL, 'prune_points_log' ) );
|
||||
$groups = array_combine( array_keys( \IPS\Member\Group::groups( TRUE, FALSE ) ), array_map( function( $_group ) { return (string) $_group; }, \IPS\Member\Group::groups( TRUE, FALSE ) ) );
|
||||
$selectedGroups = json_decode( \IPS\Settings::i()->rules_exclude_groups, TRUE );
|
||||
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'rules_exclude_groups', $selectedGroups, FALSE, array( 'options' => $groups, 'multiple' => true ), NULL, NULL, NULL, 'rules_exclude_groups' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Number( 'achievements_recognize_max_per_user_day', \IPS\Settings::i()->achievements_recognize_max_per_user_day, FALSE, [ 'unlimited' => -1 ], NULL, \IPS\Member::loggedIn()->language()->addToStack('achievements_recognize_max_per_user_day_prefix'), \IPS\Member::loggedIn()->language()->addToStack('achievements_recognize_max_per_user_day_suffix'), 'achievements_recognize_max_per_user_day' ) );
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
$values['rules_exclude_groups'] = json_encode( $values['rules_exclude_groups'] );
|
||||
$form->saveAsSettings( $values );
|
||||
|
||||
\IPS\Session::i()->log( 'acplog__achievement_settings' );
|
||||
}
|
||||
|
||||
if ( \IPS\Settings::i()->achievements_enabled )
|
||||
{
|
||||
\IPS\Output::i()->sidebar['actions']['rebuild'] = array(
|
||||
'primary' => true,
|
||||
'icon' => 'plus',
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=achievements&controller=settings&do=rebuildForm' )->csrf(),
|
||||
'data' => array('ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack( 'acp_rebuild_achievements' )),
|
||||
'title' => 'acp_rebuild_achievements',
|
||||
);
|
||||
}
|
||||
|
||||
if( $data = \IPS\core\Achievements\Rule::getRebuildProgress() )
|
||||
{
|
||||
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate( 'achievements' )->rebuildProgress( $data, TRUE );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('achievements_settings');
|
||||
\IPS\Output::i()->output .= $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild Members' Achievements
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function rebuildForm()
|
||||
{
|
||||
if ( ! \IPS\Settings::i()->achievements_enabled )
|
||||
{
|
||||
\IPS\Output::i()->error( 'achievements_not_enabled', '1C421/1', 403, '' );
|
||||
}
|
||||
|
||||
$form = new \IPS\Helpers\Form( 'rebuild_form', 'acp_rebuild_achievements_rebuild');
|
||||
$form->addMessage('acp_rebuild_achievements_blurb', 'ipsMessage ipsMessage_info');
|
||||
$form->add( new \IPS\Helpers\Form\Checkbox( 'acp_rebuild_achievements_checkbox', FALSE, TRUE ) );
|
||||
$form->add( new \IPS\Helpers\Form\Date( 'acp_rebuild_achievements_time', 0, TRUE, [ 'unlimited' => 0, 'unlimitedLang' => 'acp_rebuild_achievements_time_unlimited' ] ) );
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
if ( ! $values['acp_rebuild_achievements_checkbox'] )
|
||||
{
|
||||
$form->error = \IPS\Member::loggedIn()->language()->addToStack('acp_achievements_rebuild_not_checked');
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\core\Achievements\Rule::rebuildAllAchievements( $values['acp_rebuild_achievements_time'] ?: NULL );
|
||||
|
||||
\IPS\Settings::i()->changeValues( array('achievements_rebuilding' => 1) );
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=achievements&controller=settings' ) );
|
||||
}
|
||||
}
|
||||
|
||||
\IPS\Output::i()->bypassCsrfKeyCheck = TRUE;
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('acp_rebuild_achievements');
|
||||
\IPS\Output::i()->output = $form;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,6 +26,11 @@ class _communityactivity extends \IPS\Dispatcher\Controller
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Allow MySQL RW separation for efficiency
|
||||
*/
|
||||
public static $allowRWSeparation = TRUE;
|
||||
|
||||
/**
|
||||
* Execute
|
||||
|
||||
@@ -26,6 +26,11 @@ class _emailstats extends \IPS\Dispatcher\Controller
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Allow MySQL RW separation for efficiency
|
||||
*/
|
||||
public static $allowRWSeparation = TRUE;
|
||||
|
||||
/**
|
||||
* Execute
|
||||
@@ -201,7 +206,7 @@ class _emailstats extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
catch( \UnderflowException $e ){}
|
||||
|
||||
$results[ $_date ][ $lang ] = $row['value_1'];
|
||||
$results[ $_date ][ $lang ] += (int) $row['value_1'];
|
||||
}
|
||||
|
||||
return $results;
|
||||
|
||||
@@ -26,6 +26,11 @@ class _keywords extends \IPS\Dispatcher\Controller
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Allow MySQL RW separation for efficiency
|
||||
*/
|
||||
public static $allowRWSeparation = TRUE;
|
||||
|
||||
/**
|
||||
* Execute
|
||||
|
||||
@@ -22,11 +22,21 @@ if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
*/
|
||||
class _overview extends \IPS\core\modules\admin\stats\overview
|
||||
{
|
||||
/**
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Template group to use to output
|
||||
*/
|
||||
protected $templateGroup = 'activitystats';
|
||||
|
||||
/**
|
||||
* @brief Allow MySQL RW separation for efficiency
|
||||
*/
|
||||
public static $allowRWSeparation = TRUE;
|
||||
|
||||
/**
|
||||
* Create the general page layout, but we will load the individual cells via AJAX to ensure there are no performance concerns loading the page
|
||||
*
|
||||
|
||||
@@ -26,6 +26,11 @@ class _reactions extends \IPS\Dispatcher\Controller
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Allow MySQL RW separation for efficiency
|
||||
*/
|
||||
public static $allowRWSeparation = TRUE;
|
||||
|
||||
/**
|
||||
* Execute
|
||||
|
||||
@@ -27,6 +27,16 @@ class _search extends \IPS\Dispatcher\Controller
|
||||
*/
|
||||
protected $defaultLimit = 25;
|
||||
|
||||
/**
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Allow MySQL RW separation for efficiency
|
||||
*/
|
||||
public static $allowRWSeparation = TRUE;
|
||||
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
@@ -150,7 +160,7 @@ class _search extends \IPS\Dispatcher\Controller
|
||||
$where[] = \IPS\Db::i()->like( 'value_4', $term, TRUE, TRUE, TRUE );
|
||||
}
|
||||
|
||||
foreach( \IPS\Db::i()->select( 'value_4, COUNT(*) as total', 'core_statistics', $where, 'total DESC', $this->defaultLimit, 'value_4' ) as $searchedValue )
|
||||
foreach( \IPS\Db::i()->select( 'SQL_BIG_RESULT value_4, COUNT(*) as total', 'core_statistics', $where, 'total DESC', $this->defaultLimit, 'value_4' ) as $searchedValue )
|
||||
{
|
||||
$this->_topSearchTerms[ $term ][] = $searchedValue['value_4'];
|
||||
}
|
||||
|
||||
@@ -26,6 +26,11 @@ class _tags extends \IPS\Dispatcher\Controller
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Allow MySQL RW separation for efficiency
|
||||
*/
|
||||
public static $allowRWSeparation = TRUE;
|
||||
|
||||
/**
|
||||
* Execute
|
||||
@@ -69,6 +74,9 @@ class _tags extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
$where[] = array( "tag_added<?", $chart->end->getTimestamp() );
|
||||
}
|
||||
|
||||
/* Only get visible tags */
|
||||
$where[] = array( 'tag_aai_lookup IN(?)', \IPS\Db::i()->select( 'tag_perm_aai_lookup', 'core_tags_perms', [ 'tag_perm_visible=1' ] ) );
|
||||
|
||||
foreach( \IPS\Db::i()->select( 'tag_text', 'core_tags', $where, NULL, NULL, array( 'tag_text' ) ) as $tag )
|
||||
{
|
||||
|
||||
@@ -76,7 +76,7 @@ class _apiReference extends \IPS\Dispatcher\Controller
|
||||
$params = array_merge( $params, $endpoints[ $selected ]['details']['apiparam'] );
|
||||
}
|
||||
|
||||
foreach( $params as $data )
|
||||
foreach( $params as $index => $data )
|
||||
{
|
||||
if ( mb_strpos( $data[0], '|' ) === FALSE AND !\in_array( $data[0], array( 'int', 'string', 'float', 'datetime', 'bool', 'object', 'array' ) ) )
|
||||
{
|
||||
@@ -84,12 +84,26 @@ class _apiReference extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
if ( !\in_array( mb_substr( $data[0], 1, -1 ), array( 'int', 'string', 'float', 'datetime', 'bool', 'object', 'array' ) ) )
|
||||
{
|
||||
$additionalClassesToReference = array_merge( $additionalClassesToReference, $this->_getAdditionalClasses( mb_substr( $data[0], 1, -1 ) ) );
|
||||
if( $returned = $this->_getAdditionalClasses( mb_substr( $data[0], 1, -1 ) ) )
|
||||
{
|
||||
$additionalClassesToReference = array_merge( $additionalClassesToReference, $returned );
|
||||
}
|
||||
elseif( $returned === NULL )
|
||||
{
|
||||
unset( $params[ $index ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$additionalClassesToReference = array_merge( $additionalClassesToReference, $this->_getAdditionalClasses( $data[0] ) );
|
||||
if( $returned = $this->_getAdditionalClasses( $data[0] ) )
|
||||
{
|
||||
$additionalClassesToReference = array_merge( $additionalClassesToReference, $returned );
|
||||
}
|
||||
elseif( $returned === NULL )
|
||||
{
|
||||
unset( $params[ $index ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,7 +133,7 @@ class _apiReference extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
if ( isset( $endpoints[ $selected ]['details']['apiresponse'] ) )
|
||||
{
|
||||
foreach ( $endpoints[ $selected ]['details']['apiresponse'] as $data )
|
||||
foreach ( $endpoints[ $selected ]['details']['apiresponse'] as $index => $data )
|
||||
{
|
||||
if ( mb_strpos( $data[0], '|' ) === FALSE AND !\in_array( $data[0], array( 'int', 'string', 'float', 'datetime', 'bool', 'object', 'array' ) ) )
|
||||
{
|
||||
@@ -127,12 +141,26 @@ class _apiReference extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
if ( !\in_array( mb_substr( $data[0], 1, -1 ), array( 'int', 'string', 'float', 'datetime', 'bool', 'object', 'array' ) ) )
|
||||
{
|
||||
$additionalClassesToReference = array_merge( $additionalClassesToReference, $this->_getAdditionalClasses( mb_substr( $data[0], 1, -1 ) ) );
|
||||
if( $returned = $this->_getAdditionalClasses( mb_substr( $data[0], 1, -1 ) ) )
|
||||
{
|
||||
$additionalClassesToReference = array_merge( $additionalClassesToReference, $returned );
|
||||
}
|
||||
elseif( $returned === NULL )
|
||||
{
|
||||
unset( $endpoints[ $selected ]['details']['apiresponse'][ $index ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$additionalClassesToReference = array_merge( $additionalClassesToReference, $this->_getAdditionalClasses( $data[0] ) );
|
||||
if( $returned = $this->_getAdditionalClasses( $data[0] ) )
|
||||
{
|
||||
$additionalClassesToReference = array_merge( $additionalClassesToReference, $returned );
|
||||
}
|
||||
elseif( $returned === NULL )
|
||||
{
|
||||
unset( $endpoints[ $selected ]['details']['apiresponse'][ $index ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,10 +219,15 @@ class _apiReference extends \IPS\Dispatcher\Controller
|
||||
*
|
||||
* @param string $class The classname
|
||||
* @param bool $exclude If FALSE, will include this class itself in the return array
|
||||
* @return array
|
||||
* @return array|NULL
|
||||
*/
|
||||
protected function _getAdditionalClasses( $class, $exclude=FALSE )
|
||||
{
|
||||
if( !class_exists( $class ) )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
$return = $exclude ? array() : array( $class => $class );
|
||||
$reflection = new \ReflectionMethod( $class, 'apiOutput' );
|
||||
$decoded = \IPS\Api\Controller::decodeDocblock( $reflection->getDocComment() );
|
||||
@@ -206,12 +239,18 @@ class _apiReference extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
if ( !\in_array( mb_substr( $response[0], 1, -1 ), $return ) and !\in_array( mb_substr( $response[0], 1, -1 ), array( 'int', 'string', 'float', 'datetime', 'bool', 'object', 'array' ) ) )
|
||||
{
|
||||
$return = array_merge( $return, $this->_getAdditionalClasses( mb_substr( $response[0], 1, -1 ) ) );
|
||||
if( $returned = $this->_getAdditionalClasses( mb_substr( $response[0], 1, -1 ) ) )
|
||||
{
|
||||
$return = array_merge( $return, $returned );
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif ( !\in_array( $response[0], $return ) )
|
||||
{
|
||||
$return = array_merge( $return, $this->_getAdditionalClasses( $response[0] ) );
|
||||
if( $returned = $this->_getAdditionalClasses( $response[0] ) )
|
||||
{
|
||||
$return = array_merge( $return, $returned );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,14 @@ class _applications extends \IPS\Node\Controller
|
||||
/* Create the basic tree */
|
||||
if ( !\IPS\Request::i()->isAjax() )
|
||||
{
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('forms')->blurb( 'applications_blurb' );
|
||||
if( \IPS\IPS::canManageResources() )
|
||||
{
|
||||
\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_upload' );
|
||||
}
|
||||
}
|
||||
parent::manage();
|
||||
|
||||
@@ -120,26 +127,38 @@ class _applications extends \IPS\Node\Controller
|
||||
{
|
||||
foreach ( $uninstalled as $k => $app )
|
||||
{
|
||||
$rows[ $k ] = \IPS\Theme::i()->getTemplate( 'trees' )->row( $baseUrl, $k, $app['title'], FALSE, array(
|
||||
'add' => array(
|
||||
'icon' => 'plus-circle',
|
||||
'title' => 'install',
|
||||
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=applications&appKey={$k}&do=install" )->csrf(),
|
||||
)
|
||||
) );
|
||||
$buttons = array();
|
||||
if( \IPS\IPS::canManageResources() )
|
||||
{
|
||||
$buttons = array(
|
||||
'add' => array(
|
||||
'icon' => 'plus-circle',
|
||||
'title' => 'install',
|
||||
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=applications&appKey={$k}&do=install" )->csrf(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$rows[ $k ] = \IPS\Theme::i()->getTemplate( 'trees' )->row( $baseUrl, $k, $app['title'], FALSE, $buttons );
|
||||
}
|
||||
}
|
||||
return $rows;
|
||||
},
|
||||
function( $key, $root=FALSE ) use ( $uninstalled, $baseUrl )
|
||||
{
|
||||
return \IPS\Theme::i()->getTemplate( 'trees' )->row( $baseUrl, $key, $uninstalled[ $key ]['title'], FALSE, array(
|
||||
'add' => array(
|
||||
'icon' => 'plus-circle',
|
||||
'title' => 'install',
|
||||
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=applications&appKey={$key}&do=install" )->csrf(),
|
||||
)
|
||||
), '', NULL, NULL, $root );
|
||||
$buttons = array();
|
||||
if( \IPS\IPS::canManageResources() )
|
||||
{
|
||||
$buttons = array(
|
||||
'add' => array(
|
||||
'icon' => 'plus-circle',
|
||||
'title' => 'install',
|
||||
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=applications&appKey={$key}&do=install" )->csrf(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return \IPS\Theme::i()->getTemplate( 'trees' )->row( $baseUrl, $key, $uninstalled[ $key ]['title'], FALSE, $buttons, '', NULL, NULL, $root );
|
||||
},
|
||||
function() { return 0; },
|
||||
function() { return array(); },
|
||||
@@ -185,14 +204,16 @@ class _applications extends \IPS\Node\Controller
|
||||
{
|
||||
foreach ( $legacyApps as $k => $app )
|
||||
{
|
||||
$buttons = array(
|
||||
'upgrade' => array(
|
||||
'icon' => 'upload',
|
||||
'title' => 'upload_new_version',
|
||||
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=applications&appKey={$k}&do=upload" ),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('upload_new_version') )
|
||||
)
|
||||
);
|
||||
$buttons = array();
|
||||
if( \IPS\IPS::canManageResources() )
|
||||
{
|
||||
$buttons['upgrade'] = array(
|
||||
'icon' => 'upload',
|
||||
'title' => 'upload_new_version',
|
||||
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=applications&appKey={$k}&do=upload" ),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack( 'upload_new_version' ) )
|
||||
);
|
||||
}
|
||||
|
||||
if( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'applications', 'app_delete' ) )
|
||||
{
|
||||
@@ -212,14 +233,16 @@ class _applications extends \IPS\Node\Controller
|
||||
},
|
||||
function( $key, $root=FALSE ) use ( $legacyApps, $baseUrl )
|
||||
{
|
||||
$buttons = array(
|
||||
'upgrade' => array(
|
||||
'icon' => 'upload',
|
||||
'title' => 'upload_new_version',
|
||||
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=applications&appKey={$key}&do=upload" ),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('upload_new_version') )
|
||||
)
|
||||
);
|
||||
$buttons = array();
|
||||
if( \IPS\IPS::canManageResources() )
|
||||
{
|
||||
$buttons['upgrade'] = array(
|
||||
'icon' => 'upload',
|
||||
'title' => 'upload_new_version',
|
||||
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=applications&appKey={$key}&do=upload" ),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack( 'upload_new_version' ) )
|
||||
);
|
||||
}
|
||||
|
||||
if( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'applications', 'app_delete' ) )
|
||||
{
|
||||
@@ -460,11 +483,12 @@ class _applications extends \IPS\Node\Controller
|
||||
\IPS\Member::loggedIn()->language()->words['app_enabled_desc'] = \IPS\Member::loggedIn()->language()->addToStack( 'app_enabled_desc_no_writes' );
|
||||
}
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'app_disabled_groups', ( $node->disabled_groups == '*' or $node->disabled_groups === NULL ) ? '*' : explode( ',', $node->disabled_groups ), FALSE, array(
|
||||
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'app_disabled_groups', ( $node->disabled_groups == '*' or $node->disabled_groups === NULL ) ? '*' : explode( ',', $node->disabled_groups ), FALSE, array(
|
||||
'options' => array_combine( array_keys( \IPS\Member\Group::groups() ), array_map( function( $_group ) { return (string) $_group; }, \IPS\Member\Group::groups() ) ),
|
||||
'multiple' => true,
|
||||
'unlimited' => '*',
|
||||
'unlimitedLang' => 'all'
|
||||
'unlimitedLang' => 'all',
|
||||
'impliedUnlimited' => TRUE
|
||||
), NULL, NULL, NULL, 'app_disabled_groups' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Editor( 'app_disabled_message', $node->disabled_message, FALSE, array( 'app' => 'core', 'key' => 'Admin', 'autoSaveKey' => $node->_key . 'app_disabled_message', 'attachIds' => array( $node->id, NULL, 'appdisabled' ) ), NULL, NULL, NULL, 'app_disabled_message_editor' ) );
|
||||
|
||||
@@ -533,13 +557,23 @@ class _applications extends \IPS\Node\Controller
|
||||
|
||||
$form = new \IPS\Helpers\Form;
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'developer_build_type', 'rebuild', TRUE, $options ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'versions_human', $defaults['human'], NULL, array(), NULL, NULL, NULL, 'versions_human' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'versions_human', $defaults['human'], NULL, array(), function( $val )
|
||||
{
|
||||
if ( !preg_match( '/^([0-9]+\.[0-9]+\.[0-9]+)/', $val ) )
|
||||
{
|
||||
throw new \DomainException( 'versions_human_bad' );
|
||||
}
|
||||
}, NULL, NULL, 'versions_human' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'versions_long', $defaults['long'], NULL, array(), function( $val ) use ( $json )
|
||||
{
|
||||
if ( !preg_match( '/^\d*$/', $val ) )
|
||||
{
|
||||
throw new \DomainException( 'form_number_bad' );
|
||||
}
|
||||
if( $val < 10000 )
|
||||
{
|
||||
throw new \DomainException( 'versions_long_too_low' );
|
||||
}
|
||||
if( isset( $json[ $val ] ) )
|
||||
{
|
||||
throw new \DomainException( 'versions_long_exists' );
|
||||
@@ -568,6 +602,12 @@ class _applications extends \IPS\Node\Controller
|
||||
{
|
||||
foreach ( \IPS\Application::applications() as $application )
|
||||
{
|
||||
/* Skip building Marketplace applications */
|
||||
if( $application->marketplace_id )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( $values['developer_build_type'] === 'new' )
|
||||
{
|
||||
$application->assignNewVersion( $values['versions_long'], $values['versions_human'] );
|
||||
@@ -643,6 +683,12 @@ class _applications extends \IPS\Node\Controller
|
||||
public function download()
|
||||
{
|
||||
$application = \IPS\Application::load( \IPS\Request::i()->appKey );
|
||||
|
||||
/* Don't allow downloads of Marketplace resources */
|
||||
if( $application->marketplace_id )
|
||||
{
|
||||
\IPS\Output::i()->error( 'application_cannot_build_marketplace', '2C133/Y', 403, '' );
|
||||
}
|
||||
|
||||
$form = $this->_buildForm( $application->directory, TRUE );
|
||||
if ( $values = $form->values() )
|
||||
@@ -682,7 +728,7 @@ class _applications extends \IPS\Node\Controller
|
||||
unset($download);
|
||||
\Phar::unlinkArchive($pharPath);
|
||||
|
||||
\IPS\Output::i()->sendOutput( $output, 200, 'application/tar', array( 'Content-Disposition' => \IPS\Output::getContentDisposition( 'attachment', $application->directory . '.tar' ) ), FALSE, FALSE, FALSE );
|
||||
\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 );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->output = $form;
|
||||
@@ -726,9 +772,9 @@ class _applications extends \IPS\Node\Controller
|
||||
}
|
||||
|
||||
/* Get the application data to update the application record */
|
||||
if( file_exists( \IPS\ROOT_PATH . '/applications/' . \IPS\Request::i()->appKey . '/data/application.json' ) )
|
||||
if( file_exists( \IPS\Application::getRootPath( \IPS\Request::i()->appKey ) . '/applications/' . \IPS\Request::i()->appKey . '/data/application.json' ) )
|
||||
{
|
||||
$application = json_decode( file_get_contents( \IPS\ROOT_PATH . '/applications/' . $app->directory . '/data/application.json' ), TRUE );
|
||||
$application = json_decode( file_get_contents( \IPS\Application::getRootPath( $app->directory ) . '/applications/' . $app->directory . '/data/application.json' ), TRUE );
|
||||
|
||||
//\IPS\Lang::saveCustom( $app->directory, "__app_{$app->directory}", $application['application_title'] );
|
||||
|
||||
@@ -921,10 +967,10 @@ class _applications extends \IPS\Node\Controller
|
||||
$_SESSION['cmsConflictKey'] = '';
|
||||
try
|
||||
{
|
||||
$result = \IPS\cms\Templates::importTemplatesFromXml( \IPS\ROOT_PATH . '/applications/' . \IPS\Application::load( $data['key'] )->directory . '/data/cmsTemplates.xml' );
|
||||
$result = \IPS\cms\Templates::importUserTemplateXml( \IPS\Application::getRootPath( $data['key'] ) . '/applications/' . \IPS\Application::load( $data['key'] )->directory . '/data/cmsTemplates.xml' );
|
||||
if( $result instanceof \IPS\Http\Url\Internal )
|
||||
{
|
||||
$_SESSION['cmsConflictKey'] = $result->setQueryString( [ 'app' => \IPS\Application::load( $data['key'] )->directory, 'lang' => 'updated' ] );
|
||||
$_SESSION['cmsConflictKey'] = $result->setQueryString( [ 'application' => \IPS\Application::load( $data['key'] )->directory, 'lang' => 'updated' ] );
|
||||
}
|
||||
}
|
||||
catch( \Throwable $e )
|
||||
@@ -1174,10 +1220,10 @@ class _applications extends \IPS\Node\Controller
|
||||
$_SESSION['cmsConflictKey'] = '';
|
||||
try
|
||||
{
|
||||
$result = \IPS\cms\Templates::importTemplatesFromXml( \IPS\Application::getRootPath( $data['key'] ) . '/applications/' . $data['key'] . '/data/cmsTemplates.xml' );
|
||||
$result = \IPS\cms\Templates::importUserTemplateXml( \IPS\Application::getRootPath( $data['key'] ) . '/applications/' . $data['key'] . '/data/cmsTemplates.xml' );
|
||||
if( $result instanceof \IPS\Http\Url\Internal )
|
||||
{
|
||||
$_SESSION['cmsConflictKey'] = $result->setQueryString( [ 'app' => $data['key'], 'lang' => 'installed' ] );
|
||||
$_SESSION['cmsConflictKey'] = $result->setQueryString( [ 'application' => $data['key'], 'lang' => 'installed' ] );
|
||||
}
|
||||
}
|
||||
catch( \Throwable $e )
|
||||
@@ -1507,7 +1553,7 @@ class _applications extends \IPS\Node\Controller
|
||||
\IPS\Output::i()->error( 'no_app_to_update', '2C133/C', 403, '' );
|
||||
}
|
||||
|
||||
if( !is_writable( \IPS\ROOT_PATH . "/applications/" . $app->directory ) )
|
||||
if( !\IPS\CIC2 AND !is_writable( \IPS\ROOT_PATH . "/applications/" . $app->directory ) )
|
||||
{
|
||||
\IPS\Output::i()->error( \IPS\Member::loggedIn()->language()->addToStack( "app_specific_dir_nowrite", FALSE, array( 'sprintf' => $app->directory ) ), '4C133/D', 500, '' );
|
||||
}
|
||||
|
||||
@@ -35,6 +35,10 @@ class _developer extends \IPS\Dispatcher\Controller
|
||||
*/
|
||||
public function execute( $command='do' )
|
||||
{
|
||||
/* This controller can only be accessed in developer mode, so we can bypass the check global */
|
||||
|
||||
\IPS\Output::i()->bypassCsrfKeyCheck = true;
|
||||
|
||||
/* Are we in developer mode? */
|
||||
if( !\IPS\IN_DEV )
|
||||
{
|
||||
@@ -130,17 +134,20 @@ class _developer extends \IPS\Dispatcher\Controller
|
||||
if ( $activeTabContents )
|
||||
{
|
||||
/* Add the build and download button */
|
||||
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'admin_system.js', 'core', 'admin' ) );
|
||||
\IPS\Output::i()->sidebar['actions']['build'] = array(
|
||||
'icon' => 'download',
|
||||
'title' => 'download',
|
||||
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=applications&appKey={$this->application->directory}&do=download" ),
|
||||
'data' => array(
|
||||
'controller' => 'system.buildApp',
|
||||
'downloadURL' => \IPS\Http\Url::internal( "app=core&module=applications&controller=applications&appKey={$this->application->directory}&do=download&type=download" ),
|
||||
'buildURL' => \IPS\Http\Url::internal( "app=core&module=applications&controller=applications&appKey={$this->application->directory}&do=download&type=build" ),
|
||||
)
|
||||
);
|
||||
if( !$this->application->marketplace_id )
|
||||
{
|
||||
\IPS\Output::i()->jsFiles = array_merge(\IPS\Output::i()->jsFiles, \IPS\Output::i()->js('admin_system.js', 'core', 'admin'));
|
||||
\IPS\Output::i()->sidebar['actions']['build'] = array(
|
||||
'icon' => 'download',
|
||||
'title' => 'download',
|
||||
'link' => \IPS\Http\Url::internal("app=core&module=applications&controller=applications&appKey={$this->application->directory}&do=download"),
|
||||
'data' => array(
|
||||
'controller' => 'system.buildApp',
|
||||
'downloadURL' => \IPS\Http\Url::internal("app=core&module=applications&controller=applications&appKey={$this->application->directory}&do=download&type=download"),
|
||||
'buildURL' => \IPS\Http\Url::internal("app=core&module=applications&controller=applications&appKey={$this->application->directory}&do=download&type=build"),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
\IPS\Output::i()->title = $this->application->_title;
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->tabs( $tabs, $this->activeTab, $activeTabContents, \IPS\Http\Url::internal( "app=core&module=applications&controller=developer&appKey={$this->application->directory}" ) );
|
||||
@@ -1842,10 +1849,30 @@ class _developer extends \IPS\Dispatcher\Controller
|
||||
|
||||
elseif ( $activeTab === 'inserts' )
|
||||
{
|
||||
$keys = [];
|
||||
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" ) );
|
||||
$output->langPrefix = "‌";
|
||||
$output->limit = 150;
|
||||
|
||||
|
||||
foreach( $keys as $key )
|
||||
{
|
||||
$output->parsers[ $key ] = function( $val )
|
||||
{
|
||||
if ( mb_strlen( $val ) > 100 )
|
||||
{
|
||||
return mb_substr( $val, 0, 97 ) . '...';
|
||||
}
|
||||
|
||||
return $val;
|
||||
};
|
||||
}
|
||||
|
||||
$output->rootButtons = array(
|
||||
'add' => array(
|
||||
'icon' => 'plus',
|
||||
@@ -1994,6 +2021,15 @@ class _developer extends \IPS\Dispatcher\Controller
|
||||
$values[ $k ] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check default value is a number, where it should be a number */
|
||||
if( !$values['database_column_allow_null'] AND empty( $values['database_column_default'] ) )
|
||||
{
|
||||
if( \in_array( $values['database_column_type'], array( 'TINYINT', 'SMALLINT', 'MEDIUMINT', 'INT', 'INTEGER', 'BIGINT', 'REAL', 'DOUBLE', 'FLOAT', 'DECIMAL', 'NUMERIC' ) ) )
|
||||
{
|
||||
$values['database_column_default'] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get a column definition */
|
||||
$save = array();
|
||||
@@ -2245,16 +2281,16 @@ class _developer extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
\IPS\Db::i()->addIndex( $definition['name'], $save );
|
||||
$this->_writeQueries( 'working', $this->_addQueryToJson( $this->_getQueries( 'working' ), array( 'method' => 'addIndex', 'params' => array( $definition['name'], $save ) ) ) );
|
||||
|
||||
if ( $index['name'] != $save['name'] )
|
||||
{
|
||||
unset( $schema[ $definition['name'] ]['indexes'][ $index['name'] ] );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Db::i()->changeIndex( $definition['name'], $index['name'], $save );
|
||||
$this->_writeQueries( 'working', $this->_addQueryToJson( $this->_getQueries( 'working' ), array( 'method' => 'changeIndex', 'params' => array( $definition['name'], $index['name'], $save ) ) ) );
|
||||
|
||||
if ( $index['name'] != $save['name'] )
|
||||
{
|
||||
unset( $schema[ $definition['name'] ]['indexes'][ $index['name'] ] );
|
||||
}
|
||||
}
|
||||
$schema[ $definition['name'] ]['indexes'][ $save['name'] ] = $save;
|
||||
|
||||
@@ -3103,7 +3139,7 @@ class _developer extends \IPS\Dispatcher\Controller
|
||||
$queries = $this->_getQueries( $long );
|
||||
$order = 1;
|
||||
$rows = array();
|
||||
foreach ( $queries as $qid => $data )
|
||||
foreach ( ( $queries ?? [] ) as $qid => $data )
|
||||
{
|
||||
$params = array();
|
||||
if ( isset( $data['params'] ) and \is_array( $data['params'] ) )
|
||||
@@ -3121,7 +3157,7 @@ class _developer extends \IPS\Dispatcher\Controller
|
||||
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=developer&appKey={$this->application->directory}&do=deleteVersionQuery&version={$long}&query={$qid}" ),
|
||||
'data' => array( 'delete' => '' )
|
||||
)
|
||||
), NULL, NULL, $order, FALSE, NULL, NULL, NULL, TRUE );
|
||||
), NULL, NULL, NULL, FALSE, NULL, NULL, NULL, TRUE, FALSE, FALSE, FALSE );
|
||||
|
||||
$order++;
|
||||
}
|
||||
@@ -3147,7 +3183,13 @@ class _developer extends \IPS\Dispatcher\Controller
|
||||
/* Create New */
|
||||
case 'new':
|
||||
$form->addMessage( 'versions_add_information' );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'versions_human', NULL, TRUE, array( 'placeholder' => '1.0.0' ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'versions_human', NULL, TRUE, array( 'placeholder' => '1.0.0' ), function( $val )
|
||||
{
|
||||
if ( !preg_match( '/^([0-9]+\.[0-9]+\.[0-9]+)/', $val ) )
|
||||
{
|
||||
throw new \DomainException( 'versions_human_bad' );
|
||||
}
|
||||
} ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'versions_long', NULL, TRUE, array( 'placeholder' => '10000' ), function( $val ) use ( $json )
|
||||
{
|
||||
if ( !preg_match( '/^\d*$/', $val ) )
|
||||
|
||||
@@ -206,7 +206,7 @@ class _menu extends \IPS\Dispatcher\Controller
|
||||
'options' => array( 0 => 'menu_manager_access_type_inherit', 1 => 'menu_manager_access_type_override' ),
|
||||
'toggles' => array( 1 => array( 'menu_manager_access' ) )
|
||||
), NULL, NULL, NULL, 'menu_manager_access_type' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'menu_manager_access', $existing ? ( $existing['permissions'] == '*' ? '*' : explode( ',', $existing['permissions'] ) ) : '*', NULL, array( 'multiple' => TRUE, 'options' => $groups, 'unlimited' => '*', 'unlimitedLang' => 'everyone' ), NULL, NULL, NULL, 'menu_manager_access' ) );
|
||||
$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;
|
||||
|
||||
@@ -472,7 +472,7 @@ class _oauth extends \IPS\Node\Controller
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'oauth_tokens' );
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
|
||||
\IPS\Db::i()->delete( 'core_oauth_server_access_tokens', array( 'client_id=? AND access_token=?', \IPS\Request::i()->client_id, \IPS\Request::i()->token ) );
|
||||
\IPS\Db::i()->update( 'core_oauth_server_access_tokens', array( 'status' => 'revoked' ), array( 'client_id=? AND access_token=?', \IPS\Request::i()->client_id, \IPS\Request::i()->token ) );
|
||||
\IPS\Session::i()->log( 'acplogs__oauth_revoke_token', array( 'core_oauth_client_' . \IPS\Request::i()->client_id => TRUE ) );
|
||||
|
||||
if ( \IPS\Request::i()->r === 'c' )
|
||||
@@ -481,7 +481,7 @@ class _oauth extends \IPS\Node\Controller
|
||||
}
|
||||
elseif ( \IPS\Request::i()->r === 'p' )
|
||||
{
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=view" )->setQueryString( 'id', \IPS\Request::i()->client_id ) );
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=view" )->setQueryString( 'id', \IPS\Request::i()->member_id ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -501,13 +501,13 @@ class _oauth extends \IPS\Node\Controller
|
||||
|
||||
if ( \IPS\Request::i()->member_id )
|
||||
{
|
||||
\IPS\Db::i()->delete( 'core_oauth_server_access_tokens', array( 'member_id=?', \IPS\Request::i()->member_id ) );
|
||||
\IPS\Db::i()->update( 'core_oauth_server_access_tokens', array( 'status' => 'revoked' ), array( 'member_id=?', \IPS\Request::i()->member_id ) );
|
||||
\IPS\Session::i()->log( 'acplogs__oauth_revoke_member', array( \IPS\Member::load( \IPS\Request::i()->member_id )->name => FALSE ) );
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=applications&controller=oauth&do=tokens" )->setQueryString( 'member_id', \IPS\Request::i()->member_id ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Db::i()->delete( 'core_oauth_server_access_tokens', array( 'client_id=?', \IPS\Request::i()->client_id ) );
|
||||
\IPS\Db::i()->update( 'core_oauth_server_access_tokens', array( 'status' => 'revoked' ), array( 'client_id=?', \IPS\Request::i()->client_id ) );
|
||||
\IPS\Session::i()->log( 'acplogs__oauth_revoke_client', array( 'core_oauth_client_' . \IPS\Request::i()->client_id => TRUE ) );
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=applications&controller=oauth&do=tokens" )->setQueryString( 'client_id', \IPS\Request::i()->client_id ) );
|
||||
}
|
||||
@@ -521,7 +521,7 @@ class _oauth extends \IPS\Node\Controller
|
||||
* @param string $lastUsedTab The tab last used in the form
|
||||
* @return void
|
||||
*/
|
||||
protected function _afterSave( \IPS\Node\Model $old = NULL, \IPS\Node\Model $new, $lastUsedTab = FALSE )
|
||||
protected function _afterSave( ?\IPS\Node\Model $old, \IPS\Node\Model $new, $lastUsedTab = FALSE )
|
||||
{
|
||||
if ( $new->_clientSecret )
|
||||
{
|
||||
|
||||
@@ -72,7 +72,7 @@ class _plugins extends \IPS\Node\Controller
|
||||
);
|
||||
}
|
||||
|
||||
if ( !\IPS\Request::i()->isAjax() )
|
||||
if ( !\IPS\Request::i()->isAjax() AND \IPS\IPS::canManageResources() )
|
||||
{
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('forms')->blurb( 'plugins_blurb' );
|
||||
}
|
||||
@@ -211,8 +211,7 @@ class _plugins extends \IPS\Node\Controller
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
/* Already installed? */
|
||||
$xml = new \IPS\Xml\XMLReader;
|
||||
$xml->open( $values['plugin_upload'] );
|
||||
$xml = \IPS\Xml\XMLReader::safeOpen( $values['plugin_upload'] );
|
||||
if ( !@$xml->read() )
|
||||
{
|
||||
\IPS\Output::i()->error( 'xml_upload_invalid', '2C145/D', 403, '' );
|
||||
@@ -271,8 +270,7 @@ class _plugins extends \IPS\Node\Controller
|
||||
function( $data )
|
||||
{
|
||||
/* Open XML file */
|
||||
$xml = new \IPS\Xml\XMLReader;
|
||||
$xml->open( \IPS\Request::i()->file );
|
||||
$xml = \IPS\Xml\XMLReader::safeOpen( \IPS\Request::i()->file );
|
||||
$new = FALSE;
|
||||
|
||||
$xml->read();
|
||||
@@ -302,6 +300,12 @@ class _plugins extends \IPS\Node\Controller
|
||||
\IPS\Theme::removeTemplates( 'core', 'global', 'plugins', $plugin->id );
|
||||
\IPS\Theme::removeCss( 'core', 'front', 'custom', $plugin->id );
|
||||
\IPS\Theme::removeResources( 'core', 'global', 'plugins', $plugin->id );
|
||||
|
||||
foreach( \IPS\Db::i()->select( '*', 'core_javascript', array( 'javascript_plugin=?', $plugin->id ) ) as $javascript )
|
||||
{
|
||||
$jsObject = \IPS\Output\Javascript::constructFromData( $javascript );
|
||||
$jsObject->delete();
|
||||
}
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
{
|
||||
@@ -367,7 +371,8 @@ class _plugins extends \IPS\Node\Controller
|
||||
$i = 0;
|
||||
do
|
||||
{
|
||||
if ( ( isset( \IPS\Data\Store::i()->syncCompleted ) AND \IPS\Data\Store::i()->syncCompleted ) OR $i >= 30 ) # 30 x 0.25 seconds
|
||||
if ( ( ( isset( \IPS\Data\Store::i()->syncCompleted ) AND \IPS\Data\Store::i()->syncCompleted ) OR $i >= 30 )
|
||||
AND \file_exists( \IPS\SITE_FILES_PATH . "/plugins/{$plugin->location}/index.html" ) ) # 30 x 0.25 seconds
|
||||
{
|
||||
/* We need to wait for the backend to process the file */
|
||||
sleep(3);
|
||||
@@ -555,7 +560,7 @@ class _plugins extends \IPS\Node\Controller
|
||||
}
|
||||
catch ( \RuntimeException $e )
|
||||
{
|
||||
throw new \RuntimeException( \IPS\Member::loggedIn()->language()->addToStack('dev_could_not_write_data') );
|
||||
throw new \RuntimeException( \IPS\Member::loggedIn()->language()->addToStack('dev_plugin_not_writable') );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -647,7 +652,7 @@ class _plugins extends \IPS\Node\Controller
|
||||
}
|
||||
catch ( \RuntimeException $e )
|
||||
{
|
||||
throw new \RuntimeException( \IPS\Member::loggedIn()->language()->addToStack('dev_could_not_write_data') );
|
||||
throw new \RuntimeException( \IPS\Member::loggedIn()->language()->addToStack('dev_plugin_not_writable') );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -700,12 +705,6 @@ class _plugins extends \IPS\Node\Controller
|
||||
|
||||
case 'js':
|
||||
$name = $xml->getAttribute('filename');
|
||||
try
|
||||
{
|
||||
$js = \IPS\Output\Javascript::find( 'core', 'plugins', '/', $name );
|
||||
$js->delete();
|
||||
}
|
||||
catch ( \OutOfRangeException $e ) {}
|
||||
|
||||
$js = new \IPS\Output\Javascript;
|
||||
$js->plugin = $plugin->id;
|
||||
@@ -883,7 +882,7 @@ class _plugins extends \IPS\Node\Controller
|
||||
}
|
||||
catch ( \RuntimeException $e )
|
||||
{
|
||||
throw new \RuntimeException( \IPS\Member::loggedIn()->language()->addToStack( 'dev_could_not_write_data' ) );
|
||||
throw new \RuntimeException( \IPS\Member::loggedIn()->language()->addToStack( 'dev_plugin_not_writable' ) );
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -985,7 +984,7 @@ class _plugins extends \IPS\Node\Controller
|
||||
|
||||
$widgets[ $key ] = array(
|
||||
'class' => $xml->getAttribute('class'),
|
||||
'restrict' => json_encode( explode( ",", $xml->getAttribute('restrict') ) ),
|
||||
'restrict' => explode( ",", $xml->getAttribute('restrict') ),
|
||||
'default_area' => $xml->getAttribute('default_area'),
|
||||
'allow_reuse' => \intval( $xml->getAttribute('allow_reuse') ),
|
||||
'menu_style' => $xml->getAttribute('menu_style'),
|
||||
@@ -1005,7 +1004,7 @@ class _plugins extends \IPS\Node\Controller
|
||||
}
|
||||
catch ( \RuntimeException $e )
|
||||
{
|
||||
throw new \RuntimeException( \IPS\Member::loggedIn()->language()->addToStack( 'dev_could_not_write_data' ) );
|
||||
throw new \RuntimeException( \IPS\Member::loggedIn()->language()->addToStack( 'dev_plugin_not_writable' ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1310,13 +1309,16 @@ class _plugins extends \IPS\Node\Controller
|
||||
if ( $activeTabContents )
|
||||
{
|
||||
/* Add Download Button */
|
||||
\IPS\Output::i()->sidebar['actions'] = array(
|
||||
'download' => array(
|
||||
'icon' => 'download',
|
||||
'title' => 'download',
|
||||
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=plugins&do=download&id={$plugin->id}" )->csrf(),
|
||||
)
|
||||
);
|
||||
if( !$plugin->marketplace_id )
|
||||
{
|
||||
\IPS\Output::i()->sidebar['actions'] = array(
|
||||
'download' => array(
|
||||
'icon' => 'download',
|
||||
'title' => 'download',
|
||||
'link' => \IPS\Http\Url::internal("app=core&module=applications&controller=plugins&do=download&id={$plugin->id}")->csrf(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
\IPS\Output::i()->title = $plugin->name;
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->tabs( $tabs, $activeTab, $activeTabContents, \IPS\Http\Url::internal( "app=core&module=applications&controller=plugins&do=developer&id={$plugin->id}" ) );
|
||||
@@ -1691,7 +1693,13 @@ class _plugins extends \IPS\Node\Controller
|
||||
$defaults['long'] = $long + 1;
|
||||
break;
|
||||
}
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'versions_human', $defaults['human'], TRUE ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'versions_human', $defaults['human'], TRUE, [], function( $val )
|
||||
{
|
||||
if ( !preg_match( '/^([0-9]+\.[0-9]+\.[0-9]+)/', $val ) )
|
||||
{
|
||||
throw new \DomainException( 'versions_human_bad' );
|
||||
}
|
||||
} ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'versions_long', $defaults['long'], TRUE, array(), function( $val ) use ( $json )
|
||||
{
|
||||
if ( !preg_match( '/^\d*$/', $val ) )
|
||||
@@ -1790,6 +1798,11 @@ class _plugins extends \IPS\Node\Controller
|
||||
*/
|
||||
public function download()
|
||||
{
|
||||
if( !\IPS\IN_DEV )
|
||||
{
|
||||
\IPS\Output::i()->error( 'not_in_dev', '2C145/R', 403, '' );
|
||||
}
|
||||
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
|
||||
/* Load */
|
||||
@@ -1802,6 +1815,12 @@ class _plugins extends \IPS\Node\Controller
|
||||
\IPS\Output::i()->error( 'node_error', '2C145/B', 404, '' );
|
||||
}
|
||||
|
||||
/* Don't allow downloads of Marketplace resources */
|
||||
if( $plugin->marketplace_id )
|
||||
{
|
||||
\IPS\Output::i()->error( 'plugin_cannot_build_marketplace', '2C145/Q', 403, '' );
|
||||
}
|
||||
|
||||
/* Init */
|
||||
$xml = \IPS\Xml\SimpleXML::create('plugin');
|
||||
$xml->addAttribute( 'name', $plugin->name );
|
||||
@@ -2129,7 +2148,7 @@ class _plugins extends \IPS\Node\Controller
|
||||
}
|
||||
|
||||
/* Build */
|
||||
\IPS\Output::i()->sendOutput( $xml->asXML(), 200, 'application/xml', array( "Content-Disposition" => \IPS\Output::getContentDisposition( 'attachment', $plugin->name . '.xml' ) ), FALSE, FALSE, FALSE );
|
||||
\IPS\Output::i()->sendOutput( $xml->asXML(), 200, 'application/xml', array( "Content-Disposition" => \IPS\Output::getContentDisposition( 'attachment', "{$plugin->name} {$plugin->version_human}.xml" ) ), FALSE, FALSE, FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -265,6 +265,11 @@ class _rss extends \IPS\Node\Controller
|
||||
}
|
||||
} ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'rss_import_showlink', \IPS\Member::loggedIn()->language()->addToStack('rss_import_showlink_default') ) );
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'rss_import_enclosures', 'import', FALSE, array( 'options' => array(
|
||||
'import' => "rss_import_enclosures_import",
|
||||
'hotlink' => "rss_import_enclosures_hotlink",
|
||||
'ignore' => "rss_import_enclosures_ignore",
|
||||
) ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'rss_import_topic_pre', NULL, FALSE, array( 'trim' => FALSE ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'rss_import_auto_follow', NULL, FALSE, array(), NULL, NULL, NULL, 'rss_import_auto_follow' ) );
|
||||
$extension->form( $form, $import );
|
||||
@@ -281,6 +286,13 @@ class _rss extends \IPS\Node\Controller
|
||||
$import->auth_user = ( $data['rss_import_auth_user'] or $data['rss_import_auth_pass'] ) ? $data['rss_import_auth_user'] : NULL;
|
||||
$import->auth_pass = ( $data['rss_import_auth_user'] or $data['rss_import_auth_pass'] ) ? $data['rss_import_auth_pass'] : NULL;
|
||||
$import->auto_follow = $values['rss_import_auto_follow'];
|
||||
$import->enclosures = $values['rss_import_enclosures'];
|
||||
|
||||
/* If we are ignoring enclosures, reset the has_enclosures flag */
|
||||
if( $import->enclosures == 'ignore' )
|
||||
{
|
||||
$import->has_enclosures = false;
|
||||
}
|
||||
|
||||
if ( $settings = $extension->saveForm( $values, $import ) )
|
||||
{
|
||||
|
||||
@@ -304,6 +304,8 @@ class _clubs extends \IPS\Dispatcher\Controller
|
||||
/* Approve */
|
||||
$club->approved = TRUE;
|
||||
$club->save();
|
||||
|
||||
$club->owner->achievementAction( 'core', 'NewClub', $club );
|
||||
|
||||
/* Redirect */
|
||||
$target = \IPS\Http\Url::internal("app=core&module=clubs&controller=clubs");
|
||||
|
||||
@@ -232,7 +232,15 @@ class _ajax extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
if ( $result['template_user_edited'] )
|
||||
{
|
||||
$outOfDate = $result['template_user_edited'] < \IPS\Application::load( $result['template_app'] )->long_version;
|
||||
try
|
||||
{
|
||||
$outOfDate = $result['template_user_edited'] < \IPS\Application::load( $result['template_app'] )->long_version;
|
||||
}
|
||||
catch( \UnexpectedValueException | \OutOfRangeException $e )
|
||||
{
|
||||
unset( $return[ $result['template_app'] ][ $result['template_location'] ][ $result['template_group'] ][ $result['template_name'] ] );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ( $outOfDate and !\in_array( 'outofdate', explode( ',', \IPS\Request::i()->filters ) ) ) or ( !$outOfDate and !\in_array( 'modified', explode( ',', \IPS\Request::i()->filters ) ) ) )
|
||||
{
|
||||
@@ -313,8 +321,16 @@ class _ajax extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
if ( $result['css_user_edited'] )
|
||||
{
|
||||
$outOfDate = $result['css_user_edited'] < \IPS\Application::load( $result['css_app'] )->long_version;
|
||||
|
||||
try
|
||||
{
|
||||
$outOfDate = $result['css_user_edited'] < \IPS\Application::load( $result['css_app'] )->long_version;
|
||||
}
|
||||
catch( \UnexpectedValueException | \OutOfRangeException $e )
|
||||
{
|
||||
unset( $return[ $result['css_app'] ][ $result['css_location'] ][ $result['css_path'] ][ $result['css_name'] ] );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !$result['css_added_to'] and ( ( $outOfDate and !\in_array( 'outofdate', explode( ',', \IPS\Request::i()->filters ) ) ) or ( !$outOfDate and !\in_array( 'modified', explode( ',', \IPS\Request::i()->filters ) ) ) ) )
|
||||
{
|
||||
unset( $return[ $result['css_app'] ][ $result['css_location'] ][ $result['css_path'] ][ $result['css_name'] ] );
|
||||
|
||||
@@ -370,7 +370,7 @@ class _emails extends \IPS\Dispatcher\Controller
|
||||
*/
|
||||
public function emailPreview()
|
||||
{
|
||||
$email = \IPS\Email::buildFromContent( '', \IPS\Member::loggedIn()->language()->addToStack( 'email_preview_content' ) );
|
||||
$email = \IPS\Email::buildFromContent( '', \IPS\Member::loggedIn()->language()->addToStack( 'email_preview_content' ), NULL, \IPS\Email::TYPE_TRANSACTIONAL );
|
||||
\IPS\Output::i()->sendOutput( $email->compileContent( 'html' ) );
|
||||
}
|
||||
}
|
||||
@@ -58,6 +58,10 @@ 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' ) ) );
|
||||
|
||||
/* 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' ) ) );
|
||||
|
||||
/* Safari pinned tabs icon and highlight color */
|
||||
$form->add( new \IPS\Helpers\Form\Upload( 'icons_mask_icon', \IPS\Settings::i()->icons_mask_icon ? \IPS\File::get( 'core_Icons', \IPS\Settings::i()->icons_mask_icon ) : NULL, FALSE, array( 'allowedFileTypes' => array( 'svg' ), 'storageExtension' => 'core_Icons', 'obscure' => FALSE ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Color( 'icons_mask_color', \IPS\Settings::i()->icons_mask_color, FALSE ) );
|
||||
@@ -129,9 +133,34 @@ class _icons extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
}
|
||||
|
||||
/* Homescreen icons are the hardest part, as we need to generate different sizes.. */
|
||||
/* Homescreen icon is the hardest part, as we need to generate different sizes.. */
|
||||
$values = static::processApplicationIcon( $values, $homeScreen );
|
||||
|
||||
/* The start screen requires more different sizes so we'll rebuild that after submit */
|
||||
$rebuildStartScreen = FALSE;
|
||||
|
||||
if( $values['icons_apple_startup'] AND ( !isset( $apple['original'] ) OR !$apple['original'] OR (string) $values['icons_apple_startup'] != $apple['original'] ) )
|
||||
{
|
||||
$rebuildStartScreen = TRUE;
|
||||
}
|
||||
|
||||
if( ( !isset( $manifestDetails['background_color'] ) AND $values['manifest_bgcolor'] ) OR ( isset( $manifestDetails['background_color'] ) AND !$values['manifest_bgcolor'] ) OR ( isset( $manifestDetails['background_color'] ) AND $values['manifest_bgcolor'] != $manifestDetails['background_color'] ) )
|
||||
{
|
||||
if( $values['icons_apple_startup'] )
|
||||
{
|
||||
$rebuildStartScreen = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if( $rebuildStartScreen )
|
||||
{
|
||||
$values = static::processAppleStartupScreen( $values, $apple );
|
||||
}
|
||||
else
|
||||
{
|
||||
$values['icons_apple_startup'] = json_encode( $apple );
|
||||
}
|
||||
|
||||
/* We need the string value of this uploaded file as well */
|
||||
$values['icons_mask_icon'] = (string) $values['icons_mask_icon'];
|
||||
|
||||
@@ -166,7 +195,14 @@ class _icons extends \IPS\Dispatcher\Controller
|
||||
\IPS\Session::i()->log( 'acplogs__icons_and_logos' );
|
||||
|
||||
/* And Redirect */
|
||||
\IPS\Output::i()->redirect( $this->url , 'saved' );
|
||||
if( $rebuildStartScreen === TRUE )
|
||||
{
|
||||
\IPS\Output::i()->redirect( $this->url->setQueryString( 'do', 'buildStartupScreenImages' ), 'saved' );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->redirect( $this->url, 'saved' );
|
||||
}
|
||||
}
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_customization_icons');
|
||||
@@ -253,4 +289,182 @@ class _icons extends \IPS\Dispatcher\Controller
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Minimum padding on either side of startup image (in px)
|
||||
*/
|
||||
const MINIMUM_STARTUP_IMAGE_PADDING = 50;
|
||||
|
||||
/**
|
||||
* Process Apple startup screen images
|
||||
*
|
||||
* @param array $values Values from form submission
|
||||
* @param array $apple Existing values, if any
|
||||
* @return array
|
||||
*/
|
||||
public static function processAppleStartupScreen( $values, $apple = array() )
|
||||
{
|
||||
if( $values['icons_apple_startup'] )
|
||||
{
|
||||
$values['icons_apple_startup'] = json_encode( array( 'original' => (string) $values['icons_apple_startup'] ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Delete any images that may already exist */
|
||||
foreach( $apple as $key => $image )
|
||||
{
|
||||
try
|
||||
{
|
||||
\IPS\File::get( 'core_Icons', ( $key == 'original' ) ? $image : $image['url'] )->delete();
|
||||
}
|
||||
catch( \Exception $e ){}
|
||||
}
|
||||
|
||||
$values['icons_apple_startup'] = '';
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process Apple startup screen images
|
||||
*
|
||||
* @param array $values Values from form submission
|
||||
* @param array $apple Existing values, if any
|
||||
* @return array
|
||||
*/
|
||||
public function buildStartupScreenImages()
|
||||
{
|
||||
$self = $this;
|
||||
|
||||
$multiRedirect = new \IPS\Helpers\MultipleRedirect(
|
||||
$this->url->setQueryString('do', 'buildStartupScreenImages'),
|
||||
function( $data )
|
||||
{
|
||||
/* Get the necessary data */
|
||||
$manifestDetails = json_decode( \IPS\Settings::i()->manifest_details, TRUE );
|
||||
$setting = json_decode( \IPS\Settings::i()->icons_apple_startup, TRUE ) ?? array();
|
||||
|
||||
if( isset( $setting['original'] ) )
|
||||
{
|
||||
$sizes = array(
|
||||
'apple-startup-1136x640' => array( 320, 568, 2, 'landscape' ),
|
||||
'apple-startup-2436x1125' => array( 375, 812, 3, 'landscape' ),
|
||||
'apple-startup-1792x828' => array( 414, 896, 2, 'landscape' ),
|
||||
'apple-startup-828x1792' => array( 414, 896, 2, 'portrait' ),
|
||||
'apple-startup-1334x750' => array( 375, 667, 2, 'landscape' ),
|
||||
'apple-startup-1242x2688' => array( 414, 896, 3, 'portrait' ),
|
||||
'apple-startup-2208x1242' => array( 414, 736, 3, 'landscape' ),
|
||||
'apple-startup-1125x2436' => array( 375, 812, 3, 'portrait' ),
|
||||
'apple-startup-1242x2208' => array( 414, 736, 3, 'portrait' ),
|
||||
'apple-startup-2732x2048' => array( 1024, 1366, 2, 'landscape' ),
|
||||
'apple-startup-2688x1242' => array( 414, 896, 3, 'landscape' ),
|
||||
'apple-startup-2224x1668' => array( 834, 1112, 2, 'landscape' ),
|
||||
'apple-startup-750x1334' => array( 375, 667, 2, 'portrait' ),
|
||||
'apple-startup-2048x2732' => array( 1024, 1366, 2, 'portrait' ),
|
||||
'apple-startup-2388x1668' => array( 834, 1194, 2, 'landscape' ),
|
||||
'apple-startup-1668x2224' => array( 834, 1112, 2, 'portrait' ),
|
||||
'apple-startup-640x1136' => array( 320, 568, 2, 'portrait' ),
|
||||
'apple-startup-1668x2388' => array( 834, 1194, 2, 'portrait' ),
|
||||
'apple-startup-2048x1536' => array( 768, 1024, 2, 'landscape' ),
|
||||
'apple-startup-1536x2048' => array( 768, 1024, 2, 'portrait' ),
|
||||
'apple-startup-2360x1640' => array( 1180, 820, 2, 'landscape' ),
|
||||
'apple-startup-1640x2360' => array( 1180, 820, 2, 'portrait' ),
|
||||
'apple-startup-2160x1620' => array( 1080, 810, 2, 'landscape' ),
|
||||
'apple-startup-1620x2160' => array( 1080, 810, 2, 'portrait' ),
|
||||
'apple-startup-2778x1284' => array( 428, 926, 3, 'landscape' ),
|
||||
'apple-startup-1284x2778' => array( 428, 926, 3, 'portrait' ),
|
||||
'apple-startup-2532x1170' => array( 390, 844, 3, 'landscape' ),
|
||||
'apple-startup-1170x2532' => array( 390, 844, 3, 'portrait' ),
|
||||
'apple-startup-2340x1080' => array( 360, 780, 3, 'landscape' ),
|
||||
'apple-startup-1080x2340' => array( 360, 780, 3, 'portrait' ),
|
||||
);
|
||||
|
||||
$file = \IPS\File::get( 'core_Icons', $setting['original'] );
|
||||
$originalContents = $file->contents();
|
||||
|
||||
$backgroundColor = ( isset( $manifestDetails['background_color'] ) ) ? str_replace( '#', '', $manifestDetails['background_color'] ) : 'FFFFFF';
|
||||
|
||||
$rgb = array();
|
||||
|
||||
if ( \strlen( $backgroundColor ) == 3 )
|
||||
{
|
||||
$rgb[] = hexdec( \substr( $backgroundColor, 0, 1 ) . \substr( $backgroundColor, 0, 1 ) ); // R
|
||||
$rgb[] = hexdec( \substr( $backgroundColor, 1, 1 ) . \substr( $backgroundColor, 1, 1 ) ); // G
|
||||
$rgb[] = hexdec( \substr( $backgroundColor, 2, 1 ) . \substr( $backgroundColor, 2, 1 ) ); // B
|
||||
}
|
||||
else
|
||||
{
|
||||
$rgb[] = hexdec( \substr( $backgroundColor, 0, 2 ) ); // R
|
||||
$rgb[] = hexdec( \substr( $backgroundColor, 2, 2 ) ); // G
|
||||
$rgb[] = hexdec( \substr( $backgroundColor, 4, 2 ) ); // B
|
||||
}
|
||||
|
||||
$did = 0;
|
||||
|
||||
foreach( $sizes as $filename => $_sizes )
|
||||
{
|
||||
$did++;
|
||||
|
||||
if( isset( $setting[ $filename ] ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$width = ( $_sizes[3] == 'landscape' ) ? $_sizes[1] * $_sizes[2] : $_sizes[0] * $_sizes[2];
|
||||
$height = ( $_sizes[3] == 'portrait' ) ? $_sizes[1] * $_sizes[2] : $_sizes[0] * $_sizes[2];
|
||||
|
||||
$logoImage = \IPS\Image::create( $originalContents );
|
||||
$canvas = \IPS\Image::newImageCanvas( $width, $height, $rgb );
|
||||
|
||||
$logoImage->resizeToMax( $width - static::MINIMUM_STARTUP_IMAGE_PADDING, $height - static::MINIMUM_STARTUP_IMAGE_PADDING );
|
||||
|
||||
$xPos = ( $width - $logoImage->width ) / 2;
|
||||
$yPos = ( $height - $logoImage->height ) / 2;
|
||||
|
||||
$canvas->impose( $logoImage, $xPos, $yPos );
|
||||
|
||||
if( $canvas::exifSupported() )
|
||||
{
|
||||
$canvas->setExifData( $originalContents );
|
||||
}
|
||||
|
||||
$setting[ $filename ] = array(
|
||||
'url' => (string) \IPS\File::create( 'core_Icons', $filename . '.png', (string) $canvas, NULL, TRUE, NULL, FALSE ),
|
||||
'width' => $canvas->width,
|
||||
'height' => $canvas->height,
|
||||
'density' => $_sizes[2],
|
||||
'orientation' => $_sizes[3],
|
||||
);
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
\IPS\Log::log( $e, 'apple-startup-image' );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Are we done? */
|
||||
if( $did == \count( $sizes ) )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
\IPS\Settings::i()->changeValues( array( 'icons_apple_startup' => json_encode( $setting ) ) );
|
||||
}
|
||||
|
||||
return array( $data, \IPS\Member::loggedIn()->language()->addToStack('build_start_images_title'), ( $did ) ? round( ( 100 / \count( $sizes ) * $did ), 2 ) : 0 );
|
||||
},
|
||||
function() use( $self )
|
||||
{
|
||||
\IPS\Output::i()->redirect( $self->url, 'completed' );
|
||||
}
|
||||
);
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('build_start_images_title');
|
||||
\IPS\Output::i()->output = $multiRedirect;
|
||||
}
|
||||
}
|
||||
@@ -546,7 +546,7 @@ class _themes extends \IPS\Node\Controller
|
||||
}
|
||||
|
||||
/* Start importing */
|
||||
$data = array( 'buildingDesignersFiles' => true, 'toBuild' => $toBuild, 'extensions' => $extensions, 'totalItems' => \count( array_keys( $toBuild ) ) + \count( array_keys( $extensions ) ), 'totalDone' => 0, 'extra' => array() );
|
||||
$data = array( 'buildingDesignersFiles' => true, 'toBuild' => $toBuild, 'extensions' => $extensions, 'totalItems' => \count( array_keys( $toBuild ) ) * 3 + \count( array_keys( $extensions ) ), 'totalDone' => 0, 'extra' => array() );
|
||||
|
||||
return array( $data, \IPS\Member::loggedIn()->language()->addToStack('theme_dm_building') );
|
||||
}
|
||||
@@ -664,7 +664,7 @@ class _themes extends \IPS\Node\Controller
|
||||
$table->parsers = array(
|
||||
'resource_data' => function( $val, $row ) use ($set)
|
||||
{
|
||||
if ( \in_array( mb_substr( $row['resource_name'], mb_strrpos( $row['resource_name'], '.' ) + 1 ), array_merge( \IPS\Image::$imageExtensions, array( 'svg' ) ) ) )
|
||||
if ( \in_array( mb_substr( $row['resource_name'], mb_strrpos( $row['resource_name'], '.' ) + 1 ), array_merge( \IPS\Image::supportedExtensions(), array( 'svg' ) ) ) )
|
||||
{
|
||||
$row['resource_filename'] = $set->resource( ltrim( $row['resource_path'], '/' ) . $row['resource_name'], $row['resource_app'], $row['resource_location'] );
|
||||
}
|
||||
@@ -674,7 +674,7 @@ class _themes extends \IPS\Node\Controller
|
||||
'resource_name' => function( $val, $row ) use ($set)
|
||||
{
|
||||
|
||||
if ( \in_array( mb_substr( $row['resource_name'], mb_strrpos( $row['resource_name'], '.' ) + 1 ), array_merge( \IPS\Image::$imageExtensions, array( 'svg' ) ) ) )
|
||||
if ( \in_array( mb_substr( $row['resource_name'], mb_strrpos( $row['resource_name'], '.' ) + 1 ), array_merge( \IPS\Image::supportedExtensions(), array( 'svg' ) ) ) )
|
||||
{
|
||||
$row['resource_filename'] = $set->resource( ltrim( $row['resource_path'], '/' ) . $row['resource_name'], $row['resource_app'], $row['resource_location'] );
|
||||
}
|
||||
@@ -864,7 +864,7 @@ class _themes extends \IPS\Node\Controller
|
||||
\IPS\Member::loggedIn()->language()->words['core_theme_resource_name_choice_o_filename_desc'] = sprintf( \IPS\Member::loggedIn()->language()->get('core_theme_resource_name_choice_o_filename_desc'), $current['resource_name'] );
|
||||
}
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Upload( 'core_theme_resource_filename', ( isset( $current['resource_filename'] ) ? \IPS\File::get( 'core_Theme', $current['resource_filename'] ) : NULL ), TRUE, array( 'obscure' => FALSE, 'maxFileSize' => 1.2, 'storageExtension' => 'core_Theme', 'storageContainer' => 'set_resources_' . $set->_id ), NULL, NULL, NULL, 'core_theme_resource_filename' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Upload( 'core_theme_resource_filename', ( isset( $current['resource_filename'] ) ? \IPS\File::get( 'core_Theme', $current['resource_filename'] ) : NULL ), TRUE, array( 'obscure' => ( $id ) ? TRUE : FALSE, 'maxFileSize' => 1.2, 'storageExtension' => 'core_Theme', 'storageContainer' => 'set_resources_' . $set->_id ), NULL, NULL, NULL, 'core_theme_resource_filename' ) );
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
@@ -919,12 +919,14 @@ class _themes extends \IPS\Node\Controller
|
||||
/* Resource may be used in CSS */
|
||||
\IPS\File::getClass('core_Theme')->deleteContainer( 'css_built_' . $set->id );
|
||||
$set->css_map = array();
|
||||
$set->css_updated = time();
|
||||
$set->save();
|
||||
|
||||
foreach( $set->children() as $child )
|
||||
{
|
||||
\IPS\File::getClass('core_Theme')->deleteContainer( 'css_built_' . $child->id );
|
||||
$child->css_map = array();
|
||||
$child->css_updated = time();
|
||||
$child->save();
|
||||
}
|
||||
|
||||
@@ -1101,8 +1103,7 @@ class _themes extends \IPS\Node\Controller
|
||||
$masterCss = \IPS\Theme::load( \IPS\Request::i()->id )->getRawCss( null, null, null, \IPS\Theme::RETURN_BIT_NAMES, false );
|
||||
|
||||
/* Open XML file */
|
||||
$xml = new \IPS\Xml\XMLReader;
|
||||
$xml->open( \IPS\Request::i()->file );
|
||||
$xml = \IPS\Xml\XMLReader::safeOpen( \IPS\Request::i()->file );
|
||||
|
||||
if ( ! @$xml->read() )
|
||||
{
|
||||
@@ -1489,11 +1490,7 @@ class _themes extends \IPS\Node\Controller
|
||||
|
||||
$url = (string) \IPS\File::create( 'core_Theme', $name, base64_decode( $xml->readString() ) );
|
||||
|
||||
$image = \IPS\Image::create( base64_decode( $xml->readString() ) );
|
||||
$width = $image->width;
|
||||
$height = $image->height;
|
||||
|
||||
$set->saveSet( array( 'logo' => array( $type => array( 'url' => $url, 'width' => $width, 'height' => $height ) ) ) );
|
||||
$set->saveSet( array( 'logo' => array( $type => array( 'url' => $url, 'height' => $xml->getAttribute('height') ?? 100 ) ) ) );
|
||||
}
|
||||
|
||||
$xml->next();
|
||||
@@ -1528,6 +1525,9 @@ class _themes extends \IPS\Node\Controller
|
||||
\IPS\Theme::setThemeCustomized( $set->id );
|
||||
|
||||
$set->resetCacheKey();
|
||||
|
||||
/* Update children */
|
||||
$set->updateChildrenThemeSettings();
|
||||
|
||||
/* Conflicts to fix? */
|
||||
if ( \IPS\Db::i()->select( 'count(*)', 'core_theme_conflict', array( 'conflict_set_id=?', \IPS\Request::i()->id ) )->first() )
|
||||
@@ -2014,6 +2014,10 @@ class _themes extends \IPS\Node\Controller
|
||||
$xml->text( $file );
|
||||
$xml->endAttribute();
|
||||
|
||||
$xml->startAttribute('height');
|
||||
$xml->text( $data['height'] );
|
||||
$xml->endAttribute();
|
||||
|
||||
/* Get image data */
|
||||
$original = \IPS\File::get( 'core_Theme', $data['url'] );
|
||||
|
||||
@@ -2311,10 +2315,8 @@ class _themes extends \IPS\Node\Controller
|
||||
|
||||
/* Finish */
|
||||
$xml->endDocument();
|
||||
|
||||
$name = addslashes( str_replace( array( ' ', '.', ',' ), '_', \IPS\Member::loggedIn()->language()->get('core_theme_set_title_' . $themeSet->_id ) ) . '.xml' );
|
||||
|
||||
\IPS\Output::i()->sendOutput( $xml->outputMemory(), 200, 'application/xml', array( 'Content-Disposition' => \IPS\Output::getContentDisposition( 'attachment', $name ), FALSE, FALSE ) );
|
||||
|
||||
\IPS\Output::i()->sendOutput( $xml->outputMemory(), 200, 'application/xml', array( 'Content-Disposition' => \IPS\Output::getContentDisposition( 'attachment', \IPS\Member::loggedIn()->language()->get('core_theme_set_title_' . $themeSet->_id ) . " {$version}.xml" ), FALSE, FALSE ) );
|
||||
}
|
||||
|
||||
/* Display */
|
||||
@@ -3035,7 +3037,7 @@ class _themes extends \IPS\Node\Controller
|
||||
{
|
||||
$form = new \IPS\Helpers\Form;
|
||||
$form->hiddenvalues['id'] = \IPS\Request::i()->id;
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'member_reset_where', '*', TRUE, array( 'options' => \IPS\Member\Group::groups( TRUE, FALSE ), 'multiple' => TRUE, 'parse' => 'normal', 'unlimited' => '*', 'unlimitedLang' => 'all' ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'member_reset_where', '*', TRUE, array( 'options' => \IPS\Member\Group::groups( TRUE, FALSE ), 'multiple' => TRUE, 'parse' => 'normal', 'unlimited' => '*', 'unlimitedLang' => 'all', 'impliedUnlimited' => TRUE ) ) );
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
|
||||
@@ -286,7 +286,7 @@ class _themesettings extends \IPS\Dispatcher\Controller
|
||||
$form = new \IPS\Helpers\Form();
|
||||
$form->hiddenValues['set_id'] = $setId;
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'theme_custom_setting_title', $current['sc_title'], TRUE ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'theme_custom_setting_title', $id ? $current['sc_title'] : null, TRUE ) );
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'theme_custom_setting_app', $id ? $current['sc_app'] : null, TRUE, array( 'options' => $apps ) ) );
|
||||
|
||||
@@ -469,7 +469,7 @@ class _themesettings extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
|
||||
/* Default is changed, change value? */
|
||||
if ( $save['sc_default'] !== $current['sc_default'] )
|
||||
if ( $current and ( $save['sc_default'] !== $current['sc_default'] ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -501,6 +501,9 @@ class _themesettings extends \IPS\Dispatcher\Controller
|
||||
$set->template_settings = json_encode( $themeSettings );
|
||||
$set->save();
|
||||
}
|
||||
|
||||
/* Update children */
|
||||
$set->updateChildrenThemeSettings();
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=themesettings&set_id=' . $setId ), 'saved' );
|
||||
}
|
||||
|
||||
@@ -253,8 +253,8 @@ class _toolbar extends \IPS\Dispatcher\Controller
|
||||
$form->addTab('permissions');
|
||||
}
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'editor_permission_groups', isset( $current[ $button ] ) ? $current[ $button ]['groups'] : '*', TRUE, array( 'options' => \IPS\Member\Group::groups(), 'parse' => 'normal', 'multiple' => TRUE, 'unlimited' => '*', 'unlimitedLang' => 'everyone' ), NULL, NULL, NULL, $b64Button ) );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'editor_permission_areas', isset( $current[ $button ] ) ? $current[ $button ]['areas'] : '*', TRUE, array( 'options' => $areas, 'multiple' => TRUE, 'unlimited' => '*', 'unlimitedLang' => 'everywhere' ), NULL, NULL, NULL, 'l' . $b64Button ) );
|
||||
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'editor_permission_groups', isset( $current[ $button ] ) ? $current[ $button ]['groups'] : '*', TRUE, array( 'options' => \IPS\Member\Group::groups(), 'parse' => 'normal', 'multiple' => TRUE, 'unlimited' => '*', 'unlimitedLang' => 'everyone', 'impliedUnlimited' => TRUE ), NULL, NULL, NULL, $b64Button ) );
|
||||
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'editor_permission_areas', isset( $current[ $button ] ) ? $current[ $button ]['areas'] : '*', TRUE, array( 'options' => $areas, 'multiple' => TRUE, 'unlimited' => '*', 'unlimitedLang' => 'everywhere', 'impliedUnlimited' => TRUE ), NULL, NULL, NULL, 'l' . $b64Button ) );
|
||||
|
||||
/* If this is a custom one, add a delete button */
|
||||
if ( \in_array( mb_strtolower( $button ), explode( ',', mb_strtolower( \IPS\Settings::i()->ckeditor_extraPlugins ) ) ) or preg_match( '/^custom-\w{32}$/i', $button ) )
|
||||
@@ -421,6 +421,8 @@ class _toolbar extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
}
|
||||
while( TRUE );
|
||||
|
||||
$pluginDirectory = \IPS\SITE_FILES_PATH . '/ckeditor_custom/' . $name;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -435,10 +437,6 @@ class _toolbar extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
|
||||
$pluginDirectory = \IPS\ROOT_PATH . '/applications/core/interface/ckeditor/ckeditor/plugins/' . $name;
|
||||
if ( \IPS\CIC2 )
|
||||
{
|
||||
$pluginDirectory = \IPS\SITE_FILES_PATH . '/ckeditor_custom/' . $name;
|
||||
}
|
||||
|
||||
/* Check it isn't already installed */
|
||||
if ( \in_array( $name, array_merge( static::$defaultPlugins, explode( ',', \IPS\Settings::i()->ckeditor_extraPlugins ) ) ) )
|
||||
@@ -482,41 +480,50 @@ class _toolbar extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
if ( !file_exists( $path ) )
|
||||
{
|
||||
/* If we just extracted some files remove them as the plugin was apparently not valid */
|
||||
if ( isset( $values['editor_plugin_zip'] ) )
|
||||
/* If we're on Cloud2, sleep for a few seconds to make sure the plugin isn't just still being transferred */
|
||||
if( \IPS\CIC2 )
|
||||
{
|
||||
if( \is_dir( $pluginDirectory ) )
|
||||
{
|
||||
if ( \IPS\CIC2 )
|
||||
{
|
||||
\IPS\Cicloud\deleteFolder( 'ckeditor_custom/' . $name );
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
$iterator = new \RecursiveDirectoryIterator( $pluginDirectory, \FilesystemIterator::SKIP_DOTS );
|
||||
foreach ( new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::CHILD_FIRST ) as $file )
|
||||
{
|
||||
if ( $file->isDir() )
|
||||
{
|
||||
@rmdir( $file->getPathname() );
|
||||
}
|
||||
else
|
||||
{
|
||||
@unlink( $file->getPathname() );
|
||||
}
|
||||
}
|
||||
$handle = opendir( $pluginDirectory );
|
||||
closedir ( $pluginDirectory );
|
||||
@rmdir( $pluginDirectory );
|
||||
}
|
||||
catch( \UnexpectedValueException $e ){}
|
||||
}
|
||||
}
|
||||
sleep(5);
|
||||
}
|
||||
|
||||
\IPS\Output::i()->error( 'editor_bad_plugin', '1C120/B', 400, '' );
|
||||
if( !file_exists( $path ) )
|
||||
{
|
||||
/* If we just extracted some files remove them as the plugin was apparently not valid */
|
||||
if ( isset( $values['editor_plugin_zip'] ) )
|
||||
{
|
||||
if( \is_dir( $pluginDirectory ) )
|
||||
{
|
||||
if ( \IPS\CIC2 )
|
||||
{
|
||||
\IPS\Cicloud\folderDelete( 'ckeditor_custom/' . $name );
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
$iterator = new \RecursiveDirectoryIterator( $pluginDirectory, \FilesystemIterator::SKIP_DOTS );
|
||||
foreach ( new \RecursiveIteratorIterator( $iterator, \RecursiveIteratorIterator::CHILD_FIRST ) as $file )
|
||||
{
|
||||
if ( $file->isDir() )
|
||||
{
|
||||
@rmdir( $file->getPathname() );
|
||||
}
|
||||
else
|
||||
{
|
||||
@unlink( $file->getPathname() );
|
||||
}
|
||||
}
|
||||
$handle = opendir( $pluginDirectory );
|
||||
closedir ( $pluginDirectory );
|
||||
@rmdir( $pluginDirectory );
|
||||
}
|
||||
catch( \UnexpectedValueException $e ){}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
\IPS\Output::i()->error( 'editor_bad_plugin', '1C120/B', 400, '' );
|
||||
}
|
||||
}
|
||||
|
||||
$file = \file_get_contents( $path );
|
||||
@@ -545,7 +552,16 @@ class _toolbar extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
return str_replace( 'contextmenu', 'ipscontextmenu', $matches[0] );
|
||||
}, $file );
|
||||
\file_put_contents( \IPS\ROOT_PATH . '/applications/core/interface/ckeditor/ckeditor/plugins/' . $name . '/plugin.js', $file );
|
||||
|
||||
if( \IPS\CIC2 )
|
||||
{
|
||||
sleep(5); // @Mark
|
||||
\IPS\Cicloud\file( 'plugin.js', $file, 'ckeditor_custom/' . $name );
|
||||
}
|
||||
else
|
||||
{
|
||||
\file_put_contents( \IPS\ROOT_PATH . '/applications/core/interface/ckeditor/ckeditor/plugins/' . $name . '/plugin.js', $file );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -952,7 +968,7 @@ JS;
|
||||
{
|
||||
if ( \IPS\CIC2 )
|
||||
{
|
||||
\IPS\Cicloud\deleteFolder( "ckeditor_custom/{$key}" );
|
||||
\IPS\Cicloud\folderDelete( "ckeditor_custom/{$key}" );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -121,7 +121,7 @@ class _languages extends \IPS\Node\Controller
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function addWord(): void
|
||||
protected function addWord()
|
||||
{
|
||||
$form = new \IPS\Helpers\Form( 'wordForm', 'save', NULL, array( 'data-role' => 'wordForm' ) );
|
||||
\IPS\Lang::wordForm( $form );
|
||||
@@ -150,7 +150,7 @@ class _languages extends \IPS\Node\Controller
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function deleteWord(): void
|
||||
protected function deleteWord()
|
||||
{
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
|
||||
@@ -336,7 +336,7 @@ class _languages extends \IPS\Node\Controller
|
||||
if ( !\IPS\Request::i()->status )
|
||||
{
|
||||
$count = \IPS\Db::i()->select( 'count(*)', 'core_members', array( 'language=?', $language->_id ) )->first();
|
||||
if ( $count['count'] )
|
||||
if ( $count )
|
||||
{
|
||||
if ( \IPS\Request::i()->isAjax() )
|
||||
{
|
||||
@@ -851,7 +851,7 @@ class _languages extends \IPS\Node\Controller
|
||||
|
||||
/* Finish */
|
||||
$xml->endDocument();
|
||||
\IPS\Output::i()->sendOutput( $xml->outputMemory(), 200, 'application/xml', array( 'Content-Disposition' => \IPS\Output::getContentDisposition( 'attachment', $lang->title . '.xml' ) ), FALSE, FALSE, FALSE );
|
||||
\IPS\Output::i()->sendOutput( $xml->outputMemory(), 200, 'application/xml', array( 'Content-Disposition' => \IPS\Output::getContentDisposition( 'attachment', $lang->title . " {$lang->version}.xml" ) ), FALSE, FALSE, FALSE );
|
||||
}
|
||||
|
||||
/* Display */
|
||||
@@ -920,8 +920,7 @@ class _languages extends \IPS\Node\Controller
|
||||
function( $data )
|
||||
{
|
||||
/* Open XML file */
|
||||
$xml = new \IPS\Xml\XMLReader;
|
||||
$xml->open( \IPS\Request::i()->file );
|
||||
$xml = \IPS\Xml\XMLReader::safeOpen( \IPS\Request::i()->file );
|
||||
$xml->read();
|
||||
|
||||
/* If this is the first batch, create the language record */
|
||||
@@ -1160,7 +1159,7 @@ class _languages extends \IPS\Node\Controller
|
||||
{
|
||||
$form = new \IPS\Helpers\Form;
|
||||
$form->hiddenvalues['id'] = \IPS\Request::i()->id;
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'member_reset_where', '*', TRUE, array( 'options' => \IPS\Member\Group::groups( TRUE, FALSE ), 'multiple' => TRUE, 'parse' => 'normal', 'unlimited' => '*', 'unlimitedLang' => 'all' ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'member_reset_where', '*', TRUE, array( 'options' => \IPS\Member\Group::groups( TRUE, FALSE ), 'multiple' => TRUE, 'parse' => 'normal', 'unlimited' => '*', 'unlimitedLang' => 'all', 'impliedUnlimited' => TRUE ) ) );
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
|
||||
@@ -32,7 +32,7 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute(): void
|
||||
public function execute()
|
||||
{
|
||||
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'admin_marketplace.js', 'core', 'admin' ) );
|
||||
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'marketplace/marketplace.css', 'core', 'admin' ) );
|
||||
@@ -59,7 +59,7 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
$tree = $this->_buildTree( $response['results'] );
|
||||
|
||||
}
|
||||
catch( \Throwable | \IPS\Http\Exception $e )
|
||||
catch( \Throwable $e )
|
||||
{
|
||||
return [];
|
||||
}
|
||||
@@ -75,7 +75,7 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
final protected function manage(): void
|
||||
final protected function manage()
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -101,17 +101,45 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Install resource
|
||||
* Renew Resource
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
final protected function install(): void
|
||||
final protected function renewFile()
|
||||
{
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
|
||||
/* Check License Validity */
|
||||
$this->_licenseCheck();
|
||||
|
||||
if ( $this->_token() )
|
||||
{
|
||||
$response = $this->_apiWrapper( "marketplace/files/" . (int) \IPS\Request::i()->id . "/renew/" . (int) \IPS\Request::i()->invoiceId, NULL, TRUE, [] );
|
||||
|
||||
if ( isset( $response['errorCode'] ) )
|
||||
{
|
||||
\IPS\Output::i()->error( 'marketplace_cannot_purchase', '2C409/J', 403, '', array(), $response['errorMessage'] );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::external( $response['checkoutUrl'] ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->error( 'marketplace_sign_in_required', '2C409/K', 403, '' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Install resource
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
final protected function install()
|
||||
{
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
|
||||
if ( \IPS\DEMO_MODE )
|
||||
{
|
||||
\IPS\Output::i()->error( 'demo_mode_function_blocked', '2C409/4', 403, '' );
|
||||
@@ -155,6 +183,18 @@ 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'] ) )
|
||||
{
|
||||
\IPS\Output::i()->error( 'marketplace_resource_not_purchased', '2C171/A', 403, '' );
|
||||
}
|
||||
/* If we can still download this paid file, don't check license */
|
||||
elseif( !( $file['isPaid'] AND $file['isPurchased'] AND $file['canDownload'] ) )
|
||||
{
|
||||
/* Check License Validity */
|
||||
$this->_licenseCheck();
|
||||
}
|
||||
|
||||
if( empty( \IPS\Request::i()->confirm ) AND \in_array( $file['disclaimerLocation'], [ 'both', 'download'] ) AND !empty( $file['downloadDisclaimer'] ) )
|
||||
{
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('marketplace_terms_and_conditions');
|
||||
@@ -258,6 +298,12 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
catch( \Throwable $e )
|
||||
{
|
||||
if( $fileExtension == 'tar' )
|
||||
{
|
||||
/* Application install renames the temporary file */
|
||||
$temporaryFile .= '.tar';
|
||||
}
|
||||
|
||||
@unlink( $temporaryFile );
|
||||
\IPS\Log::log( $e, 'mp_install_error' );
|
||||
\IPS\Output::i()->error( 'marketplace_error', '2C409/D', 403, '' );
|
||||
@@ -269,7 +315,7 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
final protected function viewCategory(): void
|
||||
final protected function viewCategory()
|
||||
{
|
||||
$page = \IPS\Request::i()->page ?? 1;
|
||||
try
|
||||
@@ -309,7 +355,7 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
final protected function viewFile(): void
|
||||
final protected function viewFile()
|
||||
{
|
||||
$file = $this->_apiWrapper( "marketplace/files/" . (int) \IPS\Request::i()->id, [ 'termFields' => 1 ], TRUE );
|
||||
|
||||
@@ -321,45 +367,52 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
case 'apps':
|
||||
/* Plugin or App? */
|
||||
try
|
||||
{
|
||||
$app = \IPS\Application::load( $file['id'], 'app_marketplace_id' );
|
||||
$updateAvailable = \count( $app->availableUpgrade() ) ? TRUE : FALSE;
|
||||
}
|
||||
catch( \OutOfRangeException $e )
|
||||
{
|
||||
$plugin = \IPS\Plugin::load( $file['id'], 'plugin_marketplace_id' );
|
||||
if( $plugin->update_check_data )
|
||||
{
|
||||
$data = json_decode( $plugin->update_check_data, TRUE );
|
||||
if( $data['longversion'] > $plugin->version_long )
|
||||
{
|
||||
$updateAvailable = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'themes':
|
||||
$theme = \IPS\Theme::load( $file['id'], 'set_marketplace_id' );
|
||||
if( $theme->update_data )
|
||||
try
|
||||
{
|
||||
$data = json_decode( $theme->update_data, TRUE );
|
||||
if( $data['longversion'] > $theme->long_version )
|
||||
$app = \IPS\Application::load( $file['id'], 'app_marketplace_id' );
|
||||
if( $file['latestLongVersion'] > $app->long_version )
|
||||
{
|
||||
$updateAvailable = TRUE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'languages':
|
||||
$lang = \IPS\Lang::load( $file['id'], 'lang_marketplace_id' );
|
||||
if( $lang->update_data )
|
||||
catch( \OutOfRangeException $e )
|
||||
{
|
||||
$data = json_decode( $lang->update_data, TRUE );
|
||||
if( $data['longversion'] > $lang->version_long )
|
||||
$plugin = \IPS\Plugin::load( $file['id'], 'plugin_marketplace_id' );
|
||||
if( $file['latestLongVersion'] > $plugin->version_long )
|
||||
{
|
||||
$updateAvailable = TRUE;
|
||||
}
|
||||
}
|
||||
/* Legacy apps, or those with missing files */
|
||||
catch( \UnexpectedValueException $e )
|
||||
{
|
||||
if ( mb_stristr( $e->getMessage(), 'Missing:' ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
$appVersion = \IPS\Db::i()->select('app_long_version', 'core_applications', array( 'app_marketplace_id=?', $file['id'] ) )->first();
|
||||
if( $file['latestLongVersion'] > $appVersion )
|
||||
{
|
||||
$updateAvailable = TRUE;
|
||||
}
|
||||
}
|
||||
catch( \UnderflowException $e ) {}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'themes':
|
||||
$theme = \IPS\Theme::load( $file['id'], 'set_marketplace_id' );
|
||||
if( $file['latestLongVersion'] > $theme->long_version )
|
||||
{
|
||||
$updateAvailable = TRUE;
|
||||
}
|
||||
break;
|
||||
case 'languages':
|
||||
$lang = \IPS\Lang::load( $file['id'], 'lang_marketplace_id' );
|
||||
if( $file['latestLongVersion'] > $lang->version_long )
|
||||
{
|
||||
$updateAvailable = TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
$installed = TRUE;
|
||||
@@ -391,7 +444,7 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
final protected function buyFile(): void
|
||||
final protected function buyFile()
|
||||
{
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
|
||||
@@ -409,9 +462,7 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
|
||||
if ( $this->_token() )
|
||||
{
|
||||
$response = $this->_apiWrapper( "downloads/files/" . (int) \IPS\Request::i()->id . "/buy", NULL, TRUE, array(
|
||||
'returnUri' => 'app=core&module=system&controller=ajax&do=dismissWindow'
|
||||
) );
|
||||
$response = $this->_apiWrapper( "marketplace/files/" . (int) \IPS\Request::i()->id . "/buy", NULL, TRUE, [] );
|
||||
|
||||
if ( isset( $response['errorCode'] ) )
|
||||
{
|
||||
@@ -433,7 +484,7 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
final protected function fileChangelog(): void
|
||||
final protected function fileChangelog()
|
||||
{
|
||||
$fileHistory = $this->_apiWrapper( "marketplace/files/" . (int) \IPS\Request::i()->id . '/history' );
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'marketplace' )->fileChangelogGrid( $fileHistory );
|
||||
@@ -444,7 +495,7 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
final public function search(): void
|
||||
final public function search()
|
||||
{
|
||||
\IPS\Output::i()->output =\IPS\Theme::i()->getTemplate('marketplace')->onboardSearch( \IPS\Request::i()->category, (bool) \IPS\Request::i()->compatible );
|
||||
}
|
||||
@@ -454,7 +505,7 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
final protected function signout(): void
|
||||
final protected function signout()
|
||||
{
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
\IPS\Db::i()->delete( 'core_marketplace_tokens', array( 'id=?', \IPS\Member::loggedIn()->member_id ) );
|
||||
@@ -466,7 +517,7 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
final protected function apiSearch(): void
|
||||
final protected function apiSearch()
|
||||
{
|
||||
$queryString = [
|
||||
'q' => \IPS\Request::i()->title,
|
||||
@@ -500,14 +551,14 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
if ( $results['totalResults'] == 1 )
|
||||
{
|
||||
\IPS\Output::i()->json([
|
||||
\IPS\Output::i()->json( [
|
||||
'id' => \intval( $results['results'][0]['objectId'] ),
|
||||
'html' => \IPS\Theme::i()->getTemplate('marketplace')->fileCardMini( $this->_api( "downloads/files/" . (int) $results['results'][0]['objectId'] ) )
|
||||
]);
|
||||
] );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->sendOutput( '' );
|
||||
\IPS\Output::i()->json( [ 'error' => \IPS\Member::loggedIn()->language()->addToStack( 'no_results' ) ], 404 );
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -525,16 +576,16 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
/* Nothing compatible */
|
||||
if( !$results['totalResults'] )
|
||||
{
|
||||
\IPS\Output::i()->sendOutput( \IPS\Member::loggedIn()->language()->addToStack( 'no_results' ) );
|
||||
\IPS\Output::i()->json( [ 'error' => \IPS\Member::loggedIn()->language()->addToStack( 'no_results' ) ], 404 );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate('marketplace')->fileCardList( $results['results'] ) );
|
||||
\IPS\Output::i()->json( [ 'html' => \IPS\Theme::i()->getTemplate('marketplace')->fileCardList( $results['results'] ) ] );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->sendOutput( \IPS\Member::loggedIn()->language()->addToStack( 'no_results' ) );
|
||||
\IPS\Output::i()->json( [ 'error' => \IPS\Member::loggedIn()->language()->addToStack( 'no_results' ) ], 404 );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -544,15 +595,17 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
final protected function apiLookup(): void
|
||||
final protected function apiLookup()
|
||||
{
|
||||
try
|
||||
{
|
||||
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate('marketplace')->fileCardMini( $this->_api( "downloads/files/" . (int) \IPS\Request::i()->id ) ) );
|
||||
\IPS\Output::i()->json( [
|
||||
'html' => \IPS\Theme::i()->getTemplate('marketplace')->fileCardMini( $this->_api( "downloads/files/" . (int) \IPS\Request::i()->id ) )
|
||||
] );
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
\IPS\Output::i()->sendOutput( '' );
|
||||
\IPS\Output::i()->json( [ 'error' => \IPS\Member::loggedIn()->language()->addToStack( 'no_results' ) ], 403 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -590,12 +643,13 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
* @param bool $authenticate Send Authentication data
|
||||
* @param array|null $data POST/PUT Data
|
||||
* @param int $timeout Custom timeout value for request
|
||||
* @param bool $retry Retried request after token expire
|
||||
* @return array JSON decoded response
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
final protected function _api( string $endpoint, array $queryString = NULL, bool $authenticate = FALSE, array $data = NULL, int $timeout = \IPS\DEFAULT_REQUEST_TIMEOUT ): array
|
||||
final protected function _api( string $endpoint, array $queryString = NULL, bool $authenticate = FALSE, array $data = NULL, int $timeout = \IPS\DEFAULT_REQUEST_TIMEOUT, bool $retry = FALSE ): array
|
||||
{
|
||||
$token = $authenticate ? ( $this->_token() ?? \IPS\MARKETPLACE_GUEST_TOKEN ) : \IPS\MARKETPLACE_GUEST_TOKEN;
|
||||
$url = \IPS\Http\Url::external( \IPS\MARKETPLACE_URL . '/api/' . $endpoint )->setQueryString( 'ips_version', \IPS\Application::load('core')->long_version );
|
||||
@@ -607,13 +661,32 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
$request = $url ->request( $timeout )
|
||||
->setHeaders( array( 'Authorization' => "Bearer {$token}" ) );
|
||||
|
||||
if ( $data === NULL )
|
||||
try
|
||||
{
|
||||
$response = $request->get();
|
||||
if ( $data === NULL )
|
||||
{
|
||||
$response = $request->get();
|
||||
}
|
||||
else
|
||||
{
|
||||
$response = $request->post( $data );
|
||||
}
|
||||
}
|
||||
else
|
||||
catch( \IPS\Http\Request\Exception $e )
|
||||
{
|
||||
$response = $request->post( $data );
|
||||
throw new \UnexpectedValueException();
|
||||
}
|
||||
|
||||
$json = $response->decodeJson( TRUE );
|
||||
|
||||
if( !$retry AND $response->httpResponseCode == 401 AND !empty( $json['errorCode'] ) AND \in_array( $json['errorCode'], [ '1S290/F', '3S290/9' ] ) )
|
||||
{
|
||||
/* Delete token */
|
||||
$this->_token = NULL;
|
||||
\IPS\Db::i()->delete( 'core_marketplace_tokens', array( 'id=?', \IPS\Member::loggedIn()->member_id ) );
|
||||
|
||||
/* Retry request */
|
||||
return $this->_api( $endpoint, $queryString, $authenticate, $data, $timeout, TRUE );
|
||||
}
|
||||
|
||||
if( $response->httpResponseCode != 200 )
|
||||
@@ -621,8 +694,6 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
throw new \RuntimeException( (string) $response, $response->httpResponseCode );
|
||||
}
|
||||
|
||||
$json = $response->decodeJson( TRUE );
|
||||
|
||||
if( $json === NULL )
|
||||
{
|
||||
throw new \UnexpectedValueException();
|
||||
@@ -656,6 +727,12 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
return $hold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate renewal term string from API response
|
||||
*
|
||||
* @param array $renewalTerm
|
||||
* @return string
|
||||
*/
|
||||
final protected function _calculateRenewalTerm( array $renewalTerm ): string
|
||||
{
|
||||
$lang = \IPS\Member::loggedIn()->language();
|
||||
@@ -727,8 +804,17 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
try
|
||||
{
|
||||
$response = \IPS\Http\Url::external( \IPS\MARKETPLACE_URL . '/api/core/me' )->request()->setHeaders( array( 'Authorization' => "Bearer {$token}" ) )->get();
|
||||
$memberData = $response->decodeJson();
|
||||
$authenticatedMember = $memberData['name'];
|
||||
$data = $response->decodeJson();
|
||||
|
||||
/* Revoked Token, delete our local token and re-set the header */
|
||||
if( !empty( $data['errorCode'] ) AND \in_array( $data['errorCode'], [ '1S290/F', '3S290/9' ] ) )
|
||||
{
|
||||
/* Delete token */
|
||||
$this->_token = NULL;
|
||||
\IPS\Db::i()->delete( 'core_marketplace_tokens', array( 'id=?', \IPS\Member::loggedIn()->member_id ) );
|
||||
return $this->_customHeader( $title, $file, $categoryIds );
|
||||
}
|
||||
$authenticatedMember = $data['name'];
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
@@ -777,7 +863,7 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
* @param array $file REST API file array
|
||||
* @param string $temporaryFile Path to temporary file for application
|
||||
*/
|
||||
final protected function _installApplication( array $file, string $temporaryFile ): void
|
||||
final protected function _installApplication( array $file, string $temporaryFile )
|
||||
{
|
||||
/* Test the phar */
|
||||
rename( $temporaryFile, $temporaryFile . '.tar' );
|
||||
@@ -813,6 +899,14 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
/* New install */
|
||||
$_type = 'install';
|
||||
}
|
||||
catch( \UnexpectedValueException $e )
|
||||
{
|
||||
/* Catch exception if it is a legacy application that needs upgrading */
|
||||
if ( !mb_stristr( $e->getMessage(), 'Missing:' ) )
|
||||
{
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( \IPS\CIC2 )
|
||||
@@ -860,11 +954,10 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
* @param array $file REST API file array
|
||||
* @param string $temporaryFile Path to temporary language XML file
|
||||
*/
|
||||
final protected function _installLanguage( array $file, string $temporaryFile ): void
|
||||
final protected function _installLanguage( array $file, string $temporaryFile )
|
||||
{
|
||||
/* Already installed? */
|
||||
$xml = new \IPS\Xml\XMLReader;
|
||||
$xml->open( $temporaryFile );
|
||||
$xml = \IPS\Xml\XMLReader::safeOpen( $temporaryFile );
|
||||
if ( !@$xml->read() )
|
||||
{
|
||||
throw new \UnexpectedValueException;
|
||||
@@ -891,11 +984,10 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
* @param array $file REST API file array
|
||||
* @param string $temporaryFile Path to temporary plugin XML file
|
||||
*/
|
||||
final protected function _installPlugin( array $file, string $temporaryFile ): void
|
||||
final protected function _installPlugin( array $file, string $temporaryFile )
|
||||
{
|
||||
/* Already installed? */
|
||||
$xml = new \IPS\Xml\XMLReader;
|
||||
$xml->open( $temporaryFile );
|
||||
$xml = \IPS\Xml\XMLReader::safeOpen( $temporaryFile );
|
||||
if ( !@$xml->read() )
|
||||
{
|
||||
throw new \UnexpectedValueException;
|
||||
@@ -930,10 +1022,9 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
* @param array $file REST API file array
|
||||
* @param string $temporaryFile Path to temporary theme XML file
|
||||
*/
|
||||
final protected function _installTheme( array $file, string $temporaryFile ): void
|
||||
final protected function _installTheme( array $file, string $temporaryFile )
|
||||
{
|
||||
$xml = new \IPS\Xml\XMLReader;
|
||||
$xml->open( $temporaryFile );
|
||||
$xml = \IPS\Xml\XMLReader::safeOpen( $temporaryFile );
|
||||
if ( !@$xml->read() )
|
||||
{
|
||||
throw new \UnexpectedValueException;
|
||||
@@ -984,7 +1075,16 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
catch( \OutOfRangeException $e )
|
||||
{
|
||||
$categories = [ 'apps' => [], 'themes' => [], 'languages' => [], 'all' => [] ];
|
||||
$response = $this->_api( 'marketplace/categories', NULL, FALSE, NULL, 2 );
|
||||
|
||||
try
|
||||
{
|
||||
$response = $this->_api( 'marketplace/categories', NULL, FALSE, NULL, 2 );
|
||||
}
|
||||
catch( \RuntimeException | \UnexpectedValueException $e )
|
||||
{
|
||||
return $categories;
|
||||
}
|
||||
|
||||
foreach( $response['results'] as $cat )
|
||||
{
|
||||
if( isset( $categories[ $cat['resourceType'] ] ) )
|
||||
@@ -1007,7 +1107,7 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
* @param int $ttl Time to live in minutes
|
||||
* @return void
|
||||
*/
|
||||
final protected function _setDatastoreCache( string $name, array $content, int $ttl ): void
|
||||
final protected function _setDatastoreCache( string $name, array $content, int $ttl )
|
||||
{
|
||||
$ttlSeconds = $ttl * 60;
|
||||
$variableName = 'marketplace_' . $name . '_' . md5( $this->_token() ?? \IPS\MARKETPLACE_GUEST_TOKEN );
|
||||
@@ -1131,13 +1231,12 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
/**
|
||||
* Handler for Core update check functionality
|
||||
*
|
||||
* @param int $fileId Marketplace ID
|
||||
* @param int $currentVersion Current Long Version
|
||||
* @param array $files Array of files, key is Marketplace ID, value is current version
|
||||
* @return array
|
||||
*/
|
||||
final public function _updateCheck( int $fileId, int $currentVersion ):? array
|
||||
final public function _updateCheck( array $files ):? array
|
||||
{
|
||||
return $this->_api("marketplace/files/{$fileId}/updatecheck", [ 'version' => $currentVersion ] );
|
||||
return $this->_api("marketplace/files/versions", [ 'files' => $files ] );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1150,7 +1249,7 @@ class _marketplace extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
$valid = TRUE;
|
||||
$licenseData = \IPS\IPS::licenseKey();
|
||||
if ( !$licenseData or strtotime( $licenseData['expires'] ) < time() )
|
||||
if ( !$licenseData or ( $licenseData['expires'] !== NULL AND strtotime( $licenseData['expires'] ) < time() ) )
|
||||
{
|
||||
$valid = FALSE;
|
||||
}
|
||||
|
||||
@@ -32,10 +32,16 @@ class _onboard extends \IPS\Dispatcher\Controller
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function manage(): void
|
||||
final public function manage()
|
||||
{
|
||||
/* Has onboarding been completed? */
|
||||
if( \IPS\Settings::i()->mp_onboard_complete )
|
||||
{
|
||||
\IPS\Output::i()->error( 'marketplace_onboard_already_complete', '2C420/1', 403, '' );
|
||||
}
|
||||
|
||||
$steps = [];
|
||||
if ( array_filter( array_keys( \IPS\Application::applications() ), function( $k ) { return !\in_array( $k, \IPS\Application::$ipsApps ); } ) )
|
||||
if ( array_filter( array_keys( \IPS\Application::applications() ), function( $k ) { return !\in_array( $k, \IPS\IPS::$ipsApps ); } ) )
|
||||
{
|
||||
$steps['marketplace_onboard_applications'] = array( $this, '_applications' );
|
||||
}
|
||||
@@ -71,7 +77,7 @@ class _onboard extends \IPS\Dispatcher\Controller
|
||||
* @param mixed $data Wizard data
|
||||
* @return mixed
|
||||
*/
|
||||
public function _applications( $data )
|
||||
final public function _applications( $data )
|
||||
{
|
||||
if ( \IPS\Request::i()->match_apps )
|
||||
{
|
||||
@@ -90,7 +96,7 @@ class _onboard extends \IPS\Dispatcher\Controller
|
||||
$rows = [];
|
||||
foreach ( \IPS\Application::applications() as $application )
|
||||
{
|
||||
if ( !\in_array( $application->directory, \IPS\Application::$ipsApps ) )
|
||||
if ( !\in_array( $application->directory, \IPS\IPS::$ipsApps ) )
|
||||
{
|
||||
$rows[ $application->directory ] = \IPS\Theme::i()->getTemplate('marketplace')->onboardRow( $application->directory, $application->marketplace_id, 'apps', $application->_title, $application->version, $application->author, $application->website, 'apps' );
|
||||
}
|
||||
@@ -111,7 +117,7 @@ class _onboard extends \IPS\Dispatcher\Controller
|
||||
* @param mixed $data Wizard data
|
||||
* @return mixed
|
||||
*/
|
||||
public function _plugins( $data )
|
||||
final public function _plugins( $data )
|
||||
{
|
||||
if ( \IPS\Request::i()->match_plugins )
|
||||
{
|
||||
@@ -148,7 +154,7 @@ class _onboard extends \IPS\Dispatcher\Controller
|
||||
* @param mixed $data Wizard data
|
||||
* @return mixed
|
||||
*/
|
||||
public function _themes( $data )
|
||||
final public function _themes( $data )
|
||||
{
|
||||
if ( \IPS\Request::i()->match_themes )
|
||||
{
|
||||
@@ -188,7 +194,7 @@ class _onboard extends \IPS\Dispatcher\Controller
|
||||
* @param mixed $data Wizard data
|
||||
* @return mixed
|
||||
*/
|
||||
public function _languages( $data )
|
||||
final public function _languages( $data )
|
||||
{
|
||||
if ( \IPS\Request::i()->match_languages )
|
||||
{
|
||||
@@ -228,8 +234,11 @@ class _onboard extends \IPS\Dispatcher\Controller
|
||||
* @param mixed $data Wizard data
|
||||
* @return mixed
|
||||
*/
|
||||
public function _complete()
|
||||
final public function _complete()
|
||||
{
|
||||
/* Set onboard complete flag */
|
||||
\IPS\Settings::i()->changeValues( array( 'mp_onboard_complete' => 1 ) );
|
||||
|
||||
\IPS\core\AdminNotification::remove( 'core', 'ConfigurationError', 'marketplaceSetup' );
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal('app=core&module=marketplace&controller=marketplace') );
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ class _devices extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
$oauthClients = \IPS\Api\OAuthClient::roots();
|
||||
$apps = array();
|
||||
foreach ( \IPS\Db::i()->select( '*', 'core_oauth_server_access_tokens', array( 'member_id=? AND device_key=?', \IPS\Member::loggedIn()->member_id, $device->device_key ), 'issued DESC' ) as $accessToken )
|
||||
foreach ( \IPS\Db::i()->select( '*', 'core_oauth_server_access_tokens', array( 'member_id=? AND device_key=?', $member->member_id, $device->device_key ), 'issued DESC' ) as $accessToken )
|
||||
{
|
||||
if ( isset( $oauthClients[ $accessToken['client_id'] ] ) )
|
||||
{
|
||||
@@ -269,7 +269,7 @@ class _devices extends \IPS\Dispatcher\Controller
|
||||
$device->login_key = NULL;
|
||||
$device->save();
|
||||
|
||||
\IPS\Db::i()->delete( 'core_oauth_server_access_tokens', array( 'member_id=? AND device_key=?', $device->member_id, $device->device_key ) );
|
||||
\IPS\Db::i()->update( 'core_oauth_server_access_tokens', array( 'status' => 'revoked' ), array( 'member_id=? AND device_key=?', $device->member_id, $device->device_key ) );
|
||||
|
||||
$member->logHistory( 'core', 'login', array( 'type' => 'logout', 'device' => $device->device_key ) );
|
||||
\IPS\Session::i()->log( 'acplog__members_disabled_autologin', array( $member->name => FALSE ) );
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -142,13 +142,13 @@ class _ban extends \IPS\Dispatcher\Controller
|
||||
|
||||
/* Build form */
|
||||
$form = new \IPS\Helpers\Form();
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'ban_type', $current['ban_type'], TRUE, array( 'options' => array(
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'ban_type', $current ? $current['ban_type'] : NULL , TRUE, array( 'options' => array(
|
||||
'ip' => 'ban_filter_ip_select',
|
||||
'email' => 'ban_filter_email_select',
|
||||
'name' => 'ban_filter_name_select'
|
||||
) ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'ban_content', $current['ban_content'], TRUE ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'ban_reason', $current['ban_reason'] ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'ban_content', $current ? $current['ban_content'] : "", TRUE ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'ban_reason', $current ? $current['ban_reason'] : "" ) );
|
||||
|
||||
/* Handle submissions */
|
||||
if ( $values = $form->values() )
|
||||
|
||||
@@ -64,18 +64,6 @@ class _notifications extends \IPS\Dispatcher\Controller
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
$methodTable = new \IPS\Helpers\Table\Custom( array( 'inline' => array( 'method' => \IPS\Member::loggedIn()->language()->addToStack('member_notifications_inline') ) ), \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=notifications', 'admin' ) );
|
||||
$methodTable->langPrefix = 'notificationsettings_';
|
||||
$methodTable->rowButtons = function( $row, $k ) {
|
||||
return array(
|
||||
'edit' => array(
|
||||
'title' => 'edit',
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=notifications&do=method', 'admin' )->setQueryString( 'id', $k ),
|
||||
'icon' => 'pencil'
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
if( !\IPS\CIC )
|
||||
{
|
||||
@@ -100,7 +88,13 @@ class _notifications extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('notifications');
|
||||
\IPS\Output::i()->output = $typeTable . $methodTable;
|
||||
|
||||
if ( ! \IPS\Notification::canUseWebPush() )
|
||||
{
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('global')->message( 'acp_notifications_cannot_use_web_push', 'general' );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->output .= $typeTable;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,7 +130,8 @@ class _notifications extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
\IPS\Task::queue( 'core', 'PruneLargeTable', array(
|
||||
'table' => 'core_follow',
|
||||
'where' => array( 'follow_member_id IN(?)', \IPS\Db::i()->select( 'member_id', 'core_members', array( 'last_activity < ?', \IPS\DateTime::create()->sub( new \DateInterval( 'P' . $values['prune_follows'] . 'D' ) )->getTimestamp() ) ) ),
|
||||
'where' => array( 'follow_app!=? AND follow_area!=? AND follow_member_id IN(?)', 'core', 'member', \IPS\Db::i()->select( 'member_id', 'core_members', array( 'last_activity < ?', \IPS\DateTime::create()->sub( new \DateInterval( 'P' . $values['prune_follows'] . 'D' ) )->getTimestamp() ) ) ),
|
||||
'setting' => 'prune_follows',
|
||||
'deleteJoin' => array(
|
||||
'column' => 'member_id',
|
||||
'table' => 'core_members',
|
||||
@@ -187,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 and !\in_array( 'push', $option['disabled'] ) ) or !\in_array( 'email', $option['disabled'] ) )
|
||||
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'] ) )
|
||||
{
|
||||
$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" ),
|
||||
@@ -212,8 +207,16 @@ class _notifications extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( array( 'inline', 'push', 'email' ) as $k )
|
||||
|
||||
if ( \IPS\Notification::canUseWebPush() )
|
||||
{
|
||||
$types = array( 'inline', 'push', 'email' );
|
||||
}
|
||||
else
|
||||
{
|
||||
$types = array( 'inline', 'email' );
|
||||
}
|
||||
foreach ( $types as $k )
|
||||
{
|
||||
if ( !\in_array( $k, $option['disabled'] ) )
|
||||
{
|
||||
@@ -278,7 +281,7 @@ class _notifications extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
}
|
||||
|
||||
if ( !\in_array( 'inline', $option['disabled'] ) or ( \IPS\Settings::i()->mobile_app_id and !\in_array( 'push', $option['disabled'] ) ) or !\in_array( 'email', $option['disabled'] ) )
|
||||
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'] ) )
|
||||
{
|
||||
$row = array(
|
||||
'notification_key' => $key,
|
||||
@@ -293,7 +296,7 @@ class _notifications extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
if ( $k === 'push' )
|
||||
{
|
||||
if ( !\IPS\Settings::i()->mobile_app_id )
|
||||
if ( !\IPS\Settings::i()->mobile_app_id && !\IPS\Notification::webPushEnabled() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -349,36 +352,6 @@ class _notifications extends \IPS\Dispatcher\Controller
|
||||
\IPS\Output::i()->output = $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification List Settings
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function method()
|
||||
{
|
||||
$form = new \IPS\Helpers\Form;
|
||||
|
||||
$form->addHeader('notification_preferences');
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'notification_prefs_sound', \IPS\Settings::i()->notification_prefs_sound ? 'default' : 'optional', FALSE, array( 'options' => array(
|
||||
'default' => 'admin_notification_pref_default',
|
||||
'optional' => 'admin_notification_pref_optional',
|
||||
) ), NULL, NULL, NULL, 'notification_prefs_sound' ) );
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
$values['notification_prefs_sound'] = ( $values['notification_prefs_sound'] === 'default' );
|
||||
|
||||
$form->saveAsSettings( $values );
|
||||
|
||||
\IPS\Session::i()->log( 'acplog__notification_settings' );
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=notifications' ), 'saved' );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->breadcrumb[] = array( \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=notifications' ), \IPS\Member::loggedIn()->language()->addToStack( 'menu__core_membersettings_notifications' ) );
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('notificationsettings');
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('global')->block( 'notificationsettings', $form, FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Member Auto Follow Preferences
|
||||
*
|
||||
|
||||
@@ -117,7 +117,7 @@ class _profilesettings extends \IPS\Dispatcher\Controller
|
||||
'options' => array( 'public' => 'profile_birthday_type_public', 'private' => 'profile_birthday_type_private', 'none' => 'profile_birthday_type_none' )
|
||||
), NULL, NULL, NULL, 'profile_birthday_type' ) );
|
||||
|
||||
if( !\IPS\CIC AND \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'profiles', 'member_history_prune' ) )
|
||||
if( !\IPS\CIC AND \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'membersettings', 'member_history_prune' ) )
|
||||
{
|
||||
$form->addHeader( 'profile_member_history' );
|
||||
$form->add( new \IPS\Helpers\Form\Interval( 'prune_member_history', \IPS\Settings::i()->prune_member_history, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL, 'prune_member_history' ) );
|
||||
@@ -214,6 +214,7 @@ class _profilesettings extends \IPS\Dispatcher\Controller
|
||||
\IPS\Task::queue( 'core', 'PruneLargeTable', array(
|
||||
'table' => 'core_member_history',
|
||||
'where' => array( 'log_date < ?', \IPS\DateTime::create()->sub( new \DateInterval( 'P' . $values['prune_member_history'] . 'D' ) )->getTimestamp() ),
|
||||
'setting' => 'prune_member_history',
|
||||
), 4 );
|
||||
}
|
||||
|
||||
@@ -222,6 +223,7 @@ class _profilesettings extends \IPS\Dispatcher\Controller
|
||||
\IPS\Task::queue( 'core', 'PruneLargeTable', array(
|
||||
'table' => 'core_members_known_ip_addresses',
|
||||
'where' => array( 'last_seen < ?', \IPS\DateTime::create()->sub( new \DateInterval( 'P' . $values['prune_known_ips'] . 'D' ) )->getTimestamp() ),
|
||||
'setting' => 'prune_known_ips',
|
||||
), 4 );
|
||||
}
|
||||
|
||||
@@ -230,6 +232,7 @@ class _profilesettings extends \IPS\Dispatcher\Controller
|
||||
\IPS\Task::queue( 'core', 'PruneLargeTable', array(
|
||||
'table' => 'core_members_known_devices',
|
||||
'where' => array( 'last_seen < ?', \IPS\DateTime::create()->sub( new \DateInterval( 'P' . $values['prune_known_devices'] . 'D' ) )->getTimestamp() ),
|
||||
'setting' => 'prune_known_devices',
|
||||
), 4 );
|
||||
}
|
||||
|
||||
@@ -238,6 +241,7 @@ class _profilesettings extends \IPS\Dispatcher\Controller
|
||||
\IPS\Task::queue( 'core', 'PruneLargeTable', array(
|
||||
'table' => 'core_item_markers',
|
||||
'where' => array( 'item_member_id IN(?)', \IPS\Db::i()->select( 'member_id', 'core_members', array( 'last_activity < ?', \IPS\DateTime::create()->sub( new \DateInterval( 'P' . $values['prune_item_markers'] . 'D' ) )->getTimestamp() ) ) ),
|
||||
'setting' => 'prune_item_markers',
|
||||
'deleteJoin' => array(
|
||||
'column' => 'member_id',
|
||||
'table' => 'core_members',
|
||||
|
||||
@@ -1,258 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief ranks
|
||||
* @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 Apr 2013
|
||||
*/
|
||||
|
||||
namespace IPS\core\modules\admin\membersettings;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* ranks
|
||||
*/
|
||||
class _ranks extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
/**
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'ranks_manage' );
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
/* Create the table */
|
||||
$table = new \IPS\Helpers\Table\Db( 'core_member_ranks', \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=ranks' ) );
|
||||
|
||||
/* Columns */
|
||||
$table->joins = array(
|
||||
array( 'select' => 'w.word_custom', 'from' => array( 'core_sys_lang_words', 'w' ), 'where' => "w.word_key=CONCAT( 'core_member_rank_', core_member_ranks.id ) AND w.lang_id=" . \IPS\Member::loggedIn()->language()->id )
|
||||
);
|
||||
|
||||
$table->include = array( 'word_custom', 'posts', 'pips' );
|
||||
$table->langPrefix = 'member_ranks_';
|
||||
$table->mainColumn = 'title';
|
||||
$table->noSort = array( 'pips' );
|
||||
|
||||
$table->parsers = array(
|
||||
'pips' => function( $val, $row )
|
||||
{
|
||||
if ( $row['use_icon'] and $row['icon'] )
|
||||
{
|
||||
return "<img src='" . \IPS\File::get( 'core_Theme', $row['icon'] )->url . "' alt=''>";
|
||||
}
|
||||
else
|
||||
{
|
||||
return str_repeat("<span class='ipsPip'></span>", \intval($val) );
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$table->sortBy = $table->sortBy ?: 'posts';
|
||||
$table->sortDirection = $table->sortDirection ?: 'asc';
|
||||
|
||||
/* Root Buttons */
|
||||
\IPS\Output::i()->sidebar['actions'] = array(
|
||||
'add' => array(
|
||||
'primary' => TRUE,
|
||||
'icon' => 'plus',
|
||||
'title' => 'member_ranks_add',
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('member_ranks_add') ),
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=ranks&do=form' ),
|
||||
)
|
||||
);
|
||||
|
||||
/* Row buttons */
|
||||
$table->rowButtons = function( $row )
|
||||
{
|
||||
$return = array();
|
||||
|
||||
$return['edit'] = array(
|
||||
'icon' => 'pencil',
|
||||
'title' => 'edit',
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('edit') ),
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=ranks&do=form&id=' ) . $row['id'],
|
||||
);
|
||||
|
||||
$return['delete'] = array(
|
||||
'icon' => 'times-circle',
|
||||
'title' => 'delete',
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=ranks&do=delete&id=' ) . $row['id'],
|
||||
'data' => array( 'delete' => '' ),
|
||||
);
|
||||
|
||||
return $return;
|
||||
};
|
||||
|
||||
/* The incredibly complex search code */
|
||||
$table->quickSearch = 'word_custom';
|
||||
|
||||
/* Show a message about the setting to allow members to set their own */
|
||||
switch ( \IPS\Settings::i()->post_titlechange )
|
||||
{
|
||||
case -1:
|
||||
$message = \IPS\Member::loggedIn()->language()->addToStack('rankown_none');
|
||||
break;
|
||||
|
||||
case 0:
|
||||
$message = \IPS\Member::loggedIn()->language()->addToStack('rankown_always');
|
||||
break;
|
||||
|
||||
default:
|
||||
$message = \IPS\Member::loggedIn()->language()->addToStack( 'rankown_limit', FALSE, array( 'pluralize' => array( \IPS\Settings::i()->post_titlechange ) ) );
|
||||
break;
|
||||
}
|
||||
$message .= ' <a href="' . \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=ranks&do=settings' ) . '" data-ipsDialog data-ipsDialog-title="' . \IPS\Member::loggedIn()->language()->addToStack('change') . '">' . \IPS\Member::loggedIn()->language()->addToStack('change') . '</a>';
|
||||
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->message( $message, 'information', NULL, FALSE );
|
||||
|
||||
/* Display */
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('ranks');
|
||||
\IPS\Output::i()->output .= (string) $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add/Edit Rank
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function form()
|
||||
{
|
||||
$current = NULL;
|
||||
|
||||
if ( \IPS\Request::i()->id )
|
||||
{
|
||||
$current = \IPS\Db::i()->select( '*', 'core_member_ranks', array( 'id=?', \IPS\Request::i()->id ) )->first();
|
||||
}
|
||||
|
||||
/* Build form */
|
||||
$form = new \IPS\Helpers\Form();
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Translatable( 'member_ranks_word_custom', NULL, TRUE, array( 'app' => 'core', 'key' => ( $current ? "core_member_rank_{$current['id']}" : NULL ) ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Number( 'member_ranks_posts', $current ? $current['posts'] : 0, TRUE, array( 'min' => 0 ) ) );
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'member_use_icon', $current ? $current['use_icon'] : NULL, FALSE, array(
|
||||
'parse' => 'raw',
|
||||
'options' => array( 0 => \IPS\Member::loggedIn()->language()->addToStack('member_ranks_pip_number', FALSE, array( 'htmlsprintf' => array( "<span class='ipsPip'></span>" ) ) ), 1 => \IPS\Member::loggedIn()->language()->addToStack('member_ranks_pip_icon') ),
|
||||
'toggles' => array( 0 => array('member_ranks_pips'), 1 => array('member_ranks_icon') )
|
||||
) ) );
|
||||
|
||||
/* Pip Icon */
|
||||
$pip = NULL;
|
||||
if ( $current AND $current['icon'] )
|
||||
{
|
||||
$pip = \IPS\File::get( 'core_Theme', $current['icon'] );
|
||||
}
|
||||
$form->add( new \IPS\Helpers\Form\Number( 'member_ranks_num_pips' , $current ? $current['pips'] : 0, FALSE, array( 'min' => 0, 'max' => 100 ), NULL, NULL, NULL, 'member_ranks_pips' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Upload( 'member_ranks_icon', $pip, FALSE, array( 'image' => array( 'maxWidth' => 140, 'maxHeight' => 140 ), 'storageExtension' => 'core_Theme' ), NULL, NULL, NULL, 'member_ranks_icon' ) );
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
$save = array(
|
||||
'posts' => \intval( $values['member_ranks_posts'] ),
|
||||
'pips' => \intval( $values['member_ranks_num_pips'] ),
|
||||
'use_icon' => \intval( $values['member_use_icon'] ),
|
||||
'icon' => '',
|
||||
);
|
||||
|
||||
if ( $values['member_ranks_icon'] instanceof \IPS\File )
|
||||
{
|
||||
$save['icon'] = (string) $values['member_ranks_icon'];
|
||||
}
|
||||
|
||||
if ( $current )
|
||||
{
|
||||
\IPS\Db::i()->update( 'core_member_ranks', $save, array( 'id=?', $current['id'] ) );
|
||||
$id = $current['id'];
|
||||
\IPS\Session::i()->log( 'acplogs__rank_edited', array( $save['posts'] => FALSE ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
$id = \IPS\Db::i()->insert( 'core_member_ranks', $save );
|
||||
\IPS\Session::i()->log( 'acplogs__rank_created', array( $save['posts'] => FALSE ) );
|
||||
}
|
||||
|
||||
\IPS\Lang::saveCustom( 'core', "core_member_rank_{$id}", $values['member_ranks_word_custom'] );
|
||||
unset( \IPS\Data\Store::i()->ranks );
|
||||
|
||||
/* Clear guest page caches */
|
||||
\IPS\Data\Cache::i()->clearAll();
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=ranks' ), 'saved' );
|
||||
}
|
||||
|
||||
/* Display */
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->block( $current ? "core_member_rank_{$current['id']}" : 'add', $form, FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'ranks_manage' );
|
||||
|
||||
/* Make sure the user confirmed the deletion */
|
||||
\IPS\Request::i()->confirmedDelete();
|
||||
|
||||
try
|
||||
{
|
||||
$current = \IPS\Db::i()->select( '*', 'core_member_ranks', array( 'id=?', \IPS\Request::i()->id ) )->first();
|
||||
|
||||
\IPS\Session::i()->log( 'acplogs__rank_deleted', array( $current['posts'] => FALSE ) );
|
||||
\IPS\Db::i()->delete( 'core_member_ranks', array( 'id=?', \IPS\Request::i()->id ) );
|
||||
\IPS\Lang::deleteCustom( 'core', 'core_member_rank_' . \IPS\Request::i()->id );
|
||||
}
|
||||
catch ( \UnderflowException $e ) { }
|
||||
|
||||
unset( \IPS\Data\Store::i()->ranks );
|
||||
|
||||
/* Clear guest page caches */
|
||||
\IPS\Data\Cache::i()->clearAll();
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=ranks' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function settings()
|
||||
{
|
||||
$form = new \IPS\Helpers\Form;
|
||||
$form->add( new \IPS\Helpers\Form\Number( 'post_titlechange', \IPS\Settings::i()->post_titlechange, FALSE, array( 'unlimited' => -1, 'unlimitedLang' => 'never_allow' ) ) );
|
||||
if ( $form->values() )
|
||||
{
|
||||
$form->saveAsSettings();
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=ranks' ), 'saved' );
|
||||
}
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->block( 'ranks', $form, FALSE );
|
||||
}
|
||||
}
|
||||
@@ -156,7 +156,7 @@ class _reputation extends \IPS\Node\Controller
|
||||
/* Build Form */
|
||||
$form = new \IPS\Helpers\Form();
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'reputation_enabled', \IPS\Settings::i()->reputation_enabled, FALSE, array( 'togglesOn' => array( 'reputation_point_types', 'reputation_protected_groups', 'reputation_can_self_vote', 'reputation_highlight', 'reputation_show_profile', 'overall_reaction_count' ) ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'reputation_protected_groups', explode( ',', \IPS\Settings::i()->reputation_protected_groups ), FALSE, array( 'options' => \IPS\Member\Group::groups( TRUE, FALSE ), 'parse' => 'normal', 'multiple' => TRUE ), NULL, NULL, NULL, 'reputation_protected_groups' ) );
|
||||
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'reputation_protected_groups', explode( ',', \IPS\Settings::i()->reputation_protected_groups ), FALSE, array( 'options' => \IPS\Member\Group::groups( TRUE, FALSE ), 'parse' => 'normal', 'multiple' => TRUE ), NULL, NULL, NULL, 'reputation_protected_groups' ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'reputation_can_self_vote', \IPS\Settings::i()->reputation_can_self_vote, FALSE, array(), NULL, NULL, NULL, 'reputation_can_self_vote' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Number( 'reputation_highlight', \IPS\Settings::i()->reputation_highlight, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('reputation_highlight_prefix'), \IPS\Member::loggedIn()->language()->addToStack('reputation_highlight_suffix'), 'reputation_highlight' ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'reputation_show_profile', \IPS\Settings::i()->reputation_show_profile, FALSE, array(), NULL, NULL, NULL, 'reputation_show_profile' ) );
|
||||
@@ -186,7 +186,7 @@ class _reputation extends \IPS\Node\Controller
|
||||
$form = new \IPS\Helpers\Form();
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'reputation_leaderboard_on', \IPS\Settings::i()->reputation_leaderboard_on, FALSE, array( 'togglesOn' => array( 'reputation_leaderboard_default_tab', 'form_header_leaderboard_tabs_leaderboard', 'reputation_show_days_won_trophy', 'reputation_timezone', 'form_header_leaderboard_tabs_members', 'reputation_top_members_overview', 'reputation_overview_max_members', 'reputation_top_members_filters', 'reputation_max_members', 'leaderboard_excluded_groups' ) ), NULL, NULL, NULL, 'reputation_leaderboard_on' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'reputation_leaderboard_default_tab', \IPS\Settings::i()->reputation_leaderboard_default_tab, FALSE, array( 'options' => array( 'leaderboard' => 'leaderboard_tabs_leaderboard', 'history' => 'leaderboard_tabs_history', 'members' => 'leaderboard_tabs_members' ) ), NULL, NULL, NULL, 'reputation_leaderboard_default_tab' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'leaderboard_excluded_groups', explode( ',', \IPS\Settings::i()->leaderboard_excluded_groups ), FALSE, array( 'options' => \IPS\Member\Group::groups( TRUE, FALSE ), 'parse' => 'normal', 'multiple' => TRUE ), NULL, NULL, NULL, 'leaderboard_excluded_groups' ) );
|
||||
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'leaderboard_excluded_groups', explode( ',', \IPS\Settings::i()->leaderboard_excluded_groups ), FALSE, array( 'options' => \IPS\Member\Group::groups( TRUE, FALSE ), 'parse' => 'normal', 'multiple' => TRUE ), NULL, NULL, NULL, 'leaderboard_excluded_groups' ) );
|
||||
$form->addHeader('leaderboard_tabs_leaderboard');
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'reputation_show_days_won_trophy', \IPS\Settings::i()->reputation_show_days_won_trophy, FALSE, array( 'togglesOn' => array() ), NULL, NULL, NULL, 'reputation_show_days_won_trophy' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Timezone( 'reputation_timezone', \IPS\Settings::i()->reputation_timezone, FALSE, array(), NULL, NULL, NULL, 'reputation_timezone' ) );
|
||||
@@ -411,7 +411,7 @@ class _reputation extends \IPS\Node\Controller
|
||||
* @param string $lastUsedTab The tab last used in the form
|
||||
* @return void
|
||||
*/
|
||||
protected function _afterSave( \IPS\Node\Model $old = NULL, \IPS\Node\Model $new, $lastUsedTab = FALSE )
|
||||
protected function _afterSave( ?\IPS\Node\Model $old, \IPS\Node\Model $new, $lastUsedTab = FALSE )
|
||||
{
|
||||
if( \IPS\Request::i()->isAjax() )
|
||||
{
|
||||
|
||||
@@ -159,7 +159,7 @@ class _menu extends \IPS\Dispatcher\Controller
|
||||
'toggles' => array( 1 => array( 'menu_manager_access' ) )
|
||||
), NULL, NULL, NULL, 'menu_manager_access_type' ) );
|
||||
}
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'menu_manager_access', $existing ? ( $existing['permissions'] == '*' ? '*' : explode( ',', $existing['permissions'] ) ) : '*', NULL, array( 'multiple' => TRUE, 'options' => $groups, 'unlimited' => '*', 'unlimitedLang' => 'everyone' ), NULL, NULL, NULL, 'menu_manager_access' ) );
|
||||
$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;
|
||||
|
||||
@@ -34,6 +34,11 @@ class _mobile extends \IPS\Dispatcher\Controller
|
||||
*/
|
||||
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();
|
||||
}
|
||||
@@ -117,6 +122,12 @@ class _mobile extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
|
||||
$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' ) );
|
||||
@@ -245,7 +256,7 @@ class _mobile extends \IPS\Dispatcher\Controller
|
||||
if ( $file )
|
||||
{
|
||||
$image = \IPS\Image::create( $file->contents() );
|
||||
if ( $image->width !== $image->height )
|
||||
if ( !$image->isSquare() )
|
||||
{
|
||||
throw new \DomainException('mobile_app_listing_logo_square');
|
||||
}
|
||||
@@ -288,7 +299,7 @@ class _mobile extends \IPS\Dispatcher\Controller
|
||||
$langauges = ['en'];
|
||||
}
|
||||
}
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'mobile_app_listing_languages', $existing ? explode( ',', $existing['languages'] ): ['en'], TRUE, array( 'multiple' => TRUE, 'options' => $langaugeOptions ) ) );
|
||||
$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 )
|
||||
{
|
||||
|
||||
@@ -197,7 +197,9 @@ class _spam extends \IPS\Dispatcher\Controller
|
||||
$licenseData = \IPS\IPS::licenseKey();
|
||||
|
||||
/* Build Form */
|
||||
$actions = array( 1 => 'spam_service_act_1', 2 => 'spam_service_act_2', 3 => 'spam_service_act_3', 4 => 'spam_service_act_4' );
|
||||
$actions = array( 1 => 'spam_service_act_1', 5 => 'spam_service_act_5', 2 => 'spam_service_act_2', 3 => 'spam_service_act_3', 4 => 'spam_service_act_4' );
|
||||
$days = json_decode( \IPS\Settings::i()->spam_service_days, TRUE );
|
||||
|
||||
$form = new \IPS\Helpers\Form;
|
||||
$form->addHeader( 'enhancements__core_SpamMonitoring' );
|
||||
|
||||
@@ -215,16 +217,35 @@ class _spam extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
}
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'spam_service_enabled', 0, FALSE, array( 'disabled' => $disabled, 'togglesOn' => array( 'spam_service_send_to_ips', 'spam_service_action_0', 'spam_service_action_1', 'spam_service_action_2', 'spam_service_action_3', 'spam_service_action_4' ) ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'spam_service_send_to_ips', 0, FALSE, array( 'disabled' => $disabled ), NULL, NULL, NULL, 'spam_service_send_to_ips' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'spam_service_action_1', \IPS\Settings::i()->spam_service_action_1, FALSE, array( 'disabled' => $disabled, 'options' => $actions ), NULL, NULL, NULL, 'spam_service_action_1' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'spam_service_action_2', \IPS\Settings::i()->spam_service_action_2, FALSE, array( 'disabled' => $disabled, 'options' => $actions ), NULL, NULL, NULL, 'spam_service_action_2' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'spam_service_action_3', \IPS\Settings::i()->spam_service_action_3, FALSE, array( 'disabled' => $disabled, 'options' => $actions ), NULL, NULL, NULL, 'spam_service_action_3' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'spam_service_action_4', \IPS\Settings::i()->spam_service_action_4, FALSE, array( 'disabled' => $disabled, 'options' => $actions ), NULL, NULL, NULL, 'spam_service_action_4' ) );
|
||||
$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\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' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Number( 'spam_service_action_2_num', ( isset( $days[2] ) ? $days[2] : -1 ), FALSE, array( 'unlimited' => -1, 'unlimitedLang' => 'spam_service_action_unlimited_days'), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('days'), 'spam_service_action_2_num' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'spam_service_action_3', \IPS\Settings::i()->spam_service_action_3, FALSE, array( 'disabled' => $disabled, 'options' => $actions, 'toggles' => array( '5' => array( 'spam_service_action_3_num' ) ) ), NULL, NULL, NULL, 'spam_service_action_3' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Number( 'spam_service_action_3_num', ( isset( $days[3] ) ? $days[3] : -1 ), FALSE, array( 'unlimited' => -1, 'unlimitedLang' => 'spam_service_action_unlimited_days'), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('days'), 'spam_service_action_3_num' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'spam_service_action_4', \IPS\Settings::i()->spam_service_action_4, FALSE, array( 'disabled' => $disabled, 'options' => $actions, 'toggles' => array( '5' => array( 'spam_service_action_4_num' ) ) ), NULL, NULL, NULL, 'spam_service_action_4' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Number( 'spam_service_action_4_num', ( isset( $days[4] ) ? $days[4] : -1 ), FALSE, array( 'unlimited' => -1, 'unlimitedLang' => 'spam_service_action_unlimited_days'), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('days'), 'spam_service_action_4_num' ) );
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'spam_service_action_0', \IPS\Settings::i()->spam_service_action_0, FALSE, array( 'disabled' => $disabled, 'options' => $actions ), NULL, NULL, NULL, 'spam_service_action_0' ) );
|
||||
if ( $form->values() )
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
$form->saveAsSettings();
|
||||
$values['spam_service_days'] = array();
|
||||
foreach( array( 1, 2, 3, 4 ) as $num )
|
||||
{
|
||||
if ( $values['spam_service_action_' . $num ] == 5 )
|
||||
{
|
||||
$values['spam_service_days'][ $num ] = $values['spam_service_action_' . $num . '_num' ];
|
||||
}
|
||||
|
||||
unset( $values['spam_service_action_' . $num . '_num' ] );
|
||||
}
|
||||
|
||||
$values['spam_service_days'] = json_encode( $values['spam_service_days'] );
|
||||
|
||||
$form->saveAsSettings( $values );
|
||||
\IPS\Session::i()->log( 'acplog__enhancements_edited', array( 'enhancements__core_SpamMonitoring' => TRUE ) );
|
||||
}
|
||||
|
||||
|
||||
@@ -156,7 +156,7 @@ class _warnings extends \IPS\Node\Controller
|
||||
'warn_show_own',
|
||||
'warnings_acknowledge'
|
||||
) ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'warn_protected', explode( ',', \IPS\Settings::i()->warn_protected ), FALSE, array( 'options' => \IPS\Member\Group::groups( TRUE, FALSE ), 'parse' => 'normal', 'multiple' => TRUE ), NULL, NULL, NULL, 'warn_protected' ) );
|
||||
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'warn_protected', explode( ',', \IPS\Settings::i()->warn_protected ), FALSE, array( 'options' => \IPS\Member\Group::groups( TRUE, FALSE ), 'parse' => 'normal', 'multiple' => TRUE ), NULL, NULL, NULL, 'warn_protected' ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'warn_show_own', \IPS\Settings::i()->warn_show_own, FALSE, array(), NULL, NULL, NULL, 'warn_show_own' ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'warnings_acknowledge', \IPS\Settings::i()->warnings_acknowledge, FALSE, array(), NULL, NULL, NULL, 'warnings_acknowledge' ) );
|
||||
|
||||
@@ -212,7 +212,7 @@ class _warnings extends \IPS\Node\Controller
|
||||
$form->add( new \IPS\Helpers\Form\Number( 'wa_points', ( $current ? $current['wa_points'] : 0 ), TRUE ) );
|
||||
foreach ( array( 'mq', 'rpa', 'suspend' ) as $k )
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\Custom( 'wa_' . $k, ( $current ? array( $current[ 'wa_' . $k ], $current[ 'wa_' . $k . '_unit' ] ) : 0 ), FALSE, array(
|
||||
$form->add( new \IPS\Helpers\Form\Custom( 'wa_' . $k, ( $current ? array( $current[ 'wa_' . $k ], $current[ 'wa_' . $k . '_unit' ] ) : array( NULL, NULL ) ), FALSE, array(
|
||||
'getHtml' => function( $element )
|
||||
{
|
||||
return \IPS\Theme::i()->getTemplate( 'members' )->warningTime( $element->name, $element->value, 'for', 'indefinitely' );
|
||||
|
||||
@@ -94,6 +94,10 @@ class _files extends \IPS\Dispatcher\Controller
|
||||
else
|
||||
{
|
||||
$url = \IPS\Http\Url::external( \IPS\Settings::i()->base_url . "applications/core/interface/file/attachment.php" )->setQueryString( 'id', $row['attach_id'] );
|
||||
if ( $row['attach_security_key'] )
|
||||
{
|
||||
$url = $url->setQueryString( 'key', $row['attach_security_key'] );
|
||||
}
|
||||
$val = htmlentities( $val, ENT_DISALLOWED, 'UTF-8', FALSE );
|
||||
return "<a href='{$url}' target='_blank' rel='noreferrer'>{$val}</a>";
|
||||
}
|
||||
@@ -303,12 +307,24 @@ class _files extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
$settings = json_decode( \IPS\Settings::i()->upload_settings, TRUE );
|
||||
|
||||
$configurations = array();
|
||||
$configurations = array();
|
||||
$cicDisabled = FALSE;
|
||||
|
||||
if( \IPS\CIC )
|
||||
{
|
||||
$cicDisabled = array();
|
||||
}
|
||||
|
||||
foreach ( \IPS\Db::i()->select( '*', 'core_file_storage' ) as $row )
|
||||
{
|
||||
$handlers = \IPS\File::storageHandlers( $row );
|
||||
$classname = $handlers[ $row['method'] ];
|
||||
$configurations[ $row['id'] ] = $classname::displayName( json_decode( $row['configuration'], TRUE ) );
|
||||
|
||||
if( $row['method'] == 'FileSystem' AND \IPS\CIC )
|
||||
{
|
||||
$cicDisabled[ $row['id'] ] = $row['id'];
|
||||
}
|
||||
}
|
||||
|
||||
$form = new \IPS\Helpers\Form;
|
||||
@@ -341,7 +357,14 @@ class _files extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
}
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'filestorage__' . $name, (int) $value, TRUE, array( 'options' => $configurations, 'disabled' => $disabled, 'toggles' => $toggles ) ) );
|
||||
$handlerDisabled = $cicDisabled;
|
||||
|
||||
if( isset( $handlerDisabled[ $value ] ) )
|
||||
{
|
||||
unset( $handlerDisabled[ $value ] );
|
||||
}
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'filestorage__' . $name, (int) $value, TRUE, array( 'options' => $configurations, 'disabled' => $disabled ?: $handlerDisabled, 'toggles' => $toggles ) ) );
|
||||
|
||||
if ( $disabled )
|
||||
{
|
||||
@@ -353,6 +376,18 @@ class _files extends \IPS\Dispatcher\Controller
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
/* Block moves to filesystem on CIC */
|
||||
foreach ( $values as $k => $v )
|
||||
{
|
||||
if ( isset( $settings[ $k ] ) AND !\is_array( $settings[ $k ] ) )
|
||||
{
|
||||
if( $settings[ $k ] != $v AND $k != 'filestorage_move' AND \in_array( $v, $cicDisabled ) )
|
||||
{
|
||||
\IPS\Output::i()->error( 'file_storage_cic_filesystem', '3C158/7', 403, '' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( isset( $values['filestorage_move'] ) AND $values['filestorage_move'] )
|
||||
{
|
||||
$rebuild = FALSE;
|
||||
@@ -376,7 +411,7 @@ class _files extends \IPS\Dispatcher\Controller
|
||||
if ( $settings[ $k ] != $v and $k != 'filestorage__core_Theme' AND $k != 'filestorage_move' )
|
||||
{
|
||||
/* Do we need to move files at all? */
|
||||
$configurations = \IPS\File::getConfigurations();
|
||||
$configurations = \IPS\File::getStore();
|
||||
$exploded = explode( '_', $k );
|
||||
$classname = "IPS\\{$exploded[2]}\\extensions\\core\\FileStorage\\{$exploded[3]}";
|
||||
$extension = new $classname;
|
||||
@@ -434,11 +469,19 @@ class _files extends \IPS\Dispatcher\Controller
|
||||
$table = new \IPS\Helpers\Table\Db( 'core_file_storage', \IPS\Http\Url::internal( "app=core&module=overview&controller=files&do=settings&tab=configurations" ) );
|
||||
$table->include = array( 'filestorage_method' );
|
||||
$table->noSort = array( 'filestorage_method' );
|
||||
$table->mainColumn = 'filestorage_method';
|
||||
$table->parsers = array( 'filestorage_method' => function( $val, $row )
|
||||
{
|
||||
$handlers = \IPS\File::storageHandlers( $row );
|
||||
$classname = $handlers[ $row['method'] ];
|
||||
return $classname::displayName( json_decode( $row['configuration'], TRUE ) );
|
||||
$title = $classname::displayName( json_decode( $row['configuration'], TRUE ) );
|
||||
|
||||
if( \IPS\CIC AND $row['method'] == 'FileSystem' )
|
||||
{
|
||||
return \IPS\Theme::i()->getTemplate( 'dashboard' )->filesystemNotCic( $title );
|
||||
}
|
||||
|
||||
return $title;
|
||||
} );
|
||||
|
||||
$table->rootButtons = array( 'add' => array(
|
||||
|
||||
@@ -52,6 +52,7 @@ class _onboard extends \IPS\Dispatcher\Controller
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('onboard_pagetitle');
|
||||
\IPS\Output::i()->showTitle = FALSE;
|
||||
\IPS\Output::i()->bypassCsrfKeyCheck = TRUE;
|
||||
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate( 'dashboard' )->onboardWelcome();
|
||||
}
|
||||
|
||||
@@ -97,6 +98,9 @@ class _onboard extends \IPS\Dispatcher\Controller
|
||||
$form->add( new \IPS\Helpers\Form\Address( 'site_address', \IPS\GeoLocation::buildFromJson( \IPS\Settings::i()->site_address ), FALSE ) );
|
||||
$form->add( new \IPS\Helpers\Form\Stack( 'site_social_profiles', \IPS\Settings::i()->site_social_profiles ? json_decode( \IPS\Settings::i()->site_social_profiles, true ) : array(), FALSE, array( 'stackFieldType' => '\IPS\core\Form\SocialProfiles', 'maxItems' => 50, 'key' => array( 'placeholder' => 'http://example.com', 'size' => 20 ) ) ) );
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Email( 'email_out', \IPS\Settings::i()->email_out, TRUE, array(), NULL, NULL, NULL, 'email_out' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Email( 'email_in', \IPS\Settings::i()->email_in, TRUE ) );
|
||||
|
||||
$form->addTab( 'onboard_tab_appearance' );
|
||||
|
||||
/* If we do not have an email logo and none of our themes have a logo, show the logo field */
|
||||
|
||||
@@ -115,6 +115,7 @@ class _advertisements extends \IPS\Dispatcher\Controller
|
||||
|
||||
$table = new \IPS\Helpers\Table\Db( 'core_advertisements', $url, $where );
|
||||
$table->langPrefix = 'ads_';
|
||||
$table->rowClasses = array( 'ad_html' => array( 'ipsTable_wrap' ) );
|
||||
|
||||
/* Columns we need */
|
||||
$table->include = array( 'word_custom', 'ad_html', 'ad_impressions' );
|
||||
@@ -368,7 +369,14 @@ class _advertisements extends \IPS\Dispatcher\Controller
|
||||
\IPS\Session::i()->log( 'acplog_ad_disabled', array() );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=promotion&controller=advertisements' )->setQueryString( 'filter', \IPS\Request::i()->filter ), \IPS\Request::i()->status ? 'ad_toggled_visible' : 'ad_toggled_notvisible' );
|
||||
if( \IPS\Request::i()->isAjax() )
|
||||
{
|
||||
\IPS\Output::i()->json( 'OK' );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=promotion&controller=advertisements' )->setQueryString( 'filter', \IPS\Request::i()->filter ), \IPS\Request::i()->status ? 'ad_toggled_visible' : 'ad_toggled_notvisible' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -508,7 +516,7 @@ class _advertisements extends \IPS\Dispatcher\Controller
|
||||
$form->add( new \IPS\Helpers\Form\Stack( 'ad_location_custom', $customLocations, FALSE, array(), NULL, NULL, NULL, 'ad_location_custom' ) );
|
||||
|
||||
/* Generic fields available for both html and image ads */
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'ad_exempt', ( $record->id ) ? ( ( $record->exempt == '*' ) ? '*' : json_decode( $record->exempt, TRUE ) ) : '*', FALSE, array( 'options' => \IPS\Member\Group::groups(), 'parse' => 'normal', 'multiple' => TRUE, 'unlimited' => '*', 'unlimitedLang' => 'everyone' ), NULL, NULL, NULL, 'ad_exempt' ) );
|
||||
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'ad_exempt', ( $record->id ) ? ( ( $record->exempt == '*' ) ? '*' : json_decode( $record->exempt, TRUE ) ) : '*', FALSE, array( 'options' => \IPS\Member\Group::groups(), 'parse' => 'normal', 'multiple' => TRUE, 'unlimited' => '*', 'unlimitedLang' => 'everyone', 'impliedUnlimited' => TRUE ), NULL, NULL, NULL, 'ad_exempt' ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'ad_nocontent_page_output', ( $record->id ) ? $record->nocontent_page_output : FALSE ) );
|
||||
}
|
||||
else
|
||||
|
||||
@@ -73,12 +73,18 @@ class _seo extends \IPS\Dispatcher\Controller
|
||||
|
||||
/* Build tab list */
|
||||
$tabs = array();
|
||||
if ( !\IPS\CIC )
|
||||
if ( !\IPS\CIC AND \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'promotion', 'seo_furls' ) )
|
||||
{
|
||||
$tabs['urls'] = 'seo_tab_furls';
|
||||
}
|
||||
$tabs['sitemap'] = 'seo_tab_sitemap';
|
||||
$tabs['metatags'] = 'seo_tab_metatags';
|
||||
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'promotion', 'seo_sitemap' ) )
|
||||
{
|
||||
$tabs['sitemap'] = 'seo_tab_sitemap';
|
||||
}
|
||||
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'promotion', 'seo_meta' ) )
|
||||
{
|
||||
$tabs['metatags'] = 'seo_tab_metatags';
|
||||
}
|
||||
|
||||
/* Display */
|
||||
if ( $activeTabContents )
|
||||
|
||||
@@ -141,11 +141,12 @@ class _advanced extends \IPS\Dispatcher\Controller
|
||||
$toggles = array();
|
||||
$disabled = array();
|
||||
$storeConfigurationFields = array();
|
||||
$options = array(
|
||||
'FileSystem' => 'datastore_method_FileSystem',
|
||||
'Database' => 'datastore_method_Database',
|
||||
'Redis' => 'datastore_method_Redis',
|
||||
);
|
||||
$options = [];
|
||||
foreach( \IPS\Data\Store::availableMethods() AS $key => $class )
|
||||
{
|
||||
$options[ $key ] = 'datastore_method_' . $key;
|
||||
}
|
||||
|
||||
$existingConfiguration = json_decode( \IPS\STORE_CONFIG, TRUE );
|
||||
foreach ( $options as $k => $v )
|
||||
{
|
||||
@@ -190,17 +191,10 @@ class _advanced extends \IPS\Dispatcher\Controller
|
||||
$toggles = array( 'Redis' => array( 'redis_enabled' ) );
|
||||
$disabled = array();
|
||||
$cacheConfigurationFields = array();
|
||||
$options = array(
|
||||
'None' => 'datastore_method_None',
|
||||
'Apc' => 'datastore_method_Apc',
|
||||
'Memcache' => 'datastore_method_Memcache',
|
||||
'Redis' => 'datastore_method_Redis',
|
||||
'Wincache' => 'datastore_method_Wincache',
|
||||
'Xcache' => 'datastore_method_Xcache',
|
||||
);
|
||||
if ( \IPS\TEST_CACHING )
|
||||
$options = [];
|
||||
foreach( \IPS\Data\Cache::availableMethods() AS $key => $class )
|
||||
{
|
||||
$options['Test'] = 'datastore_method_Test';
|
||||
$options[ $key ] = 'datastore_method_' . $key;
|
||||
}
|
||||
|
||||
$cacheConfig = \IPS\CACHE_CONFIG;
|
||||
@@ -211,20 +205,10 @@ class _advanced extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
|
||||
$existingConfiguration = json_decode( $cacheConfig, TRUE );
|
||||
|
||||
/* Remove deprecated options if they're not in use @todo 4.6 */
|
||||
$deprecatedCacheMethods = array( 'Apc', 'Memcache', 'Wincache', 'Xcache' );
|
||||
foreach( $deprecatedCacheMethods as $cache )
|
||||
{
|
||||
if( $cache !== \IPS\CACHE_METHOD )
|
||||
{
|
||||
unset( $options[ $cache ] );
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $options as $k => $v )
|
||||
{
|
||||
$class = 'IPS\Data\Cache\\' . $k;
|
||||
$class = \IPS\Data\Cache::availableMethods()[ $k ];
|
||||
if ( !$class::supported() )
|
||||
{
|
||||
$disabled[] = $k;
|
||||
@@ -243,12 +227,6 @@ class _advanced extends \IPS\Dispatcher\Controller
|
||||
$toggles[ $k ][] = $input->htmlId;
|
||||
$cacheConfigurationFields[ $k ][ $inputKey ] = $input->name;
|
||||
}
|
||||
|
||||
/* Deprecated Warnings */
|
||||
if( \in_array( $k, $deprecatedCacheMethods ) )
|
||||
{
|
||||
\IPS\Member::loggedIn()->language()->words["datastore_method_{$k}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'datastore_method_deprecated', FALSE, array( 'sprintf' => array( $k ) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'cache_method', \IPS\CACHE_METHOD, TRUE, array(
|
||||
@@ -497,6 +475,14 @@ class _advanced extends \IPS\Dispatcher\Controller
|
||||
)
|
||||
) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'csp_header', \IPS\Settings::i()->csp_header, FALSE, array( 'placeholder' => "default-src *; frame-ancestors 'self' *.example.com" ), NULL, NULL, NULL, 'csp_header' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'referrer_policy_header', \IPS\Settings::i()->referrer_policy_header, FALSE, array(
|
||||
'options' => array(
|
||||
'0' => 'referrerpolicy_disabled',
|
||||
'1' => 'referrerpolicy_acp_only',
|
||||
'2' => 'referrerpolicy_enabled',
|
||||
)
|
||||
) ) );
|
||||
|
||||
$form->addHeader('performance_settings');
|
||||
$form->add( new \IPS\Helpers\Form\Interval( 'widget_cache_ttl', ( isset( \IPS\Settings::i()->widget_cache_ttl ) ) ? \IPS\Settings::i()->widget_cache_ttl : 60, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::SECONDS, 'min' => 60 ), NULL, \IPS\Member::loggedIn()->language()->addToStack('for'), NULL ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'lazy_load_enabled', \IPS\Settings::i()->lazy_load_enabled, FALSE ) );
|
||||
@@ -761,11 +747,13 @@ class _advanced extends \IPS\Dispatcher\Controller
|
||||
$output = \IPS\Member::loggedIn()->language()->addToStack( $output, FALSE );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->bypassCsrfKeyCheck = true;
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'advancedsettings' )->taskResult( TRUE, $output, $task->id );
|
||||
}
|
||||
}
|
||||
catch ( \IPS\Task\Exception $e )
|
||||
{
|
||||
\IPS\Output::i()->bypassCsrfKeyCheck = true;
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'advancedsettings' )->taskResult( FALSE, \IPS\Member::loggedIn()->language()->addToStack( $e->getMessage(), FALSE ), $task->id );
|
||||
}
|
||||
catch ( \RuntimeException $e )
|
||||
|
||||
@@ -84,11 +84,12 @@ class _contactus extends \IPS\Dispatcher\Controller
|
||||
$disabled = array();
|
||||
$formFields = array();
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'contact_access', ( \IPS\Settings::i()->contact_access == '*' ) ? '*' : explode( ',', \IPS\Settings::i()->contact_access ), FALSE, array(
|
||||
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'contact_access', ( \IPS\Settings::i()->contact_access == '*' ) ? '*' : explode( ',', \IPS\Settings::i()->contact_access ), FALSE, array(
|
||||
'options' => array_combine( array_keys( \IPS\Member\Group::groups() ), array_map( function( $_group ) { return (string) $_group; }, \IPS\Member\Group::groups() ) ),
|
||||
'multiple' => true,
|
||||
'unlimited' => '*',
|
||||
'unlimitedLang' => 'everyone'
|
||||
'unlimitedLang' => 'everyone',
|
||||
'impliedUnlimited' => TRUE
|
||||
), NULL, NULL, NULL, 'contact_access' ) );
|
||||
|
||||
/* Get extensions */
|
||||
|
||||
@@ -102,7 +102,7 @@ class _email extends \IPS\Dispatcher\Controller
|
||||
$settingsForm->add( new \IPS\Helpers\Form\Select( 'smtp_protocol', \IPS\Settings::i()->smtp_protocol, FALSE, array( 'options' => $options ), NULL, NULL, NULL, 'smtp_protocol' ) );
|
||||
$settingsForm->add( new \IPS\Helpers\Form\Number( 'smtp_port', \IPS\Settings::i()->smtp_port, FALSE, array(), NULL, NULL, NULL, 'smtp_port' ) );
|
||||
$settingsForm->add( new \IPS\Helpers\Form\Text( 'smtp_user', \IPS\Settings::i()->smtp_user, FALSE, array(), NULL, NULL, NULL, 'smtp_user' ) );
|
||||
$settingsForm->add( new \IPS\Helpers\Form\Password( 'smtp_pass', \IPS\Settings::i()->smtp_pass, FALSE, array(), NULL, NULL, NULL, 'smtp_pass' ) );
|
||||
$settingsForm->add( new \IPS\Helpers\Form\Password( 'smtp_pass', \IPS\Settings::i()->smtp_pass, FALSE, array( 'enforceMaxLimit' => FALSE ), NULL, NULL, NULL, 'smtp_pass' ) );
|
||||
if ( !\IPS\CIC )
|
||||
{
|
||||
$settingsForm->add( new \IPS\Helpers\Form\Text( 'php_mail_extra', \IPS\Settings::i()->php_mail_extra, FALSE, array(), NULL, NULL, NULL, 'php_mail_extra' ) );
|
||||
@@ -155,6 +155,7 @@ class _email extends \IPS\Dispatcher\Controller
|
||||
|
||||
/* Sent successfully, remove notification */
|
||||
\IPS\core\AdminNotification::remove( 'core', 'ConfigurationError', 'failedMail' );
|
||||
\IPS\Db::i()->update( 'core_mail_error_logs', array( 'mlog_notification_sent' => TRUE ) );
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
@@ -189,6 +190,7 @@ class _email extends \IPS\Dispatcher\Controller
|
||||
|
||||
/* Sent successfully, remove notification */
|
||||
\IPS\core\AdminNotification::remove( 'core', 'ConfigurationError', 'failedMail' );
|
||||
\IPS\Db::i()->update( 'core_mail_error_logs', array( 'mlog_notification_sent' => TRUE ) );
|
||||
|
||||
$messageHtml = \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->message( 'email_test_okay', 'success' );
|
||||
}
|
||||
@@ -363,7 +365,7 @@ class _email extends \IPS\Dispatcher\Controller
|
||||
unset( $emailData['headers']['Content-Type'] );
|
||||
unset( $emailData['headers']['Content-Transfer-Encoding'] );
|
||||
|
||||
$email = \IPS\Email::buildFromContent( $log['mlog_subject'], $emailData['body']['html'], $emailData['body']['plain'], isset( $emailData['type'] ) ? $emailData['type'] : NULL );
|
||||
$email = \IPS\Email::buildFromContent( $log['mlog_subject'], $emailData['body']['html'], $emailData['body']['plain'], isset( $emailData['type'] ) ? $emailData['type'] : \IPS\Email::TYPE_TRANSACTIONAL );
|
||||
$email->_send(
|
||||
array_map( 'trim', explode( ',', $log['mlog_to'] ) ),
|
||||
isset( $emailData['headers']['Cc'] ) ? array_map( 'trim', explode( ',', $emailData['headers']['Cc'] ) ) : array(),
|
||||
@@ -381,9 +383,10 @@ class _email extends \IPS\Dispatcher\Controller
|
||||
\IPS\Db::i()->delete( 'core_mail_error_logs', array( 'mlog_id=?', $id ) );
|
||||
|
||||
/* Remove notification since this was re-sent with success and it is below the recent threshold */
|
||||
if( \IPS\Email::countFailedMail( \IPS\DateTime::create()->sub( new \DateInterval( 'P3D' ), FALSE ), TRUE ) <= 3 )
|
||||
if( \IPS\Email::countFailedMail( \IPS\DateTime::create()->sub( new \DateInterval( 'P3D' ) ), FALSE, TRUE ) <= 3 )
|
||||
{
|
||||
\IPS\core\AdminNotification::remove( 'core', 'ConfigurationError', 'failedMail' );
|
||||
\IPS\Db::i()->update( 'core_mail_error_logs', array( 'mlog_notification_sent' => TRUE ), array( 'mlog_id=?', $id ) );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=settings&controller=email&do=errorLog" ), 'emailerror_resent' );
|
||||
|
||||
@@ -56,6 +56,7 @@ class _general extends \IPS\Dispatcher\Controller
|
||||
$form->add( new \IPS\Helpers\Form\Stack( 'site_social_profiles', \IPS\Settings::i()->site_social_profiles ? json_decode( \IPS\Settings::i()->site_social_profiles, true ) : array(), FALSE, array( 'stackFieldType' => '\IPS\core\Form\SocialProfiles', 'maxItems' => 50, 'key' => array( 'placeholder' => 'http://example.com', 'size' => 20 ) ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'site_twitter_id', \IPS\Settings::i()->site_twitter_id, FALSE, array( 'placeholder' => \IPS\Member::loggedIn()->language()->addToStack('site_twitter_id_placeholder'), 'size' => 20 ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Translatable( 'copyright_line', NULL, FALSE, array( 'app' => 'core', 'key' => 'copyright_line_value', 'placeholder' => \IPS\Member::loggedIn()->language()->addToStack('copyright_line_placeholder') ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'relative_dates_enable', \IPS\Settings::i()->relative_dates_enable, FALSE ) );
|
||||
/* $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',
|
||||
@@ -75,9 +76,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 );
|
||||
|
||||
|
||||
@@ -423,6 +423,12 @@ class _login extends \IPS\Node\Controller
|
||||
{
|
||||
$values['allowed_reg_email'] = implode( ',', $values['allowed_reg_email'] );
|
||||
}
|
||||
|
||||
if ( isset( $values['coppa_address'] ) AND ( $values['coppa_address'] instanceof \IPS\GeoLocation ) )
|
||||
{
|
||||
$values['coppa_address'] = json_encode( $values['coppa_address'] );
|
||||
}
|
||||
|
||||
$form->saveAsSettings( $values );
|
||||
\IPS\Session::i()->log( 'acplogs__registration_settings' );
|
||||
|
||||
|
||||
@@ -197,11 +197,12 @@ class _mfa extends \IPS\Dispatcher\Controller
|
||||
|
||||
$form->addHeader('mfa_header_setup');
|
||||
$groups = array_combine( array_keys( \IPS\Member\Group::groups() ), array_map( function( $_group ) { return (string) $_group; }, \IPS\Member\Group::groups() ) );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'mfa_required_groups', \IPS\Settings::i()->mfa_required_groups == '*' ? '*' : explode( ',', \IPS\Settings::i()->mfa_required_groups ), FALSE, array(
|
||||
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'mfa_required_groups', \IPS\Settings::i()->mfa_required_groups == '*' ? '*' : explode( ',', \IPS\Settings::i()->mfa_required_groups ), FALSE, array(
|
||||
'multiple' => TRUE,
|
||||
'options' => $groups,
|
||||
'unlimited' => '*',
|
||||
'unlimitedLang' => 'everyone',
|
||||
'impliedUnlimited' => TRUE
|
||||
) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'mfa_required_prompt', \IPS\Settings::i()->mfa_required_prompt, FALSE, array(
|
||||
'options' => array(
|
||||
|
||||
@@ -121,7 +121,7 @@ class _posting extends \IPS\Dispatcher\Controller
|
||||
$form->addHeader('posting_images');
|
||||
$form->add( new \IPS\Helpers\Form\WidthHeight( 'attachment_resample_size', \IPS\Settings::i()->attachment_resample_size ? explode( 'x', \IPS\Settings::i()->attachment_resample_size ) : array( 0, 0 ), FALSE, array( 'resizableDiv' => FALSE, 'unlimited' => array( 0, 0 ) ), NULL, NULL, NULL, 'attachment_resample_size' ) );
|
||||
$current = ( isset( \IPS\Settings::i()->attachment_image_size ) ) ? explode( 'x', \IPS\Settings::i()->attachment_image_size ) : array( 1000, 750 );
|
||||
$form->add( new \IPS\Helpers\Form\WidthHeight( 'attachment_image_size', $current, FALSE, array( 'resizableDiv' => FALSE ), NULL, NULL, NULL, 'attachment_image_size' ) );
|
||||
$form->add( new \IPS\Helpers\Form\WidthHeight( 'attachment_image_size', $current, FALSE, array( 'resizableDiv' => FALSE, 'unlimited' => array( 0, 0 ) ), NULL, NULL, NULL, 'attachment_image_size' ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'editor_embeds', \IPS\Settings::i()->editor_embeds, FALSE, array( 'togglesOn' => array( 'max_video_width', 'max_internalembed_width' ) ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Number( 'max_video_width', \IPS\Settings::i()->max_video_width, FALSE, array( 'unlimited' => 0 ), NULL, NULL, 'px', 'max_video_width' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Number( 'max_internalembed_width', \IPS\Settings::i()->max_internalembed_width, FALSE, array( 'unlimited' => 0 ), NULL, NULL, 'px', 'max_internalembed_width' ) );
|
||||
@@ -130,6 +130,10 @@ class _posting extends \IPS\Dispatcher\Controller
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'allow_remote_images', \IPS\Settings::i()->allow_remote_images, FALSE, array( 'togglesOn' => array( 'allow_only_https_remote_images' ) ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'allow_only_https_remote_images', \IPS\Settings::i()->allow_only_https_remote_images, FALSE, array(), NULL, NULL, NULL, 'allow_only_https_remote_images' ) );
|
||||
|
||||
$form->addHeader('post_settings_embed');
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'fb_ig_oembed_appid', \IPS\Settings::i()->fb_ig_oembed_appid, FALSE ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'fb_ig_oembed_appsecret', \IPS\Settings::i()->fb_ig_oembed_appsecret, FALSE ) );
|
||||
|
||||
$form->addHeader('posting_content');
|
||||
if ( \IPS\Login::registrationType() != 'disabled' )
|
||||
{
|
||||
@@ -193,8 +197,8 @@ class _posting extends \IPS\Dispatcher\Controller
|
||||
|
||||
$clearCss = ( $values['max_video_width'] !== \IPS\Settings::i()->max_video_width );
|
||||
|
||||
$values['attachment_resample_size'] = implode( 'x', $values['attachment_resample_size'] );
|
||||
$values['attachment_image_size'] = implode( 'x', $values['attachment_image_size'] );
|
||||
$values['attachment_resample_size'] = implode( 'x', $values['attachment_resample_size'] );
|
||||
$values['attachment_image_size'] = implode( 'x', $values['attachment_image_size'] );
|
||||
$values['attach_allowed_extensions'] = \is_array( $values['attach_allowed_extensions'] ) ? implode( ',', array_unique( array_map( function( $val )
|
||||
{
|
||||
return ltrim( $val, '.' );
|
||||
@@ -539,7 +543,8 @@ class _posting extends \IPS\Dispatcher\Controller
|
||||
'type' => $values['profanity_type'],
|
||||
'swop' => $values['profanity_swop'],
|
||||
'm_exact' => $values['profanity_m_exact'],
|
||||
'action' => $values['profanity_action']
|
||||
'action' => $values['profanity_action'],
|
||||
'min_posts' => $values['profanity_min_posts']
|
||||
);
|
||||
|
||||
\IPS\core\Profanity::createFromForm( $save, $current );
|
||||
@@ -631,8 +636,7 @@ class _posting extends \IPS\Dispatcher\Controller
|
||||
$data = \intval( $data );
|
||||
|
||||
/* Open XML file */
|
||||
$xml = new \IPS\Xml\XMLReader;
|
||||
$xml->open( \IPS\Request::i()->file );
|
||||
$xml = \IPS\Xml\XMLReader::safeOpen( \IPS\Request::i()->file );
|
||||
$xml->read(); //badwordexport
|
||||
$xml->read(); //badwordexport
|
||||
$xml->read(); //badwordgroup
|
||||
@@ -781,4 +785,35 @@ class _posting extends \IPS\Dispatcher\Controller
|
||||
/* And redirect */
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=settings&controller=posting" ), 'thumbnails_rebuilt' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild URL refs
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function rebuildUrlRels()
|
||||
{
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
|
||||
/* Remove any existing rebuilds */
|
||||
\IPS\Db::i()->delete( 'core_queue', \IPS\Db::i()->in( '`key`', array( 'RebuildUrlRels' ) ) );
|
||||
|
||||
/* Unset task datastore */
|
||||
unset( \IPS\Data\Store::i()->currentUrlRefRebuild );
|
||||
|
||||
foreach ( \IPS\Content::routedClasses( FALSE, TRUE ) as $class )
|
||||
{
|
||||
if( isset( $class::$databaseColumnMap['content'] ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
\IPS\Task::queue( 'core', 'RebuildUrlRels', array( 'class' => $class ), 4 );
|
||||
}
|
||||
catch( \OutOfRangeException $ex ) { }
|
||||
}
|
||||
}
|
||||
|
||||
/* And redirect */
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=settings&controller=posting" ), 'refurls_rebuilt' );
|
||||
}
|
||||
}
|
||||
@@ -67,7 +67,7 @@ class _securityquestions extends \IPS\Node\Controller
|
||||
* @param string $lastUsedTab The tab last used in the form
|
||||
* @return void
|
||||
*/
|
||||
protected function _afterSave( \IPS\Node\Model $old = NULL, \IPS\Node\Model $new, $lastUsedTab = FALSE )
|
||||
protected function _afterSave( ?\IPS\Node\Model $old, \IPS\Node\Model $new, $lastUsedTab = FALSE )
|
||||
{
|
||||
if( \IPS\Request::i()->isAjax() )
|
||||
{
|
||||
|
||||
@@ -259,7 +259,17 @@ class _admin extends \IPS\Dispatcher\Controller
|
||||
'row_updated' => time()
|
||||
);
|
||||
\IPS\Db::i()->insert( 'core_admin_permission_rows', $current );
|
||||
\IPS\Session::i()->log( 'acplog__acpr_created', array( ( $values['acprestrictions_type'] == 'group' ? \IPS\Member\Group::load( $values['acprestrictions_group'] )->name : $values['acprestrictions_member']->name ) => FALSE ) );
|
||||
|
||||
if( $values['acprestrictions_type'] == 'group' )
|
||||
{
|
||||
$logValue = array( 'core_group_' . $values['acprestrictions_group'] => TRUE );
|
||||
}
|
||||
else
|
||||
{
|
||||
$logValue = array( $values['acprestrictions_member']->name => FALSE );
|
||||
}
|
||||
|
||||
\IPS\Session::i()->log( 'acplog__acpr_created', $logValue );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -316,24 +326,37 @@ class _admin extends \IPS\Dispatcher\Controller
|
||||
$restrictions = array();
|
||||
foreach ( \IPS\Application::applications() as $app )
|
||||
{
|
||||
$restrictions['applications'][ $app->directory ] = $app->id;
|
||||
|
||||
$_restrictions = array();
|
||||
$file = \IPS\ROOT_PATH . "/applications/{$app->directory}/data/acprestrictions.json";
|
||||
if ( file_exists( $file ) )
|
||||
{
|
||||
$_restrictions = json_decode( file_get_contents( $file ), TRUE );
|
||||
}
|
||||
|
||||
foreach ( $app->modules( 'admin' ) as $module )
|
||||
{
|
||||
if ( !$module->protected )
|
||||
if ( !\IPS\CIC or !\in_array( "{$app->directory}", \IPS\Member::$cicBlockedAcpRestrictions ) )
|
||||
{
|
||||
$restrictions['applications'][ $app->directory ] = $app->id;
|
||||
|
||||
$_restrictions = array();
|
||||
$file = $app->getApplicationPath() . '/data/acprestrictions.json';
|
||||
|
||||
if ( file_exists( $file ) )
|
||||
{
|
||||
$restrictions['modules'][ $app->id ][ $module->key ] = $module->id;
|
||||
|
||||
if ( isset( $_restrictions[ $module->key ] ) )
|
||||
$_restrictions = json_decode( file_get_contents( $file ), TRUE );
|
||||
}
|
||||
|
||||
foreach ( $app->modules( 'admin' ) as $module )
|
||||
{
|
||||
if ( !$module->protected and ( !\IPS\CIC or !\in_array( "{$app->directory}.{$module->key}", \IPS\Member::$cicBlockedAcpRestrictions ) ) )
|
||||
{
|
||||
$restrictions['items'][ $module->id ] = $_restrictions[ $module->key ];
|
||||
$restrictions['modules'][ $app->id ][ $module->key ] = $module->id;
|
||||
|
||||
if ( isset( $_restrictions[ $module->key ] ) )
|
||||
{
|
||||
foreach ( $_restrictions[ $module->key ] as $group => $items )
|
||||
{
|
||||
foreach ( $items as $restrictionKey => $langKey )
|
||||
{
|
||||
if ( !\IPS\CIC or !\in_array( "{$app->directory}.{$module->key}.{$restrictionKey}", \IPS\Member::$cicBlockedAcpRestrictions ) )
|
||||
{
|
||||
$restrictions['items'][ $module->id ][ $group ][ $restrictionKey ] = $langKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -369,7 +392,16 @@ class _admin extends \IPS\Dispatcher\Controller
|
||||
unset( \IPS\Data\Store::i()->administrators );
|
||||
|
||||
/* Log */
|
||||
\IPS\Session::i()->log( 'acplog__acpr_edited', array( ( $current['row_id_type'] == 'group' ? \IPS\Member\Group::load( $current['row_id'] )->name : \IPS\Member::load( $current['row_id'] )->name ) => FALSE ) );
|
||||
if( $current['row_id_type'] == 'group' )
|
||||
{
|
||||
$logValue = array( 'core_group_' . $current['row_id'] => TRUE );
|
||||
}
|
||||
else
|
||||
{
|
||||
$logValue = array( \IPS\Member::load( $current['row_id'] )->name => FALSE );
|
||||
}
|
||||
|
||||
\IPS\Session::i()->log( 'acplog__acpr_edited', $logValue );
|
||||
|
||||
/* Boink */
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=staff&controller=admin' ), 'saved' );
|
||||
@@ -400,12 +432,14 @@ class _admin extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
try
|
||||
{
|
||||
$name = \IPS\Member\Group::load( $current['row_id'] )->name;
|
||||
$name = 'core_group_' . $current['row_id'];
|
||||
}
|
||||
catch( \OutOfRangeException $e )
|
||||
{
|
||||
$name = \IPS\Member::loggedIn()->language()->get('deleted_group');
|
||||
$name = 'deleted_group';
|
||||
}
|
||||
|
||||
$logValue = array( $name => TRUE );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -413,15 +447,16 @@ class _admin extends \IPS\Dispatcher\Controller
|
||||
|
||||
if( $member->member_id )
|
||||
{
|
||||
$name = $member->name;
|
||||
$logValue = array( $member->name => FALSE );
|
||||
}
|
||||
else
|
||||
{
|
||||
$name = \IPS\Member::loggedIn()->language()->get('deleted_member');
|
||||
$logValue = array( 'deleted_member' => TRUE );
|
||||
}
|
||||
}
|
||||
|
||||
\IPS\Session::i()->log( 'acplog__acpr_deleted', array( $name => FALSE ) );
|
||||
\IPS\Session::i()->log( 'acplog__acpr_deleted', $logValue );
|
||||
|
||||
\IPS\Db::i()->delete( 'core_admin_permission_rows', array( 'row_id=? AND row_id_type=?', \intval( \IPS\Request::i()->id ), \IPS\Request::i()->type ) );
|
||||
unset ( \IPS\Data\Store::i()->administrators );
|
||||
|
||||
|
||||
@@ -224,8 +224,17 @@ class _moderators extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
$ext->onChange( $current, $values );
|
||||
}
|
||||
|
||||
\IPS\Session::i()->log( 'acplog__moderator_created', array( ( $values['moderators_type'] == 'g' ? \IPS\Member::loggedIn()->language()->get( "core_group_{$values['moderators_group']}" ) : $values['moderators_member']->name ) => FALSE ) );
|
||||
|
||||
if( $values['moderators_type'] == 'g' )
|
||||
{
|
||||
$logValue = array( 'core_group_' . $values['moderators_group'] => TRUE );
|
||||
}
|
||||
else
|
||||
{
|
||||
$logValue = array( $values['moderators_member']->name => FALSE );
|
||||
}
|
||||
|
||||
\IPS\Session::i()->log( 'acplog__moderator_created', $logValue );
|
||||
|
||||
unset (\IPS\Data\Store::i()->moderators);
|
||||
}
|
||||
@@ -481,8 +490,18 @@ class _moderators extends \IPS\Dispatcher\Controller
|
||||
$ext->onChange( $current, $changed );
|
||||
}
|
||||
}
|
||||
|
||||
\IPS\Session::i()->log( 'acplog__moderator_edited', array( ( $current['type'] == 'g' ? \IPS\Member::loggedIn()->language()->get( "core_group_{$current['id']}" ) : \IPS\Member::load( $current['id'] )->name ) => FALSE ) );
|
||||
|
||||
if( $current['type'] == 'g' )
|
||||
{
|
||||
$logValue = array( 'core_group_' . $current['id'] => TRUE );
|
||||
}
|
||||
else
|
||||
{
|
||||
$logValue = array( \IPS\Member::load( $current['id'] )->name => FALSE );
|
||||
}
|
||||
|
||||
\IPS\Session::i()->log( 'acplog__moderator_edited', $logValue );
|
||||
|
||||
$currentPermissions = $values;
|
||||
|
||||
unset( \IPS\Data\Store::i()->moderators );
|
||||
@@ -536,7 +555,35 @@ class _moderators extends \IPS\Dispatcher\Controller
|
||||
unset (\IPS\Data\Store::i()->moderators);
|
||||
|
||||
/* Log and redirect */
|
||||
\IPS\Session::i()->log( 'acplog__moderator_deleted', array( ( $current['type'] == 'g' ? \IPS\Member::loggedIn()->language()->get( "core_group_{$current['id']}" ) : \IPS\Member::load( $current['id'] )->name ) => FALSE ) );
|
||||
if( $current['type'] == 'g' )
|
||||
{
|
||||
try
|
||||
{
|
||||
$name = 'core_group_' . $current['id'];
|
||||
}
|
||||
catch( \OutOfRangeException $e )
|
||||
{
|
||||
$name = 'deleted_group';
|
||||
}
|
||||
|
||||
$logValue = array( $name => TRUE );
|
||||
}
|
||||
else
|
||||
{
|
||||
$member = \IPS\Member::load( $current['id'] );
|
||||
|
||||
if( $member->member_id )
|
||||
{
|
||||
$logValue = array( $member->name => FALSE );
|
||||
}
|
||||
else
|
||||
{
|
||||
$logValue = array( 'deleted_member' => TRUE );
|
||||
}
|
||||
}
|
||||
|
||||
\IPS\Session::i()->log( 'acplog__moderator_deleted', $logValue );
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=staff&controller=moderators' ) );
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief badges
|
||||
* @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 Mar 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* badges
|
||||
*/
|
||||
class _badges extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
/**
|
||||
* @brief Allow MySQL RW separation for efficiency
|
||||
*/
|
||||
public static $allowRWSeparation = TRUE;
|
||||
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'overview_manage' );
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Points 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' );
|
||||
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_badges_title');
|
||||
$chart->availableTypes = array( 'AreaChart', 'ColumnChart', 'BarChart' );
|
||||
$chart->enableHourly = FALSE;
|
||||
|
||||
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('stats_member_badges'), 'number', 'COUNT(*)', TRUE );
|
||||
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_badges_title');
|
||||
$chart->availableTypes = array( 'AreaChart', 'ColumnChart', 'BarChart' );
|
||||
$chart->showIntervals = TRUE;
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_stats_badges');
|
||||
\IPS\Output::i()->output = (string) $chart;
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,11 @@ class _deviceusage extends \IPS\Dispatcher\Controller
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Allow MySQL RW separation for efficiency
|
||||
*/
|
||||
public static $allowRWSeparation = TRUE;
|
||||
|
||||
/**
|
||||
* Execute
|
||||
|
||||
@@ -26,6 +26,11 @@ class _membersactivity extends \IPS\Dispatcher\Controller
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Allow MySQL RW separation for efficiency
|
||||
*/
|
||||
public static $allowRWSeparation = TRUE;
|
||||
|
||||
/**
|
||||
* Execute
|
||||
|
||||
@@ -26,6 +26,11 @@ class _membervisits extends \IPS\Dispatcher\Controller
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Allow MySQL RW separation for efficiency
|
||||
*/
|
||||
public static $allowRWSeparation = TRUE;
|
||||
|
||||
/**
|
||||
* Execute
|
||||
|
||||
@@ -26,6 +26,11 @@ class _onlineusers extends \IPS\Dispatcher\Controller
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Allow MySQL RW separation for efficiency
|
||||
*/
|
||||
public static $allowRWSeparation = TRUE;
|
||||
|
||||
/**
|
||||
* Execute
|
||||
|
||||
@@ -27,6 +27,11 @@ class _overview extends \IPS\Dispatcher\Controller
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Allow MySQL RW separation for efficiency
|
||||
*/
|
||||
public static $allowRWSeparation = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Date range to restrict to, or NULL for no restriction
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief points
|
||||
* @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 Mar 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* points
|
||||
*/
|
||||
class _points extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
/**
|
||||
* @brief Allow MySQL RW separation for efficiency
|
||||
*/
|
||||
public static $allowRWSeparation = TRUE;
|
||||
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'overview_manage' );
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Points earned activity chart
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
$chart = new \IPS\Helpers\Chart\Database( \IPS\Http\Url::internal( 'app=core&module=stats&controller=points' ), 'core_points_log', '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' );
|
||||
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_points_title');
|
||||
$chart->availableTypes = array( 'AreaChart', 'ColumnChart', 'BarChart' );
|
||||
$chart->enableHourly = FALSE;
|
||||
|
||||
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('stats_member_points'), 'number', 'SUM(points)', TRUE );
|
||||
|
||||
$chart->title = \IPS\Member::loggedIn()->language()->addToStack('stats_points_title');
|
||||
$chart->availableTypes = array( 'AreaChart', 'ColumnChart', 'BarChart' );
|
||||
$chart->showIntervals = TRUE;
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_stats_points');
|
||||
\IPS\Output::i()->output = (string) $chart;
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,11 @@ class _registrationstats extends \IPS\Dispatcher\Controller
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Allow MySQL RW separation for efficiency
|
||||
*/
|
||||
public static $allowRWSeparation = TRUE;
|
||||
|
||||
/**
|
||||
* Manage Members
|
||||
|
||||
@@ -40,13 +40,13 @@ class _hookedFiles extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* ...
|
||||
* Show all third party hooks
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
$table = new \IPS\Helpers\Table\Db( 'core_hooks', \IPS\Http\Url::internal( 'app=core&module=support&controller=hookedFiles' ) );
|
||||
$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->langPrefix = 'hooks_';
|
||||
$table->quickSearch = array( 'class', 'hook_class' );
|
||||
$table->include = array( 'hooks_app', 'hooks_plugin', 'hooks_type', 'hooks_class', 'hooks_file' );
|
||||
@@ -104,7 +104,7 @@ class _hookedFiles extends \IPS\Dispatcher\Controller
|
||||
);
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'hooked_classes' );
|
||||
\IPS\Output::i()->output = $table;
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'support' )->hookedClasses( $table );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Display phpinfo
|
||||
* @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 4 December 2020
|
||||
*/
|
||||
|
||||
namespace IPS\core\modules\admin\support;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display phpinfo
|
||||
*/
|
||||
class _phpinfo extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
/**
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'get_support' );
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Support Wizard
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
phpinfo();
|
||||
exit;
|
||||
}
|
||||
}
|
||||
@@ -88,12 +88,14 @@ class _redis extends \IPS\Dispatcher\Controller
|
||||
if ( \IPS\CACHE_METHOD == 'Redis' and \IPS\REDIS_ENABLED )
|
||||
{
|
||||
$datasource[] = array( 'type' => 'redis_sessions', 'count' => \count( \IPS\Redis::i()->debugGetKeys( 'session_id_*', TRUE ) ), 'enabled' => true );
|
||||
$datasource[] = array( 'type' => 'redis_topic_views', 'count' => \IPS\Redis::i()->zSize('topic_views'), 'enabled' => true );
|
||||
$datasource[] = array( 'type' => 'redis_topic_views', 'count' => \IPS\Redis::i()->zCard('topic_views'), 'enabled' => true );
|
||||
$datasource[] = array( 'type' => 'redis_advert_impressions', 'count' => \IPS\Redis::i()->zCard('advert_impressions'), 'enabled' => true );
|
||||
}
|
||||
else
|
||||
{
|
||||
$datasource[] = array( 'type' => 'redis_sessions' , 'count' => NULL, 'enabled' => false );
|
||||
$datasource[] = array( 'type' => 'redis_topic_views', 'count' => NULL, 'enabled' => false );
|
||||
$datasource[] = array( 'type' => 'redis_advert_impressions', 'count' => NULL, 'enabled' => false );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,6 +131,6 @@ class _redis extends \IPS\Dispatcher\Controller
|
||||
);
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('redis_info');
|
||||
\IPS\Output::i()->output = \IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'support' )->redis( $info, $table );
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'support' )->redis( $info, $table );
|
||||
}
|
||||
}
|
||||
@@ -82,7 +82,7 @@ class _sql extends \IPS\Dispatcher\Controller
|
||||
'view' => array(
|
||||
'icon' => 'search',
|
||||
'title' => 'view',
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=support&controller=sql&do=query' )->setQueryString( 'q', "SELECT * FROM {$row['name']}" )->csrf()
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=support&controller=sql&do=query&action=view' )->setQueryString( 'table', $row['name'] )
|
||||
),
|
||||
);
|
||||
};
|
||||
@@ -103,22 +103,31 @@ class _sql extends \IPS\Dispatcher\Controller
|
||||
*/
|
||||
protected function query()
|
||||
{
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
|
||||
/* Select from table */
|
||||
$query = NULL;
|
||||
if ( isset( \IPS\Request::i()->q ) )
|
||||
$table = NULL;
|
||||
if ( isset( \IPS\Request::i()->table ) AND isset( \IPS\Request::i()->action ) )
|
||||
{
|
||||
$query = urldecode( \IPS\Request::i()->q );
|
||||
$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 */
|
||||
@@ -159,13 +168,21 @@ class _sql extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
$page = isset( \IPS\Request::i()->page ) ? \intval( \IPS\Request::i()->page ) : 1;
|
||||
|
||||
if( $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(
|
||||
\IPS\Http\Url::internal( 'app=core&module=support&controller=sql&do=query' )->setQueryString( 'q', $query )->csrf(),
|
||||
$paginationUrl,
|
||||
ceil( $result->num_rows / static::SELECT_LIMIT ),
|
||||
$page,
|
||||
static::SELECT_LIMIT
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -104,7 +104,7 @@ class _utf8mb4 extends \IPS\Dispatcher\Controller
|
||||
return $wizardData;
|
||||
}
|
||||
|
||||
$baseUrl = \IPS\Http\Url::internal( 'app=core&module=support&controller=utf8mb4' );
|
||||
$baseUrl = \IPS\Http\Url::internal( 'app=core&module=support&controller=utf8mb4' )->csrf();
|
||||
|
||||
return new \IPS\Helpers\MultipleRedirect( $baseUrl,
|
||||
function( $mrData )
|
||||
@@ -197,7 +197,7 @@ class _utf8mb4 extends \IPS\Dispatcher\Controller
|
||||
$length += $tableDefinition['columns'][ $column ]['length'];
|
||||
}
|
||||
|
||||
if( $tableDefinition['columns'][ $column ]['collation'] !== 'utf8mb4_unicode_ci' )
|
||||
if( isset( $tableDefinition['columns'][ $column ]['collation'] ) AND $tableDefinition['columns'][ $column ]['collation'] !== 'utf8mb4_unicode_ci' )
|
||||
{
|
||||
$needToRecreate = TRUE;
|
||||
}
|
||||
@@ -244,7 +244,7 @@ class _utf8mb4 extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
|
||||
/* Now if we have any changes to make....make them */
|
||||
if( count( $aggregatedChanges ) )
|
||||
if( \count( $aggregatedChanges ) )
|
||||
{
|
||||
\IPS\Db::i()->query( "ALTER TABLE " . \IPS\Db::i()->prefix . \IPS\Db::i()->escape_string( $table ) . " " . implode( ', ', $aggregatedChanges ) );
|
||||
}
|
||||
|
||||
@@ -37,6 +37,11 @@ class _login extends \IPS\Dispatcher\Controller
|
||||
/* Do we have an unfinished upgrade? */
|
||||
if ( !\IPS\IN_DEV and ( \IPS\RECOVERY_MODE !== TRUE OR !isset( \IPS\Request::i()->noWarning ) ) and \IPS\Settings::i()->setup_in_progress )
|
||||
{
|
||||
/* Don't allow the upgrade in progress page to be cached, it will only be displayed for a very short period of time */
|
||||
foreach( \IPS\Output::getNoCacheHeaders() as $headerKey => $headerValue )
|
||||
{
|
||||
header( "{$headerKey}: {$headerValue}" );
|
||||
}
|
||||
include( \IPS\ROOT_PATH . '/' . \IPS\CP_DIRECTORY . '/upgrade/upgradeStarted.php' );
|
||||
session_abort();
|
||||
exit;
|
||||
@@ -285,5 +290,14 @@ class _login extends \IPS\Dispatcher\Controller
|
||||
|
||||
\IPS\Db::i()->insert( 'core_admin_login_logs', $save );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return current CSRF token
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getCsrfKey(): ?string
|
||||
{
|
||||
\IPS\Output::i()->json( [ 'key' => \IPS\Session::i()->csrfKey ] );
|
||||
}
|
||||
}
|
||||
@@ -64,7 +64,6 @@ class _upgrade extends \IPS\Dispatcher\Controller
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'upgrade_manage', 'core', 'overview' );
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('ips_suite_upgrade');
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::external( "https://ipbmafia.ru/files/file/2342-invision-community-nulled/" ) );
|
||||
}
|
||||
|
||||
@@ -108,6 +107,13 @@ class _upgrade extends \IPS\Dispatcher\Controller
|
||||
$coreApp->update_version = json_encode( $response );
|
||||
$coreApp->update_last_check = time();
|
||||
$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'] )
|
||||
{
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('system')->upgradeManagedContactUs( \IPS\Cicloud\managedSupportEmail() );
|
||||
\IPS\Dispatcher::i()->finish();
|
||||
}
|
||||
}
|
||||
catch ( \Exception $e ) { }
|
||||
|
||||
@@ -771,8 +777,10 @@ class _upgrade extends \IPS\Dispatcher\Controller
|
||||
\fwrite( $fh, (string) $response );
|
||||
\fclose( $fh );
|
||||
|
||||
$mrData['range'] = $endRange + 1;
|
||||
return array( $mrData, \IPS\Member::loggedIn()->language()->addToStack('delta_upgrade_downloading'), 100 / $totalFileSize * $mrData['range'] );
|
||||
$mrData['range'] = $endRange + 1;
|
||||
$complete = 100 / $totalFileSize * $mrData['range'];
|
||||
|
||||
return array( $mrData, \IPS\Member::loggedIn()->language()->addToStack('delta_upgrade_downloading'), ( $complete > 100 ) ? 100 : $complete );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -929,7 +937,7 @@ class _upgrade extends \IPS\Dispatcher\Controller
|
||||
\IPS\core\Setup\Upgrade::setUpgradingFlag( FALSE );
|
||||
|
||||
$databaseErrors = FALSE;
|
||||
foreach ( \IPS\Application::enabledApplications() as $app )
|
||||
foreach ( \IPS\Application::applications() as $app )
|
||||
{
|
||||
if( \in_array( $app->directory, \IPS\IPS::$ipsApps ) )
|
||||
{
|
||||
@@ -940,14 +948,20 @@ class _upgrade extends \IPS\Dispatcher\Controller
|
||||
$latestHVersion = array_pop( $humanVersions );
|
||||
\IPS\Db::i()->update( 'core_applications', array( 'app_version' => $latestHVersion, 'app_long_version' => $latestLVersion ), array( 'app_directory=?', $app->directory ) );
|
||||
\IPS\Db::i()->insert( 'core_upgrade_history', array( 'upgrade_version_human' => $latestHVersion, 'upgrade_version_id' => $latestLVersion, 'upgrade_date' => time(), 'upgrade_mid' => (int) \IPS\Member::loggedIn()->member_id, 'upgrade_app' => $app->directory ) );
|
||||
|
||||
|
||||
if( !$app->enabled )
|
||||
{
|
||||
// Do not run the database check if the app is disabled.
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( $app->databaseCheck() )
|
||||
{
|
||||
$databaseErrors = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
unset( \IPS\Data\Store::i()->applications );
|
||||
unset( \IPS\Data\Store::i()->applications, \IPS\Data\Store::i()->updatecount_applications );
|
||||
|
||||
\IPS\core\AdminNotification::remove( 'core', 'NewVersion' );
|
||||
|
||||
@@ -1134,8 +1148,7 @@ class _upgrade extends \IPS\Dispatcher\Controller
|
||||
$perLoop = 150;
|
||||
$i = 0;
|
||||
$done = 0;
|
||||
$xml = new \IPS\Xml\XMLReader;
|
||||
$xml->open( \IPS\ROOT_PATH . "/applications/{$app}/data/theme.xml" );
|
||||
$xml = \IPS\Xml\XMLReader::safeOpen( \IPS\ROOT_PATH . "/applications/{$app}/data/theme.xml" );
|
||||
$xml->read();
|
||||
while ( $xml->read() )
|
||||
{
|
||||
@@ -1321,8 +1334,7 @@ class _upgrade extends \IPS\Dispatcher\Controller
|
||||
$i = 0;
|
||||
$done = 0;
|
||||
$inserts = array();
|
||||
$xml = new \IPS\Xml\XMLReader;
|
||||
$xml->open( \IPS\ROOT_PATH . "/applications/{$app}/data/lang.xml" );
|
||||
$xml = \IPS\Xml\XMLReader::safeOpen( \IPS\ROOT_PATH . "/applications/{$app}/data/lang.xml" );
|
||||
$xml->read();
|
||||
$xml->read();
|
||||
$xml->read();
|
||||
@@ -1455,8 +1467,7 @@ class _upgrade extends \IPS\Dispatcher\Controller
|
||||
$perLoop = 150;
|
||||
$i = 0;
|
||||
$done = 0;
|
||||
$xml = new \IPS\Xml\XMLReader;
|
||||
$xml->open( \IPS\ROOT_PATH . "/applications/{$app}/data/javascript.xml" );
|
||||
$xml = \IPS\Xml\XMLReader::safeOpen( \IPS\ROOT_PATH . "/applications/{$app}/data/javascript.xml" );
|
||||
$xml->read();
|
||||
while ( $xml->read() )
|
||||
{
|
||||
@@ -1575,4 +1586,15 @@ class _upgrade extends \IPS\Dispatcher\Controller
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the database checker
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function databaseChecker()
|
||||
{
|
||||
/* We can re-use the method used for the support section, however we need to pass a different bool */
|
||||
( new \IPS\core\modules\admin\support\support )->_databaseChecker( TRUE );
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user