Version 4.7.23
This commit is contained in:
1 parent
7124a02564
commit
25ddeb65d6
1791 files changed
+76990
-44452
No files matched your search
@@ -55,20 +55,25 @@ class _ranks extends \IPS\Node\Controller
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
static::$counts = [];
|
||||
$previousCount = NULL;
|
||||
|
||||
$allRanks = array_values( \IPS\core\Achievements\Rank::getStore() );
|
||||
$totalRanks = \count( $allRanks );
|
||||
for ( $i = 0; $i < $totalRanks; $i++ )
|
||||
/* If the members table is very large, this can take the whole thing down,
|
||||
so let's skip it. */
|
||||
if( !\IPS\Db::i()->recommendManualQuery( 'core_members' ) )
|
||||
{
|
||||
$where = [];
|
||||
$where[] = [ 'achievements_points>=' . \intval( $allRanks[ $i ]->points ) ];
|
||||
if ( isset( $allRanks[ $i + 1 ] ) )
|
||||
static::$counts = [];
|
||||
$previousCount = NULL;
|
||||
|
||||
$allRanks = array_values( \IPS\core\Achievements\Rank::getStore() );
|
||||
$totalRanks = \count( $allRanks );
|
||||
for ( $i = 0; $i < $totalRanks; $i++ )
|
||||
{
|
||||
$where[] = [ 'achievements_points<' . \intval( $allRanks[ $i + 1 ]->points ) ];
|
||||
$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();
|
||||
}
|
||||
static::$counts[ $allRanks[ $i ]->id ] = \IPS\Db::i()->select( 'COUNT(*)', 'core_members', $where )->first();
|
||||
}
|
||||
|
||||
if( $data = \IPS\core\Achievements\Rule::getRebuildProgress() )
|
||||
@@ -215,6 +220,12 @@ class _ranks extends \IPS\Node\Controller
|
||||
*/
|
||||
public function _getRowHtml( $node )
|
||||
{
|
||||
/* If the counts are empty, then the table was too large, so ignore this. */
|
||||
if( empty( static::$counts ) )
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
$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 ] );
|
||||
|
||||
@@ -37,60 +37,68 @@ class _api extends \IPS\Dispatcher\Controller
|
||||
public function __call( $method, $args )
|
||||
{
|
||||
\IPS\Output::i()->responsive = FALSE;
|
||||
|
||||
/* Check htaccess is correct */
|
||||
if ( \IPS\Settings::i()->use_friendly_urls and \IPS\Settings::i()->htaccess_mod_rewrite OR \IPS\Request::i()->fromZapier )
|
||||
|
||||
/* We don't need to test Cloud */
|
||||
if( !\IPS\CIC )
|
||||
{
|
||||
$url = \IPS\Http\Url::external( rtrim( \IPS\Settings::i()->base_url, '/' ) . '/api/core/hello' );
|
||||
}
|
||||
else
|
||||
{
|
||||
$url = \IPS\Http\Url::external( rtrim( \IPS\Settings::i()->base_url, '/' ) . '/api/index.php?/core/hello' );
|
||||
}
|
||||
try
|
||||
{
|
||||
if ( \IPS\Request::i()->isCgi() )
|
||||
/* Check htaccess is correct */
|
||||
if ( \IPS\Settings::i()->use_friendly_urls and \IPS\Settings::i()->htaccess_mod_rewrite or \IPS\Request::i()->fromZapier )
|
||||
{
|
||||
$response = $url->setQueryString( 'key', 'test' )->request()->get();
|
||||
$url = \IPS\Http\Url::external( rtrim( \IPS\Settings::i()->base_url, '/' ) . '/api/core/hello' );
|
||||
}
|
||||
else
|
||||
{
|
||||
$response = $url->request()->login( 'test', '' )->get();
|
||||
$url = \IPS\Http\Url::external( rtrim( \IPS\Settings::i()->base_url, '/' ) . '/api/index.php?/core/hello' );
|
||||
}
|
||||
|
||||
$response = $response->decodeJson();
|
||||
|
||||
if( isset( $response['errorMessage'] ) )
|
||||
try
|
||||
{
|
||||
if ( $response['errorMessage'] === 'IP_ADDRESS_BANNED' )
|
||||
if ( \IPS\Request::i()->isCgi() )
|
||||
{
|
||||
\IPS\Output::i()->error( 'api_blocked_self', '3C402/1', 500, '' );
|
||||
$response = $url->setQueryString( 'key', 'test' )->request()->get();
|
||||
}
|
||||
else
|
||||
{
|
||||
$response = $url->request()->login( 'test', '' )->get();
|
||||
}
|
||||
|
||||
if ( $response['errorMessage'] !== 'INVALID_API_KEY' AND $response['errorMessage'] !== 'TOO_MANY_REQUESTS_WITH_BAD_KEY' )
|
||||
$response = $response->decodeJson();
|
||||
|
||||
if ( isset( $response['errorMessage'] ) )
|
||||
{
|
||||
/* Is it being blocked by a file? */
|
||||
if( @\file_exists( \IPS\ROOT_PATH . '/api/core' ) )
|
||||
if ( $response['errorMessage'] === 'IP_ADDRESS_BANNED' )
|
||||
{
|
||||
\IPS\Output::i()->error( \IPS\Member::loggedIn()->language()->addToStack( 'api_blocked_file', FALSE, array( 'sprintf' => array( \IPS\ROOT_PATH ) ) ), '3C402/2', 500, '' );
|
||||
\IPS\Output::i()->error( 'api_blocked_self', '3C402/1', 500, '' );
|
||||
}
|
||||
|
||||
/* Plain not working, so bubble up. */
|
||||
throw new \Exception($response['errorMessage']);
|
||||
if ( $response['errorMessage'] !== 'INVALID_API_KEY' and $response['errorMessage'] !== 'TOO_MANY_REQUESTS_WITH_BAD_KEY' )
|
||||
{
|
||||
/* Is it being blocked by a file? */
|
||||
if ( @\file_exists( \IPS\ROOT_PATH . '/api/core' ) )
|
||||
{
|
||||
\IPS\Output::i()->error( \IPS\Member::loggedIn()->language()->addToStack( 'api_blocked_file', FALSE, array( 'sprintf' => array( \IPS\ROOT_PATH ) ) ), '3C402/2', 500, '' );
|
||||
}
|
||||
|
||||
/* Plain not working, so bubble up. */
|
||||
throw new \Exception( $response['errorMessage'] );
|
||||
}
|
||||
}
|
||||
// Throw an exception if the response is null; This will be the case if the request was sent to api/core/hello but the htaccess file missing
|
||||
else
|
||||
{
|
||||
if ( $response === NULL )
|
||||
{
|
||||
throw new \Exception;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Throw an exception if the response is null; This will be the case if the request was sent to api/core/hello but the htaccess file missing
|
||||
else if ( $response === NULL )
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
throw new \Exception;
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'menu__core_applications_api' );
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'api' )->htaccess( isset( \IPS\Request::i()->recheck ), $url, $e->getMessage() );
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_applications_api');
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'api' )->htaccess( isset( \IPS\Request::i()->recheck ), $url, $e->getMessage() );
|
||||
return;
|
||||
}
|
||||
|
||||
/* Work out tabs */
|
||||
$tabs = array();
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
namespace IPS\core\modules\admin\applications;
|
||||
|
||||
use IPS\Application;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
@@ -121,7 +123,20 @@ class _apiLogs extends \IPS\Dispatcher\Controller
|
||||
'endpoint' => array( \IPS\Helpers\Table\SEARCH_SELECT, array( 'options' => $endpoints ), function( $val )
|
||||
{
|
||||
$exploded = explode( ' ', $val );
|
||||
return array( 'method=? AND endpoint=?', $exploded[0], trim( $exploded[1], '/' ) );
|
||||
$endpoint = array();
|
||||
foreach( explode( '/', $exploded[1] ) AS $piece )
|
||||
{
|
||||
if ( str_starts_with( $piece, '{' ) )
|
||||
{
|
||||
$endpoint[] = '([a-zA-Z0-9-_]+)';
|
||||
}
|
||||
else
|
||||
{
|
||||
$endpoint[] = $piece;
|
||||
}
|
||||
}
|
||||
|
||||
return array( 'method=? AND endpoint REGEXP ?', $exploded[0], trim( implode( '/', $endpoint ), '/' ) . '$' );
|
||||
} ),
|
||||
'api_key' => array( \IPS\Helpers\Table\SEARCH_NODE, array( 'class' => 'IPS\Api\Key' ) ),
|
||||
'ip_address' => \IPS\Helpers\Table\SEARCH_CONTAINS_TEXT,
|
||||
@@ -156,7 +171,16 @@ class _apiLogs extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'applications', 'api_logs_settings' ) )
|
||||
{
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'forms' )->blurb( \IPS\Member::loggedIn()->language()->addToStack( 'api_log_blurb_change', FALSE, array( 'sprintf' => array( \IPS\Settings::i()->api_log_prune ) ) ), TRUE, TRUE ) . $table;
|
||||
if ( Application::appIsEnabled('cloud') )
|
||||
{
|
||||
$blurb = \IPS\Member::loggedIn()->language()->addTostack( 'api__log_blurb_change_cloud' );
|
||||
}
|
||||
else
|
||||
{
|
||||
$blurb = \IPS\Member::loggedIn()->language()->addToStack( 'api__log_blurb_change', options: [ 'sprintf' => [ \IPS\Settings::i()->api_log_prune ] ] );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'forms' )->blurb( $blurb, FALSE, TRUE ) . $table;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -223,18 +247,26 @@ class _apiLogs extends \IPS\Dispatcher\Controller
|
||||
*/
|
||||
protected function settings()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'api_logs_settings' );
|
||||
|
||||
$form = new \IPS\Helpers\Form;
|
||||
$form->add( new \IPS\Helpers\Form\Interval( 'api_log_prune', \IPS\Settings::i()->api_log_prune, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL ) );
|
||||
|
||||
if ( $values = $form->values() )
|
||||
if ( ! Application::appIsEnabled('cloud') )
|
||||
{
|
||||
$form->saveAsSettings();
|
||||
\IPS\Session::i()->log( 'acplogs__api_log_settings' );
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal('app=core&module=applications&controller=api&tab=apiLogs') );
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'api_logs_settings' );
|
||||
|
||||
$form = new \IPS\Helpers\Form;
|
||||
$form->add( new \IPS\Helpers\Form\Interval( 'api_log_prune', \IPS\Settings::i()->api_log_prune, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL ) );
|
||||
$form->add( new \IPS\Helpers\Form\Interval( 'api_log_prune_failures', \IPS\Settings::i()->api_log_prune_failures, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL ) );
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
$form->saveAsSettings();
|
||||
\IPS\Session::i()->log( 'acplogs__api_log_settings' );
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal('app=core&module=applications&controller=api&tab=apiLogs') );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->output = $form;
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->error( 'api__log_no_cloud', '2C293/3', 403 );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->output = $form;
|
||||
}
|
||||
}
|
||||
@@ -60,15 +60,6 @@ class _applications extends \IPS\Node\Controller
|
||||
{
|
||||
if( \IPS\IPS::canManageResources() )
|
||||
{
|
||||
foreach( \IPS\Application::applications() as $app )
|
||||
{
|
||||
if( $app->marketplace_id )
|
||||
{
|
||||
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate('marketplace')->removalBanner(FALSE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate('forms')->blurb( 'applications_blurb' );
|
||||
}
|
||||
else
|
||||
@@ -685,10 +676,11 @@ class _applications extends \IPS\Node\Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Export an application from ipbmafia.ru
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function customDownload()
|
||||
public function downloadMafia()
|
||||
{
|
||||
$application = \IPS\Application::load( \IPS\Request::i()->appKey );
|
||||
|
||||
@@ -802,10 +794,6 @@ class _applications extends \IPS\Node\Controller
|
||||
unset( \IPS\Data\Store::i()->syncCompleted );
|
||||
|
||||
$url = \IPS\Http\Url::internal( "app=core&module=applications&controller=applications&do=upgrade&appKey=" . \IPS\Request::i()->appKey )->csrf();
|
||||
if ( isset( \IPS\Request::i()->marketplace ) )
|
||||
{
|
||||
$url = $url->setQueryString( 'marketplace', \IPS\Request::i()->marketplace );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->output = new \IPS\Helpers\MultipleRedirect(
|
||||
$url,
|
||||
@@ -1050,7 +1038,6 @@ class _applications extends \IPS\Node\Controller
|
||||
/* Return null to indicate we are done */
|
||||
if( $laststep === NULL )
|
||||
{
|
||||
\IPS\Application::load( $data['key'] )->marketplace_id = \IPS\Request::i()->marketplace ? (int) \IPS\Request::i()->marketplace : NULL;
|
||||
\IPS\Application::load( $data['key'] )->save();
|
||||
|
||||
\IPS\Session::i()->log( 'acplog__application_updated', array( \IPS\Application::load( $data['key'] )->titleForLog() => FALSE, \IPS\Application::load( $data['key'] )->version => TRUE ) );
|
||||
@@ -1081,22 +1068,11 @@ class _applications extends \IPS\Node\Controller
|
||||
/* If CMS Template install had conflicts, solve those now */
|
||||
if( !empty( $_SESSION['cmsConflictKey'] ) )
|
||||
{
|
||||
if( $application->marketplace_id )
|
||||
{
|
||||
$_SESSION['cmsConflictKey'] = $_SESSION['cmsConflictKey']->setQueryString( 'marketplace', $application->marketplace_id );
|
||||
}
|
||||
\IPS\Output::i()->redirect( $_SESSION['cmsConflictKey'] );
|
||||
}
|
||||
|
||||
/* And redirect back to the overview screen (or marketplace if it was installed from there) */
|
||||
if( $application->marketplace_id )
|
||||
{
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=marketplace&controller=marketplace&do=viewFile&id=' . $application->marketplace_id ), 'application_now_updated' );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=applications&controller=applications' ), 'application_now_updated' );
|
||||
}
|
||||
/* And redirect back to the overview screen */
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=applications&controller=applications' ), 'application_now_updated' );
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -1116,10 +1092,6 @@ class _applications extends \IPS\Node\Controller
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('installing_application');
|
||||
|
||||
$url = \IPS\Http\Url::internal( "app=core&module=applications&controller=applications&do=install&appKey=" . \IPS\Request::i()->appKey )->csrf();
|
||||
if ( isset( \IPS\Request::i()->marketplace ) )
|
||||
{
|
||||
$url = $url->setQueryString( 'marketplace', \IPS\Request::i()->marketplace );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->output = new \IPS\Helpers\MultipleRedirect(
|
||||
$url,
|
||||
@@ -1307,7 +1279,6 @@ class _applications extends \IPS\Node\Controller
|
||||
/* Return null to indicate we are done */
|
||||
if( $laststep === NULL )
|
||||
{
|
||||
\IPS\Application::load( $data['key'] )->marketplace_id = \IPS\Request::i()->marketplace ? (int) \IPS\Request::i()->marketplace : NULL;
|
||||
\IPS\Application::load( $data['key'] )->save();
|
||||
|
||||
\IPS\Session::i()->log( 'acplog__application_installed', array( "__app_" . $data['key'] => TRUE ) );
|
||||
@@ -1341,22 +1312,11 @@ class _applications extends \IPS\Node\Controller
|
||||
/* If CMS Template install had conflicts, solve those now */
|
||||
if( !empty( $_SESSION['cmsConflictKey'] ) )
|
||||
{
|
||||
if( $application->marketplace_id )
|
||||
{
|
||||
$_SESSION['cmsConflictKey'] = $_SESSION['cmsConflictKey']->setQueryString( 'marketplace', $application->marketplace_id );
|
||||
}
|
||||
\IPS\Output::i()->redirect( $_SESSION['cmsConflictKey'] );
|
||||
}
|
||||
|
||||
/* And redirect back to the overview screen (or marketplace if it was installed from there) */
|
||||
if( $application->marketplace_id )
|
||||
{
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=marketplace&controller=marketplace&do=viewFile&id=' . $application->marketplace_id ), 'application_now_installed' );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=applications&controller=applications' ), 'application_now_installed' );
|
||||
}
|
||||
/* And redirect back to the overview screen */
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=applications&controller=applications' ), 'application_now_installed' );
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -1622,10 +1582,6 @@ class _applications extends \IPS\Node\Controller
|
||||
}
|
||||
|
||||
$form = new \IPS\Helpers\Form( 'form', 'install' );
|
||||
if( $_type == 'upgrade' AND $app->marketplace_id )
|
||||
{
|
||||
$form->addMessage( 'marketplace_resource_manual_upgrade', 'ipsMessage ipsMessage_info' );
|
||||
}
|
||||
$form->addMessage('applications_manual_install_warning');
|
||||
$form->add( new \IPS\Helpers\Form\Upload( 'application_file', NULL, TRUE, array( 'allowedFileTypes' => array( 'tar' ), 'temporary' => TRUE ) ) );
|
||||
|
||||
|
||||
@@ -74,15 +74,6 @@ class _plugins extends \IPS\Node\Controller
|
||||
|
||||
if ( !\IPS\Request::i()->isAjax() AND \IPS\IPS::canManageResources() )
|
||||
{
|
||||
foreach( \IPS\Plugin::plugins() as $app )
|
||||
{
|
||||
if( $app->marketplace_id )
|
||||
{
|
||||
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate('marketplace')->removalBanner(FALSE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate('forms')->blurb( 'plugins_blurb' );
|
||||
}
|
||||
|
||||
@@ -210,11 +201,6 @@ class _plugins extends \IPS\Node\Controller
|
||||
if ( isset( \IPS\Request::i()->id ) )
|
||||
{
|
||||
$form->hiddenValues['id'] = \IPS\Request::i()->id;
|
||||
|
||||
if( \IPS\Plugin::load( (int) \IPS\Request::i()->id,'plugin_id')->marketplace_id )
|
||||
{
|
||||
$form->addMessage( 'marketplace_resource_manual_upgrade', 'ipsMessage ipsMessage_info' );
|
||||
}
|
||||
}
|
||||
$form->addMessage('plugins_manual_install_warning');
|
||||
$form->add( new \IPS\Helpers\Form\Upload( 'plugin_upload', NULL, TRUE, array( 'allowedFileTypes' => array( 'xml' ), 'temporary' => TRUE ) ) );
|
||||
@@ -273,10 +259,6 @@ class _plugins extends \IPS\Node\Controller
|
||||
}
|
||||
|
||||
$url = \IPS\Http\Url::internal( 'app=core&module=applications&controller=plugins&do=doInstall' )->setQueryString( array( 'file' => \IPS\Request::i()->file, 'key' => \IPS\Request::i()->key, 'id' => \IPS\Request::i()->id ) )->csrf();
|
||||
if ( isset( \IPS\Request::i()->marketplace ) )
|
||||
{
|
||||
$url = $url->setQueryString( 'marketplace', \IPS\Request::i()->marketplace );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->output = new \IPS\Helpers\MultipleRedirect(
|
||||
$url,
|
||||
@@ -368,6 +350,7 @@ class _plugins extends \IPS\Node\Controller
|
||||
}
|
||||
}
|
||||
|
||||
// nulled by ipbmafia.ru
|
||||
\file_put_contents( \IPS\ROOT_PATH . "/plugins/{$plugin->location}/{$plugin->name}_{$plugin->version_human}.xml", \file_get_contents( \IPS\Request::i()->file ) );
|
||||
@chmod( \IPS\ROOT_PATH . "/plugins/{$plugin->location}/{$plugin->name}_{$plugin->version_human}.xml", \IPS\IPS_FILE_PERMISSION );
|
||||
|
||||
@@ -1187,9 +1170,6 @@ class _plugins extends \IPS\Node\Controller
|
||||
/* Log */
|
||||
\IPS\Session::i()->log( 'acplog__plugin_installed', array( $plugin->name => FALSE ) );
|
||||
|
||||
/* Set Marketplace ID. */
|
||||
$plugin->marketplace_id = \IPS\Request::i()->marketplace ? (int) \IPS\Request::i()->marketplace : NULL;
|
||||
|
||||
/* Re-enable the plugin */
|
||||
$plugin->enabled = TRUE;
|
||||
$plugin->save();
|
||||
@@ -1222,19 +1202,9 @@ class _plugins extends \IPS\Node\Controller
|
||||
/* If CMS Template install had conflicts, solve those now */
|
||||
if( !empty( $_SESSION['cmsConflictKey'] ) )
|
||||
{
|
||||
if( \IPS\Request::i()->marketplace )
|
||||
{
|
||||
$_SESSION['cmsConflictKey'] = $_SESSION['cmsConflictKey']->setQueryString( 'marketplace', \IPS\Request::i()->marketplace );
|
||||
}
|
||||
\IPS\Output::i()->redirect( $_SESSION['cmsConflictKey'] );
|
||||
}
|
||||
|
||||
/* Redirect back to marketplace if it was installed from there. */
|
||||
if( \IPS\Request::i()->marketplace )
|
||||
{
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=marketplace&controller=marketplace&do=viewFile&id=' . \IPS\Request::i()->marketplace ), 'plugin_now_installed' );
|
||||
}
|
||||
|
||||
/* And redirect back to the overview screen */
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=applications&controller=plugins' ) );
|
||||
}
|
||||
|
||||
@@ -144,20 +144,28 @@ class _rss extends \IPS\Node\Controller
|
||||
}
|
||||
|
||||
$response = $response->decodeXml();
|
||||
|
||||
if ( !( $response instanceof \IPS\Xml\Rss ) and !( $response instanceof \IPS\Xml\Atom ) )
|
||||
{
|
||||
$form->error = \IPS\Member::loggedIn()->language()->addToStack( 'rss_import_invalid' );
|
||||
\IPS\Log::log( (string) $response, 'rss_import' );
|
||||
return $form;
|
||||
}
|
||||
}
|
||||
catch ( \IPS\Http\Request\Exception $e )
|
||||
{
|
||||
$form->error = \IPS\Member::loggedIn()->language()->addToStack( 'form_url_bad' );
|
||||
\IPS\Log::log( $e, 'rss_import');
|
||||
return $form;
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
$form->error = \IPS\Member::loggedIn()->language()->addToStack( 'rss_import_invalid' );
|
||||
if( isset( $response ) )
|
||||
{
|
||||
\IPS\Log::log( (string) $response, 'rss_import');
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
|
||||
@@ -322,6 +322,13 @@ class _stats extends \IPS\Dispatcher\Controller
|
||||
$where = array();
|
||||
$join = NULL;
|
||||
|
||||
/* Get our container IDs */
|
||||
$containerIds = array();
|
||||
foreach( $clubNodes[ $classMap[ $class ] ] as $node )
|
||||
{
|
||||
$containerIds[] = $node->id;
|
||||
}
|
||||
|
||||
if( is_subclass_of( $class, '\IPS\Content\Comment' ) )
|
||||
{
|
||||
/* We're going to need a subquery... */
|
||||
@@ -329,7 +336,7 @@ class _stats extends \IPS\Dispatcher\Controller
|
||||
|
||||
$where[] = array(
|
||||
$class::$databasePrefix . $class::$databaseColumnMap['item'] . " IN(" .
|
||||
(string) \IPS\Db::i()->select( $parentClass::$databasePrefix . $parentClass::$databaseColumnId, $parentClass::$databaseTable, array( \IPS\Db::i()->in( $parentClass::$databasePrefix . $parentClass::$databaseColumnMap['container'], array_keys( $clubNodes[ $classMap[ $class ] ] ) ) ) )
|
||||
(string) \IPS\Db::i()->select( $parentClass::$databasePrefix . $parentClass::$databaseColumnId, $parentClass::$databaseTable, array( \IPS\Db::i()->in( $parentClass::$databasePrefix . $parentClass::$databaseColumnMap['container'], $containerIds ) ) )
|
||||
. ")"
|
||||
);
|
||||
|
||||
@@ -347,7 +354,7 @@ class _stats extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
else
|
||||
{
|
||||
$where[] = array( \IPS\Db::i()->in( $class::$databasePrefix . $class::$databaseColumnMap['container'], array_keys( $clubNodes[ $classMap[ $class ] ] ) ) );
|
||||
$where[] = array( \IPS\Db::i()->in( $class::$databasePrefix . $class::$databaseColumnMap['container'], $containerIds ) );
|
||||
|
||||
if( mb_strpos( $chart->identifier, 'byclub' ) !== FALSE )
|
||||
{
|
||||
@@ -385,7 +392,15 @@ class _stats extends \IPS\Dispatcher\Controller
|
||||
foreach( \IPS\Member\Club::availableNodeTypes( NULL ) as $nodeType )
|
||||
{
|
||||
$contentClass = $nodeType::$contentItemClass;
|
||||
$results[ $row['time'] ][ \IPS\Member::loggedIn()->language()->get( $contentClass::$title . '_pl' ) ] = 0;
|
||||
$languageKey = \IPS\Member::loggedIn()->language()->words[ $contentClass::$title . '_pl'];
|
||||
|
||||
/* Pages Databases may need the language string parsing */
|
||||
if( mb_substr( $contentClass, 0, 15 ) === 'IPS\cms\Records' )
|
||||
{
|
||||
\IPS\Member::loggedIn()->language()->parseOutputForDisplay( $languageKey );
|
||||
}
|
||||
|
||||
$results[ $row['time'] ][ $languageKey ] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -394,12 +409,24 @@ class _stats extends \IPS\Dispatcher\Controller
|
||||
|
||||
if( mb_strpos( $chart->identifier, 'byclub' ) !== FALSE )
|
||||
{
|
||||
$results[ $row['time'] ][ \IPS\Member\Club::load( $clubNodeMap[ $class::$databaseTable . '-' . $row['container'] ] )->name ] += $row['total'];
|
||||
try
|
||||
{
|
||||
$results[ $row['time'] ][ \IPS\Member\Club::load( $clubNodeMap[ $class::$databaseTable . '-' . $row['container'] ] )->name ] += $row['total'];
|
||||
}
|
||||
catch( \OutOfRangeException $e ){}
|
||||
}
|
||||
else
|
||||
{
|
||||
$languageKey = \IPS\Member::loggedIn()->language()->words[ $contentClass::$title . '_pl'];
|
||||
|
||||
/* Pages Databases may need the language string parsing */
|
||||
if( mb_substr( $contentClass, 0, 15 ) === 'IPS\cms\Records' )
|
||||
{
|
||||
\IPS\Member::loggedIn()->language()->parseOutputForDisplay( $languageKey );
|
||||
}
|
||||
|
||||
$contentClass = $classMap[ $class ]::$contentItemClass;
|
||||
$results[ $row['time'] ][ \IPS\Member::loggedIn()->language()->get( $contentClass::$title . '_pl' ) ] += $row['total'];
|
||||
$results[ $row['time'] ][ $languageKey ] += $row['total'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,18 +66,6 @@ class _themes extends \IPS\Node\Controller
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
if ( !\IPS\Request::i()->isAjax() AND \IPS\IPS::canManageResources() )
|
||||
{
|
||||
foreach( \IPS\Theme::themes() as $app )
|
||||
{
|
||||
if( $app->marketplace_id )
|
||||
{
|
||||
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate('marketplace')->removalBanner(FALSE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( \IPS\Theme::designersModeEnabled() and ! isset( \IPS\Request::i()->root ) )
|
||||
{
|
||||
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->message( \IPS\Member::loggedIn()->language()->addToStack('theme_designer_mode_warning'), 'information', NULL, FALSE );
|
||||
@@ -1136,13 +1124,6 @@ class _themes extends \IPS\Node\Controller
|
||||
if ( !\is_array( $data ) )
|
||||
{
|
||||
$_SESSION['theme_import'] = array( 'css' => array(), 'isNewSet' => false );
|
||||
|
||||
/* Remove marketplace link */
|
||||
if( \IPS\Theme::load( \IPS\Request::i()->id )->marketplace_id AND \IPS\Request::i()->manual )
|
||||
{
|
||||
\IPS\Theme::load( \IPS\Request::i()->id )->marketplace_id = NULL;
|
||||
\IPS\Theme::load( \IPS\Request::i()->id )->save();
|
||||
}
|
||||
|
||||
/* Save snapshot */
|
||||
\IPS\Theme::load( \IPS\Request::i()->id )->saveHistorySnapshot();
|
||||
@@ -1572,10 +1553,6 @@ class _themes extends \IPS\Node\Controller
|
||||
{
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=conflicts&id=' . \IPS\Request::i()->id ) );
|
||||
}
|
||||
elseif( $set->marketplace_id )
|
||||
{
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=marketplace&controller=marketplace&do=viewFile&id=' . $set->marketplace_id ), 'theme_now_installed' );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes' ) );
|
||||
@@ -1886,10 +1863,6 @@ class _themes extends \IPS\Node\Controller
|
||||
}
|
||||
|
||||
$form = new \IPS\Helpers\Form( 'form', 'theme_set_import_button' );
|
||||
if( $themeSet->marketplace_id )
|
||||
{
|
||||
$form->addMessage( 'marketplace_resource_manual_upgrade', 'ipsMessage ipsMessage_info' );
|
||||
}
|
||||
$form->addMessage('themes_manual_install_warning');
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Upload( 'core_theme_set_new_import', NULL, FALSE, array( 'allowedFileTypes' => array( 'xml' ), 'temporary' => TRUE ), NULL, NULL, NULL, 'core_theme_set_new_import' ) );
|
||||
|
||||
@@ -179,7 +179,8 @@ class _streams extends \IPS\Node\Controller
|
||||
$toSave[ 'activity_stream_rss' ] = $values['activity_stream_rss'];
|
||||
$toSave[ 'activity_stream_subscriptions' ] = $values['activity_stream_subscriptions'];
|
||||
$toSave[ 'activity_stream_subscriptions_max' ] = $values['activity_stream_subscriptions_max'];
|
||||
|
||||
$toSave[ 'activity_stream_subscriptions_inactive_limit' ] = $values['activity_stream_subscriptions_inactive_limit'];
|
||||
|
||||
\IPS\Session::i()->Log( 'acplog__all_activity_settings' );
|
||||
|
||||
$form->saveAsSettings( $toSave );
|
||||
|
||||
@@ -478,7 +478,7 @@ class _emoticons extends \IPS\Dispatcher\Controller
|
||||
* Returns the filename and extension for given emoticon path
|
||||
*
|
||||
* @param string $path Emoticon path
|
||||
* @return array
|
||||
* @return string
|
||||
*/
|
||||
protected function _getRawFilename( $path )
|
||||
{
|
||||
|
||||
@@ -84,18 +84,6 @@ class _languages extends \IPS\Node\Controller
|
||||
protected function manage()
|
||||
{
|
||||
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'flags.css', 'core', 'global' ) );
|
||||
|
||||
if ( !\IPS\Request::i()->isAjax() AND \IPS\IPS::canManageResources() )
|
||||
{
|
||||
foreach ( \IPS\Lang::languages() as $app )
|
||||
{
|
||||
if ( $app->marketplace_id )
|
||||
{
|
||||
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate( 'marketplace' )->removalBanner( FALSE );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'languages', 'lang_words' ) )
|
||||
{
|
||||
@@ -698,7 +686,6 @@ class _languages extends \IPS\Node\Controller
|
||||
|
||||
$lang['lang_title'] = $lang['lang_title'] . ' ' . \IPS\Member::loggedIn()->language()->get('copy_noun');
|
||||
$lang['lang_default'] = FALSE;
|
||||
$lang['lang_marketplace_id'] = NULL;
|
||||
|
||||
$insertId = \IPS\Db::i()->insert( 'core_sys_lang', $lang );
|
||||
|
||||
@@ -896,10 +883,6 @@ class _languages extends \IPS\Node\Controller
|
||||
$language = \IPS\Lang::load( \IPS\Request::i()->id );
|
||||
|
||||
$form = new \IPS\Helpers\Form;
|
||||
if( $language->marketplace_id )
|
||||
{
|
||||
$form->addMessage( 'marketplace_resource_manual_upgrade', 'ipsMessage ipsMessage_info' );
|
||||
}
|
||||
$form->add( new \IPS\Helpers\Form\Upload( 'lang_upload', NULL, TRUE, array( 'allowedFileTypes' => array( 'xml' ), 'temporary' => TRUE ) ) );
|
||||
|
||||
/* Handle submissions */
|
||||
@@ -935,10 +918,6 @@ class _languages extends \IPS\Node\Controller
|
||||
{
|
||||
$url = $url->setQueryString( 'into', \IPS\Request::i()->into );
|
||||
}
|
||||
if ( isset( \IPS\Request::i()->marketplace ) )
|
||||
{
|
||||
$url = $url->setQueryString( 'marketplace', \IPS\Request::i()->marketplace );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->output = new \IPS\Helpers\MultipleRedirect(
|
||||
$url,
|
||||
@@ -1002,8 +981,7 @@ class _languages extends \IPS\Node\Controller
|
||||
'lang_version_long' => $xml->getAttribute('long_version'),
|
||||
'lang_author_name' => $xml->getAttribute('author_name'),
|
||||
'lang_author_url' => $xml->getAttribute('author_url'),
|
||||
'lang_update_url' => $xml->getAttribute('update_check'),
|
||||
'lang_marketplace_id' => \IPS\Request::i()->marketplace ? (int) \IPS\Request::i()->marketplace : NULL
|
||||
'lang_update_url' => $xml->getAttribute('update_check')
|
||||
) );
|
||||
|
||||
/* Copy over default language strings */
|
||||
@@ -1084,14 +1062,8 @@ class _languages extends \IPS\Node\Controller
|
||||
|
||||
@unlink( \IPS\Request::i()->file );
|
||||
|
||||
/* Redirect back to marketplace if it was installed from there. */
|
||||
if( \IPS\Request::i()->marketplace )
|
||||
if ( isset( \IPS\Request::i()->into ) )
|
||||
{
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=marketplace&controller=marketplace&do=viewFile&id=' . \IPS\Request::i()->marketplace ), 'lang_installed' );
|
||||
}
|
||||
elseif ( isset( \IPS\Request::i()->into ) )
|
||||
{
|
||||
\IPS\Lang::load( (int) \IPS\Request::i()->into )->marketplace_id = NULL;
|
||||
\IPS\Lang::load( (int) \IPS\Request::i()->into )->save();
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,245 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Marketplace Onboarding Controller
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
* @since 1 May 2020
|
||||
*/
|
||||
|
||||
namespace IPS\core\modules\admin\marketplace;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marketplace Onboarding Controller
|
||||
*/
|
||||
class _onboard extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
/**
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* Run
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
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\IPS::$ipsApps ); } ) )
|
||||
{
|
||||
$steps['marketplace_onboard_applications'] = array( $this, '_applications' );
|
||||
}
|
||||
if ( \IPS\Plugin::plugins() )
|
||||
{
|
||||
$steps['marketplace_onboard_plugins'] = array( $this, '_plugins' );
|
||||
}
|
||||
if ( array_filter( \IPS\Theme::themes(), function( $t ) { return $t->isCustomized(); } ) )
|
||||
{
|
||||
$steps['marketplace_onboard_themes'] = array( $this, '_themes' );
|
||||
}
|
||||
if ( \IPS\Db::i()->select( 'COUNT(*)', 'core_sys_lang_words', array( 'word_custom IS NOT NULL AND word_export=1' ) )->first() )
|
||||
{
|
||||
$steps['marketplace_onboard_languages'] = array( $this, '_languages' );
|
||||
}
|
||||
$steps['marketplace_onboard_complete'] = array( $this, '_complete' );
|
||||
if ( !$steps )
|
||||
{
|
||||
$this->_complete();
|
||||
}
|
||||
|
||||
$wizard = new \IPS\Helpers\Wizard( $steps, \IPS\Http\Url::internal('app=core&module=marketplace&controller=onboard') );
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('marketplace_onboard_title');
|
||||
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'admin_marketplace.js', 'core', 'admin' ) );
|
||||
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'marketplace/marketplace.css', 'core', 'admin' ) );
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'forms', 'core' )->blurb( \IPS\Member::loggedIn()->language()->addToStack( 'marketplace_onboard_blurb' ) ) . $wizard;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applications
|
||||
*
|
||||
* @param mixed $data Wizard data
|
||||
* @return mixed
|
||||
*/
|
||||
final public function _applications( $data )
|
||||
{
|
||||
if ( \IPS\Request::i()->match_apps )
|
||||
{
|
||||
foreach ( \IPS\Application::applications() as $application )
|
||||
{
|
||||
if ( isset( \IPS\Request::i()->match_apps[ $application->directory ] ) and \IPS\Request::i()->match_apps[ $application->directory ] )
|
||||
{
|
||||
$application->marketplace_id = \IPS\Request::i()->match_apps[ $application->directory ];
|
||||
$application->save();
|
||||
}
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
foreach ( \IPS\Application::applications() as $application )
|
||||
{
|
||||
if ( !\in_array( $application->directory, \IPS\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' );
|
||||
}
|
||||
}
|
||||
if ( $rows )
|
||||
{
|
||||
return \IPS\Theme::i()->getTemplate('marketplace')->onboardTemplate( $rows );
|
||||
}
|
||||
else
|
||||
{
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugins
|
||||
*
|
||||
* @param mixed $data Wizard data
|
||||
* @return mixed
|
||||
*/
|
||||
final public function _plugins( $data )
|
||||
{
|
||||
if ( \IPS\Request::i()->match_plugins )
|
||||
{
|
||||
foreach ( \IPS\Plugin::plugins() as $plugin )
|
||||
{
|
||||
if ( isset( \IPS\Request::i()->match_plugins[ $plugin->id ] ) and \IPS\Request::i()->match_plugins[ $plugin->id ] )
|
||||
{
|
||||
$plugin->marketplace_id = \IPS\Request::i()->match_plugins[ $plugin->id ];
|
||||
$plugin->save();
|
||||
}
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
foreach ( \IPS\Plugin::plugins() as $plugin )
|
||||
{
|
||||
$rows[ $plugin->id ] = \IPS\Theme::i()->getTemplate('marketplace')->onboardRow( $plugin->id, $plugin->marketplace_id, 'apps', $plugin->name, $plugin->version_human, $plugin->author, $plugin->website, 'plugins' );
|
||||
}
|
||||
if ( $rows )
|
||||
{
|
||||
return \IPS\Theme::i()->getTemplate('marketplace')->onboardTemplate( $rows );
|
||||
}
|
||||
else
|
||||
{
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Themes
|
||||
*
|
||||
* @param mixed $data Wizard data
|
||||
* @return mixed
|
||||
*/
|
||||
final public function _themes( $data )
|
||||
{
|
||||
if ( \IPS\Request::i()->match_themes )
|
||||
{
|
||||
foreach ( \IPS\Theme::themes() as $theme )
|
||||
{
|
||||
if ( isset( \IPS\Request::i()->match_themes[ $theme->id ] ) and \IPS\Request::i()->match_themes[ $theme->id ] )
|
||||
{
|
||||
$theme->marketplace_id = \IPS\Request::i()->match_themes[ $theme->id ];
|
||||
$theme->save();
|
||||
}
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
foreach ( \IPS\Theme::themes() as $theme )
|
||||
{
|
||||
if ( $theme->isCustomized() )
|
||||
{
|
||||
$rows[ $theme->id ] = \IPS\Theme::i()->getTemplate('marketplace')->onboardRow( $theme->id, $theme->marketplace_id, 'themes', $theme->_title, $theme->version, $theme->author_name, $theme->author_url, 'themes' );
|
||||
}
|
||||
}
|
||||
if ( $rows )
|
||||
{
|
||||
return \IPS\Theme::i()->getTemplate('marketplace')->onboardTemplate( $rows );
|
||||
}
|
||||
else
|
||||
{
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Languages
|
||||
*
|
||||
* @param mixed $data Wizard data
|
||||
* @return mixed
|
||||
*/
|
||||
final public function _languages( $data )
|
||||
{
|
||||
if ( \IPS\Request::i()->match_languages )
|
||||
{
|
||||
foreach ( \IPS\Lang::languages() as $language )
|
||||
{
|
||||
if ( isset( \IPS\Request::i()->match_languages[ $language->id ] ) and \IPS\Request::i()->match_languages[ $language->id ] )
|
||||
{
|
||||
$language->marketplace_id = \IPS\Request::i()->match_languages[ $language->id ];
|
||||
$language->save();
|
||||
}
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
foreach ( \IPS\Lang::languages() as $language )
|
||||
{
|
||||
if ( \IPS\Db::i()->select( 'COUNT(*)', 'core_sys_lang_words', array( 'lang_id=? AND word_custom IS NOT NULL AND word_export=1', $language->_id ) )->first() )
|
||||
{
|
||||
$rows[ $language->id ] = \IPS\Theme::i()->getTemplate('marketplace')->onboardRow( $language->id, $language->marketplace_id, 'languages', $language->_title, $language->version, $language->author_name, $language->author_url, 'languages' );
|
||||
}
|
||||
}
|
||||
if ( $rows )
|
||||
{
|
||||
return \IPS\Theme::i()->getTemplate('marketplace')->onboardTemplate( $rows );
|
||||
}
|
||||
else
|
||||
{
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete
|
||||
*
|
||||
* @param mixed $data Wizard data
|
||||
* @return mixed
|
||||
*/
|
||||
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') );
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
namespace IPS\core\modules\admin\members;
|
||||
|
||||
use IPS\Text\Encrypt;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
@@ -123,6 +125,7 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
/* Create the table */
|
||||
$table = new \IPS\Helpers\Table\Db( 'core_members', \IPS\Http\Url::internal( 'app=core&module=members&controller=members' ), array( array( 'core_members.email<>?', '' ) ), $forceIndex );
|
||||
$table->langPrefix = 'members_';
|
||||
$table->keyField = 'member_id';
|
||||
|
||||
/* Columns we need */
|
||||
$table->include = array( 'photo', 'name', 'email', 'joined', 'group_name' );
|
||||
@@ -196,7 +199,7 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
$options = array( '' => "");
|
||||
}
|
||||
|
||||
if( \count( $field->options['options'] ) )
|
||||
if( \is_array( $field->options['options'] ) AND \count( $field->options['options'] ) )
|
||||
{
|
||||
foreach ( $field->options['options'] as $option )
|
||||
{
|
||||
@@ -2530,7 +2533,9 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
{
|
||||
\IPS\Output::i()->error( 'node_error', '2C114/9', 404, '' );
|
||||
}
|
||||
$member->failed_logins = array();
|
||||
/* Remove login failures */
|
||||
\IPS\Db::i()->delete( 'core_login_failures', [ 'login_member_id=?', $member->member_id ] );
|
||||
$member->failed_login_count = 0;
|
||||
$member->failed_mfa_attempts = 0;
|
||||
$mfaDetails = $member->mfa_details;
|
||||
if ( isset( $mfaDetails['_lockouttime'] ) )
|
||||
@@ -2682,25 +2687,31 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
{
|
||||
if ( !$row['user_verified'] )
|
||||
{
|
||||
$plainTextKey = '';
|
||||
if( !empty( $row['security_key'] ) )
|
||||
{
|
||||
$plainTextKey = Encrypt::fromTag( $row['security_key'] )->decrypt();
|
||||
}
|
||||
|
||||
/* Lost Pass */
|
||||
if ( $row['lost_pass'] )
|
||||
{
|
||||
\IPS\Email::buildFromTemplate( 'core', 'lost_password_init', array( $member, $row['vid'] ), \IPS\Email::TYPE_TRANSACTIONAL )->send( $member );
|
||||
\IPS\Email::buildFromTemplate( 'core', 'lost_password_init', array( $member, $row['vid'], $plainTextKey ), \IPS\Email::TYPE_TRANSACTIONAL )->send( $member );
|
||||
}
|
||||
/* New Reg */
|
||||
elseif ( $row['new_reg'] )
|
||||
{
|
||||
\IPS\Email::buildFromTemplate( 'core', 'registration_validate', array( $member, $row['vid'] ), \IPS\Email::TYPE_TRANSACTIONAL )->send( $member );
|
||||
\IPS\Email::buildFromTemplate( 'core', 'registration_validate', array( $member, $row['vid'], $plainTextKey ), \IPS\Email::TYPE_TRANSACTIONAL )->send( $member );
|
||||
}
|
||||
/* Email Change */
|
||||
elseif ( $row['email_chg'] )
|
||||
{
|
||||
\IPS\Email::buildFromTemplate( 'core', 'email_change', array( $member, $row['vid'] ), \IPS\Email::TYPE_TRANSACTIONAL )->send( $member );
|
||||
\IPS\Email::buildFromTemplate( 'core', 'email_change', array( $member, $row['vid'], $plainTextKey ), \IPS\Email::TYPE_TRANSACTIONAL )->send( $member );
|
||||
}
|
||||
/* Forgot security answers */
|
||||
elseif ( $row['forgot_security'] )
|
||||
{
|
||||
\IPS\Email::buildFromTemplate( 'core', 'forgotSecurityAnswers', array( $member, $row['vid'] ), \IPS\Email::TYPE_TRANSACTIONAL )->send( $member );
|
||||
\IPS\Email::buildFromTemplate( 'core', 'mfaRecovery', array( $member, $row['vid'], $plainTextKey ), \IPS\Email::TYPE_TRANSACTIONAL )->send( $member );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3028,7 +3039,7 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
|
||||
\IPS\Session::i()->log( 'acplog__deleted_guest_content', array( $values['guest_name_to_delete'] => FALSE ) );
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=moderation&controller=spam&searchResult=guest_captcha" ), 'deleted' );
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=moderation&controller=spam" ), 'deleted' );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->output = $form;
|
||||
@@ -3573,7 +3584,7 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
break;
|
||||
}
|
||||
|
||||
$matrix->rows[] = array( 'import_column' => $header, 'import_as' => $value );
|
||||
$matrix->rows[] = array( 'import_column' => htmlspecialchars( $header, ENT_DISALLOWED | ENT_QUOTES, 'UTF-8', FALSE ), 'import_as' => $value );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3634,7 +3645,11 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
{
|
||||
fseek( $fh, $mrData['currentPosition'] );
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
begin:
|
||||
$line = fgetcsv( $fh );
|
||||
$count++;
|
||||
|
||||
/* Are we done/ */
|
||||
if ( !$line )
|
||||
@@ -3883,6 +3898,12 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
\IPS\Email::buildFromTemplate( 'core', 'admin_reg', array( $member, $member->members_bitoptions['password_reset_forced'], md5( \IPS\SUITE_UNIQUE_KEY . $member->email . $member->real_name ) ), \IPS\Email::TYPE_TRANSACTIONAL )->send( $member );
|
||||
}
|
||||
|
||||
/* If we haven't hit our limit, go again. */
|
||||
if ( $count <= 100 )
|
||||
{
|
||||
goto begin;
|
||||
}
|
||||
|
||||
/* Continue */
|
||||
$mrData['currentPosition'] = ftell( $fh );
|
||||
fclose( $fh );
|
||||
@@ -4128,9 +4149,9 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
$mrData['count'] = $query->first();
|
||||
}
|
||||
|
||||
/* Compile query */
|
||||
$query = \IPS\Db::i()->select( implode( ',', $select ), 'core_members', $where, 'core_members.member_id', array( $mrData['offset'], $doPerLoop ) );
|
||||
|
||||
/* Compile member_id query. Just getting the member_ids first is more efficient on larger tables */
|
||||
$query = \IPS\Db::i()->select( 'core_members.member_id', 'core_members', $where, 'core_members.member_id', array( $mrData['offset'], $doPerLoop ) );
|
||||
|
||||
/* Run callbacks */
|
||||
foreach ( \IPS\Application::allExtensions( 'core', 'MemberFilter', TRUE, 'core' ) as $key => $extension )
|
||||
{
|
||||
@@ -4145,36 +4166,73 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
}
|
||||
}
|
||||
|
||||
/* Finished? */
|
||||
if ( !$query->count() )
|
||||
/* Get the IDs */
|
||||
$memberIds = iterator_to_array( $query );
|
||||
|
||||
/* Nothing to do? */
|
||||
if ( ! count( $memberIds ) )
|
||||
{
|
||||
if( \count( $mrData['removedData'] ) AND ( !isset( $wizardData['includeInsecure'] ) OR !$wizardData['includeInsecure'] ) )
|
||||
{
|
||||
$_SESSION['removedData'] = $mrData['removedData'];
|
||||
}
|
||||
|
||||
/* Finish here as we got everything */
|
||||
\IPS\Output::i()->redirect( $baseUrl->setQueryString( array( 'buildDone' => 1 ) ) );
|
||||
}
|
||||
|
||||
|
||||
/* Now prepare the actual query to get all the data */
|
||||
$where[] = [ \IPS\Db::i()->in( 'core_members.member_id', $memberIds ) ];
|
||||
$dataQuery = \IPS\Db::i()->select( implode( ',', $select ), 'core_members', $where, 'core_members.member_id' );
|
||||
|
||||
/* Run callbacks */
|
||||
foreach ( \IPS\Application::allExtensions( 'core', 'MemberFilter', TRUE, 'core' ) as $key => $extension )
|
||||
{
|
||||
if( method_exists( $extension, 'queryCallback' ) )
|
||||
{
|
||||
/* Grab our fields and add to the form */
|
||||
if( !empty( $wizardData['filters'][ $key ] ) )
|
||||
{
|
||||
$data = $wizardData['filters'][ $key ];
|
||||
$extension->queryCallback( $data, $dataQuery );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Open file */
|
||||
$fh = fopen( $wizardData['file'], 'a' );
|
||||
|
||||
|
||||
/* Run */
|
||||
foreach ( $query as $member )
|
||||
$done = 0;
|
||||
foreach ( $dataQuery as $member )
|
||||
{
|
||||
$dataToWrite = $this->_getDataToWriteCsv( $member, $wizardData, $mrData );
|
||||
|
||||
$done++; /* This is here because the query fetched 2500 but fewer than 2500 may be processed throwing the last bit out */
|
||||
|
||||
if( $dataToWrite === FALSE )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
/* Write */
|
||||
fputcsv( $fh, $dataToWrite );
|
||||
}
|
||||
|
||||
|
||||
/* Close and loop */
|
||||
fclose( $fh );
|
||||
|
||||
if ( $done < $doPerLoop )
|
||||
{
|
||||
if( \count( $mrData['removedData'] ) AND ( !isset( $wizardData['includeInsecure'] ) OR !$wizardData['includeInsecure'] ) )
|
||||
{
|
||||
$_SESSION['removedData'] = $mrData['removedData'];
|
||||
}
|
||||
|
||||
/* Just finish here as we got everything */
|
||||
\IPS\Output::i()->redirect( $baseUrl->setQueryString( array( 'buildDone' => 1 ) ) );
|
||||
}
|
||||
|
||||
$mrData['offset'] += $doPerLoop;
|
||||
return array( $mrData, \IPS\Member::loggedIn()->language()->addToStack('export_members_processing'), ( $mrData['offset'] > $mrData['count'] ) ? 100 : ( 100 / $mrData['count'] ) * $mrData['offset'] );
|
||||
},
|
||||
@@ -4289,7 +4347,14 @@ class _members extends \IPS\Helpers\CoverPhoto\Controller
|
||||
if ( mb_substr( $column, 0, 7 ) == 'pfield_' )
|
||||
{
|
||||
$fieldId = mb_substr( $column, 7 );
|
||||
$valueToWrite = $member[ 'field_' . $fieldId ] ? \IPS\core\ProfileFields\Field::load( $fieldId )->displayValue( $member[ 'field_' . $fieldId ], FALSE, 0, NULL, TRUE, ',' ) : '';
|
||||
|
||||
/* If this is a Checkbox or YesNo field, then we can't use the displayValue() as it will just come out as a hashed string, and the CSV file can be large, so just replace these at generation time rather than parsing the entire file. */
|
||||
$field = \IPS\core\ProfileFields\Field::load( $fieldId );
|
||||
|
||||
$valueToWrite = match( $field->type ) {
|
||||
'YesNo', 'Checkbox' => (bool) $member[ 'field_' . $fieldId ] ? \IPS\Member::loggedIn()->language()->get('yes') : \IPS\Member::loggedIn()->language()->get('no'),
|
||||
default => $member[ 'field_' . $fieldId ] ? \IPS\core\ProfileFields\Field::load( $fieldId )->displayValue( $member[ 'field_' . $fieldId ], FALSE, 0, NULL, TRUE, ',' ) : ''
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
namespace IPS\core\modules\admin\membersettings;
|
||||
|
||||
use IPS\Application;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
@@ -133,6 +135,11 @@ class _profilesettings extends \IPS\Dispatcher\Controller
|
||||
$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' ) );
|
||||
|
||||
if ( Application::appIsEnabled('nexus') )
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\Interval( 'nexus_prune_history', \IPS\Settings::i()->nexus_prune_history, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL, 'nexus_prune_history' ) );
|
||||
}
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Interval( 'prune_known_ips', \IPS\Settings::i()->prune_known_ips, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'never' ), function( $val ) {
|
||||
if( $val > 0 AND $val < 7 )
|
||||
{
|
||||
@@ -227,7 +234,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() ),
|
||||
'where' => array( 'log_date < ? and log_app != ?', \IPS\DateTime::create()->sub( new \DateInterval( 'P' . $values['prune_member_history'] . 'D' ) )->getTimestamp(), 'nexus' ),
|
||||
'setting' => 'prune_member_history',
|
||||
), 4 );
|
||||
}
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief report
|
||||
* @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 17 Sep 2024
|
||||
*/
|
||||
|
||||
namespace IPS\core\modules\admin\moderation;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
|
||||
use IPS\Db;
|
||||
use IPS\Dispatcher;
|
||||
use IPS\Dispatcher\Controller;
|
||||
use IPS\Helpers\Form;
|
||||
use IPS\Helpers\Form\Number;
|
||||
use IPS\Helpers\Form\YesNo;
|
||||
use IPS\Http\Url;
|
||||
use IPS\Member;
|
||||
use IPS\Output;
|
||||
use IPS\Session;
|
||||
use IPS\Settings;
|
||||
use IPS\Theme;
|
||||
use function defined;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* report
|
||||
*/
|
||||
class _report extends Controller
|
||||
{
|
||||
/**
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
/* Add a button for settings */
|
||||
\IPS\Output::i()->sidebar['actions'] = array(
|
||||
'automatic' => array(
|
||||
'title' => 'manage_automatic_rules',
|
||||
'icon' => 'bolt',
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=moderation&controller=reportedContent' )
|
||||
),
|
||||
'settings' => array(
|
||||
'title' => 'reportedContent_types',
|
||||
'icon' => 'cog',
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=moderation&controller=reportedContentTypes' )
|
||||
)
|
||||
);
|
||||
|
||||
Dispatcher::i()->checkAcpPermission( 'reportedContentTypes_manage' );
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings form
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
Dispatcher::i()->checkAcpPermission( 'reportedContent_manage' );
|
||||
|
||||
/* Work out output */
|
||||
\IPS\Request::i()->tab = isset( \IPS\Request::i()->tab ) ? \IPS\Request::i()->tab : 'settings';
|
||||
if ( $pos = mb_strpos( \IPS\Request::i()->tab, '-' ) )
|
||||
{
|
||||
$tabMethod = '_manage' . mb_ucfirst( mb_substr( \IPS\Request::i()->tab, 0, $pos ) );
|
||||
$activeTabContents = $this->$tabMethod( mb_substr( \IPS\Request::i()->tab, $pos + 1 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
$tabMethod = '_manage' . mb_ucfirst( \IPS\Request::i()->tab );
|
||||
$activeTabContents = $this->$tabMethod();
|
||||
}
|
||||
|
||||
/* If this is an AJAX request, just return it */
|
||||
if( \IPS\Request::i()->isAjax() )
|
||||
{
|
||||
\IPS\Output::i()->output = $activeTabContents;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Build tab list */
|
||||
$tabs = [
|
||||
'settings' => 'menu__core_moderation_report',
|
||||
'notifications' => 'report_author_notification_title'
|
||||
];
|
||||
|
||||
/* Display */
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_moderation_report');
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->tabs( $tabs, \IPS\Request::i()->tab, $activeTabContents, \IPS\Http\Url::internal( "app=core&module=moderation&controller=report" ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage settings
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _manageSettings(): string
|
||||
{
|
||||
$form = new Form;
|
||||
|
||||
/* Can guests report */
|
||||
$guest = new \IPS\Member;
|
||||
$guestDisabled = false;
|
||||
$guestSetting = Settings::i()->report_capture_guest_details;
|
||||
if ( ! $guest->group['g_can_report'] )
|
||||
{
|
||||
$guestDisabled = true;
|
||||
$guestSetting = false;
|
||||
\IPS\Member::loggedIn()->language()->words['report_capture_guest_details_warning'] = \IPS\Member::loggedIn()->language()->addToStack( 'report_capture_guest_details__warning' );
|
||||
}
|
||||
|
||||
$form->add( new YesNo( 'report_capture_guest_details', $guestSetting, FALSE, array( 'disabled' => $guestDisabled, 'togglesOn' => [ 'report_guest_details_name_mandatory', 'report_guest_details_store_days'] ), NULL, NULL, NULL, 'report_capture_guest_details' ) );
|
||||
$form->add( new YesNo( 'report_guest_details_name_mandatory', Settings::i()->report_guest_details_name_mandatory, FALSE, array(), NULL, NULL, NULL, 'report_guest_details_name_mandatory' ) );
|
||||
$form->add( new Number( 'report_guest_details_store_days', Settings::i()->report_guest_details_store_days, FALSE, array( 'unlimited' => -1, 'unlimitedLang' => 'report_guest_details_store_unlimited' ), NULL, NULL, Member::loggedIn()->language()->addToStack('days'), 'report_guest_details_store_days' ) );
|
||||
|
||||
$form->add( new YesNo( 'report_content_mandatory', Settings::i()->report_content_mandatory, FALSE, array(), NULL, NULL, NULL, 'report_content_mandatory' ) );
|
||||
|
||||
$form->add( new YesNo( 'automoderation_enabled', Settings::i()->automoderation_enabled, FALSE, array( 'togglesOn' => [ 'automoderation_report_again_mins' ] ), NULL, NULL, NULL, 'automoderation_enabled' ) );
|
||||
$form->add( new Number( 'automoderation_report_again_mins', Settings::i()->automoderation_report_again_mins, FALSE, array(), NULL, NULL, Member::loggedIn()->language()->addToStack('automoderation_report_again_mins_suffix'), 'automoderation_report_again_mins' ) );
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
$form->saveAsSettings();
|
||||
|
||||
Db::i()->update( 'core_tasks', array( 'enabled' => (int) $values['automoderation_enabled'] ), array( '`key`=?', 'automaticmoderation' ) );
|
||||
|
||||
Session::i()->log( 'acplog__automoderation_settings' );
|
||||
Output::i()->redirect( Url::internal( 'app=core&module=moderation&controller=report&tab=settings' ), 'saved' );
|
||||
}
|
||||
|
||||
return Theme::i()->getTemplate('forms')->blurb( 'reporting_content_blurb', true, true ) . $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function _manageNotifications(): string
|
||||
{
|
||||
/* Init */
|
||||
$table = new \IPS\Helpers\Table\Db( 'core_rc_author_notification_text', \IPS\Http\Url::internal( 'app=core&module=moderation&controller=report&tab=notifications' ) );
|
||||
$table->include = array( 'title' );
|
||||
$table->sortBy = $table->sortBy ?: 'title';
|
||||
$table->sortDirection = $table->sortDirection ?: 'asc';
|
||||
|
||||
/* Row buttons */
|
||||
$table->rowButtons = function( $row )
|
||||
{
|
||||
$return = array();
|
||||
|
||||
$return['edit'] = array(
|
||||
'icon' => 'pencil',
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=moderation&controller=report&tab=notifications&do=notificationForm&id=' ) . $row['id'],
|
||||
'title' => 'edit',
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('edit') )
|
||||
);
|
||||
|
||||
$return['delete'] = array(
|
||||
'icon' => 'times-circle',
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=moderation&controller=report&tab=notifications&do=notificationDelete&id=' ) . $row['id'],
|
||||
'title' => 'delete',
|
||||
'data' => array( 'delete' => '' )
|
||||
);
|
||||
|
||||
return $return;
|
||||
};
|
||||
|
||||
/* Add button */
|
||||
$table->rootButtons = array(
|
||||
'add' => array(
|
||||
'icon' => 'plus',
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=moderation&controller=report&tab=notifications&do=notificationForm' ),
|
||||
'title' => 'add',
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('add') )
|
||||
)
|
||||
);
|
||||
|
||||
/* Display */
|
||||
return Theme::i()->getTemplate('forms')->blurb( 'reporting_content_notifications_blurb', true, true ) . $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Report notification to author of content form
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function notificationForm()
|
||||
{
|
||||
$current = NULL;
|
||||
if ( \IPS\Request::i()->id )
|
||||
{
|
||||
$current = \IPS\Db::i()->select( '*', 'core_rc_author_notification_text', array( 'id=?', \IPS\Request::i()->id ) )->first();
|
||||
}
|
||||
|
||||
$form = new \IPS\Helpers\Form;
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'report_notifications_title', ( $current ? $current['title'] : '' ), true ) );
|
||||
$form->add( new \IPS\Helpers\Form\TextArea( 'report_notifications_text', ( $current ? $current['text'] : '' ), true, [ 'rows' => 20 ] ) );
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
$save = [
|
||||
'title' => $values['report_notifications_title'],
|
||||
'text' => $values['report_notifications_text']
|
||||
];
|
||||
|
||||
if ( $current )
|
||||
{
|
||||
\IPS\Db::i()->update( 'core_rc_author_notification_text', $save, array( 'id=?', $current['id'] ) );
|
||||
\IPS\Session::i()->log( 'acplog__report_author_notification_edited', array( $current['title'] => true ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Db::i()->insert( 'core_rc_author_notification_text', $save );
|
||||
\IPS\Session::i()->log( 'acplog__report_author_notification_created', array( $save['title'] => true ) );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=moderation&controller=report&tab=notifications' ), 'saved' );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate( 'global' )->block( 'report_author_notification_title', $form, FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Report notification to author of content delete
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function notificationDelete()
|
||||
{
|
||||
/* Make sure the user confirmed the deletion */
|
||||
\IPS\Request::i()->confirmedDelete();
|
||||
|
||||
try
|
||||
{
|
||||
$current = \IPS\Db::i()->select( '*', 'core_rc_author_notification_text', array( 'id=?', \IPS\Request::i()->id ) )->first();
|
||||
|
||||
\IPS\Session::i()->log( 'acplog__report_author_notification_deleted', array( $current['title'] => true ) );
|
||||
\IPS\Db::i()->delete( 'core_rc_author_notification_text', array( 'id=?', \IPS\Request::i()->id ) );
|
||||
}
|
||||
catch ( \UnderflowException $e ) { }
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=moderation&controller=report&tab=notifications' ), 'deleted' );
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,10 @@
|
||||
namespace IPS\core\modules\admin\moderation;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
|
||||
use IPS\Http\Url;
|
||||
use IPS\Output;
|
||||
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
@@ -40,20 +44,8 @@ class _reportedContent extends \IPS\Node\Controller
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
/* Add a button for settings */
|
||||
\IPS\Output::i()->sidebar['actions'] = array(
|
||||
'settings' => array(
|
||||
'title' => 'reportedContent_settings',
|
||||
'icon' => 'cog',
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=moderation&controller=reportedContent&do=settings' ),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('reportedContent_settings') )
|
||||
),
|
||||
'types' => array(
|
||||
'title' => 'reportedContent_types',
|
||||
'icon' => 'cog',
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=moderation&controller=reportedContentTypes' )
|
||||
)
|
||||
);
|
||||
Output::i()->breadcrumb[] = array( Url::internal( "app=core&module=moderation&controller=report" ), 'menu__core_moderation_report' );
|
||||
Output::i()->breadcrumb[] = array( NULL, 'manage_automatic_rules' );
|
||||
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('forms')->blurb( 'automaticmoderation_blurb' );
|
||||
|
||||
@@ -65,32 +57,4 @@ class _reportedContent extends \IPS\Node\Controller
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'reportedContent_manage' );
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Profile Settings
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function settings()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'reportedContent_manage' );
|
||||
|
||||
$form = new \IPS\Helpers\Form;
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'automoderation_enabled', \IPS\Settings::i()->automoderation_enabled, FALSE, array(), NULL, NULL, NULL, 'automoderation_enabled' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Number( 'automoderation_report_again_mins', \IPS\Settings::i()->automoderation_report_again_mins, FALSE, array(), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('automoderation_report_again_mins_suffix'), 'automoderation_report_again_mins' ) );
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
$form->saveAsSettings();
|
||||
|
||||
\IPS\Db::i()->update( 'core_tasks', array( 'enabled' => (int) $values['automoderation_enabled'] ), array( '`key`=?', 'automaticmoderation' ) );
|
||||
|
||||
\IPS\Session::i()->log( 'acplog__automoderation_settings' );
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=moderation&controller=reportedContent' ), 'saved' );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('reportedContent_settings');
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('global')->block( 'reportedContent_settings', $form, FALSE );
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,12 @@
|
||||
namespace IPS\core\modules\admin\moderation;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
|
||||
use IPS\Http\Url;
|
||||
use IPS\Output;
|
||||
use IPS\Request;
|
||||
use IPS\Theme;
|
||||
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
@@ -40,6 +46,14 @@ class _reportedContentTypes extends \IPS\Node\Controller
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
Output::i()->breadcrumb[] = array( Url::internal( "app=core&module=moderation&controller=report" ), 'menu__core_moderation_report' );
|
||||
Output::i()->breadcrumb[] = array( NULL, 'reportedContent_types' );
|
||||
|
||||
if ( ! Request::i()->do )
|
||||
{
|
||||
Output::i()->output = Theme::i()->getTemplate( 'forms', 'core' )->blurb( 'moderation_reporttypes_desc' );
|
||||
}
|
||||
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'reportedContentTypes_manage' );
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
@@ -11,6 +11,12 @@
|
||||
namespace IPS\core\modules\admin\moderation;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
|
||||
use IPS\Helpers\Form;
|
||||
use IPS\Helpers\Form\Radio;
|
||||
use IPS\Helpers\Form\Text;
|
||||
use IPS\Settings;
|
||||
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
@@ -116,26 +122,45 @@ class _spam extends \IPS\Dispatcher\Controller
|
||||
* @param string $type 'options' for the select options, 'toggles' for the toggles
|
||||
* @return array
|
||||
*/
|
||||
protected function getCaptchaOptions( $type='options' )
|
||||
protected static function getCaptchaOptions( string $type='options' ) : array
|
||||
{
|
||||
switch( $type )
|
||||
{
|
||||
case 'options':
|
||||
return array( 'none' => 'captcha_type_none', 'invisible' => 'captcha_type_invisible', 'recaptcha2' => 'captcha_type_recaptcha2', 'keycaptcha' => 'captcha_type_keycaptcha', 'hcaptcha' => 'captcha_type_hcaptcha' );
|
||||
break;
|
||||
$options = [
|
||||
'none' => 'captcha_type_none',
|
||||
'turnstile' => 'captcha_type_turnstile',
|
||||
'invisible' => 'captcha_type_invisible',
|
||||
'recaptcha2' => 'captcha_type_recaptcha2',
|
||||
'keycaptcha' => 'captcha_type_keycaptcha',
|
||||
'hcaptcha' => 'captcha_type_hcaptcha'
|
||||
];
|
||||
|
||||
if( \IPS\Settings::i()->bot_antispam_type != 'keycaptcha' )
|
||||
{
|
||||
unset( $options['keycaptcha'] );
|
||||
}
|
||||
|
||||
return $options;
|
||||
|
||||
case 'toggles':
|
||||
return array(
|
||||
'none' => array( 'bot_antispam_type_warning' ),
|
||||
'recaptcha2' => array( 'guest_captcha', 'recaptcha2_public_key', 'recaptcha2_private_key' ),
|
||||
'invisible' => array( 'guest_captcha', 'recaptcha2_public_key', 'recaptcha2_private_key' ),
|
||||
'keycaptcha' => array( 'guest_captcha', 'keycaptcha_privatekey' ),
|
||||
'hcaptcha' => array( 'guest_captcha', 'hcaptcha_sitekey', 'hcaptcha_secret')
|
||||
);
|
||||
break;
|
||||
$toggles = [
|
||||
'none' => [ 'bot_antispam_type_warning' ],
|
||||
'turnstile' => [ 'turnstile_site_key', 'turnstile_secret_key' ],
|
||||
'recaptcha2' => [ 'recaptcha2_public_key', 'recaptcha2_private_key' ],
|
||||
'invisible' => [ 'recaptcha2_public_key', 'recaptcha2_private_key' ],
|
||||
'keycaptcha' => [ 'keycaptcha_privatekey' ],
|
||||
'hcaptcha' => [ 'hcaptcha_sitekey', 'hcaptcha_secret' ]
|
||||
];
|
||||
|
||||
if( \IPS\Settings::i()->bot_antispam_type != 'keycaptcha' )
|
||||
{
|
||||
unset( $toggles['keycaptcha'] );
|
||||
}
|
||||
return $toggles;
|
||||
}
|
||||
|
||||
return array();
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,16 +172,7 @@ class _spam extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
/* Build Form */
|
||||
$form = new \IPS\Helpers\Form;
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'bot_antispam_type', \IPS\Settings::i()->bot_antispam_type, TRUE, array(
|
||||
'options' => $this->getCaptchaOptions( 'options' ),
|
||||
'toggles' => $this->getCaptchaOptions( 'toggles' ),
|
||||
), NULL, NULL, NULL, 'bot_antispam_type' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'recaptcha2_public_key', \IPS\Settings::i()->recaptcha2_public_key, FALSE, array(), NULL, NULL, NULL, 'recaptcha2_public_key' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'recaptcha2_private_key', \IPS\Settings::i()->recaptcha2_private_key, FALSE, array(), NULL, NULL, NULL, 'recaptcha2_private_key' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'keycaptcha_privatekey', \IPS\Settings::i()->keycaptcha_privatekey, FALSE, array(), NULL, NULL, NULL, 'keycaptcha_privatekey' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'hcaptcha_sitekey', \IPS\Settings::i()->hcaptcha_sitekey, FALSE, array(), NULL, NULL, NULL, 'hcaptcha_sitekey' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'hcaptcha_secret', \IPS\Settings::i()->hcaptcha_secret, FALSE, array(), NULL, NULL, NULL, 'hcaptcha_secret' ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'guest_captcha', \IPS\Settings::i()->guest_captcha, FALSE, array(), NULL, NULL, NULL, 'guest_captcha' ) );
|
||||
static::captchaForm( $form );
|
||||
|
||||
/* Save values */
|
||||
if ( $form->values() )
|
||||
@@ -172,6 +188,35 @@ class _spam extends \IPS\Dispatcher\Controller
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \IPS\Helpers\Form $form
|
||||
* @return void
|
||||
*/
|
||||
public static function captchaForm( Form $form ): void
|
||||
{
|
||||
$form->add( new Radio( 'bot_antispam_type', Settings::i()->bot_antispam_type, TRUE, array(
|
||||
'options' => static::getCaptchaOptions( 'options' ),
|
||||
'toggles' => static::getCaptchaOptions( 'toggles' ),
|
||||
), NULL, NULL, NULL, 'bot_antispam_type' ) );
|
||||
$form->add( new Text( 'turnstile_site_key', Settings::i()->turnstile_site_key, id: 'turnstile_site_key' ) );
|
||||
$form->add( new Text( 'turnstile_secret_key', Settings::i()->turnstile_secret_key, id: 'turnstile_secret_key' ) );
|
||||
|
||||
if( \IPS\CIC )
|
||||
{
|
||||
\IPS\Member::loggedIn()->language()->words['turnstile_secret_key_desc'] = \IPS\Member::loggedIn()->language()->addToStack('turnstile_secret_key_cloud_desc');
|
||||
}
|
||||
|
||||
$form->add( new Text( 'recaptcha2_public_key', Settings::i()->recaptcha2_public_key, FALSE, array(), NULL, NULL, NULL, 'recaptcha2_public_key' ) );
|
||||
$form->add( new Text( 'recaptcha2_private_key', Settings::i()->recaptcha2_private_key, FALSE, array(), NULL, NULL, NULL, 'recaptcha2_private_key' ) );
|
||||
if( \IPS\Settings::i()->bot_antispam_type == 'keycaptcha' )
|
||||
{
|
||||
$form->add( new Text( 'keycaptcha_privatekey', Settings::i()->keycaptcha_privatekey, FALSE, [], NULL, NULL, NULL, 'keycaptcha_privatekey' ) );
|
||||
}
|
||||
$form->add( new Text( 'hcaptcha_sitekey', Settings::i()->hcaptcha_sitekey, FALSE, array(), NULL, NULL, NULL, 'hcaptcha_sitekey' ) );
|
||||
$form->add( new Text( 'hcaptcha_secret', Settings::i()->hcaptcha_secret, FALSE, array(), NULL, NULL, NULL, 'hcaptcha_secret' ) );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Show spammer flagging settings
|
||||
*
|
||||
@@ -217,16 +262,16 @@ class _spam extends \IPS\Dispatcher\Controller
|
||||
$disabled = TRUE;
|
||||
if( !\IPS\Settings::i()->ipb_reg_number )
|
||||
{
|
||||
\IPS\Member::loggedIn()->language()->words['spam_service_enabled_desc'] = \IPS\Member::loggedIn()->language()->addToStack( '__nulling_null_alert' ); //Changing error message to custom
|
||||
\IPS\Member::loggedIn()->language()->words['spam_service_enabled_desc'] = \IPS\Member::loggedIn()->language()->addToStack( '__ipbmafia__nulling_null_alert' ); //Changing error message to custom
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Member::loggedIn()->language()->words['spam_service_enabled_desc'] = \IPS\Member::loggedIn()->language()->addToStack( '__nulling_null_alert' ); //Changing error message to custom
|
||||
\IPS\Member::loggedIn()->language()->words['spam_service_enabled_desc'] = \IPS\Member::loggedIn()->language()->addToStack( '__ipbmafia__nulling_null_alert' ); //Changing error message to custom
|
||||
}
|
||||
}
|
||||
|
||||
$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', 'spam_service_disposable' ) ) ) );
|
||||
$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\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', 'spam_service_disposable' ) ) ) );
|
||||
$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' ) );
|
||||
@@ -697,11 +742,11 @@ class _spam extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
if( !\IPS\Settings::i()->ipb_reg_number )
|
||||
{
|
||||
\IPS\Member::loggedIn()->language()->words['spam_service_enabled_desc'] = \IPS\Member::loggedIn()->language()->addToStack( '__nulling_null_alert' ); //Changing error message to custom
|
||||
\IPS\Member::loggedIn()->language()->words['spam_service_enabled_desc'] = \IPS\Member::loggedIn()->language()->addToStack( '__ipbmafia__nulling_null_alert' ); //Changing error message to custom
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Member::loggedIn()->language()->words['spam_service_enabled_desc'] = \IPS\Member::loggedIn()->language()->addToStack( '__nulling_null_alert' ); //Changing error message to custom
|
||||
\IPS\Member::loggedIn()->language()->words['spam_service_enabled_desc'] = \IPS\Member::loggedIn()->language()->addToStack( '__ipbmafia__nulling_null_alert' ); //Changing error message to custom
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -325,6 +325,11 @@ class _files extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
$cicDisabled[ $row['id'] ] = $row['id'];
|
||||
}
|
||||
|
||||
if ( $row['method'] == 'Amazon' AND \IPS\CIC AND json_decode( $row['configuration'], TRUE )['bucket'] == 'ips-cic-filestore' )
|
||||
{
|
||||
$cicDisabled[ $row['id'] ] = $row['id'];
|
||||
}
|
||||
}
|
||||
|
||||
$form = new \IPS\Helpers\Form;
|
||||
@@ -492,6 +497,18 @@ class _files extends \IPS\Dispatcher\Controller
|
||||
|
||||
$table->rowButtons = function( $row )
|
||||
{
|
||||
$config = json_decode( $row['configuration'], true );
|
||||
if( \IPS\CIC AND ( ( $row['method'] == 'Amazon' AND $config['bucket'] == 'ips-cic-filestore' ) OR $row['method'] == 'Cloud' ) )
|
||||
{
|
||||
return array(
|
||||
'log' => array(
|
||||
'icon' => 'search',
|
||||
'title' => 'file_config_log_title',
|
||||
'link' => \IPS\Http\Url::internal( "app=core&module=overview&controller=files&do=configurationLog&id={$row['id']}" )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return array(
|
||||
'edit' => array(
|
||||
'icon' => 'pencil',
|
||||
@@ -576,6 +593,18 @@ class _files extends \IPS\Dispatcher\Controller
|
||||
$current = \IPS\Db::i()->select( '*', 'core_file_storage', array( 'id=?', \intval( \IPS\Request::i()->id ) ) )->first();
|
||||
$currentHandlerSettings = json_decode( $current['configuration'], TRUE );
|
||||
|
||||
/* If this is the special S3 handler for Cloud, throw error */
|
||||
if ( \IPS\CIC AND $current['method'] == 'Amazon' AND $currentHandlerSettings['bucket'] == 'ips-cic-filestore' )
|
||||
{
|
||||
throw new \UnderflowException;
|
||||
}
|
||||
|
||||
/* If this is CiC and is FileSystem, error */
|
||||
if ( \IPS\CIC AND $current['method'] == 'FileSystem' )
|
||||
{
|
||||
throw new \UnderflowException;
|
||||
}
|
||||
|
||||
if ( \in_array( \intval( \IPS\Request::i()->id ), array_filter( json_decode( \IPS\Settings::i()->upload_settings, TRUE ), function( $key ){ return $key != 'filestorage_move'; }, ARRAY_FILTER_USE_KEY ) ) )
|
||||
{
|
||||
if ( isset( \IPS\Request::i()->delete ) )
|
||||
@@ -626,6 +655,11 @@ class _files extends \IPS\Dispatcher\Controller
|
||||
$toggles = array();
|
||||
foreach ( \IPS\File::storageHandlers( $current ) as $key => $class )
|
||||
{
|
||||
if ( \IPS\CIC AND $key == 'Cloud' AND !\IPS\Member::loggedIn()->members_bitoptions['is_support_account'] )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$handlers[ $key ] = 'filehandler__' . $key;
|
||||
foreach ( $class::settings( $currentHandlerSettings ) as $k => $v )
|
||||
{
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
namespace IPS\core\modules\admin\overview;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
|
||||
use IPS\core\modules\admin\moderation\spam;
|
||||
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
@@ -205,6 +208,10 @@ class _onboard extends \IPS\Dispatcher\Controller
|
||||
/* Get the htaccess mod_Rewrite setting */
|
||||
$form->addTab( 'onboard_tab_furls' );
|
||||
\IPS\core\modules\admin\promotion\seo::htaccessSetting( $form );
|
||||
|
||||
/* Add captcha. */
|
||||
$form->addTab( 'onboard_tab_spamprevention' );
|
||||
spam::captchaForm( $form );
|
||||
}
|
||||
|
||||
/* Add the terms/privacy policy/etc. */
|
||||
|
||||
@@ -116,9 +116,16 @@ class _promote extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
try
|
||||
{
|
||||
$response = \IPS\Http\Url::external( "https://api-ssl.bitly.com/v3/shorten" )->setQueryString( array( 'access_token' => $val, 'longUrl' => 'https://www.invisioncommunity.com' ) )->request()->get()->decodeJson();
|
||||
$request = \IPS\Http\Url::external( "https://api-ssl.bitly.com/v4/shorten" )->request()
|
||||
->setHeaders([
|
||||
'Content-Type' => 'application/json',
|
||||
'Authorization' => 'Bearer ' . $val
|
||||
])
|
||||
->post( json_encode( [
|
||||
'long_url' => 'https://www.invisioncommunity.com',
|
||||
] ) );
|
||||
|
||||
if ( $response['status_code'] !== 200 )
|
||||
if ( (int) $request->httpResponseCode !== 201 )
|
||||
{
|
||||
throw new \DomainException("bitly_auth_failed");
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ class _seo extends \IPS\Dispatcher\Controller
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'seo_manage' );
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'seo_furls' );
|
||||
|
||||
/* Get tab content */
|
||||
$this->activeTab = \IPS\Request::i()->tab ?: 'urls';
|
||||
@@ -189,6 +189,8 @@ class _seo extends \IPS\Dispatcher\Controller
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Codemirror( 'use_robotstxt_custom_editor', ( $value === 'custom' ? \IPS\Settings::i()->robots_txt : '' ), FALSE, [], NULL, NULL, NULL, 'use_robotstxt_custom_editor' ) );
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'seo_reduce_links', \IPS\Settings::i()->seo_reduce_links, TRUE, [], NULL, NULL, NULL, 'seo_reduce_links' ) );
|
||||
|
||||
/* Are we saving? */
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
@@ -199,7 +201,7 @@ class _seo extends \IPS\Dispatcher\Controller
|
||||
$value = $values['use_robotstxt_custom_editor'];
|
||||
}
|
||||
|
||||
$form->saveAsSettings( [ 'robots_txt' => $value ] );
|
||||
$form->saveAsSettings( [ 'robots_txt' => $value, 'seo_reduce_links' => $values['seo_reduce_links'] ] );
|
||||
|
||||
\IPS\Session::i()->log( 'acplog__robotstxt_edited' );
|
||||
|
||||
@@ -275,7 +277,10 @@ class _seo extends \IPS\Dispatcher\Controller
|
||||
/* Init */
|
||||
$form = new \IPS\Helpers\Form;
|
||||
$form->addMessage( 'sitemap_blurb', 'ipsMessage ipsMessage_info' );
|
||||
$form->add( new \IPS\Helpers\Form\Url( 'sitemap_url', \IPS\Settings::i()->sitemap_url ?: \IPS\Settings::i()->base_url . 'sitemap.php', FALSE ) );
|
||||
if( !\IPS\CIC )
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\Url( 'sitemap_url', \IPS\Settings::i()->sitemap_url ?: \IPS\Settings::i()->base_url . 'sitemap.php', FALSE ) );
|
||||
}
|
||||
|
||||
/* Get extension settings */
|
||||
$useRecommendedSettings = TRUE;
|
||||
@@ -312,34 +317,37 @@ class _seo extends \IPS\Dispatcher\Controller
|
||||
/* Are we saving? */
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
if ( $values['sitemap_configuration_info'] )
|
||||
if( $values[ 'sitemap_configuration_info' ] )
|
||||
{
|
||||
$values = array_merge( $values, $recommendedSettings );
|
||||
}
|
||||
|
||||
try
|
||||
if( !\IPS\CIC )
|
||||
{
|
||||
if( !( $values['sitemap_url'] instanceof \IPS\Http\Url ) )
|
||||
try
|
||||
{
|
||||
throw new \RuntimeException;
|
||||
if( !( $values[ 'sitemap_url' ] instanceof \IPS\Http\Url ) )
|
||||
{
|
||||
throw new \RuntimeException;
|
||||
}
|
||||
$response = $values[ 'sitemap_url' ]->setQueryString( 'testsettings', 1 )->request()->get();
|
||||
|
||||
if( $response->httpResponseCode != 200 or !mb_strpos( $values[ 'sitemap_url' ], 'sitemap.php' ) )
|
||||
{
|
||||
$form->error = \IPS\Member::loggedIn()->language()->addToStack( 'invalid_sitemap_url' );
|
||||
}
|
||||
}
|
||||
|
||||
$response = $values['sitemap_url']->setQueryString( 'testsettings', 1 )->request()->get();
|
||||
|
||||
if ( $response->httpResponseCode != 200 or !mb_strpos( $values['sitemap_url'], "sitemap.php" ) )
|
||||
catch( \RuntimeException $e )
|
||||
{
|
||||
$form->error = \IPS\Member::loggedIn()->language()->addToStack( 'invalid_sitemap_url' );
|
||||
$form->error = \IPS\Member::loggedIn()->language()->addToStack( 'form_url_bad' );
|
||||
}
|
||||
|
||||
}
|
||||
catch ( \RuntimeException $e )
|
||||
{
|
||||
$form->error = \IPS\Member::loggedIn()->language()->addToStack( 'form_url_bad' );
|
||||
}
|
||||
|
||||
if( !$form->error )
|
||||
{
|
||||
$values['sitemap_url'] = (string) $values['sitemap_url'];
|
||||
if( isset( $values[ 'sitemap_url' ] ) )
|
||||
{
|
||||
$values['sitemap_url'] = (string) $values['sitemap_url'];
|
||||
}
|
||||
|
||||
|
||||
foreach( \IPS\Application::allExtensions( 'core', 'Sitemap', FALSE, 'core' ) as $extKey => $extension )
|
||||
{
|
||||
@@ -348,9 +356,10 @@ class _seo extends \IPS\Dispatcher\Controller
|
||||
$extension->saveSettings( $values );
|
||||
}
|
||||
}
|
||||
|
||||
$form->saveAsSettings( array( 'sitemap_url' => $values['sitemap_url'] ) );
|
||||
|
||||
if( !\IPS\CIC )
|
||||
{
|
||||
$form->saveAsSettings( ['sitemap_url' => $values[ 'sitemap_url' ]] );
|
||||
}
|
||||
/* Clear guest page caches */
|
||||
\IPS\Data\Cache::i()->clearAll();
|
||||
|
||||
|
||||
@@ -354,6 +354,22 @@ class _dataLayer extends \IPS\Dispatcher\Controller
|
||||
|
||||
if ( $property['custom'] ?? 0 )
|
||||
{
|
||||
$defaults = [
|
||||
'pii' => false,
|
||||
'value' => null,
|
||||
'page_level' => true,
|
||||
'short' => "",
|
||||
'description' => "",
|
||||
'event_keys' => []
|
||||
];
|
||||
|
||||
foreach ( $defaults as $key => $value )
|
||||
{
|
||||
if ( !array_key_exists( $key, $property ) )
|
||||
{
|
||||
$property[$key] = $value;
|
||||
}
|
||||
}
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'datalayer_property_value', $property['value'], true ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'datalayer_property_page_level', $property['page_level'], true ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'datalayer_property_short', $property['short'], false, array( 'maxLength' => 50 ) ) );
|
||||
|
||||
@@ -10,6 +10,30 @@
|
||||
|
||||
namespace IPS\core\modules\admin\settings;
|
||||
|
||||
use IPS\core\AdminNotification;
|
||||
use IPS\DateTime;
|
||||
use IPS\Db;
|
||||
use IPS\Dispatcher;
|
||||
use IPS\Email;
|
||||
use IPS\File;
|
||||
use IPS\Helpers\Form;
|
||||
use IPS\Helpers\Form\Color;
|
||||
use IPS\Helpers\Form\Email as FormEmail;
|
||||
use IPS\Helpers\Form\Interval;
|
||||
use IPS\Helpers\Form\Radio;
|
||||
use IPS\Helpers\Form\Text;
|
||||
use IPS\Helpers\Form\TextArea;
|
||||
use IPS\Helpers\Form\Upload;
|
||||
use IPS\Helpers\Form\YesNo;
|
||||
use IPS\Http\Url;
|
||||
use IPS\Member;
|
||||
use IPS\Output;
|
||||
use IPS\Request;
|
||||
use IPS\Session;
|
||||
use IPS\Settings;
|
||||
use IPS\Theme;
|
||||
use const IPS\DEMO_MODE;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
@@ -34,7 +58,7 @@ class _email extends \IPS\Dispatcher\Controller
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'email_manage' );
|
||||
Dispatcher::i()->checkAcpPermission( 'email_manage' );
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
@@ -48,32 +72,32 @@ class _email extends \IPS\Dispatcher\Controller
|
||||
/* Build the settings form */
|
||||
$messageHtml = '';
|
||||
|
||||
$settingsForm = new \IPS\Helpers\Form( 'settings_form' );
|
||||
$settingsForm = new Form( 'settings_form' );
|
||||
$settingsForm->addHeader( 'basic_settings' );
|
||||
$settingsForm->add( new \IPS\Helpers\Form\Email( 'email_out', \IPS\Settings::i()->email_out, TRUE, array(), NULL, NULL, NULL, 'email_out' ) );
|
||||
$settingsForm->add( new \IPS\Helpers\Form\Email( 'email_in', \IPS\Settings::i()->email_in, TRUE ) );
|
||||
$settingsForm->add( new \IPS\Helpers\Form\Color( 'email_color', \IPS\Settings::i()->email_color, TRUE ) );
|
||||
$settingsForm->add( new \IPS\Helpers\Form\Upload( 'email_logo', \IPS\Settings::i()->email_logo ? \IPS\File::get( 'core_Theme', \IPS\Settings::i()->email_logo ) : NULL, FALSE, array( 'image' => TRUE, 'storageExtension' => 'core_Theme' ) ) );
|
||||
$settingsForm->add( new \IPS\Helpers\Form\YesNo( 'social_links_in_email', \IPS\Settings::i()->social_links_in_email, FALSE, array(), NULL, NULL, NULL, 'social_links_in_email' ) );
|
||||
$settingsForm->add( new FormEmail( 'email_out', Settings::i()->email_out, TRUE, [], NULL, NULL, NULL, 'email_out' ) );
|
||||
$settingsForm->add( new FormEmail( 'email_in', Settings::i()->email_in, TRUE ) );
|
||||
$settingsForm->add( new Color( 'email_color', Settings::i()->email_color, TRUE ) );
|
||||
$settingsForm->add( new Upload( 'email_logo', Settings::i()->email_logo ? File::get( 'core_Theme', Settings::i()->email_logo ) : NULL, FALSE, [ 'image' => TRUE, 'storageExtension' => 'core_Theme' ] ) );
|
||||
$settingsForm->add( new YesNo( 'social_links_in_email', Settings::i()->social_links_in_email, FALSE, [], NULL, NULL, NULL, 'social_links_in_email' ) );
|
||||
|
||||
if ( \IPS\Settings::i()->promote_community_enabled )
|
||||
if ( Settings::i()->promote_community_enabled )
|
||||
{
|
||||
$settingsForm->add( new \IPS\Helpers\Form\YesNo( 'our_picks_in_email', \IPS\Settings::i()->our_picks_in_email, FALSE, array(), NULL, NULL, NULL, 'our_picks_in_email' ) );
|
||||
$settingsForm->add( new YesNo( 'our_picks_in_email', Settings::i()->our_picks_in_email, FALSE, [], NULL, NULL, NULL, 'our_picks_in_email' ) );
|
||||
}
|
||||
|
||||
$settingsForm->add( new \IPS\Helpers\Form\YesNo( 'email_truncate', ( \IPS\Settings::i()->email_truncate ), FALSE, array(), NULL, NULL, NULL, 'email_truncate' ) );
|
||||
$settingsForm->add( new \IPS\Helpers\Form\YesNo( 'email_log_do', ( \IPS\Settings::i()->prune_log_emailstats != 0 ), FALSE, array( 'togglesOn' => array( 'prune_log_emailstats' ) ), NULL, NULL, NULL, 'email_log_do' ) );
|
||||
$settingsForm->add( new \IPS\Helpers\Form\Interval( 'prune_log_emailstats', \IPS\Settings::i()->prune_log_emailstats ?: 60, FALSE, array( 'valueAs' => 'd', 'unlimited' => '-1', 'unlimitedLang' => 'never' ), NULL, NULL, NULL, 'prune_log_emailstats' ) );
|
||||
if ( !\IPS\DEMO_MODE )
|
||||
$settingsForm->add( new YesNo( 'email_truncate', ( Settings::i()->email_truncate ), FALSE, [], NULL, NULL, NULL, 'email_truncate' ) );
|
||||
$settingsForm->add( new YesNo( 'email_log_do', ( Settings::i()->prune_log_emailstats != 0 ), FALSE, [ 'togglesOn' => [ 'prune_log_emailstats' ] ], NULL, NULL, NULL, 'email_log_do' ) );
|
||||
$settingsForm->add( new Interval( 'prune_log_emailstats', Settings::i()->prune_log_emailstats ?: 60, FALSE, [ 'valueAs' => 'd', 'unlimited' => '-1', 'unlimitedLang' => 'never' ], NULL, NULL, NULL, 'prune_log_emailstats' ) );
|
||||
if ( !DEMO_MODE )
|
||||
{
|
||||
$settingsForm->addHeader( 'advanced_settings' );
|
||||
|
||||
$method = \IPS\Settings::i()->mail_method;
|
||||
$disabled = array();
|
||||
$method = Settings::i()->mail_method;
|
||||
$disabled = [];
|
||||
|
||||
if ( \IPS\Settings::i()->sendgrid_api_key AND \IPS\Settings::i()->sendgrid_use_for > 0 )
|
||||
if ( Settings::i()->sendgrid_api_key AND Settings::i()->sendgrid_use_for > 0 )
|
||||
{
|
||||
if ( \IPS\Settings::i()->sendgrid_use_for == 2 )
|
||||
if ( Settings::i()->sendgrid_use_for == 2 )
|
||||
{
|
||||
$method = 'sendgrid';
|
||||
}
|
||||
@@ -89,23 +113,49 @@ class _email extends \IPS\Dispatcher\Controller
|
||||
$method = 'php';
|
||||
}
|
||||
|
||||
$settingsForm->add( new \IPS\Helpers\Form\Radio( 'mail_method', $method, TRUE, array(
|
||||
'options' => array( 'php' => ( \IPS\CIC ? 'mail_method_cic' : 'mail_method_php' ), 'smtp' => 'mail_method_smtp', 'sendgrid' => 'mail_method_sendgrid' ),
|
||||
'disabled' => $disabled,
|
||||
'toggles' => array(
|
||||
'php' => array( 'php_mail_extra' ),
|
||||
'smtp' => array( 'smtp_host', 'smtp_port', 'smtp_user', 'smtp_pass', 'smtp_helo', 'smtp_protocol' ),
|
||||
)
|
||||
) ) );
|
||||
$options = array( 'plain' => 'smtp_plaintext', 'ssl' => 'smtp_ssl', 'tls' => 'smtp_tls' );
|
||||
$settingsForm->add( new \IPS\Helpers\Form\Text( 'smtp_host', \IPS\Settings::i()->smtp_host, FALSE, array(), NULL, NULL, NULL, 'smtp_host' ) );
|
||||
$settingsForm->add( new \IPS\Helpers\Form\Select( 'smtp_protocol', \IPS\Settings::i()->smtp_protocol, FALSE, array( 'options' => $options ), NULL, NULL, NULL, 'smtp_protocol' ) );
|
||||
$settingsForm->add( new \IPS\Helpers\Form\Number( 'smtp_port', \IPS\Settings::i()->smtp_port, FALSE, array(), NULL, NULL, NULL, 'smtp_port' ) );
|
||||
$settingsForm->add( new \IPS\Helpers\Form\Text( 'smtp_user', \IPS\Settings::i()->smtp_user, FALSE, array(), NULL, NULL, NULL, 'smtp_user' ) );
|
||||
$settingsForm->add( new \IPS\Helpers\Form\Password( 'smtp_pass', \IPS\Settings::i()->smtp_pass, FALSE, array( 'enforceMaxLimit' => FALSE ), NULL, NULL, NULL, 'smtp_pass' ) );
|
||||
if ( !\IPS\CIC )
|
||||
$debug = FALSE;
|
||||
if( Email::classToUse( Email::TYPE_TRANSACTIONAL ) == 'IPS\\Email\\Outgoing\\Debug' )
|
||||
{
|
||||
$settingsForm->add( new \IPS\Helpers\Form\Text( 'php_mail_extra', \IPS\Settings::i()->php_mail_extra, FALSE, array(), NULL, NULL, NULL, 'php_mail_extra' ) );
|
||||
$debug = true;
|
||||
$method = 'debug';
|
||||
}
|
||||
|
||||
$mailOptions = $toggles = $fields = [];
|
||||
foreach( Email::outgoingHandlers() as $key => $handler )
|
||||
{
|
||||
if( ! $handler::isUsable( Email::TYPE_TRANSACTIONAL ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
$mailOptions[ $key ] = 'mail_method_' . $key;
|
||||
|
||||
$formFields = $handler::form();
|
||||
$toggles[ $key ] = array_keys( $formFields );
|
||||
$fields = $fields + $formFields;
|
||||
}
|
||||
|
||||
/* Change value back to default for platform */
|
||||
if( !\in_array( $method, array_keys( $mailOptions ) ) )
|
||||
{
|
||||
$method = \IPS\CIC ? 'cloud' : 'php';
|
||||
}
|
||||
|
||||
if( !$debug )
|
||||
{
|
||||
$settingsForm->add( new Radio( 'mail_method', $method, TRUE, [
|
||||
'options' => $mailOptions,
|
||||
'disabled' => $disabled,
|
||||
'toggles' => $toggles
|
||||
] ) );
|
||||
|
||||
foreach( $fields as $f )
|
||||
{
|
||||
$settingsForm->add( $f );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$settingsForm->addDummy( 'mail_method', Member::loggedIn()->language()->addToStack('mail_method_debug', false, [ 'sprintf' => [ \IPS\EMAIL_DEBUG_PATH ] ] ) );
|
||||
}
|
||||
}
|
||||
if ( $values = $settingsForm->values() )
|
||||
@@ -114,7 +164,7 @@ class _email extends \IPS\Dispatcher\Controller
|
||||
|
||||
foreach( array( 'mail_method', 'smtp_host', 'smtp_port', 'smtp_pass', 'smtp_user', 'php_mail_extra', 'smtp_protocol' ) as $setting )
|
||||
{
|
||||
if( $values[ $setting ] != \IPS\Settings::i()->$setting )
|
||||
if( isset( $values[ $setting ] ) AND $values[ $setting ] != Settings::i()->$setting )
|
||||
{
|
||||
$sendTestEmail = TRUE;
|
||||
}
|
||||
@@ -126,14 +176,11 @@ class _email extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
|
||||
unset( $values['email_log_do'] );
|
||||
|
||||
if ( $values['mail_method'] != 'sendgrid' and \IPS\Settings::i()->sendgrid_use_for == 2 )
|
||||
|
||||
/* Allow individual handlers to save settings */
|
||||
foreach( Email::outgoingHandlers() as $key => $handler )
|
||||
{
|
||||
$values['sendgrid_use_for'] = 1;
|
||||
}
|
||||
elseif ( $values['mail_method'] == 'sendgrid' )
|
||||
{
|
||||
$values['sendgrid_use_for'] = 2;
|
||||
$values = $handler::processSettings( $values );
|
||||
}
|
||||
|
||||
if ( $values['email_logo'] )
|
||||
@@ -142,57 +189,56 @@ class _email extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
|
||||
$settingsForm->saveAsSettings( $values );
|
||||
\IPS\Session::i()->log( 'acplogs__email_settings' );
|
||||
Session::i()->log( 'acplogs__email_settings' );
|
||||
|
||||
if( $sendTestEmail )
|
||||
{
|
||||
$email = \IPS\Email::buildFromContent( \IPS\Member::loggedIn()->language()->get('email_test_subject'), \IPS\Member::loggedIn()->language()->addToStack('email_test_message'), \IPS\Member::loggedIn()->language()->addToStack('email_test_message'), \IPS\Email::TYPE_TRANSACTIONAL );
|
||||
$email = Email::buildFromContent( Member::loggedIn()->language()->get('email_test_subject'), Member::loggedIn()->language()->addToStack('email_test_message'), Member::loggedIn()->language()->addToStack('email_test_message'), Email::TYPE_BULK );
|
||||
|
||||
try
|
||||
{
|
||||
$result = $email->_send( \IPS\Member::loggedIn(), array(), array(), \IPS\Settings::i()->email_out );
|
||||
$messageHtml = \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->message( 'email_test_okay_auto', 'success' );
|
||||
$email->_send( Member::loggedIn(), fromEmail: Settings::i()->email_out );
|
||||
$messageHtml = Theme::i()->getTemplate( 'global', 'core', 'global' )->message( 'email_test_okay_auto', 'success' );
|
||||
|
||||
/* Sent successfully, remove notification */
|
||||
\IPS\core\AdminNotification::remove( 'core', 'ConfigurationError', 'failedMail' );
|
||||
\IPS\Db::i()->update( 'core_mail_error_logs', array( 'mlog_notification_sent' => TRUE ) );
|
||||
AdminNotification::remove( 'core', 'ConfigurationError', 'failedMail' );
|
||||
Db::i()->update( 'core_mail_error_logs', [ 'mlog_notification_sent' => TRUE ] );
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
$messageHtml = \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->message( 'email_test_error_auto', 'error', $e->getMessage() );
|
||||
$messageHtml = Theme::i()->getTemplate( 'global', 'core', 'global' )->message( 'email_test_error_auto', 'error', $e->getMessage() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Build the test form */
|
||||
$testFormHtml = '';
|
||||
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'settings', 'email_test' ) )
|
||||
if ( !DEMO_MODE AND Member::loggedIn()->hasAcpRestriction( 'core', 'settings', 'email_test' ) )
|
||||
{
|
||||
\IPS\Output::i()->sidebar['actions']['test'] = array(
|
||||
Output::i()->sidebar['actions']['test'] = array(
|
||||
'title' => 'email_test',
|
||||
'icon' => 'cog',
|
||||
'link' => \IPS\Http\Url::internal( '#testForm' ),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('email_test'), 'ipsDialog-content' => '#testForm' )
|
||||
'link' => Url::internal( '#testForm' ),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => Member::loggedIn()->language()->addToStack('email_test'), 'ipsDialog-content' => '#testForm' )
|
||||
);
|
||||
|
||||
$testForm = new \IPS\Helpers\Form( 'test_form' );
|
||||
$testForm->add( new \IPS\Helpers\Form\Email( 'from', \IPS\Settings::i()->email_out, TRUE ) );
|
||||
$testForm->add( new \IPS\Helpers\Form\Email( 'to', \IPS\Member::loggedIn()->email, TRUE ) );
|
||||
$testForm->add( new \IPS\Helpers\Form\Text( 'subject', \IPS\Member::loggedIn()->language()->addToStack('email_test_subject'), TRUE ) );
|
||||
$testForm->add( new \IPS\Helpers\Form\TextArea( 'message', \IPS\Member::loggedIn()->language()->addToStack('email_test_message'), TRUE ) );
|
||||
$testForm = new Form( 'test_form' );
|
||||
$testForm->add( new FormEmail( 'from', Settings::i()->email_out, TRUE ) );
|
||||
$testForm->add( new FormEmail( 'to', Member::loggedIn()->email, TRUE ) );
|
||||
$testForm->add( new Text( 'subject', Member::loggedIn()->language()->addToStack('email_test_subject'), TRUE ) );
|
||||
$testForm->add( new TextArea( 'message', Member::loggedIn()->language()->addToStack('email_test_message'), TRUE ) );
|
||||
if ( $values = $testForm->values() )
|
||||
{
|
||||
try
|
||||
{
|
||||
$email = \IPS\Email::buildFromContent( $values['subject'], $values['message'], $values['message'], \IPS\Email::TYPE_TRANSACTIONAL );
|
||||
$result = $email->_send( $values['to'], array(), array(), $values['from'] );
|
||||
$email = Email::buildFromContent( $values['subject'], $values['message'], $values['message'], Email::TYPE_BULK );
|
||||
$email->_send( $values['to'], fromEmail: $values['from'] );
|
||||
|
||||
/* Sent successfully, remove notification */
|
||||
\IPS\core\AdminNotification::remove( 'core', 'ConfigurationError', 'failedMail' );
|
||||
\IPS\Db::i()->update( 'core_mail_error_logs', array( 'mlog_notification_sent' => TRUE ) );
|
||||
AdminNotification::remove( 'core', 'ConfigurationError', 'failedMail' );
|
||||
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' );
|
||||
$messageHtml = Theme::i()->getTemplate( 'global', 'core', 'global' )->message( 'email_test_okay', 'success' );
|
||||
}
|
||||
catch ( \Exception $exception )
|
||||
{
|
||||
@@ -201,28 +247,28 @@ class _email extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
$content = \get_class( $exception ) . ": " . $exception->getMessage() . " (" . $exception->getCode() . ")";
|
||||
}
|
||||
$messageHtml = \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->message( $content, 'error' );
|
||||
$messageHtml = Theme::i()->getTemplate( 'global', 'core', 'global' )->message( $content ?: 'email_test_error_auto', 'error' );
|
||||
}
|
||||
}
|
||||
|
||||
$testFormHtml = \IPS\Theme::i()->getTemplate( 'global' )->block( 'email_test', $testForm, FALSE, 'ipsJS_hide', 'testForm' );
|
||||
$testFormHtml = Theme::i()->getTemplate( 'global' )->block( 'email_test', $testForm, FALSE, 'ipsJS_hide', 'testForm' );
|
||||
}
|
||||
|
||||
/* Add a button for logs */
|
||||
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'settings', 'email_errorlog' ) )
|
||||
if ( Member::loggedIn()->hasAcpRestriction( 'core', 'settings', 'email_errorlog' ) )
|
||||
{
|
||||
\IPS\Output::i()->sidebar['actions']['errorLog'] = array(
|
||||
Output::i()->sidebar['actions']['errorLog'] = array(
|
||||
'title' => 'emailerrorlogs',
|
||||
'icon' => 'exclamation-triangle',
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=settings&controller=email&do=errorLog' ),
|
||||
'link' => Url::internal( 'app=core&module=settings&controller=email&do=errorLog' ),
|
||||
);
|
||||
}
|
||||
|
||||
/* Display */
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('email_settings');
|
||||
\IPS\Output::i()->output = $messageHtml;
|
||||
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate( 'global' )->block( 'email_settings', $settingsForm );
|
||||
\IPS\Output::i()->output .= $testFormHtml;
|
||||
Output::i()->title = Member::loggedIn()->language()->addToStack('email_settings');
|
||||
Output::i()->output = $messageHtml;
|
||||
Output::i()->output .= Theme::i()->getTemplate( 'global' )->block( 'email_settings', $settingsForm );
|
||||
Output::i()->output .= $testFormHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -232,34 +278,34 @@ class _email extends \IPS\Dispatcher\Controller
|
||||
*/
|
||||
protected function errorLog()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'email_errorlog' );
|
||||
Dispatcher::i()->checkAcpPermission( 'email_errorlog' );
|
||||
|
||||
/* Add a button for settings */
|
||||
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'settings', 'email_errorlog_prune' ) )
|
||||
if ( Member::loggedIn()->hasAcpRestriction( 'core', 'settings', 'email_errorlog_prune' ) )
|
||||
{
|
||||
\IPS\Output::i()->sidebar['actions'] = array(
|
||||
Output::i()->sidebar['actions'] = array(
|
||||
'settings' => array(
|
||||
'title' => 'prunesettings',
|
||||
'icon' => 'cog',
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=settings&controller=email&do=errorLogSettings' ),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('prunesettings') )
|
||||
'link' => Url::internal( 'app=core&module=settings&controller=email&do=errorLogSettings' ),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => Member::loggedIn()->language()->addToStack('prunesettings') )
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/* Display */
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('emailerrorlogs');
|
||||
\IPS\Output::i()->output = (string) static::emailErrorLogTable( \IPS\Http\Url::internal( 'app=core&module=settings&controller=email&do=errorLog' ) );
|
||||
Output::i()->title = Member::loggedIn()->language()->addToStack('emailerrorlogs');
|
||||
Output::i()->output = (string) static::emailErrorLogTable( Url::internal( 'app=core&module=settings&controller=email&do=errorLog' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get email error log table
|
||||
*
|
||||
* @param \IPS\Http\Url $url Base URL for table
|
||||
* @param Url $url Base URL for table
|
||||
* @param array $where Where Array
|
||||
* @return \IPS\Helpers\Table\Db
|
||||
*/
|
||||
public static function emailErrorLogTable( \IPS\Http\Url $url, array $where=array() ): \IPS\Helpers\Table\Db
|
||||
public static function emailErrorLogTable( Url $url, array $where=array() ): \IPS\Helpers\Table\Db
|
||||
{
|
||||
/* Create the table */
|
||||
$table = new \IPS\Helpers\Table\Db( 'core_mail_error_logs', $url, $where );
|
||||
@@ -287,13 +333,13 @@ class _email extends \IPS\Dispatcher\Controller
|
||||
$table->parsers = array(
|
||||
'mlog_date' => function( $val, $row )
|
||||
{
|
||||
return \IPS\DateTime::ts( $val );
|
||||
return DateTime::ts( $val );
|
||||
},
|
||||
'mlog_msg' => function( $val, $row )
|
||||
{
|
||||
if( !$val )
|
||||
{
|
||||
$val = \IPS\Member::loggedIn()->language()->get('phpmail_not_sent');
|
||||
$val = Member::loggedIn()->language()->get('phpmail_not_sent');
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -303,17 +349,17 @@ class _email extends \IPS\Dispatcher\Controller
|
||||
if( isset( $data['message'] ) )
|
||||
{
|
||||
$val = \is_string( $data['message'] ) ?
|
||||
( ( isset( $data['details'] ) AND $data['details'] ) ? \IPS\Member::loggedIn()->language()->addToStack( $data['message'], FALSE, array( 'sprintf' => $data['details'] ) ) : \IPS\Member::loggedIn()->language()->addToStack( $data['message'] ) ) :
|
||||
( ( isset( $data['details'] ) AND $data['details'] ) ? Member::loggedIn()->language()->addToStack( $data['message'], FALSE, array( 'sprintf' => $data['details'] ) ) : Member::loggedIn()->language()->addToStack( $data['message'] ) ) :
|
||||
$val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return \IPS\Theme::i()->getTemplate( 'logs' )->emailErrorLog( nl2br( htmlspecialchars( $val, ENT_DISALLOWED, 'UTF-8', FALSE ) ), $row );
|
||||
return Theme::i()->getTemplate( 'logs' )->emailErrorLog( nl2br( htmlspecialchars( $val, ENT_DISALLOWED, 'UTF-8', FALSE ) ), $row );
|
||||
},
|
||||
'mlog_content' => function( $val, $row )
|
||||
{
|
||||
return \IPS\Theme::i()->getTemplate( 'logs' )->emailErrorBody( $val, $row );
|
||||
return Theme::i()->getTemplate( 'logs' )->emailErrorBody( $val, $row );
|
||||
},
|
||||
);
|
||||
|
||||
@@ -325,15 +371,15 @@ class _email extends \IPS\Dispatcher\Controller
|
||||
$return['view'] = array(
|
||||
'icon' => 'search',
|
||||
'title' => 'view_email_error_body',
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=settings&controller=email&do=errorLogView&id=' ) . $row['mlog_id'],
|
||||
'link' => Url::internal( 'app=core&module=settings&controller=email&do=errorLogView&id=' ) . $row['mlog_id'],
|
||||
'hotkey' => 'Return',
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('emailerrorlogs_mlog_content') )
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => Member::loggedIn()->language()->addToStack('emailerrorlogs_mlog_content') )
|
||||
);
|
||||
|
||||
$return['resend'] = array(
|
||||
'icon' => 'refresh',
|
||||
'title' => 'resend_email_error',
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=settings&controller=email&do=errorLogResend&id=' . $row['mlog_id'] )->csrf(),
|
||||
'link' => Url::internal( 'app=core&module=settings&controller=email&do=errorLogResend&id=' . $row['mlog_id'] )->csrf(),
|
||||
);
|
||||
|
||||
return $return;
|
||||
@@ -350,11 +396,11 @@ class _email extends \IPS\Dispatcher\Controller
|
||||
*/
|
||||
public function errorLogResend()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'email_errorlog' );
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
Dispatcher::i()->checkAcpPermission( 'email_errorlog' );
|
||||
Session::i()->csrfCheck();
|
||||
|
||||
$id = (int) \IPS\Request::i()->id;
|
||||
$log = \IPS\Db::i()->select( '*', 'core_mail_error_logs', array( 'mlog_id=?', $id ) )->first();
|
||||
$id = (int) Request::i()->id;
|
||||
$log = Db::i()->select( '*', 'core_mail_error_logs', array( 'mlog_id=?', $id ) )->first();
|
||||
$emailData = json_decode( $log['mlog_resend_data'], true );
|
||||
|
||||
try
|
||||
@@ -370,7 +416,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'] : \IPS\Email::TYPE_TRANSACTIONAL );
|
||||
$email = Email::buildFromContent( $log['mlog_subject'], $emailData['body']['html'], $emailData['body']['plain'], isset( $emailData['type'] ) ? $emailData['type'] : Email::TYPE_TRANSACTIONAL );
|
||||
$email->_send(
|
||||
array_map( 'trim', explode( ',', $log['mlog_to'] ) ),
|
||||
isset( $emailData['headers']['Cc'] ) ? array_map( 'trim', explode( ',', $emailData['headers']['Cc'] ) ) : array(),
|
||||
@@ -382,19 +428,19 @@ class _email extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
catch( \Exception $e )
|
||||
{
|
||||
\IPS\Output::i()->error( $e->getMessage(), '4C143/1', 500, '' );
|
||||
Output::i()->error( $e->getMessage(), '4C143/1', 500, '' );
|
||||
}
|
||||
|
||||
\IPS\Db::i()->delete( 'core_mail_error_logs', array( 'mlog_id=?', $id ) );
|
||||
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( Email::countFailedMail( 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 ) );
|
||||
AdminNotification::remove( 'core', 'ConfigurationError', 'failedMail' );
|
||||
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' );
|
||||
Output::i()->redirect( Url::internal( "app=core&module=settings&controller=email&do=errorLog" ), 'emailerror_resent' );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -404,12 +450,12 @@ class _email extends \IPS\Dispatcher\Controller
|
||||
*/
|
||||
public function errorLogView()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'email_errorlog' );
|
||||
Dispatcher::i()->checkAcpPermission( 'email_errorlog' );
|
||||
|
||||
$id = (int) \IPS\Request::i()->id;
|
||||
$log = \IPS\Db::i()->select( '*', 'core_mail_error_logs', array( 'mlog_id=?', $id ) )->first();
|
||||
$id = (int) Request::i()->id;
|
||||
$log = Db::i()->select( '*', 'core_mail_error_logs', array( 'mlog_id=?', $id ) )->first();
|
||||
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('logs')->emailErrorBody( $log );
|
||||
Output::i()->output = Theme::i()->getTemplate('logs')->emailErrorBody( $log );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -419,19 +465,19 @@ class _email extends \IPS\Dispatcher\Controller
|
||||
*/
|
||||
protected function errorLogSettings()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'email_errorlog_prune' );
|
||||
Dispatcher::i()->checkAcpPermission( 'email_errorlog_prune' );
|
||||
|
||||
$form = new \IPS\Helpers\Form;
|
||||
$form->add( new \IPS\Helpers\Form\Interval( 'prune_log_email_error', \IPS\Settings::i()->prune_log_email_error, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL, 'prune_log_email_error' ) );
|
||||
$form = new Form;
|
||||
$form->add( new Interval( 'prune_log_email_error', Settings::i()->prune_log_email_error, FALSE, array( 'valueAs' => Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, Member::loggedIn()->language()->addToStack('after'), NULL, 'prune_log_email_error' ) );
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
$form->saveAsSettings();
|
||||
\IPS\Session::i()->log( 'acplog__emailerrorlog_settings' );
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=settings&controller=email&do=errorLog' ), 'saved' );
|
||||
Session::i()->log( 'acplog__emailerrorlog_settings' );
|
||||
Output::i()->redirect( Url::internal( 'app=core&module=settings&controller=email&do=errorLog' ), 'saved' );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('emailerrorlogssettings');
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('global')->block( 'emailerrorlogssettings', $form, FALSE );
|
||||
Output::i()->title = Member::loggedIn()->language()->addToStack('emailerrorlogssettings');
|
||||
Output::i()->output = Theme::i()->getTemplate('global')->block( 'emailerrorlogssettings', $form, FALSE );
|
||||
}
|
||||
}
|
||||
@@ -60,11 +60,7 @@ class _general extends \IPS\Dispatcher\Controller
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'site_site_elsewhere', \IPS\Settings::i()->site_site_elsewhere, FALSE, [ 'togglesOn' => [ 'site_main_url', 'site_main_title' ] ] ) );
|
||||
$form->add( new \IPS\Helpers\Form\Url( 'site_main_url', \IPS\Settings::i()->site_main_url, FALSE, [], NULL, NULL, NULL, 'site_main_url' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'site_main_title', \IPS\Settings::i()->site_main_title, FALSE, [], NULL, NULL, NULL, 'site_main_title' ) );
|
||||
/* $form->add( new \IPS\Helpers\Form\CheckboxSet( 'diagnostics_reporting', array( 'usage' => ( (bool) \IPS\Settings::i()->usage_reporting ), 'diagnostics' => ( (bool) \IPS\Settings::i()->diagnostics_reporting ) ), FALSE, array( 'options' => array(
|
||||
'usage' => 'diagnostics_reporting_usage',
|
||||
'diagnostics' => 'diagnostics_reporting_diagnostics',
|
||||
) ) ) ); */
|
||||
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
\IPS\Lang::saveCustom( 'core', "copyright_line_value", $values['copyright_line'] );
|
||||
@@ -78,11 +74,7 @@ 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' ) );
|
||||
|
||||
|
||||
$form->saveAsSettings( $values );
|
||||
|
||||
if ( $values['site_online'] )
|
||||
|
||||
@@ -242,7 +242,7 @@ class _login extends \IPS\Node\Controller
|
||||
}
|
||||
|
||||
/* Clear caches */
|
||||
unset( \IPS\Data\Store::i()->loginMethods );
|
||||
unset( \IPS\Data\Store::i()->loginMethods, \IPS\Data\Store::i()->essentialCookieNames );
|
||||
\IPS\Data\Cache::i()->clearAll();
|
||||
|
||||
/* Toggle */
|
||||
@@ -297,12 +297,18 @@ class _login extends \IPS\Node\Controller
|
||||
|
||||
/* Build Form */
|
||||
$form = new \IPS\Helpers\Form();
|
||||
$form->addHeader( 'ipb_bruteforce_attempts' );
|
||||
$form->addHeader( 'ipb_bruteforce_attempts_title' );
|
||||
$form->addMessage( 'ipb_bruteforce_attempts_block_desc', 'ipsMessage ipsMessage_info' );
|
||||
$form->add( new \IPS\Helpers\Form\Number( 'ipb_bruteforce_attempts', \IPS\Settings::i()->ipb_bruteforce_attempts, FALSE, array( 'min' => 0, 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), \IPS\Member::loggedIn()->language()->addToStack('failed_logins'), 'ipb_bruteforce_attempts' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Interval( 'ipb_bruteforce_period', \IPS\Settings::i()->ipb_bruteforce_period, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::MINUTES, 'min' => 0, 'max' => 10000, 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL, 'ipb_bruteforce_period' ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'ipb_bruteforce_unlock', \IPS\Settings::i()->ipb_bruteforce_unlock, FALSE, array(), NULL, NULL, NULL, 'ipb_bruteforce_unlock' ) );
|
||||
$form->addHeader( 'bruteforce_global_attempts_title' );
|
||||
$form->addMessage( 'bruteforce_global_attempts_block_desc', 'ipsMessage ipsMessage_info' );
|
||||
$form->add( new \IPS\Helpers\Form\Number( 'bruteforce_global_attempts', \IPS\Settings::i()->bruteforce_global_attempts, FALSE, array( 'min' => 5 ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), \IPS\Member::loggedIn()->language()->addToStack('failed_logins'), 'bruteforce_global_attempts' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Interval( 'bruteforce_global_period', \IPS\Settings::i()->bruteforce_global_period, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::MINUTES, 'min' => 0, 'max' => 10000 ), NULL, \IPS\Member::loggedIn()->language()->addToStack('after'), NULL, 'bruteforce_global_period' ) );
|
||||
$form->addHeader( 'login_settings' );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'new_device_email', \IPS\Settings::i()->new_device_email, FALSE ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'login_after_inactivity_notification', \IPS\Settings::i()->login_after_inactivity_notification, FALSE ) );
|
||||
|
||||
/* Save */
|
||||
if ( $values = $form->values() )
|
||||
@@ -310,7 +316,8 @@ class _login extends \IPS\Node\Controller
|
||||
/* unlock all locked members if we have disabled the locking */
|
||||
if ( \IPS\Settings::i()->ipb_bruteforce_attempts > 0 AND $values['ipb_bruteforce_attempts'] == 0 )
|
||||
{
|
||||
\IPS\Member::updateAllMembers( array( 'failed_logins' => NULL , 'failed_login_count' => 0 ) );
|
||||
\IPS\Member::updateAllMembers( array( 'failed_login_count' => 0 ) );
|
||||
\IPS\Db::i()->delete( 'core_login_failures', [ 'login_member_id IS NOT NULL' ] );
|
||||
|
||||
/* disable task */
|
||||
\IPS\Db::i()->update( 'core_tasks', array( 'enabled' => 0 ), "`key`='unlockmembers'" );
|
||||
@@ -360,7 +367,7 @@ class _login extends \IPS\Node\Controller
|
||||
protected function _registration()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'registration_settings' );
|
||||
|
||||
|
||||
/* Build Form */
|
||||
$form = new \IPS\Helpers\Form();
|
||||
$form->addHeader('registration_standard_settings');
|
||||
@@ -438,9 +445,10 @@ class _login extends \IPS\Node\Controller
|
||||
/* Redirect */
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=settings&controller=login&tab=registration' ), 'saved' );
|
||||
}
|
||||
$desc = \IPS\Theme::i()->getTemplate( 'global', 'core' )->message( \IPS\Member::loggedIn()->language()->addToStack('registration_default_group_info', NULL, [ 'sprintf' => [\IPS\Member\Group::load( \IPS\Settings::i()->member_group )->formattedName ] ] ), 'info' );
|
||||
|
||||
/* Display */
|
||||
return (string) $form;
|
||||
return (string) $desc . $form;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -121,7 +121,23 @@ 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, 'unlimited' => array( 0, 0 ) ), function( $value ){ if( $value[0] > 4800 OR $value[1] > 4800 ) { throw new \InvalidArgumentException('form_image_too_large'); } }, NULL, NULL, 'attachment_image_size' ) );
|
||||
$form->add( new \IPS\Helpers\Form\WidthHeight( 'attachment_image_size', $current, FALSE, array( 'resizableDiv' => FALSE, 'unlimited' => array( 0, 0 ) ), function( $value ){
|
||||
|
||||
if( $value[0] > 4800 OR $value[1] > 4800 )
|
||||
{
|
||||
throw new \InvalidArgumentException('form_image_too_large');
|
||||
}
|
||||
$notAllowed = [ 0, NULL, "" ];
|
||||
|
||||
if( in_array( $value[0], $notAllowed ) or in_array( $value[1], $notAllowed ) )
|
||||
{
|
||||
if( $value[0] == 0 AND $value[1] == 0 )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
throw new \InvalidArgumentException('form_image_not_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' ) );
|
||||
@@ -137,7 +153,12 @@ class _posting extends \IPS\Dispatcher\Controller
|
||||
$form->addHeader('posting_content');
|
||||
if ( \IPS\Login::registrationType() != 'disabled' )
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'post_before_registering', \IPS\Settings::i()->post_before_registering, FALSE, array( 'options' => array( 1 => 'post_before_registering_on', 0 => 'post_before_registering_off' ) ), NULL, NULL, NULL, 'post_before_registering' ) );
|
||||
$disabled = ( \IPS\Settings::i()->bot_antispam_type === 'none' );
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'post_before_registering', \IPS\Settings::i()->post_before_registering, FALSE, [ 'options' => [ 1 => 'post_before_registering_on', 0 => 'post_before_registering_off' ], 'disabled' => $disabled ], id:'post_before_registering' ) );
|
||||
if( $disabled )
|
||||
{
|
||||
\IPS\Member::loggedIn()->language()->words['post_before_registering_desc'] = \IPS\Member::loggedIn()->language()->addToStack('post_before_registering_captcha_req');
|
||||
}
|
||||
}
|
||||
\IPS\Member::loggedIn()->language()->words['post_before_registering_on'] = sprintf( \IPS\Member::loggedIn()->language()->get('post_before_registering_on'), \IPS\Member::loggedIn()->language()->addToStack( 'core_group_' . \IPS\Settings::i()->member_group ) );
|
||||
$form->add( new \IPS\Helpers\Form\Number( 'max_title_length', \IPS\Settings::i()->max_title_length, FALSE, array( 'max' => 255 ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('max_title_length_suffix') ) );
|
||||
|
||||
@@ -164,7 +164,7 @@ class _webapp extends \IPS\Dispatcher\Controller
|
||||
|
||||
unset( $values['configure_manifest'], $values['manifest_shortname'], $values['manifest_fullname'], $values['manifest_description'], $values['manifest_display'], $values['manifest_bgcolor'], $values['manifest_themecolor'], $values['manifest_custom_url_toggle'], $values['manifest_short_url'] );
|
||||
|
||||
$values['manifest_details'] = json_encode( $values['manifest_details'] );
|
||||
$values['manifest_details'] = json_encode( array_merge( $values['manifest_details'], [ 'cache_key' => time() ] ) );
|
||||
|
||||
/* Save the settings */
|
||||
$form->saveAsSettings( $values );
|
||||
@@ -473,6 +473,10 @@ class _webapp extends \IPS\Dispatcher\Controller
|
||||
},
|
||||
function() use( $self )
|
||||
{
|
||||
$manifest = json_decode( \IPS\Settings::i()->manifest_details, TRUE );
|
||||
$manifest['cache_key'] = time();
|
||||
|
||||
\IPS\Settings::i()->changeValues( [ 'manifest_details' => json_encode( $manifest ) ] );
|
||||
\IPS\Output::i()->redirect( $self->url, 'completed' );
|
||||
}
|
||||
);
|
||||
|
||||
@@ -22,6 +22,11 @@ if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
*/
|
||||
class _moderators extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
/**
|
||||
* @brief Allow MySQL RW separation for efficiency
|
||||
*/
|
||||
public static $allowRWSeparation = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
|
||||
@@ -133,7 +133,7 @@ class _membersactivity extends \IPS\Dispatcher\Controller
|
||||
/* And now build the table */
|
||||
$table = new \IPS\Helpers\Table\Db( 'core_members', \IPS\Request::i()->url()->setQueryString( array( 'postDateStart' => $startTime, 'postDateEnd' => $endTime, 'postGroups' => \is_array( $groups ) ? implode( ',', $groups ) : NULL ) ), $where );
|
||||
|
||||
$table->include = array( 'name', 'email', 'member_posts', 'member_last_post', 'group_name', 'ip_address' );
|
||||
$table->include = array( 'name', 'email', 'member_last_post', 'group_name', 'ip_address' );
|
||||
$table->mainColumn = 'name';
|
||||
$table->langPrefix = 'activity_';
|
||||
$table->rowClasses = array( 'email' => array( 'ipsTable_wrap' ), 'group_name' => array( 'ipsTable_wrap' ) );
|
||||
|
||||
@@ -11,8 +11,21 @@
|
||||
|
||||
namespace IPS\core\modules\admin\stats;
|
||||
|
||||
use IPS\Dispatcher;
|
||||
use IPS\Dispatcher\Controller;
|
||||
use IPS\Member;
|
||||
use IPS\Request;
|
||||
use IPS\Output;
|
||||
use IPS\Theme;
|
||||
use IPS\Http\Url;
|
||||
use IPS\core\Statistics\Chart;
|
||||
use IPS\Helpers\Table\Db as TableDb;
|
||||
|
||||
use function defined;
|
||||
use function header;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
@@ -21,13 +34,18 @@ if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
/**
|
||||
* moderators
|
||||
*/
|
||||
class _moderators extends \IPS\Dispatcher\Controller
|
||||
class _moderators extends Controller
|
||||
{
|
||||
/**
|
||||
* @brief Has been CSRF-protected
|
||||
*/
|
||||
public static $csrfProtected = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Allow MySQL RW separation for efficiency
|
||||
*/
|
||||
public static $allowRWSeparation = TRUE;
|
||||
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
@@ -35,7 +53,7 @@ class _moderators extends \IPS\Dispatcher\Controller
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
\IPS\Dispatcher::i()->checkAcpPermission( 'moderatorstats_manage' );
|
||||
Dispatcher::i()->checkAcpPermission( 'moderatorstats_manage' );
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
@@ -46,14 +64,27 @@ class _moderators extends \IPS\Dispatcher\Controller
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
if ( \IPS\Request::i()->isAjax() )
|
||||
$tabs = array(
|
||||
'users' => 'stats_moderators_users',
|
||||
'actions' => 'stats_moderators_actions'
|
||||
);
|
||||
|
||||
Request::i()->tab ??= 'users';
|
||||
$activeTab = ( array_key_exists( Request::i()->tab, $tabs ) ) ? Request::i()->tab : 'users';
|
||||
|
||||
$output = match( $activeTab ) {
|
||||
'users' => Chart::loadFromExtension( 'core', 'Moderators' )->getChart( Url::internal( 'app=core&module=stats&controller=moderators&tab=users' ) ),
|
||||
'actions' => Chart::loadFromExtension( 'core', 'ModeratorsByAction' )->getChart( Url::internal( "app=core&module=stats&controller=moderators&tab=actions" ) )
|
||||
};
|
||||
|
||||
if ( Request::i()->isAjax() )
|
||||
{
|
||||
\IPS\Output::i()->output = (string) \IPS\core\Statistics\Chart::loadFromExtension( 'core', 'Moderators' )->getChart( \IPS\Http\Url::internal( 'app=core&module=stats&controller=moderators' ) );
|
||||
Output::i()->output = (string) $output;
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__core_stats_moderators');
|
||||
\IPS\Output::i()->output = (string) \IPS\core\Statistics\Chart::loadFromExtension( 'core', 'Moderators' )->getChart( \IPS\Http\Url::internal( 'app=core&module=stats&controller=moderators' ) );
|
||||
Output::i()->title = Member::loggedIn()->language()->addToStack('menu__core_stats_moderators');
|
||||
Output::i()->output = Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $activeTab, (string) $output, Url::internal( "app=core&module=stats&controller=moderators" ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -79,12 +79,12 @@ class _referrals extends \IPS\Dispatcher\Controller
|
||||
|
||||
if( $params['start'] )
|
||||
{
|
||||
$where[] = array( 'joined>?', $params['start'] );
|
||||
$where[] = array( 'core_members.joined>?', $params['start'] );
|
||||
}
|
||||
|
||||
if( $params['end'] )
|
||||
{
|
||||
$where[] = array( 'joined<?', $params['end'] );
|
||||
$where[] = array( 'core_members.joined<?', $params['end'] );
|
||||
}
|
||||
|
||||
$page = isset( \IPS\Request::i()->page ) ? \intval( \IPS\Request::i()->page ) : 1;
|
||||
@@ -96,7 +96,8 @@ class _referrals extends \IPS\Dispatcher\Controller
|
||||
|
||||
try
|
||||
{
|
||||
$total = \IPS\Db::i()->select( 'COUNT(DISTINCT(core_referrals.referred_by))', 'core_referrals', $where )->join( 'core_members', 'core_members.member_id = core_referrals.referred_by')->first();
|
||||
$total = \IPS\Db::i()->select( 'COUNT(DISTINCT(core_referrals.referred_by))', 'core_referrals', $where )
|
||||
->join( 'core_members', 'core_members.member_id = core_referrals.member_id')->first();
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
{
|
||||
@@ -105,7 +106,8 @@ class _referrals extends \IPS\Dispatcher\Controller
|
||||
|
||||
if( $total )
|
||||
{
|
||||
$select = \IPS\Db::i()->select( 'core_referrals.referred_by as member_id, count(*) as count', 'core_referrals', $where, 'count DESC', array( ( $page - 1 ) * static::PER_PAGE, static::PER_PAGE ), 'member_id' )->join( 'core_members', 'core_members.member_id = core_referrals.referred_by');
|
||||
$select = \IPS\Db::i()->select( 'core_referrals.referred_by as member_id, count(*) as count', 'core_referrals', $where, 'count DESC', array( ( $page - 1 ) * static::PER_PAGE, static::PER_PAGE ), 'member_id' )
|
||||
->join( 'core_members', 'core_members.member_id = core_referrals.member_id');
|
||||
$mids = array();
|
||||
|
||||
foreach( $select as $row )
|
||||
|
||||
@@ -253,7 +253,7 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
*/
|
||||
protected function guideSearch()
|
||||
{
|
||||
\IPS\Output::i()->json( array() );
|
||||
\IPS\Output::i()->json( $guides );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -308,7 +308,14 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
switch( $k )
|
||||
{
|
||||
case 'php':
|
||||
$requirements['list'][ $k ]['detail'] = \IPS\Member::loggedIn()->language()->addToStack( 'health_check_update_recommended' );
|
||||
if( $v['downgrade'] === TRUE )
|
||||
{
|
||||
$requirements['list'][ $k ]['detail'] = \IPS\Member::loggedIn()->language()->addToStack( 'health_check_downgrade_recommended' );
|
||||
}
|
||||
else
|
||||
{
|
||||
$requirements['list'][ $k ]['detail'] = \IPS\Member::loggedIn()->language()->addToStack( 'health_check_update_recommended' );
|
||||
}
|
||||
break;
|
||||
|
||||
case 'curl':
|
||||
@@ -479,7 +486,7 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
/**
|
||||
* Get block: Invision Community
|
||||
*
|
||||
* @return void
|
||||
* @return array
|
||||
*/
|
||||
protected function _showBlockVersion()
|
||||
{
|
||||
@@ -531,19 +538,23 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
);
|
||||
}
|
||||
|
||||
/* Run MD5 check */
|
||||
$modifiedFiles = $this->_md5Checker( (bool) \IPS\Request::i()->fix );
|
||||
|
||||
if( $modifiedFiles )
|
||||
/* V5 */
|
||||
$v5Actions = ( new \IPS\core\modules\admin\support\v5 )->v5Actions();
|
||||
foreach( $v5Actions as $action )
|
||||
{
|
||||
$requirements['failures']++;
|
||||
$requirements['list'][] = array(
|
||||
'critical' => TRUE,
|
||||
'advice' => FALSE,
|
||||
'success' => FALSE,
|
||||
'link' => \IPS\Http\Url::internal( "app=core&module=support&controller=support&do=getBlock&block=version&fix=1" ),
|
||||
'detail' => \IPS\Member::loggedIn()->language()->addToStack('md5_check_fail')
|
||||
);
|
||||
if( $action === FALSE or \is_array( $action ) AND count( $action ) )
|
||||
{
|
||||
$requirements['advice']++;
|
||||
$requirements['list'][] = [
|
||||
'critical' => FALSE,
|
||||
'advice' => TRUE,
|
||||
'success' => FALSE,
|
||||
'button' => array( 'lang' => 'health_info', 'href' => \IPS\Http\Url::internal( 'app=core&module=support&controller=v5' ), 'css' => 'ipsButton_intermediate' ),
|
||||
'link' => \IPS\Http\Url::internal( "app=core&module=support&controller=v5" ),
|
||||
'detail' => \IPS\Member::loggedIn()->language()->addToStack('health_v5_upgrade_attention')
|
||||
];
|
||||
break; # At least one thing needs attention
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
@@ -852,6 +863,7 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
'success' => FALSE,
|
||||
'link' => \IPS\Http\Url::internal( 'app=core&module=support&controller=redis' ),
|
||||
'detail' => $detail,
|
||||
'button' => array( 'lang' => 'health_view_redis_config' ),
|
||||
'dialogTitle' => 'health__more_information',
|
||||
);
|
||||
}
|
||||
@@ -1131,6 +1143,7 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
'advice' => FALSE,
|
||||
'success' => FALSE,
|
||||
'link' => NULL,
|
||||
'downgrade' => NULL,
|
||||
'detail' => $requirement['message']
|
||||
);
|
||||
|
||||
@@ -1154,6 +1167,7 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
'advice' => TRUE,
|
||||
'success' => FALSE,
|
||||
'link' => NULL,
|
||||
'downgrade' => $requirements['requirements'][ $category ]['version']['downgrade'] ?? NULL,
|
||||
'detail' => $requirement
|
||||
);
|
||||
}
|
||||
@@ -1848,17 +1862,6 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->blankTemplate( \IPS\Output::i()->output ), 200, 'text/html' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Run MD5 checker
|
||||
*
|
||||
* @param bool $fix Fix the issue instead of returning the count
|
||||
* @return mixed
|
||||
*/
|
||||
protected function _md5Checker( $fix = FALSE )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a count of third party apps and plugins
|
||||
*
|
||||
@@ -1908,37 +1911,22 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
/**
|
||||
* Get third-party applications
|
||||
*
|
||||
* @param bool $separateMarketplace If TRUE, will separate ones installed from the marketplace and custom ones
|
||||
* @return array
|
||||
*/
|
||||
protected function _thirdPartyApps( $separateMarketplace = FALSE )
|
||||
protected function _thirdPartyApps()
|
||||
{
|
||||
if ( \IPS\NO_WRITES )
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
$apps = $separateMarketplace ? [ 'marketplace' => [], 'custom' => [] ] : [];
|
||||
$apps = [];
|
||||
|
||||
foreach ( \IPS\Application::applications() as $app )
|
||||
{
|
||||
if ( $app->enabled and !\in_array( $app->directory, \IPS\IPS::$ipsApps ) )
|
||||
{
|
||||
if ( $separateMarketplace )
|
||||
{
|
||||
if ( $app->marketplace_id )
|
||||
{
|
||||
$apps['marketplace'][] = $app;
|
||||
}
|
||||
else
|
||||
{
|
||||
$apps['custom'][] = $app;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$apps[] = $app;
|
||||
}
|
||||
$apps[] = $app;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1948,37 +1936,22 @@ class _support extends \IPS\Dispatcher\Controller
|
||||
/**
|
||||
* Get third-party plugins
|
||||
*
|
||||
* @param bool $separateMarketplace If TRUE, will separate ones installed from the marketplace and custom ones
|
||||
* @return array
|
||||
*/
|
||||
protected function _thirdPartyPlugins( $separateMarketplace = FALSE )
|
||||
protected function _thirdPartyPlugins()
|
||||
{
|
||||
if ( \IPS\NO_WRITES )
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
$plugins = $separateMarketplace ? [ 'marketplace' => [], 'custom' => [] ] : [];
|
||||
$plugins = [];
|
||||
|
||||
foreach ( \IPS\Plugin::plugins() as $plugin )
|
||||
{
|
||||
if ( $plugin->enabled )
|
||||
{
|
||||
if ( $separateMarketplace )
|
||||
{
|
||||
if ( $plugin->marketplace_id )
|
||||
{
|
||||
$plugins['marketplace'][] = $plugin;
|
||||
}
|
||||
else
|
||||
{
|
||||
$plugins['custom'][] = $plugin;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$plugins[] = $plugin;
|
||||
}
|
||||
$plugins[] = $plugin;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -183,7 +183,7 @@ class _systemLogs extends \IPS\Dispatcher\Controller
|
||||
protected function deleteAll()
|
||||
{
|
||||
/* Delete */
|
||||
\IPS\Db::i()->query( "TRUNCATE core_log" );
|
||||
\IPS\Db::i()->query( "TRUNCATE " . \IPS\Settings::i()->sql_tbl_prefix . "core_log" );
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=support&controller=systemLogs' ), 'deleted' );
|
||||
}
|
||||
|
||||
@@ -134,7 +134,8 @@ class _utf8mb4 extends \IPS\Dispatcher\Controller
|
||||
try
|
||||
{
|
||||
$app = \IPS\Application::load( $appName );
|
||||
$schema = json_decode( file_get_contents( \IPS\ROOT_PATH . "/applications/{$app->directory}/data/schema.json" ), TRUE );
|
||||
$schemaRoot = \in_array( $app->directory, \IPS\IPS::$ipsApps ) ? \IPS\ROOT_PATH : \IPS\SITE_FILES_PATH;
|
||||
$schema = json_decode( file_get_contents( $schemaRoot . "/applications/{$app->directory}/data/schema.json" ), TRUE );
|
||||
if ( !array_key_exists( $table, $schema ) )
|
||||
{
|
||||
/* Make a special exception for CMS database tables */
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief v5
|
||||
* @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 19 Sep 2024
|
||||
*/
|
||||
|
||||
namespace IPS\core\modules\admin\support;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
|
||||
use IPS\Application;
|
||||
use IPS\Db;
|
||||
use IPS\Http\Url;
|
||||
use IPS\Login;
|
||||
use IPS\Member;
|
||||
use IPS\Output;
|
||||
use IPS\Settings;
|
||||
use IPS\Theme;
|
||||
use function count;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* v5
|
||||
*/
|
||||
class _v5 extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
public static bool $csrfProtected = true;
|
||||
|
||||
/**
|
||||
* ...
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
$actions = $this->v5Actions();
|
||||
|
||||
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'support/dashboard.css', 'core', 'admin' ) );
|
||||
Output::i()->title = Member::loggedIn()->language()->addToStack( 'prepare_v5_title' );
|
||||
Output::i()->output = Theme::i()->getTemplate( 'support' )->v5( ...$actions );
|
||||
}
|
||||
|
||||
public function v5actions()
|
||||
{
|
||||
if( \IPS\CIC )
|
||||
{
|
||||
$phpCheck = true;
|
||||
$mySqlCheck = true;
|
||||
$innoDb = true;
|
||||
$elasticsearch = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Check PHP */
|
||||
$phpCheck = ( version_compare( PHP_VERSION, "8.1.0" ) >= 0 );
|
||||
|
||||
/* Check MySQL */
|
||||
$mySqlVersion = Db::i()->server_info;
|
||||
$mySqlCheck = ( version_compare( $mySqlVersion, "8.0.13" ) >=0 );
|
||||
|
||||
/* Check InnoDb */
|
||||
$innoDb = !count( iterator_to_array( Db::i()->query( "SHOW TABLE STATUS WHERE Engine!='InnoDB'" ) ) );
|
||||
|
||||
/* Are we using elasticsearch */
|
||||
$elasticsearch = ( \IPS\Settings::i()->search_method == 'elastic' AND \IPS\Settings::i()->search_elastic_server );
|
||||
}
|
||||
|
||||
Output::i()->sidebar['actions']['deprecations'] = array(
|
||||
'icon' => 'question-circle',
|
||||
'title' => 'deprecation_tracker',
|
||||
'link' => Url::ips( 'docs/deprecation_tracker' ),
|
||||
'target' => '_blank'
|
||||
);
|
||||
|
||||
$deprecations = [];
|
||||
|
||||
$status = (bool) Db::i()->select( 'count(*)', 'core_member_status_updates' )->first();
|
||||
if( $status )
|
||||
{
|
||||
$deprecations['status'] = Member::loggedIn()->language()->addToStack( 'v5_deprecate_status' );
|
||||
}
|
||||
|
||||
$login = (bool) Db::i()->select( 'count(*)', 'core_login_methods', [
|
||||
[ 'login_enabled=?', 1 ],
|
||||
[ Db::i()->in( 'login_classname', [
|
||||
'IPS\Login\Handler\Standard',
|
||||
'IPS\Login\Handler\OAuth2\Apple',
|
||||
'IPS\Login\Handler\OAuth2\Facebook',
|
||||
'IPS\Login\Handler\OAuth2\Google',
|
||||
'IPS\Login\Handler\OAuth2\LinkedIn',
|
||||
'IPS\Login\Handler\OAuth2\Microsoft',
|
||||
'IPS\Login\Handler\OAuth1\Twitter',
|
||||
'IPS\Login\Handler\OAuth2\Invision',
|
||||
'IPS\Login\Handler\OAuth2\Wordpress',
|
||||
'IPS\Login\Handler\OAuth2\Custom',
|
||||
'IPS\Login\Handler\ExternalDatabase',
|
||||
'IPS\Login\Handler\LDAP',
|
||||
'IPS\convert\Login'
|
||||
], true ) ]
|
||||
] )->first();
|
||||
if( $login )
|
||||
{
|
||||
$deprecations['login'] = Member::loggedIn()->language()->addToStack( 'v5_deprecate_login' );
|
||||
}
|
||||
|
||||
$login = new Login;
|
||||
if( $login->authType() & Login::AUTH_TYPE_USERNAME )
|
||||
{
|
||||
$deprecations['display_names'] = Member::loggedIn()->language()->addToStack( 'v5_deprecate_display_name_login' );
|
||||
}
|
||||
|
||||
if( \IPS\CP_DIRECTORY != 'admin' )
|
||||
{
|
||||
$deprecations['cp_directory'] = Member::loggedIn()->language()->addToStack( 'v5_deprecate_cp_directory' );
|
||||
}
|
||||
|
||||
if( Settings::i()->tags_open_system )
|
||||
{
|
||||
$deprecations['open_tagging'] = Member::loggedIn()->language()->addToStack( 'v5_deprecate_open_tagging' );
|
||||
}
|
||||
|
||||
if( (bool) Db::i()->select('count(*)', 'core_social_promote_sharers', [ 'sharer_key!=? AND sharer_enabled=?', 'Internal', 1 ] )->first() )
|
||||
{
|
||||
$deprecations['social_promotes'] = Member::loggedIn()->language()->addToStack( 'v5_deprecate_social_promotes' );
|
||||
}
|
||||
|
||||
if( !\IPS\CIC )
|
||||
{
|
||||
if( !Settings::i()->use_friendly_urls )
|
||||
{
|
||||
$deprecations['core_furls'] = Member::loggedIn()->language()->addToStack( 'v5_deprecate_core_furls' );
|
||||
}
|
||||
|
||||
$dummyRequest = Url::external('https://invisioncommunity.com')->request();
|
||||
if( ( $dummyRequest instanceof \IPS\Http\Request\Sockets ) )
|
||||
{
|
||||
$deprecations['classic_sockets'] = Member::loggedIn()->language()->addToStack( 'v5_deprecate_classic_sockets' );
|
||||
}
|
||||
}
|
||||
|
||||
/* Pages Checks */
|
||||
if( Application::appIsEnabled( 'cms' ) )
|
||||
{
|
||||
$appName = Application::load('cms')->_title;
|
||||
foreach( Db::i()->select( '*', 'cms_blocks', [ 'block_type=?', 'custom'] ) as $row )
|
||||
{
|
||||
$config = json_decode( $row['block_config'], true );
|
||||
if( isset( $config['editor'] ) and $config['editor'] == 'php' )
|
||||
{
|
||||
$deprecations['php_blocks'] = Member::loggedIn()->language()->addToStack( 'v5_deprecate_php_blocks', false, [ 'sprintf' => [ $appName ] ] );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Commerce checks */
|
||||
if( Application::appIsEnabled( 'nexus' ) )
|
||||
{
|
||||
$appName = Application::load('nexus')->_title;
|
||||
$physical = (bool) Db::i()->select( 'count(*)', 'nexus_packages_products', [ 'p_physical=?', 1 ] )->first();
|
||||
if( $physical )
|
||||
{
|
||||
$deprecations['physical'] = Member::loggedIn()->language()->addToStack( 'v5_deprecate_physical', false, [ 'sprintf' => [ $appName ] ] );
|
||||
}
|
||||
|
||||
$shipping = (bool) Db::i()->select( 'count(*)', 'nexus_shipping' )->first();
|
||||
$shipOrders = (bool) Db::i()->select( 'count(*)', 'nexus_ship_orders' )->first();
|
||||
if( $shipping or $shipOrders )
|
||||
{
|
||||
$deprecations['shipping'] = Member::loggedIn()->language()->addToStack( 'v5_deprecate_shipping', false, [ 'sprintf' => [ $appName ] ] );
|
||||
}
|
||||
|
||||
$requests = (bool) Db::i()->select( 'count(*)', 'nexus_support_requests' )->first();
|
||||
if( $requests )
|
||||
{
|
||||
$deprecations['support'] = Member::loggedIn()->language()->addToStack( 'v5_deprecate_support', false, [ 'sprintf' => [ $appName ] ] );
|
||||
}
|
||||
|
||||
$gateways = iterator_to_array(
|
||||
Db::i()->select( 'distinct m_gateway', 'nexus_paymethods', Db::i()->in( 'm_gateway', [ 'Braintree', 'AuthorizeNet', 'TwoCheckout' ] ) )
|
||||
);
|
||||
if( count( $gateways ) )
|
||||
{
|
||||
$deprecations['gateways'] = Member::loggedIn()->language()->addToStack( 'v5_deprecate_gateways', false, [ 'sprintf' => [ $appName, implode( ", ", $gateways ) ] ] );
|
||||
}
|
||||
|
||||
if( Settings::i()->easypost_enable )
|
||||
{
|
||||
$deprecations['easypost'] = Member::loggedIn()->language()->addToStack( 'v5_deprecate_easypost', false, [ 'sprintf' => [ $appName ] ] );
|
||||
}
|
||||
}
|
||||
|
||||
/* Forums checks */
|
||||
if( Application::appIsEnabled( 'forums' ) )
|
||||
{
|
||||
$appName = Application::load('forums')->_title;
|
||||
if( (bool) \IPS\Db::i()->select( 'COUNT(*)', 'forums_forums', \IPS\Db::i()->bitwiseWhere( \IPS\forums\Forum::$bitOptions['forums_bitoptions'], 'bw_enable_answers' ) )->first() )
|
||||
{
|
||||
$deprecations['qa'] = Member::loggedIn()->language()->addToStack( 'v5_deprecate_forums_qa', false, [ 'sprintf' => [ $appName ] ] );
|
||||
}
|
||||
}
|
||||
|
||||
return [ 'phpCheck' => $phpCheck, 'mySqlCheck' => $mySqlCheck, 'innoDb' => $innoDb, 'elasticsearch' => $elasticsearch, 'deprecations' => $deprecations ];
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,9 @@
|
||||
|
||||
namespace IPS\core\modules\admin\system;
|
||||
|
||||
use IPS\Http\Url;
|
||||
use IPS\Output;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
@@ -303,7 +306,10 @@ class _login extends \IPS\Dispatcher\Controller
|
||||
public function getCsrfKey()
|
||||
{
|
||||
/* Don't cache the CSRF key */
|
||||
\IPS\Output::i()->pageCaching = FALSE;
|
||||
\IPS\Output::setCacheTime( false );
|
||||
|
||||
/* Restrict endpoint to our origin JS */
|
||||
Output::i()->httpHeaders['Access-Control-Allow-Origin'] = Url::baseUrl()->data[ Url::COMPONENT_SCHEME ] . '://' . Url::baseUrl()->data[ Url::COMPONENT_HOST ];
|
||||
|
||||
\IPS\Output::i()->json( [ 'key' => \IPS\Session::i()->csrfKey ] );
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ 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://nullcave.club/resources/invision-community.979/" ) );
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::external( "https://ipbmafia.ru/files/file/2342-invision-community-nulled/" ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,29 +190,8 @@ class _upgrade extends \IPS\Dispatcher\Controller
|
||||
/* Do a database check */
|
||||
if( !\IPS\IPS_ALPHA_BUILD )
|
||||
{
|
||||
$md5CheckVersion = NULL;
|
||||
/* If the files on disk are already newer, set version for MD5 check (i.e. if the files were already applied, but the upgrade hasn't happened) */
|
||||
/* If the files on disk are already newer (i.e. if the files were already applied, but the upgrade hasn't happened), go to upgrade now */
|
||||
if( (int) \IPS\Application::getAvailableVersion('core') > \IPS\Application::load('core')->long_version AND \IPS\Application::load('core')->version != \IPS\Application::getAvailableVersion( 'core', TRUE ) )
|
||||
{
|
||||
$md5CheckVersion = (int) \IPS\Application::getAvailableVersion('core');
|
||||
}
|
||||
|
||||
/* Check our files aren't modified */
|
||||
if ( !\IPS\Request::i()->skip_md5_check )
|
||||
{
|
||||
try
|
||||
{
|
||||
$files = \IPS\Application::md5Check( $md5CheckVersion );
|
||||
if ( \count( $files ) )
|
||||
{
|
||||
return \IPS\Theme::i()->getTemplate('system')->upgradeDeltaMd5( $values['version'], $files );
|
||||
}
|
||||
}
|
||||
catch ( \Exception $e ) {}
|
||||
}
|
||||
|
||||
/* If we set a custom md5 check version and it passed, proceed to full upgrader */
|
||||
if( $md5CheckVersion )
|
||||
{
|
||||
\IPS\Output::i()->redirect( 'upgrade/?autologin=1' );
|
||||
}
|
||||
@@ -236,17 +215,16 @@ class _upgrade extends \IPS\Dispatcher\Controller
|
||||
/* Check apps and plugins */
|
||||
$row = \IPS\Db::i()->union(
|
||||
array(
|
||||
\IPS\Db::i()->select( "'applications' AS `table`, app_directory AS `id`, app_version AS `current`, app_long_version AS `long`, app_marketplace_id as `marketplace_id`", 'core_applications', [ \IPS\Db::i()->in( 'app_directory', \IPS\IPS::$ipsApps, TRUE ) ] ),
|
||||
\IPS\Db::i()->select( "'plugins' AS `table`, plugin_id AS id, plugin_version_human AS `current`, plugin_version_long AS `long`, plugin_marketplace_id as `marketplace_id`", 'core_plugins' ),
|
||||
\IPS\Db::i()->select( "'themes' AS `table`, set_id AS `id`, set_version AS `current`, set_long_version AS `long`, set_marketplace_id as `marketplace_id`", 'core_themes', [ 'set_customized=?', 1 ] ),
|
||||
\IPS\Db::i()->select( "'languages' AS `table`, lang_id AS `id`, `lang_version` AS `current`, `lang_version_long` AS `long`, `lang_marketplace_id` as `marketplace_id`", "core_sys_lang" )
|
||||
\IPS\Db::i()->select( "'applications' AS `table`, app_directory AS `id`, app_version AS `current`, app_long_version AS `long`", 'core_applications', [ \IPS\Db::i()->in( 'app_directory', \IPS\IPS::$ipsApps, TRUE ) ] ),
|
||||
\IPS\Db::i()->select( "'plugins' AS `table`, plugin_id AS id, plugin_version_human AS `current`, plugin_version_long AS `long`", 'core_plugins' ),
|
||||
\IPS\Db::i()->select( "'themes' AS `table`, set_id AS `id`, set_version AS `current`, set_long_version AS `long`", 'core_themes', [ 'set_customized=?', 1 ] ),
|
||||
\IPS\Db::i()->select( "'languages' AS `table`, lang_id AS `id`, `lang_version` AS `current`, `lang_version_long` AS `long`", "core_sys_lang" )
|
||||
),
|
||||
NULL,
|
||||
200
|
||||
);
|
||||
|
||||
$marketplaceCheck = [];
|
||||
$output = [ 'updates' => [], 'noupdate' => [], 'compatible' => [], 'custom' => [] ];
|
||||
$output = [ 'custom' => [] ];
|
||||
foreach ( $row as $r )
|
||||
{
|
||||
/* Load title */
|
||||
@@ -274,49 +252,7 @@ class _upgrade extends \IPS\Dispatcher\Controller
|
||||
continue;
|
||||
}
|
||||
|
||||
$output[ ( $r['marketplace_id'] ? 'updates' : 'custom' )][ $r['table'] . $r['id'] ] = $r;
|
||||
if ( $r['marketplace_id'] )
|
||||
{
|
||||
$marketplaceCheck[] = $r;
|
||||
}
|
||||
}
|
||||
|
||||
/* Actually Check */
|
||||
if( \count( $marketplaceCheck ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
$marketplaceController = new \IPS\core\modules\admin\marketplace\marketplace;
|
||||
$response = $marketplaceController->_updateCheck( array_column( $marketplaceCheck, 'long', 'marketplace_id' ), (int) $values['version'] );
|
||||
|
||||
foreach ( $marketplaceCheck as $mp )
|
||||
{
|
||||
if( !empty( $response[ $mp['marketplace_id'] ] ) )
|
||||
{
|
||||
$mp['update'] = $response[ $mp['marketplace_id'] ];
|
||||
|
||||
if( $response[ $mp['marketplace_id'] ]['longversion'] == $mp['long'] )
|
||||
{
|
||||
$output['compatible'][ $mp['table'] . $mp['id'] ] = $mp;
|
||||
unset( $output['updates'][ $mp['table'] . $mp['id'] ] );
|
||||
}
|
||||
else
|
||||
{
|
||||
$output['updates'][ $mp['table'] . $mp['id'] ] = $mp;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
unset( $output['updates'][ $mp['table'] . $mp['id'] ] );
|
||||
$output['noupdate'][ $mp['table'] . $mp['id'] ] = $mp;
|
||||
}
|
||||
}
|
||||
catch( \RuntimeException | \UnexpectedValueException $e )
|
||||
{
|
||||
$output['nocheck'] = $marketplaceCheck;
|
||||
$output['updates'] = [];
|
||||
}
|
||||
$output['custom'][ $r['table'] . $r['id'] ] = $r;
|
||||
}
|
||||
|
||||
return \IPS\Theme::i()->getTemplate('system')->upgradeDeltaResourceIssues( $values['version'], $output );
|
||||
@@ -502,18 +438,18 @@ class _upgrade extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
if ( \IPS\CIC2 )
|
||||
{
|
||||
/* Deliberately no try/catch because if it does fail, the site will never know and it will always be an issue cloud-side. */
|
||||
\IPS\IPS::applyLatestFilesIPSCloud();
|
||||
|
||||
/* Log who started the upgrade */
|
||||
\IPS\Session::i()->log( 'acplog__upgrade_started' );
|
||||
|
||||
if ( isset( \IPS\Request::i()->check ) )
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Deliberately no try/catch because if it does fail, the site will never know and it will always be an issue cloud-side. */
|
||||
\IPS\IPS::applyLatestFilesIPSCloud();
|
||||
|
||||
/* Log who started the upgrade */
|
||||
\IPS\Session::i()->log( 'acplog__upgrade_started' );
|
||||
|
||||
return \IPS\Theme::i()->getTemplate('system')->upgradeExtractCic2();
|
||||
}
|
||||
}
|
||||
@@ -944,40 +880,6 @@ class _upgrade extends \IPS\Dispatcher\Controller
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal('app=core&module=system&controller=upgrade&check=1') );
|
||||
} );
|
||||
}
|
||||
|
||||
/* Run md5 check */
|
||||
try
|
||||
{
|
||||
$files = \IPS\Application::md5Check();
|
||||
if ( \count( $files ) )
|
||||
{
|
||||
/* Log */
|
||||
\IPS\Log::debug( "MD5 check of delta download failed with " . \count( $files ) . " reported as modified", 'auto_upgrade' );
|
||||
|
||||
/* If we'rve already tried to fix them and failed, show an error */
|
||||
if ( isset( $data['md5Fix'] ) and $data['md5Fix'] )
|
||||
{
|
||||
return \IPS\Theme::i()->getTemplate('system')->upgradeDeltaFailed( 'exception', NULL );
|
||||
}
|
||||
|
||||
/* Otherwise try to just fix them - first get a new download key */
|
||||
$files = array_map( function( $file ) {
|
||||
return str_replace( \IPS\ROOT_PATH, '', $file );
|
||||
}, $files );
|
||||
$this->_clientAreaPassword = $data['ips_pass'];
|
||||
$newDownloadKey = $this->_getDownloadKey( $data['ips_email'], $data['version'], $files );
|
||||
|
||||
/* Manipulate the wizard data */
|
||||
$data = $_SESSION[ 'wizard-' . md5( \IPS\Http\Url::internal( 'app=core&module=system&controller=upgrade' ) ) . '-data' ];
|
||||
$data['key'] = $newDownloadKey;
|
||||
$data['md5Fix'] = TRUE;
|
||||
$_SESSION[ 'wizard-' . md5( \IPS\Http\Url::internal( 'app=core&module=system&controller=upgrade' ) ) . '-data' ] = $data;
|
||||
|
||||
/* Redirect back in */
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal('app=core&module=system&controller=upgrade') );
|
||||
}
|
||||
}
|
||||
catch ( \Exception $e ) {}
|
||||
|
||||
/* Nope, we're good! */
|
||||
return $data;
|
||||
@@ -1088,6 +990,12 @@ class _upgrade extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
$databaseErrors = TRUE;
|
||||
}
|
||||
|
||||
/* If this is not a patch, send v5 notification */
|
||||
if( $app->directory === 'core' AND $versions[ $data['oldVersion'] ] !== $latestHVersion AND !\IPS\IPS::isManaged() )
|
||||
{
|
||||
\IPS\core\AdminNotification::send( 'core', 'VersionFive', NULL, TRUE );
|
||||
}
|
||||
}
|
||||
}
|
||||
unset( \IPS\Data\Store::i()->applications, \IPS\Data\Store::i()->updatecount_applications );
|
||||
|
||||
@@ -156,7 +156,10 @@ class _directory extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
}
|
||||
}
|
||||
$baseUrl = $baseUrl->setQueryString( 'f' . $field->id, array_keys( $filters[ $field->id ] ) );
|
||||
if( isset( $filters[ $field->id ] ) )
|
||||
{
|
||||
$baseUrl = $baseUrl->setQueryString( 'f' . $field->id, array_keys( $filters[ $field->id ] ) );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -249,7 +252,7 @@ class _directory extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('module__core_clubs');
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('clubs')->directory( $featuredClubs, $allClubs, $pagination, $baseUrl, $sortOption, $myClubsActivity, $mapMarkers, $view );
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('clubs')->directory( $featuredClubs, $allClubs, $pagination, $baseUrl, $sortOption, $myClubsActivity, $mapMarkers, $view, $mineOnly );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1344,6 +1344,11 @@ class _view extends \IPS\Helpers\CoverPhoto\Controller
|
||||
/* Find the purchase */
|
||||
foreach ( \IPS\core\extensions\nexus\Item\ClubMembership::getPurchases( \IPS\nexus\Customer::loggedIn(), $this->club->id ) as $purchase )
|
||||
{
|
||||
if ( $invoice = $purchase->invoice_pending )
|
||||
{
|
||||
\IPS\Output::i()->redirect( $invoice->checkoutUrl() );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->redirect( $purchase->url()->setQueryString( array( 'do' => 'renew', 'cycles' => 1 ) )->csrf() );
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ class _contact extends \IPS\Dispatcher\Controller
|
||||
\IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimal';
|
||||
|
||||
$form = new \IPS\Helpers\Form( 'contact', 'send' );
|
||||
$form->hiddenValues['contact_referrer'] = (string) \IPS\Request::i()->referrer();
|
||||
$form->class = 'ipsForm_vertical';
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Editor( 'contact_text', NULL, TRUE, array(
|
||||
@@ -71,7 +72,7 @@ class _contact extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'contact_name', NULL, TRUE ) );
|
||||
$form->add( new \IPS\Helpers\Form\Email( 'email_address', NULL, TRUE, array( 'bypassProfanity' => \IPS\Helpers\Form\Text::BYPASS_PROFANITY_ALL ) ) );
|
||||
if ( \IPS\Settings::i()->bot_antispam_type !== 'none' and \IPS\Settings::i()->guest_captcha )
|
||||
if ( \IPS\Settings::i()->bot_antispam_type !== 'none' )
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\Captcha );
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ class _popular extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
}
|
||||
|
||||
if ( \IPS\Application::appIsEnabled('cloud') and \IPS\cloud\Application::featureIsEnabled('trending') )
|
||||
if ( \IPS\Application::appIsEnabled('cloud') and \IPS\cloud\Application::trendingIsEnabled( alwaysOn: FALSE ) )
|
||||
{
|
||||
$tabs[] = 'trending';
|
||||
}
|
||||
@@ -277,25 +277,22 @@ class _popular extends \IPS\Dispatcher\Controller
|
||||
$areas[ $item::$application . '-' . $item::reactionType() ] = array( $item, \IPS\Member::loggedIn()->language()->addToStack( "{$item::$title}_pl" ) );
|
||||
}
|
||||
|
||||
if ( isset( $item::$commentClass ) )
|
||||
if ( $item::supportsComments( \IPS\Member::loggedIn() ) and $commentClass = $item::$commentClass and \IPS\IPS::classUsesTrait( $commentClass, 'IPS\Content\Reactable' ) )
|
||||
{
|
||||
$commentClass = $item::$commentClass;
|
||||
if ( \IPS\IPS::classUsesTrait( $commentClass, 'IPS\Content\Reactable' ) )
|
||||
$supportsComments = \IPS\IPS::classUsesTrait( $commentClass, 'IPS\Content\Reactable' ) and $item::supportsComments( \IPS\Member::loggedIn() );
|
||||
if ( $supportsComments )
|
||||
{
|
||||
$areas[ $item::$application . '-' . $commentClass::reactionType() ] = array( $commentClass, \IPS\Member::loggedIn()->language()->addToStack( "{$commentClass::$title}_pl" ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $item::$reviewClass ) )
|
||||
if ( $item::supportsReviews( \IPS\Member::loggedIn() ) and $reviewClass = $item::$reviewClass and \IPS\IPS::classUsesTrait( $reviewClass, 'IPS\Content\Reactable' ) )
|
||||
{
|
||||
$reviewClass = $item::$reviewClass;
|
||||
if ( \IPS\IPS::classUsesTrait( $reviewClass, 'IPS\Content\Reactable' ) )
|
||||
{
|
||||
$areas[ $item::$application . '-' . $reviewClass::reactionType() ] = array( $reviewClass, \IPS\Member::loggedIn()->language()->addToStack( "{$reviewClass::$title}_pl" ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$form = new \IPS\Helpers\Form( 'popular_date', 'continue' );
|
||||
$form->class = 'ipsForm_vertical';
|
||||
$customStart = ( isset( \IPS\Request::i()->custom_date_start ) and \is_numeric( \IPS\Request::i()->custom_date_start ) ) ? (int) \IPS\Request::i()->custom_date_start : NULL;
|
||||
|
||||
@@ -1003,6 +1003,7 @@ class _streams extends \IPS\Dispatcher\Controller
|
||||
protected function _rebuildStreams()
|
||||
{
|
||||
$default = \IPS\Member::loggedIn()->defaultStream;
|
||||
/* @note SELECT_FROM_WRITE_SERVER: Avoid race conditions when the writer has the new stream in the table, but the reader may not */
|
||||
\IPS\Member::loggedIn()->member_streams = json_encode( array( 'default' => $default, 'streams' => iterator_to_array( \IPS\Db::i()->select( 'id, title', 'core_streams', array( '`member`=?', \IPS\Member::loggedIn()->member_id ), NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->setKeyField('id')->setValueField('title') ) ) );
|
||||
\IPS\Member::loggedIn()->save();
|
||||
}
|
||||
|
||||
@@ -1,155 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief account
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
|
||||
* @since 21 Aug 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\modules\front\hive;
|
||||
|
||||
use IPS\core\Hive;
|
||||
use IPS\Db;
|
||||
use IPS\Http\Url;
|
||||
use IPS\Member;
|
||||
use IPS\Output;
|
||||
use IPS\Session;
|
||||
use IPS\Settings;
|
||||
use IPS\Theme;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* account
|
||||
*/
|
||||
class _account extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* ...
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
Output::i()->error( 'node_error', '2HV100/2', 404, '' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Follow Community
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function follow()
|
||||
{
|
||||
/* Check Hive is Enabled */
|
||||
if( empty( Settings::i()->hive_key ) )
|
||||
{
|
||||
Output::i()->error( 'node_error', '2HV100/3', 404, '' );
|
||||
}
|
||||
|
||||
if( Member::loggedIn()->member_id )
|
||||
{
|
||||
$form = new \IPS\Helpers\Form( 'hive', 'hive_subscribe_button' );
|
||||
$form->class = 'ipsForm_vertical ipsType_center ipsForm_noLabels';
|
||||
$form->add( new \IPS\Helpers\Form\Email( 'hive_subscriber_email_address', Member::loggedIn()->email ?? '', TRUE, [ 'placeholder' => Member::loggedIn()->language()->addToStack('email_address')] ) );
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
Session::i()->csrfCheck();
|
||||
|
||||
try
|
||||
{
|
||||
$response = Hive::api( 'subscribe', [
|
||||
'site_member_id' => Member::loggedIn()->member_id ?? 0,
|
||||
'group_hash' => Member::loggedin()->member_id ? Hive::groupHash( Member::loggedin() ) : 'guest',
|
||||
'member_email' => $values['hive_subscriber_email_address'],
|
||||
] );
|
||||
|
||||
/* Save subscribe */
|
||||
if ( Member::loggedIn()->member_id )
|
||||
{
|
||||
try
|
||||
{
|
||||
Db::i()->insert( 'core_hive_subscribers', [ 'member_id' => Member::loggedIn()->member_id, 'subscribe_date' => time() ] );
|
||||
}
|
||||
catch ( Db\Exception $e ){}
|
||||
}
|
||||
|
||||
if ( !empty( $response['redirect_url'] ) )
|
||||
{
|
||||
Output::i()->redirect( Url::external( $response['redirect_url'] ) );
|
||||
}
|
||||
}
|
||||
catch ( \IPS\Http\Url\Exception|\LogicException $e )
|
||||
{
|
||||
$form->error = $e->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$form = Theme::i()->getTemplate( 'hive', 'core', 'front' )->signin();
|
||||
}
|
||||
|
||||
Output::i()->title = Member::loggedIn()->language()->addToStack('hive_subscribe_to_hive', NULL, [ 'sprintf' => Settings::i()->board_name ]);
|
||||
Output::i()->output = Theme::i()->getTemplate( 'hive', 'core', 'front' )->follow( $form );
|
||||
}
|
||||
|
||||
/**
|
||||
* Anonymously follow community
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function anonymousFollow()
|
||||
{
|
||||
Output::i()->pageCaching = FALSE;
|
||||
|
||||
/* Check Hive is Enabled */
|
||||
if( empty( Settings::i()->hive_key ) )
|
||||
{
|
||||
Output::i()->error( 'node_error', '2HV100/4', 404, '' );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$response = Hive::api( 'subscribe', [
|
||||
'site_member_id' => 0,
|
||||
'group_hash' => 'guest',
|
||||
'member_email' => false,
|
||||
] );
|
||||
|
||||
/* Save subscribe */
|
||||
if ( !empty( $response['redirect_url'] ) )
|
||||
{
|
||||
Output::i()->redirect( Url::external( $response['redirect_url'] ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \LogicException( 'unknown_error' );
|
||||
}
|
||||
}
|
||||
catch ( \IPS\Http\Url\Exception|\LogicException $e )
|
||||
{
|
||||
Output::i()->error( $e->getMessage(), '2HV100/1', 403, '' );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,402 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief content
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
|
||||
* @since 21 Aug 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core\modules\front\hive;
|
||||
|
||||
use IPS\core\Hive;
|
||||
use IPS\Content\Item;
|
||||
use IPS\core\Stream;
|
||||
use IPS\Db;
|
||||
use IPS\File;
|
||||
use IPS\File\Exception;
|
||||
use IPS\Http\Url;
|
||||
use IPS\IPS;
|
||||
use IPS\Member;
|
||||
use IPS\Output;
|
||||
use IPS\Patterns\ActiveRecordIterator;
|
||||
use IPS\Request;
|
||||
use IPS\Settings;
|
||||
use Jose\Component\Checker\AudienceChecker;
|
||||
use Jose\Component\Checker\ClaimCheckerManager;
|
||||
use Jose\Component\Checker\ExpirationTimeChecker;
|
||||
use Jose\Component\Checker\InvalidClaimException;
|
||||
use Jose\Component\Checker\IssuedAtChecker;
|
||||
use Jose\Component\Checker\IssuerChecker;
|
||||
use Jose\Component\Checker\MissingMandatoryClaimException;
|
||||
use Jose\Component\Checker\NotBeforeChecker;
|
||||
use Jose\Component\Signature\Serializer\CompactSerializer;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* content
|
||||
*/
|
||||
class _content extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
public function __construct( $url = NULL )
|
||||
{
|
||||
parent::__construct( $url );
|
||||
|
||||
IPS::$PSR0Namespaces['Jose'] = \IPS\ROOT_PATH . '/system/3rd_party/JwtFramework/src';
|
||||
IPS::$PSR0Namespaces['Base64Url'] = \IPS\ROOT_PATH .'/system/3rd_party/Base64Url';
|
||||
|
||||
\IPS\Output::i()->pageCaching = FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
if( isset( Request::i()->click ) )
|
||||
{
|
||||
$this->_doClick();
|
||||
}
|
||||
elseif( isset( Request::i()->follow ) )
|
||||
{
|
||||
$this->_doFollowCommunity();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Don't feed any content if disabled */
|
||||
if( !Settings::i()->hive_enabled )
|
||||
{
|
||||
Output::i()->json( [ 'error' => 'Community Hive not enabled' ], 404 );
|
||||
}
|
||||
|
||||
$this->_verifyJwt();
|
||||
}
|
||||
|
||||
parent::execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* ...
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
try
|
||||
{
|
||||
match ( $this->payload['request_type'] )
|
||||
{
|
||||
'content' => $this->_getContent(),
|
||||
'sync' => $this->_syncMembers(),
|
||||
'unfollow' => $this->_unfollow()
|
||||
};
|
||||
}
|
||||
catch( \UnhandledMatchError $e )
|
||||
{
|
||||
Output::i()->json( array( 'error' => 'Invalid request type' ), 400 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process Hive click and redirect
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _doClick()
|
||||
{
|
||||
try
|
||||
{
|
||||
$string = base64_decode( Request::i()->click );
|
||||
$result = [];
|
||||
parse_str( $string, $result );
|
||||
|
||||
if( empty( $result['key2' ] ) )
|
||||
{
|
||||
throw new \UnexpectedValueException( 'key2 missing' );
|
||||
}
|
||||
|
||||
$item = explode( '/', $result['key2'] );
|
||||
|
||||
if( \count( $item ) !== 2 )
|
||||
{
|
||||
throw new \UnexpectedValueException( 'key2 format unexpected' );
|
||||
}
|
||||
|
||||
if( !class_exists( $item[0], TRUE ) )
|
||||
{
|
||||
throw new \UnexpectedValueException( 'key2 data unexpected' );
|
||||
}
|
||||
|
||||
$object = $item[0]::load( (int) $item[1] );
|
||||
|
||||
if( !( $object instanceof \IPS\Content ) )
|
||||
{
|
||||
throw new \UnexpectedValueException( 'key2 data unexpected 2' );
|
||||
}
|
||||
|
||||
Output::i()->redirect( $object->url() );
|
||||
}
|
||||
catch( \UnexpectedValueException | \OutOfRangeException $e )
|
||||
{
|
||||
Output::i()->redirect( Url::internal( '' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect to Hive follow page
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _doFollowCommunity()
|
||||
{
|
||||
Output::i()->redirect( Url::internal( 'app=core&module=hive&controller=account&do=follow', 'front', 'hive_follow' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get content for Hive
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _getContent()
|
||||
{
|
||||
/* Make sure we have a valid payload */
|
||||
if( ! isset ( $this->payload['group_hash'] ) )
|
||||
{
|
||||
Output::i()->json( [ 'error' => 'Missing Group Hash' ], 400 );
|
||||
}
|
||||
|
||||
/* Use new member object for permissions */
|
||||
$member = new Member;
|
||||
|
||||
/* Set secondary groups */
|
||||
$member->member_group_id = Settings::i()->guest_group;
|
||||
if( $this->payload['group_hash'] !== 'guest' )
|
||||
{
|
||||
$member->mgroup_others = $this->payload['group_hash'];
|
||||
}
|
||||
|
||||
$stream = Stream::allActivityStream();
|
||||
$stream->date_relative_days = 365;
|
||||
$stream->id = 0;
|
||||
$stream->include_comments = TRUE;
|
||||
$stream->baseUrl = Url::internal( "app=core&module=discover&controller=streams", 'front', 'discover_all' );
|
||||
|
||||
/* Content Limitations */
|
||||
$settings = json_decode( Settings::i()->hive_content, TRUE );
|
||||
if( $settings['content_classes'] !== '*' )
|
||||
{
|
||||
$stream->classes = $settings['content_classes'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$stream->classes = '';
|
||||
foreach ( \IPS\Content::routedClasses( TRUE, FALSE, TRUE ) as $class )
|
||||
{
|
||||
if ( is_subclass_of( $class, 'IPS\Content\Searchable' ) and isset( $class::$databaseColumnMap['date'] ) )
|
||||
{
|
||||
$stream->classes .= ',' . $class;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Container - Stream wants JSON and we have an array... */
|
||||
$stream->containers = json_encode( $settings['content_containers'] );
|
||||
|
||||
$query = $stream->query( $member )->setLimit( 10 );
|
||||
$results = $query->search();
|
||||
|
||||
$return = [];
|
||||
foreach( $results as $comment )
|
||||
{
|
||||
$commentData = $comment->asArray();
|
||||
$commentClass = $commentData['indexData']['index_class'];
|
||||
$itemClass = $commentClass::$itemClass ?? $commentClass;
|
||||
$item = $itemClass::load( $commentData['indexData']['index_item_id'] );
|
||||
|
||||
/* Attachments */
|
||||
$attachment = $fileObj = NULL;
|
||||
try
|
||||
{
|
||||
if( !empty( $commentData['itemData']['attachedImages'] ) )
|
||||
{
|
||||
$fileObj = File::get( $commentData['itemData']['attachedImages'][0]['extension'], $commentData['itemData']['attachedImages'][0]['thumb_location'] ?: $commentData['itemData']['attachedImages'][0]['location'] );
|
||||
}
|
||||
elseif( is_subclass_of( $commentData['indexData']['index_class'], 'IPS\Content\Item' ) AND $contentImage = $item->contentImages( 1, TRUE ) )
|
||||
{
|
||||
$attachType = key( $contentImage[0] );
|
||||
$fileObj = File::get( $attachType, $contentImage[0][ $attachType ] );
|
||||
}
|
||||
|
||||
if( $fileObj !== NULL )
|
||||
{
|
||||
if ( $fileObj->isImage() )
|
||||
{
|
||||
$fileContents = $fileObj->contents();
|
||||
if ( \strlen( $fileContents ) > 1000000 )
|
||||
{
|
||||
throw new \OutOfRangeException;
|
||||
}
|
||||
|
||||
$attachment = [
|
||||
'name' => $fileObj->originalFilename,
|
||||
'file' => base64_encode( $fileContents )
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( Exception | \UnderflowException | \OutOfRangeException $e ) { }
|
||||
|
||||
/* Truncate content */
|
||||
if( mb_strlen( $commentData['indexData']['index_content'] ) > 500 )
|
||||
{
|
||||
$commentData['indexData']['index_content'] = trim( mb_substr( $commentData['indexData']['index_content'], 0, 500 ) ) . '...';
|
||||
}
|
||||
$classObj = $commentClass::load( $commentData['indexData']['index_object_id'] );
|
||||
$return[] = array(
|
||||
'title' => $item->mapped('title'),
|
||||
'content' => $commentData['indexData']['index_content'],
|
||||
'date' => $commentData['indexData']['index_date_commented'],
|
||||
'author' => $classObj->isAnonymous() ? \IPS\Member::loggedIn()->language()->get( "post_anonymously_placename" ) : $commentData['authorData']['name'],
|
||||
'key1' => $itemClass . '/' . $commentData['indexData']['index_item_id'],
|
||||
'key2' => $commentClass . '/' . $commentData['indexData']['index_object_id'],
|
||||
'replies' => ( $item instanceof Item ) ? $item->commentCount() : NULL,
|
||||
'reactions' => IPS::classUsesTrait( $itemClass, 'IPS\Content\Reactable' ) ? $item->reactionCount() : NULL,
|
||||
'image' => $attachment
|
||||
);
|
||||
}
|
||||
|
||||
Output::i()->json([
|
||||
'results' => $return
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync Hive Membership data
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _syncMembers()
|
||||
{
|
||||
if( !isset( $this->payload['sync_data'] ) OR !\count( $this->payload['sync_data'] ) )
|
||||
{
|
||||
Output::i()->json( [ 'error' => 'Missing sync data' ], 400 );
|
||||
}
|
||||
|
||||
$memberIds = array_keys( $this->payload['sync_data'] );
|
||||
$memberData = new ActiveRecordIterator( Db::i()->select( '*', 'core_members', [ Db::i()->in( 'member_id', $memberIds ) ] ), 'IPS\Member' );
|
||||
$respond = [];
|
||||
$seen = [];
|
||||
|
||||
foreach( $memberData as $member )
|
||||
{
|
||||
$generatedHash = Hive::groupHash( $member );
|
||||
|
||||
if( $generatedHash !== $this->payload['sync_data'][ $member->member_id ] )
|
||||
{
|
||||
$respond[ $member->member_id ] = $generatedHash;
|
||||
}
|
||||
$seen[] = $member->member_id;
|
||||
}
|
||||
|
||||
/* Record subscribe confirm */
|
||||
Db::i()->update( 'core_hive_subscribers', [ 'subscribe_confirmed' => 1 ], [ [ 'subscribe_confirmed=?', 0 ], [ Db::i()->in( 'member_id', $memberIds ) ] ] );
|
||||
|
||||
/* Those that haven't been seen, thus deleted */
|
||||
$deleted = array_diff( $memberIds, $seen );
|
||||
|
||||
foreach( $deleted as $del )
|
||||
{
|
||||
$respond[ $del ] = 'guest';
|
||||
}
|
||||
|
||||
Output::i()->json( $respond );
|
||||
}
|
||||
|
||||
/**
|
||||
* Unfollow
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _unfollow()
|
||||
{
|
||||
Db::i()->delete( 'core_hive_subscribers', [ 'member_id=?', (int) $this->payload['member_id'] ] );
|
||||
Output::i()->json( 'ok' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify JWT
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _verifyJwt()
|
||||
{
|
||||
/* Make sure the request is PUT */
|
||||
if( ! isset( $_SERVER['REQUEST_METHOD'] ) OR mb_strtoupper( $_SERVER['REQUEST_METHOD'] ) !== 'POST' )
|
||||
{
|
||||
Output::i()->json( array( 'error' => 'Invalid HTTP Method' ), 400 );
|
||||
}
|
||||
|
||||
/* Make sure we have the hive key (activated) */
|
||||
if( ! Settings::i()->hive_enabled )
|
||||
{
|
||||
Output::i()->json( array( 'error' => 'Community Hive not enabled' ), 404 );
|
||||
}
|
||||
|
||||
/* Do we have the JWT */
|
||||
$token = file_get_contents('php://input');
|
||||
if( empty( $token ) )
|
||||
{
|
||||
Output::i()->json( array( 'error' => 'Missing JWT' ), 401 );
|
||||
}
|
||||
|
||||
$jwk = new \Jose\Component\Core\JWK([
|
||||
'kty' => 'oct',
|
||||
'k' => base64_encode( Settings::i()->hive_key )
|
||||
]);
|
||||
|
||||
$jwsVerifier = new \Jose\Component\Signature\JWSVerifier(
|
||||
new \Jose\Component\Core\AlgorithmManager([ new \Jose\Component\Signature\Algorithm\HS256() ])
|
||||
);
|
||||
|
||||
$jwsCompactSerializer = new CompactSerializer();
|
||||
|
||||
$data = $jwsCompactSerializer->unserialize( $token );
|
||||
if( !$jwsVerifier->verifyWithKey( $data, $jwk, 0 ) )
|
||||
{
|
||||
Output::i()->json( array( 'error' => 'Invalid JWT' ), 401 );
|
||||
}
|
||||
|
||||
$this->payload = json_decode( $data->getPayload(), true );
|
||||
|
||||
$claimCheckerManager = new ClaimCheckerManager(
|
||||
[
|
||||
new IssuerChecker( ['communityhive'] ),
|
||||
new AudienceChecker( rtrim( Settings::i()->base_url, '/' ) ),
|
||||
new IssuedAtChecker( 1000 ),
|
||||
new NotBeforeChecker( 1000 ),
|
||||
new ExpirationTimeChecker( 1000 )
|
||||
]
|
||||
);
|
||||
|
||||
try
|
||||
{
|
||||
$claimCheckerManager->check( $this->payload, [ 'iss', 'sub', 'exp', 'aud', 'nbf', 'iat' ] );
|
||||
}
|
||||
catch( MissingMandatoryClaimException | InvalidClaimException $e )
|
||||
{
|
||||
Output::i()->json( array( 'message' => $e->getMessage() ), 400 );
|
||||
}
|
||||
|
||||
parent::execute();
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,9 @@
|
||||
namespace IPS\core\modules\front\members;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
|
||||
use IPS\Member;
|
||||
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
@@ -57,12 +60,12 @@ class _profile extends \IPS\Helpers\CoverPhoto\Controller
|
||||
if( !\IPS\Request::i()->isAjax() )
|
||||
{
|
||||
/* Don't index new empty profiles */
|
||||
if( ! $this->member->member_posts )
|
||||
{
|
||||
\IPS\Output::i()->metaTags['robots'] = 'noindex, follow';
|
||||
}
|
||||
|
||||
\IPS\Output::i()->linkTags['canonical'] = (string) $this->member->url();
|
||||
if( ! $this->shouldBeIndexed() )
|
||||
{
|
||||
\IPS\Output::i()->metaTags['robots'] = 'noindex, follow';
|
||||
}
|
||||
|
||||
\IPS\Output::i()->linkTags['canonical'] = (string) $this->member->url();
|
||||
|
||||
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'front_statuses.js', 'core' ) );
|
||||
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'global_core.js', 'core', 'global' ) );
|
||||
@@ -322,8 +325,11 @@ class _profile extends \IPS\Helpers\CoverPhoto\Controller
|
||||
$page = 1;
|
||||
}
|
||||
|
||||
$clubsCount = \IPS\Member\Club::clubs( \IPS\Member::loggedIn(), array( ( $page - 1 ) * $perPage, $perPage ), 'last_activity', $this->member, array(), \IPS\Member::loggedIn()->modPermission( 'can_access_all_clubs' ) ? NULL : "( show_membertab = 'nonmember' OR show_membertab IS NULL )", TRUE );
|
||||
$allClubs = \IPS\Member\Club::clubs( \IPS\Member::loggedIn(), array( ( $page - 1 ) * $perPage, $perPage ), 'last_activity', $this->member, array(), \IPS\Member::loggedIn()->modPermission( 'can_access_all_clubs' ) ? NULL : "( show_membertab = 'nonmember' OR show_membertab IS NULL )" );
|
||||
/* Show all clubs this member belongs to if the viewer is a moderator with permissions to access all clubs or if the viewer is on his own profile */
|
||||
$extraWhere = ( Member::loggedIn()->member_id === $this->member->member_id OR \IPS\Member::loggedIn()->modPermission( 'can_access_all_clubs' ) ) ? NULL : "( show_membertab = 'nonmember' OR show_membertab IS NULL )";
|
||||
|
||||
$clubsCount = \IPS\Member\Club::clubs( \IPS\Member::loggedIn(), array( ( $page - 1 ) * $perPage, $perPage ), 'last_activity', $this->member, array(), $extraWhere, TRUE );
|
||||
$allClubs = \IPS\Member\Club::clubs( \IPS\Member::loggedIn(), array( ( $page - 1 ) * $perPage, $perPage ), 'last_activity', $this->member, array(), $extraWhere );
|
||||
$pagination = \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->pagination( $baseUrl, ( ceil( $clubsCount / $perPage ) ), $page, $perPage );
|
||||
|
||||
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/clubs.css', 'core', 'front' ) );
|
||||
@@ -354,7 +360,7 @@ class _profile extends \IPS\Helpers\CoverPhoto\Controller
|
||||
}
|
||||
|
||||
/* If this is AJAX request to change the tab, just display that */
|
||||
if ( \IPS\Request::i()->isAjax() and isset( \IPS\Request::i()->tab ) )
|
||||
if ( \IPS\Request::i()->isAjax() and isset( \IPS\Request::i()->tab ) and !isset( \IPS\Request::i()->entireSection ) )
|
||||
{
|
||||
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->blankTemplate( $tabContents ) );
|
||||
}
|
||||
@@ -428,6 +434,11 @@ class _profile extends \IPS\Helpers\CoverPhoto\Controller
|
||||
'@type' => "ProfilePage",
|
||||
'url' => (string) $this->member->url(),
|
||||
'name' => $this->member->name,
|
||||
'mainEntity'=> [
|
||||
'@type' => 'Person',
|
||||
'name' => $this->member->name,
|
||||
'identifier' => $this->member->member_id,
|
||||
],
|
||||
'primaryImageOfPage' => array(
|
||||
'@type' => "ImageObject",
|
||||
'contentUrl' => (string) $this->member->get_photo( TRUE, TRUE ),
|
||||
@@ -1988,7 +1999,6 @@ class _profile extends \IPS\Helpers\CoverPhoto\Controller
|
||||
{
|
||||
/* Get the different types */
|
||||
$types = array();
|
||||
$hasCallback = array();
|
||||
foreach ( \IPS\Content::routedClasses( TRUE, FALSE, TRUE ) as $class )
|
||||
{
|
||||
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'profile.css', $class::$application ) );
|
||||
@@ -1999,6 +2009,11 @@ class _profile extends \IPS\Helpers\CoverPhoto\Controller
|
||||
}
|
||||
}
|
||||
|
||||
if( !count( $types ) )
|
||||
{
|
||||
\IPS\Output::i()->error( 'no_module_permission', '2C138/T', 403, '' );
|
||||
}
|
||||
|
||||
/* What type are we looking at? */
|
||||
$currentType = NULL;
|
||||
if ( isset( \IPS\Request::i()->type ) AND array_key_exists( \IPS\Request::i()->type, $types ) )
|
||||
@@ -2236,4 +2251,19 @@ class _profile extends \IPS\Helpers\CoverPhoto\Controller
|
||||
\IPS\Output::i()->redirect( $content->url(), 'recognize_removed_success' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the member profile be indexed?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function shouldBeIndexed(): bool
|
||||
{
|
||||
/* Don't index new empty profiles */
|
||||
if( ! $this->member->member_posts )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
@@ -45,12 +45,16 @@ class _search extends \IPS\Dispatcher\Controller
|
||||
*/
|
||||
protected function _checkCached()
|
||||
{
|
||||
/* Check whether a 304 Not modified response is appropriate */
|
||||
if( !empty( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) AND ( new \IPS\DateTime( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) )->getTimestamp() > ( time() - $this->_cacheTimeout ) )
|
||||
try
|
||||
{
|
||||
header('HTTP/1.1 304 Not Modified' );
|
||||
exit;
|
||||
/* Check whether a 304 Not modified response is appropriate */
|
||||
if( !empty( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) AND ( new \IPS\DateTime( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) )->getTimestamp() > ( time() - $this->_cacheTimeout ) )
|
||||
{
|
||||
header('HTTP/1.1 304 Not Modified' );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
catch( \Exception $e ){}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1066,7 +1070,12 @@ class _search extends \IPS\Dispatcher\Controller
|
||||
/* Fields */
|
||||
foreach ( $fields as $id => $field )
|
||||
{
|
||||
|
||||
/* Only show to non-staff if available to view by all. */
|
||||
if ( \IPS\core\ProfileFields\Field::load( $id )->member_hide != 'all' AND ( !\IPS\Member::loggedIn()->isAdmin() OR !\IPS\Member::loggedIn()->modPermissions() ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Alias the lang keys */
|
||||
$realLangKey = "core_pfield_{$id}";
|
||||
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
|
||||
namespace IPS\core\modules\front\system;
|
||||
|
||||
use IPS\Http\Url;
|
||||
use IPS\Output;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
@@ -103,58 +106,64 @@ class _ajax extends \IPS\Dispatcher\Controller
|
||||
|
||||
/* As you can insert "other media" from other apps (such as Downloads), we need to route the attachIDs appropriately. */
|
||||
$attachmentIds = array();
|
||||
|
||||
foreach( array_keys( \IPS\Request::i()->attachIDs ) as $attachId )
|
||||
{
|
||||
if( (int) $attachId == $attachId AND mb_strlen( (int) $attachId ) == mb_strlen( $attachId ) )
|
||||
{
|
||||
$attachmentIds[] = $attachId;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
$url = \IPS\Http\Url::createFromString( $attachId );
|
||||
|
||||
/* Get the "real" query string (whatever the query string is, plus what we can get from decoding the FURL) */
|
||||
$qs = array_merge( $url->queryString, $url->hiddenQueryString );
|
||||
|
||||
/* We need an app, and it needs to not be an RSS link */
|
||||
if ( !isset( $qs['app'] ) )
|
||||
{
|
||||
throw new \UnexpectedValueException;
|
||||
}
|
||||
|
||||
/* Load the application */
|
||||
$application = \IPS\Application::load( $qs['app'] );
|
||||
|
||||
/* Loop through our content classes and see if we can find one that matches */
|
||||
foreach ( $application->extensions( 'core', 'ContentRouter' ) as $key => $extension )
|
||||
{
|
||||
$classes = $extension->classes;
|
||||
|
||||
/* So for each of those... */
|
||||
foreach ( $classes as $class )
|
||||
if( \IPS\Request::i()->attachIDs )
|
||||
{
|
||||
foreach( array_keys( \IPS\Request::i()->attachIDs ) as $attachId )
|
||||
{
|
||||
if( (int) $attachId == $attachId and mb_strlen( (int) $attachId ) == mb_strlen( $attachId ) )
|
||||
{
|
||||
$attachmentIds[] = $attachId;
|
||||
}else
|
||||
{
|
||||
try
|
||||
{
|
||||
$url = \IPS\Http\Url::createFromString( $attachId );
|
||||
|
||||
/* Get the "real" query string (whatever the query string is, plus what we can get from decoding the FURL) */
|
||||
$qs = array_merge( $url->queryString, $url->hiddenQueryString );
|
||||
|
||||
/* We need an app, and it needs to not be an RSS link */
|
||||
if( !isset( $qs[ 'app' ] ) )
|
||||
{
|
||||
/* Try to load it */
|
||||
try
|
||||
throw new \UnexpectedValueException;
|
||||
}
|
||||
|
||||
/* Load the application */
|
||||
$application = \IPS\Application::load( $qs[ 'app' ] );
|
||||
|
||||
/* Loop through our content classes and see if we can find one that matches */
|
||||
foreach( $application->extensions( 'core', 'ContentRouter' ) as $key => $extension )
|
||||
{
|
||||
$classes = $extension->classes;
|
||||
|
||||
/* So for each of those... */
|
||||
foreach( $classes as $class )
|
||||
{
|
||||
$item = $class::loadFromURL( $url );
|
||||
|
||||
if( !$item->canView() )
|
||||
/* Try to load it */
|
||||
try
|
||||
{
|
||||
throw new \OutOfRangeException;
|
||||
}
|
||||
$item = $class::loadFromURL( $url );
|
||||
|
||||
/* If we're still here, we should be good. Any exceptions will have been caught by our general try/catch. */
|
||||
$toReturn[ $attachId ] = $item->getAttachmentInfo();
|
||||
break;
|
||||
if( !$item->canView() )
|
||||
{
|
||||
throw new \OutOfRangeException;
|
||||
}
|
||||
|
||||
/* If we're still here, we should be good. Any exceptions will have been caught by our general try/catch. */
|
||||
$toReturn[ $attachId ] = $item->getAttachmentInfo();
|
||||
break;
|
||||
}
|
||||
catch( \OutOfRangeException $e )
|
||||
{
|
||||
}
|
||||
}
|
||||
catch( \OutOfRangeException $e ){}
|
||||
}
|
||||
}
|
||||
catch( \Exception $e )
|
||||
{
|
||||
}
|
||||
}
|
||||
catch( \Exception $e ){}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +206,7 @@ class _ajax extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
try
|
||||
{
|
||||
if ( $loadedExtensions[ $map['location_key'] ]->attachmentPermissionCheck( $member, $map['id1'], $map['id2'], $map['id3'], $attachment ) )
|
||||
if ( method_exists( $loadedExtensions[ $map['location_key'] ], 'attachmentPermissionCheck') AND $loadedExtensions[ $map['location_key'] ]->attachmentPermissionCheck( $member, $map['id1'], $map['id2'], $map['id3'], $attachment ) )
|
||||
{
|
||||
$permission = TRUE;
|
||||
break;
|
||||
@@ -685,7 +694,10 @@ class _ajax extends \IPS\Dispatcher\Controller
|
||||
public function getCsrfKey()
|
||||
{
|
||||
/* Don't cache the CSRF key */
|
||||
\IPS\Output::i()->pageCaching = FALSE;
|
||||
\IPS\Output::setCacheTime( false );
|
||||
|
||||
/* Restrict endpoint to our origin JS */
|
||||
Output::i()->httpHeaders['Access-Control-Allow-Origin'] = Url::internal('')->data[ Url::COMPONENT_SCHEME ] . '://' . Url::internal('')->data[ Url::COMPONENT_HOST ];
|
||||
|
||||
if ( isset( \IPS\Request::i()->path ) )
|
||||
{
|
||||
@@ -717,7 +729,7 @@ class _ajax extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
}
|
||||
|
||||
\IPS\Output::i()->json( [ 'key' => \IPS\Session::i()->csrfKey ] );
|
||||
\IPS\Output::i()->json( [ 'key' => \IPS\Session::i()->csrfKey, 'expiry' => time() + 500 ] );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
namespace IPS\core\modules\front\system;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
|
||||
use IPS\core\Alerts\Alert;
|
||||
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
@@ -47,7 +50,7 @@ class _alerts extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
$alert = \IPS\core\Alerts\Alert::load( \IPS\Request::i()->id );
|
||||
|
||||
if( $alert->reply == 2 and \IPS\Member::loggedIn()->member_id and \IPS\Member::loggedIn()->canUseMessenger() )
|
||||
if( $alert->reply == Alert::REPLY_REQUIRED and \IPS\Member::loggedIn()->member_id and \IPS\Member::loggedIn()->canUseMessenger() and \IPS\Member::load( $alert->member_id )->member_id )
|
||||
{
|
||||
\IPS\Output::i()->error( 'alert_cant_dismiss', '3C428/1', 403, '' );
|
||||
}
|
||||
|
||||
@@ -43,9 +43,18 @@ class _content extends \IPS\Dispatcher\Controller
|
||||
|
||||
try
|
||||
{
|
||||
$commentClass = $class::$commentClass;
|
||||
$item = $class::load( \IPS\Request::i()->content_id );
|
||||
|
||||
if( isset( $item::$archiveClass ) AND method_exists( $item, 'isArchived' ) AND $item->isArchived() )
|
||||
{
|
||||
$commentClass = $class::$archiveClass;
|
||||
}
|
||||
else
|
||||
{
|
||||
$commentClass = $class::$commentClass;
|
||||
}
|
||||
|
||||
$comment = $commentClass::load( \IPS\Request::i()->content_commentid );
|
||||
$item = $comment->item();
|
||||
}
|
||||
catch( \OutOfRangeException $ex )
|
||||
{
|
||||
|
||||
@@ -30,7 +30,7 @@ class _cookies extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
parent::execute();
|
||||
|
||||
\IPS\Output::i()->pageCaching = FALSE;
|
||||
\IPS\Output::setCacheTime( false );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,5 +76,6 @@ class _cookies extends \IPS\Dispatcher\Controller
|
||||
\IPS\Output::i()->redirect( $url );
|
||||
}
|
||||
}
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal(''));
|
||||
}
|
||||
}
|
||||
@@ -62,13 +62,30 @@ class _editor extends \IPS\Dispatcher\Controller
|
||||
protected function image()
|
||||
{
|
||||
$maxImageDims = \IPS\Settings::i()->attachment_image_size ? explode( 'x', \IPS\Settings::i()->attachment_image_size ) : array( 1000, 750 );
|
||||
|
||||
/* Let's do some casting here */
|
||||
|
||||
\IPS\Request::i()->width = (int) \IPS\Request::i()->width;
|
||||
\IPS\Request::i()->height = (int) \IPS\Request::i()->height;
|
||||
\IPS\Request::i()->actualWidth = (int) \IPS\Request::i()->actualWidth;
|
||||
\IPS\Request::i()->actualHeight = (int) \IPS\Request::i()->actualHeight;
|
||||
$maxImageDims = array_map('intval', $maxImageDims);
|
||||
|
||||
foreach( array( 'width', 'height', 'actualWidth', 'actualHeight' ) as $key )
|
||||
{
|
||||
if( \IPS\Request::i()->$key <= 0 )
|
||||
{
|
||||
\IPS\Output::i()->error( 'invalid_image_dimensions', '2C270/2', 403, '' );
|
||||
}
|
||||
}
|
||||
|
||||
$ratioH = round( \IPS\Request::i()->height / \IPS\Request::i()->width, 2 );
|
||||
$ratioW = round( \IPS\Request::i()->width / \IPS\Request::i()->height, 2 );
|
||||
|
||||
if( \intval( $maxImageDims[0] ) === 0 && \intval( $maxImageDims[1] ) === 0 )
|
||||
if( $maxImageDims[0] === 0 && $maxImageDims[1] === 0 )
|
||||
{
|
||||
$maxWidth = \IPS\Request::i()->actualWidth;
|
||||
$maxHeight = \IPS\Request::i()->actualHeight;
|
||||
$maxWidth = (int) \IPS\Request::i()->actualWidth;
|
||||
$maxHeight = (int) \IPS\Request::i()->actualHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -353,7 +370,7 @@ class _editor extends \IPS\Dispatcher\Controller
|
||||
try
|
||||
{
|
||||
$item = $class::load( \IPS\Request::i()->contentId );
|
||||
foreach( $item->mostRecent( \IPS\Request::i()->input ) AS $row )
|
||||
foreach( $item->mostRecent( \IPS\Request::i()->input, 10, FALSE ) AS $row )
|
||||
{
|
||||
$memberIds[] = $row->member_id;
|
||||
$results .= \IPS\Theme::i()->getTemplate( 'editor', 'core', 'global' )->mentionRow( $row );
|
||||
|
||||
@@ -57,6 +57,10 @@ class _embed extends \IPS\Content\Controller
|
||||
\IPS\Output::i()->js( 'js/commonEmbedHandler.js', 'core', 'interface' ),
|
||||
\IPS\Output::i()->js( 'js/externalEmbedHandler.js', 'core', 'interface' )
|
||||
);
|
||||
|
||||
/* We don't want search engines indexing this */
|
||||
\IPS\Output::i()->metaTags['robots'] = 'noindex';
|
||||
|
||||
/* Intentionally replace the cssFiles array with a single file here, since we don't need the complete CSS framework in external embeds */
|
||||
\IPS\Output::i()->cssFiles = \IPS\Theme::i()->css( 'styles/embeds.css', 'core', 'front' );
|
||||
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->embedExternal( $return, $js ), 200 );
|
||||
|
||||
@@ -34,7 +34,7 @@ class _login extends \IPS\Dispatcher\Controller
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
\IPS\Output::i()->pageCaching = FALSE;
|
||||
\IPS\Output::setCacheTime( false );
|
||||
|
||||
/* Init login class */
|
||||
$login = new \IPS\Login( \IPS\Http\Url::internal( "app=core&module=system&controller=login", 'front', 'login' ) );
|
||||
@@ -250,7 +250,7 @@ class _login extends \IPS\Dispatcher\Controller
|
||||
/* Otherwise show the reauthenticate form */
|
||||
\IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimal';
|
||||
\IPS\Output::i()->sidebar['enabled'] = FALSE;
|
||||
\IPS\Output::i()->pageCaching = FALSE;
|
||||
\IPS\Output::setCacheTime( false );
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('login');
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system' )->mergeSocialAccount( $handler, $member, $login, $error );
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
namespace IPS\core\modules\front\system;
|
||||
|
||||
use IPS\Text\Encrypt;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
@@ -152,27 +154,29 @@ class _lostpass extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Db::i()->update( 'core_validating', array( 'email_sent' => time() ), array( 'vid=?', $vid ) );
|
||||
$plainSecurityKey = \IPS\Login::generateRandomString();
|
||||
\IPS\Db::i()->update( 'core_validating', [ 'email_sent' => time(), 'security_key' => Encrypt::fromPlaintext( $plainSecurityKey )->tag() ], [ 'vid=?', $vid ] );
|
||||
}
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
{
|
||||
$vid = md5( $member->members_pass_hash . \IPS\Login::generateRandomString() );
|
||||
|
||||
\IPS\Db::i()->insert( 'core_validating', array(
|
||||
'vid' => $vid,
|
||||
'member_id' => $member->member_id,
|
||||
'entry_date' => time(),
|
||||
'lost_pass' => 1,
|
||||
'ip_address' => $member->ip_address,
|
||||
'email_sent' => time(),
|
||||
) );
|
||||
$plainSecurityKey = \IPS\Login::generateRandomString();
|
||||
\IPS\Db::i()->insert( 'core_validating', [
|
||||
'vid' => $vid,
|
||||
'member_id' => $member->member_id,
|
||||
'entry_date' => time(),
|
||||
'lost_pass' => 1,
|
||||
'ip_address' => $member->ip_address,
|
||||
'email_sent' => time(),
|
||||
'security_key' => Encrypt::fromPlaintext( $plainSecurityKey )->tag()
|
||||
] );
|
||||
}
|
||||
|
||||
/* Send email */
|
||||
if ( $sendEmail )
|
||||
{
|
||||
\IPS\Email::buildFromTemplate( 'core', 'lost_password_init', array( $member, $vid ), \IPS\Email::TYPE_TRANSACTIONAL )->send( $member );
|
||||
\IPS\Email::buildFromTemplate( 'core', 'lost_password_init', array( $member, $vid, $plainSecurityKey ), \IPS\Email::TYPE_TRANSACTIONAL )->send( $member );
|
||||
$message = "lost_pass_confirm";
|
||||
}
|
||||
else
|
||||
@@ -205,6 +209,12 @@ class _lostpass extends \IPS\Dispatcher\Controller
|
||||
\IPS\Output::i()->error( 'no_validation_key', '2S151/1', 410, '' );
|
||||
}
|
||||
|
||||
/* Check security key */
|
||||
if( !\IPS\Login::compareHashes( Encrypt::fromTag( $record['security_key'] )->decrypt(), \IPS\Request::i()->security_key ) )
|
||||
{
|
||||
\IPS\Output::i()->error( 'lostpass_invalid_security_key', '2S151/5', 403, '' );
|
||||
}
|
||||
|
||||
/* Show a nicer error message if their link has expired */
|
||||
if( $record['entry_date'] < \IPS\DateTime::create()->sub( new \DateInterval( 'PT1H' ) )->getTimestamp() )
|
||||
{
|
||||
@@ -223,7 +233,8 @@ class _lostpass extends \IPS\Dispatcher\Controller
|
||||
$member = \IPS\Member::load( $record['member_id'] );
|
||||
|
||||
/* Reset the failed logins storage - we don't need to save because the login handler will do that for us later */
|
||||
$member->failed_logins = array();
|
||||
\IPS\Db::i()->delete( 'core_login_failures', [ 'login_member_id=?', $member->member_id ] );
|
||||
$member->failed_login_count = 0;
|
||||
|
||||
/* Now reset the member's password. If no handlers accept the change, create a local password */
|
||||
if ( !$member->changePassword( $values['password'], 'lost' ) )
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief marketplace
|
||||
* @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 16 Jul 2020
|
||||
*/
|
||||
|
||||
namespace IPS\core\modules\front\system;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* marketplace
|
||||
*/
|
||||
class _marketplace extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
/**
|
||||
* ...
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function manage()
|
||||
{
|
||||
if ( isset( \IPS\Request::i()->member ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
$token = \IPS\Db::i()->select( 'token', 'core_marketplace_tokens', array( 'id=?', \IPS\Request::i()->member ) )->first();
|
||||
if ( $token !== 'pending-' . \IPS\Request::i()->hash )
|
||||
{
|
||||
throw new \UnderflowException;
|
||||
}
|
||||
|
||||
\IPS\Db::i()->update( 'core_marketplace_tokens', array( 'token' => \IPS\Request::i()->access_token, 'expires_at' => time() + \IPS\Request::i()->expires_in ), array( 'id=?', \IPS\Request::i()->member ) );
|
||||
|
||||
$response = 'OK';
|
||||
$responseCode = 200;
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
{
|
||||
$response = 'BAD_HASH';
|
||||
$responseCode = 403;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$response = 'NO_MEMBER';
|
||||
$responseCode = 404;
|
||||
}
|
||||
|
||||
\IPS\Output::i()->sendOutput( $response, $responseCode, 'text/plain' );
|
||||
}
|
||||
}
|
||||
@@ -176,6 +176,11 @@ class _metatags extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
$manifest = json_decode( \IPS\Settings::i()->manifest_details, TRUE );
|
||||
|
||||
if( !$manifest )
|
||||
{
|
||||
$manifest = [];
|
||||
}
|
||||
|
||||
$output = array(
|
||||
'scope' => rtrim( \IPS\Settings::i()->base_url, '/' ) . '/',
|
||||
'name' => \IPS\Settings::i()->board_name,
|
||||
@@ -205,7 +210,7 @@ class _metatags extends \IPS\Dispatcher\Controller
|
||||
$file = \IPS\File::get( 'core_Icons', $v['url'] );
|
||||
|
||||
$output['icons'][] = array(
|
||||
'src' => (string) $file->url,
|
||||
'src' => (string) $file->url->setQueryString( 'v', $manifest['cache_key'] ),
|
||||
'type' => \IPS\File::getMimeType( $file->originalFilename ),
|
||||
'sizes' => $v['width'] . 'x' . $v['height'],
|
||||
'purpose' => 'any'
|
||||
@@ -225,7 +230,7 @@ class _metatags extends \IPS\Dispatcher\Controller
|
||||
$file = \IPS\File::get( 'core_Icons', $v['url'] );
|
||||
|
||||
$output['icons'][] = array(
|
||||
'src' => (string) $file->url,
|
||||
'src' => (string) $file->url->setQueryString( 'v', $manifest['cache_key'] ),
|
||||
'type' => \IPS\File::getMimeType( $file->originalFilename ),
|
||||
'sizes' => $v['width'] . 'x' . $v['height'],
|
||||
'purpose' => 'maskable'
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
namespace IPS\core\modules\front\system;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
|
||||
use function md5;
|
||||
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
@@ -827,7 +830,7 @@ class _notifications extends \IPS\Dispatcher\Controller
|
||||
protected function unfollowFromEmail()
|
||||
{
|
||||
/* Logged in? */
|
||||
if ( \IPS\Member::loggedIn()->member_id )
|
||||
if ( \IPS\Member::loggedIn()->member_id and ! isset( \IPS\Request::i()->listunsubscribe ) )
|
||||
{
|
||||
/* Go to the normal page */
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=system&controller=notifications&do=follow&follow_app=". \IPS\Request::i()->follow_app ."&follow_area=". \IPS\Request::i()->follow_area ."&follow_id=" . \IPS\Request::i()->follow_id ) );
|
||||
@@ -835,7 +838,7 @@ class _notifications extends \IPS\Dispatcher\Controller
|
||||
|
||||
if ( ! empty( \IPS\Request::i()->gkey ) )
|
||||
{
|
||||
list( $followKey, $memberKey ) = explode( '-', \IPS\Request::i()->gkey );
|
||||
[ $followKey, $memberKey ] = explode( '-', \IPS\Request::i()->gkey );
|
||||
/* Do we follow it? */
|
||||
try
|
||||
{
|
||||
@@ -910,7 +913,29 @@ class _notifications extends \IPS\Dispatcher\Controller
|
||||
|
||||
/* Grab a count */
|
||||
$count = \IPS\Db::i()->select( 'COUNT(*)', 'core_follow', array( 'follow_member_id=? and follow_notify_freq != ?', $member->member_id, 'none' ) )->first();
|
||||
|
||||
|
||||
if ( isset( \IPS\Request::i()->listunsubscribe ) and \IPS\Request::i()->requestMethod() == 'POST' )
|
||||
{
|
||||
\IPS\Db::i()->update( 'core_follow', array( 'follow_notify_freq' => 'none' ), array( 'follow_id=? AND follow_member_id=?', $current['follow_id'], $member->member_id ) );
|
||||
|
||||
/* Unfollow club areas */
|
||||
if ( $class == "IPS\Member\Club" )
|
||||
{
|
||||
foreach ( $thing->nodes() as $node )
|
||||
{
|
||||
$itemClass = $node['node_class']::$contentItemClass;
|
||||
$followApp = $itemClass::$application;
|
||||
$followArea = mb_strtolower( mb_substr( $node['node_class'], mb_strrpos( $node['node_class'], '\\' ) + 1 ) );
|
||||
|
||||
\IPS\Db::i()->update( 'core_follow', array( 'follow_notify_freq' => 'none' ), array( 'follow_id=? AND follow_member_id=?', md5( $followApp . ';' . $followArea . ';' . $node['node_id'] . ';' . $member->member_id ), $member->member_id ) );
|
||||
}
|
||||
}
|
||||
|
||||
/* Done */
|
||||
\IPS\Output::i()->output = \IPS\Member::loggedIn()->language()->addToStack('unsubscribed');
|
||||
return;
|
||||
}
|
||||
|
||||
$form = new \IPS\Helpers\Form( 'unfollowFromEmail', 'update_follow' );
|
||||
$form->class = 'ipsForm_vertical';
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
namespace IPS\core\modules\front\system;
|
||||
|
||||
use IPS\Text\Encrypt;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
@@ -61,7 +63,7 @@ class _register extends \IPS\Dispatcher\Controller
|
||||
\IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimalNoHome';
|
||||
}
|
||||
\IPS\Output::i()->sidebar['enabled'] = FALSE;
|
||||
\IPS\Output::i()->pageCaching = FALSE;
|
||||
\IPS\Output::setCacheTime( false );
|
||||
\IPS\Output::i()->linkTags['canonical'] = (string) \IPS\Http\Url::internal( 'app=core&module=system&controller=register', 'front', 'register' );
|
||||
}
|
||||
|
||||
@@ -430,14 +432,20 @@ class _register extends \IPS\Dispatcher\Controller
|
||||
/* Set the form label */
|
||||
\IPS\Member::loggedIn()->language()->words['q_and_a'] = \IPS\Member::loggedIn()->language()->addToStack( 'core_question_and_answer_' . $question['qa_id'], FALSE );
|
||||
}
|
||||
|
||||
|
||||
$captcha = new \IPS\Helpers\Form\Captcha;
|
||||
|
||||
|
||||
/* If PBR request is from the last 5 minutes, don't ask for a captcha again */
|
||||
if( $postBeforeRegister !== NULL AND \IPS\DateTime::ts( $postBeforeRegister['timestamp'] )->add( new \DateInterval('PT5M' ) )->getTimestamp() > time() )
|
||||
{
|
||||
$captcha = '';
|
||||
}
|
||||
|
||||
if ( (string) $captcha !== '' )
|
||||
{
|
||||
$form->add( $captcha );
|
||||
}
|
||||
|
||||
|
||||
if ( $question OR (string) $captcha !== '' )
|
||||
{
|
||||
$form->addSeparator();
|
||||
@@ -600,7 +608,7 @@ class _register extends \IPS\Dispatcher\Controller
|
||||
$answers[ $v ] = array(
|
||||
'answer_question_id' => $v,
|
||||
'answer_member_id' => $member->member_id,
|
||||
'answer_answer' => \IPS\Text\Encrypt::fromPlaintext( $values[ 'security_question_a_' . $matches[1] ] )->tag()
|
||||
'answer_answer' => Encrypt::fromPlaintext( $values[ 'security_question_a_' . $matches[1] ] )->tag()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -683,6 +691,13 @@ class _register extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
{
|
||||
/* Reset the validation flag and redirect the member to the index page if we have no row */
|
||||
if( \IPS\Member::loggedIn()->members_bitoptions['validating'] )
|
||||
{
|
||||
\IPS\Member::loggedIn()->members_bitoptions['validating'] = FALSE;
|
||||
\IPS\Member::loggedIn()->save();
|
||||
$this->_performRedirect( NULL, FALSE, 'validate_no_record' );
|
||||
}
|
||||
\IPS\Output::i()->error( 'validate_no_record', '2S129/4', 404, '' );
|
||||
}
|
||||
|
||||
@@ -727,10 +742,12 @@ class _register extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
\IPS\Output::i()->error( \IPS\Member::loggedIn()->language()->addToStack('validation_email_rate_limit', FALSE, array( 'sprintf' => array( \IPS\DateTime::ts( $reg['email_sent'] )->relative( \IPS\DateTime::RELATIVE_FORMAT_LOWER ) ) ) ), '1C223/4', 429, '', array( 'Retry-After' => \IPS\DateTime::ts( $reg['email_sent'] )->add( new \DateInterval( 'PT15M' ) )->format('r') ) );
|
||||
}
|
||||
|
||||
/* Rotate security key */
|
||||
$plainSecurityKey = \IPS\Login::generateRandomString();
|
||||
\IPS\Db::i()->update( 'core_validating', [ 'security_key' => Encrypt::fromPlaintext( $plainSecurityKey )->tag(), 'email_sent' => time() ], [ 'vid=?', $reg['vid'] ] );
|
||||
|
||||
\IPS\Email::buildFromTemplate( 'core', $reg['email_chg'] ? 'email_change' : 'registration_validate', array( \IPS\Member::loggedIn(), $reg['vid'] ), \IPS\Email::TYPE_TRANSACTIONAL )->send( \IPS\Member::loggedIn() );
|
||||
|
||||
\IPS\Db::i()->update( 'core_validating', array( 'email_sent' => time() ), array( 'vid=?', $reg['vid'] ) );
|
||||
\IPS\Email::buildFromTemplate( 'core', $reg['email_chg'] ? 'email_change' : 'registration_validate', array( \IPS\Member::loggedIn(), $reg['vid'], $plainSecurityKey ), \IPS\Email::TYPE_TRANSACTIONAL )->send( \IPS\Member::loggedIn() );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=register&do=validating', 'front', 'register' ), 'reg_email_resent' );
|
||||
@@ -758,6 +775,12 @@ class _register extends \IPS\Dispatcher\Controller
|
||||
$this->_performRedirect( NULL, FALSE, 'validate_no_record' );
|
||||
}
|
||||
|
||||
/* Check security key */
|
||||
if( !\IPS\Login::compareHashes( Encrypt::fromTag( $record['security_key'] )->decrypt(), \IPS\Request::i()->security_key ) )
|
||||
{
|
||||
\IPS\Output::i()->error( 'validate_invalid_security_key', '2C223/I', 403, '' );
|
||||
}
|
||||
|
||||
if ( isset( $record['ref'] ) )
|
||||
{
|
||||
\IPS\Request::i()->ref = base64_encode( $record['ref'] );
|
||||
@@ -878,17 +901,21 @@ class _register extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
if( isset( $values['username'] ) )
|
||||
{
|
||||
\IPS\Member::loggedIn()->logHistory( 'core', 'display_name', array( 'new' => $values['username'], 'old' => \IPS\Member::loggedIn()->language()->get('none'), 'by' => 'manual' ) );
|
||||
\IPS\Member::loggedIn()->name = $values['username'];
|
||||
}
|
||||
$spamCode = NULL;
|
||||
$spamAction = NULL;
|
||||
$disposable = FALSE;
|
||||
$geoBlock = FALSE;
|
||||
if( isset( $values['email_address'] ) )
|
||||
{
|
||||
\IPS\Member::loggedIn()->logHistory( 'core', 'email_change', array( 'new' => $values['new_email'], 'old' => \IPS\Member::loggedIn()->language()->get('none'), 'by' => 'manual' ) );
|
||||
\IPS\Member::loggedIn()->email = $values['email_address'];
|
||||
|
||||
if( \IPS\Settings::i()->spam_service_enabled )
|
||||
{
|
||||
$spamAction = \IPS\Member::loggedIn()->spamService( 'register', NULL, $spamCode );
|
||||
$spamAction = \IPS\Member::loggedIn()->spamService( 'register', NULL, $spamCode, $disposable, $geoBlock );
|
||||
if( $spamAction == 4 )
|
||||
{
|
||||
$action = \IPS\Settings::i()->spam_service_action_4;
|
||||
@@ -905,6 +932,12 @@ class _register extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
\IPS\Member::loggedIn()->members_bitoptions['must_reaccept_terms'] = FALSE;
|
||||
\IPS\Member::loggedIn()->allow_admin_mails = $values['reg_admin_mails'];
|
||||
|
||||
/* We should run geolocation again, this may have been an account created via login handler that has since changed details - check for admin validation */
|
||||
if( !$spamAction )
|
||||
{
|
||||
\IPS\Member::loggedIn()->geoSpamCheck( $geoBlock );
|
||||
}
|
||||
|
||||
/* Save */
|
||||
\IPS\Member::loggedIn()->save();
|
||||
@@ -967,10 +1000,8 @@ class _register extends \IPS\Dispatcher\Controller
|
||||
$pending = null;
|
||||
}
|
||||
|
||||
/* If we're pending *admin* validation, don't let them change their email - otherwise doing so would allow them to bypass validation
|
||||
@todo - this is kind of an unsatisfcatory solution as ideally it would put them back into admin approval, but this would require
|
||||
significant reengineering to address - see commit notes on this code block */
|
||||
if ( $pending and $pending['new_reg'] and $pending['user_verified'] )
|
||||
/* If we're a new registration, no longer allow email addresses to be changed. */
|
||||
if ( $pending and $pending['new_reg'] )
|
||||
{
|
||||
\IPS\Output::i()->error( 'no_module_permission', '2C223/6', 403, '' );
|
||||
}
|
||||
@@ -1033,8 +1064,9 @@ class _register extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
|
||||
$vid = \IPS\Login::generateRandomString();
|
||||
$plainSecurityKey = \IPS\Login::generateRandomString();
|
||||
|
||||
\IPS\Db::i()->insert( 'core_validating', array(
|
||||
\IPS\Db::i()->insert( 'core_validating', [
|
||||
'vid' => $vid,
|
||||
'member_id' => \IPS\Member::loggedIn()->member_id,
|
||||
'entry_date' => time(),
|
||||
@@ -1043,9 +1075,10 @@ class _register extends \IPS\Dispatcher\Controller
|
||||
'user_verified' => ( \IPS\Settings::i()->reg_auth_type == 'admin' ) ?: FALSE,
|
||||
'ip_address' => \IPS\Request::i()->ipAddress(),
|
||||
'email_sent' => time(),
|
||||
) );
|
||||
'security_key' => Encrypt::fromPlaintext( $plainSecurityKey )->tag()
|
||||
] );
|
||||
|
||||
\IPS\Email::buildFromTemplate( 'core', $pending['email_chg'] ? 'email_change' : 'registration_validate', array( \IPS\Member::loggedIn(), $vid ), \IPS\Email::TYPE_TRANSACTIONAL )->send( \IPS\Member::loggedIn() );
|
||||
\IPS\Email::buildFromTemplate( 'core', $pending['email_chg'] ? 'email_change' : 'registration_validate', array( \IPS\Member::loggedIn(), $vid, $plainSecurityKey ), \IPS\Email::TYPE_TRANSACTIONAL )->send( \IPS\Member::loggedIn() );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1233,7 +1266,7 @@ class _register extends \IPS\Dispatcher\Controller
|
||||
return $ref;
|
||||
}
|
||||
|
||||
\IPS\Output::i()->redirect( $ref, $message );
|
||||
\IPS\Output::i()->redirect( $ref, $message );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
namespace IPS\core\modules\front\system;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
|
||||
use function mb_strlen;
|
||||
use const LIBXML_NOWARNING;
|
||||
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
@@ -30,7 +34,6 @@ class _serviceworker extends \IPS\Dispatcher\Controller
|
||||
protected function manage()
|
||||
{
|
||||
$cachedUrls = array();
|
||||
$cachedUrls[] = (string) \IPS\Http\Url::internal("app=core&module=system&controller=offline", "front", "user_offline");
|
||||
|
||||
$notificationIcon = NULL;
|
||||
|
||||
@@ -50,31 +53,55 @@ class _serviceworker extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
|
||||
/* VARIABLES TO PASS THROUGH TO JS */
|
||||
$DEBUG = ( ( \IPS\IN_DEV and \IPS\DEV_DEBUG_JS ) or \IPS\DEBUG_JS ) ? 'true' : 'false'; // Weird casting is intentional.
|
||||
$BASE_URL = \IPS\Settings::i()->base_url;
|
||||
$CACHED_ASSETS = json_encode( $cachedUrls, JSON_UNESCAPED_SLASHES );
|
||||
$OFFLINE_URL = (string) \IPS\Http\Url::internal("app=core&module=system&controller=offline", "front", "user_offline");
|
||||
$CACHE_VERSION = \IPS\Theme::i()->cssCacheBustKey();
|
||||
$NOTIFICATION_ICON = $notificationIcon ? "\"{$notificationIcon}\"" : 'null'; // Weird casting is intentional. 'null' will become literal null in JS file.
|
||||
$DEFAULT_NOTIFICATION_TITLE = \IPS\Member::loggedIn()->language()->addToStack('default_notification_title');
|
||||
$DEFAULT_NOTIFICATION_BODY = \IPS\Member::loggedIn()->language()->addToStack('default_notification_body');
|
||||
$variables = [
|
||||
"DEBUG" => boolval( ( \IPS\IN_DEV and \IPS\DEV_DEBUG_JS ) or \IPS\DEBUG_JS ),
|
||||
"BASE_URL" => \IPS\Settings::i()->base_url,
|
||||
"CACHED_ASSETS" => $cachedUrls,
|
||||
"CACHE_NAME" => "invision-community-{$CACHE_VERSION}",
|
||||
"CACHE_VERSION" => $CACHE_VERSION,
|
||||
"NOTIFICATION_ICON" => $notificationIcon ?: null,
|
||||
"DEFAULT_NOTIFICATION_TITLE" => \IPS\Member::loggedIn()->language()->addToStack('default_notification_title'),
|
||||
"DEFAULT_NOTIFICATION_BODY" => \IPS\Member::loggedIn()->language()->addToStack('default_notification_body'),
|
||||
"OFFLINE_PAGE" => $this->buildCollapsedOfflinePage(),
|
||||
];
|
||||
|
||||
$output = <<<JAVASCRIPT
|
||||
"use strict";
|
||||
const DEBUG = {$DEBUG};
|
||||
const BASE_URL = "{$BASE_URL}";
|
||||
const CACHED_ASSETS = {$CACHED_ASSETS};
|
||||
const CACHE_NAME = 'invision-community-{$CACHE_VERSION}';
|
||||
const OFFLINE_URL = "{$OFFLINE_URL}";
|
||||
const NOTIFICATION_ICON = {$NOTIFICATION_ICON};
|
||||
const DEFAULT_NOTIFICATION_TITLE = "{$DEFAULT_NOTIFICATION_TITLE}";
|
||||
const DEFAULT_NOTIFICATION_BODY = "{$DEFAULT_NOTIFICATION_BODY}";
|
||||
$variables["OFFLINE_PAGE_SIZE"] = \strlen( $variables["OFFLINE_PAGE"] );
|
||||
|
||||
$output = "\"use strict;\"\n\n";
|
||||
foreach ( $variables as $var => $value )
|
||||
{
|
||||
$toEncode = json_encode( $value, JSON_UNESCAPED_SLASHES );
|
||||
$output .= <<<JAVASCRIPT
|
||||
const {$var} = {$toEncode};
|
||||
|
||||
JAVASCRIPT;
|
||||
|
||||
}
|
||||
|
||||
\IPS\Member::loggedIn()->language()->parseOutputForDisplay( $output );
|
||||
$output .= file_get_contents( \IPS\ROOT_PATH . '/applications/core/interface/js/serviceWorker.js' );
|
||||
$cacheHeaders = \IPS\IN_DEV !== true ? \IPS\Output::getCacheHeaders(time(), 86400) : array();
|
||||
\IPS\Output::i()->sendOutput($output, 200, 'text/javascript', $cacheHeaders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return template for offline page
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function buildCollapsedOfflinePage() : string
|
||||
{
|
||||
$html = \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->swOffline();
|
||||
\IPS\Member::loggedIn()->language()->parseOutputForDisplay( $html );
|
||||
|
||||
$doc = new \IPS\Xml\DOMDocument( "2.0", "utf-8" );
|
||||
$doc->preserveWhiteSpace = false;
|
||||
$doc->loadHTML( $html, LIBXML_NOBLANKS );
|
||||
$doc->formatOutput = true;
|
||||
$compact = $doc->saveHTML();
|
||||
|
||||
// We don't need these segments of whitespace. HTML entities can be used if it's absolutely necessary
|
||||
return preg_replace( "/\s+/", " ", $compact );
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,9 @@
|
||||
|
||||
namespace IPS\core\modules\front\system;
|
||||
|
||||
use IPS\Member;
|
||||
use IPS\Text\Encrypt;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
@@ -130,8 +133,6 @@ class _settings extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
$canConfigureMfa = TRUE;
|
||||
}
|
||||
|
||||
$canChangeSignature = (bool) \IPS\Member::loggedIn()->canEditSignature();
|
||||
|
||||
/* Add login handlers */
|
||||
$loginMethods = \IPS\Login::methods();
|
||||
@@ -139,7 +140,7 @@ class _settings extends \IPS\Dispatcher\Controller
|
||||
/* Show our own oauth clients? */
|
||||
$showApps = (bool) \IPS\Db::i()->select( 'COUNT(*)', 'core_oauth_clients', array( array( 'oauth_enabled=1 AND oauth_ucp=1' ) ) )->first();
|
||||
/* Return */
|
||||
return \IPS\Theme::i()->getTemplate( 'system' )->settings( $area, $output, ( \IPS\Settings::i()->allow_email_changes != 'disabled' ), $canChangePassword, \IPS\Member::loggedIn()->group['g_dname_changes'], $canChangeSignature, $loginMethods, $canConfigureMfa, $showApps );
|
||||
return \IPS\Theme::i()->getTemplate( 'system' )->settings( $area, $output, ( \IPS\Settings::i()->allow_email_changes != 'disabled' ), $canChangePassword, \IPS\Member::loggedIn()->group['g_dname_changes'], (bool) \IPS\Settings::i()->signatures_enabled, $loginMethods, $canConfigureMfa, $showApps );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -240,7 +241,7 @@ class _settings extends \IPS\Dispatcher\Controller
|
||||
$form = new \IPS\Helpers\Form;
|
||||
$form->class = 'ipsForm_collapseTablet';
|
||||
$currentEmail = htmlspecialchars( \IPS\Member::loggedIn()->email, ENT_DISALLOWED, 'UTF-8', FALSE );
|
||||
$form->addDummy( 'current_email', \IPS\Member::loggedIn()->members_bitoptions["email_messages_bounce"] ? \IPS\Theme::i()->getTemplate( 'global', 'core' )->memberEmailBlockedMessage( $currentEmail ) : $currentEmail );
|
||||
$form->addDummy( 'current_email', $currentEmail );
|
||||
$form->add( new \IPS\Helpers\Form\Email(
|
||||
'new_email',
|
||||
'',
|
||||
@@ -301,8 +302,9 @@ class _settings extends \IPS\Dispatcher\Controller
|
||||
unset( $_SESSION['newEmail'] );
|
||||
|
||||
$vid = \IPS\Login::generateRandomString();
|
||||
$plainSecurityKey = \IPS\Login::generateRandomString();
|
||||
|
||||
\IPS\Db::i()->insert( 'core_validating', array(
|
||||
\IPS\Db::i()->insert( 'core_validating', [
|
||||
'vid' => $vid,
|
||||
'member_id' => \IPS\Member::loggedIn()->member_id,
|
||||
'entry_date' => time(),
|
||||
@@ -310,12 +312,13 @@ class _settings extends \IPS\Dispatcher\Controller
|
||||
'ip_address' => \IPS\Request::i()->ipAddress(),
|
||||
'prev_email' => $oldEmail,
|
||||
'email_sent' => time(),
|
||||
) );
|
||||
'security_key' => Encrypt::fromPlaintext( $plainSecurityKey )->tag()
|
||||
] );
|
||||
|
||||
\IPS\Member::loggedIn()->members_bitoptions['validating'] = TRUE;
|
||||
\IPS\Member::loggedIn()->save();
|
||||
|
||||
\IPS\Email::buildFromTemplate( 'core', 'email_change', array( \IPS\Member::loggedIn(), $vid ), \IPS\Email::TYPE_TRANSACTIONAL )->send( \IPS\Member::loggedIn() );
|
||||
\IPS\Email::buildFromTemplate( 'core', 'email_change', array( \IPS\Member::loggedIn(), $vid, $plainSecurityKey ), \IPS\Email::TYPE_TRANSACTIONAL )->send( \IPS\Member::loggedIn() );
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( '' ) );
|
||||
}
|
||||
@@ -614,7 +617,7 @@ class _settings extends \IPS\Dispatcher\Controller
|
||||
\IPS\Output::i()->bypassCsrfKeyCheck = true;
|
||||
|
||||
/* Validate password */
|
||||
if ( !isset( $_SESSION['passwordValidatedForMfa'] ) )
|
||||
if ( !isset( $_SESSION['passwordForMfa'] ) )
|
||||
{
|
||||
$login = new \IPS\Login( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=mfa', 'front', 'settings_mfa' ), \IPS\Login::LOGIN_REAUTHENTICATE );
|
||||
$usernamePasswordMethods = $login->usernamePasswordMethods();
|
||||
@@ -628,7 +631,7 @@ class _settings extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
if ( $success = $login->authenticate() )
|
||||
{
|
||||
$_SESSION['passwordValidatedForMfa'] = TRUE;
|
||||
$_SESSION['passwordForMfa'] = TRUE;
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=mfa', 'front', 'settings_mfa' ) );
|
||||
}
|
||||
}
|
||||
@@ -661,6 +664,9 @@ class _settings extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
return $output . $mfaOutput;
|
||||
}
|
||||
|
||||
/* We got past the prompts */
|
||||
$_SESSION['passwordValidatedForMfa'] = TRUE;
|
||||
|
||||
/* Do any enabling/disabling */
|
||||
if ( isset( \IPS\Request::i()->act ) )
|
||||
@@ -836,6 +842,13 @@ class _settings extends \IPS\Dispatcher\Controller
|
||||
*/
|
||||
protected function confirmAccountDeletion()
|
||||
{
|
||||
$mfaOutput = \IPS\MFA\MFAHandler::accessToArea( 'core', 'SecurityQuestions', \IPS\Http\Url::internal( 'app=core&module=system&controller=settings&area=confirmAccountDeletion', 'front' )->setQueryString('vid', \IPS\Request::i()->vid ) );
|
||||
if ( $mfaOutput )
|
||||
{
|
||||
\IPS\Output::i()->output = $mfaOutput;
|
||||
return;
|
||||
}
|
||||
|
||||
$key = \IPS\Request::i()->vid;
|
||||
try
|
||||
{
|
||||
@@ -1000,27 +1013,30 @@ class _settings extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Db::i()->update( 'core_validating', array( 'email_sent' => time() ), array( 'vid=?', $vid ) );
|
||||
$plainSecurityKey = \IPS\Login::generateRandomString();
|
||||
\IPS\Db::i()->update( 'core_validating', [ 'email_sent' => time(), 'security_key' => Encrypt::fromPlaintext( $plainSecurityKey )->tag() ], [ 'vid=?', $vid ] );
|
||||
}
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
{
|
||||
$vid = md5( $member->members_pass_hash . \IPS\Login::generateRandomString() );
|
||||
$plainSecurityKey = \IPS\Login::generateRandomString();
|
||||
|
||||
\IPS\Db::i()->insert( 'core_validating', array(
|
||||
\IPS\Db::i()->insert( 'core_validating', [
|
||||
'vid' => $vid,
|
||||
'member_id' => $member->member_id,
|
||||
'entry_date' => time(),
|
||||
'forgot_security' => 1,
|
||||
'ip_address' => \IPS\Request::i()->ipAddress(),
|
||||
'email_sent' => time(),
|
||||
) );
|
||||
'security_key' => Encrypt::fromPlaintext( $plainSecurityKey )->tag()
|
||||
] );
|
||||
}
|
||||
|
||||
/* Send email */
|
||||
if ( $sendEmail )
|
||||
{
|
||||
\IPS\Email::buildFromTemplate( 'core', 'mfaRecovery', array( $member, $vid ), \IPS\Email::TYPE_TRANSACTIONAL )->send( $member );
|
||||
\IPS\Email::buildFromTemplate( 'core', 'mfaRecovery', array( $member, $vid, $plainSecurityKey ), \IPS\Email::TYPE_TRANSACTIONAL )->send( $member );
|
||||
$message = "mfa_recovery_email_sent";
|
||||
}
|
||||
else
|
||||
@@ -1051,6 +1067,12 @@ class _settings extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
\IPS\Output::i()->error( 'mfa_recovery_no_validation_key', '2C122/K', 410, '' );
|
||||
}
|
||||
|
||||
/* Check security key */
|
||||
if( !\IPS\Login::compareHashes( Encrypt::fromTag( $record['security_key'] )->decrypt(), \IPS\Request::i()->security_key ) )
|
||||
{
|
||||
\IPS\Output::i()->error( 'mfavalidate_invalid_security_key', '3C122/16', 403, '' );
|
||||
}
|
||||
|
||||
/* Remove all MFA */
|
||||
$member = \IPS\Member::load( $record['member_id'] );
|
||||
@@ -1226,7 +1248,7 @@ class _settings extends \IPS\Dispatcher\Controller
|
||||
/* Check they have permission to change their signature */
|
||||
$sigLimits = explode( ":", \IPS\Member::loggedIn()->group['g_signature_limits']);
|
||||
|
||||
if( !\IPS\Member::loggedIn()->canEditSignature() )
|
||||
if( (bool) \IPS\Settings::i()->signatures_enabled === FALSE )
|
||||
{
|
||||
\IPS\Output::i()->error( 'signatures_disabled', '2C122/C', 403, '' );
|
||||
}
|
||||
@@ -1268,12 +1290,15 @@ class _settings extends \IPS\Dispatcher\Controller
|
||||
$form = new \IPS\Helpers\Form;
|
||||
$form->class = 'ipsForm_collapseTablet';
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'view_sigs', \IPS\Member::loggedIn()->members_bitoptions['view_sigs'], FALSE ) );
|
||||
$form->add( new \IPS\Helpers\Form\Editor( 'signature', \IPS\Member::loggedIn()->signature, FALSE, array( 'app' => 'core', 'key' => 'Signatures', 'autoSaveKey' => "frontsig-" .\IPS\Member::loggedIn()->member_id, 'attachIds' => array( \IPS\Member::loggedIn()->member_id ) ) ) );
|
||||
|
||||
if( Member::loggedIn()->canEditSignature() )
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\Editor( 'signature', \IPS\Member::loggedIn()->signature, FALSE, ['app' => 'core', 'key' => 'Signatures', 'autoSaveKey' => 'frontsig-'.\IPS\Member::loggedIn()->member_id, 'attachIds' => [\IPS\Member::loggedIn()->member_id]] ) );
|
||||
}
|
||||
|
||||
/* Handle submissions */
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
if( $values['signature'] )
|
||||
if( isset( $values['signature'] ) and $values['signature'] )
|
||||
{
|
||||
/* Check Limits */
|
||||
$signature = new \IPS\Xml\DOMDocument( '1.0', 'UTF-8' );
|
||||
@@ -1357,6 +1382,7 @@ class _settings extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
$errors[] = \IPS\Member::loggedIn()->language()->addToStack('sig_num_lines_exceeded');
|
||||
}
|
||||
\IPS\Member::loggedIn()->signature = $values['signature'];
|
||||
}
|
||||
|
||||
if( !empty( $errors ) )
|
||||
@@ -1367,7 +1393,6 @@ class _settings extends \IPS\Dispatcher\Controller
|
||||
return \IPS\Theme::i()->getTemplate( 'system' )->settingsSignature( $form, $sigLimits );
|
||||
}
|
||||
|
||||
\IPS\Member::loggedIn()->signature = $values['signature'];
|
||||
\IPS\Member::loggedIn()->members_bitoptions['view_sigs'] = $values['view_sigs'];
|
||||
|
||||
\IPS\Member::loggedIn()->save();
|
||||
@@ -1897,7 +1922,7 @@ class _settings extends \IPS\Dispatcher\Controller
|
||||
*/
|
||||
protected function togglePii()
|
||||
{
|
||||
if ( ! \IPS\Settings::i()->core_datalayer_member_pii_choice )
|
||||
if ( ! \IPS\Settings::i()->core_datalayer_member_pii_choice or !isset( $_SESSION['passwordValidatedForMfa'] ) OR \IPS\Settings::i()->pii_type !== 'on' )
|
||||
{
|
||||
\IPS\Output::i()->error( 'page_not_found', '3T251/7', 404 );
|
||||
}
|
||||
@@ -1982,6 +2007,12 @@ class _settings extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
|
||||
if ( $output = \IPS\MFA\MFAHandler::accessToArea( 'core', 'DeviceManagement', $this->url->setQuerystring('do','updateDeviceEmail')->csrf()) )
|
||||
{
|
||||
\IPS\Output::i()->output = $output;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Update the bitwise flag */
|
||||
\IPS\Member::loggedIn()->members_bitoptions['new_device_email'] = (bool) \IPS\Request::i()->value;
|
||||
\IPS\Member::loggedIn()->save();
|
||||
|
||||
@@ -198,6 +198,7 @@ class _warnings extends \IPS\Content\Controller
|
||||
/* Acknowledge it */
|
||||
$warning->acknowledged = TRUE;
|
||||
$warning->save();
|
||||
/* @note SELECT_FROM_WRITE_SERVER: Avoid issue where warning may be in writer table, but not in reader */
|
||||
$member->members_bitoptions['unacknowledged_warnings'] = (bool) \IPS\Db::i()->select( 'COUNT(*)', 'core_members_warn_logs', array( "wl_member=? AND wl_acknowledged=0", $member->member_id ), NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->first();
|
||||
$member->save();
|
||||
|
||||
@@ -369,7 +370,7 @@ class _warnings extends \IPS\Content\Controller
|
||||
|
||||
try
|
||||
{
|
||||
$action = \IPS\Db::i()->select( '*', 'core_members_warn_actions', array( 'wa_points<=?', ( $member->warn_level + \IPS\Request::i()->points ) ), 'wa_points DESC', 1 )->first();
|
||||
$action = \IPS\Db::i()->select( '*', 'core_members_warn_actions', array( 'wa_points<=?', ( $member->warn_level + (int) \IPS\Request::i()->points ) ), 'wa_points DESC', 1 )->first();
|
||||
foreach ( array( 'mq', 'rpa', 'suspend' ) as $k )
|
||||
{
|
||||
if ( $action[ 'wa_' . $k ] == -1 )
|
||||
|
||||
@@ -30,7 +30,7 @@ class _license extends \IPS\Dispatcher\Controller
|
||||
public function manage()
|
||||
{
|
||||
$form = new \IPS\Helpers\Form( 'license', 'continue', \IPS\Http\Url::external( ( \IPS\Request::i()->isSecure() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['SCRIPT_NAME'] . '?controller=license' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'lkey', '16BF-9C65-B3A6-1337', TRUE, array( 'size' => 50 ), function( $val )
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'lkey', NULL, TRUE, array( 'size' => 50 ), function( $val )
|
||||
{
|
||||
\IPS\IPS::checkLicenseKey( $val, ( \IPS\Request::i()->isSecure() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . mb_substr( $_SERVER['SCRIPT_NAME'], 0, -mb_strlen( \IPS\CP_DIRECTORY . '/install/index.php' ) ) );
|
||||
}, NULL, '<a href="' . \IPS\Http\Url::ips( 'docs/find_lkey' ) . '" target="_blank" rel="noopener">' . \IPS\Member::loggedIn()->language()->addToStack('lkey_help') . '</a>' ) );
|
||||
@@ -51,7 +51,7 @@ class _license extends \IPS\Dispatcher\Controller
|
||||
$values['lkey'] = mb_substr( $values['lkey'], 0, -12 );
|
||||
}
|
||||
|
||||
$toWrite = "<?php\n\n" . '$INFO = ' . var_export( array( 'lkey' => 'LICENSE KEY GOES HERE!-123456789' ), TRUE ) . ';';
|
||||
$toWrite = "<?php\n\n" . '$INFO = ' . var_export( array( 'lkey' => $values['lkey'] ), TRUE ) . ';';
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -41,7 +41,7 @@ class _serverdetails extends \IPS\Dispatcher\Controller
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'sql_tbl_prefix', NULL, FALSE, [ 'regex' => '/^([a-z0-9_-]+?)?$/i' ] ) );
|
||||
$form->addHeader( 'http_server' );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'base_url', ( \IPS\Request::i()->isSecure() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . mb_substr( $_SERVER['SCRIPT_NAME'], 0, -mb_strlen( \IPS\CP_DIRECTORY . '/install/index.php' ) ), TRUE, array( 'size' => 50 ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'diagnostics_reporting', FALSE ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'diagnostics_reporting', TRUE ) );
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
|
||||
@@ -43,8 +43,6 @@ class _done extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
|
||||
/* Delete some variables we stored in our session */
|
||||
unset( $_SESSION['apps'] );
|
||||
|
||||
if( isset( $_SESSION['upgrade_options'] ) )
|
||||
{
|
||||
unset( $_SESSION['upgrade_options'] );
|
||||
@@ -74,6 +72,14 @@ class _done extends \IPS\Dispatcher\Controller
|
||||
/* Remove any new version ACP Notifications */
|
||||
\IPS\core\AdminNotification::remove( 'core', 'NewVersion' );
|
||||
|
||||
/* Send v5 notification if the version actually changed */
|
||||
if( !empty( $_SESSION['apps']['core'] ) AND $_SESSION['apps']['core']['current'] !== $_SESSION['apps']['core']['available'] AND !\IPS\IPS::isManaged() )
|
||||
{
|
||||
\IPS\core\AdminNotification::send( 'core', 'VersionFive', NULL, TRUE );
|
||||
}
|
||||
|
||||
unset( $_SESSION['apps'] );
|
||||
|
||||
/* Check for php8 method substitution issues */
|
||||
if ( \IPS\Application\Scanner::scanCustomizationIssues( false, true ) !== null )
|
||||
{
|
||||
|
||||
@@ -30,7 +30,93 @@ class _license extends \IPS\Dispatcher\Controller
|
||||
*/
|
||||
public function manage()
|
||||
{
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "controller=applications" )->setQueryString( 'key', $_SESSION['uniqueKey'] ) );
|
||||
/* Check license key */
|
||||
if( \IPS\Db::i()->checkForTable( 'core_store' ) )
|
||||
{
|
||||
$licenseData = \IPS\IPS::licenseKey( TRUE );
|
||||
}
|
||||
else
|
||||
{
|
||||
$licenseData = NULL;
|
||||
|
||||
try
|
||||
{
|
||||
$license = \IPS\Db::i()->select( '*', 'cache_store', array( 'cs_key=?', 'licenseData' ) )->first();
|
||||
$licenseData = unserialize( $license['cs_value'] );
|
||||
}
|
||||
catch( \Exception $e ){}
|
||||
}
|
||||
|
||||
if( isset( $licenseData['key'] ) AND !isset( $licenseData['expires'] ) )
|
||||
{
|
||||
$licenseData = $this->getLicenseData();
|
||||
}
|
||||
|
||||
if( !$licenseData )
|
||||
{
|
||||
$active = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
$active = ( isset( $licenseData['expires'] ) and $licenseData['expires'] AND strtotime( $licenseData['expires'] ) > time() ) ? TRUE : ( ( isset( $licenseData['active'] ) and $licenseData['active'] ) ? TRUE : NULL );
|
||||
}
|
||||
|
||||
if ( !$active )
|
||||
{
|
||||
$response = NULL;
|
||||
$active = NULL;
|
||||
$form = new \IPS\Helpers\Form( 'licensekey', 'continue' );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'ipb_reg_number', NULL, TRUE, array(), function( $val ){
|
||||
\IPS\IPS::checkLicenseKey( $val, \IPS\Settings::i()->base_url );
|
||||
} ) );
|
||||
|
||||
if( $values = $form->values() )
|
||||
{
|
||||
$values['ipb_reg_number'] = trim( $values['ipb_reg_number'] );
|
||||
|
||||
if ( mb_substr( $values['ipb_reg_number'], -12 ) === '-TESTINSTALL' )
|
||||
{
|
||||
$values['ipb_reg_number'] = mb_substr( $values['ipb_reg_number'], 0, -12 );
|
||||
}
|
||||
|
||||
/* Save */
|
||||
$form->saveAsSettings( $values );
|
||||
|
||||
/* Refresh the locally stored license info */
|
||||
if( \IPS\Db::i()->checkForTable( 'core_store' ) )
|
||||
{
|
||||
unset( \IPS\Data\Store::i()->license_data );
|
||||
$licenseData = \IPS\IPS::licenseKey();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Call the main server */
|
||||
$licenseData = $this->getLicenseData();
|
||||
}
|
||||
|
||||
/* Reset some vars now */
|
||||
$active = ( isset( $licenseData['expires'] ) and $licenseData['expires'] AND strtotime( $licenseData['expires'] ) > time() ) ? TRUE : ( ( isset( $licenseData['active'] ) and $licenseData['active'] ) ? TRUE : FALSE );
|
||||
|
||||
if( $active )
|
||||
{
|
||||
$form = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( $active )
|
||||
{
|
||||
/* Clear any caches or else we might not see new versions on the next screen */
|
||||
if ( isset( \IPS\Data\Store::i()->applications ) )
|
||||
{
|
||||
unset( \IPS\Data\Store::i()->applications );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "controller=applications" )->setQueryString( 'key', $_SESSION['uniqueKey'] ) );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('license');
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->license( $form, $active );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -85,17 +85,13 @@ class _systemcheck extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check we have the latest version and run an md5 check */
|
||||
$incorrectFiles = array();
|
||||
/* Or do not check :) */
|
||||
|
||||
|
||||
/* Display */
|
||||
if ( $canProceed )
|
||||
{
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal("controller=license&key={$_SESSION['uniqueKey']}") );
|
||||
}
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('healthcheck');
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->healthcheck( $requirements, $designersModeEnabled, $incorrectFiles );
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->healthcheck( $requirements, $designersModeEnabled );
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user