Version 5.0.0 release

This commit is contained in:
Neo committed 2025-12-19 18:37:58 -08:00
1 parent 90e8c7e903
commit a3a4a9ccbd
651 files changed
+7275 -9481

No files matched your search

+119 -38
View File
@@ -35,6 +35,7 @@ use IPS\Member\Group;
use IPS\Node\Model;
use IPS\Patterns\ActiveRecord;
use IPS\Patterns\ActiveRecordIterator;
use IPS\Theme\Editor\Category;
use IPS\Theme\Editor\Setting;
use IPS\Theme\SandboxedTemplate;
use IPS\Theme\Dev\Theme as DevTheme;
@@ -755,7 +756,6 @@ class Theme extends Model
/* Are these core IPS apps? If so, we store these in the static directory and won't be in the resource map */
if( in_array( $app, IPS::$ipsApps ) and str_replace( '/', '', $path ) !== 'custom' )
{
$url = Url::internal( 'static/resources/' . $app . '_' . static::makeBuiltTemplateLookupHash($app, $location, $path) . '_' .$name, 'static' );
if( $noProtocol )
@@ -766,7 +766,6 @@ class Theme extends Model
return $url;
}
if ( in_array( $key, array_keys( $this->resource_map ) ) )
{
if ( $this->resource_map[ $key ] === NULL )
@@ -777,7 +776,7 @@ class Theme extends Model
{
$url = File::get( 'core_Theme', $this->resource_map[ $key ] )->url;
if( $noProtocol and $url instanceof Url)
if( $noProtocol and $url instanceof Url )
{
$url = $url->setScheme(NULL);
}
@@ -816,7 +815,7 @@ class Theme extends Model
static::unlock( $flagKey );
if( in_array( $app, IPS::$ipsApps ) )
if( in_array( $app, IPS::$ipsApps ) and $resource['resource_path'] !== '/custom/' )
{
$url = rtrim( Settings::i()->base_url, '/' ) . '/' . $resourceMap[ $key ];
}
@@ -1011,33 +1010,6 @@ class Theme extends Model
}
}
if ( $editorData = json_decode( $obj->_data['theme_editor_data'], true ) )
{
$obj->_data['theme_editor_data'] = $editorData;
if ( ! isset( $obj->_data['theme_editor_data']['header'] ) )
{
$obj->_data['theme_editor_data']['header'] = [
'logo' => 4,
'navigation' => 5,
'user' => 6,
'breadcrumb' => 7,
'search' => 9,
];
}
}
else
{
/* Set up defaults */
$obj->_data['theme_editor_data'] = [ 'header' => [
'logo' => 4,
'navigation' => 5,
'user' => 6,
'breadcrumb' => 7,
'search' => 9,
] ];
}
if ( ! is_array( $obj->_data['resource_map'] ) )
{
if( $obj->id > 0 )
@@ -1142,6 +1114,24 @@ class Theme extends Model
*/
public function getCustomCssForOutput(): string
{
/* Use pending changes, if we have any */
if ( Member::loggedIn()->isEditingTheme() and isset( Request::i()->cookie['theme_editor_vars'] ) and $cookie = json_decode( Request::i()->cookie['theme_editor_vars'], true ) )
{
if( array_key_exists( 'custom', $cookie ) )
{
/* Compile the pending CSS but don't store it */
$functionName = "css_" . uniqid();
static::makeProcessFunction( static::fixResourceTags( (string) $cookie['custom'], 'front' ), $functionName, '', FALSE, TRUE );
$fqFunc = 'IPS\\Theme\\'. $functionName;
$content = static::minifyCss( $fqFunc() );
/* Replace any <fileStore.xxx> tags in the CSS */
Output::i()->parseFileObjectUrls( $content );
return $content;
}
}
$key = $this->_id . '_theme_editor_custom_css';
if ( ! isset( Store::i()->$key ) )
{
@@ -1307,6 +1297,43 @@ class Theme extends Model
return NULL;
}
/**
* Store the default so that we can determine if this is customized or not
*
* @var array[]
*/
public static array $defaultThemeEditorData = [
'header' => [
'logo' => 4,
'navigation' => 5,
'user' => 6,
'breadcrumb' => 7,
'search' => 9,
]
];
/**
* @return array
*/
public function get_theme_editor_data() : array
{
$editorData = isset( $this->_data['theme_editor_data'] ) ? json_decode( $this->_data['theme_editor_data'], true ) : [];
if( !isset( $editorData['header'] ) )
{
$editorData['header'] = static::$defaultThemeEditorData['header'];
}
return $editorData;
}
/**
* @param mixed $val
* @return void
*/
public function set_theme_editor_data( mixed $val ) : void
{
$this->_data['theme_editor_data'] = is_array( $val ) ? json_encode( $val ) : null;
}
/**
* [Node] Return the custom badge for each row
*
@@ -1440,6 +1467,9 @@ class Theme extends Model
/* Copy logos */
$this->logo_data = NULL;
$this->copyLogosFromSet( $originalId );
/* Copy custom settings and categories */
$this->copyEditorSettings( $originalId );
Session::i()->log( 'acplogs__themeset_created', array( sprintf( Member::loggedIn()->language()->get( 'theme_clone_copy_of' ), Member::loggedIn()->language()->get( 'core_theme_set_title_' . $originalId ) ) => FALSE ) );
}
@@ -2053,6 +2083,46 @@ class Theme extends Model
$this->save();
}
/**
* Copy the editor settings and categories from the specified theme
*
* @param int $id
* @return void
*/
public function copyEditorSettings( int $id ) : void
{
$categoryMapping = [];
foreach( new ActiveRecordIterator(
Db::i()->select( '*', Category::$databaseTable, [ 'cat_set_id=?', $id ], 'cat_parent, cat_position' ),
Category::class
) as $cat )
{
/* Copy the category and change the theme ID */
$newCategory = clone $cat;
$newCategory->set_id = $this->_id;
if( $newCategory->parent and array_key_exists( $cat->parent, $categoryMapping ) )
{
$newCategory->parent = $categoryMapping[ $cat->parent ];
}
$newCategory->save();
/* Map the category IDs, we'll need them later */
$categoryMapping[ $cat->id ] = $newCategory->id;
}
/* Now loop through the settings */
foreach( new ActiveRecordIterator(
Db::i()->select( '*', Setting::$databaseTable, [ 'setting_set_id=?', $id ], 'setting_position' ),
Setting::class
) as $setting )
{
$newSetting = clone $setting;
$newSetting->set_id = $this->_id;
$newSetting->category_id = $categoryMapping[ $newSetting->category_id ] ?? 0;
$newSetting->save();
}
}
/**
* Install the theme editor settings for this theme
*
@@ -2742,7 +2812,10 @@ EOF;
{
foreach( $cookie as $group => $vars )
{
$customValues = array_merge( $customValues, $vars );
if( is_array( $vars ) )
{
$customValues = array_merge( $customValues, $vars );
}
}
}
@@ -2839,7 +2912,17 @@ EOF;
/* Parse the value based on the setting type */
try
{
$value = Setting::load( $key, 'setting_key' )->parsedValue( $value );
$setting = Setting::load( $key, 'setting_key' );
$value = $setting->parsedValue( $value );
if( $setting->type == Setting::SETTING_IMAGE )
{
if( empty( $value ) )
{
continue;
}
$value = "url('" . $value . "')";
}
}
catch( OutOfRangeException ){}
@@ -3894,9 +3977,7 @@ EOF;
if( $functionName == 'theme_core_front_global_footer' or ( $functionName == 'footer' and $app == 'core' and $location == 'front' and $group == 'global' ) )
{
$content = $content . "\n<p class='ipsCopyright'>
<span class='ipsCopyright__user'>{lang=\"copyright_line_value\"}</span>
{{if !\$licenseData = \IPS\IPS::licenseKey() or !isset(\$licenseData['products']['copyright']) or !\$licenseData['products']['copyright']}}<a rel='nofollow' title='Invision Community' href='https://www.invisioncommunity.com/'>Powered by <span translate='no'>Invision Community</span></a>{{endif}}
</p>";
<span class='ipsCopyright__user'>{lang=\"copyright_line_value\"}</span></p>";
}
/* Expand hook points */
@@ -4607,7 +4688,7 @@ PHP;
$this->custom_css = '';
$this->css_variables = null;
$this->view_options = null;
$this->theme_editor_data = json_encode( $data );
$this->theme_editor_data = $data;
$this->save();
}
@@ -4746,7 +4827,7 @@ PHP;
*/
public function isCustomized(): bool
{
if ( is_array( $this->set_theme_editor_data ) and count( $this->set_theme_editor_data ) )
if( $this->theme_editor_data !== static::$defaultThemeEditorData )
{
return true;
}