Version 5.0.0 beta 1

This commit is contained in:
Neo committed 2025-12-19 16:27:35 -08:00
1 parent 25ddeb65d6
commit 15c7beabc5
6736 files changed
+627902 -497943

No files matched your search

@@ -12,23 +12,34 @@
namespace IPS\cms\Theme\Advanced;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use DirectoryIterator;
use IPS\cms\Theme as SystemTheme;
use IPS\Data\Store;
use IPS\Db;
use IPS\Theme\Dev\Theme as DevTheme;
use IPS\Theme\Setup\Theme as SetupTheme;
use function count;
use function defined;
use function strtolower;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Designer's Mode Theme
*/
class _Theme extends \IPS\cms\Theme
class Theme extends SystemTheme
{
/**
* Get currently logged in member's theme
*
* @return static
* @return static|SetupTheme|DevTheme
*/
public static function i()
public static function i() : static|SetupTheme|DevTheme
{
return new self;
}
@@ -38,9 +49,9 @@ class _Theme extends \IPS\cms\Theme
*
* @return void
*/
public static function export()
public static function export() : void
{
foreach( \IPS\Db::i()->select(
foreach( Db::i()->select(
"*, MD5( CONCAT(template_location, ',', template_group, ',', template_title) ) as bit_key",
'cms_templates',
NULL,
@@ -56,20 +67,20 @@ class _Theme extends \IPS\cms\Theme
*
* @return void
*/
public static function import()
public static function import() : void
{
$seen = array();
foreach( array( 'block', 'page', 'database', 'js', 'css' ) as $location )
{
$templates = iterator_to_array( \IPS\Db::i()->select(
$templates = iterator_to_array( Db::i()->select(
"*, MD5( CONCAT(template_location, ',', template_group, ',', template_title) ) as bit_key",
'cms_templates',
array( 'template_location=?', $location ),
'template_user_edited ASC' /* Ensure we get edited versions, not the master version if one exists */
)->setKeyField('bit_key') );
$master = iterator_to_array( \IPS\Db::i()->select(
$master = iterator_to_array( Db::i()->select(
"*, MD5( CONCAT(template_location, ',', template_group, ',', template_title) ) as bit_key",
'cms_templates',
array( 'template_master=1 and template_location=?', $location )
@@ -79,7 +90,7 @@ class _Theme extends \IPS\cms\Theme
if ( is_dir( $path ) )
{
foreach( new \DirectoryIterator( $path ) as $group )
foreach( new DirectoryIterator( $path ) as $group )
{
if ( $group->isDot() || mb_substr( $group->getFilename(), 0, 1 ) === '.' )
{
@@ -90,7 +101,7 @@ class _Theme extends \IPS\cms\Theme
{
$seen = array();
foreach( new \DirectoryIterator( $path . '/' . $group->getFilename() ) as $file )
foreach( new DirectoryIterator( $path . '/' . $group->getFilename() ) as $file )
{
if ( $file->isDot() || ( mb_substr( $file->getFilename(), -6 ) !== '.phtml' and mb_substr( $file->getFilename(), -3 ) !== '.js' and mb_substr( $file->getFilename(), -4 ) !== '.css' ) )
{
@@ -136,7 +147,7 @@ class _Theme extends \IPS\cms\Theme
if ( ! $templates[ $key ]['template_master'] )
{
$added = TRUE;
\IPS\Db::i()->update( 'cms_templates', array(
Db::i()->update( 'cms_templates', array(
'template_content' => $html,
'template_params' => ( isset($params[1]) ) ? $params[1] : '',
'template_file_object' => NULL
@@ -158,7 +169,7 @@ class _Theme extends \IPS\cms\Theme
}
/* New template */
\IPS\Db::i()->insert( 'cms_templates', array(
Db::i()->insert( 'cms_templates', array(
'template_key' => $location . '_' . $group->getFilename() . '_' . $title,
'template_title' => $title,
'template_desc' => '',
@@ -176,19 +187,19 @@ class _Theme extends \IPS\cms\Theme
}
/* remove compiled version */
$key = \strtolower( 'template_cms_' .static::makeBuiltTemplateLookupHash( 'cms', $location, $group->getFilename() ) . '_' . static::cleanGroupName( $group->getFilename() ) );
$key = strtolower( 'template_cms_' .static::makeBuiltTemplateLookupHash( 'cms', $location, $group->getFilename() ) . '_' . static::cleanGroupName( $group->getFilename() ) );
if ( isset( \IPS\Data\Store::i()->$key ) )
if ( isset( Store::i()->$key ) )
{
unset(\IPS\Data\Store::i()->$key);
unset(Store::i()->$key);
}
}
}
/* Remove any templates we've not imported for this location/group */
if ( \count( $seen ) )
if ( count( $seen ) )
{
\IPS\Db::i()->delete( 'cms_templates', array('template_master=0 and template_location=? and template_group=? and ' . \IPS\Db::i()->in( 'template_title', $seen, TRUE ), $location, $group->getFilename() ) );
Db::i()->delete( 'cms_templates', array('template_master=0 and template_location=? and template_group=? and ' . Db::i()->in( 'template_title', $seen, TRUE ), $location, $group->getFilename() ) );
}
}
}
@@ -202,7 +213,7 @@ class _Theme extends \IPS\cms\Theme
* @param string|null $path Path or Filename
* @return string
*/
protected static function _getHtmlPath( $app, $location=null, $path=null )
protected static function _getHtmlPath( string $app, ?string $location=null, ?string $path=null ) : string
{
return rtrim( \IPS\ROOT_PATH . "/themes/cms/{$location}/{$path}", '/' ) . '/';
}
+35 -31
View File
@@ -12,31 +12,40 @@
namespace IPS\cms\Theme;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use BadMethodCallException;
use IPS\cms\Theme;
use IPS\Db;
use IPS\Theme\Template as ThemeTemplate;
use UnderflowException;
use function defined;
use function function_exists;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Magic Template Class for IN_DEV mode
*/
class _Template extends \IPS\Theme\Template
class Template extends ThemeTemplate
{
/**
* @brief Source Folder
*/
public $sourceFolder = NULL;
public ?string $sourceFolder = NULL;
/**
* Extract the data object from the param tag
* @note We always assume the tag is in the format of <ips:template parameters="" data="" />
*
* @param string $tag <ips:template> tag
* @param string $key Data key to fetch
* @return null|array
* @param string $tag <ips:template> tag
* @param string $key Data key to fetch
* @return string|null
*/
public static function extractDataFromTag( $tag, $key="data" )
public static function extractDataFromTag( string $tag, string $key="data" ): ?string
{
if ( ! preg_match( '/^<ips:template parameters="(.+?)?"([^>]+?)>(\r\n?|\n)/', $tag, $matches ) )
{
@@ -55,10 +64,10 @@ class _Template extends \IPS\Theme\Template
* Extract the data object from the param tag
* @note We always assume the tag is in the format of <ips:template parameters="" data="" />
*
* @param string $tag <ips:template> tag
* @return null|array
* @param string $tag <ips:template> tag
* @return null|string
*/
public static function extractParamsFromTag( $tag )
public static function extractParamsFromTag( string $tag ): ?string
{
if ( ! preg_match( '/^<ips:template parameters="(.+?)?"([^>]+?)>(\r\n?|\n)/', $tag, $matches ) )
{
@@ -76,18 +85,14 @@ class _Template extends \IPS\Theme\Template
* @param string $templateName Template Name
* @return void
*/
public function __construct( $app, $templateLocation, $templateName )
public function __construct( string $app, string $templateLocation, string $templateName )
{
parent::__construct( $app, $templateLocation, $templateName );
$this->app = $app;
$this->templateLocation = $templateLocation;
$this->templateName = $templateName;
if ( \IPS\Theme::designersModeEnabled() )
{
$this->sourceFolder = \IPS\ROOT_PATH . "/themes/cms/{$templateLocation}/{$templateName}/";
}
else if ( \IPS\IN_DEV )
if ( \IPS\IN_DEV )
{
$this->sourceFolder = \IPS\ROOT_PATH . "/applications/{$app}/dev/html/{$templateLocation}/{$templateName}/";
}
@@ -96,36 +101,35 @@ class _Template extends \IPS\Theme\Template
/**
* Magic Method: Call Template Bit
*
* @param string $bit Template Bit Name
* @param array $params Parameters
* @return string
* @param string $bit Template Bit Name
* @param array $params Parameters
*/
public function __call( $bit, $params )
public function __call( string $bit, array $params )
{
if ( \IPS\IN_DEV or \IPS\Theme::designersModeEnabled() )
if ( \IPS\IN_DEV )
{
/* What are we calling this? */
$functionName = "theme_{$this->app}_{$this->templateLocation}_{$this->templateName}_{$bit}";
/* If it doesn't exist, build it */
if( !\function_exists( 'IPS\\Theme\\'.$functionName ) )
if( !function_exists( 'IPS\\Theme\\'.$functionName ) )
{
/* Find the file */
$file = $this->sourceFolder . $bit . '.phtml';
/* Get the content */
if ( $file === NULL or !file_exists( $file ) )
if ( !file_exists( $file ) )
{
/* Try the database */
try
{
$template = \IPS\Db::i()->select( '*', 'cms_templates', array( 'template_location=? and LOWER(template_group)=? and template_title=?', $this->templateLocation, $this->templateName, $bit ) )->first();
$template = Db::i()->select( '*', 'cms_templates', array( 'template_location=? and LOWER(template_group)=? and template_title=?', $this->templateLocation, $this->templateName, $bit ) )->first();
\IPS\cms\Theme::makeProcessFunction( $template['template_content'], $functionName, $template['template_params'], TRUE );
Theme::makeProcessFunction( $template['template_content'], $functionName, $template['template_params'], TRUE );
}
catch ( \UnderflowException $e )
catch ( UnderflowException $e )
{
throw new \BadMethodCallException( 'NO_TEMPLATE_FILE - ' . $file );
throw new BadMethodCallException( 'NO_TEMPLATE_FILE - ' . $file );
}
}
else
@@ -136,14 +140,14 @@ class _Template extends \IPS\Theme\Template
/* Parse the header tag */
if ( !preg_match( '/^<ips:template parameters="(.+?)?"([^>]+?)>(\r\n?|\n)/', $output, $matches ) )
{
throw new \BadMethodCallException( 'NO_HEADER - ' . $file );
throw new BadMethodCallException( 'NO_HEADER - ' . $file );
}
/* Strip it */
$output = preg_replace( '/^<ips:template parameters="(.+?)?"([^>]+?)>(\r\n?|\n)/', '', $output );
/* Make it into a lovely function */
\IPS\cms\Theme::makeProcessFunction( $output, $functionName, ( isset( $matches[1] ) ? $matches[1] : '' ), TRUE );
Theme::makeProcessFunction( $output, $functionName, ( $matches[1] ?? '' ), TRUE );
}
}
+136 -159
View File
@@ -12,29 +12,59 @@
namespace IPS\cms;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use DirectoryIterator;
use DomainException;
use ErrorException;
use IPS\Data\Store;
use IPS\Db;
use IPS\Dispatcher;
use IPS\Http\Url\Friendly;
use IPS\Log;
use IPS\Login;
use IPS\Member;
use IPS\Theme as SystemTheme;
use IPS\Theme\Dev\Template;
use IPS\Theme\Dev\Theme as DevTheme;
use IPS\Theme\Setup\Theme as SetupTheme;
use ParseError;
use RuntimeException;
use UnexpectedValueException;
use function count;
use function defined;
use function file_put_contents;
use function in_array;
use function is_array;
use function is_string;
use function strtolower;
use const IPS\IPS_FILE_PERMISSION;
use const IPS\IPS_FOLDER_PERMISSION;
use const IPS\IN_DEV;
use const IPS\ROOT_PATH;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* IN_DEV Skin set
*/
class _Theme extends \IPS\Theme
class Theme extends SystemTheme
{
/**
* @brief Template Classes
*/
protected $templates;
protected ?array $templates = null;
/**
* @brief [SkinSets] Templates already loaded and evald via getTemplate()
*/
public static $calledTemplates = array();
public static array $calledTemplates = array();
/**
* @brief Return type for getRawTemplates/getRawCss: Uses DB if not IN_DEV, otherwise uses disk .phtml look up
* @brief Return type for getAllTemplates/getRawCss: Uses DB if not IN_DEV, otherwise uses disk .phtml look up
*/
const RETURN_AS_OBJECT = 32;
@@ -69,71 +99,43 @@ class _Theme extends \IPS\Theme
const RETURN_BLOCK = 2048;
/**
* @brief Return just contents of cms_templates ignoring IN_DEV and DESIGNERS' MODE
* @brief Return just contents of cms_templates ignoring IN_DEV
*/
const RETURN_DATABASE_ONLY = 4096;
/**
* Get currently logged in member's theme
*
* @return \IPS\Theme
* @return static|DevTheme|SystemTheme
*/
public static function i()
public static function i(): static|SetupTheme|DevTheme
{
if ( \IPS\Theme::designersModeEnabled() )
{
return new \IPS\cms\Theme\Advanced\Theme;
}
else
{
return new self;
}
return new self;
}
/**
* Imports templates from the /dev directories.
*
* @param string $location Location (database, block)
* @param string $location Location (database, block)
* @return void
*/
public static function importInDev( $location )
public static function importFromFiles( string $location ): void
{
/* Clear out existing template bits */
\IPS\Db::i()->delete( 'cms_templates', array( 'template_master=1 and template_user_created=0 and template_user_edited=0 AND template_location=?', $location ) );
Db::i()->delete( 'cms_templates', array( 'template_master=1 and template_user_created=0 and template_user_edited=0 AND template_location=?', $location ) );
static::importLocation( $location );
}
/**
* Write IN_DEV files
*
* @param boolean $force TRUE to rewrite templates, FALSE to check if exists first
* @return void
*/
public static function writeInDev( $force=FALSE )
{
foreach( \IPS\Db::i()->select( '*', 'cms_templates', array( 'template_master=?', 1 ) ) as $template )
{
try
{
static::writeTemplate( $template, $force );
}
catch( \RuntimeException $ex )
{
throw new \RuntimeException( $ex->getMessage() );
}
}
}
/**
* Imports templates from the /dev directories.
*
* @param string $location Location (database, block)
* @param string $location Location (database, block)
* @return array
*/
public static function importLocation( $location )
public static function importLocation( string $location ): array
{
$master = iterator_to_array( \IPS\Db::i()->select(
$master = iterator_to_array( Db::i()->select(
"*, MD5( CONCAT(template_location, ',', template_group, ',', template_title) ) as bit_key",
'cms_templates',
array( 'template_master=1 and template_user_created=0 and template_user_edited=0 AND template_location=?', $location )
@@ -144,7 +146,7 @@ class _Theme extends \IPS\Theme
if ( is_dir( $path ) )
{
foreach( new \DirectoryIterator( $path ) as $group )
foreach( new DirectoryIterator( $path ) as $group )
{
if ( $group->isDot() || mb_substr( $group->getFilename(), 0, 1 ) === '.' || $group->getFilename() == 'index.html' )
{
@@ -153,7 +155,7 @@ class _Theme extends \IPS\Theme
if ( $group->isDir() )
{
foreach( new \DirectoryIterator( $path . '/' . $group->getFilename() ) as $file )
foreach( new DirectoryIterator( $path . '/' . $group->getFilename() ) as $file )
{
if ( $file->isDot() || mb_substr( $file->getFilename(), -6 ) !== '.phtml')
{
@@ -175,7 +177,7 @@ class _Theme extends \IPS\Theme
if ( isset( $master[ $key ] ) )
{
if( \IPS\Login::compareHashes( md5( trim( $master[ $key ]['template_content'] ) ), md5( trim( $html ) ) ) )
if( Login::compareHashes( md5( trim( $master[ $key ]['template_content'] ) ), md5( trim( $html ) ) ) )
{
continue;
}
@@ -184,14 +186,14 @@ class _Theme extends \IPS\Theme
$seen[ $group->getFilename() ] = $title;
/* remove compiled version */
$key = \strtolower( 'template_cms_' .static::makeBuiltTemplateLookupHash( 'cms', $location, $group->getFilename() ) . '_' . static::cleanGroupName( $group->getFilename() ) );
$key = strtolower( 'template_cms_' .static::makeBuiltTemplateLookupHash( 'cms', $location, $group->getFilename() ) . '_' . static::cleanGroupName( $group->getFilename() ) );
if ( isset( \IPS\Data\Store::i()->$key ) )
if ( isset( Store::i()->$key ) )
{
unset(\IPS\Data\Store::i()->$key);
unset(Store::i()->$key);
}
\IPS\Db::i()->insert( 'cms_templates', array(
Db::i()->insert( 'cms_templates', array(
'template_key' => $location . '_' . $group->getFilename() . '_' . $title,
'template_title' => $title,
'template_desc' => '',
@@ -217,30 +219,30 @@ class _Theme extends \IPS\Theme
* @param array $template Template to write
* @param boolean $force Force overwrite
* @return void
* @throws \RuntimeException
* @throws RuntimeException
*/
public static function writeTemplate( $template, $force=FALSE )
public static function writeTemplate( array $template, bool $force=FALSE ) : void
{
$path = static::_getHtmlPath('cms');
if ( ! is_dir( $path ) )
{
if ( ! mkdir( $path, \IPS\IPS_FOLDER_PERMISSION, TRUE ) )
if ( ! mkdir( $path, IPS_FOLDER_PERMISSION, TRUE ) )
{
throw new \DomainException();
throw new DomainException();
}
}
if ( ! is_dir( $path . '/' . $template['template_location'] ) )
{
mkdir( $path . '/' . $template['template_location'] );
@chmod( $path . '/' . $template['template_location'], \IPS\IPS_FOLDER_PERMISSION );
@chmod( $path . '/' . $template['template_location'], IPS_FOLDER_PERMISSION );
}
if ( ! is_dir( $path . '/' . $template['template_location'] . '/' . $template['template_group'] ) )
{
mkdir( $path . '/' . $template['template_location'] . '/' . $template['template_group'] );
@chmod( $path . '/' . $template['template_location'] . '/' . $template['template_group'], \IPS\IPS_FOLDER_PERMISSION );
@chmod( $path . '/' . $template['template_location'] . '/' . $template['template_group'], IPS_FOLDER_PERMISSION );
}
$fileName = ( $template['template_type'] === 'template' ) ? $template['template_title'] . '.phtml' : $template['template_title'];
@@ -256,13 +258,13 @@ class _Theme extends \IPS\Theme
$write .= $template['template_content'];
if ( @\file_put_contents( $path . '/' . $template['template_location'] . '/' . $template['template_group'] . '/' . $fileName, $write ) === FALSE )
if ( @file_put_contents( $path . '/' . $template['template_location'] . '/' . $template['template_group'] . '/' . $fileName, $write ) === FALSE )
{
throw new \RuntimeException( \IPS\Member::loggedIn()->language()->addToStack( 'content_theme_dev_cannot_write_template', FALSE, array( 'sprintf' => array( $path . '/' . $template['template_location'] . '/' . $template['template_group'] . '/' . $fileName ) ) ) );
throw new RuntimeException( Member::loggedIn()->language()->addToStack( 'content_theme_dev_cannot_write_template', FALSE, array( 'sprintf' => array( $path . '/' . $template['template_location'] . '/' . $template['template_group'] . '/' . $fileName ) ) ) );
}
else
{
@chmod( $path . '/' . $template['template_location'] . '/' . $template['template_group'] . '/' . $fileName, \IPS\IPS_FILE_PERMISSION );
@chmod( $path . '/' . $template['template_location'] . '/' . $template['template_group'] . '/' . $fileName, IPS_FILE_PERMISSION );
}
}
}
@@ -270,31 +272,31 @@ class _Theme extends \IPS\Theme
/**
* Get raw templates. Raw means HTML logic and variables are still in {{format}}
*
* @param string|array $app Template app (e.g. core, forum)
* @param string|array $location Template location (e.g. admin,global,front)
* @param string|array $group Template group (e.g. login, share)
* @param int|constant $returnType Determines the content returned
* @param boolean $returnThisSetOnly Returns rows unique to this set only
* @param array|string $app Template app (e.g. core, forum)
* @param array|string $location Template location (e.g. admin,global,front)
* @param array|string $group Template group (e.g. login, share)
* @param int|null $returnType Determines the content returned
* @param boolean $returnThisSetOnly Returns rows unique to this set only
* @return array
*/
public function getRawTemplates( $app=array(), $location=array(), $group=array(), $returnType=null, $returnThisSetOnly=false )
public function getAllTemplates( array|string $app=array(), array|string $location=array(), array|string $group=array(), int $returnType=null, bool $returnThisSetOnly=false ): array
{
$returnType = ( $returnType === null ) ? self::RETURN_ALL : $returnType;
$app = ( \is_string( $app ) AND ! empty( $app ) ) ? array( $app ) : $app;
$location = ( \is_string( $location ) AND ! empty( $location ) ) ? array( $location ) : $location;
$group = ( \is_string( $group ) AND ! empty( $group ) ) ? array( $group ) : $group;
$app = ( is_string( $app ) AND ! empty( $app ) ) ? array( $app ) : $app;
$location = ( is_string( $location ) AND ! empty( $location ) ) ? array( $location ) : $location;
$group = ( is_string( $group ) AND ! empty( $group ) ) ? array( $group ) : $group;
$where = array();
$templates = array();
if ( ( \IPS\IN_DEV or \IPS\Theme::designersModeEnabled() ) AND ! ( $returnType & static::RETURN_DATABASE_ONLY ) )
if ( ( IN_DEV ) AND ! ( $returnType & static::RETURN_DATABASE_ONLY ) )
{
$fixedLocations = array( 'admin', 'front', 'global' );
$results = array();
$seenKeys = array();
foreach( new \DirectoryIterator( static::_getHtmlPath('cms') ) as $location )
foreach( new DirectoryIterator( static::_getHtmlPath('cms') ) as $location )
{
if ( ! \in_array( $location->getFilename(), $fixedLocations ) AND $location->isDir() AND mb_substr( $location->getFilename(), 0, 1 ) !== '.' )
if ( ! in_array( $location->getFilename(), $fixedLocations ) AND $location->isDir() AND mb_substr( $location->getFilename(), 0, 1 ) !== '.' )
{
$allowedLocations = array();
if ( $returnType & static::RETURN_ONLY_TEMPLATE )
@@ -329,31 +331,31 @@ class _Theme extends \IPS\Theme
}
}
if ( \count( $allowedLocations ) and ! \in_array( $location->getFilename(), $allowedLocations ) )
if ( count( $allowedLocations ) and ! in_array( $location->getFilename(), $allowedLocations ) )
{
continue;
}
foreach( new \DirectoryIterator( static::_getHtmlPath( 'cms', $location->getFilename() ) ) as $file )
foreach( new DirectoryIterator( static::_getHtmlPath( 'cms', $location->getFilename() ) ) as $file )
{
if ( $file->isDir() AND mb_substr( $file->getFilename(), 0, 1 ) !== '.' )
{
if ( $group === NULL or ! \count( $group ) or ( \in_array( $file->getFilename(), $group ) ) )
if ( empty( $group ) or ! count( $group ) or ( in_array( $file->getFilename(), $group ) ) )
{
foreach( new \DirectoryIterator( static::_getHtmlPath( 'cms', $location->getFilename(), $file->getFilename() ) ) as $template )
foreach( new DirectoryIterator( static::_getHtmlPath( 'cms', $location->getFilename(), $file->getFilename() ) ) as $template )
{
if ( ! $template->isDir() AND ( mb_substr( $template->getFilename(), -6 ) === '.phtml' or mb_substr( $template->getFilename(), -4 ) === '.css' or mb_substr( $template->getFilename(), -3 ) === '.js' ) )
{
$title = str_replace( ".phtml", "", $template->getFilename() );
$contents = file_get_contents( static::_getHtmlPath( 'cms', $location->getFilename(), $file->getFilename() ) . '/' . $template->getFilename() );
$key = \IPS\cms\Theme\Template::extractDataFromTag( $contents, 'key' );
$key = $key ? $key : \IPS\Http\Url\Friendly::seoTitle( $file->getFilename() . '_' . $title );
$ogroup = \IPS\cms\Theme\Template::extractDataFromTag( $contents, 'original_group' );
$params = \IPS\cms\Theme\Template::extractParamsFromTag( $contents );
$key = Theme\Template::extractDataFromTag( $contents, 'key' );
$key = $key ?: Friendly::seoTitle( $file->getFilename() . '_' . $title );
$ogroup = Theme\Template::extractDataFromTag( $contents, 'original_group' );
$params = Theme\Template::extractParamsFromTag( $contents );
$container = NULL;
if ( \in_array( $key, $seenKeys ) )
if ( in_array( $key, $seenKeys ) )
{
$key .= filemtime( static::_getHtmlPath( 'cms', $location->getFilename(), $file->getFilename() ) . '/' . $template->getFilename() ) . mt_rand();
}
@@ -364,7 +366,7 @@ class _Theme extends \IPS\Theme
if ( $returnType & static::RETURN_AS_OBJECT )
{
$object = new \IPS\cms\Templates;
$object = new Templates;
$object->key = $key;
$object->title = $title;
$object->desc = NULL;
@@ -372,7 +374,7 @@ class _Theme extends \IPS\Theme
$object->content = $contents;
$object->location = $location->getFilename();
$object->group = $file->getFilename();
$object->original_group = isset( $ogroup ) ? $ogroup : $object->group;
$object->original_group = $ogroup ?? $object->group;
$object->user_created = 0;
$object->user_edited = 0;
$object->params = $params;
@@ -389,7 +391,7 @@ class _Theme extends \IPS\Theme
'template_content' => $contents,
'template_location' => $location->getFilename(),
'template_group' => $file->getFilename(),
'template_original_group' => isset( $ogroup ) ? $ogroup : $file->getFilename(),
'template_original_group' => $ogroup ?? $file->getFilename(),
'template_user_created' => 0,
'template_user_edited' => 0,
'template_params' => $params,
@@ -416,12 +418,12 @@ class _Theme extends \IPS\Theme
}
else
{
if ( \is_array( $location ) AND \count( $location ) )
if ( is_array( $location ) AND count( $location ) )
{
$where[] = "template_location IN ('" . implode( "','", $location ) . "')";
}
if ( \is_array( $group ) AND \count( $group ) )
if ( is_array( $group ) AND count( $group ) )
{
$where[] = "template_group IN ('" . implode( "','", $group ) . "')";
}
@@ -443,7 +445,7 @@ class _Theme extends \IPS\Theme
$originalTemplateNames = array();
$groupMap = array();
foreach( \IPS\Db::i()->select( '*', 'cms_templates', implode( " AND ", $where ), 'template_location, template_group, template_key, template_user_edited ASC' ) as $row )
foreach( Db::i()->select( '*', 'cms_templates', implode( " AND ", $where ), 'template_location, template_group, template_key, template_user_edited ASC' ) as $row )
{
$rawTemplates[] = $row;
$templateNames[ $row['template_original_group'] ][] = $row['template_title'];
@@ -455,9 +457,9 @@ class _Theme extends \IPS\Theme
}
}
if ( \count( $originalGroups ) )
if ( count( $originalGroups ) )
{
foreach( \IPS\Db::i()->select( '*', 'cms_templates', array( array( 'template_original_group = template_group '), array( \IPS\Db::i()->in( 'template_original_group', $originalGroups ) ) ) ) as $row )
foreach( Db::i()->select( '*', 'cms_templates', array( array( 'template_original_group = template_group '), array( Db::i()->in( 'template_original_group', $originalGroups ) ) ) ) as $row )
{
$originalTemplates[ $row['template_group'] . '_' . $row['template_title'] ] = $row;
$originalTemplateNames[ $row['template_group'] ][] = $row['template_title'];
@@ -471,7 +473,7 @@ class _Theme extends \IPS\Theme
{
foreach( $names as $name )
{
if ( ! \in_array( $name, $templateNames[ $group ] ) )
if ( ! in_array( $name, $templateNames[ $group ] ) )
{
$rawTemplates[] = array_merge( $originalTemplates[ $group . '_' . $name ], array( 'template_group' => $groupMap[ $group ]) );
}
@@ -538,37 +540,37 @@ class _Theme extends \IPS\Theme
/**
* Get a template
*
* @param string $group Template Group
* @param string $app Application key (NULL for current application)
* @param string $location Template Location (NULL for current template location)
* @return \IPS\Theme\Template
* @param string $group Template Group
* @param string|null $app Application key (NULL for current application)
* @param string|null $location Template Location (NULL for current template location)
* @return mixed
*/
public function getTemplate( $group, $app=NULL, $location=NULL )
public function getTemplate( string $group, string $app=NULL, string $location=NULL ): mixed
{
/* Do we have an application? */
if( $app === NULL )
{
$app = \IPS\Dispatcher::i()->application->directory;
$app = Dispatcher::i()->application->directory;
}
/* How about a template location? */
if( $location === NULL )
{
$location = \IPS\Dispatcher::i()->controllerLocation;
$location = Dispatcher::i()->controllerLocation;
}
/* Get template */
if ( \IPS\IN_DEV or \IPS\Theme::designersModeEnabled() )
if ( IN_DEV )
{
if ( ! isset( $this->templates[ $app ][ $location ][ $group ] ) )
{
if ( $app === 'cms' AND ! \in_array( $location, array( 'admin', 'front', 'global' ) ) )
if ( $app === 'cms' AND ! in_array( $location, array( 'admin', 'front', 'global' ) ) )
{
$this->templates[ $app ][ $location ][ $group ] = new \IPS\cms\Theme\Template( $app, $location, $group );
$this->templates[ $app ][ $location ][ $group ] = new Template( $app, $location, $group );
}
else
{
$this->templates[ $app ][ $location ][ $group ] = \IPS\Theme::i()->getTemplate( $group, $app, $location );
$this->templates[ $app ][ $location ][ $group ] = Theme::i()->getTemplate( $group, $app, $location );
}
}
@@ -579,42 +581,42 @@ class _Theme extends \IPS\Theme
/* Group is saved clean */
$group = static::cleanGroupName( $group );
if ( ( $app !== 'cms' ) OR ( $app === 'cms' AND \in_array( $location, array( 'admin', 'front', 'global' ) ) ) )
if ( ( $app !== 'cms' ) OR ( in_array( $location, array( 'admin', 'front', 'global' ) ) ) )
{
return \IPS\Theme::i()->getTemplate( $group, $app, $location );
return SystemTheme::i()->getTemplate( $group, $app, $location );
}
$key = \strtolower( 'template_cms_' .static::makeBuiltTemplateLookupHash( $app, $location, $group ) . '_' . static::cleanGroupName( $group ) );
$key = strtolower( 'template_cms_' .static::makeBuiltTemplateLookupHash( $app, $location, $group ) . '_' . static::cleanGroupName( $group ) );
/* Still here */
if ( !\in_array( $key, array_keys( static::$calledTemplates ) ) )
if ( !in_array( $key, array_keys( static::$calledTemplates ) ) )
{
/* If we don't have a compiled template, do that now */
if ( !isset( \IPS\Data\Store::i()->$key ) )
if ( !isset( Store::i()->$key ) )
{
$this->compileTemplates( $app, $location, $group );
}
/* Still no key? */
if ( ! isset( \IPS\Data\Store::i()->$key ) )
if ( ! isset( Store::i()->$key ) )
{
\IPS\Log::log( "Template store key: {$key} missing ({$app}, {$location}, {$group})", "template_store_missing" );
Log::log( "Template store key: {$key} missing ({$app}, {$location}, {$group})", "template_store_missing" );
throw new \ErrorException('template_store_missing ' . $key);
throw new ErrorException('template_store_missing ' . $key);
}
/* Load compiled template */
$compiledGroup = \IPS\Data\Store::i()->$key;
$compiledGroup = Store::i()->$key;
try
{
if ( @eval( $compiledGroup ) === FALSE )
{
throw new \UnexpectedValueException;
throw new UnexpectedValueException;
}
}
catch ( \ParseError $e )
catch ( ParseError $e )
{
throw new \UnexpectedValueException;
throw new UnexpectedValueException;
}
/* Hooks */
@@ -633,14 +635,14 @@ class _Theme extends \IPS\Theme
* Build Templates ready for non IN_DEV use
* This fetches all templates in a group, converts HTML logic into ready to eval PHP and stores as a single PHP class per template group
*
* @param string|array $app Templates app (e.g. core, forum)
* @param string|array $location Templates location (e.g. admin,global,front)
* @param string|array $group Templates group (e.g. forms, members)
* @return void
* @param array|string|null $app Templates app (e.g. core, forum)
* @param array|string|null $location Templates location (e.g. admin,global,front)
* @param array|string|null $group Templates group (e.g. forms, members)
* @return bool
*/
public function compileTemplates( $app=null, $location=null, $group=null )
public function compileTemplates( array|string $app=null, array|string $location=null, array|string $group=null ): bool
{
$templates = $this->getRawTemplates( $app, $location, static::cleanGroupName( $group ) );
$templates = $this->getAllTemplates( $app, $location, static::cleanGroupName( $group ) );
foreach( $templates as $templateApp => $v )
{
@@ -648,37 +650,10 @@ class _Theme extends \IPS\Theme
{
foreach( $templates[ $templateApp ][ $location ] as $group => $bits )
{
/* Any template hooks? */
$templateHooks = array();
if( isset( \IPS\IPS::$hooks[ "\IPS\Theme\class_{$app}_{$location}_{$group}" ] ) AND \IPS\RECOVERY_MODE === FALSE )
{
foreach ( \IPS\IPS::$hooks[ "\IPS\Theme\class_{$app}_{$location}_{$group}" ] as $k => $data )
{
if ( !class_exists( "IPS\Theme\hook{$k}", FALSE ) )
{
try
{
if ( @eval( "namespace IPS\Theme;\n\n" . str_replace( ' extends _HOOK_CLASS_', '', file_get_contents( \IPS\ROOT_PATH . '/' . $data['file'] ) ) ) !== FALSE )
{
$class = "IPS\Theme\hook{$data['class']}";
$templateHooks = array_merge( $templateHooks, $class::hookData() );
}
}
catch ( \ParseError $e ) {}
}
}
}
/* Build all the functions */
$functions = array();
foreach( $templates[ $templateApp ][ $location ][ $group ] as $name => $data )
{
if ( isset( $templateHooks[ $name ] ) )
{
$data['template_content'] = static::themeHooks( $data['template_content'], $templateHooks[ $name ] );
}
$functions[ $name ] = static::compileTemplate( $data['template_content'], $data['template_title'], $data['template_params'], true, false, $app, $location, $group );
}
@@ -696,27 +671,29 @@ EOF;
EOF;
/* Store it */
$key = \strtolower( 'template_cms_' . static::makeBuiltTemplateLookupHash( $app, $location, $group ) . '_' . static::cleanGroupName( $group ) );
$key = strtolower( 'template_cms_' . static::makeBuiltTemplateLookupHash( $app, $location, $group ) . '_' . static::cleanGroupName( $group ) );
\IPS\Data\Store::i()->$key = $template;
Store::i()->$key = $template;
}
}
}
return TRUE;
}
/**
* Delete compiled templates
* Removes compiled templates bits for all themes that match the arguments
*
* @param string $app Application Directory (core, forums, etc)
* @param string|null $location Template location (front, admin, global, etc)
* @param string|null $group Template group (forms, messaging, etc)
* @param int|null $themeId Limit to a specific theme (and children)
* @param string|null $app Application Directory (core, forums, etc)
* @param array|string|null $location Template location (front, admin, global, etc)
* @param array|string|null $group Template group (forms, messaging, etc)
* @param int|null $themeId Limit to a specific theme (and children)
* @return void
*/
public static function deleteCompiledTemplate( $app=null, $location=null, $group=null, $themeId=null )
public static function deleteCompiledTemplate( string $app=null, array|string|null $location=null, array|string|null $group=null, int $themeId=null ): void
{
$templates = \IPS\cms\Theme::i()->getRawTemplates( $app, $location, $group );
$templates = Theme::i()->getAllTemplates( $app, $location, $group );
foreach( $templates as $templateApp => $v )
{
@@ -724,11 +701,11 @@ EOF;
{
foreach( $templates[ $templateApp ][ $location ] as $group => $bits )
{
$key = \strtolower( 'template_cms_' . static::makeBuiltTemplateLookupHash( $app, $location, $group ) . '_' . static::cleanGroupName( $group ) );
$key = strtolower( 'template_cms_' . static::makeBuiltTemplateLookupHash( $app, $location, $group ) . '_' . static::cleanGroupName( $group ) );
if ( isset( \IPS\Data\Store::i()->$key ) )
if ( isset( Store::i()->$key ) )
{
unset( \IPS\Data\Store::i()->$key );
unset( Store::i()->$key );
}
}
}
@@ -744,9 +721,9 @@ EOF;
* @param string|null $path Path or Filename
* @return string
*/
protected static function _getHtmlPath( $app, $location=null, $path=null )
protected static function _getHtmlPath( string $app, ?string $location=null, ?string $path=null ) : string
{
return rtrim( \IPS\ROOT_PATH . "/applications/{$app}/dev/html/{$location}/{$path}", '/' ) . '/';
return rtrim( ROOT_PATH . "/applications/{$app}/dev/html/{$location}/{$path}", '/' ) . '/';
}
}