Version 5.0.0 beta 7

This commit is contained in:
Neo committed 2025-12-19 17:18:38 -08:00
1 parent fa2a720105
commit 54d5dcd0a0
156 files changed
+4337 -2995

No files matched your search

+1 -1
View File
@@ -3757,7 +3757,7 @@ EOF;
}
foreach ( $newChanges['edited'] as $v )
{
if ( !in_array( $v, $previousChanges['added'] ) )
if ( !in_array( $v, $previousChanges['added'] ) and !in_array( $v, $previousChanges['edited'] ) )
{
$previousChanges['edited'][] = $v;
}
+40
View File
@@ -16,12 +16,14 @@ use BadMethodCallException;
use Exception;
use InvalidArgumentException;
use IPS\Api\Webhook;
use IPS\Content\Anonymous;
use IPS\Content\Assignable;
use IPS\Content\Comment;
use IPS\Content\Followable;
use IPS\Content\Hideable;
use IPS\Content\Item;
use IPS\Content\Review;
use IPS\Content\Solvable;
use IPS\core\Approval;
use IPS\core\Reports\Report;
use IPS\Db\Select;
@@ -1954,4 +1956,42 @@ abstract class Content extends ActiveRecord
{
return Bridge::i()->canUnmarkAsSpam( $member, $content );
}
/**
* Get a url from which to load the mini-profile bar.
*
* @return string
*/
public function get_authorMiniProfileUrl() : string
{
$params = [
'app' => 'core',
'module' => 'system',
'controller' => 'ajax',
'do' => 'miniProfile',
'authorId' => $this->author()?->member_id ?? null,
];
if ( !IPS::classUsesTrait( $this, Anonymous::class ) or $this->isAnonymous() )
{
$params['anonymous'] = '1';
}
$item = $this instanceof Comment ? $this->item() : $this;
if ( IPS::classUsesTrait( $item, Solvable::class ) )
{
if ( isset( $this->author_solved_count ) and is_int( $this->author_solved_count ) )
{
$params['solvedCount'] = $this->author_solved_count;
}
else
{
$params['solvedCount'] = 'load';
}
}
$url = Url::internal( '', 'front' )->setQueryString( $params );
return (string) $url;
}
}
+3 -11
View File
@@ -12,6 +12,7 @@ namespace IPS\Content;
/* To prevent PHP errors (extending class does not exist) revealing path */
use BadMethodCallException;
use Exception;
use IPS\Application;
use IPS\Content\Search\Index;
@@ -21,7 +22,6 @@ use IPS\forums\Topic;
use IPS\forums\Topic\Post;
use IPS\IPS;
use IPS\Member;
use IPS\Settings;
use OutOfRangeException;
use function defined;
@@ -93,10 +93,6 @@ trait ItemTopic
$title = $this->getTopicTitle();
Member::loggedIn()->language()->parseOutputForDisplay( $title );
$topic->title = $title;
if ( Settings::i()->tags_enabled )
{
$topic->setTags( $this->prefix() ? array_merge( $this->tags(), array( 'prefix' => $this->prefix() ) ) : $this->tags() );
}
$topic->save();
$firstPost = $topic->comments( 1 );
@@ -143,14 +139,10 @@ trait ItemTopic
{
$topic->setAnonymous( TRUE, $this->author );
}
catch( \BadMethodCallException ){}
catch( BadMethodCallException ){}
}
$topic->markRead( $this->author() );
if ( Settings::i()->tags_enabled )
{
$topic->setTags( $this->prefix() ? array_merge( $this->tags(), array( 'prefix' => $this->prefix() ) ) : $this->tags() );
}
/* Create post */
$content = $this->getTopicContent();
@@ -163,7 +155,7 @@ trait ItemTopic
{
$post->setAnonymous( TRUE, $this->author );
}
catch( \BadMethodCallException ){}
catch( BadMethodCallException ){}
}
$topic->topic_firstpost = $post->pid;
+19
View File
@@ -19,6 +19,7 @@ use Exception;
use InvalidArgumentException;
use IPS\Application;
use IPS\Content;
use IPS\core\FrontNavigation;
use IPS\Data\Store;
use IPS\DateTime;
use IPS\Db;
@@ -654,6 +655,24 @@ class Tag extends Model
$photo->delete();
}
/* Delete any navigation items that reference this tag */
foreach( Db::i()->select( '*', 'core_menu', [ 'app=? and extension=?', 'core', 'Tags' ] ) as $row )
{
if( $row['config'] and $config = json_decode( $row['config'], true ) )
{
if( $config['id'] == $this->id )
{
Db::i()->delete( 'core_menu', [ 'id=?', $row['id'] ] );
}
}
}
try
{
unset( Store::i()->frontNavigation );
}
catch( OutOfRangeException ){}
/* Queue the background task to remove the tag from all currently tagged items */
Task::queue( 'core', 'UpdateTaggedItems', [ 'tag' => $this->text, 'delete' => true ], 5, [ 'tag' ] );
}
+1 -1
View File
@@ -110,7 +110,7 @@ class Smtp extends Email
$connection = @fsockopen( ( ( $this->smtpProtocol == 'ssl' ) ? 'ssl://' : '' ) . $this->smtpHost, $this->smtpPort, $errno, $errstr, Request::isCliEnvironment() ? VERY_LONG_REQUEST_TIMEOUT : DEFAULT_REQUEST_TIMEOUT );
if ( !$connection )
{
throw new Exception( $errstr, $errno );
throw new Exception( $errstr ?? '', $errno ?? 0 );
}
static::$smtp[ $this->connectionKey ] = $connection;
register_shutdown_function(function( $object ){
+2 -3
View File
@@ -92,9 +92,8 @@ class Mapbox
$location = str_replace( ',', '.', $this->geolocation->long ) . ',' . str_replace( ',', '.', $this->geolocation->lat );
return Url::external( "https://api.mapbox.com/styles/v1/mapbox/streets-v10/static/pin-l-marker+f00({$location})/{$location},14,0,60/{$width}x{$height}@2x" )->setQueryString( array(
'access_token' => Settings::i()->mapbox_api_key,
'zoom' => $zoom
return Url::external( "https://api.mapbox.com/styles/v1/mapbox/streets-v10/static/pin-l-marker+f00({$location})/{$location},{$zoom},0,60/{$width}x{$height}@2x" )->setQueryString( array(
'access_token' => Settings::i()->mapbox_api_key
) );
}
}
+17 -1
View File
@@ -18,6 +18,7 @@ use http\Exception\InvalidArgumentException;
use IPS\Content\Comment;
use IPS\Content\Filter;
use IPS\Content\Item;
use IPS\Content\Reaction;
use IPS\Content\Taggable;
use IPS\DateTime;
use IPS\Db;
@@ -508,11 +509,26 @@ class Content extends Table
{
/* Get all reactions */
$allReputation = $class::getMany( 'allReactions', $rows );
$enabledReactions = [];
foreach(Reaction::enabledReactions() as $reaction )
{
$enabledReactions[] = $reaction->id;
}
foreach( $rows as $id => $item )
{
if ( isset( $allReputation[ $item->$idField ] ) )
{
$rows[ $id ]->reputation = $allReputation[ $item->$idField ];
/* Filter out any reactions that may have been deleted */
$rowReputation = [];
foreach( $allReputation[ $item->$idField ] as $k => $v )
{
if( in_array( $k, $enabledReactions ) )
{
$rowReputation[ $k ] = $v;
}
}
$rows[ $id ]->reputation = $rowReputation;
}
}
}
+29 -2
View File
@@ -24,6 +24,9 @@ use function get_called_class;
use function get_class;
use function is_array;
use function str_replace;
use function strrpos;
use function substr;
use function trim;
use function ucwords;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
@@ -350,10 +353,34 @@ class Log extends ActiveRecord
{
if( !empty( Settings::i()->debug_log_events ) )
{
/* This setting would have URLs or URL fragments that we should check */
/* This setting would listener classes and event names that we should check */
foreach( json_decode( Settings::i()->debug_log_events, true ) as $string )
{
$stringToCheck = '[event] => ' . $string;
$bits = explode( ":", $string );
/* Are we debugging this listener type? */
preg_match( '/\[listener] => (.+?)[\n\r]/is', $message, $match );
if( isset( $match[1] ) )
{
try
{
if( class_exists( $match[1] ) )
{
$parentClass = get_parent_class( $match[1] );
if( substr( $parentClass, strrpos( $parentClass, '\\' ) + 1 ) != $bits[0] )
{
continue;
}
}
}
catch( Exception )
{
continue;
}
}
/* Check if we are debugging this method */
$stringToCheck = '[event] => ' . trim( $bits[1] );
if( strpos( $message, $stringToCheck ) !== false )
{
return static::log( $message, $category );
+18 -12
View File
@@ -1118,24 +1118,28 @@ abstract class Model extends ActiveRecord
*/
public function checkAction( string $action ) : bool
{
/* Check bitoptions */
foreach( static::$bitOptions as $key => $values )
{
if( isset( $this->$key[ $action ] ) )
{
return (bool) $this->$key[ $action ];
}
}
/* Check the column mapping */
if( isset( static::$actionColumnMap[ $action ] ) )
{
$column = static::$actionColumnMap[ $action ];
return (bool) $this->$column;
}
else
{
/* Default to true because this means there is nothing in the node itself that should prevent this */
return TRUE;
}
/* Default to true because this means there is nothing in the node itself that should prevent this */
return TRUE;
/* Check bitoptions */
foreach( static::$bitOptions as $key => $values )
{
if( isset( $this->$key[ $column ] ) )
{
return (bool) $this->$key[ $column ];
}
}
/* Check the column mapping */
return (bool) $this->$column;
}
/**
@@ -3665,6 +3669,8 @@ abstract class Model extends ActiveRecord
*/
public function saveForm( array $values ): mixed
{
$values = array_merge( $values, $this->ui( 'processForm', array( $values ), true ) );
foreach ( $values as $k => $v )
{
if( $k == 'csrfKey' )
+7 -2
View File
@@ -1017,6 +1017,11 @@ class Output
*/
public bool $bypassCsrfKeyCheck = FALSE;
/**
* @var bool Flag to bypass datalayer in the page output
*/
public bool $bypassDataLayer = false;
/**
* Send output
*
@@ -1050,7 +1055,7 @@ class Output
}
/* Cache session Data Layer events */
if ( !( $httpStatusCode === 200 AND !Request::i()->isAjax() AND $contentType == 'text/html' ) )
if ( DataLayer::hasInstance() and !$this->bypassDataLayer and Dispatcher::hasInstance() and Dispatcher::i()->controllerLocation == 'front' and ( $httpStatusCode !== 200 OR Request::i()->isAjax() OR $contentType !== 'text/html' ) )
{
DataLayer::i()->cache();
}
@@ -1596,7 +1601,7 @@ class Output
}
if( isset( $this->sidebar['widgetareas']['sidebar'] ) and count( $this->sidebar['widgetareas']['sidebar'] ) )
if( isset( $this->sidebar['widgetareas']['sidebar'] ) and !empty( $this->sidebar['widgetareas']['sidebar'] ) )
{
return true;
}
+12
View File
@@ -94,6 +94,18 @@ abstract class Node
return [];
}
/**
* Any formatting necessary for custom fields added in formElements
*
* @param Model $node
* @param array $values
* @return array
*/
public function processForm( Model $node, array $values ) : array
{
return $values;
}
/**
* Called after the Node form is saved
*
+51 -23
View File
@@ -2690,11 +2690,36 @@ EOF;
{
$customValues = [];
/* Add custom settings that were added from other apps/themes */
foreach( new ActiveRecordIterator(
Db::i()->select( '*', 'core_theme_editor_settings', [
[ "(setting_app not in ('" . implode( "','", IPS::$ipsApps ) . "') OR (setting_set_id != ? and setting_set_id=?) )", 0, $this->_id ]
] ),
Setting::class
) as $setting )
{
$value = $setting->value( true );
if( is_array( $value ) )
{
foreach( $value as $_k => $_v )
{
$customValues[ $_k . '__' . $setting->key ] = $_v;
}
}
else
{
$customValues[ $setting->key ] = $value;
}
}
if( !( $flag & static::FORCE_DEFAULT ) )
{
if ( ! empty( $this->css_variables ) and $variables = json_decode( $this->css_variables, true ) )
{
$customValues = $variables;
foreach( $variables as $k => $v )
{
$customValues[ $k ] = $v;
}
}
if( ! empty( $this->view_options ) and $layouts = json_decode( $this->view_options, true ) )
@@ -2744,37 +2769,40 @@ EOF;
// Regular expression to find CSS variables
$pattern = '/--([\w-]+)\s*:\s*([^;]+)/';
preg_match_all( $pattern, $cssData, $matches );
$variableNames = $matches[1] ?? [];
$variableValues = $matches[2] ?? [];
// Extract variable names and values from the matches
if ( isset( $matches[1] ) and isset( $matches[2] ) )
/* Include any editor settings that are not in the main CSS file */
foreach( new ActiveRecordIterator(
Db::i()->select( '*', 'core_theme_editor_settings' ),
Setting::class
) as $setting )
{
$variableNames = $matches[1];
$variableValues = $matches[2];
/* Include any editor settings that are not in the main CSS file */
foreach( new ActiveRecordIterator(
Db::i()->select( '*', 'core_theme_editor_settings' ),
Setting::class
) as $setting )
try
{
$variableNames[] = $setting->key;
try
$value = $setting->value( ( $flag & static::FORCE_DEFAULT ) );
if( is_array( $value ) )
{
$variableValues[] = $setting->value( ( $flag & static::FORCE_DEFAULT ) );
foreach( $value as $_k => $_v )
{
$variableNames[] = $_k . '__' . $setting->key;
$variableValues[] = $_v;
}
}
catch( InvalidArgumentException $e )
else
{
Log::debug( $e, 'template_error' );
$variableValues[] = "";
$variableNames[] = $setting->key;
$variableValues[] = $value;
}
}
catch( InvalidArgumentException $e )
{
Log::debug( $e, 'template_error' );
$variableValues[] = "";
}
}
$this->cssVariablesCached[ $flag ] = array_merge( array_combine( $variableNames, $variableValues ), $customValues );
}
else
{
$this->cssVariablesCached[ $flag ] = [];
}
$this->cssVariablesCached[ $flag ] = array_merge( array_combine( $variableNames, $variableValues ), $customValues );
}
return $this->cssVariablesCached[ $flag ];