]+?)>\*/\n#', $css, $params );
/* Strip it */
if ( count( $params ) AND ! empty( $params[0] ) )
{
$css = str_replace( $params[0], '', $css );
}
$cssModule = '';
$cssApp = '';
$cssPos = 0;
$cssHidden = 0;
/* Tidy params */
if ( count( $params ) AND ! empty( $params[1] ) )
{
preg_match_all( '#([\d\w]+?)=\"([^"]+?)"#i', $params[1], $items, PREG_SET_ORDER );
foreach( $items as $id => $attr )
{
switch( trim( $attr[1] ) )
{
case 'module':
$cssModule = trim( $attr[2] );
break;
case 'app':
$cssApp = trim( $attr[2] );
break;
case 'position':
$cssPos = intval( $attr[2] );
break;
case 'hidden':
$cssHidden = intval( $attr[2] );
break;
}
}
}
$trimmedPath = trim( $path, '/' );
$finalPath = ( ( ! empty( $trimmedPath ) ) ? $trimmedPath : '.' );
$version = \IPS\Application::load( $app )->long_version;
$save = array(
'set_id' => $id,
'added_to' => 0,
'user_edited' => 0
);
/* If we're syncing designer mode, check for actual changes */
if ( $id > 0 )
{
$css = str_replace( '/* No Content */', '', $css );
if ( isset( $currentCss[ $app ][ $location ][ $finalPath ][ $file->getFilename() ] ) )
{
if( \IPS\Login::compareHashes( md5( trim( $css ) ), md5( trim( $currentCss[ $app ][ $location ][ $finalPath ][ $file->getFilename() ]['css_content'] ) ) ) )
{
/* No change */
continue;
}
else
{
/* It has changed */
$save['user_edited'] = $version;
$save['added_to'] = $id;
$save['set_id'] = $id;
}
}
else
{
/* New template bit */
$save['added_to'] = $id;
$save['set_id'] = $id;
}
}
$uniqueKey = md5( $id . ';' . $app . ';' . $location . ';' . $finalPath . ';' . $file->getFilename() );
\IPS\Db::i()->delete( 'core_theme_css', array( 'css_unique_key=?', $uniqueKey ) );
\IPS\Db::i()->insert( 'core_theme_css', array( 'css_set_id' => $save['set_id'],
'css_app' => $app,
'css_added_to' => $save['added_to'],
'css_location' => $location,
'css_path' => $finalPath,
'css_name' => $file->getFilename(),
'css_attributes' => '',
'css_content' => $css,
'css_modules' => $cssModule,
'css_position' => $cssPos,
'css_user_edited' => $save['user_edited'],
'css_updated' => time(),
'css_unique_key' => $uniqueKey,
'css_hidden' => $cssHidden ) );
}
}
}
/**
* Build Resourcess ready for non IN_DEV use
* @param string|array $app App (e.g. core, forum)
* @param int $id Theme Set Id (0 if IN_DEV and not in advanced theming mode)
*
* @return void
*/
public static function importDevResources( $app=NULL, $id )
{
foreach( new \DirectoryIterator( \IPS\ROOT_PATH . '/applications/' ) as $dir )
{
if ( $dir->isDot() || mb_substr( $dir->getFilename(), 0, 1 ) === '.' || $dir == 'index.html')
{
continue;
}
if ( $app === null OR $app == $dir->getFilename() )
{
\IPS\Theme::deleteCompiledResources( $dir->getFilename(), null, null, null, ( $id === 0 ? 1 : $id ) );
if ( $id )
{
foreach( \IPS\Db::i()->select( '*', 'core_theme_resources', array( 'resource_set_id=? and resource_plugin IS NOT NULL', $id ) ) as $resource )
{
static::$plugins[ $resource['resource_name'] ] = $resource['resource_plugin'];
}
}
\IPS\Db::i()->delete( 'core_theme_resources', array( 'resource_app=? AND resource_set_id=?', $dir->getFilename(), $id ) );
$path = static::_getResourcePath( $dir->getFilename() );
if ( is_dir( $path ) )
{
foreach( new \DirectoryIterator( $path ) as $location )
{
if ( $location->isDot() || mb_substr( $location->getFilename(), 0, 1 ) === '.' )
{
continue;
}
if ( $location->isDir() )
{
static::_importDevResources( $dir->getFilename(), $id, $location->getFilename() );
}
}
}
}
}
}
/**
* Build Resources ready for non IN_DEV use (Iterable)
* Theme resources should be raw binary data everywhere (filesystem and DB) except in the theme XML download where they are base64 encoded.
*
* @param string $app Application Key
* @param int $id Theme Set Id
* @param string $location Location Folder Name
* @param string $path Path
*
* @return void
*/
public static function _importDevResources( $app, $id, $location, $path='/' )
{
$root = static::_getResourcePath($app, $location);
$master = array();
$plugins = array();
if ( $id )
{
foreach( \IPS\Db::i()->select( '*', 'core_theme_resources', array( 'resource_set_id=0 and resource_app=? and resource_location=? and resource_path=?', $app, $location, $path ) ) as $resource )
{
$master[ $resource['resource_name'] ] = md5( $resource['resource_data'] );
}
}
foreach( new \DirectoryIterator( $root . $path ) as $file )
{
if ( $file->isDot() || mb_substr( $file->getFilename(), 0, 1 ) === '.' || $file == 'index.html' )
{
continue;
}
if ( $file->isDir() )
{
static::_importDevResources( $app, $id, $location, $path . $file->getFilename() . '/' );
}
else
{
/* files larger than 1.5mb don't base64_encode() as they may not get stored in the resources_data column */
if ( filesize( $root . $path . $file->getFilename() ) > ( 1.5 * 1024 * 1024 ) )
{
\IPS\Log::log( $root . $path . $file->getFilename() . " too large to import", 'designers_mode_import' );
continue;
}
$content = file_get_contents( $root . $path . $file->getFilename() );
if ( ! base64_encode( $content ) )
{
\IPS\Log::log( $root . $path . $file->getFilename() . " could not be saved correctly", 'designers_mode_import' );
continue;
}
$custom = 0;
$name = self::makeBuiltTemplateLookupHash($app, $location, $path) . '_' . $file->getFilename();
$fileName = (string) \IPS\File::create( 'core_Theme', $name, $content, 'set_resources_' . ( $id == 0 ? 1 : $id ) );
if ( ! isset( $master[ $file->getFilename() ] ) or !\IPS\Login::compareHashes( md5( $content ), $master[ $file->getFilename() ] ) )
{
$custom = 1;
}
\IPS\Db::i()->insert( 'core_theme_resources', array(
'resource_set_id' => ( $id == 0 ? 1 : $id ),
'resource_app' => $app,
'resource_location' => $location,
'resource_path' => $path,
'resource_name' => $file->getFilename(),
'resource_added' => time(),
'resource_data' => $content,
'resource_filename' => $fileName,
'resource_plugin' => ( ( $app == 'core' and $location == 'global' and $path == '/plugins/' ) and isset( static::$plugins[ $file->getFilename() ] ) ) ? static::$plugins[ $file->getFilename() ] : NULL,
'resource_user_edited' => $custom
) );
/* Store in master table */
if ( $id == 0 )
{
\IPS\Db::i()->insert( 'core_theme_resources', array(
'resource_set_id' => 0,
'resource_app' => $app,
'resource_location' => $location,
'resource_path' => $path,
'resource_name' => $file->getFilename(),
'resource_added' => time(),
'resource_data' => $content,
'resource_filename' => ''
) );
}
}
}
}
/**
* Writes the /application/{app}/dev/{container}/ directory
*
* @param string $app Application Directory
* @param string $container Container directory (e.g. html/css/resources)
* @return string Path created
* @throws \RuntimeException
*/
protected static function _writeThemeContainerDirectory( $app, $container )
{
$dirToWrite = \IPS\ROOT_PATH . "/applications/" . $app . '/dev/' . $container;
if ( ! is_dir( $dirToWrite ) )
{
if ( ! @mkdir( $dirToWrite ) )
{
throw new \RuntimeException('core_theme_dev_cannot_make_dir,' . $dirToWrite);
}
else
{
@chmod( $dirToWrite, IPS_FOLDER_PERMISSION );
}
}
/* Check its writeable */
if ( ! is_writeable( $dirToWrite ) )
{
throw new \RuntimeException('core_theme_dev_not_writeable,' . $dirToWrite);
}
/* Make sure root directory is CHMOD correctly */
@chmod( \IPS\ROOT_PATH . "/applications/" . $app . '/dev', 0777 );
return $dirToWrite;
}
/**
* Writes the /application/{app}/dev/{container}/{path} directory
*
* @param string $app Application Directory
* @param string $container Path to create (e.g. admin, front)
* @return string Path created
* @throws \RuntimeException
*/
protected static function _writeThemePathDirectory( $app, $container, $path )
{
$dirToWrite = \IPS\ROOT_PATH . "/applications/" . $app . '/dev/' . $container . '/' . $path;
if ( ! is_dir( $dirToWrite ) )
{
if ( ! @mkdir( $dirToWrite ) )
{
throw new \RuntimeException('core_theme_dev_cannot_make_dir,' . $dirToWrite);
}
else
{
@chmod( $dirToWrite, IPS_FOLDER_PERMISSION );
}
}
/* Check its writeable */
if ( ! is_writeable( $dirToWrite ) )
{
throw new \RuntimeException('core_theme_dev_not_writeable,' . $dirToWrite);
}
return $dirToWrite;
}
/**
* Write skin resources
* Theme resources should be raw binary data everywhere (filesystem and DB) except in the theme XML download where they are base64 encoded.
*
* @param string $app Application Directory
* @return void
* @throws \RuntimeException
*/
public static function exportResources( $app )
{
try
{
self::_writeThemeContainerDirectory( $app, 'img' );
}
catch( \RuntimeException $e )
{
throw new \RuntimeException( $e->getMessage() );
}
foreach( \IPS\Db::i()->select( '*', 'core_theme_resources', array( 'resource_app=? AND resource_set_id=?', $app, 1 ) )->setKeyField('resource_id') as $resourceId => $resource )
{
try
{
$pathToWrite = self::_writeThemePathDirectory( $app, 'img', $resource['resource_location'] );
}
catch( \RuntimeException $e )
{
throw new \RuntimeException( $e->getMessage() );
}
if ( $resource['resource_path'] != '/' )
{
$_path = '';
foreach( explode( '/', trim( $resource['resource_path'], '/' ) ) as $dir )
{
$_path .= '/' . trim( $dir, '/' );
try
{
$pathToWrite = self::_writeThemePathDirectory( $app, 'img', $resource['resource_location'] . $_path );
}
catch( \RuntimeException $e )
{
throw new \RuntimeException( $e->getMessage() );
}
}
}
try
{
if ( ! @\file_put_contents( $pathToWrite . '/' . $resource['resource_name'], $resource['resource_data'] ) )
{
throw new \RuntimeException('core_theme_dev_cannot_write_resource,' . $pathToWrite . '/' . $resource['resource_name']);
}
else
{
@chmod( $pathToWrite . '/' . $resource['resource_name'], 0777 );
}
}
catch( \InvalidArgumentException $e )
{
}
}
}
/**
* Write CSS into the appropriate theme directory as plain text CSS ({resource="foo.png"} intact)
*
* @param string $app Application Directory
* @return void
* @throws \RuntimeException
*/
public static function exportCss( $app )
{
try
{
self::_writeThemeContainerDirectory( $app, 'css' );
}
catch( \RuntimeException $e )
{
throw new \RuntimeException( $e->getMessage() );
}
$css = static::master()->getRawCss();
foreach( $css as $appDir => $data )
{
foreach( $css[ $appDir ] as $location => $data )
{
try
{
$pathToWrite = self::_writeThemePathDirectory( $app, 'css', $location );
}
catch( \RuntimeException $e )
{
throw new \RuntimeException( $e->getMessage() );
}
foreach( $css[ $appDir ][ $location ] as $path => $data )
{
if ( $path != '.' )
{
$_path = $path;
if ( \strstr( $path, '/' ) )
{
$_path = '';
foreach( explode( '/', $path ) as $dir )
{
$_path .= '/' . trim( $dir, '/' );
try
{
$pathToWrite = self::_writeThemePathDirectory( $app, 'css', $location . $_path );
}
catch( \RuntimeException $e )
{
throw new \RuntimeException( $e->getMessage() );
}
}
}
else
{
try
{
$pathToWrite = self::_writeThemePathDirectory( $app, 'css', $location . '/' . $path );
}
catch( \RuntimeException $e )
{
throw new \RuntimeException( $e->getMessage() );
}
}
}
foreach( $css[ $appDir ][ $location ][ $path ] as $name => $data )
{
$params = array();
$write = '';
if ( $data['css_hidden'] )
{
$params[] = 'hidden="1"';
}
if ( count( $params ) )
{
$write .= '/**/' . "\n";
}
$write .= ( empty( $data['css_content'] ) ) ? '/* No Content */' : $data['css_content'];
if ( ! @\file_put_contents( $pathToWrite . '/' . $data['css_name'], $write ) )
{
throw new \RuntimeException('core_theme_dev_cannot_write_css,' . $pathToWrite . '/' . $data['css_name']);
}
else
{
@chmod( $pathToWrite . '/' . $data['css_name'], 0777 );
}
}
}
}
}
}
/**
* Write templates into the appropriate theme directory as plain text templates ({{logic}} intact)
*
* @param string $app Application Directory
* @return void
* @throws \RuntimeException
*/
public static function exportTemplates( $app )
{
try
{
self::_writeThemeContainerDirectory( $app, 'html' );
}
catch( \RuntimeException $e )
{
throw new \RuntimeException( $e->getMessage() );
}
$templates = static::master()->getRawTemplates();
foreach( $templates as $appDir => $data )
{
foreach( $templates[ $app ] as $location => $data )
{
try
{
self::_writeThemePathDirectory( $app, 'html', $location );
}
catch( \RuntimeException $e )
{
throw new \RuntimeException( $e->getMessage() );
}
foreach( $templates[ $app ][ $location ] as $group => $data )
{
try
{
$pathToWrite = self::_writeThemePathDirectory( $app, 'html', $location . '/' . $group );
}
catch( \RuntimeException $e )
{
throw new \RuntimeException( $e->getMessage() );
}
foreach( $templates[ $app ][ $location ][ $group ] as $name => $data )
{
$write = '' . "\n";
$write .= $data['template_content'];
if ( ! @\file_put_contents( $pathToWrite . '/' . $data['template_name'] . '.phtml', $write ) )
{
throw new \RuntimeException('core_theme_dev_cannot_write_template,' . $pathToWrite . '/' . $data['css_name']);
}
else
{
@chmod( $pathToWrite . '/' . $data['template_name'] . '.phtml', 0777 );
}
}
}
}
}
}
/**
* Write JSON file so dev installs are synced
*
* @return void
*/
public static function writeThemeSettingsToDisk()
{
$data = array();
foreach (
\IPS\Db::i()->select(
'sv.*, core_theme_settings_fields.*',
'core_theme_settings_fields',
array( 'sc_set_id=?', 1 )
)->join(
array('core_theme_settings_values', 'sv'),
'sv.sv_id=core_theme_settings_fields.sc_id'
)->setKeyField('sc_key') as $row
)
{
$row['sc_default'] = ( $row['sv_value'] ) ? $row['sv_value'] : $row['sc_default'];
unset( $row['sc_id'], $row['sc_set_id'], $row['sc_updated'], $row['sv_id'], $row['sv_value'] );
$data[ $row['sc_app'] ][] = $row;
}
foreach( $data as $app => $json )
{
$file = \IPS\ROOT_PATH . "/applications/{$app}/data/themesettings.json";
if( @\file_put_contents( $file, version_compare( PHP_VERSION, '5.4.0' ) >= 0 ? json_encode( $json, JSON_PRETTY_PRINT ) : json_encode( $json ) ) === FALSE )
{
\IPS\Output::i()->error( 'dev_could_not_write_data', '1C103/4', 403, '' );
}
}
}
/**
* Remove an entire theme directory
*
* @param string $dir Path
* @return void
*/
public static function removeThemeDirectory( $dir )
{
if ( is_dir( $dir ) )
{
foreach ( new \DirectoryIterator( $dir ) as $f )
{
if ( !$f->isDot() )
{
if ( $f->isDir() )
{
static::removeThemeDirectory( $f->getPathname() );
}
else
{
@unlink( $f->getPathname() );
}
}
}
$handle = opendir( $dir );
closedir( $handle );
rmdir( $dir );
}
}
/**
* Returns the namespace for the template class
* @return string
*/
protected static function _getTemplateNamespace()
{
return 'IPS\\Theme\\Dev\\';
}
/**
* Returns the path for the IN_DEV .phtml files
* @param string $app Application Key
* @param string|null $location Location
* @param string|null $path Path or Filename
* @return string
*/
protected static function _getHtmlPath( $app, $location=null, $path=null )
{
return rtrim( \IPS\ROOT_PATH . "/applications/{$app}/dev/html/{$location}/{$path}", '/' ) . '/';
}
/**
* Returns the path for the IN_DEV CSS file
* @param string $app Application Key
* @param string|null $location Location
* @param string|null $path Path or Filename
* @return string
*/
protected static function _getCssPath( $app, $location=null, $path=null )
{
return rtrim( \IPS\ROOT_PATH . "/applications/{$app}/dev/css/{$location}/{$path}", '/' ) . ( \stristr( $path, '.css' ) ? '' : '/' );
}
/**
* Returns the path for the IN_DEV resource files
* @param string $app Application Key
* @param string|null $location Location
* @param string|null $path Path or Filename
* @return string
*/
protected static function _getResourcePath( $app, $location=null, $path=null )
{
if ( $app == 'core' and $location == 'global' and mb_substr( $path, 0, mb_strpos( $path, '/' ) ) === 'plugins' )
{
foreach ( new \GlobIterator( \IPS\ROOT_PATH . '/plugins/*/dev/resources/' . mb_substr( $path, mb_strpos( $path, '/' ) + 1 ) ) as $file )
{
return $file->getPathName();
}
}
return rtrim( \IPS\ROOT_PATH . "/applications/{$app}/dev/resources/{$location}/{$path}", '/' ) . ( ( \stristr( $path, '.' ) || \stristr( $path, '{' ) ) ? '' : '/' );
}
/**
* Basic debugging output functionality
*
* @param \Exception $e Exception that we are outputting
* @return void
*/
public static function varDumpException( $e )
{
echo '';
print_r( $e );
exit;
}
}