Version 4.7.0

This commit is contained in:
Neo committed 2025-12-19 16:00:35 -08:00
1 parent 517a5e1f70
commit 8ba7d43898
1933 files changed
+65613 -11332

No files matched your search

+207 -31
View File
@@ -273,9 +273,8 @@ class _Theme extends \IPS\Node\Model
if ( static::$inDevTheme === NULL )
{
static::$inDevTheme = new \IPS\Theme\Dev\Theme;
static::themes();
/* Add in the default theme properties (_data array, etc) */
$default = ( isset( static::$multitons[ \IPS\DEFAULT_THEME_ID ] ) ) ? static::$multitons[ \IPS\DEFAULT_THEME_ID ] : reset( static::$multitons );
@@ -283,6 +282,12 @@ class _Theme extends \IPS\Node\Model
{
static::$inDevTheme->$k = $v;
}
if ( static::isUsingEasyModeEditor() )
{
$setId = \intval( \IPS\Request::i()->cookie['vseThemeId'] );
static::$memberTheme = empty( $setId ) ? static::defaultTheme() : static::load( $setId );
}
}
return static::$inDevTheme;
@@ -354,7 +359,7 @@ class _Theme extends \IPS\Node\Model
$setId = static::defaultTheme();
}
if ( isset( \IPS\Request::i()->cookie['vseThemeId'] ) AND \IPS\Member::loggedIn()->isAdmin() AND \IPS\Member::loggedIn()->members_bitoptions['bw_using_skin_gen'] )
if ( static::isUsingEasyModeEditor() )
{
$setId = \intval( \IPS\Request::i()->cookie['vseThemeId'] );
@@ -455,22 +460,42 @@ class _Theme extends \IPS\Node\Model
/**
* Returns only the visible themes for the member
*
* @return array|bool
* @return array
*/
public static function getThemesWithAccessPermission()
{
$visibleThemes = array();
foreach ( static::themes() AS $themeId => $theme )
foreach( \IPS\Theme::roots() as $theme )
{
if ( $theme->canAccess() )
{
$visibleThemes[$theme->id] = $theme;
}
static::getChildrenWithAccessPermission( $theme, $visibleThemes );
}
return $visibleThemes;
}
/**
* Returns only the visible children
* @param \IPS\Theme $theme Parent theme
* @param Array $visibleThemes Visible themes array passed by reference
*
* @return void
*/
public static function getChildrenWithAccessPermission( \IPS\Theme $theme, Array &$visibleThemes )
{
foreach ( $theme->children() as $child )
{
if( $child->canAccess() )
{
$visibleThemes[ $child->id ] = $child;
static::getChildrenWithAccessPermission( $child, $visibleThemes );
}
}
}
/**
* Fetch the master theme object
@@ -1055,6 +1080,43 @@ class _Theme extends \IPS\Node\Model
}
}
}
/**
* Add in the header and footer special tags if they are not already present in globalTemplate
*
* @return void
*/
public function fixHeaderAndFooterTags()
{
/* Check the globalTemplate for the special tags we need */
try
{
$template = \IPS\Db::i()->select( '*', 'core_theme_templates', [ 'template_set_id=? AND template_app=? AND template_group=? AND template_location=? and template_name=?', $this->_id, 'core', 'global', 'front', 'globalTemplate'] )->first();
$update = FALSE;
if ( ! \stristr( $template['template_content'], '{theme="headerHtml"}') )
{
$template['template_content'] = preg_replace( '#(<body.*>\n)#i', "$1\n{theme=\"headerHtml\"}\n", $template['template_content'] );
$update = TRUE;
}
if ( ! \stristr( $template['template_content'], '{theme="footerHtml"}') )
{
$template['template_content'] = preg_replace( '#(</body>\n)#i', "{theme=\"footerHtml\"}\n$1", $template['template_content'] );
$update = TRUE;
}
if ( $update )
{
\IPS\Db::i()->update( 'core_theme_templates', [ 'template_content' => $template['template_content'] ], [ 'template_id=?', $template['template_id'] ] );
}
}
catch( \UnderflowException $e )
{
/* No custom template, so we're good */
}
}
/**
* Save Changed Columns
@@ -1073,7 +1135,22 @@ class _Theme extends \IPS\Node\Model
parent::save();
unset( \IPS\Data\Store::i()->themes );
/* If we are using a customized globalTemplate, we need to slap in the header/footer tags */
if ( ! empty( $this->custom_footer ) or ! empty( $this->customer_header ) )
{
$this->fixHeaderAndFooterTags();
}
foreach( [ 'header', 'footer' ] as $type )
{
$key = 'custom_' . $type . '_' . $this->_id;
if ( isset( \IPS\Data\Store::i()->$key ) )
{
unset( \IPS\Data\Store::i()->$key );
}
}
/* Reset map arrays */
if ( !isset( $this->_data['resource_map'] ) OR ! \is_array( $this->_data['resource_map'] ) )
{
@@ -1668,7 +1745,11 @@ class _Theme extends \IPS\Node\Model
$form->add( new \IPS\Helpers\Form\Upload( 'core_theme_set_editor_new', NULL, FALSE, array( 'allowedFileTypes' => array( 'zip' ), 'temporary' => TRUE ), NULL, NULL, NULL, 'core_theme_set_editor_new' ) );
}
}
$form->addTab('core_theme_set_header_and_footer_header');
$form->add( new \IPS\Helpers\Form\Codemirror( 'theme_simple_header_settings', $this->custom_header, FALSE, [ 'mode' => 'htmlmixed', 'height' => 400 ] ) );
$form->add( new \IPS\Helpers\Form\Codemirror( 'theme_simple_footer_settings', $this->custom_footer, FALSE, [ 'mode' => 'htmlmixed', 'height' => 400 ] ) );
/* THEME SETTINGS */
if ( $this->id )
{
@@ -2199,6 +2280,16 @@ class _Theme extends \IPS\Node\Model
$this->parent_id = ( $values['core_theme_parent_id'] instanceof \IPS\Theme ) ? $values['core_theme_parent_id']->id : 0;
$this->css_updated = time();
if ( isset( $values['theme_simple_header_settings'] ) )
{
$this->custom_header = $values['theme_simple_header_settings'];
}
if ( isset( $values['theme_simple_footer_settings'] ) )
{
$this->custom_footer = $values['theme_simple_footer_settings'];
}
$this->save();
if ( $creating === TRUE AND $this->id )
@@ -2942,11 +3033,11 @@ EOF;
}
else
{
# Remove it from ALL template sets
# Remove it from the theme set and any children
\IPS\Db::i()->delete( 'core_theme_templates', array(
"template_name=? AND template_group=? AND template_app=? AND template_location=?",
$template['template_name'], $template['template_group'], $template['template_app'], $template['template_location']
) );
"template_name=? AND template_group=? AND template_app=? AND template_location=? AND " . \IPS\Db::i()->in( 'template_set_id', $children ),
$template['template_name'], $template['template_group'], $template['template_app'], $template['template_location']
) );
}
}
else
@@ -3203,7 +3294,41 @@ EOF;
}
}
}
/**
* Get theme header and footer
*
* @return array
*/
public function getHeaderAndFooter(): array
{
$return = [ 'header' => null, 'footer' => null ];
if ( $this->custom_header )
{
$key = 'custom_header_' . $this->_id;
if ( ! isset( \IPS\Data\Store::i()->$key ) )
{
\IPS\Data\Store::i()->$key = static::compileTemplate( $this->custom_header, NULL );
}
$return['header'] = \IPS\Data\Store::i()->$key;
}
if ( $this->custom_footer )
{
$key = 'custom_footer_' . $this->_id;
if ( ! isset( \IPS\Data\Store::i()->$key ) )
{
\IPS\Data\Store::i()->$key = static::compileTemplate( $this->custom_footer, NULL );
}
$return['footer'] = \IPS\Data\Store::i()->$key;
}
return $return;
}
/**
* Get raw CSS. Raw means {resource..} tags and uncompiled
*
@@ -4383,25 +4508,25 @@ EOF;
{
$return = preg_replace( '#' . $matches[1] . '="json--' . $id . '--"#', $matches[0], $return );
}
/* Fix ---cln--- classes */
preg_match_all( '#class\s?=\s?[\'"]([^\'"]+?)[\'"]#', $return, $classes, PREG_SET_ORDER );
foreach( $classes as $idx => $data )
{
$return = str_replace( $data[1], str_replace( '---cln---', ':', $data[1] ), $return );
}
/* Put our {{PHP code}} back */
$return = preg_replace_callback( '/he-(.+?)--/', function( $matches ) use ( $phpQueryStore )
{
return isset( $phpQueryStore[ $matches[1] ] ) ? $phpQueryStore[ $matches[1] ] : '';
}, $return );
/* Fix ---cln--- classes */
preg_match_all( '#class\s?=\s?[\'"]([^\'"]+?)[\'"]#', $return, $classes, PREG_SET_ORDER );
foreach( $classes as $idx => $data )
{
$return = str_replace( $data[1], str_replace( '---cln---', ':', $data[1] ), $return );
}
/* Swap back certain tags that confuse phpQuery */
$return = preg_replace( '/<(\/)?temp(html|head|body)(.*?)>/', '<$1$2$3>', $return );
$return = str_replace( '<tempdoctype></tempdoctype>', '<!DOCTYPE html>', $return );
/* Return */
return $return;
}
@@ -4543,7 +4668,7 @@ EOF;
* Process template into executable code.
*
* @param string $content Content with variables and parse tags
* @param string $functionName Desired function name
* @param string|NULL $functionName Desired function name if given, otherwise <<<CONTENT CONTENT; is returned without a function wrapper or $return .= statement
* @param string $params Parameter list
* @param bool $isHTML If TRUE, HTML will automatically be escaped
* @param bool $isCSS If TRUE, the plugins will be checked for $canBeUsedInCss
@@ -4624,7 +4749,17 @@ PHP;
}
else
{
$function = <<<PHP
if ( ! $functionName )
{
$function = <<<PHP
<<<CONTENT\n
{$content}
CONTENT;\n
PHP;
}
else
{
$function = <<<PHP
function {$functionName}( {$params} ) {
\$return = '';
\$return .= <<<CONTENT\n
@@ -4633,6 +4768,7 @@ CONTENT;\n
return \$return;
}
PHP;
}
}
/* Parse {plugin="foo"} tags */
@@ -5499,12 +5635,7 @@ PHP;
public static function isUsingTemplateDiskCache()
{
if ( \IPS\CIC and ! \IPS\Settings::i()->theme_disk_cache_templates )
{
if ( \IPS\CIC2 )
{
return FALSE;
}
{
\IPS\Settings::i()->theme_disk_cache_templates = 1;
\IPS\Settings::i()->theme_disk_cache_path = \IPS\ROOT_PATH . '/uploads';
}
@@ -5556,5 +5687,50 @@ PHP;
{
\IPS\Db::i()->delete( 'core_cache', array( 'cache_key=?', 'locking_' . $key ) );
}
/**
* Are we actively editing an easy mode theme?
*
* @return bool
*/
public static function isUsingEasyModeEditor(): bool
{
return isset( \IPS\Request::i()->cookie['vseThemeId'] ) AND \IPS\Member::loggedIn()->isAdmin() AND \IPS\Member::loggedIn()->members_bitoptions['bw_using_skin_gen'];
}
/**
* Pre output handling for easy mode theme if actively used
*
* @return void
*/
public static function easyModePreOutput()
{
/* We are using the easy mode editor, and we're a logged in admin and the theme is an easy mode theme */
if ( static::isUsingEasyModeEditor() and static::$memberTheme->by_skin_gen )
{
$storage = \IPS\File::getClass( 'core_Theme' );
$baseUrl = parse_url( \IPS\Http\Url::internal('') );
$storageUrl = parse_url( $storage->baseUrl() );
/* is it a different host? */
if ( $storageUrl['host'] != $baseUrl['host'] )
{
$cssFiles = [];
foreach( \IPS\Output::i()->cssFiles as $css )
{
if ( $css->data['host'] == $storageUrl['host'] )
{
$cssFiles[] = \IPS\Http\Url::internal('app=core&module=system&controller=easymode&do=proxy')->setQueryString( [ 'f' => base64_encode( (string) $css ) ] );
}
else
{
$cssFiles[] = $css;
}
}
\IPS\Output::i()->cssFiles = $cssFiles;
}
}
}
}