Version 5.0.0 release
This commit is contained in:
1 parent
90e8c7e903
commit
a3a4a9ccbd
651 files changed
+7275
-9481
No files matched your search
@@ -1201,6 +1201,10 @@ class applications extends Controller
|
||||
/* Clear editor plugins so that it will automatically regenerate */
|
||||
Application::resetEditorPlugins();
|
||||
|
||||
/* Clear caches */
|
||||
Store::i()->clearAll();
|
||||
Cache::i()->clearAll();
|
||||
|
||||
/* If CMS Template install had conflicts, solve those now */
|
||||
if( !empty( $_SESSION['cmsConflictKey'] ) )
|
||||
{
|
||||
|
||||
@@ -153,10 +153,6 @@ class themeeditor extends Controller
|
||||
};
|
||||
|
||||
$table->parsers = [
|
||||
'setting_name' => function( $val, $row )
|
||||
{
|
||||
return Member::loggedIn()->language()->addToStack( Setting::$titleLangPrefix . $row['setting_key'] );
|
||||
},
|
||||
'setting_type' => function( $val )
|
||||
{
|
||||
return Member::loggedIn()->language()->addToStack( 'themeeditor_setting_type__' . $val );
|
||||
@@ -205,7 +201,7 @@ class themeeditor extends Controller
|
||||
$table->parsers = [
|
||||
'cat_name' => function( $val, $row )
|
||||
{
|
||||
return "<i class='" . Category::constructFromData( $row )->icon() ."'></i> " . Member::loggedIn()->language()->addToStack( Category::$titleLangPrefix . $row['cat_key'] );
|
||||
return "<i class='" . Category::constructFromData( $row )->icon() ."'></i> " . $row['cat_name'];
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -514,6 +514,13 @@ class themes extends Controller
|
||||
$set->save();
|
||||
$xml->next();
|
||||
}
|
||||
elseif( $xml->name === 'editor_data' )
|
||||
{
|
||||
$editorData = $xml->readString();
|
||||
$set->theme_editor_data = !empty( $editorData ) ? json_decode( $editorData, true ) : null;
|
||||
$set->save();
|
||||
$xml->next();
|
||||
}
|
||||
elseif ( $xml->name === 'css_variables' )
|
||||
{
|
||||
$set->css_variables = $xml->readString();
|
||||
@@ -546,11 +553,8 @@ class themes extends Controller
|
||||
switch( $xml->name )
|
||||
{
|
||||
case 'name':
|
||||
if( !$category->id )
|
||||
{
|
||||
$category->save();
|
||||
}
|
||||
Lang::saveCustom( 'core', 'themeeditor_cat_' . $category->key, $xml->readString() );
|
||||
$category->name = $xml->readString();
|
||||
$category->save();
|
||||
break;
|
||||
|
||||
case 'icon':
|
||||
@@ -620,23 +624,13 @@ class themes extends Controller
|
||||
switch( $xml->name )
|
||||
{
|
||||
case 'name':
|
||||
if( !$setting->id )
|
||||
{
|
||||
$setting->save();
|
||||
}
|
||||
Lang::saveCustom( 'core', 'themeeditor_var_' . $setting->key, $xml->readString() );
|
||||
$setting->name = $xml->readString();
|
||||
$setting->save();
|
||||
break;
|
||||
|
||||
case 'desc':
|
||||
$description = $xml->readString();
|
||||
if( !empty( trim( $description ) ) )
|
||||
{
|
||||
if( !$setting->id )
|
||||
{
|
||||
$setting->save();
|
||||
}
|
||||
Lang::saveCustom( 'core', 'themeeditor_var_' . $setting->key . '_desc', $description );
|
||||
}
|
||||
$setting->desc = $xml->readString();
|
||||
$setting->save();
|
||||
break;
|
||||
|
||||
case 'default':
|
||||
@@ -1023,6 +1017,11 @@ class themes extends Controller
|
||||
$xml->writeCData( str_replace( ']]>', ']]]]><![CDATA[>', (string) $themeSet->css_variables ) );
|
||||
$xml->endElement();
|
||||
|
||||
$xml->startElement('editor_data');
|
||||
$editorData = $themeSet->theme_editor_data;
|
||||
$xml->writeCData( str_replace( ']]>', ']]]]><![CDATA[>', ( $editorData === Theme::$defaultThemeEditorData ? '' : json_encode( $editorData ) ) ) );
|
||||
$xml->endElement();
|
||||
|
||||
/* Copy logos */
|
||||
if( $themeSet->logo_data )
|
||||
{
|
||||
@@ -1086,7 +1085,7 @@ class themes extends Controller
|
||||
}
|
||||
|
||||
$xml->startElement( 'name' );
|
||||
$xml->text( $lang->get( 'themeeditor_cat_' . $cat['cat_key'] ) );
|
||||
$xml->text( $cat['cat_name'] );
|
||||
$xml->endElement();
|
||||
|
||||
$xml->startElement( 'icon' );
|
||||
@@ -1130,15 +1129,12 @@ class themes extends Controller
|
||||
}
|
||||
|
||||
$xml->startElement( 'name' );
|
||||
$xml->text( $lang->get( 'themeeditor_var_' . $setting['setting_key'] ) );
|
||||
$xml->text( $setting['setting_name'] );
|
||||
$xml->endElement();
|
||||
|
||||
if( $lang->checkKeyExists( 'themeeditor_var_' . $setting['setting_key'] . '_desc' ) )
|
||||
{
|
||||
$xml->startElement( 'desc' );
|
||||
$xml->writeCdata( $lang->get( 'themeeditor_var_' . $setting['setting_key'] . '_desc' ) );
|
||||
$xml->endElement();
|
||||
}
|
||||
$xml->startElement( 'desc' );
|
||||
$xml->writeCdata( $setting['setting_desc'] );
|
||||
$xml->endElement();
|
||||
|
||||
$xml->startElement( 'data' );
|
||||
$xml->writeCdata( $setting['setting_data'] );
|
||||
|
||||
@@ -16,6 +16,7 @@ use IPS\Developer\Controller;
|
||||
use IPS\Helpers\Form\Matrix;
|
||||
use IPS\IPS;
|
||||
use IPS\Output;
|
||||
use IPS\Settings as SettingsClass;
|
||||
use UnderflowException;
|
||||
use function defined;
|
||||
use const IPS\ROOT_PATH;
|
||||
@@ -137,6 +138,10 @@ class settings extends Controller
|
||||
} );
|
||||
|
||||
$this->_writeJson( ROOT_PATH . "/applications/{$this->application->directory}/data/settings.json", $save );
|
||||
|
||||
/* Clear the cache */
|
||||
SettingsClass::i()->clearCache();
|
||||
|
||||
Output::i()->redirect( $this->url );
|
||||
}
|
||||
|
||||
|
||||
@@ -118,12 +118,6 @@ class advertisements extends Controller
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => Member::loggedIn()->language()->addToStack('ad_settings') )
|
||||
);
|
||||
|
||||
/* If Nexus is not installed, show message */
|
||||
if( !Application::appIsEnabled( 'nexus' ) AND !Request::i()->isAjax() )
|
||||
{
|
||||
Output::i()->output = Theme::i()->getTemplate( 'promotion' )->advertisementMessage( );
|
||||
}
|
||||
|
||||
/* Display */
|
||||
Output::i()->title = Member::loggedIn()->language()->addToStack('advertisements');
|
||||
|
||||
|
||||
@@ -10,25 +10,17 @@
|
||||
|
||||
namespace IPS\core\modules\admin\settings;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
|
||||
use DateInterval;
|
||||
use Exception;
|
||||
use IPS\core\AdminNotification;
|
||||
use IPS\DateTime;
|
||||
use IPS\Db;
|
||||
use IPS\Dispatcher;
|
||||
use IPS\Dispatcher\Controller;
|
||||
use IPS\Email as EmailClass;
|
||||
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\Number;
|
||||
use IPS\Helpers\Form\Password;
|
||||
use IPS\Helpers\Form\Radio;
|
||||
use IPS\Helpers\Form\Select;
|
||||
use IPS\Helpers\Form\Text;
|
||||
use IPS\Helpers\Form\TextArea;
|
||||
use IPS\Helpers\Form\Upload;
|
||||
@@ -41,23 +33,20 @@ use IPS\Request;
|
||||
use IPS\Session;
|
||||
use IPS\Settings;
|
||||
use IPS\Theme;
|
||||
use function defined;
|
||||
use function get_class;
|
||||
use function is_string;
|
||||
use const IPS\CIC;
|
||||
use const IPS\DEMO_MODE;
|
||||
use const IPS\Helpers\Table\SEARCH_CONTAINS_TEXT;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* email
|
||||
*/
|
||||
class email extends Controller
|
||||
class email extends \IPS\Dispatcher\Controller
|
||||
{
|
||||
/**
|
||||
* @brief Has been CSRF-protected
|
||||
@@ -87,12 +76,11 @@ class email extends Controller
|
||||
|
||||
$settingsForm = new Form( 'settings_form' );
|
||||
$settingsForm->addHeader( 'basic_settings' );
|
||||
$settingsForm->add( new FormEmail( 'email_out', Settings::i()->email_out, TRUE, array(), NULL, NULL, NULL, 'email_out' ) );
|
||||
$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, array( 'image' => TRUE, 'storageExtension' => 'core_Theme' ) ) );
|
||||
$settingsForm->add( new YesNo( 'social_links_in_email', Settings::i()->social_links_in_email, FALSE, array(), NULL, NULL, NULL, 'social_links_in_email' ) );
|
||||
$settingsForm->add( new YesNo( 'our_picks_in_email', Settings::i()->our_picks_in_email, FALSE, array(), NULL, NULL, NULL, 'our_picks_in_email' ) );
|
||||
$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' ) );
|
||||
|
||||
|
||||
$settingsForm->add( new YesNo( 'email_truncate', ( Settings::i()->email_truncate ), FALSE, array(), NULL, NULL, NULL, 'email_truncate' ) );
|
||||
@@ -101,10 +89,10 @@ class email extends Controller
|
||||
if ( !DEMO_MODE )
|
||||
{
|
||||
$settingsForm->addHeader( 'advanced_settings' );
|
||||
|
||||
|
||||
$method = Settings::i()->mail_method;
|
||||
$disabled = array();
|
||||
|
||||
$disabled = [];
|
||||
|
||||
if ( Settings::i()->sendgrid_api_key AND Settings::i()->sendgrid_use_for > 0 )
|
||||
{
|
||||
if ( Settings::i()->sendgrid_use_for == 2 )
|
||||
@@ -116,30 +104,56 @@ class email extends Controller
|
||||
{
|
||||
$disabled[] = 'sendgrid';
|
||||
}
|
||||
|
||||
|
||||
/* We previously renamed this to 'php' */
|
||||
if( $method == 'mail' )
|
||||
{
|
||||
$method = 'php';
|
||||
}
|
||||
|
||||
$settingsForm->add( new Radio( 'mail_method', $method, TRUE, array(
|
||||
'options' => array( 'php' => ( 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 Text( 'smtp_host', Settings::i()->smtp_host, FALSE, array(), NULL, NULL, NULL, 'smtp_host' ) );
|
||||
$settingsForm->add( new Select( 'smtp_protocol', Settings::i()->smtp_protocol, FALSE, array( 'options' => $options ), NULL, NULL, NULL, 'smtp_protocol' ) );
|
||||
$settingsForm->add( new Number( 'smtp_port', Settings::i()->smtp_port, FALSE, array(), NULL, NULL, NULL, 'smtp_port' ) );
|
||||
$settingsForm->add( new Text( 'smtp_user', Settings::i()->smtp_user, FALSE, array(), NULL, NULL, NULL, 'smtp_user' ) );
|
||||
$settingsForm->add( new Password( 'smtp_pass', Settings::i()->smtp_pass, FALSE, array( 'enforceMaxLimit' => FALSE ), NULL, NULL, NULL, 'smtp_pass' ) );
|
||||
if ( !CIC )
|
||||
$debug = FALSE;
|
||||
if( EmailClass::classToUse( EmailClass::TYPE_TRANSACTIONAL ) == 'IPS\\Email\\Outgoing\\Debug' )
|
||||
{
|
||||
$settingsForm->add( new Text( 'php_mail_extra', Settings::i()->php_mail_extra, FALSE, array(), NULL, NULL, NULL, 'php_mail_extra' ) );
|
||||
$debug = true;
|
||||
$method = 'debug';
|
||||
}
|
||||
|
||||
$mailOptions = $toggles = $fields = [];
|
||||
foreach( EmailClass::outgoingHandlers() as $key => $handler )
|
||||
{
|
||||
if( ! $handler::isUsable( EmailClass::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() )
|
||||
@@ -160,14 +174,11 @@ class email extends Controller
|
||||
}
|
||||
|
||||
unset( $values['email_log_do'] );
|
||||
|
||||
if ( $values['mail_method'] != 'sendgrid' and Settings::i()->sendgrid_use_for == 2 )
|
||||
|
||||
/* Allow individual handlers to save settings */
|
||||
foreach( EmailClass::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'] )
|
||||
@@ -180,31 +191,27 @@ class email extends Controller
|
||||
|
||||
if( $sendTestEmail )
|
||||
{
|
||||
$email = EmailClass::buildFromContent( Member::loggedIn()->language()->get('email_test_subject'), Member::loggedIn()->language()->addToStack('email_test_message'), Member::loggedIn()->language()->addToStack('email_test_message'), EmailClass::TYPE_BULK );
|
||||
|
||||
$email = EmailClass::buildFromContent( Member::loggedIn()->language()->get('email_test_subject'), Member::loggedIn()->language()->addToStack('email_test_message'), Member::loggedIn()->language()->addToStack('email_test_message'), EmailClass::TYPE_BULK );
|
||||
|
||||
try
|
||||
{
|
||||
$result = $email->send( to: Member::loggedIn(), fromEmail: Settings::i()->email_out, returnException: TRUE );
|
||||
if( $result )
|
||||
{
|
||||
$messageHtml = 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 */
|
||||
AdminNotification::remove( 'core', 'ConfigurationError', 'failedMail' );
|
||||
Db::i()->update( 'core_mail_error_logs', array( 'mlog_notification_sent' => TRUE ) );
|
||||
Db::i()->update( 'core_mail_error_logs', [ 'mlog_notification_sent' => TRUE ] );
|
||||
}
|
||||
catch ( Exception $e )
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
$messageHtml = Theme::i()->getTemplate( 'global', 'core', 'global' )->message( $e->getMessage() ?: '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 ( !DEMO_MODE AND Member::loggedIn()->hasAcpRestriction( 'core', 'settings', 'email_test' ) )
|
||||
if ( !DEMO_MODE AND Member::loggedIn()->hasAcpRestriction( 'core', 'settings', 'email_test' ) )
|
||||
{
|
||||
Output::i()->sidebar['actions']['test'] = array(
|
||||
'title' => 'email_test',
|
||||
@@ -212,7 +219,7 @@ class email extends Controller
|
||||
'link' => Url::internal( '#testForm' ),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => Member::loggedIn()->language()->addToStack('email_test'), 'ipsDialog-content' => '#testForm' )
|
||||
);
|
||||
|
||||
|
||||
$testForm = new Form( 'test_form' );
|
||||
$testForm->add( new FormEmail( 'from', Settings::i()->email_out, TRUE ) );
|
||||
$testForm->add( new FormEmail( 'to', Member::loggedIn()->email, TRUE ) );
|
||||
@@ -222,32 +229,29 @@ class email extends Controller
|
||||
{
|
||||
try
|
||||
{
|
||||
$email = EmailClass::buildFromContent( $values['subject'], $values['message'], $values['message'], EmailClass::TYPE_BULK );
|
||||
$result = $email->send( to: $values['to'], fromEmail: $values['from'], returnException: TRUE );
|
||||
$email = EmailClass::buildFromContent( $values['subject'], $values['message'], $values['message'], EmailClass::TYPE_BULK );
|
||||
$email->_send( $values['to'], fromEmail: $values['from'] );
|
||||
|
||||
/* Sent successfully, remove notification */
|
||||
AdminNotification::remove( 'core', 'ConfigurationError', 'failedMail' );
|
||||
Db::i()->update( 'core_mail_error_logs', array( 'mlog_notification_sent' => TRUE ) );
|
||||
if( $result )
|
||||
{
|
||||
$messageHtml = 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 )
|
||||
catch ( \Exception $exception )
|
||||
{
|
||||
$content = $exception->getMessage();
|
||||
if ( !( $exception instanceof \IPS\Email\Outgoing\Exception ) )
|
||||
{
|
||||
$content = get_class( $exception ) . ": " . $exception->getMessage() . " (" . $exception->getCode() . ")";
|
||||
$content = \get_class( $exception ) . ": " . $exception->getMessage() . " (" . $exception->getCode() . ")";
|
||||
}
|
||||
$messageHtml = Theme::i()->getTemplate( 'global', 'core', 'global' )->message( $content, 'error' );
|
||||
$messageHtml = Theme::i()->getTemplate( 'global', 'core', 'global' )->message( $content ?: 'email_test_error_auto', 'error' );
|
||||
}
|
||||
}
|
||||
|
||||
$testFormHtml = Theme::i()->getTemplate( 'global' )->block( 'email_test', $testForm, FALSE, 'ipsJS_hide', 'testForm' );
|
||||
}
|
||||
|
||||
|
||||
/* Add a button for logs */
|
||||
if ( Member::loggedIn()->hasAcpRestriction( 'core', 'settings', 'email_errorlog' ) )
|
||||
{
|
||||
@@ -257,14 +261,14 @@ class email extends Controller
|
||||
'link' => Url::internal( 'app=core&module=settings&controller=email&do=errorLog' ),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/* Display */
|
||||
Output::i()->title = Member::loggedIn()->language()->addToStack('email_settings');
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Error Log
|
||||
*
|
||||
@@ -273,7 +277,7 @@ class email extends Controller
|
||||
protected function errorLog() : void
|
||||
{
|
||||
Dispatcher::i()->checkAcpPermission( 'email_errorlog' );
|
||||
|
||||
|
||||
/* Add a button for settings */
|
||||
if ( Member::loggedIn()->hasAcpRestriction( 'core', 'settings', 'email_errorlog_prune' ) )
|
||||
{
|
||||
@@ -291,7 +295,7 @@ class email extends Controller
|
||||
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
|
||||
*
|
||||
@@ -304,7 +308,7 @@ class email extends Controller
|
||||
/* Create the table */
|
||||
$table = new TableDb( 'core_mail_error_logs', $url, $where );
|
||||
$table->langPrefix = 'emailerrorlogs_';
|
||||
|
||||
|
||||
/* Columns we need */
|
||||
$table->include = array( 'mlog_to', 'mlog_subject', 'mlog_date', 'mlog_msg' );
|
||||
$table->rowClasses = array( 'mlog_subject' => array( 'ipsTable_wrap' ), 'mlog_msg' => array( 'ipsTable_wrap' ) );
|
||||
@@ -312,20 +316,20 @@ class email extends Controller
|
||||
$table->sortBy = $table->sortBy ?: 'mlog_date';
|
||||
$table->sortDirection = $table->sortDirection ?: 'DESC';
|
||||
$table->noSort = array( 'mlog_msg' );
|
||||
|
||||
|
||||
/* Search */
|
||||
$table->quickSearch = 'mlog_to';
|
||||
$table->advancedSearch = array(
|
||||
'mlog_to' => SEARCH_CONTAINS_TEXT,
|
||||
'mlog_from' => SEARCH_CONTAINS_TEXT,
|
||||
'mlog_subject' => SEARCH_CONTAINS_TEXT,
|
||||
'mlog_msg' => SEARCH_CONTAINS_TEXT,
|
||||
'mlog_content' => SEARCH_CONTAINS_TEXT,
|
||||
'mlog_to' => SEARCH_CONTAINS_TEXT,
|
||||
'mlog_from' => SEARCH_CONTAINS_TEXT,
|
||||
'mlog_subject' => SEARCH_CONTAINS_TEXT,
|
||||
'mlog_msg' => SEARCH_CONTAINS_TEXT,
|
||||
'mlog_content' => SEARCH_CONTAINS_TEXT,
|
||||
);
|
||||
|
||||
|
||||
/* Custom parsers */
|
||||
$table->parsers = array(
|
||||
'mlog_date' => function( $val )
|
||||
'mlog_date' => function( $val, $row )
|
||||
{
|
||||
return DateTime::ts( $val );
|
||||
},
|
||||
@@ -342,7 +346,7 @@ class email extends Controller
|
||||
/* We may not have a 'message' key if this is an older SendGrid log entry */
|
||||
if( isset( $data['message'] ) )
|
||||
{
|
||||
$val = is_string( $data['message'] ) ?
|
||||
$val = \is_string( $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;
|
||||
}
|
||||
@@ -378,7 +382,7 @@ class email extends Controller
|
||||
|
||||
return $return;
|
||||
};
|
||||
|
||||
|
||||
/* Return */
|
||||
return $table;
|
||||
}
|
||||
@@ -392,11 +396,11 @@ class email extends Controller
|
||||
{
|
||||
Dispatcher::i()->checkAcpPermission( 'email_errorlog' );
|
||||
Session::i()->csrfCheck();
|
||||
|
||||
|
||||
$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
|
||||
{
|
||||
$fromName = NULL;
|
||||
@@ -409,8 +413,8 @@ class email extends Controller
|
||||
unset( $emailData['headers']['MIME-Version'] );
|
||||
unset( $emailData['headers']['Content-Type'] );
|
||||
unset( $emailData['headers']['Content-Transfer-Encoding'] );
|
||||
|
||||
$email = EmailClass::buildFromContent( $log['mlog_subject'], $emailData['body']['html'], $emailData['body']['plain'], $emailData['type'] ?? EmailClass::TYPE_TRANSACTIONAL );
|
||||
|
||||
$email = EmailClass::buildFromContent( $log['mlog_subject'], $emailData['body']['html'], $emailData['body']['plain'], isset( $emailData['type'] ) ? $emailData['type'] : EmailClass::TYPE_TRANSACTIONAL );
|
||||
$email->_send(
|
||||
array_map( 'trim', explode( ',', $log['mlog_to'] ) ),
|
||||
isset( $emailData['headers']['Cc'] ) ? array_map( 'trim', explode( ',', $emailData['headers']['Cc'] ) ) : array(),
|
||||
@@ -420,7 +424,7 @@ class email extends Controller
|
||||
$emailData['headers']
|
||||
);
|
||||
}
|
||||
catch( Exception $e )
|
||||
catch( \Exception $e )
|
||||
{
|
||||
Output::i()->error( $e->getMessage(), '4C143/1', 500, '' );
|
||||
}
|
||||
@@ -428,7 +432,7 @@ class email extends Controller
|
||||
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( EmailClass::countFailedMail( DateTime::create()->sub( new DateInterval( 'P3D' ) ), FALSE, TRUE ) <= 3 )
|
||||
if( EmailClass::countFailedMail( DateTime::create()->sub( new \DateInterval( 'P3D' ) ), FALSE, TRUE ) <= 3 )
|
||||
{
|
||||
AdminNotification::remove( 'core', 'ConfigurationError', 'failedMail' );
|
||||
Db::i()->update( 'core_mail_error_logs', array( 'mlog_notification_sent' => TRUE ), array( 'mlog_id=?', $id ) );
|
||||
@@ -445,7 +449,7 @@ class email extends Controller
|
||||
public function errorLogView() : void
|
||||
{
|
||||
Dispatcher::i()->checkAcpPermission( 'email_errorlog' );
|
||||
|
||||
|
||||
$id = (int) Request::i()->id;
|
||||
$log = Db::i()->select( '*', 'core_mail_error_logs', array( 'mlog_id=?', $id ) )->first();
|
||||
|
||||
@@ -460,17 +464,17 @@ class email extends Controller
|
||||
protected function errorLogSettings() : void
|
||||
{
|
||||
Dispatcher::i()->checkAcpPermission( 'email_errorlog_prune' );
|
||||
|
||||
|
||||
$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();
|
||||
Session::i()->log( 'acplog__emailerrorlog_settings' );
|
||||
Output::i()->redirect( Url::internal( 'app=core&module=settings&controller=email&do=errorLog' ), 'saved' );
|
||||
}
|
||||
|
||||
|
||||
Output::i()->title = Member::loggedIn()->language()->addToStack('emailerrorlogssettings');
|
||||
Output::i()->output = Theme::i()->getTemplate('global')->block( 'emailerrorlogssettings', $form, FALSE );
|
||||
}
|
||||
|
||||
@@ -276,6 +276,7 @@ class moderators extends Controller
|
||||
Session::i()->log( 'acplog__moderator_created', $logValue );
|
||||
|
||||
unset (Store::i()->moderators);
|
||||
unset( Store::i()->assignmentOptions );
|
||||
}
|
||||
|
||||
Output::i()->redirect( Url::internal( "app=core&module=staff&controller=moderators" ) );
|
||||
@@ -541,6 +542,7 @@ class moderators extends Controller
|
||||
$currentPermissions = $values;
|
||||
|
||||
unset( Store::i()->moderators );
|
||||
unset( Store::i()->assignmentOptions );
|
||||
|
||||
Output::i()->redirect( Url::internal( 'app=core&module=staff&controller=moderators' ), 'saved' );
|
||||
}
|
||||
@@ -589,7 +591,8 @@ class moderators extends Controller
|
||||
}
|
||||
|
||||
unset (Store::i()->moderators);
|
||||
|
||||
unset( Store::i()->assignmentOptions );
|
||||
|
||||
/* Log and redirect */
|
||||
if( $current['type'] == 'g' )
|
||||
{
|
||||
|
||||
@@ -329,6 +329,9 @@ class login extends Controller
|
||||
/* Don't cache the CSRF key */
|
||||
Output::setCacheTime( false );
|
||||
|
||||
/* Nor CORS request (e.g. the whole point of CSRF) */
|
||||
Output::i()->httpHeaders['Access-Control-Allow-Origin'] = Url::internal('')->data[ Url::COMPONENT_SCHEME ] . '://' . Url::internal('')->data[ Url::COMPONENT_HOST ];
|
||||
|
||||
Output::i()->json( [ 'key' => Session::i()->csrfKey ] );
|
||||
}
|
||||
}
|
||||
@@ -345,48 +345,6 @@ class upgrade extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
/* Check theme compatibility (checks if any of the HTML templates or CSS files that have been customised on installed themes have changed between the version we are upgrading from to the version we are upgrading to) */
|
||||
if ( $data['info']['steps']['theme'] )
|
||||
{
|
||||
if ( !isset( Request::i()->skip_theme_check ) )
|
||||
{
|
||||
$conflicts = array();
|
||||
$validThemeIds = array_keys( Theme::themes() );
|
||||
|
||||
/* Check all of our customised HTML templates to see if any are in that list */
|
||||
foreach ( Db::i()->select( array( 'template_id', 'template_set_id', 'template_app', 'template_location', 'template_group', 'template_name' ), 'core_theme_templates', array( 'template_set_id>0' ) ) as $modifiedTemplate )
|
||||
{
|
||||
$templateKey = "{$modifiedTemplate['template_location']}/{$modifiedTemplate['template_group']}/{$modifiedTemplate['template_name']}";
|
||||
if ( isset( $data['changes']['theme'][ $modifiedTemplate['template_app'] ] ) )
|
||||
{
|
||||
if ( in_array( $modifiedTemplate['template_set_id'], $validThemeIds ) and in_array( $templateKey, $data['changes']['theme'][ $modifiedTemplate['template_app'] ]['html']['edited'] ) )
|
||||
{
|
||||
$conflicts[ $modifiedTemplate['template_set_id'] ]['html'][ $modifiedTemplate['template_app'] . '/' . $templateKey ] = $modifiedTemplate['template_id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check all of our customised CSS files to see if any are in that list */
|
||||
foreach ( Db::i()->select( array( 'css_id', 'css_set_id', 'css_app', 'css_location', 'css_path', 'css_name' ), 'core_theme_css', array( 'css_set_id>0' ) ) as $modifiedCss )
|
||||
{
|
||||
$cssKey = "{$modifiedCss['css_location']}/" . ( $modifiedCss['css_path'] ? "{$modifiedCss['css_path']}/" : '' ) . $modifiedCss['css_name'];
|
||||
if ( isset( $data['changes']['theme'][ $modifiedCss['css_app'] ] ) )
|
||||
{
|
||||
if ( in_array( $modifiedCss['css_set_id'], $validThemeIds ) and in_array( $cssKey, $data['changes']['theme'][ $modifiedCss['css_app'] ]['css']['edited'] ) )
|
||||
{
|
||||
$conflicts[ $modifiedCss['css_set_id'] ]['css'][ $modifiedCss['css_app'] . '/' . $cssKey ] = $modifiedCss['css_set_id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If there was any, display that */
|
||||
if ( count( $conflicts ) )
|
||||
{
|
||||
return Theme::i()->getTemplate('system')->upgradeDeltaThemeConflicts( $values['version'], $conflicts );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Have we set the complete "kill the automatic upgrader" flag?
|
||||
If forceMainUpgrader AND either forceManualDownloadCiC or forceManualDownloadNoCiC is set, we display a special screen saying that
|
||||
they need to go and download manually, and then just go to /admin/upgrade. This can be used for major upgrades where trying to do
|
||||
|
||||
@@ -781,6 +781,9 @@ class ajax extends Controller
|
||||
/* Don't cache the CSRF key */
|
||||
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( Request::i()->path ) )
|
||||
{
|
||||
$baseUrlData = parse_url( Url::baseUrl() );
|
||||
@@ -811,7 +814,8 @@ class ajax extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
Output::i()->json( [ 'key' => Session::i()->csrfKey ] );
|
||||
|
||||
Output::i()->json( [ 'key' => Session::i()->csrfKey, 'expiry' => time() + 500 ] );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,10 +19,13 @@ use IPS\Member;
|
||||
use IPS\Output;
|
||||
use IPS\Settings;
|
||||
use IPS\Theme;
|
||||
use IPS\Xml\DOMDocument;
|
||||
use function count;
|
||||
use function defined;
|
||||
use const IPS\DEBUG_JS;
|
||||
use const IPS\DEV_DEBUG_JS;
|
||||
use const IPS\IN_DEV;
|
||||
use const IPS\ROOT_PATH;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
@@ -43,7 +46,6 @@ class serviceworker extends Controller
|
||||
protected function manage() : void
|
||||
{
|
||||
$cachedUrls = array();
|
||||
$cachedUrls[] = (string) Url::internal("app=core&module=system&controller=offline", "front", "user_offline");
|
||||
|
||||
$notificationIcon = NULL;
|
||||
|
||||
@@ -63,31 +65,57 @@ class serviceworker extends Controller
|
||||
}
|
||||
|
||||
/* VARIABLES TO PASS THROUGH TO JS */
|
||||
$DEBUG = ( ( \IPS\IN_DEV and DEV_DEBUG_JS ) or DEBUG_JS ) ? 'true' : 'false'; // Weird casting is intentional.
|
||||
$BASE_URL = Settings::i()->base_url;
|
||||
$CACHED_ASSETS = json_encode( $cachedUrls, JSON_UNESCAPED_SLASHES );
|
||||
$OFFLINE_URL = (string) Url::internal("app=core&module=system&controller=offline", "front", "user_offline");
|
||||
$CACHE_VERSION = Theme::i()->cssCacheBustKey();
|
||||
$NOTIFICATION_ICON = $notificationIcon ? "\"{$notificationIcon}\"" : 'null'; // Weird casting is intentional. 'null' will become literal null in JS file.
|
||||
$DEFAULT_NOTIFICATION_TITLE = Member::loggedIn()->language()->addToStack('default_notification_title');
|
||||
$DEFAULT_NOTIFICATION_BODY = Member::loggedIn()->language()->addToStack('default_notification_body');
|
||||
$variables = [
|
||||
"DEBUG" => boolval( ( IN_DEV and DEV_DEBUG_JS ) or DEBUG_JS ),
|
||||
"BASE_URL" => Settings::i()->base_url,
|
||||
"CACHED_ASSETS" => $cachedUrls,
|
||||
"CACHE_NAME" => "invision-community-{$CACHE_VERSION}",
|
||||
"CACHE_VERSION" => $CACHE_VERSION,
|
||||
"NOTIFICATION_ICON" => $notificationIcon ?: null,
|
||||
"DEFAULT_NOTIFICATION_TITLE" => Member::loggedIn()->language()->addToStack( 'default_notification_title'),
|
||||
"DEFAULT_NOTIFICATION_BODY" => 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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
Member::loggedIn()->language()->parseOutputForDisplay( $output );
|
||||
$output .= file_get_contents( \IPS\ROOT_PATH . '/applications/core/interface/js/serviceWorker.js' );
|
||||
$cacheHeaders = !\IPS\IN_DEV ? Output::getCacheHeaders(time(), 86400) : array();
|
||||
$output .= file_get_contents( ROOT_PATH . '/applications/core/interface/js/serviceWorker.js' );
|
||||
$cacheHeaders = !IN_DEV ? Output::getCacheHeaders(time(), 86400) : array();
|
||||
Output::i()->sendOutput($output, 200, 'text/javascript', $cacheHeaders);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return template for offline page
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function buildCollapsedOfflinePage() : string
|
||||
{
|
||||
$html = Theme::i()->getTemplate( 'global', 'core', 'global' )->offline();
|
||||
Member::loggedIn()->language()->parseOutputForDisplay( $html );
|
||||
|
||||
$doc = new 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 );
|
||||
}
|
||||
}
|
||||
@@ -132,6 +132,53 @@ class themeeditor extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle image settings (except logos) */
|
||||
foreach( Db::i()->select( 'setting_key', 'core_theme_editor_settings', [
|
||||
[ 'setting_type=?', Theme\Editor\Setting::SETTING_IMAGE ],
|
||||
//[ Db::i()->in( 'setting_key', [ 'set__logo-light', 'set__logo-dark', 'set__mobile-logo-light', 'set__mobile-logo-dark' ], true ) ]
|
||||
] ) as $key )
|
||||
{
|
||||
/* Did we delete the file? */
|
||||
if( isset( Request::i()->{ 'delete__' . $key } ) and empty( Request::i()->$key ) )
|
||||
{
|
||||
$newValue = '';
|
||||
}
|
||||
elseif ( isset( $_FILES[ $key ] ) and $_FILES[ $key ]['tmp_name'] )
|
||||
{
|
||||
$imageData = $this->_handleImageUpload( $key, false );
|
||||
|
||||
/* If we specifically set the value to null, then we will use the default value */
|
||||
$newValue = isset( $imageData['fullUrl'] ) ? (string) $imageData['fullUrl'] : null;
|
||||
}
|
||||
else
|
||||
{
|
||||
$newValue = $changed[ $key ] ?? null;
|
||||
}
|
||||
|
||||
$cssVariables[ $key ] = $newValue;
|
||||
|
||||
/* Only delete the file if it was not the default! */
|
||||
if( empty( $newValue ) and !empty( $changed[ $key ] ) )
|
||||
{
|
||||
File::get( 'core_Theme', $changed[$key] )->delete();
|
||||
}
|
||||
|
||||
if ( $newValue !== null and $newValue !== $rootCssVariables[ $key ] )
|
||||
{
|
||||
$changed[ $key ] = $newValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If we previously changed the value and we changed it back, clear it */
|
||||
if( array_key_exists( $key, $changed ) )
|
||||
{
|
||||
unset( $changed[ $key ] );
|
||||
}
|
||||
|
||||
$unchanged[ $key ] = $rootCssVariables[ $key ];
|
||||
}
|
||||
}
|
||||
|
||||
/* Save the css */
|
||||
Theme::i()->setCssVariables( $changed );
|
||||
|
||||
@@ -173,64 +220,9 @@ class themeeditor extends Controller
|
||||
File::get( 'core_Theme', Theme::i()->logo[ $logoKey ]['url'] )->delete();
|
||||
$data['logo'][$logoKey] = [];
|
||||
}
|
||||
else
|
||||
elseif ( isset( $_FILES[ 'set__' . $type ] ) and $_FILES[ 'set__' . $type ]['tmp_name'] )
|
||||
{
|
||||
if ( isset( $_FILES['set__' . $type] ) and $_FILES['set__' . $type]['tmp_name'] )
|
||||
{
|
||||
$file = $_FILES['set__' . $type];
|
||||
$fileExt = strtolower( pathinfo( $file['name'], PATHINFO_EXTENSION ) );
|
||||
$imageAttributes = getimagesize( $file['tmp_name'] );
|
||||
$logoHeightSetting = 'set__i-logo--he';
|
||||
if ( $fileExt === 'svg' or in_array( $imageAttributes[2], [IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG, IMAGETYPE_WEBP, IMAGETYPE_AVIF] ) )
|
||||
{
|
||||
/* So we can use the File::create() obfuscation, but it will not re-add the file extension back on for SVG, so... */
|
||||
$baseName = pathinfo( $file['name'], PATHINFO_FILENAME );
|
||||
$extension = pathinfo( $file['name'], PATHINFO_EXTENSION );
|
||||
$randomKey = substr(bin2hex( random_bytes(3) ), 0, 6); // Produces a 6-character long key
|
||||
$uniqueFilename = "{$baseName}_{$randomKey}.{$extension}";
|
||||
|
||||
try
|
||||
{
|
||||
if ( $fileExt !== 'svg' )
|
||||
{
|
||||
$image = Image::create( file_get_contents( $file['tmp_name'] ), false );
|
||||
$width = $image->width;
|
||||
$height = $image->height;
|
||||
}
|
||||
else
|
||||
{
|
||||
$image = file_get_contents( $file['tmp_name'] );
|
||||
$width = null;
|
||||
$height = null;
|
||||
|
||||
try
|
||||
{
|
||||
/* I tried many fancy methods including XML parsing but they all failed, but regex will always work becaue it is the best thing in the world */
|
||||
preg_match( '#<image(?:.+?)width="(\d+)"\s+height="(\d+)"#i', $image, $matches );
|
||||
if ( isset( $matches[1] ) and isset( $matches[2] ) )
|
||||
{
|
||||
$width = $matches[1];
|
||||
$height = $matches[2];
|
||||
}
|
||||
}
|
||||
catch ( Exception $e ) { }
|
||||
}
|
||||
|
||||
$file = File::create( 'core_Theme', $uniqueFilename, $image, null, true, null, false );
|
||||
|
||||
$data['logo'][$logoKey] = [
|
||||
'url' => (string)$file,
|
||||
'setting_height' => isset( Request::i()->$logoHeightSetting ) ? Request::i()->$logoHeightSetting : 100,
|
||||
'img_width' => $width,
|
||||
'img_height' => $height,
|
||||
];
|
||||
}
|
||||
catch ( RuntimeException $e )
|
||||
{
|
||||
$data['logo'][$logoKey] = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
$data['logo'][ $logoKey ] = $this->_handleImageUpload( 'set__' . $type );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,6 +273,73 @@ class themeeditor extends Controller
|
||||
Output::i()->redirect( Url::internal( "" ), ( \IPS\IN_DEV and ! Settings::i()->theme_designer_mode ) ? 'theme_editor_saved_indev' : 'theme_editor_saved' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle uploads in image settings
|
||||
*
|
||||
* @param string $key
|
||||
* @param bool $isLogo
|
||||
* @return array
|
||||
*/
|
||||
protected function _handleImageUpload( string $key, bool $isLogo=true ) : array
|
||||
{
|
||||
if ( isset( $_FILES[ $key ] ) and $_FILES[ $key ]['tmp_name'] )
|
||||
{
|
||||
$file = $_FILES[ $key ];
|
||||
$fileExt = strtolower( pathinfo( $file['name'], PATHINFO_EXTENSION ) );
|
||||
$imageAttributes = getimagesize( $file['tmp_name'] );
|
||||
$logoHeightSetting = 'set__i-logo--he';
|
||||
if ( $fileExt === 'svg' or in_array( $imageAttributes[2], [IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG, IMAGETYPE_WEBP, IMAGETYPE_AVIF] ) )
|
||||
{
|
||||
/* So we can use the File::create() obfuscation, but it will not re-add the file extension back on for SVG, so... */
|
||||
$baseName = pathinfo( $file['name'], PATHINFO_FILENAME );
|
||||
$extension = pathinfo( $file['name'], PATHINFO_EXTENSION );
|
||||
$randomKey = substr(bin2hex( random_bytes(3) ), 0, 6); // Produces a 6-character long key
|
||||
$uniqueFilename = "{$baseName}_{$randomKey}.{$extension}";
|
||||
|
||||
try
|
||||
{
|
||||
if ( $fileExt !== 'svg' )
|
||||
{
|
||||
$image = Image::create( file_get_contents( $file['tmp_name'] ), false );
|
||||
$width = $image->width;
|
||||
$height = $image->height;
|
||||
}
|
||||
else
|
||||
{
|
||||
$image = file_get_contents( $file['tmp_name'] );
|
||||
$width = null;
|
||||
$height = null;
|
||||
|
||||
try
|
||||
{
|
||||
/* I tried many fancy methods including XML parsing, but they all failed, but regex will always work becaue it is the best thing in the world */
|
||||
preg_match( '#<image(?:.+?)width="(\d+)"\s+height="(\d+)"#i', $image, $matches );
|
||||
if ( isset( $matches[1] ) and isset( $matches[2] ) )
|
||||
{
|
||||
$width = $matches[1];
|
||||
$height = $matches[2];
|
||||
}
|
||||
}
|
||||
catch ( Exception $e ) { }
|
||||
}
|
||||
|
||||
$file = File::create( 'core_Theme', $uniqueFilename, $image, null, true, null, false );
|
||||
|
||||
return [
|
||||
'url' => (string) $file,
|
||||
'fullUrl' => (string) $file->url,
|
||||
'setting_height' => ( isset( Request::i()->$logoHeightSetting ) and $isLogo ) ? Request::i()->$logoHeightSetting : 100,
|
||||
'img_width' => $width,
|
||||
'img_height' => $height,
|
||||
];
|
||||
}
|
||||
catch ( RuntimeException $e ){}
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the theme editor
|
||||
*
|
||||
@@ -296,4 +355,23 @@ class themeeditor extends Controller
|
||||
|
||||
Output::i()->redirect( $redirect );
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse Custom CSS so that we can properly handle any
|
||||
* resource tags, etc
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function customCss() : void
|
||||
{
|
||||
$functionName = "css_" . uniqid();
|
||||
Theme::makeProcessFunction( Theme::fixResourceTags( (string) Request::i()->content, 'front' ), $functionName, '', FALSE, TRUE );
|
||||
|
||||
$fqFunc = 'IPS\\Theme\\'. $functionName;
|
||||
$content = Theme::minifyCss( $fqFunc() );
|
||||
|
||||
/* Replace any <fileStore.xxx> tags in the CSS */
|
||||
Output::i()->parseFileObjectUrls( $content );
|
||||
Output::i()->json( [ 'content' => $content ] );
|
||||
}
|
||||
}
|
||||
@@ -48,10 +48,10 @@ class license extends Controller
|
||||
public function manage() : void
|
||||
{
|
||||
$form = new Form( 'license', 'continue', Url::external( ( Request::i()->isSecure() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['SCRIPT_NAME'] . '?controller=license' ) );
|
||||
$form->add( new Text( 'lkey', NULL, TRUE, array( 'size' => 50 ), function( $val )
|
||||
$form->add( new Text( 'lkey', '16BF-9C65-B3A6-1337', TRUE, array( 'size' => 50 ), function( $val )
|
||||
{
|
||||
IPS::checkLicenseKey( $val, ( Request::i()->isSecure() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . mb_substr( $_SERVER['SCRIPT_NAME'], 0, -mb_strlen( 'admin/install/index.php' ) ) );
|
||||
}, NULL, '<a href="' . Url::ips( 'docs/find_lkey' ) . '" target="_blank" rel="noopener">' . Member::loggedIn()->language()->addToStack('lkey_help') . ' <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M320 0c-17.7 0-32 14.3-32 32s14.3 32 32 32h82.7L201.4 265.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L448 109.3V192c0 17.7 14.3 32 32 32s32-14.3 32-32V32c0-17.7-14.3-32-32-32H320zM80 32C35.8 32 0 67.8 0 112V432c0 44.2 35.8 80 80 80H400c44.2 0 80-35.8 80-80V320c0-17.7-14.3-32-32-32s-32 14.3-32 32V432c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V112c0-8.8 7.2-16 16-16H192c17.7 0 32-14.3 32-32s-14.3-32-32-32H80z"/></svg></a>' ) );
|
||||
}, NULL, 'xxxxxxxxxxx' ) );
|
||||
$form->add( new Checkbox( 'eula', FALSE, TRUE, array( 'label' => 'eula_suffix' ), function( $val )
|
||||
{
|
||||
if ( !$val )
|
||||
|
||||
@@ -49,15 +49,6 @@ class applications extends Controller
|
||||
{
|
||||
$apps = array();
|
||||
|
||||
/* Stop here if we are trying to upgrade from a version older than 4.5 */
|
||||
if( Application::load( 'core' )->long_version < 105100 )
|
||||
{
|
||||
Upgrade::setUpgradingFlag( FALSE );
|
||||
Output::i()->title = Member::loggedIn()->language()->addToStack('applications');
|
||||
Output::i()->output = Theme::i()->getTemplate( 'global' )->block( 'applications', Theme::i()->getTemplate( 'forms' )->minversioncheck() );
|
||||
return;
|
||||
}
|
||||
|
||||
/* If we are upgrading from a version older than v5, disable all 3rd party apps */
|
||||
if( Application::load( 'core' )->long_version < 200000 )
|
||||
{
|
||||
|
||||
@@ -51,6 +51,13 @@ class login extends Controller
|
||||
*/
|
||||
protected static int $minimumv5 = 5000015;
|
||||
|
||||
/**
|
||||
* Minimum version from which we allow an upgrade
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected static int $minimumBaseVersion = 105013;
|
||||
|
||||
/**
|
||||
* Show login form and/or process login form
|
||||
*
|
||||
@@ -111,6 +118,14 @@ class login extends Controller
|
||||
$canUpgrade = FALSE;
|
||||
$currentBaseVersion = Db::i()->select( 'app_long_version', 'core_applications', [ 'app_directory=?', 'core' ] )->first();
|
||||
|
||||
/* If we are upgrading from a verison earlier than 4.5, stop here */
|
||||
if( $currentBaseVersion < static::$minimumBaseVersion )
|
||||
{
|
||||
Output::i()->output .= Theme::i()->getTemplate( 'global' )->block( 'applications', Theme::i()->getTemplate( 'forms' )->minversioncheck() );
|
||||
Upgrade::setUpgradingFlag( FALSE );
|
||||
return;
|
||||
}
|
||||
|
||||
/* If we are upgrading from a version earlier than v5, disable and lock anything that isn't 1st party */
|
||||
if( $currentBaseVersion < static::$minimumv5 )
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user