Version 4.3.0

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

No files matched your search

@@ -145,7 +145,7 @@ class _popular extends \IPS\Dispatcher\Controller
$table->sortDirection = $table->sortDirection ?: 'DESC';
/* Like or what? */
if ( \IPS\Settings::i()->reputation_point_types == 'like' )
if ( \IPS\Content\Reaction::isLikeMode() )
{
\IPS\Member::loggedIn()->language()->words['leader_rep_total'] = \IPS\Member::loggedIn()->language()->addToStack( 'leader_rep_total_likes' );
\IPS\Member::loggedIn()->language()->words['leaderboard_history__desc'] = \IPS\Member::loggedIn()->language()->addToStack( 'leaderboard_history_desc', NULL, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( 'leaderboard_history_likes') ) ) );
@@ -202,7 +202,7 @@ class _popular extends \IPS\Dispatcher\Controller
$endDate = \IPS\DateTime::ts( time() )->setTimezone( $timezone );
$firstRepDate = \IPS\Db::i()->select( 'MIN(rep_date)', 'core_reputation_index' )->first();
$firstIndexDate = \IPS\Db::i()->select( 'MIN(index_date_updated)', 'core_search_index' )->first();
$firstIndexDate = \IPS\Content\Search\Index::i()->firstIndexDate();
$appWhere = null;
$descApp = \IPS\Member::loggedIn()->language()->addToStack( 'leaderboard_in_all_apps' );
@@ -320,13 +320,14 @@ class _popular extends \IPS\Dispatcher\Controller
}
}
/* Got an app? */
$appWhere = '';
/* Do we want results for a specific app */
$customApp = FALSE;
if ( isset( \IPS\Request::i()->in ) and isset( $areas[ \IPS\Request::i()->in ] ) )
{
$appWhere = " AND rep_class='" . \IPS\Db::i()->escape_string( $areas[ \IPS\Request::i()->in ][ 0 ] ) . "'";
$descApp = \IPS\Member::loggedIn()->language()->addToStack( 'leaderboard_in_app', NULL, array( 'sprintf' => array( $areas[ \IPS\Request::i()->in ][ 1 ] ) ) );
$customApp = TRUE;
}
else
{
@@ -341,7 +342,7 @@ class _popular extends \IPS\Dispatcher\Controller
$storeKey = NULL;
$hashes = NULL;
if ( ! $customStart and ! $customEnd AND ! $appWhere )
if ( ! $customStart and ! $customEnd AND ! $customApp )
{
$storeKey = 'leaderHashes_' . \IPS\Request::i()->time . '-' . md5( implode( ',', \IPS\Member::loggedIn()->groups ) );
@@ -368,9 +369,9 @@ class _popular extends \IPS\Dispatcher\Controller
/* Get rep hashes */
$inner = \IPS\Db::i()->select( 'class_type_id_hash, SUM(rep_rating) as total_rep', array( 'core_reputation_index', 'x' ), array( 'rep_date BETWEEN ' . $date->getTimeStamp() . ' AND ' . $endDate->getTimeStamp() . $appWhere ), NULL, NULL, 'class_type_id_hash' );
$repHashes = iterator_to_array( \IPS\Db::i()->select( 'class_type_id_hash, total_rep', $inner, NULL, 'x.total_rep desc', array( 0, 500 ) )->setKeyField('class_type_id_hash') );
/* Now filter through permissions */
$searchHashes = iterator_to_array( \IPS\Db::i()->select( 'index_class_type_id_hash', array( 'core_search_index', 'si' ), array( array( "( si.index_permissions = '*' OR " . \IPS\Db::i()->findInSet( 'si.index_permissions', \IPS\Member::loggedIn()->groups ) . ' AND si.index_hidden=0 )' ), array( \IPS\Db::i()->in( 'si.index_class_type_id_hash', array_keys( $repHashes ) ) ) ) )->setKeyField('index_class_type_id_hash') );
$searchHashes = \IPS\Content\Search\Index::i()->hashesWithPermission( array_keys( $repHashes ), \IPS\Member::loggedIn() );
/* Filter out rep hashes not in search results */
$hashes = array_slice( array_intersect_key( $repHashes, $searchHashes ), 0, 50 );
@@ -386,7 +387,7 @@ class _popular extends \IPS\Dispatcher\Controller
$or = array();
$results = array();
$preLoadMembers = array();
if ( count( $hashes ) )
{
/* Now get the reputation data */
@@ -395,25 +396,15 @@ class _popular extends \IPS\Dispatcher\Controller
$data['total_rep'] = $hashes[ $data['class_type_id_hash'] ]['total_rep'];
$repData[ $data['rep_class'] . '-' . $data['type_id'] ] = $data;
$classes[ $data['rep_class'] ][] = $data['type_id'];
if ( ! empty( $data['member_id'] ) )
{
$preLoadMembers[] = $data['member_id'];
}
}
foreach( $classes as $class => $ids )
{
$or[] = '( index_class=\'' . \IPS\Db::i()->escape_string( $class ) . '\' AND ' . \IPS\Db::i()->in( 'index_object_id', $ids ) . ')';
$or[] = \IPS\Content\Search\ContentFilter::init( $class )->onlyInIds( $ids );
}
/* Construct the query */
$query = \IPS\Db::i()->select( '*', array( 'core_search_index', 'main' ), array( implode( ' OR ', $or ) ), NULL, NULL, NULL, NULL );
$array = iterator_to_array( $query );
$sorted = array();
/* Manually sort */
/* Query and manually sort */
$array = \IPS\Content\Search\Query::init()->filterByContent( $or )->search()->getArrayCopy();
foreach( $array as $index => $data )
{
if ( isset( $repData[ $data['index_class'] . '-' . $data['index_object_id'] ] ) )
@@ -433,8 +424,13 @@ class _popular extends \IPS\Dispatcher\Controller
/* Get top rated contributors */
$topContributors = array();
$innerQuery = \IPS\Db::i()->select( 'core_reputation_index.member_received as member, SUM(rep_rating) as rep', 'core_reputation_index', array( 'member_received>0 ' . $appWhere . ' and rep_date BETWEEN ' . intval( $date->getTimeStamp() ) . ' AND ' . intval( $endDate->getTimeStamp() ) ), NULL, NULL, 'member' );
$innerQueryWhere = array();
$innerQueryWhere[] = array( 'member_received>0 ' . $appWhere . ' and rep_date BETWEEN ' . intval( $date->getTimeStamp() ) . ' AND ' . intval( $endDate->getTimeStamp() ) );
$innerQueryWhere[] = \IPS\Db::i()->in( 'member_group_id', \IPS\Settings::i()->leaderboard_excluded_groups, TRUE );
$innerQuery = \IPS\Db::i()->select( 'core_reputation_index.member_received as member, SUM(rep_rating) as rep', 'core_reputation_index', $innerQueryWhere, NULL, NULL, 'member' )->join( 'core_members', array( 'core_reputation_index.member_received = core_members.member_id' ) );
foreach( \IPS\Db::i()->select( 'member, rep', array( $innerQuery, 'in' ), NULL, 'rep DESC', 4 )->setKeyField('member')->setValueField('rep') as $member => $rep )
{
$topContributors[ $member ] = $rep;
@@ -455,7 +451,7 @@ class _popular extends \IPS\Dispatcher\Controller
}
/* Work out the description for popular content */
if ( \IPS\Settings::i()->reputation_point_types == 'like' )
if ( \IPS\Content\Reaction::isLikeMode() )
{
$popularResultsSingle = 'popular_results_single_desc';
$popularResultsMany = 'popular_results_desc';
@@ -481,6 +477,12 @@ class _popular extends \IPS\Dispatcher\Controller
\IPS\Output::i()->linkTags['canonical'] = (string) \IPS\Http\Url::internal( "app=core&module=discover&controller=popular&tab=leaderboard", 'front', 'leaderboard_leaderboard' );
/* If there are no results tell search engines not to index the page */
if( !count( $results ) )
{
\IPS\Output::i()->metaTags['robots'] = 'noindex';
}
/* Display */
return \IPS\Theme::i()->getTemplate('popular')->popularWrapper( $results, $areas, $topContributors, $dates, $description, $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'popupTemplate' ) ), $tzOffsetDifference );
}
@@ -5,7 +5,6 @@
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 13 Oct 2016
*/
@@ -39,7 +39,8 @@ class _streams extends \IPS\Dispatcher\Controller
/* Necessary CSS/JS */
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js('front_streams.js', 'core' ) );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/streams.css' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'front_statuses.js', 'core' ) );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/streams.css' ) );
if ( \IPS\Theme::i()->settings['responsive'] )
{
@@ -121,7 +122,7 @@ class _streams extends \IPS\Dispatcher\Controller
{
\IPS\Output::i()->json( array(
'url' => $defaultStream->url(),
'title' => htmlspecialchars( $defaultStream->_title, ENT_QUOTES | \IPS\HTMLENTITIES, 'UTF-8', FALSE ),
'title' => htmlspecialchars( $defaultStream->_title, ENT_QUOTES | ENT_DISALLOWED, 'UTF-8', FALSE ),
'tooltip' => $defaultStream->_title, // We need to pass this individually for the tooltip that shows, but the JS itself will escape any entities
'id' => $defaultStream->_id
) );
@@ -166,7 +167,7 @@ class _streams extends \IPS\Dispatcher\Controller
{
\IPS\Output::i()->json( array(
'url' => \IPS\Http\Url::internal( "app=core&module=discover&controller=streams", 'front', 'discover_all' ),
'title' => htmlspecialchars( $defaultStream->_title, ENT_QUOTES | \IPS\HTMLENTITIES, 'UTF-8', FALSE ),
'title' => htmlspecialchars( $defaultStream->_title, ENT_QUOTES | ENT_DISALLOWED, 'UTF-8', FALSE ),
'id' => $defaultStream->_id
) );
}
@@ -187,7 +188,13 @@ class _streams extends \IPS\Dispatcher\Controller
/* but only if we haven't submitted the form on this request */
if( !\IPS\Request::i()->stream_submitted )
{
$streamFields = array( 'include_comments', 'classes', 'clubs', 'ownership', 'custom_members', 'read', 'follow', 'followed_types', 'date_type', 'date_start', 'date_end', 'date_relative_days', 'sort', 'tags', 'unread_links' );
$streamFields = array( 'include_comments', 'classes', 'ownership', 'custom_members', 'read', 'follow', 'followed_types', 'date_type', 'date_start', 'date_end', 'date_relative_days', 'sort', 'tags', 'unread_links' );
/* Clubs enabled? */
if( \IPS\Settings::i()->clubs )
{
$streamFields[] = 'clubs';
}
/* Build and format field values */
$_values = array();
@@ -379,7 +386,7 @@ class _streams extends \IPS\Dispatcher\Controller
$output = \IPS\Theme::i()->getTemplate('streams')->streamItems( $results, TRUE, ( $stream->id and !$stream->include_comments ) ? 'last_comment' : 'date', $view );
$return = array(
'title' => htmlspecialchars( $stream->_title, \IPS\HTMLENTITIES | ENT_QUOTES, 'UTF-8', FALSE ),
'title' => htmlspecialchars( $stream->_title, ENT_DISALLOWED | ENT_QUOTES, 'UTF-8', FALSE ),
'blurb' => $stream->blurb(),
'config' => json_encode( $stream->config() ),
'count' => count( $results ),
@@ -484,7 +491,7 @@ class _streams extends \IPS\Dispatcher\Controller
if ( $stream->member != \IPS\Member::loggedIn()->member_id )
{
\IPS\Output::i()->error( 'no_permission', '2C280/8', 403, '' );
\IPS\Output::i()->error( 'no_module_permission', '2C280/8', 403, '' );
}
$form = new \IPS\Helpers\Form;
@@ -599,7 +606,7 @@ class _streams extends \IPS\Dispatcher\Controller
{
$url = $stream ? $stream->baseUrl->setQueryString( 'className', $class ) : \IPS\Request::i()->url()->setQueryString( 'className', $class );
$containerClass = $class::$containerNodeClass;
$field = new \IPS\Helpers\Form\Node( 'stream_containers_' . $class::$title, isset( $currentContainers[ $class ] ) ? $currentContainers[ $class ] : array(), NULL, array( 'url' => $url, 'class' => $class::$containerNodeClass, 'multiple' => TRUE, 'permissionCheck' => $containerClass::searchableNodesPermission(), 'clubs' => \IPS\IPS::classUsesTrait( $containerClass, 'IPS\Content\ClubContainer' ) ), NULL, NULL, NULL, 'stream_containers_' . $class::$title );
$field = new \IPS\Helpers\Form\Node( 'stream_containers_' . $class::$title, isset( $currentContainers[ $class ] ) ? $currentContainers[ $class ] : array(), NULL, array( 'url' => $url, 'class' => $class::$containerNodeClass, 'multiple' => TRUE, 'permissionCheck' => $containerClass::searchableNodesPermission(), 'clubs' => ( \IPS\Settings::i()->clubs AND \IPS\IPS::classUsesTrait( $containerClass, 'IPS\Content\ClubContainer' ) ) ), NULL, NULL, NULL, 'stream_containers_' . $class::$title );
$field->label = \IPS\Member::loggedIn()->language()->addToStack( 'stream_narrow_by_container_label', NULL, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( $containerClass::$nodeTitle ) ) ) );
return \IPS\Output::i()->json( array( 'node' => \IPS\Theme::i()->getTemplate('streams')->filterFormContentTypeContent( $field, $class, \IPS\Request::i()->key ) ) );
@@ -616,6 +623,11 @@ class _streams extends \IPS\Dispatcher\Controller
*/
protected function getClubElement()
{
if( !\IPS\Settings::i()->clubs )
{
return \IPS\Output::i()->json( array( 'field' => '' ) );
}
$clubOptions = array();
foreach ( \IPS\Member\Club::clubs( \IPS\Member::loggedIn(), NULL, 'name', TRUE ) as $club )
{
@@ -681,25 +693,48 @@ class _streams extends \IPS\Dispatcher\Controller
/* Handle submissions */
if ( $values = $form->values() )
{
/* As the node container form elements are not in the actual form, we need to work some magic. And by magic, I do mean a sort of hacky thing. */
foreach ( \IPS\Request::i() as $k => $v )
/* As the node container form elements are not in the actual form, we need to work some magic.
And by magic, I do mean a sort of hacky thing. First, if we are editing a stream, we need to get our existing stream filters
and put these into an array. Then we loop over the input and overwrite the existing filters with what was submitted. If the user
never loaded the container filters we'll retain what we had (expected) but if they did and adjusted or removed container filters
then we'll update with what they submitted (expected).
*/
if( $stream->id )
{
if ( mb_substr( $k, 0, 18 ) == 'stream_containers_' and $v )
$currentContainers = $stream->containers ? json_decode( $stream->containers, TRUE ) : array();
foreach( $currentContainers as $class => $containers )
{
$vals = explode( ',', $v );
$values[ $k ] = array_combine( $vals, $vals );
$values['stream_containers_' . $class::$title ] = array_combine( $containers, $containers );
}
}
foreach ( \IPS\Request::i() as $k => $v )
{
if ( mb_substr( $k, 0, 18 ) == 'stream_containers_' )
{
if( $v )
{
$vals = explode( ',', $v );
$values[ $k ] = array_combine( $vals, $vals );
}
elseif( isset( $values[ $k ] ) )
{
$values[ $k ] = NULL;
}
}
}
/* Update only */
if ( isset( \IPS\Request::i()->updateOnly ) )
{
$formattedValues = $stream->formatFormValues( $values );
foreach ( array( 'include_comments', 'classes', 'containers', 'clubs', 'ownership', 'custom_members', 'read', 'follow', 'followed_types', 'date_type', 'date_start', 'date_end', 'date_relative_days', 'sort', 'tags', 'unread_links' ) as $k )
{
$requestKey = 'stream_' . $k;
if ( isset( $formattedValues[ $k ] ) AND $stream->$k != $formattedValues[ $k ] )
if ( array_key_exists( $k, $formattedValues ) AND $stream->$k != $formattedValues[ $k ] )
{
$stream->$k = $formattedValues[ $k ];
$stream->baseUrl = $stream->baseUrl->setQueryString( 'stream_' . $k, \IPS\Request::i()->$requestKey );