Version 5.0.0 beta 7
This commit is contained in:
1 parent
fa2a720105
commit
54d5dcd0a0
156 files changed
+4337
-2995
No files matched your search
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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,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' ] );
|
||||
}
|
||||
|
||||
@@ -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 ){
|
||||
|
||||
@@ -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
|
||||
) );
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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' )
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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 ];
|
||||
|
||||
Reference in new issue
Block a user