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

+60 -27
View File
@@ -27,7 +27,6 @@ class _Topic extends \IPS\Content\Item implements
\IPS\Content\Tags,
\IPS\Content\Followable,
\IPS\Content\Shareable,
\IPS\Content\ReportCenter,
\IPS\Content\ReadMarkers,
\IPS\Content\Views,
\IPS\Content\Polls, \SplObserver,
@@ -36,6 +35,8 @@ class _Topic extends \IPS\Content\Item implements
\IPS\Content\Embeddable,
\IPS\Content\MetaData
{
use \IPS\Content\Reportable;
const ARCHIVE_NOT = 0;
const ARCHIVE_DONE = 1;
const ARCHIVE_WORKING = 2;
@@ -343,7 +344,7 @@ class _Topic extends \IPS\Content\Item implements
{
$joinContainer = TRUE;
$whereClause = array( '( forums_forums.can_view_others=1 OR forums_topics.starter_id=? )', $member->member_id );
$whereClause = array( '( forums_forums.can_view_others=1 OR forums_topics.starter_id=? )', (int) $member->member_id );
if ( $member->modPermission( 'can_read_all_topics' ) )
{
@@ -364,7 +365,7 @@ class _Topic extends \IPS\Content\Item implements
{
$member = $member ?: \IPS\Member::loggedIn();
$joinContainer = TRUE;
$where['container'][] = array( 'forums_forums.min_posts_view<=?', \IPS\Member::loggedIn()->member_posts );
$where['container'][] = array( 'forums_forums.min_posts_view<=?', $member->member_posts );
}
/* Return */
@@ -689,7 +690,10 @@ class _Topic extends \IPS\Content\Item implements
{
$popularNowInterval = new \DateInterval( 'PT' . $popularNowSettings['minutes'] . 'M' );
$comments = $this->comments( NULL, NULL, 'date', 'desc', NULL, FALSE, \IPS\DateTime::create()->sub( $popularNowInterval ) );
$comments = iterator_to_array( new \IPS\Patterns\ActiveRecordIterator(
\IPS\Db::i()->select( '*', 'forums_posts', array( 'queued IN(0,2) AND post_date >? AND topic_id=?', \IPS\DateTime::create()->sub( $popularNowInterval )->getTimestamp(), $this->tid ), 'post_date DESC', NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER ),
'IPS\\forums\\Topic\\Post'
) );
if ( count( $comments ) >= $popularNowSettings['posts'] )
{
$commentToBasePopularNowTimeOff = array_slice( $comments, ( $popularNowSettings['posts'] - 1 ), 1 );
@@ -866,9 +870,9 @@ class _Topic extends \IPS\Content\Item implements
*/
public static function contentTableTemplate()
{
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'forums.css', 'forums' ) );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'forums_responsive.css', 'forums' ) );
return array( \IPS\Theme::i()->getTemplate( 'global', 'forums' ), 'rows' );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'forums.css', 'forums', 'front' ) );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'forums_responsive.css', 'forums', 'front' ) );
return array( \IPS\Theme::i()->getTemplate( 'global', 'forums', 'front' ), 'rows' );
}
/**
@@ -922,7 +926,7 @@ class _Topic extends \IPS\Content\Item implements
{
if ( $container and ( $container->preview_posts == 1 or $container->preview_posts == 2 ) and !$member->group['g_avoid_q'] )
{
return TRUE;
return !static::modPermission( 'approve', $member, $container );
}
return parent::moderateNewItems( $member, $container );
@@ -1325,7 +1329,7 @@ class _Topic extends \IPS\Content\Item implements
}
/* Have we already voted? */
if ( $rating !== 0 or !\IPS\Settings::i()->forums_answers_downvote )
if ( $rating !== 0 or !\IPS\Settings::i()->forums_questions_downvote )
{
$ratings = $this->votes();
if ( isset( $ratings[ $member->member_id ] ) and $ratings[ $member->member_id ] === $rating )
@@ -1367,6 +1371,29 @@ class _Topic extends \IPS\Content\Item implements
return $this->votes;
}
/**
* Clear Votes Cache
*
* @return void
* @note This is necessary so that when voting on a question or answer, the cached votes ($votes and $answerVotes) are reloaded properly.
*/
public function clearVotes()
{
$this->votes = NULL;
$this->answerVotes = array();
}
/**
* [ActiveRecord] Save
*
* @return void
*/
public function save()
{
parent::save();
$this->clearVotes();
}
/**
* Indefinite Article
*
@@ -1391,21 +1418,23 @@ class _Topic extends \IPS\Content\Item implements
/**
* Definite Article
*
* @param \IPS\Lang|NULL $language The language to use, or NULL for the language of the currently logged in member
* @param array $containerData Basic data about the container. Only includes columns returned by container::basicDataColumns()
* @param \IPS\Lang|NULL $language The language to use, or NULL for the language of the currently logged in member
* @param array $options Options to pass to \IPS\Lang::addToStack
* @return string
*/
public static function _definiteArticle( array $containerData = NULL, \IPS\Lang $lang = NULL )
public static function _definiteArticle( array $containerData = NULL, \IPS\Lang $lang = NULL, $options = array() )
{
$bitOptions = ( $containerData['forums_bitoptions'] instanceof \IPS\Patterns\Bitwise ) ? $containerData['forums_bitoptions'] : new \IPS\Patterns\Bitwise( array( 'forums_bitoptions' => $containerData['forums_bitoptions'] ), \IPS\forums\Forum::$bitOptions['forums_bitoptions'] );
if ( $bitOptions['bw_enable_answers'] )
{
$lang = $lang ?: \IPS\Member::loggedIn()->language();
return $lang->addToStack( '__defart_question', FALSE );
return $lang->addToStack( '__defart_question', FALSE, $options );
}
else
{
return parent::_definiteArticle( $containerData, $lang );
return parent::_definiteArticle( $containerData, $lang, $options );
}
}
@@ -1492,10 +1521,12 @@ class _Topic extends \IPS\Content\Item implements
$time = \IPS\DateTime::ts( $taskData['next_run'] );
$postsToBeUnarchived = \IPS\Db::i()->select( 'SUM(posts) + COUNT(*)', 'forums_topics', array( 'topic_archive_status=?', static::ARCHIVE_RESTORE ) )->first();
if ( $postsToBeUnarchived )
if ( $postsToBeUnarchived AND $postsToBeUnarchived > \IPS\forums\tasks\unarchive::PROCESS_PER_BATCH )
{
$total = $postsToBeUnarchived / \IPS\forums\tasks\unarchive::PROCESS_PER_BATCH;
$interval = new \DateInterval( $taskData['frequency'] );
foreach ( range( 1, $postsToBeUnarchived ) as $i )
foreach ( range( 1, $total ) as $i )
{
$time->add( $interval );
}
@@ -1728,9 +1759,10 @@ class _Topic extends \IPS\Content\Item implements
* @param mixed $extraWhereClause Additional where clause(s) (see \IPS\Db::build for details)
* @param bool|NULL $bypassCache Used in cases where comments may have already been loaded i.e. splitting comments on an item.
* @param bool $includeDeleted Include Deleted Content
* @param bool|NULL $canViewWarn TRUE to include Warning information, NULL to determine automatically based on moderator permissions.
* @return array|NULL|\IPS\Content\Comment If $limit is 1, will return \IPS\Content\Comment or NULL for no results. For any other number, will return an array.
*/
public function comments( $limit=NULL, $offset=NULL, $order='date', $orderDirection='asc', $member=NULL, $includeHiddenComments=NULL, $cutoff=NULL, $extraWhereClause=NULL, $bypassCache=FALSE, $includeDeleted=FALSE )
public function comments( $limit=NULL, $offset=NULL, $order='date', $orderDirection='asc', $member=NULL, $includeHiddenComments=NULL, $cutoff=NULL, $extraWhereClause=NULL, $bypassCache=FALSE, $includeDeleted=FALSE, $canViewWarn=NULL )
{
static $comments = array();
$idField = static::$databaseColumnId;
@@ -1741,7 +1773,7 @@ class _Topic extends \IPS\Content\Item implements
return $comments[ $_hash ];
}
$includeWarnings = NULL;
$includeWarnings = $canViewWarn;
$commentClass = NULL;
if ( $this->isArchived() )
@@ -1855,19 +1887,19 @@ class _Topic extends \IPS\Content\Item implements
/**
* Returns the meta description
*
* @param string|NULL $return Specific description to use (useful for paginated displays to prevent having to run extra queries)
* @return string
* @throws \BadMethodCallException
*/
public function metaDescription()
public function metaDescription( $return = NULL )
{
/* We have to store and restore this for the topic controller */
if( isset( $_SESSION['_findComment'] ) )
{
$_findComment = $_SESSION['_findComment'];
}
$metaDescription = parent::metaDescription();
$metaDescription = parent::metaDescription( $return );
if( isset( $_findComment ) )
{
@@ -1880,12 +1912,13 @@ class _Topic extends \IPS\Content\Item implements
/**
* Get output for API
*
* @param \IPS\Member|NULL $authorizedMember The member making the API request or NULL for API Key / client_credentials
* @return array
* @apiresponse int id ID number
* @apiresponse string title Title
* @apiresponse \IPS\forums\Forum forum Forum
* @apiresponse int posts Number of posts
* @apiresponse int views Number of posts
* @apiresponse int views Number of views
* @apiresponse string prefix The prefix tag, if there is one
* @apiresponse [string] tags The tags
* @apiresponse \IPS\forums\Topic\Post firstPost The first post in the topic
@@ -1900,7 +1933,7 @@ class _Topic extends \IPS\Content\Item implements
* @apiresponse string url URL
* @apiresponse float rating Average Rating
*/
public function apiOutput()
public function apiOutput( \IPS\Member $authorizedMember = NULL )
{
$firstPost = $this->comments( 1, 0, 'date', 'asc' );
$lastPost = $this->comments( 1, 0, 'date', 'desc' );
@@ -1908,20 +1941,20 @@ class _Topic extends \IPS\Content\Item implements
return array(
'id' => $this->tid,
'title' => $this->title,
'forum' => $this->container()->apiOutput(),
'forum' => $this->container()->apiOutput( $authorizedMember ),
'posts' => $this->posts,
'views' => $this->views,
'prefix' => $this->prefix(),
'tags' => $this->tags(),
'firstPost' => $firstPost ? $firstPost->apiOutput() : null,
'lastPost' => $lastPost ? $lastPost->apiOutput() : null,
'bestAnswer' => $bestAnswer ? $bestAnswer->apiOutput() : null,
'firstPost' => $firstPost ? $firstPost->apiOutput( $authorizedMember ) : null,
'lastPost' => $lastPost ? $lastPost->apiOutput( $authorizedMember ) : null,
'bestAnswer' => $bestAnswer ? $bestAnswer->apiOutput( $authorizedMember ) : null,
'locked' => (bool) $this->locked(),
'hidden' => (bool) $this->hidden(),
'pinned' => (bool) $this->mapped('pinned'),
'featured' => (bool) $this->mapped('featured'),
'archived' => (bool) $this->isArchived(),
'poll' => $this->poll_state ? \IPS\Poll::load( $this->poll_state )->apiOutput() : null,
'poll' => $this->poll_state ? \IPS\Poll::load( $this->poll_state )->apiOutput( $authorizedMember ) : null,
'url' => (string) $this->url(),
'rating' => $this->averageRating(),
);