Version 4.5.0
This commit is contained in:
1 parent
1a0c7fd3c2
commit
f79dcf067a
3791 files changed
+248032
-76372
No files matched your search
@@ -12,7 +12,7 @@
|
||||
namespace IPS\forums\Topic;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
@@ -35,19 +35,28 @@ class _ArchivedPost extends Post
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Connection
|
||||
* @return \IPS\Db
|
||||
*/
|
||||
public static function db()
|
||||
{
|
||||
return ( !\IPS\Settings::i()->archive_remote_sql_host ) ? \IPS\Db::i() : \IPS\Db::i( 'archive', array(
|
||||
'sql_host' => \IPS\Settings::i()->archive_remote_sql_host,
|
||||
'sql_user' => \IPS\Settings::i()->archive_remote_sql_user,
|
||||
'sql_pass' => \IPS\Settings::i()->archive_remote_sql_pass,
|
||||
'sql_database' => \IPS\Settings::i()->archive_remote_sql_database,
|
||||
'sql_port' => \IPS\Settings::i()->archive_sql_port,
|
||||
'sql_socket' => \IPS\Settings::i()->archive_sql_socket,
|
||||
'sql_tbl_prefix'=> \IPS\Settings::i()->archive_sql_tbl_prefix,
|
||||
'sql_utf8mb4' => isset( \IPS\Settings::i()->sql_utf8mb4 ) ? \IPS\Settings::i()->sql_utf8mb4 : FALSE
|
||||
) );
|
||||
/* Connect to the remote DB if needed */
|
||||
if ( \IPS\CIC2 )
|
||||
{
|
||||
return \IPS\Cicloud\getForumArchiveDb();
|
||||
}
|
||||
else
|
||||
{
|
||||
return ( !\IPS\Settings::i()->archive_remote_sql_host ) ? \IPS\Db::i() : \IPS\Db::i( 'archive', array(
|
||||
'sql_host' => \IPS\Settings::i()->archive_remote_sql_host,
|
||||
'sql_user' => \IPS\Settings::i()->archive_remote_sql_user,
|
||||
'sql_pass' => \IPS\Settings::i()->archive_remote_sql_pass,
|
||||
'sql_database' => \IPS\Settings::i()->archive_remote_sql_database,
|
||||
'sql_port' => \IPS\Settings::i()->archive_sql_port,
|
||||
'sql_socket' => \IPS\Settings::i()->archive_sql_socket,
|
||||
'sql_tbl_prefix'=> \IPS\Settings::i()->archive_sql_tbl_prefix,
|
||||
'sql_utf8mb4' => isset( \IPS\Settings::i()->sql_utf8mb4 ) ? \IPS\Settings::i()->sql_utf8mb4 : FALSE
|
||||
) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,7 +116,7 @@ class _ArchivedPost extends Post
|
||||
array( \IPS\Db::i()->in( 'archive_forum_id', iterator_to_array( \IPS\Db::i()->select( 'id', 'forums_forums', 'inc_postcount=0' ) ), TRUE ) )
|
||||
) )->first();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Joins (when loading comments)
|
||||
*
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
namespace IPS\forums\Topic;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
@@ -125,7 +125,7 @@ class _Post extends \IPS\Content\Comment implements \IPS\Content\EditHistory, \I
|
||||
*/
|
||||
public static function create( $item, $comment, $first=FALSE, $guestName=NULL, $incrementPostCount=NULL, $member=NULL, \IPS\DateTime $time=NULL, $ipAddress=NULL, $hiddenStatus=NULL )
|
||||
{
|
||||
$comment = call_user_func_array( 'parent::create', func_get_args() );
|
||||
$comment = parent::create( $item, $comment, $first, $guestName, $incrementPostCount, $member, $time, $ipAddress, $hiddenStatus );
|
||||
|
||||
if ( !$comment->hidden() )
|
||||
{
|
||||
@@ -165,7 +165,7 @@ class _Post extends \IPS\Content\Comment implements \IPS\Content\EditHistory, \I
|
||||
*
|
||||
* @param \IPS\Node\Model|NULL $container Container
|
||||
* @return void
|
||||
* @see \IPS\forums\Topic\Post::incrementPostCount()
|
||||
* @see incrementPostCount
|
||||
*/
|
||||
public static function incrementPostCount( \IPS\Node\Model $container = NULL )
|
||||
{
|
||||
@@ -186,6 +186,18 @@ class _Post extends \IPS\Content\Comment implements \IPS\Content\EditHistory, \I
|
||||
\IPS\Db::i()->select( 'id', 'forums_forums', 'inc_postcount=1' )
|
||||
) )->join( 'forums_topics', 'tid=topic_id' )->first() ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Post count for member
|
||||
*
|
||||
* @param \IPS\Member $member The memner
|
||||
* @return int
|
||||
*/
|
||||
public static function rawMemberPostCount( \IPS\Member $member )
|
||||
{
|
||||
/* Explicitly calling to the parent so we don't filter by forum ID */
|
||||
return parent::memberPostCount( $member );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get items with permission check
|
||||
@@ -200,7 +212,9 @@ class _Post extends \IPS\Content\Comment implements \IPS\Content\EditHistory, \I
|
||||
* @param bool $joinContainer If true, will join container data (set to TRUE if your $where clause depends on this data)
|
||||
* @param bool $joinComments If true, will join comment data (set to TRUE if your $where clause depends on this data)
|
||||
* @param bool $joinReviews If true, will join review data (set to TRUE if your $where clause depends on this data)
|
||||
* @return \IPS\Patterns\ActiveRecordIterator|int
|
||||
* @param bool $countOnly If true will return the count
|
||||
* @param array|null $joins Additional arbitrary joins for the query
|
||||
* @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 static function getItemsWithPermission( $where=array(), $order=NULL, $limit=10, $permissionKey='read', $includeHiddenItems=\IPS\Content\Hideable::FILTER_AUTOMATIC, $queryFlags=0, \IPS\Member $member=NULL, $joinContainer=FALSE, $joinComments=FALSE, $joinReviews=FALSE, $countOnly=FALSE, $joins=NULL )
|
||||
{
|
||||
@@ -246,7 +260,7 @@ class _Post extends \IPS\Content\Comment implements \IPS\Content\EditHistory, \I
|
||||
}
|
||||
|
||||
/* Minimum posts */
|
||||
elseif ( $containerData['min_posts_view'] and $containerData['min_posts_view'] >= \IPS\Member::loggedIn()->member_posts )
|
||||
elseif ( $containerData['min_posts_view'] and $containerData['min_posts_view'] > \IPS\Member::loggedIn()->member_posts )
|
||||
{
|
||||
return \IPS\Theme::i()->getTemplate( 'global', 'forums' )->searchNoPermission( \IPS\Member::loggedIn()->language()->addToStack( 'no_perm_post_min_posts', FALSE, array( 'pluralize' => array( $containerData['min_posts_view'] ) ) ) );
|
||||
}
|
||||
@@ -282,7 +296,7 @@ class _Post extends \IPS\Content\Comment implements \IPS\Content\EditHistory, \I
|
||||
public function canVote( $rating=0, $member=NULL )
|
||||
{
|
||||
/* Is $rating valid */
|
||||
if ( !in_array( $rating, array( -1, 0, 1 ) ) )
|
||||
if ( !\in_array( $rating, array( -1, 0, 1 ) ) )
|
||||
{
|
||||
throw new \InvalidArgumentException;
|
||||
}
|
||||
@@ -311,17 +325,7 @@ class _Post extends \IPS\Content\Comment implements \IPS\Content\EditHistory, \I
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Have we already voted? */
|
||||
if ( $rating !== 0 or !\IPS\Settings::i()->forums_answers_downvote )
|
||||
{
|
||||
$ratings = $this->item()->answerVotes( $member );
|
||||
if ( isset( $ratings[ $this->pid ] ) and $ratings[ $this->pid ] === $rating )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -353,13 +357,6 @@ class _Post extends \IPS\Content\Comment implements \IPS\Content\EditHistory, \I
|
||||
$item = NULL;
|
||||
}
|
||||
|
||||
/* Reset best answer if relevant */
|
||||
if ( $item !== NULL AND $item->topic_answered_pid == $this->pid )
|
||||
{
|
||||
$item->topic_answered_pid = FALSE;
|
||||
$item->save();
|
||||
}
|
||||
|
||||
parent::delete();
|
||||
|
||||
/* Deleting a post may make the item no longer popular */
|
||||
@@ -369,10 +366,23 @@ class _Post extends \IPS\Content\Comment implements \IPS\Content\EditHistory, \I
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the snippet (stripped of HTML)
|
||||
*
|
||||
* @param int $length The length of the snippet to return
|
||||
* @return string
|
||||
*/
|
||||
public function snippet( $length=300 )
|
||||
{
|
||||
$contentField = static::$databaseColumnMap['content'];
|
||||
$content = trim( str_replace( \chr(0xC2) . \chr(0xA0), ' ', strip_tags( preg_replace( "/(<br(?: \/)?>|<\/p>)/i", ' ', preg_replace( "#<blockquote(?:[^>]+?)>.+?(?<!<blockquote)</blockquote>#s", " ", preg_replace( "#<script(.*?)>(.*)</script>#uis", "", ' ' . $this->$contentField . ' ' ) ) ) ) ) );
|
||||
return mb_substr( $content, 0, $length ) . ( mb_strlen( $content ) > $length ? '…' : '' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Indefinite Article
|
||||
*
|
||||
* @param \IPS\Lang|NULL $language The language to use, or NULL for the language of the currently logged in member
|
||||
* @param \IPS\Lang|NULL $lang The language to use, or NULL for the language of the currently logged in member
|
||||
* @return string
|
||||
*/
|
||||
public function indefiniteArticle( \IPS\Lang $lang = NULL )
|
||||
@@ -411,8 +421,8 @@ class _Post extends \IPS\Content\Comment implements \IPS\Content\EditHistory, \I
|
||||
/**
|
||||
* Definite Article
|
||||
*
|
||||
* @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|NULL $containerData Basic data about the container. Only includes columns returned by container::basicDataColumns()
|
||||
* @param \IPS\Lang|NULL $lang 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
|
||||
*/
|
||||
@@ -468,6 +478,17 @@ class _Post extends \IPS\Content\Comment implements \IPS\Content\EditHistory, \I
|
||||
return 'pid';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this comment as solved
|
||||
*
|
||||
* @param boolean $value TRUE/FALSE value
|
||||
* @return void
|
||||
*/
|
||||
public function setSolved( $value )
|
||||
{
|
||||
$this->post_bwoptions['best_answer'] = (bool) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get content for embed
|
||||
*
|
||||
@@ -481,34 +502,30 @@ class _Post extends \IPS\Content\Comment implements \IPS\Content\EditHistory, \I
|
||||
}
|
||||
|
||||
/**
|
||||
* Check Moderator Permission
|
||||
* Create a query to fetch the "top members"
|
||||
*
|
||||
* @param string $type 'edit', 'hide', 'unhide', 'delete', etc.
|
||||
* @param \IPS\Member|NULL $member The member to check for or NULL for the currently logged in member
|
||||
* @param \IPS\Node\Model|NULL $container The container
|
||||
* @return bool
|
||||
* @note The intention is to formulate a query that will fetch the members with the most contributions
|
||||
* @note The group by confuses MySQL and it ends up using the 'queued' index, when the 'author_id' index is far better
|
||||
* @param int $limit The number of members to return
|
||||
* @return \IPS\Db\Select
|
||||
*/
|
||||
public static function modPermission( $type, \IPS\Member $member = NULL, \IPS\Node\Model $container = NULL )
|
||||
public static function topMembersQuery( $limit )
|
||||
{
|
||||
/* If this is called from a gateway script, i.e. email piping, just return false as we are a "guest" */
|
||||
if( $member === NULL AND !\IPS\Dispatcher::hasInstance() )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ( \in_array( $type, array( 'edit', 'hide', 'unhide', 'delete' ) ) )
|
||||
{
|
||||
/* Load Member */
|
||||
$member = $member ?: \IPS\Member::loggedIn();
|
||||
|
||||
/* Post related permission */
|
||||
if ( $member->modPermission( "can_{$type}_post" ) )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return parent::modPermission($type, $member, $container );
|
||||
$query = parent::topMembersQuery( $limit );
|
||||
|
||||
return $query->forceIndex( 'author_id' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Webhook filters
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function webhookFilters()
|
||||
{
|
||||
$return = parent::webhookFilters();
|
||||
$return['forums'] = $this->item()->container()->_id;
|
||||
$return['hasBestAnswer'] = (bool) $this->item()->topic_answered_pid;
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
namespace IPS\forums;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
@@ -35,12 +35,31 @@ class _Topic extends \IPS\Content\Item implements
|
||||
\IPS\Content\Embeddable,
|
||||
\IPS\Content\MetaData
|
||||
{
|
||||
use \IPS\Content\Reportable;
|
||||
use \IPS\Content\Reactable, \IPS\Content\Reportable, \IPS\Content\Statistics, \IPS\Content\Solvable;
|
||||
|
||||
/**
|
||||
* @brief Not archived
|
||||
*/
|
||||
const ARCHIVE_NOT = 0;
|
||||
|
||||
/**
|
||||
* @brief Archiving completed
|
||||
*/
|
||||
const ARCHIVE_DONE = 1;
|
||||
|
||||
/**
|
||||
* @brief In the process of being archived
|
||||
*/
|
||||
const ARCHIVE_WORKING = 2;
|
||||
|
||||
/**
|
||||
* @brief Excluded from archiving
|
||||
*/
|
||||
const ARCHIVE_EXCLUDE = 3;
|
||||
|
||||
/**
|
||||
* @brief Flagged to restore from archive
|
||||
*/
|
||||
const ARCHIVE_RESTORE = 4;
|
||||
|
||||
/**
|
||||
@@ -86,7 +105,7 @@ class _Topic extends \IPS\Content\Item implements
|
||||
'unapproved_comments' => 'topic_queuedposts',
|
||||
'hidden_comments' => 'topic_hiddenposts',
|
||||
'first_comment_id' => 'topic_firstpost',
|
||||
'last_comment' => array( 'last_real_post', 'last_post' ),
|
||||
'last_comment' => array( 'last_post', 'last_real_post' ),
|
||||
'last_comment_by' => 'last_poster_id',
|
||||
'last_comment_name' => 'last_poster_name',
|
||||
'views' => 'views',
|
||||
@@ -99,7 +118,8 @@ class _Topic extends \IPS\Content\Item implements
|
||||
'featured' => 'featured',
|
||||
'state' => 'state',
|
||||
'updated' => 'last_post',
|
||||
'meta_data' => 'topic_meta_data'
|
||||
'meta_data' => 'topic_meta_data',
|
||||
'solved_comment_id' => 'topic_answered_pid'
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -146,6 +166,11 @@ class _Topic extends \IPS\Content\Item implements
|
||||
* @brief Hover preview
|
||||
*/
|
||||
public $tableHoverUrl = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Setting name for show_meta
|
||||
*/
|
||||
public static $showMetaSettingKey = 'forums_topics_show_meta';
|
||||
|
||||
/**
|
||||
* Callback from \IPS\Http\Url\Inernal::correctUrlFromVerifyClass()
|
||||
@@ -190,14 +215,14 @@ class _Topic extends \IPS\Content\Item implements
|
||||
}
|
||||
|
||||
/* Did we have any? */
|
||||
if( count( $paramsToSet ) )
|
||||
if( \count( $paramsToSet ) )
|
||||
{
|
||||
if( $url === NULL )
|
||||
{
|
||||
$url = $this->url();
|
||||
}
|
||||
|
||||
if( count( $paramsToUnset ) )
|
||||
if( \count( $paramsToUnset ) )
|
||||
{
|
||||
$url = $url->stripQueryString( $paramsToUnset );
|
||||
}
|
||||
@@ -303,14 +328,14 @@ class _Topic extends \IPS\Content\Item implements
|
||||
public static function getItemsWithPermissionWhere( $where, $permissionKey, $member, &$joinContainer, $skipPermission=FALSE )
|
||||
{
|
||||
/* Don't show topics in password protected forums */
|
||||
if ( !$skipPermission and in_array( $permissionKey, array( 'view', 'read' ) ) )
|
||||
if ( !$skipPermission and \in_array( $permissionKey, array( 'view', 'read' ) ) )
|
||||
{
|
||||
$joinContainer = TRUE;
|
||||
$member = $member ?: \IPS\Member::loggedIn();
|
||||
|
||||
$whereString = 'forums_forums.password=? OR ' . \IPS\Db::i()->findInSet( 'forums_forums.password_override', $member->groups );
|
||||
$whereParams = array( NULL );
|
||||
if ( \IPS\Dispatcher::hasInstance() AND $member === \IPS\Member::loggedIn() )
|
||||
if ( \IPS\Dispatcher::hasInstance() AND $member->member_id === \IPS\Member::loggedIn()->member_id )
|
||||
{
|
||||
foreach ( \IPS\Request::i()->cookie as $k => $v )
|
||||
{
|
||||
@@ -325,43 +350,82 @@ class _Topic extends \IPS\Content\Item implements
|
||||
|
||||
$where['container'][] = array_merge( array( '( ' . $whereString . ' )' ), $whereParams );
|
||||
}
|
||||
|
||||
|
||||
/* Don't show topics from forums in which topics only show to the poster */
|
||||
if ( $skipPermission !== TRUE and in_array( $permissionKey, array( 'view', 'read' ) ) )
|
||||
if ( $skipPermission !== TRUE and \in_array( $permissionKey, array( 'view', 'read' ) ) )
|
||||
{
|
||||
$member = $member ?: \IPS\Member::loggedIn();
|
||||
|
||||
if ( !$member->modPermission( 'can_read_all_topics' ) or ( $member->modPermission( 'forums' ) !== TRUE and $member->modPermission( 'forums' ) !== -1 ) )
|
||||
if ( $skipPermission instanceof \IPS\forums\Forum )
|
||||
{
|
||||
if ( $skipPermission instanceof \IPS\forums\Forum )
|
||||
if ( !$skipPermission->can_view_others )
|
||||
{
|
||||
if ( !$skipPermission->can_view_others )
|
||||
if ( !$member->member_id )
|
||||
{
|
||||
return array( '1=0' );
|
||||
}
|
||||
if ( $club = $skipPermission->club() )
|
||||
{
|
||||
if ( !$club->isModerator( $member ) )
|
||||
{
|
||||
$where['item'][] = array( 'forums_topics.starter_id=?', $member->member_id );
|
||||
}
|
||||
}
|
||||
elseif ( !$member->modPermission('can_read_all_topics') or ( $member->modPermission('forums') !== TRUE and !\in_array( $skipPermission->_id, $member->modPermission('forums') ) ) )
|
||||
{
|
||||
$where['item'][] = array( 'forums_topics.starter_id=?', $member->member_id );
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif ( !$member->modPermission('can_read_all_topics') or \is_array( $member->modPermission('forums') ) or !$member->modPermission('can_access_all_clubs') )
|
||||
{
|
||||
$joinContainer = TRUE;
|
||||
|
||||
if ( !$member->member_id )
|
||||
{
|
||||
$where[] = array( 'forums_forums.can_view_others=1' );
|
||||
}
|
||||
else
|
||||
{
|
||||
$joinContainer = TRUE;
|
||||
$whereClause = array( 'forums_forums.can_view_others=1 OR forums_topics.starter_id=?', (int) $member->member_id );
|
||||
$ors = array();
|
||||
|
||||
$whereClause = array( '( forums_forums.can_view_others=1 OR forums_topics.starter_id=? )', (int) $member->member_id );
|
||||
|
||||
if ( $member->modPermission( 'can_read_all_topics' ) )
|
||||
if ( $member->modPermission('can_read_all_topics') )
|
||||
{
|
||||
$forums = $member->modPermission( 'forums' );
|
||||
if ( isset( $forums ) and is_array ( $forums ) )
|
||||
$forums = $member->modPermission('forums');
|
||||
if ( isset( $forums ) and \is_array( $forums ) )
|
||||
{
|
||||
$whereClause[0] = "( {$whereClause[0]} OR " . \IPS\Db::i()->in( 'forums_topics.forum_id', $forums ) . ' )';
|
||||
$ors[] = '( forums_forums.club_id IS NULL AND ' . \IPS\Db::i()->in( 'forums_topics.forum_id', $forums ) . ')';
|
||||
}
|
||||
else
|
||||
{
|
||||
$ors[] = 'forums_forums.club_id IS NULL';
|
||||
}
|
||||
}
|
||||
if ( $member->modPermission('can_access_all_clubs') )
|
||||
{
|
||||
$ors[] = 'forums_forums.club_id IS NOT NULL';
|
||||
}
|
||||
elseif ( $moderatorClubIds = $member->clubs( FALSE, TRUE ) )
|
||||
{
|
||||
$ors[] = \IPS\Db::i()->in( 'forums_forums.club_id', $moderatorClubIds );
|
||||
}
|
||||
|
||||
if ( $ors )
|
||||
{
|
||||
$whereClause[0] = "( {$whereClause[0]} OR " . implode( ' OR ', $ors ) . ' )';
|
||||
}
|
||||
else
|
||||
{
|
||||
$whereClause[0] = "( {$whereClause[0]} )";
|
||||
}
|
||||
|
||||
$where[] = $whereClause;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't show topics in forums we can't view because our post count is too low */
|
||||
if ( !$skipPermission and in_array( $permissionKey, array( 'view', 'read' ) ) )
|
||||
if ( !$skipPermission and \in_array( $permissionKey, array( 'view', 'read' ) ) )
|
||||
{
|
||||
$member = $member ?: \IPS\Member::loggedIn();
|
||||
$joinContainer = TRUE;
|
||||
@@ -373,7 +437,7 @@ class _Topic extends \IPS\Content\Item implements
|
||||
}
|
||||
|
||||
/**
|
||||
* Total item count (including children)
|
||||
* Total item \count(including children)
|
||||
*
|
||||
* @param \IPS\Node\Model $container The container
|
||||
* @param bool $includeItems If TRUE, items will be included (this should usually be true)
|
||||
@@ -436,8 +500,8 @@ class _Topic extends \IPS\Content\Item implements
|
||||
$current[] = 'pin';
|
||||
}
|
||||
}
|
||||
|
||||
if ( static::modPermission( 'hide', NULL, $container ) )
|
||||
$canHide = ( $item ) ? $item->canHide() : ( \IPS\Member::loggedIn()->group['g_hide_own_posts'] == '1' or \in_array( 'IPS\forums\Topics', explode( ',', \IPS\Member::loggedIn()->group['g_hide_own_posts'] ) ) );
|
||||
if ( static::modPermission( 'hide', NULL, $container ) or $canHide )
|
||||
{
|
||||
$options['hide'] = 'create_topic_hidden';
|
||||
$toggles['hide'] = array( 'create_topic_hidden' );
|
||||
@@ -457,7 +521,7 @@ class _Topic extends \IPS\Content\Item implements
|
||||
}
|
||||
}
|
||||
|
||||
if ( count( $options ) or count ( $toggles ) )
|
||||
if ( \count( $options ) or \count( $toggles ) )
|
||||
{
|
||||
$formElements['topic_state'] = new \IPS\Helpers\Form\CheckboxSet( 'topic_create_state', $current, FALSE, array(
|
||||
'options' => $options,
|
||||
@@ -514,17 +578,17 @@ class _Topic extends \IPS\Content\Item implements
|
||||
{
|
||||
if ( static::modPermission( 'lock', NULL, $this->container() ) )
|
||||
{
|
||||
$this->state = ( in_array( 'lock', $values['topic_create_state'] ) ) ? 'closed' : 'open';
|
||||
$this->state = ( \in_array( 'lock', $values['topic_create_state'] ) ) ? 'closed' : 'open';
|
||||
}
|
||||
|
||||
if ( static::modPermission( 'pin', NULL, $this->container() ) )
|
||||
{
|
||||
$this->pinned = ( in_array( 'pin', $values['topic_create_state'] ) ) ? 1 : 0;
|
||||
$this->pinned = ( \in_array( 'pin', $values['topic_create_state'] ) ) ? 1 : 0;
|
||||
}
|
||||
|
||||
if ( static::modPermission( 'feature', NULL, $this->container() ) )
|
||||
{
|
||||
$this->featured = ( in_array( 'feature', $values['topic_create_state'] ) ) ? 1 : 0;
|
||||
$this->featured = ( \in_array( 'feature', $values['topic_create_state'] ) ) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -534,7 +598,7 @@ class _Topic extends \IPS\Content\Item implements
|
||||
$this->topic_open_time = !empty( $values['topic_open_time'] ) ? $values['topic_open_time']->getTimestamp() : 0;
|
||||
$this->topic_close_time = !empty( $values['topic_close_time'] ) ? $values['topic_close_time']->getTimestamp() : 0;
|
||||
|
||||
if( isset( $values['topic_create_state'] ) and !in_array( 'lock', $values['topic_create_state'] ) )
|
||||
if( isset( $values['topic_create_state'] ) and !\in_array( 'lock', $values['topic_create_state'] ) )
|
||||
{
|
||||
$this->state = 'open';
|
||||
}
|
||||
@@ -544,6 +608,12 @@ class _Topic extends \IPS\Content\Item implements
|
||||
{
|
||||
$this->state = 'closed';
|
||||
}
|
||||
|
||||
/* If we specified an unlock time, but no lock time, make sure the topic is locked */
|
||||
if ( $this->topic_open_time and !$this->topic_close_time )
|
||||
{
|
||||
$this->state = 'closed';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -604,9 +674,9 @@ class _Topic extends \IPS\Content\Item implements
|
||||
/* Moderator actions */
|
||||
if ( isset( $values['topic_create_state'] ) )
|
||||
{
|
||||
if ( static::modPermission( 'hide', NULL, $this->container() ) )
|
||||
if ( $this->canHide() )
|
||||
{
|
||||
if( in_array( 'hide', $values['topic_create_state'] ) )
|
||||
if( \in_array( 'hide', $values['topic_create_state'] ) )
|
||||
{
|
||||
$this->hide( NULL );
|
||||
}
|
||||
@@ -630,7 +700,7 @@ class _Topic extends \IPS\Content\Item implements
|
||||
public static $urlBase = 'app=forums&module=forums&controller=topic&id=';
|
||||
|
||||
/**
|
||||
* @brief URL Base
|
||||
* @brief URL Template
|
||||
*/
|
||||
public static $urlTemplate = 'forums_topic';
|
||||
|
||||
@@ -642,7 +712,7 @@ class _Topic extends \IPS\Content\Item implements
|
||||
/**
|
||||
* Stats for table view
|
||||
*
|
||||
* @param bool $includeFirstCommentInCommentCount Determines whether the first comment should be inlcluded in the comment count (e.g. For "posts", use TRUE. For "replies", use FALSE)
|
||||
* @param bool $includeFirstCommentInCommentCount Determines whether the first comment should be inlcluded in the comment \count(e.g. For "posts", use TRUE. For "replies", use FALSE)
|
||||
* @return array
|
||||
*/
|
||||
public function stats( $includeFirstCommentInCommentCount=TRUE )
|
||||
@@ -682,7 +752,7 @@ class _Topic extends \IPS\Content\Item implements
|
||||
* Set the new popular time if needed
|
||||
*
|
||||
*/
|
||||
public function rebuildPopularTime( )
|
||||
public function rebuildPopularTime()
|
||||
{
|
||||
$popularNowSettings = json_decode( \IPS\Settings::i()->forums_popular_now, TRUE );
|
||||
|
||||
@@ -694,9 +764,9 @@ class _Topic extends \IPS\Content\Item implements
|
||||
\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'] )
|
||||
if ( \count( $comments ) >= $popularNowSettings['posts'] )
|
||||
{
|
||||
$commentToBasePopularNowTimeOff = array_slice( $comments, ( $popularNowSettings['posts'] - 1 ), 1 );
|
||||
$commentToBasePopularNowTimeOff = \array_slice( $comments, ( $popularNowSettings['posts'] - 1 ), 1 );
|
||||
$commentToBasePopularNowTimeOff = array_pop( $commentToBasePopularNowTimeOff );
|
||||
|
||||
$this->popular_time = \IPS\DateTime::ts( $commentToBasePopularNowTimeOff->post_date )->add( $popularNowInterval )->getTimestamp();
|
||||
@@ -760,29 +830,10 @@ class _Topic extends \IPS\Content\Item implements
|
||||
}
|
||||
}
|
||||
|
||||
if ( !$this->container()->can_view_others )
|
||||
$member = $member ?: \IPS\Member::loggedIn();
|
||||
if ( $member != $this->author() and !$this->container()->memberCanAccessOthersTopics( $member ) )
|
||||
{
|
||||
$member = $member ?: \IPS\Member::loggedIn();
|
||||
if ( $member != $this->author() )
|
||||
{
|
||||
if
|
||||
(
|
||||
(
|
||||
$member->modPermission( 'forums' ) !== -1
|
||||
and
|
||||
(
|
||||
is_array( $member->modPermission( 'forums' ) )
|
||||
and
|
||||
!in_array( $this->container()->_id, $member->modPermission( 'forums' ) )
|
||||
)
|
||||
)
|
||||
or
|
||||
!$member->modPermission( 'can_read_all_topics' )
|
||||
)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
@@ -800,7 +851,7 @@ class _Topic extends \IPS\Content\Item implements
|
||||
{
|
||||
$return = $this->container()->searchIndexPermissions();
|
||||
|
||||
if ( $this->starter_id and !$this->container()->can_view_others )
|
||||
if ( !$this->container()->can_view_others )
|
||||
{
|
||||
/* If the search index permissions are empty, just return now because no one can see content in this forum */
|
||||
if( !$return )
|
||||
@@ -918,34 +969,36 @@ class _Topic extends \IPS\Content\Item implements
|
||||
/**
|
||||
* Should new items be moderated?
|
||||
*
|
||||
* @param \IPS\Member $member The member posting
|
||||
* @param \IPS\Node\Model $container The container
|
||||
* @param \IPS\Member $member The member posting
|
||||
* @param \IPS\Node\Model $container The container
|
||||
* @param bool $considerPostBeforeRegistering If TRUE, and $member is a guest, will check if a newly registered member would be moderated
|
||||
* @return bool
|
||||
*/
|
||||
public static function moderateNewItems( \IPS\Member $member, \IPS\Node\Model $container = NULL )
|
||||
public static function moderateNewItems( \IPS\Member $member, \IPS\Node\Model $container = NULL, $considerPostBeforeRegistering = FALSE )
|
||||
{
|
||||
if ( $container and ( $container->preview_posts == 1 or $container->preview_posts == 2 ) and !$member->group['g_avoid_q'] )
|
||||
{
|
||||
return !static::modPermission( 'approve', $member, $container );
|
||||
}
|
||||
|
||||
return parent::moderateNewItems( $member, $container );
|
||||
return parent::moderateNewItems( $member, $container, $considerPostBeforeRegistering );
|
||||
}
|
||||
|
||||
/**
|
||||
* Should new comments be moderated?
|
||||
*
|
||||
* @param \IPS\Member $member The member posting
|
||||
* @param \IPS\Member $member The member posting
|
||||
* @param bool $considerPostBeforeRegistering If TRUE, and $member is a guest, will check if a newly registered member would be moderated
|
||||
* @return bool
|
||||
*/
|
||||
public function moderateNewComments( \IPS\Member $member )
|
||||
public function moderateNewComments( \IPS\Member $member, $considerPostBeforeRegistering = FALSE )
|
||||
{
|
||||
if ( ( $this->container()->preview_posts == 1 or $this->container()->preview_posts == 3 ) and !$member->group['g_avoid_q'] )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return parent::moderateNewComments( $member );
|
||||
return parent::moderateNewComments( $member, $considerPostBeforeRegistering );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1013,6 +1066,27 @@ class _Topic extends \IPS\Content\Item implements
|
||||
/* If this is a QA forum we need to make sure we only have one best answer (at most) post-merge */
|
||||
if( $this->isQuestion() )
|
||||
{
|
||||
/* Combine question ratings */
|
||||
$itemTids = array();
|
||||
|
||||
foreach( $items as $item )
|
||||
{
|
||||
/* We will use UPDATE IGNORE for the update queries, as duplicate key errors could result if we upvoted both topics */
|
||||
\IPS\Db::i()->update( 'forums_question_ratings', array( 'forum' => $this->forum_id, 'topic' => $this->tid ), array( 'topic=?', $item->tid ), array(), NULL, \IPS\Db::IGNORE );
|
||||
\IPS\Db::i()->update( 'forums_answer_ratings', array( 'topic' => $this->tid ), array( 'topic=?', $item->tid ), array(), NULL, \IPS\Db::IGNORE );
|
||||
|
||||
$itemTids[] = $item->tid;
|
||||
}
|
||||
|
||||
/* If any rows still exist for the old topic, that means it was a duplicate and we can remove */
|
||||
\IPS\Db::i()->delete( 'forums_question_ratings', "topic IN(" . implode( ',', $itemTids ) . ")" );
|
||||
\IPS\Db::i()->delete( 'forums_answer_ratings', "topic IN(" . implode( ',', $itemTids ) . ")" );
|
||||
|
||||
/* And then update the topic with the new question rating count */
|
||||
\IPS\Db::i()->update( 'forums_topics', array( 'question_rating' =>
|
||||
\IPS\Db::i()->select( 'SUM(rating)', 'forums_question_ratings', array( 'topic=?', $this->tid ) )
|
||||
), array( 'tid=?', $this->tid ) );
|
||||
|
||||
/* Does this topic already have a best answer? */
|
||||
if( $this->topic_answered_pid )
|
||||
{
|
||||
@@ -1065,6 +1139,9 @@ class _Topic extends \IPS\Content\Item implements
|
||||
}
|
||||
}
|
||||
|
||||
/* Update popular time */
|
||||
$this->rebuildPopularTime();
|
||||
|
||||
return parent::mergeIn( $items, $keepLinks );
|
||||
}
|
||||
|
||||
@@ -1133,7 +1210,7 @@ class _Topic extends \IPS\Content\Item implements
|
||||
* @param string $action The action
|
||||
* @param \IPS\Member|NULL $member The member doing the action (NULL for currently logged in member)
|
||||
* @param string|NULL $reason Reason (for hides)
|
||||
* @param bool $immedaitely Delete Immediately
|
||||
* @param bool $immediately Delete Immediately
|
||||
* @return void
|
||||
* @throws \OutOfRangeException|\InvalidArgumentException|\RuntimeException
|
||||
*/
|
||||
@@ -1141,14 +1218,14 @@ class _Topic extends \IPS\Content\Item implements
|
||||
{
|
||||
if ( mb_substr( $action, 0, 12 ) === 'savedAction-' )
|
||||
{
|
||||
$action = \IPS\forums\SavedAction::load( intval( mb_substr( $action, 12 ) ) );
|
||||
$action = \IPS\forums\SavedAction::load( \intval( mb_substr( $action, 12 ) ) );
|
||||
$action->runOn( $this );
|
||||
|
||||
/* Log */
|
||||
\IPS\Session::i()->modLog( 'modlog__saved_action', array( 'forums_mmod_' . $action->mm_id => TRUE, $this->url()->__toString() => FALSE, $this->mapped( 'title' ) => FALSE ), $this );
|
||||
}
|
||||
|
||||
call_user_func_array( 'parent::modAction', func_get_args() );
|
||||
parent::modAction( $action, $member, $reason, $immediately );
|
||||
|
||||
/* Prevent topics with an open time re-opening again after being locked */
|
||||
if ( $action == 'lock' )
|
||||
@@ -1208,9 +1285,40 @@ class _Topic extends \IPS\Content\Item implements
|
||||
}
|
||||
|
||||
/* !Questions & Answers */
|
||||
|
||||
/**
|
||||
* Any container has solvable enabled?
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function anyContainerAllowsSolvable()
|
||||
{
|
||||
return (bool) \IPS\Db::i()->select( 'COUNT(*)', 'forums_forums', '(' . \IPS\Db::i()->bitwiseWhere( \IPS\forums\Forum::$bitOptions['forums_bitoptions'], 'bw_enable_answers' ) . ') OR ( ' . \IPS\Db::i()->bitwiseWhere( \IPS\forums\Forum::$bitOptions['forums_bitoptions'], 'bw_enable_answers_moderator' ) . ' )' )->first();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is this topic a question?
|
||||
* Container has solvable enabled
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function containerAllowsSolvable()
|
||||
{
|
||||
return $this->container()->forums_bitoptions['bw_enable_answers_moderator'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Container has solvable enabled
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function containerAllowsMemberSolvable()
|
||||
{
|
||||
return $this->container()->forums_bitoptions['bw_enable_answers_member'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this topic a question? \IPS\forums\Forum::$modPerm
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
@@ -1232,9 +1340,10 @@ class _Topic extends \IPS\Content\Item implements
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* If we asked this question, we can set the best answer */
|
||||
|
||||
$member = $member ?: \IPS\Member::loggedIn();
|
||||
|
||||
/* If we asked this question, we can set the best answer */
|
||||
if ( $member == $this->author() and $this->container()->forums_bitoptions['bw_enable_answers_member'] )
|
||||
{
|
||||
return TRUE;
|
||||
@@ -1249,9 +1358,9 @@ class _Topic extends \IPS\Content\Item implements
|
||||
( $member->modPermission( \IPS\forums\Forum::$modPerm ) === TRUE or $member->modPermission( \IPS\forums\Forum::$modPerm ) === -1 )
|
||||
or
|
||||
(
|
||||
is_array( $member->modPermission( \IPS\forums\Forum::$modPerm ) )
|
||||
\is_array( $member->modPermission( \IPS\forums\Forum::$modPerm ) )
|
||||
and
|
||||
in_array( $this->container()->_id, $member->modPermission( \IPS\forums\Forum::$modPerm ) )
|
||||
\in_array( $this->container()->_id, $member->modPermission( \IPS\forums\Forum::$modPerm ) )
|
||||
)
|
||||
)
|
||||
) {
|
||||
@@ -1278,7 +1387,7 @@ class _Topic extends \IPS\Content\Item implements
|
||||
if ( !isset( $this->answerVotes[ $member->member_id ] ) )
|
||||
{
|
||||
$this->answerVotes[ $member->member_id ] = iterator_to_array(
|
||||
\IPS\Db::i()->select( 'post,rating', 'forums_answer_ratings', array( 'topic=? AND member=?', $this->tid, $member->member_id ) )
|
||||
\IPS\Db::i()->select( 'post,rating', 'forums_answer_ratings', array( 'topic=? AND `member`=?', $this->tid, $member->member_id ) )
|
||||
->setKeyField( 'post' )
|
||||
->setValueField( 'rating' )
|
||||
);
|
||||
@@ -1316,7 +1425,7 @@ class _Topic extends \IPS\Content\Item implements
|
||||
public function canVote( $rating=0, $member=NULL )
|
||||
{
|
||||
/* Is $rating valid */
|
||||
if ( !in_array( $rating, array( -1, 0, 1 ) ) )
|
||||
if ( !\in_array( $rating, array( -1, 0, 1 ) ) )
|
||||
{
|
||||
throw new \InvalidArgumentException;
|
||||
}
|
||||
@@ -1339,17 +1448,7 @@ class _Topic extends \IPS\Content\Item implements
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Have we already voted? */
|
||||
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 )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Downvoting disabled? */
|
||||
if ( $rating === -1 and !\IPS\Settings::i()->forums_questions_downvote and ( !isset( $ratings[ $member->member_id ] ) or $ratings[ $member->member_id ] != 1 ) )
|
||||
{
|
||||
@@ -1374,7 +1473,7 @@ class _Topic extends \IPS\Content\Item implements
|
||||
if ( $this->votes === NULL )
|
||||
{
|
||||
$this->votes = iterator_to_array(
|
||||
\IPS\Db::i()->select( 'member,rating', 'forums_question_ratings', array( 'topic=?', $this->tid ) )
|
||||
\IPS\Db::i()->select( '`member`,rating', 'forums_question_ratings', array( 'topic=?', $this->tid ) )
|
||||
->setKeyField( 'member' )
|
||||
->setValueField( 'rating' )
|
||||
);
|
||||
@@ -1409,7 +1508,8 @@ class _Topic extends \IPS\Content\Item implements
|
||||
/**
|
||||
* Indefinite Article
|
||||
*
|
||||
* @param \IPS\Lang|NULL $language The language to use, or NULL for the language of the currently logged in member
|
||||
* @param array $containerData Data about the container
|
||||
* @param \IPS\Lang|NULL $lang 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 )
|
||||
@@ -1431,7 +1531,7 @@ class _Topic extends \IPS\Content\Item implements
|
||||
* Definite Article
|
||||
*
|
||||
* @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 \IPS\Lang|NULL $lang 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
|
||||
*/
|
||||
@@ -1486,7 +1586,7 @@ class _Topic extends \IPS\Content\Item implements
|
||||
*/
|
||||
public function isArchived()
|
||||
{
|
||||
return in_array( $this->topic_archive_status, array( static::ARCHIVE_DONE, static::ARCHIVE_WORKING, static::ARCHIVE_RESTORE ) );
|
||||
return \in_array( $this->topic_archive_status, array( static::ARCHIVE_DONE, static::ARCHIVE_WORKING, static::ARCHIVE_RESTORE ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1532,7 +1632,7 @@ class _Topic extends \IPS\Content\Item implements
|
||||
$taskData = \IPS\Db::i()->select( '*', 'core_tasks', array( '`key`=? AND app=?', 'archive', 'forums' ) )->first();
|
||||
|
||||
$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();
|
||||
$postsToBeUnarchived = \IPS\Db::i()->select( 'SUM(posts) + count(*)', 'forums_topics', array( 'topic_archive_status=?', static::ARCHIVE_RESTORE ) )->first();
|
||||
|
||||
if ( $postsToBeUnarchived AND $postsToBeUnarchived > \IPS\forums\tasks\unarchive::PROCESS_PER_BATCH )
|
||||
{
|
||||
@@ -1550,17 +1650,18 @@ class _Topic extends \IPS\Content\Item implements
|
||||
/**
|
||||
* Can comment?
|
||||
*
|
||||
* @param \IPS\Member\NULL $member The member (NULL for currently logged in member)
|
||||
* @param \IPS\Member\NULL $member The member (NULL for currently logged in member)
|
||||
* @param bool $considerPostBeforeRegistering If TRUE, and $member is a guest, will return TRUE if "Post Before Registering" feature is enabled
|
||||
* @return bool
|
||||
*/
|
||||
public function canComment( $member=NULL )
|
||||
public function canComment( $member=NULL, $considerPostBeforeRegistering = TRUE )
|
||||
{
|
||||
if ( $this->isArchived() )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return parent::canComment( $member );
|
||||
return parent::canComment( $member, $considerPostBeforeRegistering );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1704,6 +1805,11 @@ class _Topic extends \IPS\Content\Item implements
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ( $this->moved_to )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return parent::canMerge( $member );
|
||||
}
|
||||
|
||||
@@ -1726,7 +1832,7 @@ class _Topic extends \IPS\Content\Item implements
|
||||
/**
|
||||
* Can Feature a Comment
|
||||
*
|
||||
* @param \IPS\Member|NULL The member, or NULL for currently logged in
|
||||
* @param \IPS\Member|NULL $member The member, or NULL for currently logged in
|
||||
* @return bool
|
||||
* @note This is a wrapper for the extension so content items can extend and apply their own logic
|
||||
*/
|
||||
@@ -1743,8 +1849,8 @@ class _Topic extends \IPS\Content\Item implements
|
||||
/**
|
||||
* Can perform an action on a message
|
||||
*
|
||||
* @param string The action
|
||||
* @param \IPS\Member|NULL The member, or NULL for currently logged in
|
||||
* @param string $action The action
|
||||
* @param \IPS\Member|NULL $member The member, or NULL for currently logged in
|
||||
* @return bool
|
||||
* @note This is a wrapper for the extension so content items can extend and apply their own logic
|
||||
*/
|
||||
@@ -1758,6 +1864,22 @@ class _Topic extends \IPS\Content\Item implements
|
||||
return parent::canOnMessage( $action, $member );
|
||||
}
|
||||
|
||||
/**
|
||||
* Can toggle item-level moderation?
|
||||
*
|
||||
* @param \IPS\Member|NULL $member
|
||||
* @return bool
|
||||
*/
|
||||
public function canToggleItemModeration( ?\IPS\Member $member = NULL ): bool
|
||||
{
|
||||
if ( $this->isArchived() )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return parent::canToggleItemModeration( $member );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get comments
|
||||
*
|
||||
@@ -1778,7 +1900,7 @@ class _Topic extends \IPS\Content\Item implements
|
||||
{
|
||||
static $comments = array();
|
||||
$idField = static::$databaseColumnId;
|
||||
$_hash = md5( $this->$idField . json_encode( func_get_args() ) );
|
||||
$_hash = md5( $this->$idField . json_encode( \func_get_args() ) );
|
||||
|
||||
if( !$bypassCache and isset( $comments[ $_hash ] ) )
|
||||
{
|
||||
@@ -1800,7 +1922,7 @@ class _Topic extends \IPS\Content\Item implements
|
||||
|
||||
if( $extraWhereClause !== NULL )
|
||||
{
|
||||
if( is_array( $extraWhereClause ) )
|
||||
if( \is_array( $extraWhereClause ) )
|
||||
{
|
||||
foreach( $extraWhereClause as $k => $v )
|
||||
{
|
||||
@@ -1817,9 +1939,27 @@ class _Topic extends \IPS\Content\Item implements
|
||||
{
|
||||
$class = static::$commentClass;
|
||||
}
|
||||
|
||||
$comments[ $_hash ] = $this->_comments( $class, $limit ?: static::getCommentsPerPage(), $offset, ( isset( $class::$databaseColumnMap[ $order ] ) ? ( $class::$databasePrefix . $class::$databaseColumnMap[ $order ] ) : $order ) . ' ' . $orderDirection, $member, $includeHiddenComments, $cutoff, $includeWarnings, $extraWhereClause, $includeDeleted );
|
||||
|
||||
try
|
||||
{
|
||||
$comments[ $_hash ] = $this->_comments( $class, $limit ?: static::getCommentsPerPage(), $offset, ( isset( $class::$databaseColumnMap[ $order ] ) ? ( $class::$databasePrefix . $class::$databaseColumnMap[ $order ] ) : $order ) . ' ' . $orderDirection, $member, $includeHiddenComments, $cutoff, $includeWarnings, $extraWhereClause, $includeDeleted );
|
||||
}
|
||||
catch( \IPS\Db\Exception $e )
|
||||
{
|
||||
$post = new \IPS\forums\Topic\Post;
|
||||
$post->topic_id = $this->tid;
|
||||
$post->post = '<p><em>' . \IPS\Member::loggedIn()->language()->addToStack('archived_topic_missing_posts') . '</em></p>';
|
||||
$post->post_date = $this->start_date;
|
||||
$post->author_id = $this->starter_id;
|
||||
|
||||
if ( \IPS\Member::loggedIn()->isAdmin() )
|
||||
{
|
||||
$post->post .= "<p>" . \IPS\Member::loggedIn()->language()->addToStack('archived_topic_missing_posts_admin') . "</p><p><strong>{$e->getMessage()}<br><textarea>" . var_export( $e, TRUE ) . '</textarea></p>';
|
||||
}
|
||||
|
||||
$comments[ $_hash ] = array( $post );
|
||||
}
|
||||
|
||||
/* Restore comment class now */
|
||||
if( $commentClass )
|
||||
{
|
||||
@@ -1839,6 +1979,30 @@ class _Topic extends \IPS\Content\Item implements
|
||||
return parent::resyncCommentCounts( $this->isArchived() ? static::$archiveClass : NULL );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the first comment on the item
|
||||
*
|
||||
* @return \IPS\Content\Comment|NULL
|
||||
*/
|
||||
public function firstComment()
|
||||
{
|
||||
if ( $this->isArchived() )
|
||||
{
|
||||
try
|
||||
{
|
||||
return parent::firstComment();
|
||||
}
|
||||
catch( \IPS\Db\Exception $e )
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return parent::firstComment();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check Moderator Permission
|
||||
*
|
||||
@@ -1853,18 +2017,18 @@ class _Topic extends \IPS\Content\Item implements
|
||||
$member = $member ?: \IPS\Member::loggedIn();
|
||||
|
||||
/* Compatibility checks */
|
||||
if ( in_array( $type, array( 'use_saved_actions', 'set_best_answer' ) ) )
|
||||
if ( \in_array( $type, array( 'use_saved_actions', 'set_best_answer' ) ) )
|
||||
{
|
||||
$containerClass = get_class( $container );
|
||||
$containerClass = \get_class( $container );
|
||||
$title = static::$title;
|
||||
if
|
||||
(
|
||||
$member->modPermission( $containerClass::$modPerm ) === -1
|
||||
or
|
||||
(
|
||||
is_array( $member->modPermission( $containerClass::$modPerm ) )
|
||||
\is_array( $member->modPermission( $containerClass::$modPerm ) )
|
||||
and
|
||||
in_array( $container->_id, $member->modPermission( $containerClass::$modPerm ) )
|
||||
\in_array( $container->_id, $member->modPermission( $containerClass::$modPerm ) )
|
||||
)
|
||||
)
|
||||
{
|
||||
@@ -1875,52 +2039,28 @@ class _Topic extends \IPS\Content\Item implements
|
||||
return parent::modPermission( $type, $member, $container );
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark as read
|
||||
*
|
||||
* @param \IPS\Member|NULL $member The member (NULL for currently logged in member)
|
||||
* @param int|NULL $time The timestamp to set (or NULL for current time)
|
||||
* @param mixed $extraContainerWhere Additional where clause(s) (see \IPS\Db::build for details)
|
||||
* @return void
|
||||
*/
|
||||
public function markRead( \IPS\Member $member = NULL, $time = NULL, $extraContainerWhere = NULL )
|
||||
{
|
||||
/**
|
||||
* Mark as read
|
||||
*
|
||||
* @param \IPS\Member|NULL $member The member (NULL for currently logged in member)
|
||||
* @param int|NULL $time The timestamp to set (or NULL for current time)
|
||||
* @param mixed $extraContainerWhere Additional where clause(s) (see \IPS\Db::build for details)
|
||||
* @param bool $force Mark as unread even if we already appear to be unread?
|
||||
* @return void
|
||||
*/
|
||||
public function markRead( \IPS\Member $member = NULL, $time = NULL, $extraContainerWhere = NULL, $force = FALSE )
|
||||
{
|
||||
$member = $member ?: \IPS\Member::loggedIn();
|
||||
$time = $time ?: time();
|
||||
|
||||
if ( !$this->container()->can_view_others and !$member->modPermission( 'can_read_all_topics' ) )
|
||||
if ( !$this->container()->memberCanAccessOthersTopics( $member ) )
|
||||
{
|
||||
$extraContainerWhere = array( 'starter_id = ?', $member->member_id );
|
||||
}
|
||||
|
||||
parent::markRead( $member, $time, $extraContainerWhere );
|
||||
parent::markRead( $member, $time, $extraContainerWhere, $force );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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( $return = NULL )
|
||||
{
|
||||
/* We have to store and restore this for the topic controller */
|
||||
if( isset( $_SESSION['_findComment'] ) )
|
||||
{
|
||||
$_findComment = $_SESSION['_findComment'];
|
||||
}
|
||||
|
||||
$metaDescription = parent::metaDescription( $return );
|
||||
|
||||
if( isset( $_findComment ) )
|
||||
{
|
||||
$_SESSION['_findComment'] = $_findComment;
|
||||
}
|
||||
|
||||
return $metaDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get output for API
|
||||
*
|
||||
@@ -1992,7 +2132,7 @@ class _Topic extends \IPS\Content\Item implements
|
||||
*/
|
||||
public static function supportedMetaDataTypes()
|
||||
{
|
||||
return array( 'core_FeaturedComments', 'core_ContentMessages' );
|
||||
return array( 'core_FeaturedComments', 'core_ContentMessages', 'core_ItemModeration' );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2006,4 +2146,123 @@ class _Topic extends \IPS\Content\Item implements
|
||||
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'embed.css', 'forums', 'front' ) );
|
||||
return \IPS\Theme::i()->getTemplate( 'global', 'forums' )->embedTopic( $this, $this->url()->setQueryString( $params ) );
|
||||
}
|
||||
|
||||
/* ! Reactions */
|
||||
|
||||
public $reputation = array();
|
||||
|
||||
/**
|
||||
* Reaction type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function reactionType()
|
||||
{
|
||||
return 'tid';
|
||||
}
|
||||
|
||||
/**
|
||||
* Reaction Where Clause
|
||||
*
|
||||
* @param \IPS\Content\Reaction|array|int|NULL $reactions This can be any one of the following: An \IPS\Content\Reaction object, an array of \IPS\Content\Reaction objects, an integer, or an array of integers, or NULL
|
||||
* @param bool $enabledTypesOnly If TRUE, only reactions of the enabled reaction types will be included (must join core_reactions)
|
||||
* @return array
|
||||
*/
|
||||
public function getReactionWhereClause( $reactions = NULL, $enabledTypesOnly=TRUE )
|
||||
{
|
||||
$idColumn = static::$databaseColumnId;
|
||||
$class = static::reactionClass();
|
||||
$where = array( array( 'rep_class=? and item_id=?', $class::$commentClass, $this->$idColumn ) );
|
||||
|
||||
if ( $enabledTypesOnly )
|
||||
{
|
||||
$where[] = array( 'reaction_enabled=1' );
|
||||
}
|
||||
|
||||
if ( $reactions !== NULL )
|
||||
{
|
||||
if ( !\is_array( $reactions ) )
|
||||
{
|
||||
$reactions = array( $reactions );
|
||||
}
|
||||
|
||||
$in = array();
|
||||
foreach( $reactions AS $reaction )
|
||||
{
|
||||
if ( $reaction instanceof \IPS\Content\Reaction )
|
||||
{
|
||||
$in[] = $reaction->id;
|
||||
}
|
||||
else
|
||||
{
|
||||
$in[] = $reaction;
|
||||
}
|
||||
}
|
||||
|
||||
if ( \count( $in ) )
|
||||
{
|
||||
$where[] = array( \IPS\Db::i()->in( 'reaction', $in ) );
|
||||
}
|
||||
}
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the topic summary feature?
|
||||
*
|
||||
* @param $key string Key to check (topPost, popularDays, uploads)
|
||||
* @return boolean
|
||||
*/
|
||||
public function showSummaryFeature( $key )
|
||||
{
|
||||
if ( \IPS\Settings::i()->forums_topic_activity_features )
|
||||
{
|
||||
$features = json_decode( \IPS\Settings::i()->forums_topic_activity_features, TRUE );
|
||||
if ( $features and \in_array( $key, $features ) )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the topic summary on desktop? (if so where)
|
||||
*
|
||||
* @return string [sidebar,post]
|
||||
*/
|
||||
public function showSummaryOnDesktop()
|
||||
{
|
||||
if ( ! \IPS\Settings::i()->forums_topics_activity_pages_show OR ( (int) \IPS\Settings::i()->forums_topics_activity_pages_show <= (int) $this->commentPageCount() ) )
|
||||
{
|
||||
$viewSettings = json_decode( \IPS\Settings::i()->forums_topic_activity, TRUE );
|
||||
if ( $viewSettings and \in_array( 'desktop', $viewSettings ) and isset( \IPS\Settings::i()->forums_topic_activity_desktop ) )
|
||||
{
|
||||
return \IPS\Settings::i()->forums_topic_activity_desktop;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the topic summary on mobile?
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function showSummaryOnMobile()
|
||||
{
|
||||
if ( ! \IPS\Settings::i()->forums_topics_activity_pages_show OR ( (int) \IPS\Settings::i()->forums_topics_activity_pages_show <= (int) $this->commentPageCount() ) )
|
||||
{
|
||||
$viewSettings = json_decode( \IPS\Settings::i()->forums_topic_activity, TRUE );
|
||||
if ( $viewSettings and \in_array( 'mobile', $viewSettings ) )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user