Version 5.0.6
This commit is contained in:
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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user