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

+206 -67
View File
@@ -248,10 +248,22 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
*/
public function truncated( $oneLine=FALSE )
{
/* Specifically remove quotes, and any scripts (which someone with HTML posting allowed may have legitimately enabled */
$text = \IPS\Text\Parser::removeElements( $this->content(), array( 'blockquote', 'script' ) );
/* Convert headers and paragraphs into line breaks or just spaces */
$text = str_replace( array( '</p>', '</h1>', '</h2>', '</h3>', '</h4>', '</h5>', '</h6>' ), ( $oneLine ? ' ' : '<br>' ), $text );
$text = strip_tags( $text, '<br>' );
/* 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 );
/* Remove all HTML apart from <br>s*/
$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>)\x{00a0}]*/u', '', $text );
/* Return */
return $text;
}
@@ -419,7 +431,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
isset( $containerClass::$modPerm )
and
(
$member->modPermission( $containerClass::$modPerm ) === 0
$member->modPermission( $containerClass::$modPerm ) === -1
or
(
is_array( $member->modPermission( $containerClass::$modPerm ) )
@@ -787,13 +799,15 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
$index->save();
/* Create a report */
\IPS\Db::i()->insert( 'core_rc_reports', array(
$reportInsert = array(
'rid' => $index->id,
'report' => $reportContent,
'report_by' => (int) \IPS\Member::loggedIn()->member_id,
'date_reported' => time(),
'ip_address' => \IPS\Request::i()->ipAddress()
) );
);
\IPS\Db::i()->insert( 'core_rc_reports', $reportInsert );
/* Rebuild */
@@ -822,7 +836,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
$moderators[ $mod['type'] ][] = $mod['id'];
}
}
$notification = new \IPS\Notification( \IPS\Application::load('core'), 'report_center', $index, array( $index, $reportContent, $this ) );
$notification = new \IPS\Notification( \IPS\Application::load('core'), 'report_center', $index, array( $index, $reportInsert, $this ) );
foreach ( \IPS\Db::i()->select( '*', 'core_members', ( count( $moderators['m'] ) ? \IPS\Db::i()->in( 'member_id', $moderators['m'] ) . ' OR ' : '' ) . \IPS\Db::i()->in( 'member_group_id', $moderators['g'] ) . ' OR ' . \IPS\Db::i()->findInSet( 'mgroup_others', $moderators['g'] ) ) as $member )
{
$notification->recipients->attach( \IPS\Member::constructFromData( $member ) );
@@ -871,13 +885,93 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
/**
* Get HTML for search result display
*
* @return callable
* @param array $indexData Data from the search index
* @param array $authorData Basic data about the author. Only includes columns returned by \IPS\Member::columnsForPhoto()
* @param array $itemData Basic data about the item. Only includes columns returned by item::basicDataColumns()
* @param array|NULL $containerData Basic data about the author. Only includes columns returned by container::basicDataColumns()
* @param array $reputationData Array of people who have given reputation and the reputation they gave
* @param int|NULL $reviewRating If this is a review, the rating
* @param bool $iPostedIn If the user has posted in the item
* @param string $view 'expanded' or 'condensed'
* @param bool $asItem Displaying results as items?
* @return string
*/
public function activityStreamHtml()
public static function searchResult( array $indexData, array $authorData, array $itemData, array $containerData = NULL, array $reputationData, $reviewRating, $iPostedIn, $view, $asItem )
{
return \IPS\Theme::i()->getTemplate( 'system', 'core', 'front' )->activityStreamResult( $this );
/* Item details */
$itemClass = $indexData['index_class'];
if ( in_array( 'IPS\Content\Comment', class_parents( get_called_class() ) ) )
{
$itemClass = static::$itemClass;
$unread = $itemClass::unreadFromData( NULL, $indexData['index_date_updated'], $indexData['index_date_created'], $indexData['index_item_id'], $indexData['index_container_id'], FALSE );
}
else
{
$unread = static::unreadFromData( NULL, $indexData['index_date_updated'], $indexData['index_date_created'], $indexData['index_item_id'], $indexData['index_container_id'], FALSE );
}
$itemUrl = $itemClass::urlFromIndexData( $indexData, $itemData );
/* Object URL */
$indefiniteArticle = static::_indefiniteArticle( $containerData );
if ( in_array( 'IPS\Content\Comment', class_parents( get_called_class() ) ) )
{
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'] ) );
}
else
{
$objectUrl = $itemUrl->setQueryString( array( 'do' => 'findComment', 'comment' => $indexData['index_object_id'] ) );
$showRepUrl = $itemUrl->setQueryString( array( 'do' => 'showRepComment', 'comment' => $indexData['index_object_id'] ) );
}
$indefiniteArticle = $itemClass::_indefiniteArticle( $containerData );
}
else
{
$objectUrl = $itemUrl;
$showRepUrl = $itemUrl->setQueryString( 'do', 'showRep' );
}
$articles = array( 'indefinite' => $indefiniteArticle, 'definite' => \IPS\Member::loggedIn()->language()->addToStack( $itemClass::$title, FALSE, array( 'strtolower' => TRUE ) ) );
/* Container details */
$containerUrl = NULL;
$containerTitle = NULL;
if ( isset( $itemClass::$containerNodeClass ) )
{
$containerClass = $itemClass::$containerNodeClass;
$containerTitle = \IPS\Member::loggedIn()->language()->addToStack( $containerClass::$titleLangPrefix . $indexData['index_container_id'] );
$containerUrl = $containerClass::urlFromIndexData( $indexData, $itemData, $containerData );
}
/* Reputation */
$repCount = array_sum( $reputationData );
/* Snippet */
$snippet = static::searchResultSnippet( $indexData, $authorData, $itemData, $containerData, $reputationData, $reviewRating, $view );
/* Return */
return \IPS\Theme::i()->getTemplate( 'system', 'core', 'front' )->searchResult( $indexData, $articles, $authorData, $itemData, $unread, $asItem ? $itemUrl : $objectUrl, $itemUrl, $containerUrl, $containerTitle, $repCount, $showRepUrl, $snippet, $iPostedIn, $view );
}
/**
* Get snippet HTML for search result display
*
* @param array $indexData Data from the search index
* @param array $authorData Basic data about the author. Only includes columns returned by \IPS\Member::columnsForPhoto()
* @param array $itemData Basic data about the item. Only includes columns returned by item::basicDataColumns()
* @param array|NULL $containerData Basic data about the author. Only includes columns returned by container::basicDataColumns()
* @param array $reputationData Array of people who have given reputation and the reputation they gave
* @param int|NULL $reviewRating If this is a review, the rating
* @param string $view 'expanded' or 'condensed'
* @return callable
*/
public static function searchResultSnippet( array $indexData, array $authorData, array $itemData, array $containerData = NULL, array $reputationData, $reviewRating, $view )
{
return $view == 'expanded' ? \IPS\Theme::i()->getTemplate( 'system', 'core', 'front' )->searchResultSnippet( $indexData ) : '';
}
/**
* Return the filters that are available for selecting table rows
*
@@ -938,59 +1032,85 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
*/
public function canGiveReputation( $type, \IPS\Member $member = NULL )
{
if ( \IPS\Member::loggedIn()->member_id and \IPS\Settings::i()->reputation_enabled and !in_array( $this->author()->member_group_id, explode( ',', \IPS\Settings::i()->reputation_protected_groups ) ) and $this instanceof \IPS\Content\Reputation )
{
/* Can we rep ourselves? */
$member = $member ?: \IPS\Member::loggedIn();
if ( $this->author() == $member and !\IPS\Settings::i()->reputation_can_self_vote )
{
return FALSE;
}
/* Are we allowed to give reputation at all? - We still need to allow users to undo rep they have given */
$limit = ( $type === 1 ) ? $member->group['g_rep_max_positive'] : $member->group['g_rep_max_negative'];
if ( $limit == 0 and !$this->repGiven( $member ) )
{
return false;
}
/* Protected Groups */
if ( in_array( $this->author()->member_group_id, explode( ',', \IPS\Settings::i()->reputation_protected_groups ) ) )
{
return FALSE;
}
/* Now work it out */
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 ( $this->repGiven( $member ) === 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 $this->repGiven( $member ) ? ( $type === 1 ) : ( $type === -1 );
/* If we're using a positive and negative system - it's more complicated... */
case 'both':
switch ( $this->repGiven( $member ) )
{
/* 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 );
}
}
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;
}
@@ -1028,13 +1148,15 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
}
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>?', $member->member_id, \IPS\DateTime::create()->sub( new \DateInterval( 'P1D' ) )->getTimestamp() ) )->first();
$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('rep_daily_exceeded', FALSE, array( 'sprintf' => array( $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 ) ) ) );
}
}
@@ -1049,7 +1171,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
) );
/* Send Notification */
if ( $type === 1 AND $this->author()->member_id AND $this->author() != \IPS\Member::loggedIn() )
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() );
@@ -1186,7 +1308,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
{
/* 3 random names */
$idColumn = static::$databaseColumnId;
foreach ( \IPS\Db::i()->select( '*', 'core_reputation_index', array( 'app=? AND type=? AND type_id=? AND member_id!=?', static::$application, static::$reputationType, $this->$idColumn, ( \IPS\Member::loggedIn()->member_id ) ?: 0 ), 'RAND()', 3 ) as $rep )
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()', 3 ) as $rep )
{
$people[] = \IPS\Theme::i()->getTemplate( 'global', 'core' )->userLink( \IPS\Member::load( $rep['member_id'] ) );
$reputation--;
@@ -1267,7 +1389,7 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
const FOLLOW_PUBLIC = 1;
const FOLLOW_ANONYMOUS = 2;
const NOTIFICATIONS_PER_BATCH = 100;
const NOTIFICATIONS_PER_BATCH = 50;
/**
* Send notifications
@@ -1548,13 +1670,18 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
{
continue;
}
if ( isset( $class::$commentClass ) )
if ( !( $member instanceof \IPS\Member ) )
{
$member = $member ? \IPS\Member::loggedIn() : NULL;
}
if ( isset( $class::$commentClass ) and $class::supportsComments( $member ) )
{
$classes[] = $class::$commentClass;
}
if ( isset( $class::$reviewClass ) )
if ( isset( $class::$reviewClass ) and $class::supportsReviews( $member ) )
{
$classes[] = $class::$reviewClass;
}
@@ -1610,6 +1737,18 @@ abstract class _Content extends \IPS\Patterns\ActiveRecord
* @return string
*/
public function indefiniteArticle( \IPS\Lang $lang = NULL )
{
$container = ( $this instanceof \IPS\Content\Comment ) ? $this->item()->containerWrapper() : $this->containerWrapper();
return static::_indefiniteArticle( $container ? $container->_data : array(), $lang );
}
/**
* Indefinite Article
*
* @param \IPS\Lang|NULL $language The language to use, or NULL for the language of the currently logged in member
* @return string
*/
public static function _indefiniteArticle( array $containerData = NULL, \IPS\Lang $lang = NULL )
{
$lang = $lang ?: \IPS\Member::loggedIn()->language();
return $lang->addToStack( '__indefart_' . static::$title, FALSE );