Version 4.3.0

This commit is contained in:
Neo committed 2025-12-19 05:47:12 -08:00
1 parent 96997ddd8e
commit fe1acf984a
1349 files changed
+116159 -67711

No files matched your search

+329 -117
View File
@@ -36,12 +36,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
* @brief [Content\Comment] Language prefix for forms
*/
public static $formLangPrefix = '';
/**
* @brief [Content\Item] Include this content type in user profiles
*/
public static $includeInUserProfiles = TRUE;
/**
* @brief Include In Sitemap
*/
@@ -52,6 +47,11 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
*/
protected $reputation;
/**
* @brief Can this content be moderated normally from the front-end (will be FALSE for things like Pages and Commerce Products)
*/
public static $canBeModeratedFromFrontend = TRUE;
/**
* Should posting this increment the poster's post count?
*
@@ -71,20 +71,23 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
*/
public static function memberPostCount( \IPS\Member $member )
{
return static::incrementPostCount() ? \IPS\Db::i()->select( 'COUNT(*)', static::$databaseTable, array( static::$databasePrefix . static::$databaseColumnMap['author'] . '=?', $member->member_id ) )->first() : 0;
return ( isset( static::$databaseColumnMap['author'] ) and static::incrementPostCount() ) ? \IPS\Db::i()->select( 'COUNT(*)', static::$databaseTable, array( static::$databasePrefix . static::$databaseColumnMap['author'] . '=?', $member->member_id ) )->first() : 0;
}
/**
* Load and check permissions
*
* @param mixed $id ID
* @param \IPS\Member|NULL $member Member, or NULL for logged in member
* @return static
* @throws \OutOfRangeException
*/
public static function loadAndCheckPerms( $id )
public static function loadAndCheckPerms( $id, \IPS\Member $member = NULL )
{
$obj = static::load( $id );
if ( !$obj->canView( \IPS\Member::loggedIn() ) )
$member = $member ?: \IPS\Member::loggedIn();
if ( !$obj->canView( $member ) )
{
throw new \OutOfRangeException;
}
@@ -264,7 +267,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
if ( isset( static::$databaseColumnMap['content'] ) )
{
$internal = \IPS\Db::i()->select( 'attachment_id', 'core_attachments_map', array( 'location_key=? and id1=?', static::$application . '_' . ucfirst( static::$module ), $this->$idColumn ) );
$internal = \IPS\Db::i()->select( 'attachment_id', 'core_attachments_map', array( 'location_key=? and id1=?', static::$application . '_' . mb_ucfirst( static::$module ), $this->$idColumn ) );
}
if ( $internal )
@@ -275,7 +278,8 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
}
}
return count( $attachments ) ? $attachments : NULL; }
return count( $attachments ) ? $attachments : NULL;
}
/**
* Text for use with data-ipsTruncate
@@ -293,6 +297,11 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
/* Convert headers and paragraphs into line breaks or just spaces */
$text = str_replace( array( '</p>', '</h1>', '</h2>', '</h3>', '</h4>', '</h5>', '</h6>' ), ( $oneLine ? ' ' : '<br>' ), $text );
if( $oneLine === TRUE )
{
$text = str_replace( '<br>', ' <br>', $text );
}
/* Add a space at the end of list items to prevent two list items from running into each other */
$text = str_replace( '</li>', ' </li>', $text );
@@ -300,7 +309,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
$text = strip_tags( $text, '<br>' );
/* Remove any <br>s from the start so there isn't just blank space at the top, but maintaining <br>s elsewhere */
$text = preg_replace( '/^(\s|<br>|' . chr(0xC2) . chr(0xA0) . ')+?/', '', $text );
$text = preg_replace( '/^(\s|<br>|' . chr(0xC2) . chr(0xA0) . ')+/', '', $text );
/* Return */
return $text;
@@ -320,13 +329,26 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
\IPS\Db::i()->delete( 'core_reputation_index', array( 'app=? AND type=? AND type_id=?', static::$application, $this->reactionType(), $this->$idColumn ) );
}
if ( \IPS\IPS::classUsesTrait( $this, 'IPS\Content\Reportable' ) )
{
$this->deleteReport();
}
/* Remove any entries in the promotions table */
\IPS\Db::i()->delete( 'core_social_promote', array( 'promote_class=? AND promote_class_id=?', get_class( $this ), $this->$idColumn ) );
\IPS\Db::i()->delete( 'core_deletion_log', array( "dellog_content_class=? AND dellog_content_id=?", get_class( $this ), $this->$idColumn ) );
if ( static::$hideLogKey )
{
$idColumn = static::$databaseColumnId;
\IPS\Db::i()->delete('core_soft_delete_log', array('sdl_obj_id=? AND sdl_obj_key=?', $this->$idColumn, static::$hideLogKey));
}
parent::delete();
$this->expireWidgetCaches();
$this->adjustSessions();
}
/**
@@ -473,12 +495,24 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
/* Per-container permission */
elseif ( $container )
{
return $container->modPermission( $type, $member );
return $container->modPermission( $type, $member, static::getContainerModPermissionClass() );
}
/* Still here? return false */
return FALSE;
}
/**
* Get the container item class to use for mod permission checks
*
* @return string|NULL
* @note By default we will return NULL and the container check will execute against Node::$contentItemClass, however
* in some situations we may need to override this (i.e. for Gallery Albums)
*/
protected static function getContainerModPermissionClass()
{
return NULL;
}
/**
* Do Moderator Action
@@ -510,7 +544,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
$toCheck = 'restore';
}
if ( !call_user_func( array( $this, 'can' . ucfirst( $toCheck ) ), $member ) )
if ( !call_user_func( array( $this, 'can' . mb_ucfirst( $toCheck ) ), $member ) )
{
throw new \OutOfRangeException;
}
@@ -579,7 +613,6 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
}
$idColumn = static::$databaseColumnId;
\IPS\Db::i()->delete( 'core_deletion_log', array( "dellog_content_class=? AND dellog_content_id=?", get_class( $this ), $this->$idColumn ) );
$this->delete();
return;
}
@@ -608,7 +641,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
/* Move is just a method */
elseif ( $action === 'move' )
{
$args = func_get_args();
$args = func_get_args();
$this->move( $args[2][0], $args[2][1] );
return;
}
@@ -638,27 +671,6 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
$column = static::$databaseColumnMap['approved'];
}
try
{
if ( $this->container() )
{
if ( $this->container()->_comments !== NULL )
{
/* Hidden content already decrements */
if ( !$this->hidden() )
{
$this->container()->_comments = ( $this->container()->_comments - $this->mapped('num_comments') );
}
}
if ( $this->container()->_unapprovedComments !== NULL )
{
$this->container()->_unapprovedComments = ( $this->container()->_unapprovedComments - $this->mapped( 'unapproved_comments' ) );
}
}
}
catch( \BadMethodCallException $e ) {}
$this->$column = -2;
$this->save();
@@ -686,6 +698,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
{
if ( $this->container() )
{
$this->container()->resetCommentCounts();
$this->container()->setLastComment();
$this->container()->save();
}
@@ -755,44 +768,13 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
/* Delete the log */
$log->delete();
/* Log to moderate logs */
if ( !$hidden )
/* Recount the container counters */
if( $this->container() )
{
try
{
if ( $this->container() )
{
if ( $this->container()->_comments !== NULL )
{
if ( $this instanceof \IPS\Content\Item )
{
$this->container()->_comments = ( $this->container()->_comments + $this->mapped('num_comments') );
}
else
{
$this->container()->_comments = $this->container()->_comments + 1;
}
}
if ( $this->container()->_unapprovedComments !== NULL )
{
if ( $this instanceof \IPS\Content\Item )
{
$this->container()->_unapprovedComments = ( $this->container()->_unapprovedComments + $this->mapped('unapproved_comments') );
}
}
$this->container()->setLastComment();
$this->container()->save();
if ( $this instanceof \IPS\Content\Comment )
{
$this->item()->rebuildFirstAndLastCommentData();
}
}
}
catch( \BadMethodCallException $e ) {}
$this->container()->resetCommentCounts();
$this->container()->setLastComment();
$this->container()->save();
}
}
@@ -805,7 +787,19 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
public function canRestore( $member=NULL )
{
$member = $member ?: \IPS\Member::loggedIn();
return \IPS\Member::loggedIn()->modPermission('can_restore_deleted_content');
return $member->modPermission('can_manage_deleted_content');
}
/**
* Give class a chance to inspect and manipulate search engine filters for streams
*
* @param array $filters Filters to be used for activity stream
* @param \IPS\Content\Search\Query $query Search query object
* @return void
*/
public static function searchEngineFiltering( &$filters, &$query )
{
/* Intentionally left blank but child classes can override */
}
/**
@@ -866,6 +860,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
}
$this->expireWidgetCaches();
$this->adjustSessions();
}
/**
@@ -924,7 +919,13 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
}
$this->save();
$this->onUnhide( $approving, $member );
if ( static::$hideLogKey )
{
$idColumn = static::$databaseColumnId;
\IPS\Db::i()->delete('core_soft_delete_log', array('sdl_obj_id=? AND sdl_obj_key=?', $this->$idColumn, static::$hideLogKey));
}
/* And update the tags perm cache */
if ( $this instanceof \IPS\Content\Tags )
{
@@ -937,6 +938,12 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
\IPS\Content\Search\Index::i()->index( $this );
}
/* Update report center stuff */
if ( \IPS\IPS::classUsesTrait( $this, 'IPS\Content\Reportable' ) )
{
$this->moderated( 'unhide' );
}
/* Send notifications if necessary */
if ( $approving )
{
@@ -944,8 +951,14 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
}
$this->expireWidgetCaches();
$this->adjustSessions();
}
/**
* @brief Hidden blurb cache
*/
protected $hiddenBlurb = NULL;
/**
* Blurb for when/why/by whom this content was hidden
*
@@ -958,16 +971,30 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
throw new \BadMethodCallException;
}
try
if( $this->hiddenBlurb === NULL )
{
$idColumn = static::$databaseColumnId;
$log = \IPS\Db::i()->select( '*', 'core_soft_delete_log', array( 'sdl_obj_id=? AND sdl_obj_key=?', $this->$idColumn, static::$hideLogKey ) )->first();
return \IPS\Member::loggedIn()->language()->addToStack('hidden_blurb', FALSE, array( 'sprintf' => array( \IPS\Member::load( $log['sdl_obj_member_id'] )->name, \IPS\DateTime::ts( $log['sdl_obj_date'] )->relative(), $log['sdl_obj_reason'] ?: \IPS\Member::loggedIn()->language()->addToStack('hidden_no_reason') ) ) );
}
catch ( \UnderflowException $e )
{
return \IPS\Member::loggedIn()->language()->addToStack('hidden');
try
{
$idColumn = static::$databaseColumnId;
$log = \IPS\Db::i()->select( '*', 'core_soft_delete_log', array( 'sdl_obj_id=? AND sdl_obj_key=?', $this->$idColumn, static::$hideLogKey ) )->first();
if ( $log['sdl_obj_member_id'] )
{
$this->hiddenBlurb = \IPS\Member::loggedIn()->language()->addToStack('hidden_blurb', FALSE, array( 'sprintf' => array( \IPS\Member::load( $log['sdl_obj_member_id'] )->name, \IPS\DateTime::ts( $log['sdl_obj_date'] )->relative(), \IPS\Member::loggedIn()->language()->addToStack( $log['sdl_obj_reason'] ) ?: \IPS\Member::loggedIn()->language()->addToStack('hidden_no_reason') ) ) );
}
else
{
$this->hiddenBlurb = \IPS\Member::loggedIn()->language()->addToStack('hidden_blurb_no_member', FALSE, array( 'sprintf' => array( \IPS\DateTime::ts( $log['sdl_obj_date'] )->relative(), \IPS\Member::loggedIn()->language()->addToStack( $log['sdl_obj_reason'] ) ?: \IPS\Member::loggedIn()->language()->addToStack('hidden_no_reason') ) ) );
}
}
catch ( \UnderflowException $e )
{
$this->hiddenBlurb = \IPS\Member::loggedIn()->language()->addToStack('hidden');
}
}
return $this->hiddenBlurb;
}
/**
@@ -1005,6 +1032,11 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
{
return \IPS\core\Promote::canPromote( $member );
}
/**
* @brief Have we already reported?
*/
protected $alreadyReported = NULL;
/**
* Can report?
@@ -1018,13 +1050,13 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
$member = $member ?: \IPS\Member::loggedIn();
/* Is this type of comment reportabe? */
if ( !( $this instanceof \IPS\Content\ReportCenter ) )
if ( !( \IPS\IPS::classUsesTrait( $this, 'IPS\Content\Reportable' ) ) )
{
return 'generic_error';
}
/* Can the member report content? */
if ( !$member->group['g_can_report'] )
if ( $member->group['g_can_report'] != '1' AND !in_array( get_class( $this ), explode( ',', $member->group['g_can_report'] ) ) )
{
return 'no_module_permission';
}
@@ -1036,28 +1068,71 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
}
/* Have they already subitted a report? */
$idColumn = static::$databaseColumnId;
$report = \IPS\Db::i()->select( 'id', 'core_rc_index', array( 'class=? AND content_id=?', get_called_class(), $this->$idColumn ) );
if ( count( $report ) )
if( $this->alreadyReported === TRUE )
{
$report = \IPS\Db::i()->select( '*', 'core_rc_reports', array( 'rid=? AND report_by=?', $report->first(), $member->member_id ) );
if ( count( $report ) )
return 'report_err_already_reported';
}
elseif( $this->alreadyReported === NULL )
{
$idColumn = static::$databaseColumnId;
try
{
$report = \IPS\Db::i()->select( 'id', 'core_rc_index', array( 'class=? AND content_id=?', get_called_class(), $this->$idColumn ) )->first();
$report = \IPS\Db::i()->select( '*', 'core_rc_reports', array( 'rid=? AND report_by=?', $report, $member->member_id ) )->first();
if ( \IPS\Settings::i()->automoderation_report_again_mins )
{
if ( ( ( time() - $report['date_reported'] ) / 60 ) > \IPS\Settings::i()->automoderation_report_again_mins )
{
return TRUE;
}
}
$this->alreadyReported = TRUE;
return 'report_err_already_reported';
}
catch( \UnderflowException $e ){}
$this->alreadyReported = FALSE;
}
return TRUE;
}
/**
* Can report or revoke report?
* This method will return TRUE if the link to report content should be shown (which can occur even if you have already reported if you have permission to revoke your report)
*
* @param \IPS\Member|NULL $member The member to check for (NULL for currently logged in member)
* @return bool
* @note This requires a few queries, so don't run a check in every template
*/
public function canReportOrRevoke( $member=NULL )
{
/* If we are allowed to report, then we can return TRUE. */
if( $this->canReport( $member ) === TRUE )
{
return TRUE;
}
/* If we have already reported but automatic moderation is enabled, show the link so the user can revoke their report. */
elseif( $this->alreadyReported === TRUE AND \IPS\Settings::i()->automoderation_enabled )
{
return TRUE;
}
return FALSE;
}
/**
* Report
*
* @param string $reportContent Report content message from member
* @return \\IPS\core\Reports\Report
* @param int $reportType Report type (see constants in \IPS\core\Reports\Report
* @return \IPS\core\Reports\Report
* @throws \UnexpectedValueException If there is a permission error - you should only call this method after checking canReport
*/
public function report( $reportContent )
public function report( $reportContent, $reportType=1 )
{
/* Permission check */
if ( $this->canReport() !== TRUE )
@@ -1084,6 +1159,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
$index->author = (int) $this->author()->member_id;
$index->num_reports = 1;
$index->num_comments = 0;
$index->auto_moderation_exempt = 0;
}
/* Only set this to a new report if it is not already under review */
@@ -1100,11 +1176,15 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
'report' => $reportContent,
'report_by' => (int) \IPS\Member::loggedIn()->member_id,
'date_reported' => time(),
'ip_address' => \IPS\Request::i()->ipAddress()
'ip_address' => \IPS\Request::i()->ipAddress(),
'report_type' => \IPS\Member::loggedIn()->member_id ? $reportType : 0
);
\IPS\Db::i()->insert( 'core_rc_reports', $reportInsert );
/* Run automatic moderation */
$index->runAutomaticModeration();
/* Send notification to mods */
$moderators = array( 'm' => array(), 'g' => array() );
foreach ( \IPS\Db::i()->select( '*', 'core_moderators' ) as $mod )
@@ -1149,6 +1229,11 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
{
$oldAuthor = $this->author();
if( $oldAuthor->member_id == $newAuthor->member_id )
{
return;
}
foreach ( array( 'author', 'author_name', 'edit_member_name' ) as $k )
{
if ( isset( static::$databaseColumnMap[ $k ] ) )
@@ -1211,6 +1296,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
/* Object URL */
$indefiniteArticle = static::_indefiniteArticle( $containerData );
$definiteArticle = static::_definiteArticle( $containerData );
$definiteArticleUc = static::_definiteArticle( $containerData, NULL, array( 'ucfirst' => TRUE ) );
if ( in_array( 'IPS\Content\Comment', class_parents( get_called_class() ) ) )
{
if ( in_array( 'IPS\Content\Review', class_parents( get_called_class() ) ) )
@@ -1226,13 +1312,14 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
$indefiniteArticle = $itemClass::_indefiniteArticle( $containerData );
$definiteArticle = $itemClass::_definiteArticle( $containerData );
$definiteArticleUc = $itemClass::_definiteArticle( $containerData, NULL, array( 'ucfirst' => TRUE ) );
}
else
{
$objectUrl = $itemUrl;
$showRepUrl = $itemUrl->setQueryString( 'do', 'showReactions' );
}
$articles = array( 'indefinite' => $indefiniteArticle, 'definite' => $definiteArticle );
$articles = array( 'indefinite' => $indefiniteArticle, 'definite' => $definiteArticle, 'definite_uc' => $definiteArticleUc );
/* Container details */
$containerUrl = NULL;
@@ -1244,8 +1331,23 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
$containerUrl = $containerClass::urlFromIndexData( $indexData, $itemData, $containerData );
}
/* Reputation */
$repCount = array_sum( $reputationData );
/* Reputation - if we are showing the total value, then we need to load them up and total up all of the values */
if ( \IPS\Settings::i()->reaction_count_display == 'count' )
{
$repCount = 0;
foreach( $reputationData AS $memberId => $reactionId )
{
try
{
$repCount += \IPS\Content\Reaction::load( $reactionId )->value;
}
catch( \OutOfRangeException $e ) {}
}
}
else
{
$repCount = count( $reputationData );
}
/* Snippet */
$snippet = static::searchResultSnippet( $indexData, $authorData, $itemData, $containerData, $reputationData, $reviewRating, $view );
@@ -1276,6 +1378,78 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
return $view == 'expanded' ? \IPS\Theme::i()->getTemplate( 'system', 'core', 'front' )->searchResultSnippet( $indexData ) : '';
}
/**
* Return the language string key to use in search results
*
* @note Normally we show "(user) posted a (thing) in (area)" but sometimes this may not be accurate, so this is abstracted to allow
* content classes the ability to override
* @param array $authorData Author data
* @param array $articles Articles language strings
* @param array $indexData Search index data
* @param array $itemData Data about the item
* @return string
*/
public static function searchResultSummaryLanguage( $authorData, $articles, $indexData, $itemData )
{
if( in_array( 'IPS\Content\Comment', class_parents( $indexData['index_class'] ) ) )
{
if( in_array( 'IPS\Content\Review', class_parents( $indexData['index_class'] ) ) )
{
if( isset( $itemData['author'] ) )
{
return \IPS\Member::loggedIn()->language()->addToStack( "user_other_activity_review", FALSE, array( 'sprintf' => array( $authorData['name'], $itemData['author']['name'], $articles['definite'] ) ) );
}
else
{
return \IPS\Member::loggedIn()->language()->addToStack( "user_own_activity_review", FALSE, array( 'sprintf' => array( $authorData['name'], $articles['indefinite'] ) ) );
}
}
else
{
if( static::$firstCommentRequired )
{
if( $indexData['index_title'] )
{
return \IPS\Member::loggedIn()->language()->addToStack( "user_own_activity_item", FALSE, array( 'sprintf' => array( $authorData['name'], $articles['indefinite'] ) ) );
}
else
{
if( isset( $itemData['author'] ) )
{
return \IPS\Member::loggedIn()->language()->addToStack( "user_other_activity_reply", FALSE, array( 'sprintf' => array( $authorData['name'], $itemData['author']['name'], $articles['definite'] ) ) );
}
else
{
return \IPS\Member::loggedIn()->language()->addToStack( "user_own_activity_reply", FALSE, array( 'sprintf' => array( $authorData['name'], $articles['indefinite'] ) ) );
}
}
}
else
{
if( isset( $itemData['author'] ) )
{
return \IPS\Member::loggedIn()->language()->addToStack( "user_other_activity_comment", FALSE, array( 'sprintf' => array( $authorData['name'], $itemData['author']['name'], $articles['definite'] ) ) );
}
else
{
return \IPS\Member::loggedIn()->language()->addToStack( "user_own_activity_comment", FALSE, array( 'sprintf' => array( $authorData['name'], $articles['indefinite'] ) ) );
}
}
}
}
else
{
if ( isset( static::$databaseColumnMap['author'] ) )
{
return \IPS\Member::loggedIn()->language()->addToStack( "user_own_activity_item", FALSE, array( 'sprintf' => array( $authorData['name'], $articles['indefinite'] ) ) );
}
else
{
return \IPS\Member::loggedIn()->language()->addToStack( "generic_activity_item", FALSE, array( 'sprintf' => array( $articles['definite_uc'] ) ) );
}
}
}
/**
* @brief Return a classname applied to the search result block
*/
@@ -1334,7 +1508,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
const FOLLOW_PUBLIC = 1;
const FOLLOW_ANONYMOUS = 2;
const NOTIFICATIONS_PER_BATCH = 50;
const NOTIFICATIONS_PER_BATCH = \IPS\NOTIFICATIONS_PER_BATCH;
/**
* Send notifications
@@ -1360,7 +1534,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
/* Queue if there's lots, or just send them */
if ( $count > static::NOTIFICATIONS_PER_BATCH )
{
\IPS\Task::queue( 'core', 'Follow', array( 'class' => get_class( $this ), 'item' => $this->$idColumn, 'sentTo' => $sentTo ), 1 );
\IPS\Task::queue( 'core', 'Follow', array( 'class' => get_class( $this ), 'item' => $this->$idColumn, 'sentTo' => $sentTo ), 2 );
}
else
{
@@ -1543,7 +1717,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
foreach ( $data['quotes'] as $quote )
{
$member = \IPS\Member::load( $quote );
if ( $member->member_id and $member != $this->author() and $this->canView( $member ) )
if ( $member->member_id and $member != $this->author() and $this->canView( $member ) and !$member->isIgnoring( $this->author(), 'posts' ) )
{
$notification->recipients->attach( $member );
}
@@ -1562,7 +1736,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
foreach ( $data['mentions'] as $mention )
{
$member = \IPS\Member::load( $mention );
if ( $member != $this->author() and $this->canView( $member ) and !$member->isIgnoring( $this->author(), 'mentions' ) )
if ( $member->member_id AND $member != $this->author() and $this->canView( $member ) and !$member->isIgnoring( $this->author(), 'mentions' ) )
{
$notification->recipients->attach( $member );
}
@@ -1622,6 +1796,39 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
\IPS\Widget::deleteCaches( NULL, static::$application );
}
/**
* Update "currently viewing" session data after moderator actions that invalidate that data for other users
*
* @return void
*/
public function adjustSessions()
{
if( $this instanceof \IPS\Content\Comment )
{
$item = $this->item();
}
else
{
$item = $this;
}
/* We have to send a limit even though we want all records because otherwise the Database store does not return all columns */
foreach( \IPS\Session\Store::i()->getOnlineUsers( 0, 'desc', array( 0, 5000 ), NULL, TRUE, TRUE ) as $session )
{
if( mb_strpos( $session['location_url'], (string) $item->url() ) === 0 )
{
$sessionData = $session;
$sessionData['location_url'] = NULL;
$sessionData['location_lang'] = NULL;
$sessionData['location_data'] = json_encode( array() );
$sessionData['current_id'] = 0;
$sessionData['location_permissions'] = 0;
\IPS\Session\Store::i()->updateSession( $sessionData );
}
}
}
/**
* Fetch classes from content router
*
@@ -1755,13 +1962,15 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
/**
* Definite Article
*
* @param \IPS\Lang|NULL $language The language to use, or NULL for the language of the currently logged in member
* @param array $containerData Basic data about the container. Only includes columns returned by container::basicDataColumns()
* @param \IPS\Lang|NULL $language The language to use, or NULL for the language of the currently logged in member
* @param array $options Options to pass to \IPS\Lang::addToStack
* @return string
*/
public static function _definiteArticle( array $containerData = NULL, \IPS\Lang $lang = NULL )
public static function _definiteArticle( array $containerData = NULL, \IPS\Lang $lang = NULL, $options = array() )
{
$lang = $lang ?: \IPS\Member::loggedIn()->language();
return $lang->addToStack( '__defart_' . static::$title, FALSE );
return $lang->addToStack( '__defart_' . static::$title, FALSE, $options );
}
/**
@@ -1830,22 +2039,25 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
}
}
/* !Deprecated */
/* The methods below are deprecated and will be removed in Invision Community 4.3 */
/* !Search */
/**
* Get reputation count
* Title for search index
*
* @return int
* @throws \BadMethodCallException
* @return string
*/
public function reputation()
public function searchIndexTitle()
{
if ( !\IPS\IPS::classUsesTrait( $this, 'IPS\Content\Reactable' ) )
{
throw new \BadMethodCallException;
}
return $this->reactionCount();
return $this->mapped('title');
}
/**
* Content for search index
*
* @return string
*/
public function searchIndexContent()
{
return $this->mapped('content');
}
}