Version 5.0.6

This commit is contained in:
Neo committed 2025-12-19 18:55:49 -08:00
1 parent a3a4a9ccbd
commit b89858b2ff
1214 files changed
+92828 -14859

No files matched your search

@@ -328,16 +328,19 @@ class badges extends Controller
public function badgePreview() : void
{
header('content-type: image/svg+xml');
echo new CustomBadge(
Request::i()->shape ?: 'circle',
Request::i()->foreground ?: '#fff',
Request::i()->background ?: '#eeb95f',
Request::i()->border ?: '#f7d36f',
Request::i()->icon ?: null,
(int) Request::i()->rotation ?: 0,
(int) Request::i()->sides ?: 5,
(int) Request::i()->numberoverlay ?: 0
);
$badge = new CustomBadge;
$badge->shape = Request::i()->shape ?: 'circle';
$badge->foreground = Request::i()->foreground ?: '#fff';
$badge->background = Request::i()->background ?: '#eeb95f';
$badge->border = Request::i()->border ?: '#f7d36f';
$badge->icon = Request::i()->icon ? json_decode( Request::i()->icon, true ) : null;
$badge->rotation = Request::i()->rotation ?: 0;
$badge->sides = Request::i()->sides ?: 5;
$badge->number_overlay = Request::i()->numberoverlay ?: 0;
$badge->icon_size = Request::i()->iconsize ?: 3;
$badge->generateSVG( true );
echo $badge->raw;
exit();
}
}
@@ -128,7 +128,7 @@ class adverts extends Controller
else
{
Output::i()->title = Member::loggedIn()->language()->addToStack('core_adverts_stats');
Output::i()->output = Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $activeTab, (string) $chart, Url::internal( "app=core&module=activitystats&controller=adverts" ), 'tab', '', 'i-padding_3' );
Output::i()->output = Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $activeTab, (string) $chart, Url::internal( "app=core&module=activitystats&controller=adverts" ), 'tab', '', '' );
}
}
@@ -78,7 +78,7 @@ class emailstats extends Controller
else
{
Output::i()->title = Member::loggedIn()->language()->addToStack('menu__core_activitystats_emailstats');
Output::i()->output = Theme::i()->getTemplate( 'global', 'core' )->tabs( $this->_getAvailableTabs(), $activeTab, (string) $chart, Url::internal( "app=core&module=activitystats&controller=emailstats" ), 'tab', '', 'i-padding_3' );
Output::i()->output = Theme::i()->getTemplate( 'global', 'core' )->tabs( $this->_getAvailableTabs(), $activeTab, (string) $chart, Url::internal( "app=core&module=activitystats&controller=emailstats" ), 'tab', '', '' );
}
}
@@ -175,7 +175,7 @@ class keywords extends Controller
else
{
Output::i()->title = Member::loggedIn()->language()->addToStack('menu__core_activitystats_keywords');
Output::i()->output = Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $activeTab, (string) $output, Url::internal( "app=core&module=activitystats&controller=keywords" ), 'tab', '', 'i-padding_3' );
Output::i()->output = Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $activeTab, (string) $output, Url::internal( "app=core&module=activitystats&controller=keywords" ), 'tab', '', '' );
}
}
@@ -80,7 +80,7 @@ class reactions extends Controller
else
{
Output::i()->title = Member::loggedIn()->language()->addToStack('menu__core_activitystats_reactions');
Output::i()->output = Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $activeTab, (string) $chart, Url::internal( "app=core&module=activitystats&controller=reactions" ), 'tab', '', 'i-padding_3' );
Output::i()->output = Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $activeTab, (string) $chart, Url::internal( "app=core&module=activitystats&controller=reactions" ), 'tab', '', '' );
}
}
}
@@ -23,6 +23,7 @@ use IPS\Theme;
use function defined;
use function file_exists;
use function strpos;
use const IPS\CIC;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
@@ -49,63 +50,64 @@ class api extends Controller
public function __call( string $method, mixed $args )
{
Output::i()->responsive = FALSE;
/* Check htaccess is correct */
if ( Settings::i()->use_friendly_urls and Settings::i()->htaccess_mod_rewrite OR Request::i()->fromZapier )
{
$url = Url::external( rtrim( Settings::i()->base_url, '/' ) . '/api/core/hello' );
}
else
{
$url = Url::external( rtrim( Settings::i()->base_url, '/' ) . '/api/index.php?/core/hello' );
}
try
{
if ( Request::i()->isCgi() )
{
$response = $url->setQueryString( 'key', 'test' )->request()->get();
}
else
{
$response = $url->request()->login( 'test', '' )->get();
}
$response = $response->decodeJson();
if( isset( $response['errorMessage'] ) )
/* We don't need to test Cloud */
if( !CIC )
{
/* Check htaccess is correct */
if( Settings::i()->use_friendly_urls and Settings::i()->htaccess_mod_rewrite or Request::i()->fromZapier )
{
if ( $response['errorMessage'] === 'IP_ADDRESS_BANNED' )
$url = Url::external( rtrim( Settings::i()->base_url, '/' ).'/api/core/hello' );
}else
{
$url = Url::external( rtrim( Settings::i()->base_url, '/' ).'/api/index.php?/core/hello' );
}
try
{
if( Request::i()->isCgi() )
{
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' )
{
Output::i()->error( Member::loggedIn()->language()->addToStack( 'api_blocked_file', FALSE, array( 'sprintf' => array( \IPS\ROOT_PATH ) ) ), '3C402/2', 500, '' );
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' ) )
{
Output::i()->error( Member::loggedIn()->language()->addToStack( 'api_blocked_file', FALSE, ['sprintf' => [\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;
Output::i()->title = Member::loggedIn()->language()->addToStack( 'menu__core_applications_api' );
Output::i()->output = Theme::i()->getTemplate( 'api' )->htaccess( isset( Request::i()->recheck ), $url, $e->getMessage() );
return;
}
}
catch ( Exception $e )
{
Output::i()->title = Member::loggedIn()->language()->addToStack('menu__core_applications_api');
Output::i()->output = Theme::i()->getTemplate( 'api' )->htaccess( isset( Request::i()->recheck ), $url, $e->getMessage() );
return;
}
/* Work out tabs */
$tabs = array();
if ( Member::loggedIn()->hasAcpRestriction( 'core', 'applications', 'oauth_manage' ) )
File diff suppressed because it is too large. Load diff
@@ -33,6 +33,7 @@ use IPS\Output;
use IPS\Request;
use IPS\Session;
use IPS\Theme;
use OutOfRangeException;
use UnderflowException;
use function defined;
use function explode;
@@ -158,21 +159,12 @@ class menu extends Controller
*/
protected function _getClassFromRow( array $item ) : ?FrontNavigationAbstract
{
if ( $item['app'] == 'core' and $item['extension'] == 'Menu' )
{
$class = 'IPS\\' . $item['app'] . '\extensions\core\FrontNavigation\\' . $item['extension'];
if ( class_exists( $class ) )
{
return new $class( json_decode( $item['config'], TRUE ), $item['id'], $item['permissions'], $item['menu_types'], json_decode( (string) $item['icon'], TRUE ) );
}
}
$class = 'IPS\\' . $item['app'] . '\extensions\core\FrontNavigation\\' . $item['extension'];
if ( class_exists( $class ) )
try
{
$class = Application::getExtensionClass( $item['app'], 'FrontNavigation', $item['extension'] );
return new $class( json_decode( $item['config'], TRUE ), $item['id'], $item['permissions'], $item['menu_types'], json_decode( (string) $item['icon'], TRUE ) );
}
catch( OutOfRangeException ){}
return null;
}
@@ -224,7 +216,7 @@ class menu extends Controller
'title' => 'delete',
'link' => Url::internal( "app=core&module=applications&controller=menu&do=remove&id={$item['id']}" )->csrf(),
'icon' => 'times-circle',
'data' => ['delete' => ''],
'data' => ['confirm' => ''],
];
$icon = null;
@@ -426,7 +418,7 @@ class menu extends Controller
$exploded = explode( '_', $values['menu_manager_extension'] );
/* @var FrontNavigationAbstract $class */
$class = 'IPS\\' . $exploded[0] . '\extensions\core\FrontNavigation\\' . $exploded[1];
$class = Application::getExtensionClass( $exploded[0], 'FrontNavigation', $exploded[1] );
$config = array();
if ( isset( $fieldNames[ $values['menu_manager_extension'] ] ) )
{
@@ -405,9 +405,6 @@ class themes extends Controller
/* Save snapshot */
Theme::load( Request::i()->id )->saveHistorySnapshot();
/* Wipe clean conflicts */
Db::i()->delete( 'core_theme_conflict', array( 'conflict_set_id=?', Request::i()->id ) );
/* No Name? Then this is a brand-new theme */
if ( empty( $set->name ) )
{
@@ -470,378 +467,363 @@ class themes extends Controller
$xml->read();
while ( $xml->read() )
{
if ( $xml->name === 'header' )
/* If this is a closing tag, OR if the tag is empty, move on */
if( $xml->nodeType == $xml::END_ELEMENT or empty( $xml->readInnerXml() ) )
{
$set->custom_header = $xml->readString();
$set->save();
$xml->next();
continue;
}
elseif ( $xml->name === 'footer' )
$name = $xml->name;
switch( $name )
{
$set->custom_footer = $xml->readString();
$set->save();
$xml->next();
}
elseif ( $xml->name === 'css' )
{
$set->custom_css = $xml->readString();
$set->save();
$xml->next();
}
elseif ( $xml->name === 'layout' )
{
$set->view_options = $xml->readString();
$set->save();
$xml->next();
}
elseif ( $xml->name === 'core_js' )
{
$set->core_js = $xml->readString();
$set->save();
$xml->next();
}
elseif ( $xml->name === 'core_css' )
{
$set->core_css = $xml->readString();
case 'header':
$set->custom_header = $xml->readString();
break;
/* Rebuild any designer core data */
$content = Theme::minifyCss( $set->core_css );
/* Replace any <fileStore.xxx> tags in the CSS */
Output::i()->parseFileObjectUrls( $content );
case 'footer':
$set->custom_footer = $xml->readString();
break;
$set->core_css_filename = File::create( 'core_Theme', 'theme.css', $content );
case 'css':
$set->custom_css = $xml->readString();
break;
$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();
$set->save();
$xml->next();
}
elseif( $xml->name === 'editor_category' )
{
$key = $xml->getAttribute( 'key' );
try
{
$category = Category::load( $key, 'cat_key' );
}
catch( OutOfRangeException )
{
$category = new Category;
$category->key = $key;
$category->set_id = $set->id;
case 'layout':
$set->view_options = $xml->readString();
break;
/* Set the position first, in case there is no parent specified */
$position = (int) Db::i()->select( 'max(cat_position)', 'core_theme_editor_categories', array( 'cat_parent=?', 0 ) )->first();
$category->position = $position + 1;
}
case 'core_js':
$set->core_js = $xml->readString();
break;
$category->app = $xml->getAttribute( 'app' );
case 'core_css':
$set->core_css = $xml->readString();
$xml->read();
while( $xml->read() )
{
switch( $xml->name )
{
case 'name':
$category->name = $xml->readString();
$category->save();
break;
/* Rebuild any designer core data */
$content = Theme::minifyCss( $set->core_css );
/* Replace any <fileStore.xxx> tags in the CSS */
Output::i()->parseFileObjectUrls( $content );
case 'icon':
$category->icon = json_encode( Category::buildIconData( $xml->readString() ) );
$category->save();
break;
$set->core_css_filename = File::create( 'core_Theme', 'theme.css', $content );
break;
case 'parent':
try
{
$parentId = Category::load( $xml->readString(), 'cat_key' )->id;
case 'editor_data':
$editorData = $xml->readString();
$set->theme_editor_data = !empty( $editorData ) ? json_decode( $editorData, true ) : null;
break;
/* Reset the position if we changed the category */
if( $parentId != $category->parent_id )
{
$position = (int) Db::i()->select( 'max(cat_position)', 'core_theme_editor_categories', array( 'cat_parent=?', $category->parent_id ) )->first();
$category->position = $position + 1;
}
case 'css_variables':
$set->css_variables = $xml->readString();
break;
$category->parent_id = $parentId;
$category->save();
}
catch( OutOfRangeException ){}
break;
}
if( $xml->nodeType == $xml::END_ELEMENT )
{
break;
}
$xml->next();
}
}
elseif( $xml->name === 'editor_setting' )
{
$key = $xml->getAttribute( 'key' );
try
{
$setting = Setting::load( $key, 'setting_key' );
}
catch( OutOfRangeException )
{
$setting = new Setting;
$setting->key = $key;
$setting->set_id = $set->id;
}
$setting->app = $xml->getAttribute( 'app' );
$setting->type = $xml->getAttribute( 'type' );
$setting->category_id = Category::load( $xml->getAttribute( 'category' ), 'cat_key' )->id;
if( !$setting->position )
{
$position = (int) Db::i()->select( 'max(setting_position)', 'core_theme_editor_settings', array( 'setting_category_id=?', $setting->category_id ) )->first();
$setting->position = $position + 1;
}
if( $refresh = $xml->getAttribute( 'refresh' ) )
{
$setting->refresh = true;
}
$xml->read();
while( $xml->read() )
{
switch( $xml->name )
{
case 'name':
$setting->name = $xml->readString();
$setting->save();
break;
case 'desc':
$setting->desc = $xml->readString();
$setting->save();
break;
case 'default':
case 'data':
$field = $xml->name;
$value = trim( $xml->readString() );
$setting->$field = $value ?: null;
$setting->save();
break;
}
if( $xml->nodeType == $xml::END_ELEMENT )
{
break;
}
$xml->next();
}
}
elseif ( $xml->name === 'app' )
{
$appKey = $xml->getAttribute('key');
if ( !array_key_exists( $appKey, $data['apps'] ) )
{
case 'editor_category':
$key = $xml->getAttribute( 'key' );
try
{
try
$row = Db::i()->select( '*', 'core_theme_editor_categories', [ 'cat_key=? and cat_set_id=?', $key, $set->id ] )->first();
$category = Category::constructFromData( $row );
}
catch( UnderflowException )
{
$category = new Category;
$category->key = $key;
$category->set_id = $set->id;
/* Set the position first, in case there is no parent specified */
$position = (int) Db::i()->select( 'max(cat_position)', 'core_theme_editor_categories', array( 'cat_parent=?', 0 ) )->first();
$category->position = $position + 1;
}
$category->app = $xml->getAttribute( 'app' );
$xml->read();
while( $xml->read() )
{
if( $xml->nodeType == $xml::END_ELEMENT )
{
$application = Application::load( $appKey );
}
/* Application is out of date */
catch( UnexpectedValueException $e )
{
$xml->next();
continue;
break;
}
if ( $application->enabled )
switch( $xml->name )
{
/* Import */
$xml->read();
while ( $xml->read() )
{
switch ( $xml->name )
case 'name':
$category->name = $xml->readString();
break;
case 'icon':
$category->icon = json_encode( Category::buildIconData( $xml->readString() ) );
break;
case 'parent':
try
{
case 'css':
$location = $xml->getAttribute('css_location');
$path = $xml->getAttribute('css_path');
$name = $xml->getAttribute('css_name');
$parent = Db::i()->select( '*', 'core_theme_editor_categories', [ 'cat_key=? and (cat_set_id=? or cat_set_id=?)', $xml->readString(), $set->id, 0 ], 'cat_set_id desc' )->first();
$parentId = $parent['cat_id'];
/* Keep this */
$_SESSION['theme_import']['css'][] = Db::i()->replace( 'core_theme_css', array(
'css_set_id' => Request::i()->id,
'css_app' => $appKey,
'css_location' => $xml->getAttribute('css_location'),
'css_path' => $xml->getAttribute('css_path'),
'css_name' => $xml->getAttribute('css_name'),
'css_attributes' => $xml->getAttribute('css_attributes'),
'css_content' => $xml->readString(),
'css_version' => $version,
'css_user_edited'=> 0
), true );
/* Reset the position if we changed the category */
if( $parentId != $category->parent )
{
$position = (int) Db::i()->select( 'max(cat_position)', 'core_theme_editor_categories', array( 'cat_parent=?', $category->parent ) )->first();
$category->position = $position + 1;
}
$category->parent = $parentId;
}
catch( UnderflowException ){}
break;
}
$category->save();
$xml->next();
}
break;
case 'editor_setting':
$key = $xml->getAttribute( 'key' );
try
{
$row = Db::i()->select( '*', 'core_theme_editor_settings', [ 'setting_key=? and setting_set_id=?', $key, $set->id ] )->first();
$setting = Setting::constructFromData( $row );
}
catch( UnderflowException )
{
$setting = new Setting;
$setting->key = $key;
$setting->set_id = $set->id;
}
$setting->app = $xml->getAttribute( 'app' );
$setting->type = $xml->getAttribute( 'type' );
/* Figure out the category - it will either be custom to this theme, or it will be in the master */
try
{
$category = Db::i()->select( '*', 'core_theme_editor_categories', [ 'cat_key=? and (cat_set_id=? or cat_set_id=?)', $xml->getAttribute( 'category' ), $set->id, 0 ], 'cat_set_id desc' )->first();
$setting->category_id = $category['cat_id'];
}
catch( UnderflowException )
{
/* This should never happen, but we'll do a fail-safe here so we don't break everything */
$setting->category_id = 0;
}
if( !$setting->position )
{
$position = (int) Db::i()->select( 'max(setting_position)', 'core_theme_editor_settings', array( 'setting_category_id=?', $setting->category_id ) )->first();
$setting->position = $position + 1;
}
$setting->refresh = (bool) $xml->getAttribute( 'refresh' );
$xml->read();
while( $xml->read() )
{
if( $xml->nodeType == $xml::END_ELEMENT )
{
break;
}
switch( $xml->name )
{
case 'name':
$setting->name = $xml->readString();
break;
case 'desc':
$setting->desc = $xml->readString();
break;
case 'default':
case 'data':
$field = $xml->name;
$value = trim( $xml->readString() );
$setting->$field = $value ?: null;
break;
}
$setting->save();
$xml->next();
}
break;
case 'app':
$appKey = $xml->getAttribute('key');
if( Application::appIsEnabled( $appKey ) )
{
/* Import */
$xml->read();
while ( $xml->read() )
{
if( $xml->nodeType == $xml::END_ELEMENT )
{
break;
}
switch ( $xml->name )
{
case 'css':
$location = $xml->getAttribute('css_location');
$path = $xml->getAttribute('css_path');
$name = $xml->getAttribute('css_name');
/* Keep this */
$_SESSION['theme_import']['css'][] = Db::i()->replace( 'core_theme_css', array(
'css_set_id' => Request::i()->id,
'css_app' => $appKey,
'css_location' => $xml->getAttribute('css_location'),
'css_path' => $xml->getAttribute('css_path'),
'css_name' => $xml->getAttribute('css_name'),
'css_attributes' => $xml->getAttribute('css_attributes'),
'css_content' => $xml->readString(),
'css_version' => $version,
'css_user_edited'=> 0
), true );
break;
case 'resource':
/* Theme resources should be raw binary data everywhere (filesystem and DB) except in the theme XML download where they are base64 encoded. */
$content = base64_decode( $xml->readString() );
$name = Theme::makeBuiltTemplateLookupHash( $appKey, $xml->getAttribute('location'), $xml->getAttribute('path') ) . '_' . $xml->getAttribute('name');
$fileName = (string) File::create( 'core_Theme', $name, $content, 'set_resources_' . Request::i()->id, TRUE, NULL, FALSE );
try
case 'resource':
/* Theme resources should be raw binary data everywhere (filesystem and DB) except in the theme XML download where they are base64 encoded. */
$content = base64_decode( $xml->readString() );
$name = Theme::makeBuiltTemplateLookupHash( $appKey, $xml->getAttribute('location'), $xml->getAttribute('path') ) . '_' . $xml->getAttribute('name');
$fileName = (string) File::create( 'core_Theme', $name, $content, 'set_resources_' . Request::i()->id, TRUE, NULL, FALSE );
try
{
$existingImage = Db::i()->select( '*', 'core_theme_resources', array(
'resource_set_id=? AND resource_app=? AND resource_location=? AND resource_path=? AND resource_name=?',
Request::i()->id, $appKey, $xml->getAttribute('location'), $xml->getAttribute('path'), $xml->getAttribute('name')
) )->first();
if ( $existingImage['resource_filename'] )
{
$existingImage = Db::i()->select( '*', 'core_theme_resources', array(
'resource_set_id=? AND resource_app=? AND resource_location=? AND resource_path=? AND resource_name=?',
Request::i()->id, $appKey, $xml->getAttribute('location'), $xml->getAttribute('path'), $xml->getAttribute('name')
) )->first();
if ( $existingImage['resource_filename'] )
try
{
try
{
File::get( 'core_Theme', $existingImage['resource_filename'] )->delete();
}
catch( Exception $e ) { }
File::get( 'core_Theme', $existingImage['resource_filename'] )->delete();
}
Db::i()->delete( 'core_theme_resources', array( 'resource_id=?', $existingImage['resource_id'] ) );
catch( Exception $e ) { }
}
catch( UnderflowException $e ) { }
Db::i()->replace( 'core_theme_resources', array(
'resource_set_id' => Request::i()->id,
'resource_app' => $appKey,
'resource_location' => $xml->getAttribute('location'),
'resource_path' => $xml->getAttribute('path'),
'resource_name' => $xml->getAttribute('name'),
'resource_data' => $content,
'resource_added' => time(),
'resource_filename' => $fileName,
'resource_user_edited' => intval( $xml->getAttribute('user_edited') )
) );
break;
}
if( $xml->nodeType == $xml::END_ELEMENT )
{
Db::i()->delete( 'core_theme_resources', array( 'resource_id=?', $existingImage['resource_id'] ) );
}
catch( UnderflowException $e ) { }
Db::i()->replace( 'core_theme_resources', array(
'resource_set_id' => Request::i()->id,
'resource_app' => $appKey,
'resource_location' => $xml->getAttribute('location'),
'resource_path' => $xml->getAttribute('path'),
'resource_name' => $xml->getAttribute('name'),
'resource_data' => $content,
'resource_added' => time(),
'resource_filename' => $fileName,
'resource_user_edited' => intval( $xml->getAttribute('user_edited') )
) );
break;
}
$xml->next();
}
$xml->next();
}
/* Update set so far so that mappings are saved */
$set->saveSet();
}
catch ( OutOfRangeException $e ) { }
/* Update set so far so that mappings are saved */
$set->saveSet();
/* Done */
$data['apps'][ $appKey ] = TRUE;
return array( $data, Member::loggedIn()->language()->addToStack('processing') );
}
else
{
$xml->next();
}
}
elseif ( $xml->name === 'language' )
{
$xml->read();
while ( $xml->read() )
{
if ( $xml->name == 'word' )
break;
case 'language':
$xml->read();
while ( $xml->read() )
{
$languageIds = $languageIds ?? array_keys( Lang::languages() );
foreach ( $languageIds as $langId )
if ( $xml->name == 'word' )
{
$default = $xml->readString();
$exists = Db::i()->select( 'COUNT(*)', 'core_sys_lang_words', array( array( 'lang_id=? and word_key=? and word_theme=?', $langId, $xml->getAttribute('key'), Request::i()->id ) ) )->first();
$languageIds = $languageIds ?? array_keys( Lang::languages() );
foreach ( $languageIds as $langId )
{
$default = $xml->readString();
$exists = Db::i()->select( 'COUNT(*)', 'core_sys_lang_words', array( array( 'lang_id=? and word_key=? and word_theme=?', $langId, $xml->getAttribute('key'), Request::i()->id ) ) )->first();
if ( $exists )
{
Db::i()->update( 'core_sys_lang_words', array(
'word_default' => $default,
'word_default_version' => $version,
), array( array( 'lang_id=? and word_key=? and word_theme=?', $langId, $xml->getAttribute('key'), Request::i()->id ) ) );
}
else
{
Db::i()->insert( 'core_sys_lang_words', array(
'lang_id' => $langId,
'word_app' => NULL,
'word_plugin' => NULL,
'word_theme' => Request::i()->id,
'word_key' => $xml->getAttribute('key'),
'word_default' => $default,
'word_custom' => NULL,
'word_default_version' => $version,
'word_custom_version' => NULL,
'word_js' => FALSE,
'word_export' => TRUE
) );
if ( $exists )
{
Db::i()->update( 'core_sys_lang_words', array(
'word_default' => $default,
'word_default_version' => $version,
), array( array( 'lang_id=? and word_key=? and word_theme=?', $langId, $xml->getAttribute('key'), Request::i()->id ) ) );
}
else
{
Db::i()->insert( 'core_sys_lang_words', array(
'lang_id' => $langId,
'word_app' => NULL,
'word_plugin' => NULL,
'word_theme' => Request::i()->id,
'word_key' => $xml->getAttribute('key'),
'word_default' => $default,
'word_custom' => NULL,
'word_default_version' => $version,
'word_custom_version' => NULL,
'word_js' => FALSE,
'word_export' => TRUE
) );
}
}
}
$xml->next();
}
}
}
elseif ( $xml->name === 'templates' )
{
$xml->read();
while ( $xml->read() )
{
if ( $xml->name == 'template' )
{
Db::i()->delete( 'core_theme_templates_custom', [ 'template_set_id=? and template_name=? and template_hookpoint=? and template_hookpoint_type=?', Request::i()->id, $xml->getAttribute('name'), $xml->getAttribute('hookpoint'), $xml->getAttribute('hookpoint_type') ] );
Db::i()->insert( 'core_theme_templates_custom', [
'template_set_id' => Request::i()->id,
'template_name' => $xml->getAttribute('name'),
'template_hookpoint' => $xml->getAttribute('hookpoint'),
'template_hookpoint_type' => $xml->getAttribute('hookpoint_type'),
'template_key' => $xml->getAttribute('key'),
'template_version' => $xml->getAttribute('version'),
'template_content' => $xml->readString(),
'template_updated' => time(),
] );
if( $xml->nodeType == $xml::END_ELEMENT )
{
break;
}
$xml->next();
}
}
}
elseif( $xml->name === 'logo' )
{
$type = $xml->getAttribute('type');
$name = $xml->getAttribute('name');
break;
/* Sharer logo and favicons may be present from themes generated in 4.3 and earlier, but are no longer used */
if( $type != 'sharer' AND $type != 'favicon' )
{
if( $set->logo[ $type ]['url'] !== null )
case 'templates':
$xml->read();
while ( $xml->read() )
{
File::get( 'core_Theme', $set->logo[ $type ]['url'] )->delete();
if ( $xml->name == 'template' )
{
Db::i()->delete( 'core_theme_templates_custom', [ 'template_set_id=? and template_name=? and template_hookpoint=? and template_hookpoint_type=?', Request::i()->id, $xml->getAttribute('name'), $xml->getAttribute('hookpoint'), $xml->getAttribute('hookpoint_type') ] );
Db::i()->insert( 'core_theme_templates_custom', [
'template_set_id' => Request::i()->id,
'template_name' => $xml->getAttribute('name'),
'template_hookpoint' => $xml->getAttribute('hookpoint'),
'template_hookpoint_type' => $xml->getAttribute('hookpoint_type'),
'template_key' => $xml->getAttribute('key'),
'template_version' => $xml->getAttribute('version'),
'template_content' => $xml->readString(),
'template_updated' => time(),
] );
if( $xml->nodeType == $xml::END_ELEMENT )
{
break;
}
$xml->next();
}
}
break;
$url = (string) File::create( 'core_Theme', $name, base64_decode( $xml->readString() ) );
case 'logo':
$type = $xml->getAttribute('type');
$name = $xml->getAttribute('name');
$set->saveSet( array( 'logo' => array( $type => array( 'url' => $url, 'height' => $xml->getAttribute('height') ?? 100 ) ) ) );
}
/* Sharer logo and favicons may be present from themes generated in 4.3 and earlier, but are no longer used */
if( $type != 'sharer' AND $type != 'favicon' )
{
if( isset( $set->logo[ $type ]['url'] ) )
{
File::get( 'core_Theme', $set->logo[ $type ]['url'] )->delete();
}
$xml->next();
$url = (string) File::create( 'core_Theme', $name, base64_decode( $xml->readString() ) );
$set->saveSet( array( 'logo' => array( $type => array( 'url' => $url, 'height' => $xml->getAttribute('height') ?? 100 ) ) ) );
}
break;
}
$set->save();
$xml->next();
}
return null;
},
@@ -861,6 +843,7 @@ class themes extends Controller
Store::i()->delete( 'core_theme_import_' . md5_file( Request::i()->file ) );
unset( Store::i()->themes );
unset( Store::i()->custom_templates_rows );
@unlink( Request::i()->file );
@@ -869,16 +852,8 @@ class themes extends Controller
{
$app->installThemeEditorSettings();
}
/* Conflicts to fix? */
if ( Db::i()->select( 'count(*)', 'core_theme_conflict', array( 'conflict_set_id=?', Request::i()->id ) )->first() )
{
Output::i()->redirect( Url::internal( 'app=core&module=customization&controller=themes&do=conflicts&id=' . Request::i()->id ) );
}
else
{
Output::i()->redirect( Url::internal( 'app=core&module=customization&controller=themes' ) );
}
Output::i()->redirect( Url::internal( 'app=core&module=customization&controller=themes' ) );
}
);
}
@@ -1040,9 +1015,12 @@ class themes extends Controller
$xml->text( $file );
$xml->endAttribute();
$xml->startAttribute('height');
$xml->text( $data['height'] );
$xml->endAttribute();
if( isset( $data['height'] ) )
{
$xml->startAttribute('height');
$xml->text( $data['height'] );
$xml->endAttribute();
}
/* Get image data */
$original = File::get( 'core_Theme', $data['url'] );
@@ -1137,7 +1115,7 @@ class themes extends Controller
$xml->endElement();
$xml->startElement( 'data' );
$xml->writeCdata( $setting['setting_data'] );
$xml->writeCdata( $setting['setting_data'] ?? '[]' );
$xml->endElement();
$xml->startElement( 'default' );
@@ -1327,8 +1305,9 @@ class themes extends Controller
$xml->endDocument();
Session::i()->log( 'acplog__theme_exported', array( "core_theme_set_title_{$themeSet->_id}" => TRUE ) );
Output::i()->sendOutput( $xml->outputMemory(), 200, 'application/xml', array( 'Content-Disposition' => Output::getContentDisposition( 'attachment', Member::loggedIn()->language()->get('core_theme_set_title_' . $themeSet->_id ) . " {$version}.xml" ), FALSE, FALSE ) );
$filename = Member::loggedIn()->language()->get('core_theme_set_title_' . $themeSet->_id ) . ( $version ? " {$version}" : "" ) . ".xml";
Output::i()->sendOutput( $xml->outputMemory(), 200, 'application/xml', array( 'Content-Disposition' => Output::getContentDisposition( 'attachment', $filename ), FALSE, FALSE ) );
}
/* Display */
@@ -1491,15 +1470,7 @@ class themes extends Controller
catch( Exception ) { }
}
$functionName = "css_" . uniqid();
Theme::makeProcessFunction( Theme::fixResourceTags( $theme->core_css, 'front' ), $functionName, '', FALSE, TRUE );
$fqFunc = 'IPS\\Theme\\'. $functionName;
$content = Theme::minifyCss( $fqFunc() );
/* Replace any <fileStore.xxx> tags in the CSS */
Output::i()->parseFileObjectUrls( $content );
$theme->core_css_filename = File::create( 'core_Theme', 'theme.css', $content );
$theme->core_css_filename = File::create( 'core_Theme', 'theme.css', Theme::compileCustomCss( $theme->core_css ) );
$theme->save();
if ( Request::i()->isAjax() )
@@ -11,28 +11,28 @@
namespace IPS\core\modules\admin\discovery;
use IPS\Content;
use InvalidArgumentException;
use IPS\Content\Tag;
use IPS\Content\Taggable;
use IPS\Db;
use IPS\Dispatcher;
use IPS\Helpers\Form;
use IPS\Helpers\Form\Number;
use IPS\Helpers\Form\Stack;
use IPS\Helpers\Form\Text;
use IPS\Helpers\Form\YesNo;
use IPS\Http\Url;
use IPS\IPS;
use IPS\Member;
use IPS\Node\Controller;
use IPS\Node\Model;
use IPS\Output;
use IPS\Patterns\ActiveRecordIterator;
use IPS\Request;
use IPS\Session;
use IPS\Settings;
use IPS\Task;
use IPS\Theme;
use OutOfRangeException;
use function count;
use function defined;
use function is_array;
use function in_array;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
@@ -108,6 +108,27 @@ class tags extends Controller
'title' => 'tag_settings'
];
Output::i()->sidebar['actions']['tools'] = [
'icon' => 'cog',
'title' => 'tag_tools',
'menu' => [
'merge' => [
'icon' => 'level-up',
'title' => 'merge_tag_tool',
'link' => $this->url->setQueryString( 'do', 'merge' ),
'data' => [ 'ipsDialog' => '', 'ipsDialog-title' => Member::loggedIn()->language()->addToStack( 'merge_tag_tool') ]
],
'delete' => [
'icon' => 'trash',
'title' => 'tags_mass_delete',
'link' => $this->url->setQueryString( 'do', 'massDelete' ),
'data' => [ 'ipsDialog' => '', 'ipsDialog-title' => Member::loggedIn()->language()->addToStack( 'tags_mass_delete') ]
]
]
];
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'admin_core.js', 'core', 'admin' ) );
parent::manage();
}
@@ -197,4 +218,127 @@ class tags extends Controller
Output::i()->breadcrumb[] = [ null, Member::loggedIn()->language()->addToStack( 'tag_settings' ) ];
Output::i()->output = (string) $form;
}
/**
* @return void
*/
protected function merge() : void
{
/* Skip any tags that are being merged */
$source = [];
foreach( new ActiveRecordIterator(
Db::i()->select( '*', 'core_tags_data', null, 'tag_text' ),
Tag::class
) as $tag )
{
if( !$tag->mergeInProgress() )
{
$source[] = $tag->text;
}
}
$form = new Form( 'form', 'continue' );
$form->add( new Text( 'tag_merge_from', null, true, [
'autocomplete' => array(
'unique' => TRUE,
'source' => $source,
'resultItemTemplate' => 'core.autocomplete.tagsResultItem',
'freeChoice' => false,
'minimized' => false,
'alphabetical' => true,
'addTokenText' => Member::loggedIn()->language()->get( 'tags_optional' ),
)
] ) );
$form->add( new Text( 'tag_merge_to', null, true, [
'autocomplete' => array(
'unique' => TRUE,
'source' => $source,
'resultItemTemplate' => 'core.autocomplete.tagsResultItem',
'freeChoice' => false,
'minimized' => false,
'alphabetical' => true,
'maxItems' => 1,
'addTokenText' => Member::loggedIn()->language()->get( 'tags_optional' ),
)
], function( $val ){
if( $val )
{
if( in_array( $val, explode( "\n", Request::i()->tag_merge_from ) ) )
{
throw new InvalidArgumentException( 'err__bad_tag_merge' );
}
}
} ) );
if( $values = $form->values() )
{
foreach( $values['tag_merge_from'] as $tag )
{
$tag = Tag::load( $tag, 'tag_text' );
/* Yes, this is technically used for totals, but we're wiping this anyway,
so just put a placeholder here */
$data = $tag->totals;
$data['merge'] = true;
$tag->totals = $data;
$tag->save();
/* Queue the background task to update the tags */
Task::queue( 'core', 'UpdateTaggedItems', [ 'tag' => $tag->text, 'new' => $values['tag_merge_to'], 'merge' => true ], 5, [ 'tag' ] );
}
Output::i()->redirect( $this->url, Member::loggedIn()->language()->addToStack( 'tag_merge_requested' ) );
}
Output::i()->output = (string) $form;
}
/**
* Bulk delete tool
*
* @return void
*/
protected function massDelete() : void
{
/* Skip any tags that are being merged */
$source = [];
foreach( new ActiveRecordIterator(
Db::i()->select( '*', 'core_tags_data', null, 'tag_text' ),
Tag::class
) as $tag )
{
if( !$tag->mergeInProgress() )
{
$source[] = $tag->text;
}
}
$form = new Form( 'form', 'submit' );
$form->attributes['data-controller'] = 'core.admin.core.tags';
$form->add( new Text( 'tag_to_delete', null, true, [
'autocomplete' => array(
'unique' => TRUE,
'source' => $source,
'resultItemTemplate' => 'core.autocomplete.tagsResultItem',
'freeChoice' => false,
'minimized' => false,
'alphabetical' => true,
'addTokenText' => Member::loggedIn()->language()->get( 'tags_optional' ),
)
] ) );
if( $values = $form->values() )
{
foreach( $values['tag_to_delete'] as $tag )
{
Tag::load( $tag, 'tag_text' )->delete();
}
Output::i()->redirect( $this->url );
}
Output::i()->output = (string) $form;
}
}
@@ -762,7 +762,6 @@ class languages extends Controller
$form = new Form( 'form', 'langauge_export_button' );
// This appears inside a dialog. The following instructs the dialog to handle the download via JS rather than using a native form download. This allows the dialog to close when done.
$filename = $lang->title . " {$lang->version}.xml";
$form->attributes['data-form-is-download'] = '';
$form->add( new Text( 'language_export_author_name', $lang->author_name, false ) );
@@ -885,6 +884,7 @@ class languages extends Controller
/* Finish */
$xml->endDocument();
$filename = $lang->title . " {$lang->version}.xml";
Output::i()->sendOutput(
$xml->outputMemory(),
200,
@@ -624,6 +624,15 @@ class members extends Controller
);
}
}
/* Extension */
foreach( Application::allExtensions( 'core', 'UserMenu' ) as $ext )
{
foreach( $ext->acpRowButtons( $member ) as $key => $element )
{
$return[ $key ] = $element;
}
}
return $return;
};
@@ -1319,15 +1328,15 @@ class members extends Controller
if ( isset( Request::i()->blockKey ) )
{
$exploded = explode( '_', Request::i()->blockKey );
$class = 'IPS\\' . $exploded[0] . '\\extensions\\core\\MemberACPProfileBlocks\\' . $exploded[1];
if ( class_exists( $class ) )
try
{
$class = Application::getExtensionClass( $exploded[0], 'MemberACPProfileBlocks', $exploded[1] );
$block = new $class( $member );
Output::i()->output = $block->tabOutput( Request::i()->block[ Request::i()->blockKey ] );
}
else
catch( OutOfRangeException )
{
Output::i()->output = $class;
Output::i()->output = '';
}
}
else
@@ -239,9 +239,9 @@ class profilesettings extends Controller
if( !Settings::i()->prune_member_history AND $values['prune_member_history'] )
{
Task::queue( 'core', 'PruneLargeTable', array(
'table' => 'core_member_history',
'where' => array( 'log_date < ?', DateTime::create()->sub( new DateInterval( 'P' . $values['prune_member_history'] . 'D' ) )->getTimestamp() ),
'setting' => 'prune_member_history',
'table' => 'core_member_history',
'where' => array('log_date < ? and log_app != ?', DateTime::create()->sub( new \DateInterval( 'P' .$values[ 'prune_member_history'] . 'D' ) )->getTimestamp(), 'nexus' ),
'setting' => 'prune_member_history',
), 4 );
}
@@ -29,6 +29,7 @@ use IPS\Helpers\Form\Upload;
use IPS\Helpers\Form\YesNo;
use IPS\Helpers\Table\Db as TableDb;
use IPS\Http\Url;
use IPS\Image;
use IPS\Lang;
use IPS\Member;
use IPS\Member\Group;
@@ -144,7 +145,8 @@ class reputation extends Controller
{
Dispatcher::i()->checkAcpPermission( 'reactions_manage' );
parent::manage();
$activeTabContents = Theme::i()->getTemplate( 'global' )->paddedBlock( Output::i()->output );
$activeTabContents = Output::i()->output;
if ( Bridge::i()->liveTopicsUnconverted() )
{
$blurbLangKey = 'reactions__locked__livetopics';
@@ -337,6 +339,9 @@ class reputation extends Controller
*/
public function levelForm() : void
{
/* Allow SVGs without the obscure hash removing the file extension */
File::$safeFileExtensions[] = 'svg';
$current = NULL;
if ( Request::i()->id )
{
@@ -346,13 +351,13 @@ class reputation extends Controller
$form = new Form();
$form->add( new Translatable( 'rep_level_title', NULL, TRUE, array( 'app' => 'core', 'key' => ( $current ? "core_reputation_level_{$current['level_id']}" : NULL ) ) ) );
$form->add( new Upload( 'rep_level_image', ( $current and $current['level_image'] ) ? File::get( 'core_Theme', $current['level_image'] ) : NULL, FALSE, array( 'storageExtension' => 'core_Theme' ) ) );
$form->add( new Upload( 'rep_level_image', ( $current and $current['level_image'] ) ? File::get( 'core_Theme', $current['level_image'] ) : NULL, FALSE, array( 'storageExtension' => 'core_Theme', 'allowedFileTypes' => array_merge( Image::supportedExtensions(), ['svg'] ), 'checkImage' => TRUE, 'obscure' => true ) ) );
$form->add( new Number( 'rep_level_points', $current ? $current['level_points'] : 0, TRUE, array( 'min' => NULL ) ) );
if ( $values = $form->values() )
{
$save = array(
'level_image' => $values['rep_level_image'] ? str_replace( \IPS\ROOT_PATH . '/uploads/', '', $values['rep_level_image'] ) : '',
'level_image' => ( $values['rep_level_image'] instanceof File ? (string) $values['rep_level_image'] : '' ),
'level_points' => $values['rep_level_points'],
);
@@ -93,14 +93,14 @@ class spam extends Controller
/* Work out output */
$methodFunction = '_manage' . IPS::mb_ucfirst( $this->activeTab );
$activeTabContents = $this->$methodFunction();
/* If this is an AJAX request, just return it */
if( Request::i()->isAjax() )
{
Output::i()->output = $activeTabContents;
return;
}
/* Build tab list */
$tabs = array();
$tabs['captcha'] = 'spamprevention_captcha';
@@ -136,7 +136,7 @@ class spam extends Controller
'link' => Url::internal( 'app=core&module=moderation&controller=spam&do=whitelist' ),
);
}
if ( Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'membertools_delete' ) )
{
Output::i()->sidebar['actions']['delete_guest_content'] = array(
@@ -146,7 +146,7 @@ class spam extends Controller
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => Member::loggedIn()->language()->addToStack('member_delete_guest_content') )
);
}
/* Display */
Output::i()->title = Member::loggedIn()->language()->addToStack('menu__core_moderation_spam');
Output::i()->output = Theme::i()->getTemplate( 'global' )->tabs( $tabs, $this->activeTab, $activeTabContents, Url::internal( "app=core&module=moderation&controller=spam" ) );
@@ -220,7 +220,7 @@ class spam extends Controller
$form->add( new CheckboxSet( 'spm_option', explode( ',', Settings::i()->spm_option ), FALSE, array(
'options' => array( 'disable' => 'spm_option_disable', 'unapprove' => 'spm_option_unapprove', 'delete' => 'spm_option_delete', 'ban' => 'spm_option_ban' ),
) ) );
/* Save values */
if ( $form->values() )
{
@@ -239,7 +239,7 @@ class spam extends Controller
protected function _manageService() : string
{
$licenseData = IPS::licenseKey();
/* Build Form */
$actions = array( 1 => 'spam_service_act_1', 5 => 'spam_service_act_5', 2 => 'spam_service_act_2', 3 => 'spam_service_act_3', 4 => 'spam_service_act_4' );
$days = json_decode( Settings::i()->spam_service_days, TRUE );
@@ -253,14 +253,12 @@ class spam extends Controller
$disabled = TRUE;
if( !Settings::i()->ipb_reg_number )
{
Member::loggedIn()->language()->words['spam_service_enabled_desc'] = Member::loggedIn()->language()->addToStack( 'spam_service_nokey', FALSE, array( 'sprintf' => array( Url::internal( 'app=core&module=settings&controller=licensekey' ) ) ) );
}
else
{
Member::loggedIn()->language()->words['spam_service_enabled_desc'] = Member::loggedIn()->language()->addToStack( 'spam_service_noservice' );
}
}
$form->add( new YesNo( 'spam_service_enabled', 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 YesNo( 'spam_service_send_to_ips', Settings::i()->spam_service_send_to_ips, FALSE, array( 'disabled' => $disabled ), NULL, NULL, NULL, 'spam_service_send_to_ips' ) );
$form->add( new Select( 'spam_service_action_1', 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' ) );
@@ -289,7 +287,7 @@ class spam extends Controller
unset( $values['spam_service_' . $num . '_num' ] );
}
$values['spam_service_days'] = json_encode( $values['spam_service_days'] );
$form->saveAsSettings( $values );
@@ -324,7 +322,7 @@ class spam extends Controller
$table->sortBy = $table->sortBy ?: 'qa_question';
$table->quickSearch = array( 'word_custom', 'qa_question' );
$table->sortDirection = $table->sortDirection ?: 'asc';
if ( Member::loggedIn()->hasAcpRestriction( 'core', 'moderation', 'qanda_add' ) )
{
$table->rootButtons = array(
@@ -339,7 +337,7 @@ class spam extends Controller
$table->rowButtons = function( $row )
{
$return = array();
if ( Member::loggedIn()->hasAcpRestriction( 'core', 'moderation', 'qanda_edit' ) )
{
$return['edit'] = array(
@@ -348,7 +346,7 @@ class spam extends Controller
'link' => Url::internal( 'app=core&module=moderation&controller=spam&do=question&id=' ) . $row['qa_id'],
);
}
if ( Member::loggedIn()->hasAcpRestriction( 'core', 'moderation', 'qanda_delete' ) )
{
$return['delete'] = array(
@@ -358,7 +356,7 @@ class spam extends Controller
'data' => array( 'delete' => '' ),
);
}
return $return;
};
@@ -402,7 +400,7 @@ class spam extends Controller
$save = array(
'qa_answers' => json_encode( $values['qa_answers'] ),
);
if ( $id )
{
Db::i()->update( 'core_question_and_answer', $save, array( 'qa_id=?', $question['qa_id'] ) );
@@ -414,7 +412,7 @@ class spam extends Controller
$id = Db::i()->insert( 'core_question_and_answer', $save );
Session::i()->log( 'acplogs__question_added' );
}
Lang::saveCustom( 'core', "core_question_and_answer_{$id}", $values['qa_question'] );
Output::i()->redirect( Url::internal( 'app=core&module=moderation&controller=spam&tab=qanda' ), 'saved' );
@@ -441,13 +439,13 @@ class spam extends Controller
Db::i()->delete( 'core_question_and_answer', array( 'qa_id=?', $id ) );
Session::i()->log( 'acplogs__question_deleted' );
Lang::deleteCustom( 'core', "core_question_and_answer_{$id}" );
/* And redirect */
Output::i()->redirect( Url::internal( "app=core&module=moderation&controller=spam&tab=qanda" ) );
}
/**
* Spam Service Log
*
@@ -456,18 +454,18 @@ class spam extends Controller
protected function serviceLogs() : void
{
Dispatcher::i()->checkAcpPermission( 'spam_service_log' );
/* Create the table */
$table = new TableDb( 'core_spam_service_log', Url::internal( 'app=core&module=moderation&controller=spam&do=serviceLogs' ) );
$table->langPrefix = 'spamlogs_';
/* Columns we need */
$table->include = array( 'log_date', 'log_code', 'email_address', 'ip_address' );
$table->sortBy = $table->sortBy ?: 'log_date';
$table->sortDirection = $table->sortDirection ?: 'DESC';
/* Search */
$table->advancedSearch = array(
'email_address' => SEARCH_CONTAINS_TEXT,
@@ -476,7 +474,7 @@ class spam extends Controller
);
$table->quickSearch = 'email_address';
/* Custom parsers */
$table->parsers = array(
'log_date' => function( $val )
@@ -484,7 +482,7 @@ class spam extends Controller
return DateTime::ts( $val )->localeDate();
},
);
/* Add a button for settings */
Output::i()->sidebar['actions'] = array(
'settings' => array(
@@ -494,12 +492,12 @@ class spam extends Controller
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => Member::loggedIn()->language()->addToStack('prunesettings') )
),
);
/* Display */
Output::i()->title = Member::loggedIn()->language()->addToStack('spamlogs');
Output::i()->output = (string) $table;
}
/**
* Prune Settings
*
@@ -508,18 +506,18 @@ class spam extends Controller
protected function serviceLogSettings() : void
{
Dispatcher::i()->checkAcpPermission( 'spam_service_log' );
$form = new Form;
$form->add( new Interval( 'prune_log_spam', Settings::i()->prune_log_spam, FALSE, array( 'valueAs' => Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, Member::loggedIn()->language()->addToStack('after'), NULL, 'prune_log_spam' ) );
if ( $values = $form->values() )
{
$form->saveAsSettings();
Session::i()->log( 'acplog__spamlog_settings' );
Output::i()->redirect( Url::internal( 'app=core&module=moderation&controller=spam&do=serviceLogs' ), 'saved' );
}
Output::i()->title = Member::loggedIn()->language()->addToStack('spamlogssettings');
Output::i()->output = Theme::i()->getTemplate('global')->block( 'spamlogssettings', $form, FALSE );
}
@@ -732,11 +730,9 @@ class spam extends Controller
{
if( !Settings::i()->ipb_reg_number )
{
Member::loggedIn()->language()->words['spam_service_enabled_desc'] = Member::loggedIn()->language()->addToStack( 'spam_service_nokey', FALSE, array( 'sprintf' => array( Url::internal( 'app=core&module=settings&controller=licensekey' ) ) ) );
}
else
{
Member::loggedIn()->language()->words['spam_service_enabled_desc'] = Member::loggedIn()->language()->addToStack( 'spam_service_noservice' );
}
}
@@ -252,7 +252,7 @@ class files extends Controller
catch ( OutOfRangeException $e ){ }
}
if ( isset( $loadedExtensions[ $map['location_key'] ] ) AND method_exists( $loadedExtensions[ $map['location_key'] ], 'attachmentLookup' ) )
if ( isset( $loadedExtensions[ $map['location_key'] ] ) )
{
try
{
@@ -261,7 +261,7 @@ class files extends Controller
$locations[] = $url;
}
}
catch ( LogicException $e ) { }
catch ( LogicException | OutOfRangeException $e ) { }
}
}
@@ -389,6 +389,11 @@ class files extends Controller
{
$cicDisabled[ $row['id'] ] = $row['id'];
}
if ( $row['method'] == 'Amazon' AND CIC AND json_decode( $row['configuration'], TRUE )['bucket'] == 'ips-cic-filestore' )
{
$cicDisabled[ $row['id'] ] = $row['id'];
}
}
$form = new Form;
@@ -477,7 +482,15 @@ class files extends Controller
/* Do we need to move files at all? */
$configurations = File::getStore();
$exploded = explode( '_', $k );
$classname = "IPS\\{$exploded[2]}\\extensions\\core\\FileStorage\\{$exploded[3]}";
try
{
$classname = Application::getExtensionClass( $exploded[2], 'FileStorage', $exploded[3] );
}
catch( OutOfRangeException )
{
continue;
}
$extension = new $classname;
$currentClass = File::getClass( intval( $settings[ $k ] ) );
$newClass = File::getClass( intval( $v ) );
@@ -561,6 +574,18 @@ class files extends Controller
$settings = json_decode( Settings::i()->upload_settings, TRUE );
$table->rowButtons = function( $row ) use ( $settings )
{
$config = json_decode( $row['configuration'], true );
if( 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']}" )
)
);
}
$buttons = array(
'log' => array(
'icon' => 'search',
@@ -829,19 +854,16 @@ class files extends Controller
$exploded = explode( '_', $k );
try
{
$classname = "IPS\\{$exploded[2]}\\extensions\\core\\FileStorage\\{$exploded[3]}";
$classname = Application::getExtensionClass( $exploded[2], 'FileStorage', $exploded[3] );
if( Application::appIsEnabled( $exploded[2] ) AND class_exists( $classname ) )
$extension = new $classname;
$count = $extension->count();
/* Don't bother with this task if there is nothing to move */
if( $count > 0 )
{
$extension = new $classname;
$count = $extension->count();
/* Don't bother with this task if there is nothing to move */
if( $count > 0 )
{
$totalCount += $count;
Task::queue( 'core', 'MoveFiles', array( 'storageExtension' => $k, 'oldConfiguration' => $v, 'newConfiguration' => $newStorageId, 'count' => $count ), 2 );
}
$totalCount += $count;
Task::queue( 'core', 'MoveFiles', array( 'storageExtension' => $k, 'oldConfiguration' => $v, 'newConfiguration' => $newStorageId, 'count' => $count ), 2 );
}
$settings[ $k ] = $newStorageId;
@@ -877,7 +899,7 @@ class files extends Controller
$cicConfigurations = 0;
if( CIC )
{
$cicConfigurations = (int) Db::i()->select( 'count(*)', 'core_file_storage', array( "method=? AND configuration LIKE CONCAT( '%', ?, '%' )", 'Amazon', 'ips-cic-filestore' ) )->first();
$cicConfigurations = (int) Db::i()->select( 'count(*)', 'core_file_storage', array( "method=?", 'Cloud' ) )->first();
}
if( $totalConfigurations - $cicConfigurations > 0 )
{
@@ -890,6 +912,11 @@ class files extends Controller
$toggles = array();
foreach ( File::storageHandlers( NULL ) as $key => $class )
{
if ( CIC AND $key == 'Cloud' AND !\IPS\Member::loggedIn()->members_bitoptions['is_support_account'] )
{
continue;
}
$handlers[ $key ] = 'filehandler__' . $key;
foreach ( $class::settings() as $k => $v )
{
@@ -916,7 +943,7 @@ class files extends Controller
/* Build form */
$form = new Form;
$form->add( new Radio( 'filestorage_method', ( CIC ? 'Amazon' : 'FileSystem' ), TRUE, array( 'options' => $handlers, 'toggles' => $toggles ) ) );
$form->add( new Radio( 'filestorage_method', ( CIC ? 'Cloud' : 'FileSystem' ), TRUE, array( 'options' => $handlers, 'toggles' => $toggles ) ) );
foreach ( $handlerSettings as $handlerKey => $settings )
{
foreach ( $settings as $setting )
@@ -312,8 +312,20 @@ class advertisements extends Controller
$form = new Form;
$form->add( new Select( 'ads_circulation', Settings::i()->ads_circulation, TRUE, array( 'options' => array( 'random' => 'ad_circ_random', 'newest' => 'ad_circ_newest', 'oldest' => 'ad_circ_oldest', 'least' => 'ad_circ_leasti' ) ) ) );
$form->add( new YesNo( 'ads_force_sidebar', Settings::i()->ads_force_sidebar, TRUE ) );
$form->add( new YesNo( 'ads_txt_enabled', Settings::i()->ads_txt_enabled, FALSE, array( 'togglesOn' => array( 'ads_txt' ) ) ) );
$form->add( new TextArea( 'ads_txt', Settings::i()->ads_txt, FALSE, array( 'rows' => 8 ), NULL, NULL, NULL, 'ads_txt' ) );
$adsTxtOptions = [
0 => 'no',
1 => 'yes',
2 => 'ads_txt_option_redirect',
];
$adsTxtToggles = [
1 => ['ads_txt'],
2 => ['ads_txt_redirect_url'],
];
$form->add( new Select( 'ads_txt_enabled', Settings::i()->ads_txt_enabled, FALSE, [
'options' => $adsTxtOptions,
'toggles' => $adsTxtToggles ] ) );
$form->add( new TextArea( 'ads_txt', Settings::i()->ads_txt, FALSE, [ 'rows' => 8 ], id: 'ads_txt' ) );
$form->add( new FormUrl( 'ads_txt_redirect_url', Settings::i()->ads_txt_redirect_url, FALSE, id: 'ads_txt_redirect_url' ) );
$form->add( new YesNo( 'ajax_pagination', Settings::i()->ajax_pagination, FALSE ) );
if ( $values = $form->values() )
@@ -565,7 +565,7 @@ class advanced extends Controller
/* Build and show form */
$form = new Form;
$form->add( new Codemirror( 'custom_body_code', Settings::i()->custom_body_code, FALSE, array('height' => 150, 'codeModeAllowedLanguages' => [ 'xml' ] ), NULL, NULL, NULL, 'custom_body_code' ) );
$form->add( new Codemirror( 'custom_body_code', Settings::i()->custom_body_code, FALSE, array('height' => 150, 'codeModeAllowedLanguages' => [ 'html' ] ), NULL, NULL, NULL, 'custom_body_code' ) );
$form->add( new Codemirror( 'custom_page_view_js', Settings::i()->custom_page_view_js, FALSE, array('height' => 150, 'codeModeAllowedLanguages' => [ 'javascript' ] ), NULL, NULL, NULL, 'custom_page_view_js' ) );
if ( $values = $form->values() )
@@ -81,7 +81,7 @@ class email extends \IPS\Dispatcher\Controller
$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' ) );
$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 YesNo( 'email_truncate', ( Settings::i()->email_truncate ), FALSE, array(), NULL, NULL, NULL, 'email_truncate' ) );
$settingsForm->add( new YesNo( 'email_log_do', ( Settings::i()->prune_log_emailstats != 0 ), FALSE, array( 'togglesOn' => array( 'prune_log_emailstats' ) ), NULL, NULL, NULL, 'email_log_do' ) );
@@ -87,20 +87,6 @@ class general extends Controller
$form->add( new YesNo( 'site_site_elsewhere', Settings::i()->site_site_elsewhere, FALSE, [ 'togglesOn' => [ 'site_main_url', 'site_main_title' ] ] ) );
$form->add( new FormUrl( 'site_main_url', Settings::i()->site_main_url, FALSE, [], NULL, NULL, NULL, 'site_main_url' ) );
$form->add( new Text( 'site_main_title', Settings::i()->site_main_title, FALSE, [], NULL, NULL, NULL, 'site_main_title' ) );
$diagnosticsOptions = [];
if( (bool) Settings::i()->usage_reporting === true )
{
$diagnosticsOptions[] = 'usage';
}
if( (bool) Settings::i()->diagnostics_reporting === true )
{
$diagnosticsOptions[] = 'diagnostics';
}
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'diagnostics_reporting', $diagnosticsOptions, FALSE, array( 'options' => array(
'usage' => 'diagnostics_reporting_usage',
'diagnostics' => 'diagnostics_reporting_diagnostics',
) ) ) );
if ( $values = $form->values() )
{
@@ -115,11 +101,7 @@ class general extends 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'] );
Db::i()->update( 'core_tasks', array( 'enabled' => intval( $values['usage_reporting'] ) ), array( '`key`=?', 'usagereporting' ) );
$form->saveAsSettings( $values );
if ( $values['site_online'] )
@@ -85,12 +85,14 @@ class licensekey extends Controller
'icon' => 'refresh',
'link' => Url::internal( 'app=core&module=settings&controller=licensekey&do=refresh' )->csrf(),
'title' => 'license_refresh',
'class' => 'ipsButton--disabled',
),
'remove' => array(
'icon' => 'pencil',
'link' => Url::internal( 'app=core&module=settings&controller=licensekey&do=settings' ),
'title' => 'license_change',
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => Member::loggedIn()->language()->addToStack('license_change') )
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => Member::loggedIn()->language()->addToStack('license_change') ),
'class' => 'ipsButton--disabled',
),
);
@@ -497,7 +497,7 @@ class login extends Controller
/* Redirect */
Output::i()->redirect( Url::internal( 'app=core&module=settings&controller=login&tab=registration' ), 'saved' );
}
$desc = Theme::i()->getTemplate( 'global', 'core' )->message( Member::loggedIn()->language()->addToStack('registration_default_group_info', NULL, [ 'sprintf' => [Group::load( Settings::i()->member_group )->formattedName ] ] ), 'none i-border-radius_0' );
$desc = Theme::i()->getTemplate( 'global', 'core' )->message( Member::loggedIn()->language()->addToStack('registration_default_group_info', NULL, [ 'htmlsprintf' => [Group::load( Settings::i()->member_group )->formattedName ] ] ), 'none i-border-radius_0' );
/* Display */
return $desc . $form;
@@ -12,6 +12,7 @@ namespace IPS\core\modules\admin\settings;
/* To prevent PHP errors (extending class does not exist) revealing path */
use Exception;
use InvalidArgumentException;
use IPS\Application;
use IPS\Content;
@@ -51,6 +52,7 @@ use function count;
use function defined;
use function intval;
use function is_array;
use function preg_match;
use const IPS\TEMP_DIRECTORY;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
@@ -193,6 +195,7 @@ class posting extends Controller
}, NULL, NULL, 'attachment_image_size' ) );
$form->add( new Number( 'max_internalembed_width', Settings::i()->max_internalembed_width, FALSE, array( 'unlimited' => 0 ), NULL, NULL, 'px', 'max_internalembed_width' ) );
$form->add( new Number( 'max_embeddedmedia_width', Settings::i()->max_embeddedmedia_width, FALSE, array( 'unlimited' => 0 ), NULL, NULL, 'px', 'max_embeddedmedia_width' ) );
$form->addHeader('posting_content');
if ( Login::registrationType() != 'disabled' )
@@ -925,6 +928,7 @@ class posting extends Controller
continue;
}
$isCustom = preg_match( "/^ipsCustom(Node|Mark|Extension)__/", $restriction );
if ( isset( $dependencies[ $restriction ] ) )
{
$firstLine = '';
@@ -936,6 +940,16 @@ class posting extends Controller
Member::loggedIn()->language()->words['editor_r__' . $restriction . '_desc'] = $firstLine . sprintf( Member::loggedIn()->language()->get( 'editor_r__dependency' ), Member::loggedIn()->language()->addToStack( 'editor_r__' . $dependencies[$restriction] ) );
}
else if ( !$isCustom )
{
try
{
/* Now make sure to put the _desc description in just in case so the form can use it */
$actualDesc = Member::loggedIn()->language()->get( "editor_r__" . $restriction . "__desc" );
Member::loggedIn()->language()->words["editor_r__" . $restriction . "_desc"] = $actualDesc;
}
catch ( UnderflowException ) {}
}
$dependents = [];
foreach ( $dependencies as $dependent => $dependency )
@@ -147,7 +147,7 @@ class webapp extends Controller
if( $values = $form->values() )
{
$path = Url::createFromString( Url::baseUrl() )->data[ Url::COMPONENT_PATH ];
$startUrl = $path ?? '';
$startUrl = !empty( $path ) ? $path : '/';
if ( $values['manifest_custom_url_toggle'] !== FALSE and ! empty( $values['manifest_short_url'] ) )
{
@@ -285,7 +285,7 @@ class badges extends Controller
else
{
Output::i()->title = Member::loggedIn()->language()->addToStack( 'menu__core_stats_badges' );
Output::i()->output = Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $activeTab, (string)$chart, Url::internal( "app=core&module=stats&controller=badges" ), 'tab', '', 'i-padding_3' );
Output::i()->output = Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $activeTab, (string)$chart, Url::internal( "app=core&module=stats&controller=badges" ), 'tab', '', '' );
}
}
}
@@ -126,12 +126,12 @@ class follow extends Controller
if ( Request::i()->isAjax() )
{
Output::i()->output = Theme::i()->getTemplate( 'global' )->paddedBlock( $output, NULL, "i-padding_3" );
Output::i()->output = $output;
}
else
{
Output::i()->title = Member::loggedIn()->language()->addToStack( 'menu__core_stats_follow' );
Output::i()->output = Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $activeTab, $output, Url::internal( "app=core&module=stats&controller=follow" ), 'tab', '', 'i-padding_3' );
Output::i()->output = Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $activeTab, $output, Url::internal( "app=core&module=stats&controller=follow" ), 'tab', '', '' );
}
}
@@ -86,12 +86,12 @@ class helpful extends Controller
if ( Request::i()->isAjax() )
{
Output::i()->output = Theme::i()->getTemplate( 'global' )->paddedBlock( $output, NULL, "i-padding_3" );
Output::i()->output = $output;
}
else
{
Output::i()->title = Member::loggedIn()->language()->addToStack('menu__core_stats_helpful');
Output::i()->output = Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $activeTab, $output, Url::internal( "app=core&module=stats&controller=helpful" )->setQueryString( $params ), 'tab', '', 'i-padding_3' );
Output::i()->output = Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $activeTab, $output, Url::internal( "app=core&module=stats&controller=helpful" )->setQueryString( $params ), 'tab', '', '' );
}
}
@@ -74,12 +74,12 @@ class preferences extends Controller
if ( Request::i()->isAjax() )
{
Output::i()->output = Theme::i()->getTemplate( 'global' )->paddedBlock( (string) $chart, NULL, "i-padding_3" );
Output::i()->output = (string) $chart;
}
else
{
Output::i()->title = Member::loggedIn()->language()->addToStack('menu__core_stats_preferences');
Output::i()->output = Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $activeTab, (string) $chart, Url::internal( "app=core&module=stats&controller=preferences" ), 'tab', '', 'i-padding_3' );
Output::i()->output = Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $activeTab, (string) $chart, Url::internal( "app=core&module=stats&controller=preferences" ), 'tab', '', '' );
}
}
@@ -96,12 +96,12 @@ class referrals extends 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( Request::i()->page ) ? intval( Request::i()->page ) : 1;
@@ -113,7 +113,8 @@ class referrals extends Controller
try
{
$total = 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 = 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 )
{
@@ -122,7 +123,8 @@ class referrals extends Controller
if( $total )
{
$select = 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 = 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 )
@@ -83,7 +83,7 @@ class warnings extends Controller
else
{
Output::i()->title = Member::loggedIn()->language()->addToStack('menu__core_stats_warnings');
Output::i()->output = Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $activeTab, (string) $chart, Url::internal( "app=core&module=stats&controller=warnings" ), 'tab', '', 'i-padding_3' );
Output::i()->output = Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $activeTab, (string) $chart, Url::internal( "app=core&module=stats&controller=warnings" ), 'tab', '', '' );
}
}
@@ -250,27 +250,7 @@ class support extends Controller
*/
protected function guideSearch() : void
{
/* Search term must be at least 3 characters long */
if( mb_strlen( Request::i()->search_term ) < 3 )
{
Output::i()->json( array() );
}
$guides = array();
try
{
$response = Url::ips( 'guides/' . urlencode( Request::i()->search_term ) )->request()->get();
if( $response->httpResponseCode !== 200 )
{
throw new UnexpectedValueException;
}
$guides = $response->decodeJson();
}
catch ( \Exception $e ) { }
Output::i()->json( $guides );
Output::i()->json( array() );
}
/**
@@ -806,7 +786,7 @@ class support extends Controller
/* Check connections and server time */
try
{
$result = intval( (string) Url::ips( 'connectionCheck' )->request()->get() );
$result = time();
}
catch ( \Exception $e )
{
@@ -1026,33 +1006,6 @@ class support extends Controller
*/
protected function _checkUpgrades() : bool|int|array
{
try
{
$url = Url::ips('updateCheck5')->setQueryString( array( 'type' => 'support', 'key' => Settings::i()->ipb_reg_number ) );
if ( USE_DEVELOPMENT_BUILDS )
{
$url = $url->setQueryString( 'development', 1 );
}
if ( IPS_ALPHA_BUILD )
{
$url = $url->setQueryString( 'alpha', 1 );
}
$response = $url->request()->get()->decodeJson();
if ( $response['longversion'] > Application::load('core')->long_version )
{
if ( $response['version'] != Application::load('core')->version )
{
return $response['security'] ? -1 : TRUE;
}
elseif ( count( $response['changes'] ) )
{
return $response['changes'];
}
}
}
catch ( \Exception $e ) { }
return FALSE;
}
@@ -1618,65 +1571,7 @@ class support extends Controller
*/
protected function _md5Checker( bool $fix = FALSE ) : ?int
{
try
{
$modifiedFiles = Application::md5Check( Application::load('core')->long_version );
}
catch ( \Exception $e )
{
$modifiedFiles = array();
}
if( !$fix )
{
return count( $modifiedFiles );
}
/* Build form */
$form = new Form( 'login', 'continue' );
$form->ajaxOutput = TRUE;
$form->add( new Email( 'ips_email_address', NULL ) );
$form->add( new Password( 'ips_password', NULL ) );
if ( $values = $form->values() )
{
$key = IPS::licenseKey();
$url = Url::ips( 'build5/' . $key['key'] )->setQueryString( array(
'ip' => Request::i()->ipAddress(),
'versionToDownload' => Application::getAvailableVersion('core'),
'files' => implode( ',', $modifiedFiles ),
'alpha' => (int) IPS_ALPHA_BUILD
) );
try
{
$response = $url->request( LONG_REQUEST_TIMEOUT )->login( $values['ips_email_address'], $values['ips_password'] )->get();
if ( $response->httpResponseCode == 200 and preg_match( '/^ips_[a-z0-9]{5}$/', (string) $response ) )
{
Output::i()->sendOutput( Theme::i()->getTemplate( 'global', 'core' )->blankTemplate( Theme::i()->getTemplate( 'support' )->fixMd5Download( Url::ips( "download/{$response}" ) ) ) );
}
}
catch( \IPS\Http\Request\Exception $e )
{
Log::log( $e, 'support' );
$response = NULL;
}
if ( (string) $response )
{
$form->error = (string) $response;
}
else
{
$form->error = Member::loggedIn()->language()->addToStack('md5_build_fail');
}
}
/* Output */
Output::i()->output = Theme::i()->getTemplate( 'support' )->fixMd5( $modifiedFiles, $form );
Output::i()->sendOutput( Theme::i()->getTemplate( 'global', 'core' )->blankTemplate( Output::i()->output ) );
return 0;
}
/**
@@ -82,6 +82,13 @@ class systemLogs extends Controller
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => Member::loggedIn()->language()->addToStack('prunesettings') )
),
);
Output::i()->sidebar['actions']['delete'] = array(
'icon' => 'trash',
'link' => Url::internal( 'app=core&module=support&controller=systemLogs&do=deleteAll' )->csrf(),
'title' => 'delete_logs',
'data' => array( 'confirm' => '' )
);
}
/* Button to view filebased logs */
@@ -198,6 +205,19 @@ class systemLogs extends Controller
Output::i()->breadcrumb[] = array( Url::internal( "app=core&module=support&controller=systemLogs" ), Member::loggedIn()->language()->addToStack('r__system_logs') );
Output::i()->output = Theme::i()->getTemplate( 'system' )->systemLogView( $log );
}
/**
* Delete all logs
*
* @return void
*/
protected function deleteAll()
{
/* Delete */
Db::i()->query( "TRUNCATE " . Settings::i()->sql_tbl_prefix . "core_log" );
Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=support&controller=systemLogs' ), 'deleted' );
}
/**
* Delete a log
+132 -175
View File
@@ -88,7 +88,7 @@ class upgrade extends Controller
* @brief Has been CSRF-protected
*/
public static bool $csrfProtected = TRUE;
/**
* @brief Steps that the AdminCP-based upgrader can handle
*/
@@ -98,7 +98,7 @@ class upgrade extends Controller
'lang' => '_upgradeLanguages',
'javascript'=> '_upgradeJavascript',
);
/**
* @brief IPS clientArea Password
*/
@@ -124,53 +124,10 @@ class upgrade extends Controller
protected function manage() : void
{
Dispatcher::i()->checkAcpPermission( 'upgrade_manage', 'core', 'overview' );
if ( NO_WRITES )
{
Output::i()->error( 'no_writes', '1C287/1', 403, '' );
}
$initialData = NULL;
if ( isset( Request::i()->patch ) )
{
$initialData = array( 'patch' => 1 );
}
/* If we are on 4.4.4 and start upgrading to 4.4.5, the initial $steps array will include the upgrade_extra_update key, however during the extraction this file
gets overwritten, and if we are on CiC the new array no longer contains this definition, resulting in an error "Function name must be a string". We need to
inspect the $_SESSION array to see if "upgrade_extract_update" is defined as the next step and use the first array if so. */
$upgradeUrl = Url::internal( 'app=core&module=system&controller=upgrade' );
if( isset( Request::i()->adsess ) )
{
$upgradeUrl = $upgradeUrl->stripQueryString()->setQueryString( array( 'adsess' => Request::i()->adsess, 'app' => 'core', 'module' => 'system', 'controller' => 'upgrade' ) );
}
$forceNormalExtraction = ( isset( $_SESSION[ 'wizard-' . md5( $upgradeUrl ) . '-step' ] ) ) ? ( $_SESSION[ 'wizard-' . md5( $upgradeUrl ) . '-step' ] == 'upgrade_extract_update' ) : FALSE;
if ( !CIC or $forceNormalExtraction )
{
$steps = array(
'upgrade_confirm_update' => array( $this, '_selectVersion' ),
'upgrade_login' => array( $this, '_login' ),
'upgrade_ftp_details' => array( $this, '_ftpDetails' ),
'upgrade_extract_update' => array( $this, '_extractUpdate' ),
'upgrade_upgrade' => array( $this, '_upgrade' ),
);
}
else
{
$steps = array(
'upgrade_confirm_update' => array( $this, '_selectVersion' ),
'upgrade_apply_cic' => array( $this, '_applyCic' ),
'upgrade_upgrade' => array( $this, '_upgrade' )
);
}
$wizard = new Wizard( $steps, $upgradeUrl, TRUE, $initialData );
Output::i()->title = Member::loggedIn()->language()->addToStack('ips_suite_upgrade');
Output::i()->output = (string) $wizard;
Output::i()->redirect( Url::external( "https://community.invisionpower.com/resources/" ) );
}
/**
* Select Version
*
@@ -220,7 +177,7 @@ class upgrade extends Controller
}
}
catch ( \Exception $e ) { }
/* Build form */
$form = new Form( 'select_version' );
$options = array();
@@ -255,14 +212,14 @@ class upgrade extends Controller
$options = array( $latestVersion => $options[ $latestVersion ] ); // CIC can only actually do the actual latest version
}
$form->add( new Radio( 'version', $latestVersion, TRUE, array( 'options' => $options, '_details' => $descriptions ) ) );
/* Handle submissions */
$values = $form->values();
}
/* Do we have a chosen version? */
if ( $values )
{
{
/* Check requirements */
if( !CIC )
{
@@ -290,7 +247,7 @@ class upgrade extends Controller
}
catch ( \Exception $e ) {}
}
/* Do a database check */
if( !IPS_ALPHA_BUILD )
{
@@ -344,7 +301,7 @@ class upgrade extends Controller
$data['changes'][ $k ] = $this->_changeDetails( $data['oldVersion'], $values['version'], $k );
}
}
/* 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
@@ -360,11 +317,11 @@ class upgrade extends Controller
Output::i()->redirect( $val );
}
else
{
{
return Theme::i()->getTemplate('system')->manualUpgradeRequired();
}
}
/* Check if we will need any manual queries */
if ( !CIC and $data['info']['steps']['queries'] )
{
@@ -383,12 +340,12 @@ class upgrade extends Controller
}
}
}
$tablesWeWillNeedToDoManualQueriesOn = array_keys( array_filter( $checkedTables ) );
if ( $tablesWeWillNeedToDoManualQueriesOn )
{
sort( $tablesWeWillNeedToDoManualQueriesOn );
if ( isset( Request::i()->skip_large_tables_check ) )
{
$data['largeTables'] = $tablesWeWillNeedToDoManualQueriesOn;
@@ -407,15 +364,15 @@ class upgrade extends Controller
{
$data['largeTables'] = array();
}
/* Return */
return $data;
}
/* Display */
return $form->customTemplate( array( Theme::i()->getTemplate( 'system' ), 'upgradeSelectVersion' ) );
}
/**
* Get details of what has changed between two versions
*
@@ -443,7 +400,7 @@ class upgrade extends Controller
Output::i()->error( 'delta_upgrade_fail_server', '3C287/5', 500, NULL, array(), get_class( $e ) . '::' . $e->getCode() . ": " . $e->getMessage() );
}
}
/* If there is any steps which the AdminCP upgrader cannot handle, set forceMainUpgrader so that they get redirected to the upgrader */
if ( !$item )
{
@@ -451,17 +408,17 @@ class upgrade extends Controller
$stepsThisUpgradeWantsToRun = array_keys( array_filter( $details['steps'], function( $v, $k ) {
return $v;
}, ARRAY_FILTER_USE_BOTH ) );
if ( array_diff( $stepsThisUpgradeWantsToRun, $stepsWeCanHandle ) )
{
$details['forceMainUpgrader'] = TRUE;
}
}
/* Return */
return $details;
}
/**
* Apply files for CiC
*
@@ -496,7 +453,7 @@ class upgrade extends Controller
IPS::applyLatestFilesIPSCloud();
}
catch( \IPS\Http\Request\Exception $e ){}
return Theme::i()->getTemplate('system')->upgradeDeltaFailedCic();
}
elseif ( isset( Request::i()->done ) )
@@ -506,7 +463,7 @@ class upgrade extends Controller
UpgradeClass::setUpgradingFlag( TRUE );
Session::i()->log( 'acplog__upgrade_started' );
/* Check latest version */
$versions = array();
foreach ( Db::i()->select( '*', 'core_applications', Db::i()->in( 'app_directory', IPS::$ipsApps ) ) as $app )
@@ -517,7 +474,7 @@ class upgrade extends Controller
}
}
$version = min( $versions );
$extractUrl = new Url( Settings::i()->base_url . 'admin/upgrade/extractCic.php' );
$extractUrl = $extractUrl
->setScheme( NULL ) // Use protocol-relative in case the AdminCP is being loaded over https but rest of site is not
@@ -525,17 +482,17 @@ class upgrade extends Controller
'account' => IPS::getCicUsername(),
'key' => md5( IPS::getCicUsername() . Settings::i()->sql_pass ),
'version' => $version
)
)
);
/* Send the request */
IPS::applyLatestFilesIPSCloud( $version );
/* NOTE: We still need to use an iframe here, as CiC would still be susceptible to the same failures. */
return Theme::i()->getTemplate('system')->upgradeExtractCic( $extractUrl );
}
}
/**
* Login
*
@@ -550,13 +507,13 @@ class upgrade extends Controller
$data['key'] = 'test';
return $data;
}
/* Build form */
$form = new Form( 'login', 'continue' );
$form->hiddenValues['version'] = $data['version'];
$form->add( new Email( 'ips_email_address', NULL ) );
$form->add( new Password( 'ips_password', NULL ) );
if ( !CIC )
{
$form->add( new YesNo( 'upgrade_confirm_backup', FALSE, TRUE, array(), function( $val ) {
@@ -566,7 +523,7 @@ class upgrade extends Controller
}
} ) );
}
/* Handle submissions */
if ( $values = $form->values() )
{
@@ -601,10 +558,10 @@ class upgrade extends Controller
$form->error = Member::loggedIn()->language()->addToStack('download_upgrade_error');
}
}
return (string) $form;
}
/**
* Get a download key
*
@@ -620,7 +577,7 @@ class upgrade extends Controller
{
$key = IPS::licenseKey();
$url = Url::ips( 'build5/' . $key['key'] )->setQueryString( 'ip', Request::i()->ipAddress() );
if ( USE_DEVELOPMENT_BUILDS )
{
$url = $url->setQueryString( 'development', 1 );
@@ -644,7 +601,7 @@ class upgrade extends Controller
{
$url = $url->setQueryString( 'files', implode( ',', $files ) );
}
$response = $url->request( LONG_REQUEST_TIMEOUT )->login( $clientAreaEmail, $this->_clientAreaPassword )->get();
switch ( $response->httpResponseCode )
{
@@ -657,15 +614,15 @@ class upgrade extends Controller
{
return (string) $response;
}
case 304:
return NULL;
default:
throw new LogicException( (string) $response );
}
}
/**
* Get FTP Details
*
@@ -707,7 +664,7 @@ class upgrade extends Controller
throw new DomainException('delta_upgrade_ftp_details_err');
}
};
/* If we have details stored, retreive them */
$decodedFtpDetails = NULL;
if ( Settings::i()->upgrade_ftp_details )
@@ -735,12 +692,12 @@ class upgrade extends Controller
'path' => str_replace( '/home/' . @get_current_user(), '', \IPS\ROOT_PATH )
);
}
/* Build the form */
$form = new Form( 'ftp_details', 'continue' );
$form->add( new Ftp( 'delta_upgrade_ftp_details', $defaultDetails, TRUE, array( 'rejectUnsupportedSftp' => TRUE, 'allowBypassValidation' => FALSE ), $validateCallback ) );
$form->add( new Checkbox( 'delta_upgrade_ftp_remember', TRUE ) );
/* Handle submissions */
if ( $values = $form->values() )
{
@@ -749,11 +706,11 @@ class upgrade extends Controller
$encrypted = Encrypt::fromPlaintext( json_encode( $values['delta_upgrade_ftp_details'] ) );
Settings::i()->changeValues( array( 'upgrade_ftp_details' => $encrypted->tag() ) );
}
$data['ftpDetails'] = 1;
return $data;
}
/* Display the form */
return Theme::i()->getTemplate('system')->upgradeDeltaFtp( (string) $form );
}
@@ -791,16 +748,16 @@ class upgrade extends Controller
return Theme::i()->getTemplate('system')->upgradeDeltaFailed( 'exception', isset( $data['key'] ) ? Url::ips("download/{$data['key']}") : NULL );
}
}
/* Download & Extract */
if ( $data['key'] and !isset( Request::i()->check ) )
{
{
/* If we've asked to do it manually, just show that screen */
if ( isset( $data['manual'] ) and $data['manual'] )
{
return Theme::i()->getTemplate('system')->upgradeDeltaFailed( NULL, Url::ips("download/{$data['key']}") );
}
/* Multiple Redirector */
$url = Url::internal('app=core&module=system&controller=upgrade');
return (string) new MultipleRedirect( $url, function( $mrData ) use ( $data )
@@ -814,9 +771,9 @@ class upgrade extends Controller
elseif ( $mrData['status'] == 'download' )
{
if ( !isset( $mrData['tmpFileName'] ) )
{
{
$mrData['tmpFileName'] = tempnam( TEMP_DIRECTORY, 'IPS' ) . '.zip';
return array( $mrData, Member::loggedIn()->language()->addToStack('delta_upgrade_downloading'), 0 );
}
else
@@ -835,11 +792,11 @@ class upgrade extends Controller
}
$startRange = $mrData['range'];
$endRange = $startRange + 1000000 - 1;
$response = Url::ips("download/{$data['key']}")->request( LONG_REQUEST_TIMEOUT )->setHeaders( array( 'Range' => "bytes={$startRange}-{$endRange}" ) )->get();
Log::debug( "Fetching download [range={$startRange}-{$endRange}] with a response code: " . $response->httpResponseCode, 'auto_upgrade' );
if ( $response->httpResponseCode == 404 )
{
if ( isset( $mrData['tmpFileName'] ) )
@@ -848,7 +805,7 @@ class upgrade extends Controller
}
Log::log( "Cannot fetch delta download: " . var_export( $response, TRUE ), 'auto_upgrade' );
if( CIC )
{
try
@@ -871,7 +828,7 @@ class upgrade extends Controller
$fh = fopen( $mrData['tmpFileName'], 'a' );
fwrite( $fh, (string) $response );
fclose( $fh );
$mrData['range'] = $endRange + 1;
$complete = 100 / $totalFileSize * $mrData['range'];
@@ -899,9 +856,9 @@ class upgrade extends Controller
'container' => $data['key'],
'key' => md5( Settings::i()->board_start . $mrData['tmpFileName'] . Settings::i()->sql_pass ),
'ftp' => ( isset( $data['ftpDetails'] ) ) ? $data['ftpDetails'] : ''
)
)
);
return array( Theme::i()->getTemplate('system')->upgradeExtract( $extractUrl ) );
}
},
@@ -910,7 +867,7 @@ class upgrade extends Controller
Output::i()->redirect( Url::internal('app=core&module=system&controller=upgrade&check=1') );
} );
}
/* Run md5 check */
try
{
@@ -919,13 +876,13 @@ class upgrade extends Controller
{
/* Log */
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 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 );
@@ -938,17 +895,17 @@ class upgrade extends Controller
$data['key'] = $newDownloadKey;
$data['md5Fix'] = TRUE;
$_SESSION[ 'wizard-' . md5( Url::internal( 'app=core&module=system&controller=upgrade' ) ) . '-data' ] = $data;
/* Redirect back in */
Output::i()->redirect( Url::internal('app=core&module=system&controller=upgrade') );
}
}
catch ( \Exception $e ) {}
/* Nope, we're good! */
return $data;
}
/**
* Upgrade
*
@@ -959,7 +916,7 @@ class upgrade extends Controller
{
/* Resync */
IPS::resyncIPSCloud('Uploaded new version');
/* If we cannot handle this in the AdminCP, redirect them to the upgrader */
if ( $data['info']['forceMainUpgrader'] )
{
@@ -971,7 +928,7 @@ class upgrade extends Controller
{
Output::i()->redirect( 'upgrade/?autologin=1' );
}
/* Otherwise let's do the upgrade! */
$url = Url::internal('app=core&module=system&controller=upgrade');
return (string) new MultipleRedirect( $url, function( $mrData ) use ( $data )
@@ -981,7 +938,7 @@ class upgrade extends Controller
return array( array( 'step' => 0 ), Member::loggedIn()->language()->addToStack('delta_upgrade_processing'), 0 );
}
else
{
{
$steps = array();
foreach ( $data['info']['steps'] as $step => $v )
{
@@ -997,7 +954,7 @@ class upgrade extends Controller
}
}
}
if ( count( $steps ) AND array_key_exists( $mrData['step'], $steps ) )
{
$perStepPercentage = ( 100 / count( $steps ) );
@@ -1005,10 +962,10 @@ class upgrade extends Controller
$step = $steps[ $mrData['step'] ];
$percentage = $perStepPercentage * intval( $mrData['step'] );
$stepData = $mrData[$step] ?? array();
$return = $this->$step( $data, $stepData );
if ( $return === NULL )
{
{
unset( $mrData[ $step ] );
$mrData['step']++;
$percentage += $perStepPercentage;
@@ -1022,13 +979,13 @@ class upgrade extends Controller
$mrData[ $step ] = $return[1];
$percentage += ( $return[0] / ( 100 / $perStepPercentage ) );
}
return array( $mrData, Member::loggedIn()->language()->addToStack( 'delta_upgrade' . $step ), round( $percentage, 2 ) );
}
else
{
UpgradeClass::setUpgradingFlag( FALSE );
$databaseErrors = FALSE;
foreach ( Application::applications() as $app )
{
@@ -1057,9 +1014,9 @@ class upgrade extends Controller
unset( Store::i()->applications, Store::i()->updatecount_applications );
Zapier::rebuildRESTApiPermissions();
AdminNotification::remove( 'core', 'NewVersion' );
return array( Theme::i()->getTemplate('system')->upgradeFinished( $databaseErrors ) );
}
}
@@ -1069,7 +1026,7 @@ class upgrade extends Controller
Output::i()->redirect( 'upgrade/?autologin=1' );
} );
}
/**
* Upgrade: Queries
*
@@ -1080,7 +1037,7 @@ class upgrade extends Controller
public function _upgradeQueries( array $data, array $stepData ) : ?array
{
return $this->_appLoop( 'queries', $data, $stepData, function( $app, $data, $stepData )
{
{
/* If this is the first run, work out how many things we have to do, and remove any that need removing */
if ( !isset( $stepData['offset'] ) )
{
@@ -1096,14 +1053,14 @@ class upgrade extends Controller
return array( 0, $stepData );
}
}
/* Get the next query */
$queriesToRun = array_values( $data['changes']['queries'][ $app ] );
if ( isset( $queriesToRun[ $stepData['offset'] ] ) )
{
$method = $queriesToRun[ $stepData['offset'] ]['method'];
$params = $queriesToRun[ $stepData['offset'] ]['params'];
/* Is it for a table that we need to run manual queries on? */
$tableName = ( isset( $params[0] ) and is_string( $params[0] ) ) ? $params[0] : $params[0]['name'];
if ( !isset( Request::i()->runQuery ) and in_array( $tableName, $data['largeTables'] ) )
@@ -1117,13 +1074,13 @@ class upgrade extends Controller
else
{
$stepData['offset']++;
return array( 100 / $stepData['count'] * $stepData['offset'], $stepData );
return array( 100 / $stepData['count'] * $stepData['offset'], $stepData );
}
}
/* Nope, we can run it manually */
else
{
{
try
{
Db::i()->$method( ...$params );
@@ -1136,26 +1093,26 @@ class upgrade extends Controller
{
Output::i()->json( array( 'runManualQuery' => FALSE ) );
}
return Theme::i()->getTemplate('system')->upgradeDeltaQueryFailed( Request::i()->mr, $e );
}
}
if ( isset( Request::i()->runQuery ) )
{
Output::i()->json( array( 'runManualQuery' => TRUE ) );
}
$stepData['offset']++;
return array( 100 / $stepData['count'] * $stepData['offset'], $stepData );
return array( 100 / $stepData['count'] * $stepData['offset'], $stepData );
}
}
/* If there isn't one, we're done - run a final database check */
return NULL;
} );
}
/**
* Upgrade: HTML and CSS
*
@@ -1164,37 +1121,37 @@ class upgrade extends Controller
* @return array|null array( percentage of this step complete, $stepData ) OR NULL if this step is complete
*/
public function _upgradeTheme( array $data, array $stepData ) : ?array
{
{
return $this->_appLoop( 'theme', $data, $stepData, function( $app, $data, $stepData )
{
{
/* If this is the first run, work out how many things we have to do, and remove any that need removing */
if ( !isset( $stepData['offset'] ) )
{
$numberOfChangesInThisApp = 0;
/* HTML */
$templateGroupsToClear = array();
foreach ( $data['changes']['theme'][ $app ]['html'] as $type => $templates )
{
$numberOfChangesInThisApp += count( $templates );
if ( $type == 'removed' )
{
foreach ( $templates as $template )
{
$exploded = explode( '/', $template );
$templateGroupsToClear[ $exploded[0] ][ $exploded[1] ] = TRUE;
Theme::removeTemplates( $app, $exploded[0], $exploded[1], NULL, FALSE, $exploded[2] );
}
}
}
/* CSS */
foreach ( $data['changes']['theme'][ $app ]['css'] as $type => $cssFiles )
{
$numberOfChangesInThisApp += count( $cssFiles );
if ( $type == 'removed' )
{
foreach ( $cssFiles as $cssFile )
@@ -1205,12 +1162,12 @@ class upgrade extends Controller
}
}
}
/* Resources */
foreach ( $data['changes']['theme'][ $app ]['resources'] as $type => $resources )
{
$numberOfChangesInThisApp += count( $resources );
if ( $type == 'removed' )
{
foreach ( $resources as $resource )
@@ -1221,7 +1178,7 @@ class upgrade extends Controller
}
}
}
/* Return those details so the next step can actually begin the import */
if ( !$numberOfChangesInThisApp )
{
@@ -1235,8 +1192,8 @@ class upgrade extends Controller
return array( 0, $stepData );
}
}
/* Import new stuff */
/* Import new stuff */
$perLoop = 150;
$i = 0;
$done = 0;
@@ -1258,10 +1215,10 @@ class upgrade extends Controller
continue;
}
}
/* Templates */
if( $xml->name == 'template' )
{
{
if ( $location = $xml->getAttribute('template_location') and $group = $xml->getAttribute('template_group') and $template = $xml->getAttribute('template_name') )
{
$templateKey = "{$location}/{$group}/{$template}";
@@ -1322,7 +1279,7 @@ class upgrade extends Controller
Theme::removeResources( $app, $location, $path, NULL, FALSE, $name );
}
if ( in_array( $resourceKey, $data['changes']['theme'][ $app ]['resources']['added'] ) or in_array( $resourceKey, $data['changes']['theme'][ $app ]['resources']['edited'] ) )
{
{
Theme::addResource( array(
'app' => $app,
'location' => $location,
@@ -1334,16 +1291,16 @@ class upgrade extends Controller
}
}
}
/* Have we done the most we're allowed per loop? */
if( $done >= $perLoop )
{
$stepData['offset'] = $i;
$stepData['done'] = isset( $stepData['done'] ) ? ( $stepData['done'] + $done ) : $done;
return array( 100 / $stepData['count'] * $stepData['done'], $stepData );
return array( 100 / $stepData['count'] * $stepData['done'], $stepData );
}
}
/* If we're still here, this app is complete - do cleanup */
foreach ( $stepData['templateGroupsToClear'] as $location => $groups )
{
@@ -1362,7 +1319,7 @@ class upgrade extends Controller
return NULL;
} );
}
/**
* Upgrade: Languages
*
@@ -1375,7 +1332,7 @@ class upgrade extends Controller
return $this->_appLoop( 'lang', $data, $stepData, function( $app, $data, $stepData )
{
$languages = array_keys( Lang::languages() );
/* Remove old */
if ( $data['changes']['lang'][ $app ]['normal']['removed'] )
{
@@ -1388,7 +1345,7 @@ class upgrade extends Controller
if ( !isset( $stepData['offset'] ) )
{
$numberOfChangesInThisApp = 0;
foreach ( $data['changes']['lang'][ $app ] as $jsOrNormal => $types )
{
foreach ( $types as $type => $langKeys )
@@ -1418,8 +1375,8 @@ class upgrade extends Controller
return array( 0, $stepData );
}
}
/* Import new stuff */
/* Import new stuff */
$perLoop = 150;
$batchSize = 25;
$i = 0;
@@ -1429,7 +1386,7 @@ class upgrade extends Controller
$xml->read();
$xml->read();
$xml->read();
$version = $xml->getAttribute('version');
$version = $xml->getAttribute('version');
while ( $xml->read() )
{
/* Skip to where we need to be */
@@ -1446,10 +1403,10 @@ class upgrade extends Controller
continue;
}
}
/* Templates */
if( $xml->name == 'word' )
{
{
if ( $langKey = $xml->getAttribute('key') )
{
$js = $xml->getAttribute('js');
@@ -1472,7 +1429,7 @@ class upgrade extends Controller
}
}
}
/* Have we got enough for a batch? */
if ( $done >= $batchSize )
{
@@ -1483,7 +1440,7 @@ class upgrade extends Controller
}
$batchesDone++;
}
/* Have we done the most we're allowed per loop? */
if( $done >= $perLoop )
{
@@ -1491,13 +1448,13 @@ class upgrade extends Controller
{
Db::i()->insert( 'core_sys_lang_words', $inserts, TRUE );
}
$stepData['offset'] = $i;
$stepData['done'] = isset( $stepData['done'] ) ? ( $stepData['done'] + $done ) : $done;
return array( 100 / $stepData['count'] * $stepData['done'], $stepData );
return array( 100 / $stepData['count'] * $stepData['done'], $stepData );
}
}
/* If we're still here, this app is complete */
if ( count( $inserts ) )
{
@@ -1506,7 +1463,7 @@ class upgrade extends Controller
return NULL;
} );
}
/**
* Upgrade: Javascript
*
@@ -1517,11 +1474,11 @@ class upgrade extends Controller
public function _upgradeJavascript( array $data, array $stepData ) : ?array
{
return $this->_appLoop( 'javascript', $data, $stepData, function( $app, $data, $stepData )
{
{
if ( !isset( $stepData['offset'] ) )
{
$numberOfChangesInThisApp = 0;
foreach ( $data['changes']['javascript'][ $app ]['files'] as $type => $files )
{
if ( $type === 'removed' )
@@ -1553,8 +1510,8 @@ class upgrade extends Controller
return array( 0, $stepData );
}
}
/* Import new stuff */
/* Import new stuff */
$perLoop = 150;
$i = 0;
$done = 0;
@@ -1576,13 +1533,13 @@ class upgrade extends Controller
continue;
}
}
if( $xml->name == 'file' )
{
{
if ( $location = $xml->getAttribute('javascript_location') and $path = $xml->getAttribute('javascript_path') and $name = $xml->getAttribute('javascript_name') )
{
$filePath = "{$app}/{$location}/{$path}/{$name}";
if ( in_array( $filePath, $data['changes']['javascript'][ $app ]['files']['added'] ) or in_array( $filePath, $data['changes']['javascript'][ $app ]['files']['edited'] ) )
{
Db::i()->replace( 'core_javascript', array(
@@ -1600,13 +1557,13 @@ class upgrade extends Controller
}
}
}
/* Have we done the most we're allowed per loop? */
if( $done >= $perLoop )
{
$stepData['offset'] = $i;
$stepData['done'] = isset( $stepData['done'] ) ? ( $stepData['done'] + $done ) : $done;
return array( 100 / $stepData['count'] * $stepData['done'], $stepData );
return array( 100 / $stepData['count'] * $stepData['done'], $stepData );
}
}
@@ -1615,7 +1572,7 @@ class upgrade extends Controller
return NULL;
} );
}
/**
* App Looper
*
@@ -1626,28 +1583,28 @@ class upgrade extends Controller
* @return array|string|null array( percentage of this step complete, $stepData ) OR NULL if this step is complete
*/
protected function _appLoop( string $step, array $data, array $stepData, callable $code ) : array|string|null
{
{
$returnNext = FALSE;
$apps = array_keys( Application::applications() );
$percentage = 0;
$perAppPercentage = ( 100 / count( $apps ) );
foreach ( $apps as $app )
{
if( !in_array( $app, IPS::$ipsApps ) or !isset( $data['changes'][ $step ][ $app ] ) )
{
continue;
}
if ( !isset( $stepData['app'] ) )
{
$stepData['app'] = $app;
}
if ( $stepData['app'] == $app )
{
$val = call_user_func( $code, $app, $data, $stepData );
if ( is_string( $val ) )
{
return $val;
@@ -1665,7 +1622,7 @@ class upgrade extends Controller
else
{
$percentage += $perAppPercentage;
if ( $returnNext )
{
$stepData = array( 'app' => $app );
@@ -1673,7 +1630,7 @@ class upgrade extends Controller
}
}
}
return NULL;
}