Version 4.1.19

This commit is contained in:
Neo committed 2025-12-19 05:38:56 -08:00
1 parent 28bd025b35
commit 2bd5025018
2674 files changed
+233518 -77766

No files matched your search

@@ -2,9 +2,9 @@
/**
* @brief Customization AJAX actions
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @package IPS Community Suite
* @since 07 May 2013
* @version SVN_VERSION_NUMBER
*/
@@ -84,17 +84,7 @@ class _ajax extends \IPS\Dispatcher\Controller
{
foreach( $templateNames[ $app ][ $location ] as $path => $items )
{
if ( $path === 'custom' )
{
foreach( $items as $name => $data )
{
if ( $name !== 'custom.css' )
{
unset( $templateNames[ $app ][ $location ][ $path ][ $name ] );
}
}
}
else
if ( $path != 'custom' )
{
unset( $templateNames[ $app ][ $location ][ $path ] );
}
@@ -181,7 +171,24 @@ class _ajax extends \IPS\Dispatcher\Controller
$where = array();
if ( \IPS\Request::i()->term )
{
$where[] = array( 'LOWER(template_content) LIKE ?', '%' . mb_strtolower( \IPS\Request::i()->term ) . '%' );
$where[] = array( '( LOWER(template_name) LIKE ? OR LOWER(template_content) LIKE ? )', '%' . mb_strtolower( \IPS\Request::i()->term ) . '%', '%' . mb_strtolower( \IPS\Request::i()->term ) . '%' );
/* If searching content we need to specifically only search the templates *actually* being used */
$uniqueIds = array();
foreach ( $theme->getRawTemplates( '', '', '', \IPS\Theme::RETURN_ALL_NO_CONTENT ) as $locations )
{
foreach ( $locations as $groups )
{
foreach ( $groups as $templates )
{
foreach ( $templates as $template )
{
$uniqueIds[] = "'" . $template['template_unique_key'] . "'";
}
}
}
}
$where[] = array( 'template_unique_key IN (' . implode( ',' , $uniqueIds ) . ')' );
}
if ( in_array( 'inherited', explode( ',', \IPS\Request::i()->filters ) ) )
{
@@ -203,6 +210,10 @@ class _ajax extends \IPS\Dispatcher\Controller
{
$where[] = array( 'template_user_edited>0 or template_added_to > 0' );
}
if ( !in_array( 'modified', explode( ',', \IPS\Request::i()->filters ) ) )
{
$where[] = array( 'template_added_to=0' );
}
$where[] = array( 'template_set_id IN (' . implode( ',' , $parents ) . ')' );
$select = \IPS\Db::i()->select(
@@ -247,7 +258,24 @@ class _ajax extends \IPS\Dispatcher\Controller
$where = array();
if ( \IPS\Request::i()->term )
{
$where[] = array( 'LOWER(css_content) LIKE ?', '%' . mb_strtolower( \IPS\Request::i()->term ) . '%' );
$where[] = array( '( LOWER(css_name) LIKE ? OR LOWER(css_content) LIKE ? )', '%' . mb_strtolower( \IPS\Request::i()->term ) . '%', '%' . mb_strtolower( \IPS\Request::i()->term ) . '%' );
/* If searching content we need to specifically only search the css files *actually* being used */
$uniqueIds = array();
foreach ( $theme->getRawCss( '', '', '', \IPS\Theme::RETURN_ALL_NO_CONTENT ) as $locations )
{
foreach ( $locations as $groups )
{
foreach ( $groups as $templates )
{
foreach ( $templates as $template )
{
$uniqueIds[] = "'" . $template['css_unique_key'] . "'";
}
}
}
}
$where[] = array( 'css_unique_key IN (' . implode( ',' , $uniqueIds ) . ')' );
}
if ( in_array( 'inherited', explode( ',', \IPS\Request::i()->filters ) ) )
{
@@ -2,9 +2,9 @@
/**
* @brief Email template management
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @package IPS Community Suite
* @since 02 Jul 2013
* @version SVN_VERSION_NUMBER
*/
@@ -82,6 +82,7 @@ class _emails extends \IPS\Dispatcher\Controller
'icon' => 'undo',
'title' => 'revert',
'link' => \IPS\Http\Url::internal( 'app=core&module=customization&controller=emails&do=revert&key=' ) . $row['template_key'],
'data' => array( 'delete' => '', 'delete-warning' => \IPS\Member::loggedIn()->language()->addToStack('email_revert_confirm') )
);
$return['export'] = array(
@@ -125,7 +126,7 @@ class _emails extends \IPS\Dispatcher\Controller
/* Get the template info */
try
{
$template = \IPS\DB::i()->select( '*', 'core_email_templates', array( 'template_parent>0 AND template_key=?', \IPS\Request::i()->key ) )->first();
$template = \IPS\Db::i()->select( '*', 'core_email_templates', array( 'template_parent>0 AND template_key=?', \IPS\Request::i()->key ) )->first();
}
catch ( \UnderflowException $e )
{
@@ -182,7 +183,7 @@ class _emails extends \IPS\Dispatcher\Controller
try
{
$existing = \IPS\DB::i()->select( '*', 'core_email_templates', array( 'template_parent=0 AND template_key=?', $update['template_key'] ) )->first();
$existing = \IPS\Db::i()->select( '*', 'core_email_templates', array( 'template_parent=0 AND template_key=?', $update['template_key'] ) )->first();
}
catch ( \UnderflowException $e )
{
@@ -190,8 +191,8 @@ class _emails extends \IPS\Dispatcher\Controller
}
$update['template_parent'] = $existing['template_id'];
\IPS\DB::i()->replace( 'core_email_templates', $update );
\IPS\DB::i()->update( 'core_email_templates', array( 'template_edited' => 1 ), array( 'template_id=?', $existing['template_id'] ) );
\IPS\Db::i()->replace( 'core_email_templates', $update );
\IPS\Db::i()->update( 'core_email_templates', array( 'template_edited' => 1 ), array( 'template_id=?', $existing['template_id'] ) );
}
/* Redirect */
@@ -210,11 +211,14 @@ class _emails extends \IPS\Dispatcher\Controller
*/
public function revert()
{
/* Make sure the user confirmed the deletion */
\IPS\Request::i()->confirmedDelete();
/* Get the template info for the log */
$template = \IPS\DB::i()->select( '*', 'core_email_templates', array( 'template_parent=0 AND template_key=?', \IPS\Request::i()->key ) )->first();
$template = \IPS\Db::i()->select( '*', 'core_email_templates', array( 'template_parent=0 AND template_key=?', \IPS\Request::i()->key ) )->first();
/* Revert any user-edited copies of the specified template */
\IPS\DB::i()->delete( 'core_email_templates', array( 'template_parent>0 AND template_key=?', \IPS\Request::i()->key ) );
\IPS\Db::i()->delete( 'core_email_templates', array( 'template_parent>0 AND template_key=?', \IPS\Request::i()->key ) );
/* Reset edited flag on parent */
\IPS\Db::i()->update( 'core_email_templates', array( 'template_edited' => 0 ), array( 'template_id=?', $template['template_id'] ) );
@@ -242,7 +246,7 @@ class _emails extends \IPS\Dispatcher\Controller
try
{
$template = \IPS\DB::i()->select( '*', 'core_email_templates', array( 'template_key=?', \IPS\Request::i()->key ), 'template_parent DESC', 1 )->first();
$template = \IPS\Db::i()->select( '*', 'core_email_templates', array( 'template_key=?', \IPS\Request::i()->key ), 'template_parent DESC', 1 )->first();
}
catch ( \UnderflowException $e )
{
@@ -337,19 +341,7 @@ class _emails extends \IPS\Dispatcher\Controller
*/
public function emailPreview()
{
/* Add some dummy content */
$body = \IPS\Theme::i()->getTemplate( 'customization', 'core', 'admin' )->emailPreview();
$templateData = \IPS\Db::i()->select( '*', 'core_email_templates', array( "template_app=? AND template_name=?", 'core', 'emailWrapper' ) )->first();
$template = "namespace IPS\Theme;\n" . \IPS\Theme::compileTemplate( $templateData['template_content_html'], "email_html_core_emailWrapper", $templateData['template_data'] );
if( !function_exists( 'IPS\\Theme\\email_html_core_emailWrapper' ) )
{
eval( $template );
}
$output = call_user_func_array( 'IPS\\Theme\\email_html_core_emailWrapper', array( NULL, new \IPS\Member, $body, NULL, FALSE, '', NULL ) );
\IPS\Output::i()->sendOutput( $output, 200, 'text/html' );
$email = \IPS\Email::buildFromContent( '', \IPS\Member::loggedIn()->language()->addToStack( 'email_preview_content' ) );
\IPS\Output::i()->sendOutput( $email->compileContent( 'html' ) );
}
}
@@ -2,9 +2,9 @@
/**
* @brief themes
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @package IPS Community Suite
* @since 15 Apr 2013
* @version SVN_VERSION_NUMBER
*/
@@ -37,7 +37,19 @@ class _themes extends \IPS\Node\Controller
* Description can contain HTML?
*/
public $_descriptionHtml = TRUE;
/**
* Execute
*
* @return void
*/
public function execute()
{
\IPS\Dispatcher::i()->checkAcpPermission( 'theme_sets_manage' );
parent::execute();
}
/**
* Manage
*
@@ -51,7 +63,7 @@ class _themes extends \IPS\Node\Controller
}
/* Add a button for designer's mode */
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'customization', 'theme_designers_mode' ) )
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'customization', 'theme_designers_mode' ) and !\IPS\NO_WRITES and !\IPS\CIC )
{
\IPS\Output::i()->sidebar['actions']['designersmode'] = array(
'title' => ( \IPS\Theme::designersModeEnabled() ) ? 'theme_designers_mode_on_title' : 'theme_designers_mode_title',
@@ -154,6 +166,11 @@ class _themes extends \IPS\Node\Controller
return;
}
if ( \IPS\NO_WRITES or \IPS\CIC )
{
\IPS\Output::i()->error( 'no_writes', '1C163/9', 403, '' );
}
$form = new \IPS\Helpers\Form( 'form', 'next' );
$form->addMessage( \IPS\Member::loggedIn()->language()->addToStack('theme_designers_mode_explain', FALSE, array( 'sprintf' => array( \IPS\ROOT_PATH, \IPS\Http\Url::ips('docs/designers_mode') ) ) ), 'ipsMessage ipsMessage_general', TRUE );
@@ -207,6 +224,11 @@ class _themes extends \IPS\Node\Controller
*/
public function designersmodeoff()
{
if ( \IPS\NO_WRITES or \IPS\CIC )
{
\IPS\Output::i()->error( 'no_writes', '1C163/B', 403, '' );
}
\IPS\Output::i()->output = new \IPS\Helpers\MultipleRedirect(
\IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=designersmodeoff' ),
function( $data )
@@ -406,6 +428,11 @@ class _themes extends \IPS\Node\Controller
*/
public function designersmodeon()
{
if ( \IPS\NO_WRITES or \IPS\CIC )
{
\IPS\Output::i()->error( 'no_writes', '1C163/A', 403, '' );
}
/* Nothing to build, its all up to date */
$needsBuilding = \IPS\Theme\Advanced\Theme::getToBuild();
@@ -530,7 +557,8 @@ class _themes extends \IPS\Node\Controller
}
\IPS\Theme\Advanced\Theme::$currentThemeId = $themeId;
\IPS\Theme\Advanced\Theme::$buildingFiles = true;
foreach ( \IPS\Application::applications() as $app )
{
switch ( $type )
@@ -619,7 +647,7 @@ class _themes extends \IPS\Node\Controller
},
'resource_filename' => function( $val, $row )
{
return '{resource="' . ltrim( $row['resource_path'], '/' ) . $row['resource_name'] . '" app="' . $row['resource_app'] . '" location="' . $row['resource_location'] . '"}';
return \IPS\Theme::i()->getTemplate( 'customization' )->resourceTag( '{resource="' . ltrim( $row['resource_path'], '/' ) . $row['resource_name'] . '" app="' . $row['resource_app'] . '" location="' . $row['resource_location'] . '"}' );
}
);
@@ -675,6 +703,9 @@ class _themes extends \IPS\Node\Controller
*/
public function resourceDelete()
{
/* Make sure the user confirmed the deletion */
\IPS\Request::i()->confirmedDelete();
$id = intval( \IPS\Request::i()->id );
try
@@ -698,6 +729,12 @@ class _themes extends \IPS\Node\Controller
/* Don't delete master resources */
if ( $current['resource_set_id'] > 0 )
{
try
{
\IPS\File::get( 'core_Theme', $current['resource_filename'] )->delete();
}
catch( \Exception $ex ) { }
\IPS\Db::i()->delete( 'core_theme_resources', array( 'resource_id=?', $id ) );
}
@@ -951,7 +988,9 @@ class _themes extends \IPS\Node\Controller
/* Move it to a temporary location */
$tempFile = tempnam( \IPS\TEMP_DIRECTORY, 'IPS' );
move_uploaded_file( $values['core_theme_set_new_import'], $tempFile );
$max = \IPS\Db::i()->select( 'MAX(set_order)', 'core_themes' )->first();
/* Create a default theme */
$theme = new \IPS\Theme;
$theme->parent_array = '[]';
@@ -959,12 +998,13 @@ class _themes extends \IPS\Node\Controller
$theme->parent_id = 0;
$theme->by_skin_gen = 0;
$theme->editor_skin = 'ips';
$theme->order = $max + 1;
$theme->save();
$theme->copyResourcesFromSet();
/* Initate a redirector */
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=import&file=' . urlencode( $tempFile ) . '&key=' . md5_file( $tempFile ) . '&id=' . $theme->id) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=import' )->setQueryString( array( 'file' => $tempFile, 'key' => md5_file( $tempFile ), 'id' => $theme->id ) ) );
}
else
{
@@ -988,7 +1028,7 @@ class _themes extends \IPS\Node\Controller
}
\IPS\Output::i()->output = new \IPS\Helpers\MultipleRedirect(
\IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=import&file=' . urlencode( \IPS\Request::i()->file ) . '&key=' . \IPS\Request::i()->key . '&id=' . \IPS\Request::i()->id ),
\IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=import' )->setQueryString( array( 'file' => \IPS\Request::i()->file, 'key' => \IPS\Request::i()->key, 'id' => \IPS\Request::i()->id ) ),
function( $data )
{
$set = \IPS\Theme::load( \IPS\Request::i()->id );
@@ -996,28 +1036,34 @@ class _themes extends \IPS\Node\Controller
$templates = \IPS\Theme::load( \IPS\Request::i()->id )->getRawTemplates( null, null, null, \IPS\Theme::RETURN_ALL_NO_CONTENT, true );
$css = \IPS\Theme::load( \IPS\Request::i()->id )->getRawCss( null, null, null, \IPS\Theme::RETURN_ALL_NO_CONTENT, true );
$masterCss = \IPS\Theme::load( \IPS\Request::i()->id )->getRawCss( null, null, null, \IPS\Theme::RETURN_BIT_NAMES, false );
/* Open XML file */
$xml = new \XMLReader;
$xml = new \IPS\Xml\XMLReader;
$xml->open( \IPS\Request::i()->file );
if ( ! @$xml->read() )
{
@unlink( \IPS\Request::i()->file );
\IPS\Output::i()->error( 'xml_upload_invalid', '2C163/1', 403, '' );
}
/* Is this the first batch? */
if ( !is_array( $data ) )
{
$_SESSION['theme_import'] = array( 'css' => array(), 'isNewSet' => false );
/* Save snaphot */
\IPS\Theme::load( \IPS\Request::i()->id )->saveHistorySnapshot();
/* Wipe clean conflicts */
\IPS\Db::i()->delete( 'core_theme_conflict', array( 'conflict_set_id=?', \IPS\Request::i()->id ) );
/* No Name? */
/* No Name? Then this is a brand new theme */
if ( empty( $set->name ) )
{
$_SESSION['theme_import']['isNewSet'] = TRUE;
while ( $xml->read() )
{
if ( $xml->name == 'theme' )
@@ -1033,13 +1079,15 @@ class _themes extends \IPS\Node\Controller
}
$set->saveSet( array(
'set_name' => $xml->getAttribute('name'),
'set_author_name' => $xml->getAttribute('author_name'),
'set_author_url' => $xml->getAttribute('author_url'),
'set_version' => $xml->getAttribute('version'),
'set_long_version' => ( $xml->getAttribute('long_version') ) ? $xml->getAttribute('long_version') : \IPS\Application::load('core')->long_version,
'set_is_default' => $set->is_default,
'set_permissions' => $set->id ? $set->permissions : implode( ',', $groups )
'set_name' => $xml->getAttribute('name'),
'set_author_name' => $xml->getAttribute('author_name'),
'set_author_url' => $xml->getAttribute('author_url'),
'set_version' => $xml->getAttribute('version'),
'set_update_check' => $xml->getAttribute('update_check'),
'set_long_version' => ( $xml->getAttribute('long_version') ) ? $xml->getAttribute('long_version') : \IPS\Application::load('core')->long_version,
'set_is_default' => $set->is_default,
'set_is_acp_default' => $set->is_acp_default,
'set_permissions' => $set->id ? $set->permissions : implode( ',', $groups )
) );
if ( $xml->getAttribute('editor_skin') )
@@ -1063,6 +1111,25 @@ class _themes extends \IPS\Node\Controller
}
}
}
else
{
/* We are importing an update to a theme */
while ( $xml->read() )
{
if ( $xml->name == 'theme' )
{
$set->saveSet( array(
'set_author_name' => $xml->getAttribute('author_name'),
'set_author_url' => $xml->getAttribute('author_url'),
'set_version' => $xml->getAttribute('version'),
'set_update_check' => $xml->getAttribute('update_check'),
'set_long_version' => ( $xml->getAttribute('long_version') ) ? $xml->getAttribute('long_version') : \IPS\Application::load('core')->long_version
) );
break;
}
}
}
/* Start impoprting */
$data = array( 'apps' => array() );
@@ -1083,7 +1150,17 @@ class _themes extends \IPS\Node\Controller
{
try
{
$application = \IPS\Application::load( $appKey );
try
{
$application = \IPS\Application::load( $appKey );
}
/* Application is out of date */
catch( \UnexpectedValueException $e )
{
$xml->next();
continue;
}
if ( $application->enabled )
{
/* Import */
@@ -1093,23 +1170,35 @@ class _themes extends \IPS\Node\Controller
switch ( $xml->name )
{
case 'field':
$current = NULL;
try
{
$field = \IPS\Db::i()->select( 'sc_id', 'core_theme_settings_fields', array( 'sc_set_id=? AND sc_app=? AND sc_key=?', \IPS\Request::i()->id, $appKey, $xml->getAttribute('sc_key') ) )->first();
$current = \IPS\Db::i()->select( 'sv_value', 'core_theme_settings_values', array( 'sv_id=?', $field ) )->first();
}
catch ( \UnderflowException $e ) {}
\IPS\Db::i()->delete( 'core_theme_settings_fields', array( 'sc_set_id=? AND sc_app=? AND sc_key=?', \IPS\Request::i()->id, $appKey, $xml->getAttribute('sc_key') ) );
$default = $xml->getAttribute('sc_default');
$fieldId = \IPS\Db::i()->insert( 'core_theme_settings_fields', array(
'sc_set_id' => \IPS\Request::i()->id,
'sc_key' => $xml->getAttribute('sc_key'),
'sc_type' => $xml->getAttribute('sc_type'),
'sc_multiple' => $xml->getAttribute('sc_multiple'),
'sc_default' => $xml->getAttribute('sc_default'),
'sc_default' => $default,
'sc_content' => $xml->getAttribute('sc_content'),
'sc_app' => $appKey,
'sc_tab_key' => $xml->getAttribute('sc_tab_key'),
'sc_show_in_vse' => $xml->getAttribute('sc_show_in_vse'),
'sc_title' => $xml->getAttribute('sc_title'),
'sc_order' => (int) $xml->getAttribute('sc_order')
'sc_order' => (int) $xml->getAttribute('sc_order'),
'sc_condition' => $xml->getAttribute('sc_condition')
) );
$value = $xml->readString();
$custom = $xml->readString();
$value = ( trim( $custom ) !== "" ) ? $custom : $default;
$node = new \SimpleXMLElement( $xml->readOuterXml() );
/* Any kids of your own? */
@@ -1134,7 +1223,7 @@ class _themes extends \IPS\Node\Controller
}
\IPS\Db::i()->insert( 'core_theme_settings_values', array( 'sv_id' => $fieldId, 'sv_value' => $value ) );
\IPS\Db::i()->insert( 'core_theme_settings_values', array( 'sv_id' => $fieldId, 'sv_value' => ( $current !== NULL ) ? $current : $value ) );
break;
case 'template':
$location = $xml->getAttribute('template_location');
@@ -1184,6 +1273,9 @@ class _themes extends \IPS\Node\Controller
if ( isset( $css[ $appKey ][ $location ][ $path ][ $name ] ) AND $css[ $appKey ][ $location ][ $path ][ $name ]['css_user_edited'] )
{
/* Keep this */
$_SESSION['theme_import']['css'][] = $css[ $appKey ][ $location ][ $path ][ $name ]['css_id'];
\IPS\Db::i()->insert( 'core_theme_conflict', array(
'conflict_set_id' => \IPS\Request::i()->id,
'conflict_item_id' => $css[ $appKey ][ $location ][ $path ][ $name ]['css_id'],
@@ -1200,7 +1292,17 @@ class _themes extends \IPS\Node\Controller
}
else
{
\IPS\Db::i()->replace( 'core_theme_css', array(
$edited = 0;
$addedTo = \IPS\Request::i()->id;
if ( in_array( $name, $masterCss[ $appKey ][ $location ][ $path ] ) )
{
$edited = time();
$addedTo = 0;
}
/* Keep this */
$_SESSION['theme_import']['css'][] = \IPS\Db::i()->replace( 'core_theme_css', array(
'css_set_id' => \IPS\Request::i()->id,
'css_app' => $appKey,
'css_location' => $xml->getAttribute('css_location'),
@@ -1209,10 +1311,10 @@ class _themes extends \IPS\Node\Controller
'css_attributes' => $xml->getAttribute('css_attributes'),
'css_content' => $xml->readString(),
'css_version' => $version,
'css_user_edited'=> 0,
'css_added_to' => \IPS\Request::i()->id,
'css_user_edited'=> $edited,
'css_added_to' => $addedTo,
'css_unique_key' => md5( \IPS\Request::i()->id . ';' . $xml->getAttribute('css_app') . ';' . $xml->getAttribute('css_location') . ';' . $xml->getAttribute('css_path') . ';' . $xml->getAttribute('css_name') )
) );
), true );
}
break;
case 'resource':
@@ -1252,10 +1354,6 @@ class _themes extends \IPS\Node\Controller
'resource_filename' => $fileName,
'resource_user_edited' => intval( $xml->getAttribute('user_edited') )
) );
$thisKey = \IPS\Theme::makeBuiltTemplateLookupHash( $xml->getAttribute('app'), $xml->getAttribute('location'), $xml->getAttribute('path') ) . '_' . $xml->getAttribute('name');
#$iMap[ $thisKey ] = $fileName;
break;
}
@@ -1352,6 +1450,12 @@ class _themes extends \IPS\Node\Controller
},
function()
{
/* Do we need to clean up orphaned CSS files? */
if ( $_SESSION['theme_import']['isNewSet'] === false and isset( $_SESSION['theme_import']['css'] ) )
{
\IPS\Db::i()->delete( 'core_theme_css', array( 'css_added_to=? AND ' . \IPS\Db::i()->in( 'css_id', $_SESSION['theme_import']['css'], true ), \IPS\Request::i()->id ) );
}
$set = \IPS\Theme::load( \IPS\Request::i()->id );
\IPS\Theme::deleteCompiledResources( null, null, null, null, $set->id );
\IPS\Theme::deleteCompiledTemplate( null, null, null, $set->id );
@@ -1360,6 +1464,14 @@ class _themes extends \IPS\Node\Controller
\IPS\Data\Store::i()->delete( 'core_theme_import_' . md5_file( \IPS\Request::i()->file ) );
unset( \IPS\Data\Store::i()->themes );
@unlink( \IPS\Request::i()->file );
/* Update theme settings */
foreach( \IPS\Application::applications() as $app )
{
$app->installThemeSettings( true );
}
/* Conflicts to fix? */
if ( \IPS\Db::i()->select( 'count(*)', 'core_theme_conflict', array( 'conflict_set_id=?', \IPS\Request::i()->id ) )->first() )
@@ -1408,7 +1520,7 @@ class _themes extends \IPS\Node\Controller
}
else
{
$conflicts['templates'] = array();
$conflicts['template'] = array();
}
if ( isset( $conflicts['css'] ) AND count( $conflicts['css'] ) )
@@ -1428,9 +1540,19 @@ class _themes extends \IPS\Node\Controller
foreach( $conflicts['template'] as $key => $data )
{
if ( md5( $data['conflict_content'] ) != md5( $templates[ $key ]['template_content'] ) )
if( !\IPS\Login::compareHashes( md5( $data['conflict_content'] ), md5( $templates[ $key ]['template_content'] ) ) )
{
$conflicts['template'][ $key ]['diff'] = \Diff::toTable( \Diff::compare( $templates[ $key ]['template_content'], $data['conflict_content'] ) );
if ( mb_strlen( $data['conflict_content'] ) <= 10000 )
{
$conflicts['template'][ $key ]['diff'] = \Diff::toTable( \Diff::compare( $templates[ $key ]['template_content'], $data['conflict_content'] ) );
$conflicts['template'][ $key ]['large'] = false;
}
else
{
$conflicts['template'][ $key ]['diff'] = \IPS\Theme::i()->getTemplate('customization')->templateConflictLarge( $css[ $key ]['css_content'], $data['conflict_content'], 'html' );
$conflicts['template'][ $key ]['large'] = true;
}
$form->add( new \IPS\Helpers\Form\Radio( 'conflict_' . $data['conflict_id'], 'old', false, array( 'options' => array( 'old' => '', 'new' => '' ) ) ) );
}
else
@@ -1441,9 +1563,19 @@ class _themes extends \IPS\Node\Controller
foreach( $conflicts['css'] as $key => $data )
{
if ( md5( $data['conflict_content'] ) != md5( $css[ $key ]['css_content'] ) )
if( !\IPS\Login::compareHashes( md5( $data['conflict_content'] ), md5( $css[ $key ]['css_content'] ) ) )
{
$conflicts['css'][ $key ]['diff'] = \Diff::toTable( \Diff::compare( $css[ $key ]['css_content'], $data['conflict_content'] ) );
if ( mb_strlen( $data['conflict_content'] ) <= 10000 )
{
$conflicts['css'][ $key ]['diff'] = \Diff::toTable( \Diff::compare( $css[ $key ]['css_content'], $data['conflict_content'] ) );
$conflicts['css'][ $key ]['large'] = false;
}
else
{
$conflicts['css'][ $key ]['diff'] = \IPS\Theme::i()->getTemplate('customization')->templateConflictLarge( $css[ $key ]['css_content'], $data['conflict_content'], 'css' );
$conflicts['css'][ $key ]['large'] = true;
}
$form->add( new \IPS\Helpers\Form\Radio( 'conflict_' . $data['conflict_id'], 'old', false, array( 'options' => array( 'old' => '', 'new' => '' ) ) ) );
}
else
@@ -1538,6 +1670,9 @@ class _themes extends \IPS\Node\Controller
if ( count( $conflicts['css'] ) OR count( $conflicts['template'] ) )
{
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'system/diff.css', 'core', 'admin' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'codemirror/codemirror.js', 'core', 'interface' ) );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'codemirror/codemirror.css', 'core', 'interface' ) );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'customization/themes.css', 'core', 'admin' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'admin_templates.js', 'core', 'admin' ) );
\IPS\Output::i()->output = $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'customization', 'core' ), 'templateConflict' ), $themeSet, $conflicts, $templates, $css );
@@ -1579,7 +1714,7 @@ class _themes extends \IPS\Node\Controller
/* Make sure key is unique */
try
{
$row = \IPS\Db::i()->select( '*', 'core_theme_css', array( "css_set_id=? AND css_app=? AND css_path=? AND css_location=? AND css_name=?", 0, \IPS\Request::i()->theme_css_app, $path, $location, $val ) )->first();
$row = \IPS\Db::i()->select( '*', 'core_theme_css', array( \IPS\Db::i()->in( 'css_set_id', array( 0, \IPS\Request::i()->id ) ) . " AND css_app=? AND css_path=? AND css_location=? AND css_name=?", \IPS\Request::i()->theme_css_app, $path, $location, $val ) )->first();
if ( isset( $row['css_id'] ) )
{
@@ -1649,7 +1784,7 @@ class _themes extends \IPS\Node\Controller
'content' => $values['theme_css_content'] );
$newId = \IPS\Theme::addCss( $save );
\IPS\Theme::i()->load( $id )->compileCss( $save['app'], $save['location'], $save['path'] );
if( \IPS\Request::i()->isAjax() )
@@ -1704,7 +1839,7 @@ class _themes extends \IPS\Node\Controller
move_uploaded_file( $values['core_theme_set_new_import'], $tempFile );
/* Initate a redirector */
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=import&file=' . urlencode( $tempFile ) . '&key=' . md5_file( $tempFile ) . '&id=' . \IPS\Request::i()->id) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=import' )->setQueryString( array( 'file' => $tempFile, 'key' => md5_file( $tempFile ), 'id' => \IPS\Request::i()->id) ) );
}
/* Display */
@@ -1740,6 +1875,12 @@ class _themes extends \IPS\Node\Controller
if ( \IPS\IN_DEV OR \IPS\Theme::designersModeEnabled() )
{
if ( ! isset( \IPS\Request::i()->rebuildDone ) )
{
$url = \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=devImportBeforeExport&id=' . $id );
$form->addMessage( \IPS\Member::loggedIn()->language()->addToStack('theme_export_designer_mode_build_message', NULL, array( 'sprintf' => array( $url ) ) ), 'ipsMessage ipsMessage_info', FALSE );
}
$form->add( new \IPS\Helpers\Form\Text( 'theme_template_export_author_name', ( $storedAuthor !== null ) ? $storedAuthor['name'] : false, false ) );
$form->add( new \IPS\Helpers\Form\Text( 'theme_template_export_author_url' , ( $storedAuthor !== null ) ? $storedAuthor['url'] : false, false ) );
$form->add( new \IPS\Helpers\Form\Text( 'theme_update_check' , $themeSet->update_check, false ) );
@@ -1794,7 +1935,11 @@ class _themes extends \IPS\Node\Controller
$xml->startAttribute('long_version');
$xml->text( $longVersion );
$xml->endAttribute();
$xml->startAttribute('update_check');
$xml->text( $updateCheck );
$xml->endAttribute();
/* Copy logos */
if( $themeSet->logo_data )
{
@@ -1860,6 +2005,15 @@ class _themes extends \IPS\Node\Controller
{
foreach( $templates[ $appDir->directory ][ $loc ][ $group ] as $name => $data )
{
/* Skip custom bits from other themes */
if ( ( $data['InheritedValue'] != 'inherit' and $data['InheritedValue'] != 'changed' ) and ( $data['template_user_added'] and $data['template_added_to'] != $themeSet->_id ) )
{
if ( $data['template_set_id'] != $themeSet->_id )
{
continue;
}
}
/* Remove original template bits */
if ( $data['InheritedValue'] != 'original' OR $data['template_user_added'] )
{
@@ -2104,7 +2258,7 @@ class _themes extends \IPS\Node\Controller
$name = addslashes( str_replace( array( ' ', '.', ',' ), '_', \IPS\Member::loggedIn()->language()->get('core_theme_set_title_' . $themeSet->_id ) ) . '.xml' );
\IPS\Output::i()->sendOutput( $xml->outputMemory(), 200, 'application/xml', array( 'Content-Disposition' => \IPS\Output::getContentDisposition( 'attachment', $name ) ) );
\IPS\Output::i()->sendOutput( $xml->outputMemory(), 200, 'application/xml', array( 'Content-Disposition' => \IPS\Output::getContentDisposition( 'attachment', $name ), FALSE, FALSE ) );
}
/* Display */
@@ -2123,7 +2277,8 @@ class _themes extends \IPS\Node\Controller
try
{
if ( \IPS\Theme::load( \IPS\Request::i()->id )->is_default )
$theme = \IPS\Theme::load( \IPS\Request::i()->id );
if ( $theme->is_default or $theme->is_acp_default )
{
\IPS\Output::i()->error( 'cannot_delete_default_theme', '1C163/3', 403, '' );
}
@@ -2155,10 +2310,10 @@ class _themes extends \IPS\Node\Controller
$form = new \IPS\Helpers\Form();
$form->hiddenValues['id'] = $id;
$form->add( new \IPS\Helpers\Form\Text( 'theme_template_name', NULL, TRUE, array( 'regex' => '/^([A-Z0-9_]+?)$/i' ),
$form->add( new \IPS\Helpers\Form\Text( 'theme_template_name', NULL, TRUE, array( 'regex' => '/^([a-z_][A-Z0-9_]+?)$/i' ),
function( $val )
{
if ( ! preg_match( '/^([A-Z0-9_]+?)$/i', $val ) )
if ( ! preg_match( '/^([a-z_][A-Z0-9_]+?)$/i', $val ) )
{
throw new \InvalidArgumentException( 'form_bad_value' );
}
@@ -2209,7 +2364,7 @@ class _themes extends \IPS\Node\Controller
'new' => array( 'location_new' ) )
) ) );
$form->add( new \IPS\Helpers\Form\Text( 'theme_template_location_new', NULL, FALSE, array( 'regex' => '/^([A-Z]+?)?$/i' ), NULL, NULL, NULL, 'location_new' ) );
$form->add( new \IPS\Helpers\Form\Text( 'theme_template_location_new', NULL, FALSE, array( 'regex' => '/^([A-Z][A-Z0-9]+?)$/i' ), NULL, NULL, NULL, 'location_new' ) );
$form->add( new \IPS\Helpers\Form\Select( 'theme_template_location_existing', NULL, FALSE, array( 'options' => $locations ), NULL, NULL, NULL, 'location_existing' ) );
$form->add( new \IPS\Helpers\Form\Radio( 'theme_template_group_type', 'existing', FALSE, array(
@@ -2219,7 +2374,7 @@ class _themes extends \IPS\Node\Controller
'new' => array( 'group_new' ) )
) ) );
$form->add( new \IPS\Helpers\Form\Text( 'theme_template_group_new', NULL, FALSE, array( 'regex' => '/^([a-z0-9_]+?)?$/' ), NULL, NULL, NULL, 'group_new' ) );
$form->add( new \IPS\Helpers\Form\Text( 'theme_template_group_new', NULL, FALSE, array( 'regex' => '/^([a-z_][a-z0-9_]+?)?$/' ), NULL, NULL, NULL, 'group_new' ) );
$form->add( new \IPS\Helpers\Form\Select( 'theme_template_group_existing', NULL, FALSE, array( 'options' => $groups, 'parse' => 'normal' ), NULL, NULL, NULL, 'group_existing' ) );
$form->add( new \IPS\Helpers\Form\Select( 'theme_template_app', NULL, TRUE, array( 'options' => $apps ) ) );
@@ -2299,6 +2454,9 @@ class _themes extends \IPS\Node\Controller
{
/* Check permission */
\IPS\Dispatcher::i()->checkAcpPermission( 'theme_templates_manage' );
/* Make sure the user confirmed the deletion */
\IPS\Request::i()->confirmedDelete();
$type = \IPS\Request::i()->t_type;
$id = intval( \IPS\Request::i()->id );
@@ -2437,16 +2595,37 @@ class _themes extends \IPS\Node\Controller
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=templates&id=' . $save['set_id'] . '&t_type=' . $type . '&' . implode( '&', $url ) ), 'completed' );
}
}
/**
* Rebuild master templates
*
* @return void
*/
public function devImportBeforeExport()
{
$this->devImportMultipleRedirect( "app=core&module=customization&controller=themes&do=exportForm&id=" . \IPS\Request::i()->id . '&rebuildDone=1' );
}
/**
* Rebuild master templates
*
* @return void
*/
public function devImport()
{
$this->devImportMultipleRedirect( 'app=core&module=customization&controller=themes' );
}
/**
* Rebuild master templates
*
* @param string|NULL $finalUrl Final redirect URL
* @return void
*/
public function devImportMultipleRedirect( $finalUrl=NULL )
{
\IPS\Output::i()->output = new \IPS\Helpers\MultipleRedirect(
\IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=devImport&id=' . \IPS\Request::i()->id ),
\IPS\Http\Url::internal( 'app=core&module=customization&controller=themes&do=devImportMultipleRedirect&id=' . \IPS\Request::i()->id . '&finalUrl=' . base64_encode( $finalUrl ) ),
function( $data )
{
/* Is this the first cycle? */
@@ -2506,7 +2685,7 @@ class _themes extends \IPS\Node\Controller
function()
{
/* Finished */
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes' ), 'completed' );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( base64_decode( \IPS\Request::i()->finalUrl ) ), 'completed' );
}
);
}
@@ -2700,17 +2879,7 @@ class _themes extends \IPS\Node\Controller
{
foreach( $templateNames[ $app ][ $location ] as $path => $items )
{
if ( $path === 'custom' )
{
foreach( $items as $name => $data )
{
if ( $name !== 'custom.css' )
{
unset( $templateNames[ $app ][ $location ][ $path ][ $name ] );
}
}
}
else
if ( $path != 'custom' )
{
unset( $templateNames[ $app ][ $location ][ $path ] );
}
@@ -2787,11 +2956,18 @@ class _themes extends \IPS\Node\Controller
$where = \IPS\Db::i()->in( 'member_group_id', $values['member_reset_where'] );
}
\IPS\Db::i()->update( 'core_members', array( 'skin' => \IPS\Request::i()->id ), $where );
if ( $where )
{
\IPS\Db::i()->update( 'core_members', array( 'skin' => \IPS\Request::i()->id ), $where );
}
else
{
\IPS\Member::updateAllMembers( array( 'skin' => \IPS\Request::i()->id ) );
}
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes' ), 'reset' );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=customization&controller=themes' ), 'member_theme_reset' );
}
\IPS\Output::i()->output = $form;
}
}
}
@@ -2,9 +2,9 @@
/**
* @brief theme Settings
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @package IPS Community Suite
* @since 17 May 2013
* @version SVN_VERSION_NUMBER
*/
@@ -29,7 +29,9 @@ class _themesettings extends \IPS\Dispatcher\Controller
* @return void
*/
public function execute()
{
{
\IPS\Dispatcher::i()->checkAcpPermission( 'theme_sets_manage' );
parent::execute();
/* Display */
@@ -84,7 +86,7 @@ class _themesettings extends \IPS\Dispatcher\Controller
$_tabs = array_keys( $tabs );
$activeTab = array_shift( $_tabs );
}
/* Create tree */
$self = $this;
$tree = new \IPS\Helpers\Tree\Tree(
@@ -299,7 +301,10 @@ class _themesettings extends \IPS\Dispatcher\Controller
throw new \InvalidArgumentException('core_theme_settings_error_key_not_unique');
}
}
else
{
throw new \InvalidArgumentException('core_theme_settings_error_key_not_unique');
}
}
catch( \UnderflowException $e )
{
@@ -328,13 +333,10 @@ class _themesettings extends \IPS\Dispatcher\Controller
$form->hiddenValues['theme_custom_setting_tab_key_type'] = 'new';
}
$form->add( new \IPS\Helpers\Form\Translatable( 'theme_template_tab_key_new', NULL, FALSE, array(
'app' => 'core',
'key' => ( $current ? "theme_custom_tab_{$current['sc_tab_key']}" : NULL )
),
$form->add( new \IPS\Helpers\Form\Text( 'theme_template_tab_key_new', NULL, FALSE, array( 'regex' => '#[0-9a-z_]+#' ),
function( $val )
{
$val = preg_replace( '#[^0-9a-z_]#', '', mb_strtolower( $val[ \IPS\Lang::defaultLanguage() ] ) );
$val = preg_replace( '#[^0-9a-z_]#', '', mb_strtolower( $val ) );
if ( \IPS\Request::i()->theme_custom_setting_tab_key_type === 'new' )
{
@@ -384,7 +386,7 @@ class _themesettings extends \IPS\Dispatcher\Controller
$form->add( new \IPS\Helpers\Form\Stack( 'theme_custom_setting_type_content', ( $id and $current['sc_type'] != 'other' ) ? json_decode( $current['sc_content'], TRUE ) : array(), FALSE, array( 'stackFieldType' => 'KeyValue' ), NULL, NULL, NULL, 'sc_content' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'theme_custom_setting_type_multiple', $id ? $current['sc_multiple'] : FALSE, FALSE, array(), NULL, NULL, NULL, 'sc_multiple' ) );
$form->add( new \IPS\Helpers\Form\Codemirror( 'theme_custom_setting_manual_code', '<?php' . "\n\n" . ( ( $id and $current['sc_type'] == 'other' ) ? $current['sc_content'] : 'return new \IPS\Helpers\Form\Text( "core_theme_setting_title_{$row[\'sc_id\']}", $value, FALSE, array(), NULL, NULL, NULL, \'theme_setting_\' . $row[\'sc_key\'] );' ), NULL, array( 'mode' => 'php' ), function( $val )
$form->add( new \IPS\Helpers\Form\Codemirror( 'theme_custom_setting_manual_code', '<?php' . "\n\n" . ( ( $id and $current['sc_type'] == 'other' ) ? $current['sc_content'] : 'return new \IPS\Helpers\Form\Text( "core_theme_setting_title_{$row[\'sc_id\']}", $value, FALSE, array(), NULL, NULL, NULL, \'theme_setting_\' . $row[\'sc_key\'] );' ), NULL, array( 'mode' => 'php' ), function( $val ) use ( $set )
{
$val = trim( preg_replace( '/^<\?php(.*)$/is', '$1', trim( $val ) ) );
@@ -392,6 +394,7 @@ class _themesettings extends \IPS\Dispatcher\Controller
{
$row = array( 'sc_id' => 0, 'sc_key' => \IPS\Request::i()->theme_custom_setting_key );
$value = NULL;
$theme = $set;
if ( !( eval( $val ) instanceof \IPS\Helpers\Form\FormAbstract ) )
{
throw new \DomainException('theme_custom_setting_manual_code_bad');
@@ -410,6 +413,9 @@ class _themesettings extends \IPS\Dispatcher\Controller
$form->add( new \IPS\Helpers\Form\YesNo( 'theme_custom_setting_show_in_vse', $id ? $current['sc_show_in_vse'] : FALSE, FALSE, array(), NULL, NULL, NULL, 'sc_show_in_vse' ) );
}
$form->add( new \IPS\Helpers\Form\YesNo( 'theme_custom_setting_conditional', $id ? $current['sc_condition'] : FALSE, FALSE, array( 'togglesOn' => array( 'theme_custom_setting_condition' ) ), NULL, NULL, NULL, 'theme_custom_setting_conditional' ) );
$form->add( new \IPS\Helpers\Form\CodeMirror( 'theme_custom_setting_condition', '<?php' . "\n\n" . ( ( $id and $current['sc_condition'] ) ? $current['sc_condition'] : 'return TRUE;' ), FALSE, array( 'mode' => 'php' ), NULL, NULL, NULL, 'theme_custom_setting_condition' ) );
if ( $values = $form->values() )
{
$save = array(
@@ -423,6 +429,7 @@ class _themesettings extends \IPS\Dispatcher\Controller
'sc_show_in_vse' => ( isset( $values['theme_custom_setting_show_in_vse'] ) ) ? $values['theme_custom_setting_show_in_vse'] : 0,
'sc_content' => ( $values['theme_custom_setting_type'] === 'other' ) ? trim( preg_replace( '/^<\?php(.*)$/is', '$1', trim( $values['theme_custom_setting_manual_code'] ) ) ) : json_encode( $values['theme_custom_setting_type_content'] ),
'sc_title' => $values['theme_custom_setting_title'],
'sc_condition' => $values['theme_custom_setting_conditional'] ? trim( preg_replace( '/^<\?php(.*)$/is', '$1', trim( $values['theme_custom_setting_condition'] ) ) ) : NULL
);
$type = ( isset( \IPS\Request::i()->theme_custom_setting_tab_key_type ) ) ? \IPS\Request::i()->theme_custom_setting_tab_key_type : $values['theme_custom_setting_tab_key_type'];
@@ -433,7 +440,7 @@ class _themesettings extends \IPS\Dispatcher\Controller
}
else
{
$save['sc_tab_key'] = preg_replace( '#[^0-9a-z]#', '', mb_strtolower( $values['theme_template_tab_key_new'][ \IPS\Lang::defaultLanguage() ] ) );
$save['sc_tab_key'] = mb_strtolower( $values['theme_template_tab_key_new'] );
}
if ( $current )
@@ -501,6 +508,9 @@ class _themesettings extends \IPS\Dispatcher\Controller
$setId = intval( \IPS\Request::i()->set_id );
\IPS\Dispatcher::i()->checkAcpPermission( 'theme_settings_remove' );
/* Make sure the user confirmed the deletion */
\IPS\Request::i()->confirmedDelete();
try
{