Version 4.7.0
This commit is contained in:
1 parent
517a5e1f70
commit
8ba7d43898
1933 files changed
+65613
-11332
No files matched your search
+418
-105
@@ -402,7 +402,9 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
|
||||
\IPS\Db::i()->delete( 'core_content_featured', [ 'feature_content_id=? and feature_content_class=?', $this->$idColumn, \get_class( $this ) ] );
|
||||
|
||||
|
||||
\IPS\Api\Webhook::fire( str_replace( '\\', '', \substr( \get_called_class(), 3 ) ) . '_delete', $this, $this->webhookFilters() );
|
||||
|
||||
parent::delete();
|
||||
|
||||
$this->expireWidgetCaches();
|
||||
@@ -444,19 +446,22 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
|
||||
/**
|
||||
* Check comment against profanity filters
|
||||
* Check comment against profanity filters but do not act on it
|
||||
*
|
||||
* @param bool $first Is this the first comment?
|
||||
* @param bool $edit Are we editing or merging (true) or is this a new comment (false)?
|
||||
* @param string|NULL $content The content to check - useful for if the content needs to be checked first, before it gets saved to the database.
|
||||
* @param string|NULL|bool $title The title of the content to check, or NULL to check the current title, or FALSE to not check at all.
|
||||
* @return bool Whether to send unapproved notifications
|
||||
* @param bool $first Is this the first comment?
|
||||
* @param bool $edit Are we editing or merging (true) or is this a new comment (false)?
|
||||
* @param string|NULL $content The content to check - useful for if the content needs to be checked first, before it gets saved to the database.
|
||||
* @param string|NULL|bool $title The title of the content to check, or NULL to check the current title, or FALSE to not check at all.
|
||||
* @param string $autoSaveLocation The autosave location key of any editors to check, or NULL to use the default.
|
||||
* @param string|NULL $autoSaveKeys The autosave keys (for new content) or attach lookup ids (for an edit) of any editors to check, or NULL to use the default.
|
||||
* @param array $imageUploads Images that have been uploaded that may require moderation
|
||||
* @return bool Whether to send unapproved notifications (i.e. true if the content was hidden)
|
||||
*/
|
||||
public function checkProfanityFilters( $first=FALSE, $edit=TRUE, $content=NULL, $title=NULL )
|
||||
{
|
||||
public function shouldTriggerProfanityFilters( $first=FALSE, $edit=TRUE, $content=NULL, $title=NULL, $autoSaveLocation=NULL, $autoSaveKeys=NULL, $imageUploads=[], &$hiddenByFilter = FALSE, &$itemHiddenByFilter = FALSE )
|
||||
{
|
||||
/* Set our content */
|
||||
$content = $content ?: $this->content();
|
||||
|
||||
|
||||
/* We need our item */
|
||||
$item = $this;
|
||||
if ( $this instanceof \IPS\Content\Comment )
|
||||
@@ -482,31 +487,78 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* And the author */
|
||||
/* Check the author cannot bypass */
|
||||
$member = $this->author();
|
||||
|
||||
/* Pass this through our profanity filters to see if it needs to be mod queued */
|
||||
$sendNotifications = FALSE;
|
||||
|
||||
/* Do we need to recount item comments and container comments? */
|
||||
$recount = FALSE;
|
||||
|
||||
try
|
||||
if ( $member->group['g_bypass_badwords'] )
|
||||
{
|
||||
$hiddenByFilter = FALSE;
|
||||
/* If this is a content item that doesn't require a first comment, or a comment that isn't the first comment, this just pass the content as it stands. */
|
||||
if ( !$this->hidden() AND ( ( $this instanceof \IPS\Content\Item ) OR ( $this instanceof \IPS\Content\Comment AND ( !$item::$firstCommentRequired OR ( $item::$firstCommentRequired AND !$first ) ) ) ) AND !$member->group['g_bypass_badwords'] )
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* First, check the image scanner */
|
||||
$idColumn = static::$databaseColumnId;
|
||||
$itemIdColumn = $item::$databaseColumnId;
|
||||
if ( \IPS\Settings::i()->ips_imagescanner_enable )
|
||||
{
|
||||
if ( $edit )
|
||||
{
|
||||
$autoSaveLocation = $autoSaveLocation ?: ( $item::$application . '_' . mb_ucfirst( $item::$module ) );
|
||||
|
||||
if ( !$autoSaveKeys )
|
||||
{
|
||||
if ( $this instanceof \IPS\Content\Comment and ( !( $this instanceof \IPS\Content\Review ) or !$first or !$item::$firstCommentRequired ) )
|
||||
{
|
||||
$autoSaveKeys = [ $this->attachmentIds() ];
|
||||
}
|
||||
else
|
||||
{
|
||||
$autoSaveKeys = [ [ $item->$itemIdColumn ] ];
|
||||
}
|
||||
}
|
||||
|
||||
$clauses = [];
|
||||
$binds = [];
|
||||
foreach ( $autoSaveKeys as $_autoSaveKeys )
|
||||
{
|
||||
$idLookups = [];
|
||||
foreach ( range( 1, \count( $_autoSaveKeys ) ) as $i )
|
||||
{
|
||||
$idLookups[]= "id{$i}=?";
|
||||
}
|
||||
$clauses[] = '( ' . implode( ' AND ', $idLookups ) . ' )';
|
||||
$binds = array_merge( $binds, $_autoSaveKeys );
|
||||
}
|
||||
|
||||
$hiddenByFilter = \IPS\core\Profanity::hiddenByFilters( $content, $member );
|
||||
|
||||
if ( $title AND !$hiddenByFilter )
|
||||
{
|
||||
$hiddenByFilter = \IPS\core\Profanity::hiddenByFilters( $title, $member );
|
||||
}
|
||||
|
||||
$attachmentsMapWhere = array_merge( array( 'location_key=? AND ( ' . implode( ' OR ', $clauses ) . ' )', $autoSaveLocation ), $binds );
|
||||
}
|
||||
/* If the comment is hidden (probably requires moderator approval) and this is the first post in the item that requires a comment (i.e. a post in a topic), then we need to set the item as requiring approval instead. */
|
||||
elseif ( $item instanceof \IPS\Content\Hideable AND !$member->group['g_bypass_badwords'] AND ( $item::$firstCommentRequired and $first ) )
|
||||
else
|
||||
{
|
||||
if ( $autoSaveKeys === NULL )
|
||||
{
|
||||
if ( $this instanceof \IPS\Content\Item or ( $first and $item::$firstCommentRequired ) )
|
||||
{
|
||||
$container = $item->containerWrapper();
|
||||
$autoSaveKeys[] = 'newContentItem-' . $item::$application . '/' . $item::$module . '-' . ( $container ? $container->_id : 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( $this instanceof \IPS\Content\Review )
|
||||
{
|
||||
$autoSaveKeys[] = 'review-' . $item::$application . '/' . $item::$module . '-' . $item->$itemIdColumn;
|
||||
}
|
||||
else
|
||||
{
|
||||
$autoSaveKeys[] = 'reply-' . $item::$application . '/' . $item::$module . '-' . $item->$itemIdColumn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$itemHiddenByFilter = \IPS\core\Profanity::hiddenByFilters( $content, $member );
|
||||
|
||||
if ( $title AND !$itemHiddenByFilter )
|
||||
@@ -514,54 +566,142 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
|
||||
$itemHiddenByFilter = \IPS\core\Profanity::hiddenByFilters( $title, $member );
|
||||
}
|
||||
|
||||
if ( isset( $item::$databaseColumnMap['approved'] ) and $itemHiddenByFilter )
|
||||
$attachmentsMapWhere = \IPS\Db::i()->in( 'temp', array_map( 'md5', $autoSaveKeys ) );
|
||||
}
|
||||
|
||||
foreach ( \IPS\Db::i()->select( 'attach_moderation_status', 'core_attachments', [ 'attach_id IN (?)', \IPS\Db::i()->select( 'attachment_id', 'core_attachments_map', $attachmentsMapWhere ) ] ) as $attachModerationStatus )
|
||||
{
|
||||
if ( $attachModerationStatus === 'pending' )
|
||||
{
|
||||
/* 'approved' is easy, clear and concise */
|
||||
$column = $item::$databaseColumnMap['approved'];
|
||||
$item->$column = 0;
|
||||
$item->save();
|
||||
$recount = TRUE;
|
||||
if ( $this instanceof \IPS\Content\Item or ( $first and $item::$firstCommentRequired ) )
|
||||
{
|
||||
$itemHiddenByFilter = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
$hiddenByFilter = TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
elseif ( isset( $item::$databaseColumnMap['hidden'] ) and $itemHiddenByFilter )
|
||||
}
|
||||
|
||||
if ( !$hiddenByFilter and !$itemHiddenByFilter )
|
||||
{
|
||||
foreach ( $imageUploads as $file )
|
||||
{
|
||||
/* 'hidden' is backwards */
|
||||
$column = $item::$databaseColumnMap['hidden'];
|
||||
$item->$column = 1;
|
||||
$item->save();
|
||||
$recount = TRUE;
|
||||
if ( $file->requiresModeration )
|
||||
{
|
||||
if ( $this instanceof \IPS\Content\Item or ( $first and $item::$firstCommentRequired ) )
|
||||
{
|
||||
$itemHiddenByFilter = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
$hiddenByFilter = TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( $edit === TRUE )
|
||||
/* Then pass this through our profanity and link filters */
|
||||
try
|
||||
{
|
||||
if ( $this instanceof \IPS\Content\Item or ( $first and $item::$firstCommentRequired ) )
|
||||
{
|
||||
if ( !$itemHiddenByFilter )
|
||||
{
|
||||
$sendNotifications = TRUE;
|
||||
$itemHiddenByFilter = \IPS\core\Profanity::hiddenByFilters( $content );
|
||||
}
|
||||
if ( !$itemHiddenByFilter and $title )
|
||||
{
|
||||
$itemHiddenByFilter = \IPS\core\Profanity::hiddenByFilters( $title );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !$hiddenByFilter )
|
||||
{
|
||||
$hiddenByFilter = \IPS\core\Profanity::hiddenByFilters( $content );
|
||||
}
|
||||
if ( !$hiddenByFilter and $title )
|
||||
{
|
||||
$hiddenByFilter = \IPS\core\Profanity::hiddenByFilters( $title );
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( \BadMethodCallException $e ) { }
|
||||
|
||||
if ( $hiddenByFilter === TRUE )
|
||||
/* Return */
|
||||
return ( $hiddenByFilter or $itemHiddenByFilter );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check comment against profanity filters AND act on it
|
||||
*
|
||||
* @param bool $first Is this the first comment?
|
||||
* @param bool $edit Are we editing or merging (true) or is this a new comment (false)?
|
||||
* @param string|NULL $content The content to check - useful for if the content needs to be checked first, before it gets saved to the database.
|
||||
* @param string|NULL|bool $title The title of the content to check, or NULL to check the current title, or FALSE to not check at all.
|
||||
* @param string $autoSaveLocation The autosave location key of any editors to check, or NULL to use the default.
|
||||
* @param string|NULL $autoSaveKeys The autosave keys (for new content) or attach lookup ids (for an edit) of any editors to check, or NULL to use the default.
|
||||
* @param array $imageUploads Images that have been uploaded that may require moderation
|
||||
* @return bool Whether to send unapproved notifications (i.e. true if the content was hidden)
|
||||
*/
|
||||
public function checkProfanityFilters( $first=FALSE, $edit=TRUE, $content=NULL, $title=NULL, $autoSaveLocation=NULL, $autoSaveKeys=NULL, $imageUploads=[] )
|
||||
{
|
||||
/* Check it */
|
||||
$hiddenByFilter = FALSE;
|
||||
$itemHiddenByFilter = FALSE;
|
||||
$sendNotifications = FALSE;
|
||||
$item = ( $this instanceof \IPS\Content\Item ) ? $this : $this->item();
|
||||
$this->shouldTriggerProfanityFilters( $first, $edit, $content, $title, $autoSaveLocation, $autoSaveKeys, $imageUploads, $hiddenByFilter, $itemHiddenByFilter );
|
||||
|
||||
/* If we need to hide the item, then do that */
|
||||
if ( $itemHiddenByFilter )
|
||||
{
|
||||
$recount = TRUE;
|
||||
$sendNotifications = $edit;
|
||||
|
||||
/* 'approved' is easy, clear and concise */
|
||||
if ( isset( $item::$databaseColumnMap['approved'] ) )
|
||||
{
|
||||
$column = $item::$databaseColumnMap['approved'];
|
||||
$item->$column = 0;
|
||||
$item->save();
|
||||
}
|
||||
/* 'hidden' is backwards */
|
||||
elseif ( isset( $item::$databaseColumnMap['hidden'] ) )
|
||||
{
|
||||
$column = $item::$databaseColumnMap['hidden'];
|
||||
$item->$column = 1;
|
||||
$item->save();
|
||||
}
|
||||
}
|
||||
|
||||
/* If we need to hide this, then do that */
|
||||
if ( $hiddenByFilter === TRUE or $itemHiddenByFilter === TRUE )
|
||||
{
|
||||
$sendNotifications = TRUE;
|
||||
|
||||
/* 'approved' is easy, clear and concise */
|
||||
if ( isset( static::$databaseColumnMap['approved'] ) )
|
||||
{
|
||||
/* 'approved' is easy, clear and concise */
|
||||
$column = static::$databaseColumnMap['approved'];
|
||||
$this->$column = 0;
|
||||
$this->save();
|
||||
}
|
||||
/* 'hidden' is backwards */
|
||||
else if ( isset( static::$databaseColumnMap['hidden'] ) )
|
||||
{
|
||||
/* 'hidden' is backwards */
|
||||
$column = static::$databaseColumnMap['hidden'];
|
||||
$this->$column = 1;
|
||||
$this->$column = ( $itemHiddenByFilter and $this instanceof \IPS\Content\Comment ) ? 2 : 1; # We use the special 2 flag to note it is only hidden because the parent is
|
||||
$this->save();
|
||||
}
|
||||
|
||||
/* We have to save here because the status change is relevant to resyncCommentCounts() and resyncLastComment(), and our comment object changes we just set can be overwritten when those methods are called. */
|
||||
$this->save();
|
||||
|
||||
$sendNotifications = TRUE;
|
||||
}
|
||||
|
||||
if ( $recount === TRUE )
|
||||
|
||||
/* If we did either, then recount number of comments */
|
||||
if ( $itemHiddenByFilter or $hiddenByFilter )
|
||||
{
|
||||
$item->resyncCommentCounts();
|
||||
$item->resyncLastComment();
|
||||
@@ -574,6 +714,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
}
|
||||
|
||||
/* Return */
|
||||
return $sendNotifications;
|
||||
}
|
||||
|
||||
@@ -646,7 +787,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
|
||||
/**
|
||||
* Return a list of groups that cannot see this item
|
||||
*
|
||||
* @return NULL|Array
|
||||
* @return NULL|array
|
||||
*/
|
||||
public function cannotViewGroups()
|
||||
{
|
||||
@@ -1235,7 +1376,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
$this->sendApprovedNotification();
|
||||
}
|
||||
|
||||
|
||||
/* Award points */
|
||||
$itemClass = \in_array( 'IPS\Content\Comment', class_parents( \get_called_class() ) ) ? static::$itemClass : \get_called_class();
|
||||
if ( $this instanceof \IPS\Content\Item )
|
||||
@@ -1246,7 +1387,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
$this->author()->achievementAction( 'core', 'Review', $this );
|
||||
}
|
||||
elseif ( $this instanceof \IPS\Content\Comment and ( !$itemClass::$firstCommentRequired or $this->isFirst() ) )
|
||||
elseif ( $this instanceof \IPS\Content\Comment and ( ( $itemClass::$firstCommentRequired AND ! $this->isFirst() ) OR ( ! $itemClass::$firstCommentRequired ) ) )
|
||||
{
|
||||
$this->author()->achievementAction( 'core', 'Comment', $this );
|
||||
}
|
||||
@@ -1455,19 +1596,23 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
|
||||
/**
|
||||
* Report
|
||||
*
|
||||
* @param string $reportContent Report content message from member
|
||||
* @param int $reportType Report type (see constants in \IPS\core\Reports\Report
|
||||
* @param string $reportContent Report content message from member
|
||||
* @param int $reportType Report type (see constants in \IPS\core\Reports\Report)
|
||||
* @param \IPS\Member|NULL $member Member making the report (or NULL for loggedIn())
|
||||
* @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, $reportType=1 )
|
||||
public function report( $reportContent, $reportType=1, $member=NULL )
|
||||
{
|
||||
$member = ( $member ) ? $member : \IPS\Member::loggedIn();
|
||||
|
||||
/* Permission check */
|
||||
if ( $this->canReport() !== TRUE )
|
||||
$permCheck = $this->canReport( $member );
|
||||
if ( $permCheck !== TRUE )
|
||||
{
|
||||
throw new \UnexpectedValueException;
|
||||
throw new \UnexpectedValueException( $permCheck );
|
||||
}
|
||||
|
||||
|
||||
/* Find or create an index */
|
||||
$idColumn = static::$databaseColumnId;
|
||||
try
|
||||
@@ -1481,7 +1626,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
|
||||
$index->class = \get_called_class();
|
||||
$index->content_id = $this->$idColumn;
|
||||
$index->perm_id = $this->permId();
|
||||
$index->first_report_by = (int) \IPS\Member::loggedIn()->member_id;
|
||||
$index->first_report_by = (int) $member->member_id;
|
||||
$index->first_report_date = time();
|
||||
$index->last_updated = time();
|
||||
$index->author = (int) $this->author()->member_id;
|
||||
@@ -1502,10 +1647,10 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
|
||||
$reportInsert = array(
|
||||
'rid' => $index->id,
|
||||
'report' => $reportContent,
|
||||
'report_by' => (int) \IPS\Member::loggedIn()->member_id,
|
||||
'report_by' => (int) $member->member_id,
|
||||
'date_reported' => time(),
|
||||
'ip_address' => \IPS\Request::i()->ipAddress(),
|
||||
'report_type' => \IPS\Member::loggedIn()->member_id ? $reportType : 0
|
||||
'report_type' => $member->member_id ? $reportType : 0
|
||||
);
|
||||
|
||||
$insertID = \IPS\Db::i()->insert( 'core_rc_reports', $reportInsert );
|
||||
@@ -1596,12 +1741,19 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
|
||||
case 'author':
|
||||
$this->$col = $newAuthor->member_id ? $newAuthor->member_id : 0;
|
||||
break;
|
||||
|
||||
case 'author_name':
|
||||
case 'edit_member_name':
|
||||
/* Real name will contain the custom guest name if available or '' if not */
|
||||
$this->$col = $newAuthor->member_id ? $newAuthor->name : $newAuthor->real_name;
|
||||
break;
|
||||
case 'edit_member_name':
|
||||
/* Real name will contain the custom guest name if available or '' if not.
|
||||
But we only want to update the user if oldName is the same as the current edit_member_name
|
||||
So if "Bob" edited his own post, you want that to change if Bob becomes Bob2.
|
||||
But if a moderator edited it, we don't want to change that name. */
|
||||
if ( $oldAuthor->name == $this->$col )
|
||||
{
|
||||
$this->$col = $newAuthor->member_id ? $newAuthor->name : $newAuthor->real_name;
|
||||
}
|
||||
break;
|
||||
case 'is_anon':
|
||||
/* We are specifying an author so turn off is_anon column */
|
||||
if ( $newAuthor->member_id )
|
||||
@@ -1621,6 +1773,29 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get language articles for the given container
|
||||
*
|
||||
* @param string $itemClass The classname of this content
|
||||
* @param array|NULL $containerData Basic data about the container. Only includes columns returned by container::basicDataColumns()
|
||||
*/
|
||||
public static function articlesFromIndexData( $itemClass, ?array $containerData ): array
|
||||
{
|
||||
/* 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() ) ) )
|
||||
{
|
||||
$indefiniteArticle = $itemClass::_indefiniteArticle( $containerData );
|
||||
$definiteArticle = $itemClass::_definiteArticle( $containerData );
|
||||
$definiteArticleUc = $itemClass::_definiteArticle( $containerData, NULL, array( 'ucfirst' => TRUE ) );
|
||||
}
|
||||
|
||||
return array( 'indefinite' => $indefiniteArticle, 'definite' => $definiteArticle, 'definite_uc' => $definiteArticleUc );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get HTML for search result display
|
||||
*
|
||||
@@ -1654,9 +1829,6 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
|
||||
$itemUrl = $itemClass::urlFromIndexData( $indexData, $itemData, 'getPrefComment' );
|
||||
|
||||
/* 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() ) ) )
|
||||
@@ -1669,17 +1841,15 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
|
||||
$objectUrl = $itemUrl->setQueryString( array( 'do' => 'findComment', 'comment' => $indexData['index_object_id'] ) );
|
||||
$showRepUrl = $itemUrl->setQueryString( array( 'do' => 'showReactionsComment', 'comment' => $indexData['index_object_id'] ) );
|
||||
}
|
||||
|
||||
$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, 'definite_uc' => $definiteArticleUc );
|
||||
|
||||
/* Articles language */
|
||||
$articles = static::articlesFromIndexData( $itemClass, $containerData );
|
||||
|
||||
/* Container details */
|
||||
$containerUrl = NULL;
|
||||
@@ -1740,7 +1910,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
|
||||
*/
|
||||
public static function searchResultSnippet( array $indexData, array $authorData, array $itemData, ?array $containerData, array $reputationData, $reviewRating, $view )
|
||||
{
|
||||
return $view == 'expanded' ? \IPS\Theme::i()->getTemplate( 'system', 'core', 'front' )->searchResultSnippet( $indexData ) : '';
|
||||
return $view == 'expanded' ? \IPS\Theme::i()->getTemplate( 'system', 'core', 'front' )->searchResultSnippet( $indexData, $itemData ) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1752,17 +1922,32 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
|
||||
* @param array $articles Articles language strings
|
||||
* @param array $indexData Search index data
|
||||
* @param array $itemData Data about the item
|
||||
* @param bool $includeLinks Include links to member profile
|
||||
* @return string
|
||||
*/
|
||||
public static function searchResultSummaryLanguage( $authorData, $articles, $indexData, $itemData )
|
||||
public static function searchResultSummaryLanguage( $authorData, $articles, $indexData, $itemData, $includeLinks = TRUE )
|
||||
{
|
||||
$authorTemplate = \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->userLinkFromData( $authorData['member_id'], $authorData['name'], $authorData['members_seo_name'], $authorData['member_group_id'] ?? \IPS\Settings::i()->guest_group, NULL, $indexData['index_is_anon'] );
|
||||
if( $includeLinks )
|
||||
{
|
||||
$authorTemplate = \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->userLinkFromData( $authorData['member_id'], $authorData['name'], $authorData['members_seo_name'], $authorData['member_group_id'] ?? \IPS\Settings::i()->guest_group, NULL, $indexData['index_is_anon'] );
|
||||
}
|
||||
else
|
||||
{
|
||||
$authorTemplate = \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->userNameFromData( $authorData['name'], $authorData['member_group_id'] ?? \IPS\Settings::i()->guest_group, NULL, $indexData['index_is_anon'] );
|
||||
}
|
||||
|
||||
if( \in_array( 'IPS\Content\Comment', class_parents( $indexData['index_class'] ) ) )
|
||||
{
|
||||
if( isset( $itemData['author'] ) )
|
||||
{
|
||||
$itemAuthorTemplate = \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->userLinkFromData( $itemData['author']['member_id'], $itemData['author']['name'], $itemData['author']['members_seo_name'], $itemData['author']['member_group_id'] ?? \IPS\Settings::i()->guest_group );
|
||||
if( $includeLinks )
|
||||
{
|
||||
$itemAuthorTemplate = \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->userLinkFromData( $itemData['author']['member_id'], $itemData['author']['name'], $itemData['author']['members_seo_name'], $itemData['author']['member_group_id'] ?? \IPS\Settings::i()->guest_group );
|
||||
}
|
||||
else
|
||||
{
|
||||
$itemAuthorTemplate = \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->userNameFromData( $itemData['author']['name'], $itemData['author']['member_group_id'] ?? \IPS\Settings::i()->guest_group );
|
||||
}
|
||||
}
|
||||
|
||||
if( \in_array( 'IPS\Content\Review', class_parents( $indexData['index_class'] ) ) )
|
||||
@@ -1930,6 +2115,75 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
return \IPS\Email::template( 'core', '_genericContent', $type, array( $this, $includeLinks, $includeAuthor, $email ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns "Foo posted {{indefart}} in {{container}}, {{date}}
|
||||
*
|
||||
* @param \IPS\Lang|NULL $plaintextLanguage If specified, will return plaintext (not linking the user or the container in the language specified). If NULL, returns with links based on logged in user's theme and language
|
||||
* @note This function was extracted from IPS\Promote
|
||||
* @return string
|
||||
*/
|
||||
public function objectMetaDescription( $plaintextLanguage=NULL )
|
||||
{
|
||||
$object = $this;
|
||||
$author = $this->author();
|
||||
|
||||
if ( $object instanceof \IPS\Content\Item )
|
||||
{
|
||||
$container = $object->containerWrapper();
|
||||
|
||||
if ( $container )
|
||||
{
|
||||
if ( !$plaintextLanguage )
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'promote_metadescription_container', FALSE, array(
|
||||
'htmlsprintf' => array( $author->link(), \IPS\DateTime::ts( $this->mapped('date') )->html( FALSE ) ),
|
||||
'sprintf' => array( $object->indefiniteArticle(), $container->url(), $container->_title ),
|
||||
) );
|
||||
}
|
||||
else
|
||||
{
|
||||
return $plaintextLanguage->addToStack( 'promote_metadescription_container_nolink', FALSE, array(
|
||||
'sprintf' => array( $object->indefiniteArticle( $plaintextLanguage ), $container->getTitleForLanguage( $plaintextLanguage ), $author->name, \IPS\DateTime::ts( $this->mapped('date') ) ),
|
||||
) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !$plaintextLanguage )
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'promote_metadescription_nocontainer', FALSE, array(
|
||||
'htmlsprintf' => array( $author->link(), \IPS\DateTime::ts( $this->mapped('date') )->html( FALSE ) ),
|
||||
'sprintf' => array( $object->indefiniteArticle() )
|
||||
) );
|
||||
}
|
||||
else
|
||||
{
|
||||
return $plaintextLanguage->addToStack( 'promote_metadescription_nocontainer', FALSE, array(
|
||||
'sprintf' => array( $object->indefiniteArticle( $plaintextLanguage ), $author->name, \IPS\DateTime::ts( $this->mapped('date') ) )
|
||||
) );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( $object instanceof \IPS\Content\Comment )
|
||||
{
|
||||
if ( !$plaintextLanguage )
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'promote_metadescription_nocontainer', FALSE, array(
|
||||
'htmlsprintf' => array( $author->link(), \IPS\DateTime::ts( $this->mapped('date') )->html( FALSE ) ),
|
||||
'sprintf' => array( $object->indefiniteArticle() )
|
||||
) );
|
||||
}
|
||||
else
|
||||
{
|
||||
return $plaintextLanguage->addToStack( 'promote_metadescription_nocontainer', FALSE, array(
|
||||
'sprintf' => array( $object->indefiniteArticle( $plaintextLanguage ), $author->name, \IPS\DateTime::ts( $this->mapped('date') ) )
|
||||
) );
|
||||
}
|
||||
}
|
||||
|
||||
throw new \OutofRangeException('object_not_valid');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a count of the database table
|
||||
@@ -2046,8 +2300,17 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
|
||||
$followIds = array();
|
||||
$followers = iterator_to_array( $this->notificationRecipients( array( $offset, static::NOTIFICATIONS_PER_BATCH ), $extra ) );
|
||||
|
||||
$followers = $this->notificationRecipients( array( $offset, static::NOTIFICATIONS_PER_BATCH ), $extra );
|
||||
|
||||
/* If $followers is NULL (which can be the case if the follows are just people following the author), just return as there is nothing to do */
|
||||
if ( $followers === NULL )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* If we're still here, our iterator may not necessarily be one that implements Countable so we need to convert it to an array. */
|
||||
$followers = iterator_to_array( $followers );
|
||||
|
||||
if( !\count( $followers ) )
|
||||
{
|
||||
return NULL;
|
||||
@@ -2351,21 +2614,28 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
|
||||
$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 ) as $session )
|
||||
/** Item::url() can throw a LogicException exception in specific cases like when a Pages Record has no valid page */
|
||||
try
|
||||
{
|
||||
if( mb_strpos( $session['location_url'], (string) $item->url() ) === 0 )
|
||||
/* 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 ) as $session )
|
||||
{
|
||||
$sessionData = $session;
|
||||
$sessionData['location_url'] = NULL;
|
||||
$sessionData['location_lang'] = NULL;
|
||||
$sessionData['location_data'] = json_encode( array() );
|
||||
$sessionData['current_id'] = 0;
|
||||
$sessionData['location_permissions'] = 0;
|
||||
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 );
|
||||
\IPS\Session\Store::i()->updateSession( $sessionData );
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( \LogicException $e ){}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2709,58 +2979,90 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
$filters = array();
|
||||
$filters['author'] = $this->author()->member_id;
|
||||
|
||||
/* All our Zapier Triggers for content have a hidden setting, so we're using this one global for items and comments */
|
||||
$filters['hidden'] = (bool) $this->hidden();
|
||||
|
||||
return $filters;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set anonymous state
|
||||
*
|
||||
* @param bool $state The state, TRUE for anonymous FALSE for not
|
||||
* @param \IPS\Member|NULL $member The member posting anonymously or NULL for logged in member
|
||||
* @param bool $state The state, TRUE for anonymous FALSE for not
|
||||
* @param \IPS\Member|NULL $member The member posting anonymously or NULL for logged in member
|
||||
* @return void
|
||||
*/
|
||||
public function setAnonymous( $state = TRUE, $member = NULL )
|
||||
public function setAnonymous( bool $state = TRUE, \IPS\Member $member = NULL)
|
||||
{
|
||||
if( !$this instanceof \IPS\Content\Anonymous or !$this->container()->canPostAnonymously() )
|
||||
if( !$this instanceof \IPS\Content\Anonymous )
|
||||
{
|
||||
throw new \BadMethodCallException;
|
||||
throw new \BadMethodCallException();
|
||||
}
|
||||
|
||||
$member = $member ?: \IPS\Member::loggedIn();
|
||||
|
||||
if( $state == $this->isAnonymous() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$class = \get_class( $this );
|
||||
$idColumn = static::$databaseColumnId;
|
||||
$anonColumn = static::$databaseColumnMap['is_anon'];
|
||||
|
||||
if( !$state )
|
||||
{
|
||||
try
|
||||
{
|
||||
$originalAuthor = \IPS\DB::i()->select( 'anonymous_member_id', 'core_anonymous_posts', array( 'anonymous_object_class=? and anonymous_object_id=?', $class, $this->$idColumn ) )->first();
|
||||
|
||||
$member = \IPS\Member::load( $originalAuthor );
|
||||
}
|
||||
catch (\UnderflowException $e )
|
||||
{
|
||||
$member = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
$member = $member ?: \IPS\Member::loggedIn();
|
||||
|
||||
if( $state and !$this->container()->canPostAnonymously( 0, $member ) )
|
||||
{
|
||||
throw new \BadMethodCallException();
|
||||
}
|
||||
|
||||
if( $state )
|
||||
{
|
||||
{
|
||||
/* Insert the anonymous map */
|
||||
$save = array(
|
||||
'anonymous_member_id' => $member->member_id,
|
||||
'anonymous_member_id' => ( $this->author()->member_id ) ? $this->author()->member_id : $member->member_id,
|
||||
'anonymous_object_class' => $class,
|
||||
'anonymous_object_id' => $this->$idColumn
|
||||
);
|
||||
|
||||
\IPS\Db::i()->replace( 'core_anonymous_posts', $save );
|
||||
|
||||
|
||||
$member = new \IPS\Member;
|
||||
$this->$anonColumn = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Db::i()->delete( 'core_anonymous_posts', array( 'anonymous_member_id=? and anonymous_object_class=? and anonymous_object_id=?', $member->member_id, $class, $this->$idColumn ) );
|
||||
\IPS\Db::i()->delete( 'core_anonymous_posts', array( 'anonymous_object_class=? and anonymous_object_id=?', $class, $this->$idColumn ) );
|
||||
|
||||
$this->$anonColumn = 0;
|
||||
}
|
||||
|
||||
|
||||
/* @todo only run the rest of the code here if the $anonColumn state is different from original. Waste of processing otherwise */
|
||||
$this->save();
|
||||
$this->changeAuthor( $member, FALSE );
|
||||
|
||||
|
||||
if ( \in_array( 'IPS\Content\Comment', class_parents( $this ) ) )
|
||||
{
|
||||
$this->item()->rebuildFirstAndLastCommentData();
|
||||
}
|
||||
|
||||
|
||||
$this->expireWidgetCaches();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is this an anonymous entry?
|
||||
*
|
||||
@@ -2770,4 +3072,15 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
return $this->mapped('is_anon');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content images
|
||||
*
|
||||
* @return array|NULL
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
public function imageLabelsForSearch()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user