Version 4.7.23

This commit is contained in:
Neo committed 2025-12-19 16:21:27 -08:00
1 parent 7124a02564
commit 25ddeb65d6
1791 files changed
+76990 -44452

No files matched your search

+99 -45
View File
@@ -11,6 +11,10 @@
namespace IPS\forums;
use IPS\Content\Comment;
use IPS\Db;
use IPS\Settings;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
@@ -27,6 +31,7 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
use \IPS\Node\Colorize;
use \IPS\Node\Statistics;
use \IPS\Content\ViewUpdates;
use \IPS\Node\DelayedCount;
/**
* @brief [ActiveRecord] Multiton Store
@@ -329,55 +334,40 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
* Last Poster ID Column
*/
protected static $lastPosterIdColumn = 'last_poster_id';
/**
* Set last comment
*
* @param \IPS\Content\Comment $comment The latest comment or NULL to work it out
* @return void
* @param Comment|null $comment The latest comment or NULL to work it out
* @param Item|null $updatedItem We sometimes run setLastComment() when an item has been edited, if so, that item will be here
* @return void
*/
public function setLastComment( \IPS\Content\Comment $comment=NULL )
public function _setLastComment( \IPS\Content\Comment $comment=NULL, \IPS\Content\Item $updatedItem=NULL )
{
if ( $comment === NULL )
/* If we were given an item, but it's older than the current last post, clear it */
if( $updatedItem !== NULL AND $updatedItem->mapped('last_comment') < $this->last_post )
{
$updatedItem = null;
}
/* If we still have no item, load the latest topic */
if( $updatedItem === null )
{
try
{
/*
/*
* We prefer fetching post, joining topic, etc. but that is not efficient and causes a temp table and filesort against posts table so we'll lean on the cached last_post value for the topic
* We also need to fetch from the write server in the event that something has just been deleted and the comment hasn't been passed.
* We also cannot look for future entries since this code is used in older upgrades, but we can look for the date instead.
*/
$select = \IPS\Db::i()->select( '*', 'forums_topics', array( "forums_topics.forum_id=? AND forums_topics.approved=1 AND forums_topics.state != ? AND forums_topics.last_post<=? AND forums_topics.publish_date <=?", $this->id, 'link', time(), time() ), 'forums_topics.last_post DESC', 1, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->first();
$select = \IPS\Db::i()->select( '*', 'forums_topics', array( "forums_topics.forum_id=? AND forums_topics.approved=1 AND forums_topics.state != ? AND forums_topics.last_post<=? AND forums_topics.publish_date <=?", $this->id, 'link', time(), time() ), 'forums_topics.last_post DESC', 1 )->first();
$topic = \IPS\forums\Topic::constructFromData( $select );
if ( $topic->last_poster_id and ! $topic->last_poster_name )
{
$member = \IPS\Member::load( $topic->last_poster_id );
if ( $member->member_id )
{
$topic->last_poster_name = $member->name;
}
else
{
$topic->last_poster_name = '';
$topic->last_poster_id = 0;
}
}
$this->last_post = $topic->last_post;
$this->last_poster_id = (int) $topic->last_poster_id;
$this->last_poster_name = $topic->last_poster_name;
$this->seo_last_name = \IPS\Http\Url\Friendly::seoTitle( $this->last_poster_name );
$this->last_title = $topic->title;
$this->seo_last_title = \IPS\Http\Url\Friendly::seoTitle( $this->last_title );
$this->last_id = $topic->tid;
$this->last_poster_anon = $topic->last_poster_anon;
return;
$updatedItem = \IPS\forums\Topic::constructFromData( $select );
}
catch ( \UnderflowException $e )
catch( \UnderflowException $e )
{
/* At this point we have nothing, so clear the last post data */
$this->last_post = NULL;
$this->last_poster_id = 0;
$this->last_poster_name = '';
@@ -387,15 +377,29 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
return;
}
}
$this->last_post = $comment->mapped('date');
$this->last_poster_id = (int) $comment->author()->member_id;
$this->last_poster_name = $comment->author()->member_id ? $comment->author()->name : $comment->mapped('author_name');
if ( $updatedItem->last_poster_id and ! $updatedItem->last_poster_name )
{
$member = \IPS\Member::load( $updatedItem->last_poster_id );
if ( $member->member_id )
{
$updatedItem->last_poster_name = $member->name;
}
else
{
$updatedItem->last_poster_name = '';
$updatedItem->last_poster_id = 0;
}
}
$this->last_post = $updatedItem->last_post;
$this->last_poster_id = (int) $updatedItem->last_poster_id;
$this->last_poster_name = $updatedItem->last_poster_name;
$this->seo_last_name = \IPS\Http\Url\Friendly::seoTitle( $this->last_poster_name );
$this->last_title = $comment->item()->title;
$this->last_title = $updatedItem->title;
$this->seo_last_title = \IPS\Http\Url\Friendly::seoTitle( $this->last_title );
$this->last_id = $comment->item()->tid;
$this->last_poster_anon = $comment->isAnonymous();
$this->last_id = $updatedItem->tid;
$this->last_poster_anon = $updatedItem->last_poster_anon;
}
/**
@@ -1064,8 +1068,9 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'forum_qa_rate_answers', $this->id ? ( ( $this->qa_rate_answers == '*' or $this->qa_rate_answers === NULL ) ? '*' : explode( ',', $this->qa_rate_answers ) ) : '*', FALSE, array( 'options' => $groupsNoGuests, 'unlimited' => '*', 'multiple' => TRUE, 'impliedUnlimited' => TRUE ), NULL, NULL, NULL, 'forum_qa_rate_answers' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'forum_forum_allow_rating', $this->id ? $this->forum_allow_rating : FALSE, FALSE, array(), NULL, NULL, NULL, 'forum_allow_rating' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'forum_disable_sharelinks', $this->id ? !$this->disable_sharelinks : TRUE, FALSE, array(), NULL, NULL, NULL, 'forum_disable_sharelinks' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'bw_enable_answers_moderator', $this->id ? $this->forums_bitoptions['bw_enable_answers_moderator'] : FALSE, FALSE, array( 'togglesOn' => array( 'bw_enable_answers__member' ) ), NULL, NULL, NULL, 'forum_best_answer' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'bw_enable_answers_moderator', $this->id ? $this->forums_bitoptions['bw_enable_answers_moderator'] : FALSE, FALSE, array( 'togglesOn' => array( 'bw_enable_answers__member', 'solved_stats_from_cutoff' ) ), NULL, NULL, NULL, 'forum_best_answer' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'bw_enable_answers__member', $this->id ? $this->forums_bitoptions['bw_enable_answers_member'] : FALSE, FALSE, array(), NULL, NULL, NULL, 'bw_enable_answers__member' ) );
$form->add( new \IPS\Helpers\Form\Interval( 'solved_stats_from_cutoff', $this->id ? $this->solved_stats_from_cutoff : 0, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'unlimited' => 0, 'unlimitedLang' => 'alltime' ), NULL, NULL, NULL, 'solved_stats_from_cutoff' ) );
$form->addTab( 'posting_settings' );
$form->addHeader('posts');
@@ -1220,6 +1225,20 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
}
}
if ( isset( $values['solved_stats_from_cutoff'] ) )
{
/* If answers is disabled, set this to no. */
if ( !$values['forums_bitoptions']['bw_enable_answers_moderator'] )
{
$values['solved_stats_from_cutoff'] = 0;
}
else
{
/* Otherwise we need to reset the current time so it rebuilds */
$values['solved_stats_from'] = 0;
}
}
/* Remove forum_ prefix */
$_values = $values;
$values = array();
@@ -1633,7 +1652,7 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
/* If this is not a true mass move of contents of one container to another, then let parent handle it normally */
if( isset( $data['additional'] ) AND
( isset( $data['additional']['author'] ) OR ( isset( $data['additional']['no_comments'] ) AND $data['additional']['no_comments'] > 0 ) OR
( isset( $data['additional']['num_comments'] ) AND $data['additional']['num_comments'] > 0 ) OR isset( $data['additional']['state'] ) OR
( isset( $data['additional']['num_comments'] ) AND $data['additional']['num_comments'] >= 0 ) OR isset( $data['additional']['state'] ) OR
( isset( $data['additional']['pinned'] ) AND $data['additional']['pinned'] === TRUE ) OR ( isset( $data['additional']['featured'] ) AND $data['additional']['featured'] === TRUE ) OR ( isset( $data['additional']['last_post'] ) AND $data['additional']['last_post'] > 0 ) OR ( isset( $data['additional']['date'] ) AND $data['additional']['date'] > 0 ) )
)
{
@@ -1913,8 +1932,15 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
{
try
{
$where = [['forum_id=?', $this->_id]];
if ( $this->solved_stats_from_cutoff )
{
$cutoff = \IPS\DateTime::create()->sub( new \DateInterval( 'P' . $this->solved_stats_from_cutoff . 'D' ) )->getTimestamp();
$where[] = ['start_date>?', $cutoff];
}
$post = \IPS\Db::i()->select( '*', 'forums_posts', [
'pid IN (?)', \IPS\Db::i()->select( 'topic_answered_pid', 'forums_topics', ['forum_id=?', $this->_id] )
'pid IN (?)', \IPS\Db::i()->select( 'topic_answered_pid', 'forums_topics', $where )
], 'post_date ASC', 1 )->first();
try
@@ -2266,8 +2292,36 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
break;
case 'comment':
return (bool) ( \In_array( $this->preview_posts, array( 1, 3 ) ) );
return (bool) ( \in_array( $this->preview_posts, array( 1, 3 ) ) );
break;
}
}
}
/**
* Count all comments, items, etc
*
* @return void
*/
protected function recount()
{
$this->_items = (int) Db::i()->select( 'count(*)', 'forums_topics', [ 'forum_id=? and approved=? and state != ?', $this->_id, 1, 'link' ] )->first();
$this->_unapprovedItems = (int) Db::i()->select( 'count(*)', 'forums_topics', [ 'forum_id=? and approved = ? and state !=?', $this->_id, 0, 'link' ] )->first();
$this->_comments = (int) Db::i()->select( 'count(*)', 'forums_posts', [ 'queued=? and forum_id=? and approved=? and state != ?', 0, $this->_id, 1, 'link' ] )
->join( 'forums_topics', 'forums_posts.topic_id=forums_topics.tid' )
->first();
$this->_unapprovedComments = (int) Db::i()->select( 'count(*)', 'forums_posts', [ 'queued =? and forum_id=? and approved=? and state != ?', 1, $this->_id, 1, 'link' ] )
->join( 'forums_topics', 'forums_posts.topic_id=forums_topics.tid' )
->first();
/* Count archived posts */
if( Settings::i()->archive_on )
{
/* Here we use the sum from the topics table because once the topic is archived, these won't change */
$archiveStats = Db::i()->select( 'IFNULL( sum(posts), 0 ) as approved, IFNULL( sum(topic_queuedposts), 0 ) as unapproved', 'forums_topics', [ 'forum_id=? and approved=? and state != ? and topic_archive_status=?', $this->_id, 1, 'link', Topic::ARCHIVE_DONE ] )->first();
$this->_comments += $archiveStats['approved'];
$this->_unapprovedComments += $archiveStats['unapproved'];
}
$this->save();
}
}