Version 4.7.0

This commit is contained in:
Neo committed 2025-12-19 16:00:35 -08:00
1 parent 517a5e1f70
commit 8ba7d43898
1933 files changed
+65613 -11332

No files matched your search

+210 -20
View File
@@ -59,15 +59,18 @@ abstract class _Comment extends \IPS\Content
* @param \IPS\DateTime|NULL $time The time
* @param string|NULL $ipAddress The IP address or NULL to detect automatically
* @param int|NULL $hiddenStatus NULL to set automatically or override: 0 = unhidden; 1 = hidden, pending moderator approval; -1 = hidden (as if hidden by a moderator)
* @param int|NULL $anonymous NULL for no value, 0 or 1 for a value (0=no, 1=yes)
* @return static
*/
public static function create( $item, $comment, $first=FALSE, $guestName=NULL, $incrementPostCount=NULL, $member=NULL, \IPS\DateTime $time=NULL, $ipAddress=NULL, $hiddenStatus=NULL )
public static function create( $item, $comment, $first=FALSE, $guestName=NULL, $incrementPostCount=NULL, $member=NULL, \IPS\DateTime $time=NULL, $ipAddress=NULL, $hiddenStatus=NULL, $anonymous=NULL )
{
if ( $member === NULL )
{
$member = \IPS\Member::loggedIn();
}
$idColumn = $item::$databaseColumnId;
/* Create the object */
$obj = new static;
foreach ( array( 'item', 'date', 'author', 'author_name', 'content', 'ip_address', 'first', 'approved', 'hidden' ) as $k )
@@ -78,7 +81,6 @@ abstract class _Comment extends \IPS\Content
switch ( $k )
{
case 'item':
$idColumn = $item::$databaseColumnId;
$val = $item->$idColumn;
break;
@@ -184,42 +186,52 @@ abstract class _Comment extends \IPS\Content
}
/* Check if profanity filters should mod-queue this comment */
$obj->checkProfanityFilters( $first );
$obj->checkProfanityFilters( $first, FALSE );
/* Save the comment */
$obj->save();
/* Set anonymous status */
if( $anonymous !== NULL )
{
try
{
$obj->setAnonymous( (bool) $anonymous, $member );
}
catch ( \Exception $e ) { }
}
/* Increment post count */
try
{
if ( !$obj->isAnonymous() and !$obj->hidden() and ( $incrementPostCount === TRUE or ( $incrementPostCount === NULL and static::incrementPostCount( $item->container() ) ) ) )
{
$obj->author()->member_posts++;
$member->member_posts++;
}
}
catch( \BadMethodCallException $e ) { }
/* Update member's last post and daily post limits */
if( $obj->author()->member_id AND $obj::incrementPostCount() )
if( $member->member_id AND $obj::incrementPostCount() )
{
$obj->author()->member_last_post = time();
$member->member_last_post = time();
/* Update posts per day limits */
if ( $obj->author()->group['g_ppd_limit'] )
if ( $member->group['g_ppd_limit'] )
{
$current = $obj->author()->members_day_posts;
$current = $member->members_day_posts;
$current[0] += 1;
if ( $current[1] == 0 )
{
$current[1] = \IPS\DateTime::create()->getTimestamp();
}
$obj->author()->members_day_posts = $current;
$member->members_day_posts = $current;
}
$obj->author()->save();
$member->save();
}
/* Send notifications and dish out points */
@@ -228,7 +240,7 @@ abstract class _Comment extends \IPS\Content
if ( !$obj->hidden() and ( !$first or !$item::$firstCommentRequired ) )
{
$obj->sendNotifications();
$obj->author()->achievementAction( 'core', 'Comment', $obj );
$member->achievementAction( 'core', 'Comment', $obj );
}
else if( $obj->hidden() === 1 )
{
@@ -241,10 +253,45 @@ abstract class _Comment extends \IPS\Content
{
\IPS\Api\Webhook::fire( str_replace( '\\', '', \substr( \get_called_class(), 3 ) ) . '_create', $obj, $obj->webhookFilters() );
}
/* DataLayer Event */
if ( \IPS\Settings::i()->core_datalayer_enabled and !( $item::$firstCommentRequired and $obj->isFirst() ) )
{
if ( isset( $item::$title ) and $item::$title === \IPS\core\Statuses\Status::$title )
{
$event = 'social_reply';
$member = \IPS\Member::load( $item->member_id );
$properties = array(
'profile_id' => $member->member_id,
'profile_name' => $member->name,
);
}
else
{
$event = 'comment_create';
$properties = $obj->getDataLayerProperties();
}
if ( $properties['content_area'] === 'personal_messages' )
{
return;
}
$properties = array_merge( $properties, array(
'author_id' => $obj->author()->member_id,
'author_name' => $obj->author()->name ?? 'Guest',
) );
\IPS\core\DataLayer::i()->addEvent( $event, $properties );
}
/* Update item */
$obj->postCreate();
/* This needs to happen before the index is added */
$parameters = array_merge( array( 'reply-' . $item::$application . '/' . $item::$module . '-' . $item->$idColumn ), $obj->attachmentIds() );
\IPS\File::claimAttachments( ...$parameters );
/* Add to search index */
if ( $obj instanceof \IPS\Content\Searchable )
{
@@ -254,6 +301,32 @@ abstract class _Comment extends \IPS\Content
/* Return */
return $obj;
}
/**
* @brief Field cache for getDataLayerProperties
*/
protected $_dataLayerProperties = array();
/**
* Get the properties that can be added to the datalayer for this key
*
* @return array
*/
public function getDataLayerProperties()
{
if ( empty( $this->_dataLayerProperties ) )
{
$item = $this->item();
$this->_dataLayerProperties = $item->getDataLayerProperties( $this );
if ( $this->_dataLayerProperties['content_type'] === 'question' )
{
$this->_dataLayerProperties['comment_type'] = 'answer';
}
$this->_dataLayerProperties = \IPS\core\DataLayer::i()->filterProperties( $this->_dataLayerProperties );
}
return $this->_dataLayerProperties;
}
/**
* Join profile fields when loading comments?
@@ -376,6 +449,18 @@ abstract class _Comment extends \IPS\Content
{
$item->container()->rebuildPostedIn( array( $item->$itemIdColumn ), array( $this->author() ) );
}
/* Update trending */
$class = \get_class( $item );
if( \IPS\Application::appIsEnabled( 'cloud' ) and \IPS\cloud\Realtime::i()->isEnabled('trending') and $class::$includeInTrending )
{
/* Score by timestamp for trending */
try
{
\IPS\Redis::i()->zIncrBy( 'trending', time(), $class . '__' . $item->$itemIdColumn );
}
catch( \Exception $e ) { }
}
}
/**
@@ -827,6 +912,12 @@ abstract class _Comment extends \IPS\Content
{
$item->clearCachedStatistics();
}
/* Send the content items URL to IndexNow if the guest can view it */
if( $item->canView( new \IPS\Member ) )
{
\IPS\core\IndexNow::addUrlToQueue( $item->url() );
}
}
/**
@@ -844,6 +935,48 @@ abstract class _Comment extends \IPS\Content
$itemColumn = static::$databaseColumnMap['item'];
$commentIdColumn = static::$databaseColumnId;
$this->$itemColumn = $item->$idColumn;
/* Does this comment's hidden status align with its item's hidden status? */
if ( $this instanceof \IPS\Content\Hideable AND $this->hidden() !== 1 )
{
if ( $item->hidden() === -1 )
{
if ( isset( static::$databaseColumnMap['hidden'] ) )
{
$column = static::$databaseColumnMap['hidden'];
/* if this comment was directly hidden, we don't want to inherit its status from the item */
if ( \intval( $this->$column ) !== -1 )
{
$this->$column = 2;
}
}
elseif ( isset( static::$databaseColumnMap['approved'] ) )
{
$column = static::$databaseColumnMap['approved'];
$this->$column = -1;
}
}
elseif ( $this->hidden() === -1 )
{
if ( isset( static::$databaseColumnMap['hidden'] ) )
{
$column = static::$databaseColumnMap['hidden'];
/* if this comment was directly hidden, we don't want to inherit its status from the item */
if ( \intval( $this->$column ) !== -1 )
{
$this->$column = 0;
}
}
elseif ( isset( static::$databaseColumnMap['approved'] ) )
{
$column = static::$databaseColumnMap['approved'];
$this->$column = 1;
}
}
}
$this->save();
/* The new item needs to re-claim any attachments associated with this comment */
@@ -865,7 +998,16 @@ abstract class _Comment extends \IPS\Content
}
/* Update member map */
\IPS\Db::i()->update( 'core_item_member_map', array( 'map_item_id' => $item->$idColumn ), array( 'map_class=? and map_item_id=?', \get_class( $oldItem ), $oldItem->$idColumn ) );
try
{
\IPS\Db::i()->update( 'core_item_member_map', array( 'map_item_id' => $item->$idColumn ), array( 'map_class=? and map_item_id=?', \get_class( $oldItem ), $oldItem->$idColumn ) );
}
catch( \IPS\Db\Exception $e )
{
/* If we are splitting to a topic that this member has already posted in, this will cause a duplicate key issue. Best to remove rows and let the next page load rebuild */
\IPS\Db::i()->delete( 'core_item_member_map', array( 'map_class=? and map_item_id=?', \get_class( $oldItem ), $oldItem->$idColumn ) );
\IPS\Db::i()->delete( 'core_item_member_map', array( 'map_class=? and map_item_id=?', \get_class( $item ), $item->$idColumn ) );
}
if( $skip === FALSE )
{
@@ -883,6 +1025,23 @@ abstract class _Comment extends \IPS\Content
$item->clearCachedStatistics();
}
}
/* Send the content items URL to IndexNow */
$noIndexUrls = [];
$guest = new \IPS\Member;
if( $oldItem->canView( $guest ) )
{
$noIndexUrls[] = $oldItem->url();
}
if( $item->canView( $guest ) )
{
$noIndexUrls[] = $item->url();
}
if( $oldItem->canView( new \IPS\Member ) )
{
\IPS\core\IndexNow::addUrlsToQueue( $noIndexUrls );
}
}
/**
@@ -1505,7 +1664,7 @@ abstract class _Comment extends \IPS\Content
$attachments[] = array( 'core_Clubs' => $club->cover_photo );
}
return \count( $attachments ) ? $attachments : NULL;
return \count( $attachments ) ? \array_slice( $attachments, 0, $limit ) : NULL;
}
/**
@@ -1593,12 +1752,13 @@ abstract class _Comment extends \IPS\Content
* @apiresponse string content The content
* @apiresponse bool hidden Is hidden?
* @apiresponse string url URL to content
* @apiresponse array reactions Array of reactions given, array key is member_id of reaction giver ([member_id => [ title, id, value, icon ]])
*/
public function apiOutput( \IPS\Member $authorizedMember = NULL )
{
$idColumn = static::$databaseColumnId;
$itemColumn = static::$databaseColumnMap['item'];
return array(
$return = array(
'id' => $this->$idColumn,
'item_id' => $this->$itemColumn,
'author' => $this->author()->apiOutput( $authorizedMember ),
@@ -1607,6 +1767,35 @@ abstract class _Comment extends \IPS\Content
'hidden' => (bool) $this->hidden(),
'url' => (string) $this->url()
);
if ( \IPS\IPS::classUsesTrait( $this, 'IPS\Content\Reactable' ) )
{
if ( $reactions = $this->reactions() )
{
$enabledReactions = \IPS\Content\Reaction::enabledReactions();
$finalReactions = [];
foreach( $reactions as $memberId => $array )
{
foreach( $array as $reaction )
{
$finalReactions[ $memberId ][] = [
'title' => $enabledReactions[ $reaction ]->_title,
'id' => $reaction,
'value' => $enabledReactions[ $reaction ]->value,
'icon' => (string) $enabledReactions[ $reaction ]->_icon->url
];
}
}
$return['reactions'] = $finalReactions;
}
else
{
$return['reactions'] = [];
}
}
return $return;
}
/**
@@ -1618,9 +1807,10 @@ abstract class _Comment extends \IPS\Content
{
$filters = parent::webhookFilters();
$item = $this->item();
/* This is kept for legacy compatibility, but starting with 4.6.13 the Content and Item class handle this inside their own webhookFilter methods */
$filters['hidden'] = ( ( (bool) $this->hidden() ) or ( (bool) $item->hidden() ) );
if ( \in_array( 'IPS\Content\Lockable', class_implements( $item ) ) )
{
$filters['locked'] = $item->locked();