Version 4.1.2

This commit is contained in:
Neo committed 2025-12-19 05:32:19 -08:00
1 parent 3ba79295ee
commit 28bd025b35
1131 files changed
+63658 -20579

No files matched your search

+77 -18
View File
@@ -623,6 +623,67 @@ class _Javascript extends \IPS\Patterns\ActiveRecord
}
}
/**
* Delete a compiled JS file
*
* @param string $app Application
* @param string|null $location Location (front, global, etc)
* @param string|null $file File to remove
* @return boolean|null
*/
public static function deleteCompiled( $app, $location=null, $file=null )
{
$map = ( isset( \IPS\Data\Store::i()->javascript_map ) ) ? \IPS\Data\Store::i()->javascript_map : array();
$files = ( isset( \IPS\Data\Store::i()->javascript_file_map ) ) ? \IPS\Data\Store::i()->javascript_file_map : array();
if ( $location === NULL and $file === NULL )
{
if ( isset( $map[ $app ] ) )
{
foreach( $map[ $app ] as $hash => $path )
{
try
{
\IPS\File::get( 'core_Theme', $path )->delete();
}
catch( \Exception $e ) { }
}
$map[ $app ] = array();
}
}
else
{
if ( isset( $map[ $app ] ) )
{
foreach( $map[ $app ] as $hash => $path )
{
if ( $file === NULL )
{
$lookFor = 'javascript_' . $app . '/' . $location . '_';
}
else
{
$lookFor = 'javascript_' . $app . '/' . $location . '_' . $file;
}
if ( mb_substr( $path, 0, mb_strlen( $lookFor ) ) === $lookFor )
{
try
{
\IPS\File::get( 'core_Theme', $path )->delete();
}
catch( \Exception $e ) { }
unset( $map[ $app ][ $hash ] );
}
}
}
}
\IPS\Data\Store::i()->javascript_map = $map;
}
/**
* Compiles JS into fewer minified files suitable for non IN_DEV use.
* Imports the fewer files into a database for writing out.
@@ -733,9 +794,18 @@ class _Javascript extends \IPS\Patterns\ActiveRecord
{
if ( $file === null OR $file === 'plugins.js' )
{
$obj = static::_writeJavascriptFromResultset( \IPS\Db::i()->select( '*', 'core_javascript', array( 'javascript_app=? AND javascript_location=? AND javascript_type=?', 'core', 'plugins', 'plugin' ), 'javascript_app, javascript_location, javascript_position' )->setKeyField('javascript_id'), 'plugins.js', 'core', 'plugins' );
$ids = array();
$map[ $app ][ md5( 'core-plugins-plugins.js' ) ] = $obj ? (string) $obj : null;
foreach ( \IPS\Db::i()->select( '*', 'core_plugins', 'plugin_enabled=1' ) as $row )
{
$ids[] = intval( $row['plugin_id'] );
}
if ( count( $ids ) )
{
$obj = static::_writeJavascriptFromResultset( \IPS\Db::i()->select( '*', 'core_javascript', array( array( 'javascript_app=? AND javascript_location=? AND javascript_type=? AND javascript_plugin IN(' . implode( ',', array_values( $ids ) ) . ')', 'core', 'plugins', 'plugin' ) ), 'javascript_app, javascript_location, javascript_position' )->setKeyField('javascript_id'), 'plugins.js', 'core', 'plugins' );
$map[ $app ][ md5( 'core-plugins-plugins.js' ) ] = $obj ? (string) $obj : null;
}
}
}
@@ -893,15 +963,7 @@ class _Javascript extends \IPS\Patterns\ActiveRecord
*/
protected static function _writeJavascript( $content, $fileName, $app, $location )
{
$header = <<<EOF
/**
* IPS Social Suite 4
* (c) 2013 Invision Power Services - http://www.invisionpower.com
* This file contains minified javascript and is not directly editable
*/
EOF;
return \IPS\File::create( 'core_Theme', $location . '_' . $fileName, $header . "\n\n" . $content, 'javascript_' . $app, TRUE );
return \IPS\File::create( 'core_Theme', $location . '_' . $fileName, $content, 'javascript_' . $app, TRUE );
}
/**
@@ -980,20 +1042,17 @@ EOF;
}
/**
* Minifies javascript if IPS created
* Minifies javascript
*
* @param string $js Javascript code
* @return string
*/
protected static function _minify( $js )
{
require_once( \IPS\ROOT_PATH . '/system/3rd_party/Minify/lib/JSMin.php' );
if ( mb_stristr( mb_substr( $js, 0, 1000 ), '* IPS Social Suite 4' ) )
{
$js = \JSMin::minify( $js );
}
require_once( \IPS\ROOT_PATH . '/system/3rd_party/JShrink/Minifier.php' );
$js = \JShrink\Minifier::minify( $js, array( 'flaggedComments' => false ) );
return $js;
}
+67 -14
View File
@@ -625,9 +625,10 @@ class _Output
* @param array $httpHeaders Additional HTTP Headers
* @param bool $cacheThisPage Can/should this page be cached?
* @param bool $pageIsCached Is the page from a cache? If TRUE, no language parsing will be done
* @param bool $parseFileObjects Should <fileStore.xxx> and <___base_url___> be replaced in the output?
* @return void
*/
public function sendOutput( $output='', $httpStatusCode=200, $contentType='text/html', $httpHeaders=array(), $cacheThisPage=TRUE, $pageIsCached=FALSE )
public function sendOutput( $output='', $httpStatusCode=200, $contentType='text/html', $httpHeaders=array(), $cacheThisPage=TRUE, $pageIsCached=FALSE, $parseFileObjects=TRUE )
{
/* Replace language stack keys with actual content */
if ( \IPS\Dispatcher::hasInstance() and !in_array( $contentType, array( 'text/javascript', 'text/css', 'application/json' ) ) and $output and !$pageIsCached )
@@ -636,11 +637,11 @@ class _Output
}
/* Parse file storage URLs */
if ( $output )
if ( $output and $parseFileObjects )
{
$this->parseFileObjectUrls( $output );
}
/* Full page caching for guests */
if (
\IPS\CACHE_PAGE_TIMEOUT and // Page caching is enabled
@@ -651,7 +652,7 @@ class _Output
\IPS\Dispatcher::hasInstance() and class_exists( 'IPS\Dispatcher', FALSE ) and \IPS\Dispatcher::i()->controllerLocation === 'front' and // Is a normal, front-end page (necessary to know if user is logged in)
!\IPS\Member::loggedIn()->member_id // User is not logged in
) {
\IPS\Data\Cache::i()->storeWithExpire( 'page_' . md5( $_SERVER['REQUEST_URI'] ) . '_' . \IPS\Member::loggedIn()->language()->id . '_' . \IPS\Theme::i()->id, array(
\IPS\Data\Cache::i()->storeWithExpire( 'page_' . md5( ( !empty( $_SERVER['HTTP_HOST'] ) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME'] ) . '/' . $_SERVER['REQUEST_URI'] ) . '_' . \IPS\Member::loggedIn()->language()->id . '_' . \IPS\Theme::i()->id, array(
'output' => str_replace( \IPS\Session::i()->csrfKey, '{{csrfKey}}', $output ),
'code' => $httpStatusCode,
'contentType' => $contentType,
@@ -710,9 +711,16 @@ class _Output
}
/* Send headers */
$this->sendHeader( "Content-type: {$contentType};charset=UTF-8" );
$size = ob_get_length();
$this->sendHeader( "Content-Length: {$size}" ); // Makes sure the connection closes after sending output so that tasks etc aren't holding it open
$this->sendHeader( "Content-type: {$contentType};charset=UTF-8" );
/* Send content-length header, but only if not using zlib.output_compression, because in that case the length we send in the header
will not match the length of the actual content sent to the browser, breaking things (particularly json) */
if( (bool) ini_get('zlib.output_compression') === false )
{
$size = ob_get_length();
$this->sendHeader( "Content-Length: {$size}" ); // Makes sure the connection closes after sending output so that tasks etc aren't holding it open
}
foreach ( $httpHeaders as $key => $header )
{
$this->sendHeader( $key . ': ' . $header );
@@ -778,6 +786,10 @@ class _Output
}
elseif ( $forceScreen === TRUE or ( $message and !$url->isInternal ) )
{
/* We cannot send a 3xx status code without a Location header, or some browsers (cough IE) will not actually redirect. We are showing
an intermediary page performing the redirect through a meta refresh tag, so a 200 status is appropriate in this case. */
$httpStatusCode = ( mb_substr( $httpStatusCode, 0, 1 ) == 3 ) ? 200 : $httpStatusCode;
$this->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->redirect( $url, $message ), $httpStatusCode );
}
else
@@ -848,7 +860,8 @@ class _Output
*/
public function showOffline()
{
$this->bodyClasses[] = 'ipsLayout_minimal ipsLayout_minimalNoHome';
$this->bodyClasses[] = 'ipsLayout_minimal';
$this->bodyClasses[] = 'ipsLayout_minimalNoHome';
$this->output = \IPS\Theme::i()->getTemplate( 'system', 'core' )->offline( \IPS\Settings::i()->site_offline_message );
$this->title = \IPS\Settings::i()->board_name;
@@ -895,7 +908,7 @@ class _Output
/* Add Admin-specified ones */
if( !$this->metaTagsUrl )
{
$this->metaTagsUrl = \IPS\Request::i()->url()->queryPath();
$this->metaTagsUrl = \IPS\Request::i()->url()->getFurlQuery();
if ( isset( \IPS\Data\Store::i()->metaTags ) )
{
@@ -913,7 +926,7 @@ class _Output
{
if( \strpos( $row['meta_url'], '*' ) !== FALSE )
{
if( preg_match( "#^" . str_replace( '*', '(.*)', trim( $row['meta_url'], '/' ) ) . "$#i", trim( $this->metaTagsUrl, '/' ) ) )
if( preg_match( "#^" . str_replace( '\*', '(.*)', trim( preg_quote( $row['meta_url'], '#' ), '/' ) ) . "$#i", trim( $this->metaTagsUrl, '/' ) ) )
{
$_tags = json_decode( $row['meta_tags'], TRUE );
@@ -958,6 +971,35 @@ class _Output
}
}
}
$baseUrl = parse_url( \IPS\Settings::i()->base_url );
$lookFor = '//' . $baseUrl['host'];
foreach( $this->metaTags as $name => $value )
{
if ( ! is_array( $value ) )
{
$value = array( $value );
}
foreach( $value as $tag )
{
if ( mb_substr( $tag, 0, mb_strlen( $lookFor ) ) === $lookFor )
{
/* Try to preserve http vs https */
if( isset( $baseUrl['scheme'] ) )
{
$tag = str_replace( $lookFor, $baseUrl['scheme'] . '://' . $baseUrl['host'], $tag );
}
else
{
$tag = str_replace( $lookFor, 'http://' . $baseUrl['host'], $tag );
}
$this->metaTags[ $name ] = $tag;
}
}
}
}
/**
@@ -996,6 +1038,11 @@ class _Output
* @brief Contextual search menu options
*/
public $contextualSearchOptions = array();
/**
* @brief Default search option
*/
public $defaultSearchOption = array( 'all', 'search_everything' );
/**
* Retrieve options for search menu
@@ -1010,14 +1057,20 @@ class _Output
{
if( is_subclass_of( $class, 'IPS\Content\Searchable' ) )
{
$type = mb_strtolower( str_replace( '\\', '_', mb_substr( $class, 4 ) ) );
$this->globalSearchMenuOptions[ $type ] = $type . '_pl';
if ( $class::$includeInSiteSearch )
{
$type = mb_strtolower( str_replace( '\\', '_', mb_substr( $class, 4 ) ) );
$this->globalSearchMenuOptions[ $type ] = $type . '_pl';
}
}
}
}
/* This is also supported, but is not a content item class implementing \Searchable */
$this->globalSearchMenuOptions['core_members'] = 'core_members_pl';
if ( \IPS\Member::loggedIn()->canAccessModule( \IPS\Application\Module::get( 'core', 'members', 'front' ) ) )
{
$this->globalSearchMenuOptions['core_members'] = 'core_members_pl';
}
return $this->globalSearchMenuOptions;
}
+8
View File
@@ -48,6 +48,14 @@ class _Datetime
{
$return['return'] = '(string) $val';
}
elseif ( isset( $options['lowercase'] ) )
{
$return['return'] = '$val->html(FALSE)';
}
elseif ( isset( $options['short'] ) )
{
$return['return'] = '$val->html(TRUE, TRUE)';
}
else
{
$return['return'] = '$val->html()';
+8 -1
View File
@@ -38,6 +38,13 @@ class _Setting
*/
public static function runPlugin( $data, $options )
{
return "\IPS\Settings::i()->$data";
if ( isset( $options['escape'] ) )
{
return "htmlspecialchars( \IPS\Settings::i()->$data, ENT_QUOTES | \IPS\HTMLENTITIES, 'UTF-8', TRUE )";
}
else
{
return "\IPS\Settings::i()->$data";
}
}
}
+1 -1
View File
@@ -48,6 +48,6 @@ class _Truncate
$_argument = ( mb_strpos( $data, '$' ) !== FALSE ) ? $data : '"' . $data . '"';
$_length = ( mb_strpos( $options['length'], '$' ) !== FALSE ) ? $options['length'] : '"' . $options['length'] . '"';
return "htmlentities( mb_substr( {$_argument}, '{$options['start']}', {$_length} ), ENT_QUOTES | \IPS\HTMLENTITIES, 'UTF-8', FALSE ) . ( ( mb_strlen( {$_argument} ) > {$_length} ) ? '{$options['append']}' : '' )";
return "htmlspecialchars( mb_substr( {$_argument}, '{$options['start']}', {$_length} ), ENT_QUOTES | \IPS\HTMLENTITIES, 'UTF-8', FALSE ) . ( ( mb_strlen( {$_argument} ) > {$_length} ) ? '{$options['append']}' : '' )";
}
}
+1 -1
View File
@@ -37,6 +37,6 @@ class _Wordbreak
*/
public static function runPlugin( $data, $options )
{
return "\IPS\Lang::wordbreak( htmlentities( {$data}, ENT_QUOTES | \IPS\HTMLENTITIES, 'UTF-8', FALSE ) )";
return "\IPS\Lang::wordbreak( htmlspecialchars( {$data}, ENT_QUOTES | \IPS\HTMLENTITIES, 'UTF-8', FALSE ) )";
}
}