Version 4.7.0

This commit is contained in:
Neo committed 2025-12-19 16:00:35 -08:00
1 parent 517a5e1f70
commit 8ba7d43898
1933 files changed
+65613 -11332

No files matched your search

+201 -74
View File
@@ -47,7 +47,25 @@ class _Controller extends \IPS\Helpers\CoverPhoto\Controller
/* Ensure JS loaded for forms/content functions such as moderation */
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'front_core.js', 'core' ) );
return parent::execute();
parent::execute();
/* Do this to prevent non-existent and non-accessible items from returning a status of 200 */
if ( empty( \IPS\Output::i()->output ) AND !\IPS\Request::i()->_bypassItemIdCheck AND isset( \IPS\Request::i()->id ) )
{
$contentModel = static::$contentModel;
try
{
$item = $contentModel::load( \IPS\Request::i()->id );
if ( !$item->can( 'read' ) )
{
\IPS\Output::i()->error( 'node_error', '2S136/1X', 403, '' );
}
}
catch ( \OutOfRangeException $e )
{
\IPS\Output::i()->error( 'node_error', '2S136/1Y', 404, '' );
}
}
}
/**
@@ -132,7 +150,15 @@ class _Controller extends \IPS\Helpers\CoverPhoto\Controller
{
\IPS\Output::i()->redirect( $redirectToUrl );
}
/* Get ready to store data layer info */
$dataLayer = \IPS\Settings::i()->core_datalayer_enabled;
$dataLayerProperties = array();
if ( $dataLayer )
{
$dataLayerProperties = $item->getDataLayerProperties();
}
/* Check we're on a valid page */
$paginationType = 'comment';
if ( isset( \IPS\Request::i()->tab ) and \IPS\Request::i()->tab === 'reviews' )
@@ -168,6 +194,8 @@ class _Controller extends \IPS\Helpers\CoverPhoto\Controller
$lastPageMethod = 'last' . mb_ucfirst( $paginationType ) . 'PageUrl';
\IPS\Output::i()->redirect( $item->$lastPageMethod(), NULL, 303 );
}
$dataLayerProperties['page_number'] = \intval( \IPS\Request::i()->page );
}
/* Add rel tags */
@@ -211,10 +239,21 @@ class _Controller extends \IPS\Helpers\CoverPhoto\Controller
try
{
\IPS\Redis::i()->zIncrBy( 'topic_views', 1, $class .'__' . $item->$idColumn );
$countUpdated = true;
}
catch( \Exception $e ) {}
}
if( \IPS\Application::appIsEnabled( 'cloud' ) and \IPS\cloud\Realtime::i()->isEnabled('trending') and $class::$includeInTrending )
{
try
{
/* Score by timestamp for trending */
\IPS\Redis::i()->zIncrBy( 'trending', time() * 0.1, $class .'__' . $item->$idColumn );
}
catch( \Exception $e ) {}
}
if ( ! $countUpdated )
{
@@ -262,7 +301,21 @@ class _Controller extends \IPS\Helpers\CoverPhoto\Controller
'item_type' => isset( $class::$contentType ) ? $class::$contentType : $class::$title,
'category_name' => isset( $item::$databaseColumnMap['container'] ) ? $item->container()->_title : NULL
);
/* Data Layer */
if ( $dataLayer )
{
if ( !\IPS\Request::i()->isAjax() )
{
\IPS\core\DataLayer::i()->addEvent( 'content_view', $dataLayerProperties );
}
unset( $dataLayerProperties['ips_key'] );
foreach ( $dataLayerProperties as $key => $value )
{
\IPS\core\DataLayer::i()->addContextProperty( $key, $value );
}
}
if( $item->mapped( 'updated' ) OR $item->mapped( 'last_comment' ) OR $item->mapped( 'last_review' ) )
{
\IPS\Output::i()->metaTags['og:updated_time'] = \IPS\DateTime::ts( $item->mapped( 'updated' ) ? $item->mapped( 'updated' ) : ( $item->mapped( 'last_comment' ) ? $item->mapped( 'last_comment' ) : $item->mapped( 'last_review' ) ) )->rfc3339();
@@ -356,6 +409,14 @@ class _Controller extends \IPS\Helpers\CoverPhoto\Controller
$item = $class::loadAndCheckPerms( \IPS\Request::i()->id );
$commentClass = $class::$commentClass;
/* Don't auto-poll archived content, it never gets updated */
if( isset( $item::$archiveClass ) AND method_exists( $item, 'isArchived' ) AND $item->isArchived() )
{
\IPS\Output::i()->json( array( 'error' => 'auto_polling_disabled' ) );
return;
}
$commentIdColumn = $commentClass::$databaseColumnId;
$commentDateColumn = $commentClass::$databaseColumnMap['date'];
@@ -514,7 +575,7 @@ class _Controller extends \IPS\Helpers\CoverPhoto\Controller
$item->processAfterEdit( $values );
/* Moderator log */
$toLog = array( $item::$title => FALSE, $item->url()->__toString() => FALSE, $item::$title => TRUE, $item->mapped( 'title' ) => FALSE );
$toLog = array( $item::$title => TRUE, $item->url()->__toString() => FALSE, $item::$title => TRUE, $item->mapped( 'title' ) => FALSE );
if ( $oldTitle != $item->$titleField )
{
@@ -723,12 +784,12 @@ class _Controller extends \IPS\Helpers\CoverPhoto\Controller
$newTitle = new \IPS\Helpers\Form\Text( 'newTitle', \IPS\Request::i()->newTitle );
if( $newTitle->validate() !== TRUE or $newTitle->value !== \IPS\Request::i()->newTitle )
if( $newTitle->validate() !== TRUE )
{
throw new \InvalidArgumentException( \IPS\Member::loggedIn()->language()->addToStack( 'form_tags_not_allowed', FALSE, array( 'sprintf' => array( \IPS\Request::i()->newTitle ) ) ) );
}
$item->$titleField = \IPS\Request::i()->newTitle;
$item->$titleField = $newTitle->value;
$idField = $item::$databaseColumnId;
$item->save();
@@ -761,7 +822,7 @@ class _Controller extends \IPS\Helpers\CoverPhoto\Controller
\IPS\Session::i()->modLog( 'modlog__comment_edit_title', array( (string) $item->url() => FALSE, $item->$titleField => FALSE, $oldTitle => FALSE ), $item );
}
\IPS\Output::i()->json( 'OK' );
\IPS\Output::i()->json( $item->$titleField );
}
catch( \LogicException $e )
{
@@ -1557,7 +1618,7 @@ class _Controller extends \IPS\Helpers\CoverPhoto\Controller
/* If we do not have a time last read set for this content, fallback to the reset time */
$resetTimes = \IPS\Member::loggedIn()->markersResetTimes( $class::$application );
if ( array_key_exists( $item->container()->_id, $resetTimes ) and $item->mapped('date') < $resetTimes[ $item->container()->_id ] )
if ( ( \is_array( $resetTimes ) AND array_key_exists( $item->container()->_id, $resetTimes ) ) and $item->mapped('date') < $resetTimes[ $item->container()->_id ] )
{
$comment = $item->comments( 1, NULL, 'date', 'asc', NULL, NULL, \IPS\DateTime::ts( $resetTimes[ $item->container()->_id ] ) );
@@ -1597,7 +1658,7 @@ class _Controller extends \IPS\Helpers\CoverPhoto\Controller
$error = 'node_error';
}
\IPS\Output::i()->error( $error, '2S136/V', 404, '' );
\IPS\Output::i()->error( $error, '2S136/V', 403, '' );
}
catch( \LogicException $e )
{
@@ -1666,7 +1727,7 @@ class _Controller extends \IPS\Helpers\CoverPhoto\Controller
$error = 'node_error';
}
\IPS\Output::i()->error( $error, '2S136/Q', 404, '' );
\IPS\Output::i()->error( $error, '2S136/Q', 403, '' );
}
}
@@ -1708,7 +1769,7 @@ class _Controller extends \IPS\Helpers\CoverPhoto\Controller
}
catch( \LogicException $e )
{
\IPS\Output::i()->error( 'node_error', '2S136/X', 404, '' );
\IPS\Output::i()->error( 'node_error', '2S136/X', 403, '' );
}
}
@@ -1909,7 +1970,8 @@ class _Controller extends \IPS\Helpers\CoverPhoto\Controller
}
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\YesNo( 'message_is_public', isset( $current['is_public'] ) ? $current['is_public'] : TRUE ) );
$form->attributes['data-controller'] = 'core.front.core.contentMessage';
$form->add( new \IPS\Helpers\Form\YesNo( 'message_is_public', isset( $current['is_public'] ) ? $current['is_public'] : FALSE ) );
$form->add( new \IPS\Helpers\Form\Editor( 'message', $current ? $current['message'] : NULL , TRUE, array( 'app' => 'core', 'key' => 'Meta', 'autoSaveKey' => $current ? "meta-message-" . \IPS\Request::i()->meta_id : "meta-message-new", 'attachIds' => $current ? array( \IPS\Request::i()->meta_id, NULL, 'core_ContentMessages' ) : NULL ) ) );
$form->add( new \IPS\Helpers\Form\Custom( 'message_color', isset( $current['color'] ) ? $current['color'] : 'none', FALSE, array( 'getHtml' => function( $element )
{
@@ -2083,7 +2145,7 @@ class _Controller extends \IPS\Helpers\CoverPhoto\Controller
if ( \IPS\Member::loggedIn()->modPermission('can_set_best_answer') )
{
\IPS\Session::i()->modLog( 'modlog__best_answer_unset', array( $item->$solvedField => FALSE ), $item );
\IPS\Session::i()->modLog( 'modlog__best_answer_unset', array( $comment->$idField => FALSE ), $item );
}
}
catch ( \Exception $e ) {}
@@ -2248,7 +2310,7 @@ class _Controller extends \IPS\Helpers\CoverPhoto\Controller
}
}
}
$commentPosition = \IPS\Db::i()->select( 'COUNT(*) AS position', $commentClass::$databaseTable, $where )->first();
$commentPosition = $commentClass::db()->select( 'COUNT(*) AS position', $commentClass::$databaseTable, $where )->first();
/* Now work out what page that makes it */
$url = $item->url();
@@ -2381,7 +2443,7 @@ class _Controller extends \IPS\Helpers\CoverPhoto\Controller
if ( isset( \IPS\Request::i()->restoreAsHidden ) )
{
\IPS\Session::i()->modLog( 'modlog__action_restore_hidden', array(
$comment::$title => FALSE,
$comment::$title => TRUE,
$comment->url()->__toString() => FALSE
) );
@@ -2390,7 +2452,7 @@ class _Controller extends \IPS\Helpers\CoverPhoto\Controller
else
{
\IPS\Session::i()->modLog( 'modlog__action_restore', array(
$comment::$title => FALSE,
$comment::$title => TRUE,
$comment->url()->__toString() => FALSE
) );
@@ -2452,7 +2514,7 @@ class _Controller extends \IPS\Helpers\CoverPhoto\Controller
/* Post Anonymously */
$container = $item->containerWrapper();
if ( $container and $container->canPostAnonymously( $container::ANON_COMMENTS ) and $comment instanceof \IPS\Content\Anonymous and \IPS\Member::loggedIn()->member_id and \IPS\Member::loggedIn()->group['gbw_can_post_anonymously'] )
if ( $container and $container->canPostAnonymously( $container::ANON_COMMENTS ) and $comment instanceof \IPS\Content\Anonymous and ( $comment->author() and $comment->author()->group['gbw_can_post_anonymously'] or $comment->isAnonymous() ) )
{
$form->add ( new \IPS\Helpers\Form\YesNo( 'post_anonymously', $comment->isAnonymous(), FALSE, array( 'label' => \IPS\Member::loggedIn()->language()->addToStack( 'post_anonymously_suffix' ) ), NULL, NULL, NULL, 'post_anonymously' ) );
}
@@ -2545,11 +2607,17 @@ class _Controller extends \IPS\Helpers\CoverPhoto\Controller
$comment->editRating( $values['rating_value'] );
}
/* Anonymous posting */
if( isset( $values[ 'post_anonymously' ] ) )
{
$comment->setAnonymous( $values[ 'post_anonymously' ] );
/* The anon changes to the user details (photo, name, etc) is outside the JS scope to change when editing a comment */
if ( (bool) $comment->isAnonymous() !== (bool) $values['post_anonymously'] )
{
$reloadPage = true;
}
$comment->setAnonymous( $values[ 'post_anonymously' ], $comment->author() );
}
/* Moderator log */
@@ -2770,12 +2838,18 @@ class _Controller extends \IPS\Helpers\CoverPhoto\Controller
\IPS\Application::load('core')->extensions( 'core', 'MetaData' )['FeaturedComments']->unfeatureComment( $oldItem, $comment );
}
/* Remove solved associations */
if ( $oldItem and \IPS\IPS::classUsesTrait( $oldItem, 'IPS\Content\Solvable' ) and $oldItem->isSolved() and ( $oldItem->mapped('solved_comment_id') === $comment->$commentIdColumn ) )
{
$oldItem->toggleSolveComment( $comment->$commentIdColumn, FALSE );
}
$comment->move( $item );
$oldItem->rebuildFirstAndLastCommentData();
/* Log it */
\IPS\Session::i()->modLog( 'modlog__action_split', array(
$item::$title => FALSE,
$item::$title => TRUE,
$item->url()->__toString() => FALSE,
$item->mapped( 'title' ) => FALSE,
$oldItem->url()->__toString() => FALSE,
@@ -3239,7 +3313,7 @@ class _Controller extends \IPS\Helpers\CoverPhoto\Controller
/* Create a copy of the old item for logging */
$oldItem = $item;
$comments = new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select(
$comments = iterator_to_array( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select(
'*',
$commentClass::$databaseTable,
array(
@@ -3247,7 +3321,7 @@ class _Controller extends \IPS\Helpers\CoverPhoto\Controller
\IPS\Db::i()->in( $commentClass::$databasePrefix . $commentClass::$databaseColumnId, array_keys( \IPS\Request::i()->multimod ) )
),
$commentClass::$databasePrefix . $commentClass::$databaseColumnMap['date']
), $commentClass );
), $commentClass ) );
foreach ( $comments as $comment )
{
@@ -3322,6 +3396,12 @@ class _Controller extends \IPS\Helpers\CoverPhoto\Controller
\IPS\Application::load('core')->extensions( 'core', 'MetaData' )['FeaturedComments']->unfeatureComment( $oldItem, $comment );
}
/* Remove solved associations */
if ( $oldItem and \IPS\IPS::classUsesTrait( $oldItem, 'IPS\Content\Solvable' ) and $oldItem->isSolved() and ( $oldItem->mapped('solved_comment_id') === $comment->$commentIdColumn ) )
{
$oldItem->toggleSolveComment( $comment->$commentIdColumn, FALSE );
}
if( $comment == $firstComment AND $comment->hidden() !== 0 )
{
if ( isset( $comment::$databaseColumnMap['hidden'] ) )
@@ -3364,7 +3444,7 @@ class _Controller extends \IPS\Helpers\CoverPhoto\Controller
/* Log it */
\IPS\Session::i()->modLog( 'modlog__action_split', array(
$item::$title => FALSE,
$item::$title => TRUE,
$item->url()->__toString() => FALSE,
$item->mapped( 'title' ) => FALSE,
$oldItem->url()->__toString() => FALSE,
@@ -3460,7 +3540,7 @@ class _Controller extends \IPS\Helpers\CoverPhoto\Controller
/* Log it */
\IPS\Session::i()->modLog( 'modlog__action_merge_comments', array(
$firstComment::$title => FALSE,
$firstComment::$title => TRUE,
$firstComment->url()->__toString() => FALSE,
$firstComment->$commentIdColumn => FALSE,
), $item );
@@ -3751,64 +3831,96 @@ class _Controller extends \IPS\Helpers\CoverPhoto\Controller
{
$class = static::$contentModel;
$item = $class::loadAndCheckPerms( \IPS\Request::i()->id );
$items = [];
if( !$item instanceof \IPS\Content\Tags OR ( $item->tags() === NULL AND $item->prefix( FALSE ) === NULL ) )
/* Are we using elasticsearch? */
if ( \IPS\Settings::i()->search_method == 'elastic' )
{
return NULL;
}
$query = \IPS\Content\Search\Query::init( \IPS\Member::loggedIn() );
$query->resultsToGet = $limit;
$query->filterByMoreLikeThis( $item );
$query->filterByLastUpdatedDate( \IPS\DateTime::create()->sub( new \DateInterval( 'P2Y' ) ) );
$results = $query->search();
$results->init();
/* Store tags in array, so that we can add a prefix if set */
$tags = $item->tags() ?: array();
$tags[] = $item->prefix( FALSE );
/* Build the where clause */
$where = array(
array( '(' . \IPS\Db::i()->in( 'tag_text', $tags ) . ')' ),
array( '!(tag_meta_app=? and tag_meta_area=? and tag_meta_id=?)', $class::$application, $class::$module, \IPS\Request::i()->id ),
array( '(' . \IPS\Db::i()->findInSet( 'tag_perm_text', \IPS\Member::loggedIn()->groups ) . ' OR ' . 'tag_perm_text=? )', '*' ),
array( 'tag_perm_visible=1' )
);
/* Allow the item to manipulate the query if needed */
if( $item->similarContentFilter() )
{
$where = array_merge( $where, $item->similarContentFilter() );
}
$select = \IPS\Db::i()->select(
'tag_meta_app,tag_meta_area,tag_meta_id',
'core_tags',
$where,
'tag_added DESC',
array( 0, $limit ),
array( 'tag_meta_app', 'tag_meta_area', 'tag_meta_id', 'tag_added' )
)->join(
'core_tags_perms',
array( 'tag_perm_aai_lookup=tag_aai_lookup' )
);
$items = array();
foreach( $select as $result )
{
foreach( \IPS\Application::load( $result['tag_meta_app'] )->extensions( 'core', 'ContentRouter' ) as $key => $router )
foreach( $results as $result )
{
foreach( $router->classes AS $itemClass )
$object = $result->asArray()['indexData'];
$class = $object['index_class'];
$loaded = $class::loadAndCheckPerms( $object['index_object_id'] );
if ( $loaded instanceof \IPS\Content\Item )
{
if( $itemClass::$module == $result['tag_meta_area'] )
{
try
{
$items[ $result['tag_meta_id'] ] = $itemClass::loadAndCheckPerms( $result['tag_meta_id'] );
break;
}
catch( \Exception $e ){}
}
$obj = $loaded;
}
else
{
$obj = $loaded->item();
}
$items[] = $obj;
}
}
else
{
if ( !$item instanceof \IPS\Content\Tags or ( $item->tags() === NULL and $item->prefix( FALSE ) === NULL ) )
{
return NULL;
}
/* Store tags in array, so that we can add a prefix if set */
$tags = $item->tags() ?: array();
$tags[] = $item->prefix( FALSE );
/* Build the where clause */
$where = array(
array('(' . \IPS\Db::i()->in( 'tag_text', $tags ) . ')'),
array('!(tag_meta_app=? and tag_meta_area=? and tag_meta_id=?)', $class::$application, $class::$module, \IPS\Request::i()->id),
array('(' . \IPS\Db::i()->findInSet( 'tag_perm_text', \IPS\Member::loggedIn()->groups ) . ' OR ' . 'tag_perm_text=? )', '*'),
array('tag_perm_visible=1')
);
/* Allow the item to manipulate the query if needed */
if ( $item->similarContentFilter() )
{
$where = array_merge( $where, $item->similarContentFilter() );
}
$select = \IPS\Db::i()->select(
'tag_meta_app,tag_meta_area,tag_meta_id',
'core_tags',
$where,
'tag_added DESC',
array(0, $limit),
array('tag_meta_app', 'tag_meta_area', 'tag_meta_id', 'tag_added')
)->join(
'core_tags_perms',
array('tag_perm_aai_lookup=tag_aai_lookup')
);
foreach ( $select as $result )
{
foreach ( \IPS\Application::load( $result['tag_meta_app'] )->extensions( 'core', 'ContentRouter' ) as $key => $router )
{
foreach ( $router->classes as $itemClass )
{
if ( $itemClass::$module == $result['tag_meta_area'] )
{
try
{
$items[$result['tag_meta_id']] = $itemClass::loadAndCheckPerms( $result['tag_meta_id'] );
break;
}
catch ( \Exception $e )
{
}
}
}
}
}
}
return $items;
}
catch ( \Exception $e )
@@ -3905,7 +4017,12 @@ class _Controller extends \IPS\Helpers\CoverPhoto\Controller
}
catch ( \UnderflowException $e )
{
\IPS\Output::i()->error( 'node_error', '2S136/1W', 403, '' );
if( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicHover( \IPS\Member::loggedIn()->language()->addToStack( 'anon_user_deleted' ) );
return;
}
\IPS\Output::i()->error( 'anon_user_deleted', '2S136/1W', 403, '' );
}
$author = \IPS\Member::load( $anonymousAuthor );
@@ -3935,4 +4052,14 @@ class _Controller extends \IPS\Helpers\CoverPhoto\Controller
{
$this->reveal();
}
/**
* A convenient hook point to finish any set up in manage()
*
* @param \IPS\Content\Item $item The item that is being set up in manage()
* @return void
*/
protected function finishManage( $item )
{
}
}