Version 4.2.0

This commit is contained in:
Neo committed 2025-12-19 05:44:59 -08:00
1 parent 0dc2aee5ed
commit 96997ddd8e
2074 files changed
+125454 -55780

No files matched your search

+406 -432
View File
@@ -1,12 +1,11 @@
<?php
/**
* @brief Abstract Content Model
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Community Suite
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 3 Oct 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS;
@@ -48,6 +47,11 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
*/
public static $includeInSitemap = TRUE;
/**
* @brief Reputation Store
*/
protected $reputation;
/**
* Should posting this increment the poster's post count?
*
@@ -244,6 +248,35 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
return $this->mapped('content');
}
/**
* Returns the content images
*
* @param int|null $limit Number of attachments to fetch, or NULL for all
*
* @return array|NULL
* @throws \BadMethodCallException
*/
public function contentImages( $limit = NULL )
{
$idColumn = static::$databaseColumnId;
$internal = NULL;
$attachments = array();
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 ) );
}
if ( $internal )
{
foreach( \IPS\Db::i()->select( '*', 'core_attachments', array( array( 'attach_id IN(?)', $internal ), array( 'attach_is_image=1' ) ), 'attach_id ASC', $limit ) as $row )
{
$attachments[] = array( 'core_Attachment' => $row['attach_location'] );
}
}
return count( $attachments ) ? $attachments : NULL; }
/**
* Text for use with data-ipsTruncate
* Returns the post with paragraphs turned into line breaks
@@ -280,12 +313,17 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
*/
public function delete()
{
if ( $this instanceof \IPS\Content\Reputation )
$idColumn = static::$databaseColumnId;
if ( \IPS\IPS::classUsesTrait( $this, 'IPS\Content\Reactable' ) )
{
$idColumn = static::$databaseColumnId;
\IPS\Db::i()->delete( 'core_reputation_index', array( 'app=? AND type=? AND type_id=?', static::$application, static::$reputationType, $this->$idColumn ) );
\IPS\Db::i()->delete( 'core_reputation_index', array( 'app=? AND type=? AND type_id=?', static::$application, $this->reactionType(), $this->$idColumn ) );
}
/* 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 ) );
parent::delete();
$this->expireWidgetCaches();
@@ -329,14 +367,15 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
* Content is hidden?
*
* @return int
* @li -2 is pending deletion
* @li -1 is hidden having been hidden by a moderator
* @li 0 is unhidden
* @li 1 is hidden needing approval
* @note The actual column may also contain 2 which means the item is hidden because the parent is hidden, but it is not hidden in itself. This method will return -1 in that case.
*
* @note A piece of content (item and comment) can have an alias for hidden OR approved.
* With hidden: 0=not hidden, 1=hidden (needs moderator approval), -1=hidden by moderator, 2=parent item is hidden
* With approved: 1=not hidden, 0=hidden (needs moderator approval), -1=hidden by moderator
* With hidden: 0=not hidden, 1=hidden (needs moderator approval), -1=hidden by moderator, 2=parent item is hidden, -2=pending deletion
* With approved: 1=not hidden, 0=hidden (needs moderator approval), -1=hidden by moderator, -2=pending deletion
*
* User posting has moderator approval set: When adding an unapproved ITEM (approved=0, hidden=1) you should *not* increment container()->_comments but you should update container()->_unapprovedItems
* User posting has moderator approval set: When adding an unapproved COMMENT (approved=0, hidden=1) you should *not* increment item()->num_comments in item or container()->_comments but you should update item()->unapproved_comments and container()->_unapprovedComments
@@ -361,6 +400,10 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
elseif ( isset( static::$databaseColumnMap['approved'] ) )
{
$column = static::$databaseColumnMap['approved'];
if ( $this->$column == -2 )
{
return intval( $this->$column );
}
return $this->$column == -1 ? intval( $this->$column ) : intval( !$this->$column );
}
else
@@ -430,27 +473,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
/* Per-container permission */
elseif ( $container )
{
$containerClass = get_class( $container );
$title = static::$title;
if
(
isset( $containerClass::$modPerm )
and
(
$member->modPermission( $containerClass::$modPerm ) === -1
or
(
is_array( $member->modPermission( $containerClass::$modPerm ) )
and
in_array( $container->_id, $member->modPermission( $containerClass::$modPerm ) )
)
)
and
$member->modPermission( "can_{$type}_{$title}" )
)
{
return TRUE;
}
return $container->modPermission( $type, $member );
}
/* Still here? return false */
@@ -463,10 +486,11 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
* @param string $action The action
* @param \IPS\Member|NULL $member The member doing the action (NULL for currently logged in member)
* @param string|NULL $reason Reason (for hides)
* @param bool $immediately Delete Immediately
* @return void
* @throws \OutOfRangeException|\InvalidArgumentException|\RuntimeException
*/
public function modAction( $action, \IPS\Member $member = NULL, $reason = NULL )
public function modAction( $action, \IPS\Member $member = NULL, $reason = NULL, $immediately = FALSE )
{
if( $action === 'approve' )
{
@@ -474,13 +498,19 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
}
/* Check it's a valid action */
if ( !in_array( $action, array( 'pin', 'unpin', 'feature', 'unfeature', 'hide', 'unhide', 'move', 'lock', 'unlock', 'delete', 'publish' ) ) )
if ( !in_array( $action, array( 'pin', 'unpin', 'feature', 'unfeature', 'hide', 'unhide', 'move', 'lock', 'unlock', 'delete', 'publish', 'restore', 'restoreAsHidden' ) ) )
{
throw new \InvalidArgumentException;
}
/* And that we can do it */
if ( !call_user_func( array( $this, 'can' . ucfirst( $action ) ), $member ) )
$toCheck = $action;
if ( $action == 'restoreAsHidden' )
{
$toCheck = 'restore';
}
if ( !call_user_func( array( $this, 'can' . ucfirst( $toCheck ) ), $member ) )
{
throw new \OutOfRangeException;
}
@@ -541,9 +571,32 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
/* Delete is just a method */
elseif ( $action === 'delete' )
{
/* If we are retaining content for a period of time, we need to just hide it instead for deleting later - this only works, though, with items that implement \IPS\Content\Hideable */
if ( \IPS\Settings::i()->dellog_retention_period AND ( $this instanceof \IPS\Content\Hideable ) AND $immediately === FALSE )
{
$this->logDelete();
return;
}
$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;
}
/* Restore is just a method */
elseif ( $action === 'restore' )
{
$this->restore();
return;
}
/* Restore As Hidden is just a method */
elseif ( $action === 'restoreAsHidden' )
{
$this->restore( TRUE );
return;
}
/* Publish is just a method */
elseif ( $action === 'publish' )
@@ -561,6 +614,200 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
}
}
/**
* Log for deletion later
*
* \IPS\Member|NULL $member The member or NULL for currently logged in
* @return void
*/
public function logDelete( $member = NULL )
{
$member = $member ?: \IPS\Member::loggedIn();
/* Log it! */
$log = new \IPS\core\DeletionLog;
$log->setContentAndMember( $this, $member );
$log->save();
if ( isset( static::$databaseColumnMap['hidden'] ) )
{
$column = static::$databaseColumnMap['hidden'];
}
else if ( isset( static::$databaseColumnMap['approved'] ) )
{
$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();
if ( $this instanceof \IPS\Content\Comment )
{
$item = $this->item();
/* Update last comment stuff */
$item->resyncLastComment();
/* Update last review stuff */
$item->resyncLastReview();
/* Update number of comments */
$item->resyncCommentCounts();
/* Update number of reviews */
$item->resyncReviewCounts();
/* Save*/
$item->save();
}
try
{
if ( $this->container() )
{
$this->container()->setLastComment();
$this->container()->save();
}
}
catch( \BadMethodCallException $e ) {}
}
/**
* Restore Content
*
* @param bool Restore as hidden?
* @return void
* @throws \BadMethodCallException
*/
public function restore( $hidden = FALSE )
{
try
{
$idColumn = static::$databaseColumnId;
$log = \IPS\core\DeletionLog::constructFromData( \IPS\Db::i()->select( '*', 'core_deletion_log', array( "dellog_content_class=? AND dellog_content_id", get_class( $this ), $this->$idColumn ) )->first() );
}
catch( \UnderflowException $e )
{
throw new \BadMethodCallException;
}
/* Restoring as hidden? */
if ( $hidden )
{
if ( isset( static::$databaseColumnMap['hidden'] ) )
{
$column = static::$databaseColumnMap['hidden'];
}
else if ( isset( static::$databaseColumnMap['approved'] ) )
{
$column = static::$databaseColumnMap['approved'];
}
$this->$column = -1;
}
else
{
if ( isset( static::$databaseColumnMap['hidden'] ) )
{
$column = static::$databaseColumnMap['hidden'];
$this->$column = 0;
}
else if ( isset( static::$databaseColumnMap['approved'] ) )
{
$column = static::$databaseColumnMap['approved'];
$this->$column = 1;
}
}
/* Save the changes */
$this->save();
/* Reindex the now hidden content */
if ( $this instanceof \IPS\Content\Item and static::$firstCommentRequired )
{
\IPS\Content\Search\Index::i()->index( $this->firstComment() );
}
else
{
\IPS\Content\Search\Index::i()->index( $this );
}
/* Delete the log */
$log->delete();
/* Log to moderate logs */
if ( !$hidden )
{
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 ) {}
}
}
/**
* Can restore?*
*
* @param \IPS\Member|NULL The member, or currently logged in member
* @return bool
*/
public function canRestore( $member=NULL )
{
$member = $member ?: \IPS\Member::loggedIn();
return \IPS\Member::loggedIn()->modPermission('can_restore_deleted_content');
}
/**
* Hide
*
@@ -722,7 +969,43 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
return \IPS\Member::loggedIn()->language()->addToStack('hidden');
}
}
/**
* Blurb for when/why/by whom this content was deleted
*
* @return string
* @throws \BadMethodCallException
*/
public function deletedBlurb()
{
if ( !( $this instanceof \IPS\Content\Hideable ) )
{
throw new \BadMethodCallException;
}
try
{
$idColumn = static::$databaseColumnId;
$log = \IPS\core\DeletionLog::constructFromData( \IPS\Db::i()->select( '*', 'core_deletion_log', array( "dellog_content_class=? AND dellog_content_id=?", get_class( $this ), $this->$idColumn ) )->first() );
return \IPS\Member::loggedIn()->language()->addToStack( 'deletion_blurb', FALSE, array( 'sprintf' => array( $log->_deleted_by->name, $log->deleted_date->fullYearLocaleDate(), $log->deletion_date->fullYearLocaleDate() ) ) );
}
catch( \UnderflowException $e )
{
return \IPS\Member::loggedIn()->language()->addToStack('deleted');
}
}
/**
* Can promote this comment/item?
*
* @param \IPS\Member|NULL $member The member to check for (NULL for currently logged in member)
* @return boolean
*/
public function canPromoteToSocialMedia( $member=NULL )
{
return \IPS\core\Promote::canPromote( $member );
}
/**
* Can report?
*
@@ -741,7 +1024,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
}
/* Can the member report content? */
if ( $member->group['gbw_no_report'] )
if ( !$member->group['g_can_report'] )
{
return 'no_module_permission';
}
@@ -866,7 +1149,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
{
$oldAuthor = $this->author();
foreach ( array( 'author', 'author_name' ) as $k )
foreach ( array( 'author', 'author_name', 'edit_member_name' ) as $k )
{
if ( isset( static::$databaseColumnMap[ $k ] ) )
{
@@ -878,6 +1161,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
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;
@@ -906,9 +1190,10 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
* @param bool $asItem Displaying results as items?
* @param bool $canIgnoreComments Can ignore comments in the result stream? Activity stream can, but search results cannot.
* @param array $template Optional custom template
* @param array $reactions Reaction Data
* @return string
*/
public static function searchResult( array $indexData, array $authorData, array $itemData, array $containerData = NULL, array $reputationData, $reviewRating, $iPostedIn, $view, $asItem, $canIgnoreComments=FALSE, $template=NULL )
public static function searchResult( array $indexData, array $authorData, array $itemData, array $containerData = NULL, array $reputationData, $reviewRating, $iPostedIn, $view, $asItem, $canIgnoreComments=FALSE, $template=NULL, $reactions=array() )
{
/* Item details */
$itemClass = $indexData['index_class'];
@@ -931,12 +1216,12 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
if ( in_array( 'IPS\Content\Review', class_parents( get_called_class() ) ) )
{
$objectUrl = $itemUrl->setQueryString( array( 'do' => 'findReview', 'review' => $indexData['index_object_id'] ) );
$showRepUrl = $itemUrl->setQueryString( array( 'do' => 'showRepReview', 'review' => $indexData['index_object_id'] ) );
$showRepUrl = $itemUrl->setQueryString( array( 'do' => 'showReactionsReview', 'review' => $indexData['index_object_id'] ) );
}
else
{
$objectUrl = $itemUrl->setQueryString( array( 'do' => 'findComment', 'comment' => $indexData['index_object_id'] ) );
$showRepUrl = $itemUrl->setQueryString( array( 'do' => 'showRepComment', 'comment' => $indexData['index_object_id'] ) );
$showRepUrl = $itemUrl->setQueryString( array( 'do' => 'showReactionsComment', 'comment' => $indexData['index_object_id'] ) );
}
$indefiniteArticle = $itemClass::_indefiniteArticle( $containerData );
@@ -945,7 +1230,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
else
{
$objectUrl = $itemUrl;
$showRepUrl = $itemUrl->setQueryString( 'do', 'showRep' );
$showRepUrl = $itemUrl->setQueryString( 'do', 'showReactions' );
}
$articles = array( 'indefinite' => $indefiniteArticle, 'definite' => $definiteArticle );
@@ -971,7 +1256,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
}
/* Return */
return call_user_func_array( $template, array( $indexData, $articles, $authorData, $itemData, $unread, $asItem ? $itemUrl : $objectUrl, $itemUrl, $containerUrl, $containerTitle, $repCount, $showRepUrl, $snippet, $iPostedIn, $view, $canIgnoreComments ) );
return call_user_func_array( $template, array( $indexData, $articles, $authorData, $itemData, $unread, $asItem ? $itemUrl : $objectUrl, $itemUrl, $containerUrl, $containerTitle, $repCount, $showRepUrl, $snippet, $iPostedIn, $view, $canIgnoreComments, $reactions ) );
}
/**
@@ -991,6 +1276,11 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
return $view == 'expanded' ? \IPS\Theme::i()->getTemplate( 'system', 'core', 'front' )->searchResultSnippet( $indexData ) : '';
}
/**
* @brief Return a classname applied to the search result block
*/
public static $searchResultClassName = '';
/**
* Return the filters that are available for selecting table rows
*
@@ -1039,397 +1329,6 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
}
/* !Reputation */
/**
* Can give reputation?
*
* @note This method is also ran to check if a member can "unrep"
* @param int $type 1 for positive, -1 for negative
* @param \IPS\Member|NULL $member The member to check for (NULL for currently logged in member)
* @return bool
*/
public function canGiveReputation( $type, \IPS\Member $member = NULL )
{
return static::_canGiveReputation( $type, $this->author()->member_id, $this->author()->member_group_id, $this->repGiven( $member ), $member );
}
/**
* Can give reputation?
*
* @note This method is also ran to check if a member can "unrep"
* @param int $type 1 for positive, -1 for negative
* @param int $authorId The author ID
* @param int $authorGroup The author group
* @param int $repGiven The reputation we have already given (1 = positive, -1 = negative, 0 = no rep given)
* @param \IPS\Member|NULL $member The member to check for (NULL for currently logged in member)
* @return bool
*/
public static function _canGiveReputation( $type, $authorId, $authorGroup, $repGiven, \IPS\Member $member = NULL )
{
/* Reputation needs to be enabled */
if ( !\IPS\Settings::i()->reputation_enabled or !in_array( 'IPS\Content\Reputation', class_implements( get_called_class() ) ) )
{
return FALSE;
}
/* Guests cannot rep */
$member = $member ?: \IPS\Member::loggedIn();
if ( !$member->member_id )
{
return FALSE;
}
/* Author needs to not be protected */
if ( in_array( $authorGroup, explode( ',', \IPS\Settings::i()->reputation_protected_groups ) ) )
{
return FALSE;
}
/* Users may not be able to rep themselves */
if ( !\IPS\Settings::i()->reputation_can_self_vote and $authorId == $member->member_id )
{
return FALSE;
}
/* Rep per day */
$limit = ( $type === 1 ) ? $member->group['g_rep_max_positive'] : $member->group['g_rep_max_negative'];
if ( $limit == 0 and !$repGiven )
{
return FALSE;
}
/* Have we already given rep? */
switch ( \IPS\Settings::i()->reputation_point_types )
{
/* If we're using a positive system, we can rep positive only if we haven't repped yet, or negative only if we have */
case 'like':
case 'positive':
return ( $repGiven === 1 ) ? ( $type === -1 ) : ( $type === 1 );
/* If we're using a negative system, we can rep negative only if we haven't repped yet, or positive only if we have */
case 'negative':
return $repGiven ? ( $type === 1 ) : ( $type === -1 );
/* If we're using a positive and negative system - it's more complicated... */
case 'both':
switch ( $repGiven )
{
/* If we haven't repped yet, we can do either */
case 0:
return TRUE;
/* If we've given positive rep already, we can only do negative now */
case 1:
return ( $type === -1 );
/* If we've given negative rep already, we can only do positive now */
case -1:
return ( $type === 1 );
}
}
/* Catch all */
return FALSE;
}
/**
* Give reputation
*
* @param int $type 1 for positive, -1 for negative
* @param \IPS\Member|NULL $member The member to check for (NULL for currently logged in member)
* @return void
* @throws \DomainException|\BadMethodCallException
*/
public function giveReputation( $type, \IPS\Member $member=NULL )
{
if ( !( $this instanceof \IPS\Content\Reputation ) )
{
throw new \BadMethodCallException;
}
$member = $member ?: \IPS\Member::loggedIn();
if ( !$this->canGiveReputation( $type, $member ) )
{
throw new \DomainException( 'cannot_rep_user' );
}
$idColumn = static::$databaseColumnId;
\IPS\Db::i()->delete( 'core_reputation_index', array( 'app=? AND type=? AND member_id=? AND type_id=?', static::$application, static::$reputationType, $member->member_id, $this->$idColumn ) );
if ( $this->repGiven( $member ) )
{
if ( $this->reputation !== NULL )
{
unset( $this->reputation[ $member->member_id ] );
}
}
else
{
$limit = ( $type === 1 ) ? $member->group['g_rep_max_positive'] : $member->group['g_rep_max_negative'];
if ( $limit !== 999 )
{
$count = \IPS\Db::i()->select( 'COUNT(*)', 'core_reputation_index', array( 'member_id=? AND rep_date>? AND rep_rating=?', $member->member_id, \IPS\DateTime::create()->sub( new \DateInterval( 'P1D' ) )->getTimestamp(), $type ) )->first();
if ( $count >= $limit )
{
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack( \IPS\Settings::i()->reputation_point_types == 'like' ? 'rep_daily_exceeded_likes' :'rep_daily_exceeded', FALSE, array( 'sprintf' => array( $limit ) ) ) );
}
}
\IPS\Db::i()->insert( 'core_reputation_index', array(
'member_id' => $member->member_id,
'app' => static::$application,
'type' => static::$reputationType,
'type_id' => $this->$idColumn,
'rep_date' => time(),
'rep_rating' => $type,
'member_received' => (int) $this->author()->member_id,
'class_type_id_hash' => md5( get_class( $this ) . ':' . $this->$idColumn ),
'rep_class' => get_class( $this ),
'item_id' => (int) $this->mapped('item')
) );
/* Send Notification */
if ( $type === 1 AND $this->author()->member_id AND $this->author() != \IPS\Member::loggedIn() AND $this->canView( $this->author() ) )
{
$notification = new \IPS\Notification( \IPS\Application::load('core'), 'new_likes', $this, array( $this, \IPS\Member::loggedIn() ) );
$notification->recipients->attach( $this->author() );
$notification->send();
}
if ( $this->reputation !== NULL )
{
$this->reputation[ $member->member_id ] = $type;
}
}
if( $this->author()->member_id )
{
$this->author()->pp_reputation_points += $type;
$this->author()->save();
}
}
/**
* @brief Reputation Cache
*/
public $reputation = NULL;
/**
* Get reputation count
*
* @return int
* @throws \BadMethodCallException
*/
public function reputation()
{
if ( !( $this instanceof \IPS\Content\Reputation ) )
{
throw new \BadMethodCallException;
}
if ( $this->reputation === NULL )
{
$this->reputation = iterator_to_array( \IPS\Db::i()->select( 'member_id, rep_rating', 'core_reputation_index', $this->getReputationWhereClause() )->setKeyField( 'member_id' )->setValueField( 'rep_rating' ) );
return array_sum( $this->reputation );
}
else
{
$repCount = 0;
foreach( $this->reputation as $member => $value )
{
if( is_int( $value ) )
{
$repCount += $value;
}
else
{
$repCount += $value['rep_rating'];
}
}
return $repCount;
}
}
/**
* Get reputation where clause
*
* @return array
* @throws \BadMethodCallException
*/
public function getReputationWhereClause()
{
if ( !( $this instanceof \IPS\Content\Reputation ) )
{
throw new \BadMethodCallException;
}
$idColumn = static::$databaseColumnId;
$where = array( array( 'app=? AND type=? AND type_id=?', static::$application, static::$reputationType, $this->$idColumn ) );
switch( \IPS\Settings::i()->reputation_point_types )
{
case 'positive':
case 'like':
$where[] = array( 'rep_rating=?', "1" );
break;
case 'negative':
$where[] = array( 'rep_rating=?', "-1" );
break;
}
return $where;
}
/**
* @brief Cached like blurb
*/
public $likeBlurb = NULL;
/**
* Get "like" blurb (You and X others like this)
*
* @return string
*/
public function likeBlurb()
{
if( $this->likeBlurb === NULL )
{
/* Did anyone like it? */
$numberOfLikes = $this->reputation(); # int
if ( $numberOfLikes )
{
/* Is it just us? */
$userLiked = ( $this->repGiven() === 1 );
if ( $userLiked and $numberOfLikes < 2 )
{
$this->likeBlurb = \IPS\Member::loggedIn()->language()->addToStack('like_blurb_just_you');
}
/* Nope, we need to display a number... */
else
{
$peopleToDisplayInMainView = array();
$andXOthers = $numberOfLikes;
/* If the user liked, we always show "You" first */
if ( $userLiked )
{
$peopleToDisplayInMainView[] = \IPS\Member::loggedIn()->language()->addToStack('like_blurb_you_and_others');
$andXOthers--;
}
/* If we have permission to see who the others are, we need to do that... */
if ( \IPS\Member::loggedIn()->group['gbw_view_reps'] )
{
$peopleToDisplayInSecondaryView = array();
/* Some random names */
$idColumn = static::$databaseColumnId;
$i = 0;
$peopleToDisplayInSecondaryView = array();
foreach ( \IPS\Db::i()->select( '*', 'core_reputation_index', array( 'app=? AND type=? AND type_id=? AND member_id!=? AND rep_rating=?', static::$application, static::$reputationType, $this->$idColumn, ( \IPS\Member::loggedIn()->member_id ) ?: 0, 1 ), 'RAND()', $userLiked ? 17 : 18 ) as $rep )
{
if ( $i < ( $userLiked ? 2 : 3 ) )
{
$peopleToDisplayInMainView[] = \IPS\Theme::i()->getTemplate( 'global', 'core' )->userLink( \IPS\Member::load( $rep['member_id'] ) );
$andXOthers--;
}
else
{
$peopleToDisplayInSecondaryView[] = htmlspecialchars( \IPS\Member::load( $rep['member_id'] )->name, ENT_QUOTES | \IPS\HTMLENTITIES, 'UTF-8', FALSE );
}
$i++;
}
/* If there's people to display in the secondary view, add that */
if ( $peopleToDisplayInSecondaryView )
{
if ( count( $peopleToDisplayInSecondaryView ) < $andXOthers )
{
$peopleToDisplayInSecondaryView[] = \IPS\Member::loggedIn()->language()->addToStack( 'like_blurb_others_secondary', FALSE, array( 'pluralize' => array( $andXOthers - count( $peopleToDisplayInSecondaryView ) ) ) );
}
$peopleToDisplayInMainView[] = \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->reputationOthers( $this->url( 'showRep' ), \IPS\Member::loggedIn()->language()->addToStack( 'like_blurb_others', FALSE, array( 'pluralize' => array( $andXOthers ) ) ), json_encode( $peopleToDisplayInSecondaryView ) );
}
}
/* Otherwise the "and X others" will just be static */
else
{
$peopleToDisplayInMainView[] = \IPS\Member::loggedIn()->language()->addToStack( empty( $peopleToDisplayInMainView ) ? 'like_blurb_generic' : 'like_blurb_others', FALSE, array( 'pluralize' => array( $andXOthers ) ) );
}
/* Put it all together */
$this->likeBlurb = \IPS\Member::loggedIn()->language()->addToStack( 'like_blurb', FALSE, array( 'pluralize' => array( $numberOfLikes ), 'htmlsprintf' => array( \IPS\Member::loggedIn()->language()->formatList( $peopleToDisplayInMainView ) ) ) );
}
}
/* Nobody liked it - show nothing */
else
{
$this->likeBlurb = '';
}
}
return $this->likeBlurb;
}
/**
* Has reputation been given by a particular member?
*
* @param \IPS\Member|NULL $member The member to check for (NULL for currently logged in member)
* @return int 1 = Positive rep given. -1 = Negative rep given. 0 = No rep given
* @throws \BadMethodCallException
*/
public function repGiven( \IPS\Member $member = NULL )
{
if ( !( $this instanceof \IPS\Content\Reputation ) )
{
throw new \BadMethodCallException;
}
if ( $this->reputation === NULL )
{
$this->reputation();
}
$member = $member ?: \IPS\Member::loggedIn();
return array_key_exists( $member->member_id, $this->reputation ) ? intval( $this->reputation[ $member->member_id ] ) : 0;
}
/**
* Get table showing who has given reputation
*
* @return \IPS\Helpers\Table\Db
* @throws \DomainException
* @throws \BadMethodCallException
*/
public function reputationTable()
{
if ( !( $this instanceof \IPS\Content\Reputation ) )
{
throw new \BadMethodCallException;
}
if ( !\IPS\Member::loggedIn()->group['gbw_view_reps'] )
{
throw new \DomainException;
}
$idColumn = static::$databaseColumnId;
$table = new \IPS\Helpers\Table\Db( 'core_reputation_index', $this->url('showRep'), $this->getReputationWhereClause() );
$table->sortBy = 'rep_date';
$table->sortDirection = 'desc';
$table->tableTemplate = array( \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' ), 'reputationLogTable' );
$table->rowsTemplate = array( \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' ), 'reputationLog' );
return $table;
}
/* !Follow */
const FOLLOW_PUBLIC = 1;
@@ -1874,4 +1773,79 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
{
return (string) \IPS\Theme::i()->logo_sharer;
}
/**
* Log keyword usage, if any
*
* @param string $content Content/text of submission
* @param string|NULL $title Title of submission
* @return void
*/
public function checkKeywords( $content, $title=NULL )
{
/* Do we have any keywords to track? */
if( !\IPS\Settings::i()->stats_keywords )
{
return;
}
/* We need to know the ID */
$idColumn = static::$databaseColumnId;
/* If this is a content item and first comment is required, skip checking the comment */
if ( $this instanceof \IPS\Content\Comment )
{
$itemClass = static::$itemClass;
if( $itemClass::$firstCommentRequired === TRUE )
{
/* During initial post, at this point the firstCommentIdColumn value won't be set, so we check for that or explicitly if this is the first post */
if( !$this->item()->mapped('first_comment_id') OR $this->$idColumn == $this->item()->mapped('first_comment_id') )
{
return;
}
}
}
$words = preg_split("/[\s]+/", trim( strip_tags( preg_replace( "/<br( \/)?>/", "\n", $content ) ) ), NULL, PREG_SPLIT_NO_EMPTY );
if( $title !== NULL )
{
$titleWords = explode( ' ', $title );
$words = array_merge( $words, $titleWords );
}
$words = array_unique( $words );
$keywords = json_decode( \IPS\Settings::i()->stats_keywords, true );
$extraData = json_encode( array( 'class' => get_class( $this ), 'id' => $this->$idColumn ) );
foreach( $keywords as $keyword )
{
if( in_array( $keyword, $words ) )
{
\IPS\Db::i()->insert( 'core_statistics', array( 'time' => time(), 'type' => 'keyword', 'value_4' => $keyword, 'extra_data' => $extraData ) );
}
}
}
/* !Deprecated */
/* The methods below are deprecated and will be removed in Invision Community 4.3 */
/**
* Get reputation count
*
* @return int
* @throws \BadMethodCallException
*/
public function reputation()
{
if ( !\IPS\IPS::classUsesTrait( $this, 'IPS\Content\Reactable' ) )
{
throw new \BadMethodCallException;
}
return $this->reactionCount();
}
}