Version 4.1.2

This commit is contained in:
Neo committed 2025-12-19 05:32:19 -08:00
1 parent 3ba79295ee
commit 28bd025b35
1131 files changed
+63658 -20579

No files matched your search

+436 -48
View File
@@ -33,11 +33,41 @@ class _Results extends \ArrayIterator
*/
protected $initated = FALSE;
/**
* @brief Author data
*/
protected $authorData = array();
/**
* @brief Item titles
*/
protected $itemData = array();
/**
* @brief Container data
*/
protected $containerData = array();
/**
* @brief Reputation data
*/
protected $reputationData = array();
/**
* @brief Review Ratings
*/
protected $reviewRatings = array();
/**
* @brief Index IDs of items I have posted in
*/
protected $iPostedIn = array();
/**
* Constructor
*
* @param array $results The results
* @param int $countAllRows Count for all rows
* @param array $results The results
* @param int $countAllRows Count for all rows
* @return void
*/
public function __construct( $results, $countAllRows )
@@ -51,34 +81,107 @@ class _Results extends \ArrayIterator
*
* @return void
*/
public function init( $loadReputation=FALSE )
public function init()
{
$classesToLoad = array( 'IPS\Member' => array() );
/* Init */
$membersToLoad = array();
$itemsToLoad = array();
$containersToLoad = array();
$reputationIds = array();
$reviewRatings = array();
$itemIndexIds = array();
foreach ( $this->getArrayCopy() as $row )
/* Loop */
foreach ( $this->getArrayCopy() as $result )
{
$classesToLoad[ 'IPS\Member' ][ $row['index_author'] ] = $row['index_author'];
$classesToLoad[ $row['index_class'] ][ $row['index_object_id'] ] = $row['index_object_id'];
$itemClass = NULL;
if ( $row['index_item_id'] and in_array( 'IPS\Content\Comment', class_parents( $row['index_class'] ) ) )
if ( $result['index_author'] )
{
$itemClass = $row['index_class']::$itemClass;
$classesToLoad[ $itemClass ][ $row['index_item_id'] ] = $row['index_item_id'];
}
if ( $row['index_container_id'] )
{
$containerClass = $itemClass ? $itemClass::$containerNodeClass : $row['index_class']::$containerNodeClass;
$classesToLoad[ $containerClass ][ $row['index_container_id'] ] = $row['index_container_id'];
$membersToLoad[ $result['index_author'] ] = $result['index_author'];
}
if ( $loadReputation and in_array( 'IPS\Content\Reputation', class_implements( $row['index_class'] ) ) )
if ( in_array( 'IPS\Content\Comment', class_parents( $result['index_class'] ) ) )
{
$reputationIds[ $row['index_class'] ][ $row['index_object_id'] ] = $row['index_object_id'];
$commentClass = $result['index_class'];
$itemsToLoad[ $commentClass::$itemClass ][ $result['index_item_id'] ] = $result['index_item_id'];
}
else
{
$itemsToLoad[ $result['index_class'] ][ $result['index_item_id'] ] = $result['index_item_id'];
}
if ( $result['index_container_id'] )
{
$class = $result['index_class'];
if ( $class == 'IPS\core\Statuses\Status' )
{
if ( $result['index_container_id'] != $result['index_author'] )
{
$membersToLoad[ $result['index_container_id'] ] = $result['index_container_id'];
}
}
else
{
$itemClass = in_array( 'IPS\Content\Comment', class_parents( $class ) ) ? $class::$itemClass : $class;
if ( isset( $itemClass::$containerNodeClass ) )
{
$containerClass = $itemClass::$containerNodeClass;
if ( isset( $containerClass::$seoTitleColumn ) )
{
$containersToLoad[ $itemClass::$containerNodeClass ][ $result['index_container_id'] ] = $result['index_container_id'];
}
}
}
}
if ( in_array( 'IPS\Content\Reputation', class_implements( $result['index_class'] ) ) )
{
$reputationIds[ $result['index_class'] ][ $result['index_object_id'] ] = $result['index_object_id'];
}
if ( in_array( 'IPS\Content\Review', class_parents( $result['index_class'] ) ) )
{
$reviewRatings[ $result['index_class'] ][ $result['index_object_id'] ] = $result['index_object_id'];
}
if ( $result['index_item_index_id'] )
{
$itemIndexIds[] = $result['index_item_index_id'];
}
}
/* Load item data */
foreach ( $itemsToLoad as $itemClass => $itemIds )
{
foreach ( \IPS\Db::i()->select( $itemClass::basicDataColumns(), $itemClass::$databaseTable, \IPS\Db::i()->in( $itemClass::$databasePrefix . $itemClass::$databaseColumnId, $itemIds ) )->setKeyField( $itemClass::$databasePrefix . $itemClass::$databaseColumnId ) as $itemId => $itemData )
{
$this->itemData[ $itemClass ][ $itemId ] = $itemData;
if ( isset( $itemData[ $itemClass::$databasePrefix . $itemClass::$databaseColumnMap['author'] ] ) )
{
$memberId = $itemData[ $itemClass::$databasePrefix . $itemClass::$databaseColumnMap['author'] ];
$membersToLoad[ $memberId ] = $memberId;
}
}
if ( method_exists( $itemClass, 'searchResultExtraData' ) )
{
foreach ( $itemClass::searchResultExtraData( $this->itemData[ $itemClass ] ) as $id => $extraData )
{
$this->itemData[ $itemClass ][ $id ]['extra'] = $extraData;
}
}
}
/* Load author data */
$this->authorData = iterator_to_array( \IPS\Db::i()->select( \IPS\Member::columnsForPhoto(), 'core_members', \IPS\Db::i()->in( 'member_id', $membersToLoad ) )->setKeyField( 'member_id' ) );
/* Load container data */
foreach ( $containersToLoad as $containerClass => $containerIds )
{
$this->containerData[ $containerClass ] = iterator_to_array( \IPS\Db::i()->select( $containerClass::basicDataColumns(), $containerClass::$databaseTable, \IPS\Db::i()->in( $containerClass::$databasePrefix . $containerClass::$databaseColumnId, $containerIds ) )->setKeyField( $containerClass::$databasePrefix . $containerClass::$databaseColumnId ) );
}
/* Load reputation data */
$reputation = array();
if ( count( $reputationIds ) )
{
@@ -103,37 +206,25 @@ class _Results extends \ArrayIterator
break;
}
foreach ( \IPS\Db::i()->select( '*', 'core_reputation_index', $where ) as $rep )
foreach ( \IPS\Db::i()->select( array( 'app', 'type', 'type_id', 'member_id', 'rep_rating' ), 'core_reputation_index', $where ) as $rep )
{
$reputation[ $rep['app'] ][ $rep['type'] ][ $rep['type_id'] ][ $rep['member_id'] ] = $rep;
}
}
foreach ( $classesToLoad as $class => $ids )
{
$ids = array_diff( $ids, $class::multitonIds() );
$idColumn = $class::$databaseColumnId;
if ( !empty( $ids ) )
{
foreach ( \IPS\Db::i()->select( '*', $class::$databaseTable, \IPS\Db::i()->in( $class::$databasePrefix . $class::$databaseColumnId, $ids ) ) as $row )
{
$obj = $class::constructFromData( $row );
if ( $loadReputation and $obj instanceof \IPS\Content\Reputation )
{
if ( isset( $reputation[ $class::$application ][ $class::$reputationType ][ $obj->$idColumn ] ) )
{
$obj->reputation = $reputation[ $class::$application ][ $class::$reputationType ][ $obj->$idColumn ];
}
else
{
$obj->reputation = array();
}
}
}
$this->reputationData[ $rep['app'] ][ $rep['type'] ][ $rep['type_id'] ][ $rep['member_id'] ] = $rep['rep_rating'];
}
}
/* Load rating reviews */
foreach ( $reviewRatings as $reviewClass => $reviewIds )
{
$this->reviewRatings[ $reviewClass ] = iterator_to_array( \IPS\Db::i()->select( array( $reviewClass::$databasePrefix . $reviewClass::$databaseColumnId, $reviewClass::$databasePrefix . $reviewClass::$databaseColumnMap['rating'] ), $reviewClass::$databaseTable, \IPS\Db::i()->in( $reviewClass::$databasePrefix . $reviewClass::$databaseColumnId, $reviewIds ) )->setKeyField( $reviewClass::$databasePrefix . $reviewClass::$databaseColumnId )->setValueField( $reviewClass::$databasePrefix . $reviewClass::$databaseColumnMap['rating'] ) );
}
/* Load data for the "I posted in this" stars */
if ( count( $itemIndexIds ) and \IPS\Member::loggedIn()->member_id )
{
$this->iPostedIn = iterator_to_array( \IPS\Db::i()->select( 'index_item_index_id', 'core_search_index', array( array( \IPS\Db::i()->in( 'index_item_index_id', $itemIndexIds ) ), array( 'index_author=?', \IPS\Member::loggedIn()->member_id ) ), NULL, NULL, NULL, NULL, \IPS\Db::SELECT_DISTINCT ) );
}
/* Set that we're initiated */
$this->initated = TRUE;
}
@@ -144,8 +235,41 @@ class _Results extends \ArrayIterator
*/
public function current()
{
$row = parent::current();
return call_user_func( array( $row['index_class'], 'load' ), $row['index_object_id'] );
$data = parent::current();
$class = $data['index_class'];
$itemClass = in_array( 'IPS\Content\Comment', class_parents( $class ) ) ? $class::$itemClass : $class;
$containerClass = isset( $itemClass::$containerNodeClass ) ? $itemClass::$containerNodeClass : NULL;
$reputationData = array();
if ( in_array( 'IPS\Content\Reputation', class_implements( $class ) ) and isset( $this->reputationData[ $class::$application ][ $class::$reputationType ][ $data['index_object_id'] ] ) )
{
$reputationData = $this->reputationData[ $class::$application ][ $class::$reputationType ][ $data['index_object_id'] ];
}
$itemData = $this->itemData[ $itemClass ][ $data['index_item_id'] ];
if ( $class == 'IPS\core\Statuses\Status' and $data['index_container_id'] != $data['index_author'] )
{
$itemData['profile'] = $this->authorData[ $data['index_container_id'] ];
}
if ( isset( $itemData[ $itemClass::$databasePrefix . $itemClass::$databaseColumnMap['author'] ] ) )
{
$memberId = $itemData[ $itemClass::$databasePrefix . $itemClass::$databaseColumnMap['author'] ];
if ( isset( $this->authorData[ $memberId ] ) )
{
$itemData['author'] = $this->authorData[ $memberId ];
}
}
return new Result\Content(
$data,
isset( $this->authorData[ $data['index_author'] ] ) ? $this->authorData[ $data['index_author'] ] : array( 'member_id' => 0, 'name' => \IPS\Member::loggedIn()->language()->addToStack('guest'), 'members_seo_name' => '', 'pp_main_photo' => NULL, 'pp_photo_type' => 'none' ),
$itemData,
( $containerClass and isset( $this->containerData[ $containerClass ] ) and isset( $this->containerData[ $containerClass ][ $data['index_container_id'] ] ) ) ? $this->containerData[ $containerClass ][ $data['index_container_id'] ] : NULL,
$reputationData,
isset( $this->reviewRatings[ $data['index_class'] ][ $data['index_object_id'] ] ) ? $this->reviewRatings[ $data['index_class'] ][ $data['index_object_id'] ] : NULL,
in_array( $data['index_item_index_id'], $this->iPostedIn )
);
}
/**
@@ -179,4 +303,268 @@ class _Results extends \ArrayIterator
return parent::count();
}
}
/**
* Add in "extra" items
*
* @param array $extra Types ('register', 'follow_member', 'follow_content', 'photo', 'like', 'rep_neg')
* @param \IPS\Member|NULL $author The author to limit extra items to
* @param \IPS\DateTime|NULL $lastTime If provided, only items since this date is included. If NULL, it works out which to include based on what results are being shown
* @param \IPS\DateTime|NULL $firstTime If provided, only items before this date is included. If NULL, it works out which to include based on what results are being shown
* @return array
* @note Each thing is limited to 10 items. Even though this may result in accuracies, the limit is necessary so that if there's more extra items that content it doesn't create lots of queries and slow loading
*/
public function addExtraItems( $extra, \IPS\Member $author = NULL, $lastTime=NULL, $firstTime = NULL )
{
/* Work out the timestamps */
$results = iterator_to_array( $this );
if ( $firstTime )
{
$firstTime = $firstTime->getTimestamp();
}
elseif ( isset( \IPS\Request::i()->page ) and \IPS\Request::i()->page != 1 )
{
foreach ( $results as $result )
{
$firstTime = $result->createdDate->getTimestamp();
break;
}
}
if ( $lastTime )
{
$lastTime = $lastTime->getTimestamp();
}
else
{
foreach ( $results as $result )
{
$lastTime = $result->createdDate->getTimestamp();
}
}
/* We need at least a last time */
if ( !$lastTime )
{
return $results;
}
/* Get the extra items... */
$extraItems = array();
if ( $lastTime )
{
/* Users registered */
if ( in_array( 'register', $extra ) )
{
$where = array( array( 'joined>?', $lastTime ), array( 'name<>?', '' ) );
if ( $firstTime )
{
$where[] = array( 'joined<?', $firstTime );
}
if ( $author )
{
$where[] = array( 'core_members.member_id=?', $author->member_id );
}
foreach ( \IPS\Db::i()->select( 'core_members.member_id, core_members.name, core_members.members_seo_name, core_members.joined, core_validating.new_reg', 'core_members', $where, 'joined DESC', 10 )
->join( 'core_validating', 'core_validating.member_id=core_members.member_id' ) as $member )
{
if ( empty( $member['new_reg'] ) )
{
$extraItems[] = new Result\Custom( \IPS\DateTime::ts( $member['joined'] ), \IPS\Member::loggedIn()->language()->addToStack( 'activity_member_joined', FALSE, array( 'htmlsprintf' => \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->userLinkFromData( $member['member_id'], $member['name'], $member['members_seo_name'] ) ) ) );
}
}
}
/* Users changed photo */
if ( in_array( 'photo', $extra ) )
{
$where = array( array( 'photo_last_update>?', $lastTime ) );
if ( $firstTime )
{
$where[] = array( 'photo_last_update<?', $firstTime );
}
if ( $author )
{
$where[] = array( 'member_id=?', $author->member_id );
}
foreach ( \IPS\Db::i()->select( array_merge( \IPS\Member::columnsForPhoto(), array( 'photo_last_update' ) ), 'core_members', $where, 'photo_last_update DESC', 10 ) as $member )
{
$extraItems[] = new Result\Custom(
\IPS\DateTime::ts( $member['photo_last_update'] ),
\IPS\Member::loggedIn()->language()->addToStack( 'activity_member_updated_photo', FALSE, array( 'htmlsprintf' => \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->userLinkFromData( $member['member_id'], $member['name'], $member['members_seo_name'] ) ) ),
\IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->userPhotoFromData( $member['member_id'], $member['name'], $member['members_seo_name'], \IPS\Member::photoUrl( $member ), 'tiny' )
);
}
}
/* Follow */
if ( in_array( 'follow_member', $extra ) or in_array( 'follow_content', $extra ) )
{
$where = array( array( 'follow_is_anon=0' ), array( 'follow_added>?', $lastTime ) );
if ( $firstTime )
{
$where[] = array( 'follow_added<?', $firstTime );
}
if ( in_array( 'follow_member', $extra ) xor in_array( 'follow_content', $extra ) )
{
if ( in_array( 'follow_member', $extra ) )
{
$where[] = array( 'follow_app=? AND follow_area=?', 'core', 'member' );
}
else
{
$where[] = array( '( follow_app!=? OR follow_area!=? )', 'core', 'member' );
}
}
if ( $author )
{
if ( in_array( 'follow_member', $extra ) and in_array( 'follow_content', $extra ) )
{
$where[] = array( '( follow_member_id=? OR ( follow_app=? AND follow_area=? AND follow_rel_id=? ) )', $author->member_id, 'core', 'member', $author->member_id );
}
elseif ( in_array( 'follow_member', $extra ) )
{
$where[] = array( 'follow_rel_id=?', $author->member_id );
}
else
{
$where[] = array( 'follow_member_id=?', $author->member_id );
}
}
foreach ( \IPS\Db::i()->select( array( 'follow_app', 'follow_area', 'follow_rel_id', 'follow_member_id', 'follow_added' ), 'core_follow', $where, 'follow_added DESC', 10 ) as $follow )
{
if ( $follow['follow_app'] == 'core' and $follow['follow_area'] == 'member' )
{
$extraItems[] = new Result\Custom(
\IPS\DateTime::ts( $follow['follow_added'] ),
\IPS\Member::loggedIn()->language()->addToStack( 'activity_member_followed', FALSE, array( 'htmlsprintf' => array( \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->userLink( \IPS\Member::load( $follow['follow_member_id'] ) ), \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->userLink( \IPS\Member::load( $follow['follow_rel_id'] ) ) ) ) )
);
}
else
{
$class = 'IPS\\' . $follow['follow_app'] . '\\' . ucfirst( $follow['follow_area'] );
try
{
$thingBeingFollowed = $class::loadAndCheckPerms( $follow['follow_rel_id'] );
$extraItems[] = new Result\Custom(
\IPS\DateTime::ts( $follow['follow_added'] ),
\IPS\Member::loggedIn()->language()->addToStack( 'activity_member_followed', FALSE, array( 'htmlsprintf' => array( \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->userLink( \IPS\Member::load( $follow['follow_member_id'] ) ), \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $thingBeingFollowed->url(), FALSE, $thingBeingFollowed instanceof \IPS\Node\Model ? $thingBeingFollowed->_title : $thingBeingFollowed->mapped('title'), FALSE ) ) ) )
);
}
catch ( \OutOfRangeException $e ) { }
}
}
}
/* Likes */
if ( in_array( 'like', $extra ) or in_array( 'rep_neg', $extra ) )
{
$where = array( array( 'rep_date>?', $lastTime ) );
if ( $firstTime )
{
$where[] = array( 'rep_date<?', $firstTime );
}
if ( !in_array( 'rep_neg', $extra ) or ( \IPS\Settings::i()->reputation_point_types != 'both' and \IPS\Settings::i()->reputation_point_types != 'negative' ) )
{
$where[] = array( 'rep_rating=1' );
}
elseif ( !in_array( 'like', $extra ) or ( \IPS\Settings::i()->reputation_point_types == 'negative' ) )
{
$where[] = array( 'rep_rating=-1' );
}
if ( $author )
{
$where[] = array( '( member_id=? OR member_received=? )', $author->member_id, $author->member_id );
}
foreach ( \IPS\Db::i()->select( '*', 'core_reputation_index', $where, 'rep_date DESC', 10 ) as $rep )
{
try
{
$thingBeingRepped = NULL;
foreach ( \IPS\Application::load( $rep['app'] )->extensions( 'core', 'ContentRouter', TRUE, TRUE ) as $ext )
{
foreach ( $ext->classes as $class )
{
if ( in_array( 'IPS\Content\Reputation', class_implements( $class ) ) and $class::$reputationType == $rep['type'] )
{
$thingBeingRepped = $class::loadAndCheckPerms( $rep['type_id'] );
break;
}
if ( isset( $class::$commentClass ) )
{
$commentClass = $class::$commentClass;
if ( in_array( 'IPS\Content\Reputation', class_implements( $commentClass ) ) and $commentClass::$reputationType == $rep['type'] )
{
$thingBeingRepped = $commentClass::loadAndCheckPerms( $rep['type_id'] );
break;
}
}
if ( isset( $class::$reviewClass ) )
{
$reviewClass = $class::$reviewClass;
if ( in_array( 'IPS\Content\Reputation', class_implements( $reviewClass ) ) and $reviewClass::$reputationType == $rep['type'] )
{
$thingBeingRepped = $reviewClass::loadAndCheckPerms( $rep['type_id'] );
break;
}
}
}
}
if ( !$thingBeingRepped )
{
throw new \OutOfRangeException;
}
if ( \IPS\Settings::i()->reputation_point_types == 'like' )
{
$lang = 'activity_member_liked';
}
elseif ( $rep['rep_rating'] > 0 )
{
$lang = 'activity_rep_pos';
}
else
{
$lang = 'activity_rep_neg';
}
$extraItems[] = new Result\Custom(
\IPS\DateTime::ts( $rep['rep_date'] ),
\IPS\Member::loggedIn()->language()->addToStack( $lang, FALSE, array( 'htmlsprintf' => array(
\IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->userLink( \IPS\Member::load( $rep['member_id'] ) ),
$thingBeingRepped->indefiniteArticle(),
\IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $thingBeingRepped->url(), FALSE, $thingBeingRepped instanceof \IPS\Content\Item ? $thingBeingRepped->mapped('title') : $thingBeingRepped->item()->mapped('title'), FALSE )
) ) )
);
}
catch ( \OutOfRangeException $e ) { }
}
}
}
/* Merge them in */
if ( !empty( $extraItems ) )
{
$results = array_merge( $results, $extraItems );
uasort( $results, function( $a, $b )
{
if ( $a->createdDate->getTimestamp() == $b->createdDate->getTimestamp() )
{
return 0;
}
elseif( $a->createdDate->getTimestamp() < $b->createdDate->getTimestamp() )
{
return 1;
}
else
{
return -1;
}
} );
}
/* And return */
return $results;
}
}