Version 4.1.19
This commit is contained in:
1 parent
28bd025b35
commit
2bd5025018
2674 files changed
+233518
-77766
No files matched your search
@@ -2,10 +2,10 @@
|
||||
/**
|
||||
* @brief Feed Import Node
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Social Suite
|
||||
* @subpackage Board
|
||||
* @package IPS Community Suite
|
||||
* @subpackage Forums
|
||||
* @since 4 Fed 2014
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
@@ -107,7 +107,7 @@ class _Feed extends \IPS\Node\Model
|
||||
{
|
||||
if ( $enabled )
|
||||
{
|
||||
\IPS\DB::i()->update( 'core_tasks', array( 'enabled' => 1 ), array( '`key`=?', 'rssimport' ) );
|
||||
\IPS\Db::i()->update( 'core_tasks', array( 'enabled' => 1 ), array( '`key`=?', 'rssimport' ) );
|
||||
}
|
||||
$this->enabled = $enabled;
|
||||
}
|
||||
@@ -254,6 +254,12 @@ class _Feed extends \IPS\Node\Model
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
/* Skip this if the member is restricted from posting */
|
||||
if( \IPS\Member::load( $this->mid )->restrict_post or \IPS\Member::load( $this->mid )->members_bitoptions['unacknowledged_warnings'] )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$previouslyImportedGuids = iterator_to_array( \IPS\Db::i()->select( 'rss_imported_guid', 'forums_rss_imported', array( 'rss_imported_impid=?', $this->id ) ) );
|
||||
|
||||
$request = \IPS\Http\Url::external( $this->url )->request();
|
||||
@@ -269,11 +275,11 @@ class _Feed extends \IPS\Node\Model
|
||||
$inserts = array();
|
||||
$request = $request->decodeXml();
|
||||
|
||||
if( !in_array( $request->getname(), array( 'feed', 'rss' ) ) )
|
||||
if( !( $request instanceof \IPS\Xml\RssOne ) AND !( $request instanceof \IPS\Xml\Rss ) AND !( $request instanceof \IPS\Xml\Atom ) )
|
||||
{
|
||||
throw new \RuntimeException( 'rss_import_invalid' );
|
||||
}
|
||||
|
||||
|
||||
foreach ( $request->articles( $this->id ) as $guid => $article )
|
||||
{
|
||||
if ( !in_array( $guid, $previouslyImportedGuids ) )
|
||||
@@ -293,8 +299,16 @@ class _Feed extends \IPS\Node\Model
|
||||
if ( $article['link'] and $this->showlink )
|
||||
{
|
||||
$rel = array();
|
||||
$rel['nofollow'] = \IPS\Settings::i()->posts_add_nofollow ? "nofollow" : FALSE;
|
||||
$rel['external'] = \IPS\Settings::i()->links_external ? "external" : FALSE;
|
||||
|
||||
if( \IPS\Settings::i()->posts_add_nofollow )
|
||||
{
|
||||
$rel['nofollow'] = 'nofollow';
|
||||
}
|
||||
|
||||
if( \IPS\Settings::i()->links_external )
|
||||
{
|
||||
$rel['external'] = 'external';
|
||||
}
|
||||
|
||||
$linkRelPart = '';
|
||||
if ( count ( $rel ) )
|
||||
@@ -306,7 +320,7 @@ class _Feed extends \IPS\Node\Model
|
||||
}
|
||||
|
||||
$member = \IPS\Member::load( $this->mid );
|
||||
$content = \IPS\Text\Parser::parseStatic( $article['content'] . $readMoreLink, FALSE, NULL, $member, 'forums_Forums', TRUE, !(bool) $member->group['g_dohtml'] );
|
||||
$content = \IPS\Text\Parser::parseStatic( $article['content'] . $readMoreLink, TRUE, NULL, $member, 'forums_Forums', TRUE, !(bool) $member->group['g_dohtml'] );
|
||||
|
||||
$post = \IPS\forums\Topic\Post::create( $topic, $content, TRUE, NULL, \IPS\forums\Topic\Post::incrementPostCount( $container ), $member, $article['date'] );
|
||||
$topic->topic_firstpost = $post->pid;
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
/**
|
||||
* @brief Forum Node
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Social Suite
|
||||
* @subpackage Board
|
||||
* @package IPS Community Suite
|
||||
* @subpackage Forums
|
||||
* @since 7 Jan 2014
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
@@ -143,6 +143,20 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
|
||||
*/
|
||||
public static $icon = 'comments';
|
||||
|
||||
/**
|
||||
* Callback from \IPS\Http\Url\Inernal::correctUrlFromVerifyClass()
|
||||
*
|
||||
* This is called when verifying the *the URL currently being viewed* is correct, before calling self::loadFromUrl()
|
||||
* Can be used if there is a more effecient way to load and cache the objects that will be used later on that page
|
||||
*
|
||||
* @param \IPS\Http\Url $url The URL of the page being viewed, which belongs to this class
|
||||
* @return void
|
||||
*/
|
||||
public static function preCorrectUrlFromVerifyClass( \IPS\Http\Url $url )
|
||||
{
|
||||
static::loadIntoMemory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Form fields prefix with "forum_" but the database columns do not have this prefix - let's strip for the massChange feature
|
||||
*
|
||||
@@ -162,6 +176,24 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
|
||||
parent::__set( $k, $v );
|
||||
}
|
||||
|
||||
/**
|
||||
* When setting parent ID to -1 (category) make sure sub_can_post is toggled off too
|
||||
*
|
||||
* @param int $val Parent ID
|
||||
* @return void
|
||||
*/
|
||||
protected function set_parent_id( $val )
|
||||
{
|
||||
$this->_data['parent_id'] = $val;
|
||||
$this->changed['parent_id'] = $val;
|
||||
|
||||
/* sub_can_post should get set to 0 for a category */
|
||||
if( $val == -1 )
|
||||
{
|
||||
$this->sub_can_post = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get SEO name
|
||||
*
|
||||
@@ -171,11 +203,11 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
|
||||
{
|
||||
if( !$this->_data['name_seo'] )
|
||||
{
|
||||
$this->name_seo = \IPS\Http\Url::seoTitle( \IPS\Lang::load( \IPS\Lang::defaultLanguage() )->get( 'forums_forum_' . $this->id ) );
|
||||
$this->name_seo = \IPS\Http\Url\Friendly::seoTitle( \IPS\Lang::load( \IPS\Lang::defaultLanguage() )->get( 'forums_forum_' . $this->id ) );
|
||||
$this->save();
|
||||
}
|
||||
|
||||
return $this->_data['name_seo'] ?: \IPS\Http\Url::seoTitle( \IPS\Lang::load( \IPS\Lang::defaultLanguage() )->get( 'forums_forum_' . $this->id ) );
|
||||
return $this->_data['name_seo'] ?: \IPS\Http\Url\Friendly::seoTitle( \IPS\Lang::load( \IPS\Lang::defaultLanguage() )->get( 'forums_forum_' . $this->id ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -292,13 +324,27 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
|
||||
/* 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 */
|
||||
$select = \IPS\Db::i()->select( '*', 'forums_topics', array( "forums_topics.forum_id=? AND forums_topics.approved=1 AND forums_topics.state != ?", $this->id, 'link' ), '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::seoTitle( $this->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::seoTitle( $this->last_title );
|
||||
$this->seo_last_title = \IPS\Http\Url\Friendly::seoTitle( $this->last_title );
|
||||
$this->last_id = $topic->tid;
|
||||
return;
|
||||
}
|
||||
@@ -316,9 +362,9 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
|
||||
$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');
|
||||
$this->seo_last_name = \IPS\Http\Url::seoTitle( $this->last_poster_name );
|
||||
$this->seo_last_name = \IPS\Http\Url\Friendly::seoTitle( $this->last_poster_name );
|
||||
$this->last_title = $comment->item()->title;
|
||||
$this->seo_last_title = \IPS\Http\Url::seoTitle( $this->last_title );
|
||||
$this->seo_last_title = \IPS\Http\Url\Friendly::seoTitle( $this->last_title );
|
||||
$this->last_id = $comment->item()->tid;
|
||||
}
|
||||
|
||||
@@ -370,28 +416,41 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
|
||||
*/
|
||||
public function lastPost()
|
||||
{
|
||||
/* If this is a forum where users cannot see other user's posts, and the user is not a moderator
|
||||
who can override that, we'll show the last post that was created by the user */
|
||||
$fetchLastPostFromUser = FALSE;
|
||||
|
||||
/* This forum does not allow you to see topics from other users... */
|
||||
if( !$this->can_view_others )
|
||||
{
|
||||
/* If we do not have the 'can_read_all_topics' permission, then it doesn't matter what forums we can moderate - we can't see
|
||||
other users posts */
|
||||
if( !\IPS\Member::loggedIn()->modPermission( 'can_read_all_topics' ) )
|
||||
{
|
||||
$fetchLastPostFromUser = TRUE;
|
||||
}
|
||||
/* If we hit the else statement, we do have moderator permissions to read all topics, so now we have to see if we can
|
||||
moderate in this forum */
|
||||
else
|
||||
{
|
||||
/* If we are not a moderator of all forums... */
|
||||
if( \IPS\Member::loggedIn()->modPermission( 'forums' ) !== -1 AND \IPS\Member::loggedIn()->modPermission( 'forums' ) !== TRUE )
|
||||
{
|
||||
/* If we cannot moderate in this forum, we need to fetch our last post */
|
||||
if( !is_array( \IPS\Member::loggedIn()->modPermission( 'forums' ) ) OR
|
||||
!in_array( $this->_id, \IPS\Member::loggedIn()->modPermission( 'forums' ) ) )
|
||||
{
|
||||
$fetchLastPostFromUser = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !$this->loggedInMemberHasPasswordAccess() )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
elseif
|
||||
(
|
||||
!$this->can_view_others
|
||||
and
|
||||
(
|
||||
!(
|
||||
\IPS\Member::loggedIn()->modPermission( 'forums' ) === -1
|
||||
or
|
||||
(
|
||||
is_array( \IPS\Member::loggedIn()->modPermission( 'forums' ) )
|
||||
and
|
||||
in_array( $this->_id, \IPS\Member::loggedIn()->modPermission( 'forums' ) )
|
||||
)
|
||||
)
|
||||
and
|
||||
!\IPS\Member::loggedIn()->modPermission( 'can_read_all_topics' )
|
||||
)
|
||||
)
|
||||
elseif ( $fetchLastPostFromUser )
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -420,7 +479,7 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
|
||||
|
||||
return $result;
|
||||
}
|
||||
elseif ( !$this->permission_showtopic and !$this->can('read') )
|
||||
elseif ( !$this->permission_showtopic and !$this->can('view') )
|
||||
{
|
||||
$return = NULL;
|
||||
|
||||
@@ -475,7 +534,12 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
|
||||
$result = $childLastPost;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( $this->sub_can_post and !$this->permission_showtopic and !$this->can('read') AND !is_null( $result ) )
|
||||
{
|
||||
$result['topic_title'] = NULL;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -526,7 +590,7 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if ( isset( \IPS\Request::i()->cookie[ 'ipbforumpass_' . $this->id ] ) and md5( $this->password ) === \IPS\Request::i()->cookie[ 'ipbforumpass_' . $this->id ] )
|
||||
if ( isset( \IPS\Request::i()->cookie[ 'ipbforumpass_' . $this->id ] ) and \IPS\Login::compareHashes( md5( $this->password ), \IPS\Request::i()->cookie[ 'ipbforumpass_' . $this->id ] ) )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
@@ -642,7 +706,7 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
|
||||
$where[] = array( 'min_posts_view<=?', $member->member_posts );
|
||||
if ( !$member->member_id )
|
||||
{
|
||||
$where[] = array( 'sub_can_post=0 OR can_view_others=1' );
|
||||
$where[] = array( '(sub_can_post=0 OR can_view_others=1)' );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -815,10 +879,13 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
|
||||
'forum_permission_custom_error',
|
||||
"form_{$id}_header_permissions",
|
||||
"form_{$id}_tab_forum_display",
|
||||
'forum_allow_rating',
|
||||
'forum_disable_sharelinks',
|
||||
"form_{$id}_tab_posting_settings",
|
||||
"form_{$id}_header_forum_display_topic",
|
||||
'forum_preview_posts',
|
||||
'forum_icon',
|
||||
'forum_sort_key'
|
||||
),
|
||||
'qa' => array(
|
||||
'forum_password_on',
|
||||
@@ -830,6 +897,8 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
|
||||
'forum_permission_custom_error',
|
||||
"form_{$id}_header_permissions",
|
||||
"form_{$id}_tab_forum_display",
|
||||
'forum_allow_rating',
|
||||
'forum_disable_sharelinks',
|
||||
"form_{$id}_tab_posting_settings",
|
||||
"form_{$id}_header_forum_display_question",
|
||||
'forum_can_view_others_qa',
|
||||
@@ -838,6 +907,12 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
|
||||
'forum_qa_rate_answers',
|
||||
'forum_preview_posts_qa',
|
||||
'forum_icon',
|
||||
'forum_sort_key_qa'
|
||||
),
|
||||
'category' => array(
|
||||
"form_{$id}_tab_forum_display",
|
||||
'forum_rules_title',
|
||||
'forum_rules_text'
|
||||
),
|
||||
'redirect' => array(
|
||||
'forum_password_on',
|
||||
@@ -914,14 +989,19 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
|
||||
$form->add( new \IPS\Helpers\Form\Number( 'forum_min_posts_view', $this->id ? $this->min_posts_view : 0, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'no_restriction' ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('approved_posts_comments'), 'forum_min_posts_view' ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'forum_can_view_others', $this->id ? $this->can_view_others : TRUE, FALSE, array(), NULL, NULL, NULL, 'forum_can_view_others' ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'forum_can_view_others_qa', $this->id ? $this->can_view_others : TRUE, FALSE, array(), NULL, NULL, NULL, 'forum_can_view_others_qa' ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'forum_permission_showtopic', $this->permission_showtopic, FALSE, array(), NULL, NULL, NULL, 'forum_permission_showtopic' ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'forum_permission_showtopic_qa', $this->permission_showtopic, FALSE, array(), NULL, NULL, NULL, 'forum_permission_showtopic_qa' ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'forum_permission_showtopic', $this->permission_showtopic ?: 0, FALSE, array(), NULL, NULL, NULL, 'forum_permission_showtopic' ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'forum_permission_showtopic_qa', $this->permission_showtopic ?: 0, FALSE, array(), NULL, NULL, NULL, 'forum_permission_showtopic_qa' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Translatable( 'forum_permission_custom_error', NULL, FALSE, array( 'app' => 'forums', 'key' => ( $this->id ? "forums_forum_{$this->id}_permerror" : NULL ), 'editor' => array( 'app' => 'forums', 'key' => 'Forums', 'autoSaveKey' => ( $this->id ? "forums-permerror-{$this->id}" : "forums-new-permerror" ), 'attachIds' => $this->id ? array( $this->id, NULL, 'permerror' ) : NULL, 'minimize' => 'forum_permerror_placeholder' ) ), NULL, NULL, NULL, 'forum_permission_custom_error' ) );
|
||||
|
||||
$form->addTab( 'forum_display' );
|
||||
$form->addHeader( 'forum_display_forum' );
|
||||
|
||||
$sortOptions = array( 'last_post' => 'sort_updated', 'last_real_post' => 'sort_last_comment', 'posts' => 'sort_num_comments', 'views' => 'sort_views', 'title' => 'sort_title', 'starter_name' => 'sort_author_name', 'last_poster_name' => 'sort_last_comment_name', 'start_date' => 'sort_date' );
|
||||
$sortOptionsQA = array( 'question_rating' => 'sort_question_rating' );
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'forum_sort_key', $this->id ? $this->sort_key : 'last_post', FALSE, array( 'options' => $sortOptions ), NULL, NULL, NULL, 'forum_sort_key' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'forum_sort_key_qa', $this->id ? $this->sort_key : 'last_post', FALSE, array( 'options' => array_merge( $sortOptions, $sortOptionsQA ) ), NULL, NULL, NULL, 'forum_sort_key_qa' ) );
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'forum_sort_key', $this->id ? $this->sort_key : 'last_post', FALSE, array( 'options' => array( 'last_post' => 'sort_updated', 'last_real_post' => 'sort_last_comment', 'posts' => 'sort_num_comments', 'views' => 'sort_views', 'title' => 'sort_title', 'starter_name' => 'sort_author_name', 'last_poster_name' => 'sort_last_comment_name', 'start_date' => 'sort_date' ) ), NULL, NULL, NULL, 'forum_sort_key' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'forum_show_rules', $this->id ? $this->show_rules : 0, FALSE, array(
|
||||
'options' => array(
|
||||
0 => 'forum_show_rules_none',
|
||||
@@ -1033,6 +1113,7 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
|
||||
$values['forum_preview_posts'] = $values['forum_preview_posts_qa'];
|
||||
$values['forum_can_view_others'] = $values['forum_can_view_others_qa'];
|
||||
$values['forum_permission_showtopic'] = $values['forum_permission_showtopic_qa'];
|
||||
$values['forum_sort_key'] = $values['forum_sort_key_qa'];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1048,6 +1129,7 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
|
||||
unset( $values['forum_can_view_others_qa'] );
|
||||
unset( $values['forum_permission_showtopic_qa'] );
|
||||
unset( $values['forum_preview_posts_qa'] );
|
||||
unset( $values['forum_sort_key_qa'] );
|
||||
}
|
||||
|
||||
if ( isset( $values['forum_parent_id'] ) )
|
||||
@@ -1101,6 +1183,13 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
|
||||
{
|
||||
$values['password'] = NULL;
|
||||
}
|
||||
|
||||
/* Reset password and can view others if toggling back to a category */
|
||||
if( in_array( $values['type'], array( 'category', 'redirect' ) ) )
|
||||
{
|
||||
$values['password'] = NULL;
|
||||
$values['can_view_others'] = TRUE;
|
||||
}
|
||||
|
||||
/* Reverse */
|
||||
if( isset( $values['disable_sharelinks'] ) )
|
||||
@@ -1136,13 +1225,13 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
|
||||
|
||||
foreach ( array( 'name' => "forums_forum_{$this->id}", 'description' => "forums_forum_{$this->id}_desc", 'rules_title' => "forums_forum_{$this->id}_rulestitle", 'rules_text' => "forums_forum_{$this->id}_rules", 'permission_custom_error' => "forums_forum_{$this->id}_permerror" ) as $fieldKey => $langKey )
|
||||
{
|
||||
if ( isset( $values[ $fieldKey ] ) )
|
||||
if ( array_key_exists( $fieldKey, $values ) )
|
||||
{
|
||||
\IPS\Lang::saveCustom( 'forums', $langKey, $values[ $fieldKey ] );
|
||||
|
||||
if ( $fieldKey === 'name' )
|
||||
{
|
||||
$this->name_seo = \IPS\Http\Url::seoTitle( $values[ $fieldKey ][ \IPS\Lang::defaultLanguage() ] );
|
||||
$this->name_seo = \IPS\Http\Url\Friendly::seoTitle( $values[ $fieldKey ][ \IPS\Lang::defaultLanguage() ] );
|
||||
$this->save();
|
||||
}
|
||||
|
||||
@@ -1158,6 +1247,13 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
|
||||
unset( $values[ $k ] );
|
||||
}
|
||||
}
|
||||
|
||||
/* Update index */
|
||||
if( $this->can_view_others !== NULL and isset( $values['can_view_others'] ) and $values['can_view_others'] != $this->can_view_others )
|
||||
{
|
||||
$this->can_view_others = $values['can_view_others'];
|
||||
$this->updateSearchIndexPermissions();
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
@@ -1217,6 +1313,13 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
|
||||
public function delete()
|
||||
{
|
||||
\IPS\File::unclaimAttachments( 'forums_Forums', $this->id );
|
||||
|
||||
try
|
||||
{
|
||||
\IPS\File::get( 'forums_Icons', $this->icon )->delete();
|
||||
}
|
||||
catch( \Exception $ex ) { }
|
||||
|
||||
parent::delete();
|
||||
|
||||
foreach ( array( 'rules_title' => "forums_forum_{$this->id}_rulestitle", 'rules_text' => "forums_forum_{$this->id}_rules", 'permission_custom_error' => "forums_forum_{$this->id}_permerror" ) as $fieldKey => $langKey )
|
||||
@@ -1346,6 +1449,62 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get which permission keys can access all topics in a forum which
|
||||
* can normally only show topics to the author
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function permissionsThatCanAccessAllTopics()
|
||||
{
|
||||
$normal = $this->searchIndexPermissions();
|
||||
$return = array();
|
||||
$members = array();
|
||||
|
||||
foreach ( \IPS\Db::i()->select( '*', 'core_moderators' ) as $moderator )
|
||||
{
|
||||
if ( $moderator['perms'] === '*' or in_array( 'can_read_all_topics', explode( ',', $moderator['perms'] ) ) )
|
||||
{
|
||||
if( $moderator['type'] === 'g' )
|
||||
{
|
||||
$return[] = $moderator['id'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$members[] = "m{$moderator['id']}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$return = ( $normal == '*' ) ? array_unique( $return ) : array_intersect( explode( ',', $normal ), array_unique( $return ) );
|
||||
|
||||
if( count( $members ) )
|
||||
{
|
||||
$return = array_merge( $return, $members );
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update search index permissions
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function updateSearchIndexPermissions()
|
||||
{
|
||||
if ( $this->can_view_others )
|
||||
{
|
||||
return parent::updateSearchIndexPermissions();
|
||||
}
|
||||
else
|
||||
{
|
||||
$permissions = implode( ',', $this->permissionsThatCanAccessAllTopics() );
|
||||
\IPS\Content\Search\Index::i()->massUpdate( 'IPS\forums\Topic', $this->_id, NULL, $permissions, NULL, NULL, NULL, NULL, NULL, TRUE );
|
||||
\IPS\Content\Search\Index::i()->massUpdate( 'IPS\forums\Topic\Post', $this->_id, NULL, $permissions, NULL, NULL, NULL, NULL, NULL, TRUE );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mass move content items in this node to another node
|
||||
*
|
||||
@@ -1477,7 +1636,10 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
|
||||
\IPS\Widget::deleteCaches( NULL, static::$permApp );
|
||||
|
||||
/* Log */
|
||||
\IPS\Session::i()->modLog( 'modlog__action_massmove', array( $contentItemClass::$title . '_pl_lc' => TRUE, $node->url()->__toString() => FALSE, $node->_title => FALSE ) );
|
||||
if ( \IPS\Dispatcher::hasInstance() )
|
||||
{
|
||||
\IPS\Session::i()->modLog( 'modlog__action_massmove', array( $contentItemClass::$title . '_pl_lc' => TRUE, $node->url()->__toString() => FALSE, $node->_title => FALSE ) );
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -1512,11 +1674,76 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
|
||||
$disabled = array();
|
||||
$guestGroup = \IPS\Member\Group::load( \IPS\Settings::i()->guest_group );
|
||||
|
||||
if( !$this->can_view_others )
|
||||
if( $this->sub_can_post and !$this->can_view_others )
|
||||
{
|
||||
$disabled[ $guestGroup->g_id ][] = 'view';
|
||||
}
|
||||
|
||||
return $disabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* The permission key or function used when building a node selector
|
||||
* in search or stream functions.
|
||||
*
|
||||
* @return string|callable function
|
||||
*/
|
||||
public static function searchableNodesPermission()
|
||||
{
|
||||
return function( $node )
|
||||
{
|
||||
if ( $node->can( 'view' ) and $node->sub_can_post )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Cached unsearchable node ids
|
||||
*/
|
||||
protected static $unsearchableNodeIds = FALSE;
|
||||
|
||||
/**
|
||||
* Return either NULL for no restrictions, or a list of container IDs we cannot search in because of app specific permissions and configuration
|
||||
* You do not need to check for 'view' permissions against the logged in member here. The Query search class does this for you.
|
||||
* This method is intended for more complex set up items, like needing to have X posts to see a forum, etc.
|
||||
* This is used for search and the activity stream.
|
||||
* We return a list of IDs and not node objects for memory efficiency.
|
||||
*
|
||||
* return null|array
|
||||
*/
|
||||
public static function unsearchableNodeIds()
|
||||
{
|
||||
if( static::$unsearchableNodeIds !== FALSE )
|
||||
{
|
||||
return static::$unsearchableNodeIds;
|
||||
}
|
||||
|
||||
/* For memory efficiency, we query the database directly rather than manage nodes */
|
||||
$forums = iterator_to_array( \IPS\Db::i()->select( 'id', 'forums_forums', array( 'min_posts_view > ?', \IPS\Member::loggedIn()->member_posts ) )->setKeyField('id') );
|
||||
static::$unsearchableNodeIds = count( $forums ) ? $forums : NULL;
|
||||
return static::$unsearchableNodeIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get output for API
|
||||
*
|
||||
* @return array
|
||||
* @apiresponse int id ID number
|
||||
* @apiresponse string name Forum name
|
||||
* @apiresponse int topics Number of topics in forum
|
||||
* @apiresponse string url URL
|
||||
*/
|
||||
public function apiOutput()
|
||||
{
|
||||
return array(
|
||||
'id' => $this->id,
|
||||
'name' => $this->_title,
|
||||
'topics' => $this->topics,
|
||||
'url' => (string) $this->url()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,10 @@
|
||||
/**
|
||||
* @brief Saved Action Node
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Social Suite
|
||||
* @subpackage Board
|
||||
* @package IPS Community Suite
|
||||
* @subpackage Forums
|
||||
* @since 31 Jan 2014
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
@@ -230,10 +230,6 @@ class _SavedAction extends \IPS\Node\Model
|
||||
{
|
||||
throw new \DomainException('DISABLED');
|
||||
}
|
||||
if ( $this->mm_forums !== '*' and !in_array( $topic->container()->_id, explode( ',', $this->mm_forums ) ) )
|
||||
{
|
||||
throw new \DomainException('BAD_FORUM');
|
||||
}
|
||||
if ( $this->mm_forums !== '*' and !in_array( $topic->container()->_id, explode( ',', $this->mm_forums ) ) )
|
||||
{
|
||||
throw new \DomainException('BAD_FORUM');
|
||||
@@ -278,11 +274,11 @@ class _SavedAction extends \IPS\Node\Model
|
||||
/* Hide/Unhide */
|
||||
if ( $this->topic_approve == 1 )
|
||||
{
|
||||
$topic->unhide();
|
||||
$topic->unhide( $member );
|
||||
}
|
||||
elseif ( $this->topic_approve == 2 )
|
||||
{
|
||||
$topic->hide();
|
||||
$topic->hide( $member );
|
||||
}
|
||||
|
||||
/* Reply */
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
/**
|
||||
* @brief Archived Post Model
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Social Suite
|
||||
* @subpackage Board
|
||||
* @package IPS Community Suite
|
||||
* @subpackage Forums
|
||||
* @since 24 Jan 2014
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
@@ -28,6 +28,11 @@ class _ArchivedPost extends Post
|
||||
* @brief [ActiveRecord] Multiton Store
|
||||
*/
|
||||
protected static $multitons;
|
||||
|
||||
/**
|
||||
* @brief Title
|
||||
*/
|
||||
public static $archiveTitle = 'archivedpost';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Connection
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
/**
|
||||
* @brief Post Model
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Social Suite
|
||||
* @subpackage Board
|
||||
* @package IPS Community Suite
|
||||
* @subpackage Forums
|
||||
* @since 8 Jan 2014
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
@@ -116,41 +116,53 @@ class _Post extends \IPS\Content\Comment implements \IPS\Content\ReportCenter, \
|
||||
/**
|
||||
* Create comment
|
||||
*
|
||||
* @param \IPS\Content\Item $item The content item just created
|
||||
* @param string $comment The comment
|
||||
* @param bool $first Is the first comment?
|
||||
* @param string $guestName If author is a guest, the name to use
|
||||
* @param \IPS\Content\Item $item The content item just created
|
||||
* @param string $comment The comment
|
||||
* @param bool $first Is the first comment?
|
||||
* @param string $guestName If author is a guest, the name to use
|
||||
* @param bool|NULL $incrementPostCount Increment post count? If NULL, will use static::incrementPostCount()
|
||||
* @param \IPS\Member|NULL $member The author of this comment. If NULL, uses currently logged in member.
|
||||
* @param \IPS\DateTime|NULL $time The time
|
||||
* @param string|NULL $ipAddress The IP address or NULL to detect automatically
|
||||
* @param int|NULL $hiddenStatus NULL to set automatically or override: 0 = unhidden; 1 = hidden, pending moderator approval; -1 = hidden (as if hidden by a moderator)
|
||||
* @return static
|
||||
*/
|
||||
public static function create( $item, $comment, $first=FALSE, $guestName=NULL, $incrementPostCount=NULL, $member=NULL, \IPS\DateTime $time=NULL )
|
||||
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() );
|
||||
|
||||
if ( !$comment->hidden() and $popularNowSettings = json_decode( \IPS\Settings::i()->forums_popular_now, TRUE ) and $popularNowSettings['posts'] and $popularNowSettings['minutes'] )
|
||||
if ( !$comment->hidden() )
|
||||
{
|
||||
$popularNowInterval = new \DateInterval( 'PT' . $popularNowSettings['minutes'] . 'M' );
|
||||
|
||||
$comments = $item->comments( NULL, NULL, 'date', 'desc', NULL, FALSE, \IPS\DateTime::create()->sub( $popularNowInterval ) );
|
||||
if ( count( $comments ) >= $popularNowSettings['posts'] )
|
||||
{
|
||||
$commentToBasePopularNowTimeOff = array_slice( $comments, ( $popularNowSettings['posts'] - 1 ), 1 );
|
||||
$commentToBasePopularNowTimeOff = array_pop( $commentToBasePopularNowTimeOff );
|
||||
|
||||
$item->popular_time = \IPS\DateTime::ts( $commentToBasePopularNowTimeOff->post_date )->add( $popularNowInterval )->getTimestamp();
|
||||
$item->save();
|
||||
}
|
||||
elseif ( $item->popular_time !== NULL )
|
||||
{
|
||||
$item->popular_time = NULL;
|
||||
$item->save();
|
||||
}
|
||||
$item->rebuildPopularTime();
|
||||
}
|
||||
|
||||
return $comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Syncing to run when hiding
|
||||
*
|
||||
* @param \IPS\Member|NULL|FALSE $member The member doing the action (NULL for currently logged in member, FALSE for no member)
|
||||
* @return void
|
||||
*/
|
||||
public function onHide( $member )
|
||||
{
|
||||
parent::onHide( $member );
|
||||
$this->item()->rebuildPopularTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Syncing to run when unhiding
|
||||
*
|
||||
* @param bool $approving If true, is being approved for the first time
|
||||
* @param \IPS\Member|NULL|FALSE $member The member doing the action (NULL for currently logged in member, FALSE for no member)
|
||||
* @return void
|
||||
*/
|
||||
public function onUnhide( $approving, $member )
|
||||
{
|
||||
parent::onUnhide( $approving, $member );
|
||||
$this->item()->rebuildPopularTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Should posting this increment the poster's post count?
|
||||
@@ -186,7 +198,7 @@ class _Post extends \IPS\Content\Comment implements \IPS\Content\ReportCenter, \
|
||||
* @param string $order MySQL ORDER BY clause (NULL to order by date)
|
||||
* @param int|array $limit Limit clause
|
||||
* @param string $permissionKey A key which has a value in the permission map (either of the container or of this class) matching a column ID in core_permission_index
|
||||
* @param bool|NULL $includeHiddenItems Include hidden files? Boolean or NULL to detect if currently logged member has permission
|
||||
* @param mixed $includeHiddenItems Include hidden comments? NULL to detect if currently logged in member has permission, -1 to return public content only, TRUE to return unapproved content and FALSE to only return unapproved content the viewing member submitted
|
||||
* @param int $queryFlags Select bitwise flags
|
||||
* @param \IPS\Member $member The member (NULL to use currently logged in member)
|
||||
* @param bool $joinContainer If true, will join container data (set to TRUE if your $where clause depends on this data)
|
||||
@@ -194,7 +206,7 @@ class _Post extends \IPS\Content\Comment implements \IPS\Content\ReportCenter, \
|
||||
* @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
|
||||
*/
|
||||
public static function getItemsWithPermission( $where=array(), $order=NULL, $limit=10, $permissionKey='read', $includeHiddenItems=NULL, $queryFlags=0, \IPS\Member $member=NULL, $joinContainer=FALSE, $joinComments=FALSE, $joinReviews=FALSE, $countOnly=FALSE, $joins=NULL )
|
||||
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 )
|
||||
{
|
||||
$where = \IPS\forums\Topic::getItemsWithPermissionWhere( $where, $permissionKey, $member, $joinContainer );
|
||||
|
||||
@@ -207,15 +219,17 @@ class _Post extends \IPS\Content\Comment implements \IPS\Content\ReportCenter, \
|
||||
* @param array $indexData Data from the search index
|
||||
* @param array $authorData Basic data about the author. Only includes columns returned by \IPS\Member::columnsForPhoto()
|
||||
* @param array $itemData Basic data about the item. Only includes columns returned by item::basicDataColumns()
|
||||
* @param array|NULL $containerData Basic data about the author. Only includes columns returned by container::basicDataColumns()
|
||||
* @param array|NULL $containerData Basic data about the container. Only includes columns returned by container::basicDataColumns()
|
||||
* @param array $reputationData Array of people who have given reputation and the reputation they gave
|
||||
* @param int|NULL $reviewRating If this is a review, the rating
|
||||
* @param bool $iPostedIn If the user has posted in the item
|
||||
* @param string $view 'expanded' or 'condensed'
|
||||
* @param bool $asItem Displaying results as items?
|
||||
* @param bool $canIgnoreComments Can ignore comments in the result stream? Activity stream can, but search results cannot.
|
||||
* @param array $template Optional custom template
|
||||
* @return string
|
||||
*/
|
||||
public static function searchResult( array $indexData, array $authorData, array $itemData, array $containerData = NULL, array $reputationData, $reviewRating, $iPostedIn, $view, $asItem )
|
||||
public static function searchResult( array $indexData, array $authorData, array $itemData, array $containerData = NULL, array $reputationData, $reviewRating, $iPostedIn, $view, $asItem, $canIgnoreComments=FALSE, $template=NULL )
|
||||
{
|
||||
/* Password protected */
|
||||
if (
|
||||
@@ -224,7 +238,7 @@ class _Post extends \IPS\Content\Comment implements \IPS\Content\ReportCenter, \
|
||||
and (
|
||||
!isset( \IPS\Request::i()->cookie[ 'ipbforumpass_' . $indexData['index_container_id'] ] )
|
||||
or
|
||||
md5( $containerData['password'] ) !== \IPS\Request::i()->cookie[ 'ipbforumpass_' . $indexData['index_container_id'] ]
|
||||
!\IPS\Login::compareHashes( md5( $containerData['password'] ), \IPS\Request::i()->cookie[ 'ipbforumpass_' . $indexData['index_container_id'] ] )
|
||||
) // We don't have the correct password
|
||||
)
|
||||
{
|
||||
@@ -233,9 +247,9 @@ class _Post extends \IPS\Content\Comment implements \IPS\Content\ReportCenter, \
|
||||
\IPS\Http\Url::internal( \IPS\forums\Forum::$urlBase . $indexData['index_container_id'], 'front', \IPS\forums\Forum::$urlTemplate, array( $containerData[ \IPS\forums\Forum::$databasePrefix . \IPS\forums\Forum::$seoTitleColumn ] ) )->setQueryString( 'topic', $indexData['index_item_id'] )
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/* Minimum posts */
|
||||
elseif ( $containerData['min_posts_view'] and $containerData['min_posts_view'] >= \IPS\Member::loggedIn()->member_po )
|
||||
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'] ) ) ) );
|
||||
}
|
||||
@@ -243,7 +257,7 @@ class _Post extends \IPS\Content\Comment implements \IPS\Content\ReportCenter, \
|
||||
/* Normal */
|
||||
else
|
||||
{
|
||||
return parent::searchResult( $indexData, $authorData, $itemData, $containerData, $reputationData, $reviewRating, $iPostedIn, $view, $asItem );
|
||||
return parent::searchResult( $indexData, $authorData, $itemData, $containerData, $reputationData, $reviewRating, $iPostedIn, $view, $asItem, $canIgnoreComments, $template );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,22 +324,56 @@ class _Post extends \IPS\Content\Comment implements \IPS\Content\ReportCenter, \
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
/* It is possible to delete a post that is orphaned, so let's try to protect against that */
|
||||
try
|
||||
{
|
||||
$item = $this->item();
|
||||
}
|
||||
catch( \OutOfRangeException $e )
|
||||
{
|
||||
$item = NULL;
|
||||
}
|
||||
|
||||
/* Reset best answer if relevant */
|
||||
if ( $this->item()->topic_answered_pid == $this->pid )
|
||||
if ( $item !== NULL AND $item->topic_answered_pid == $this->pid )
|
||||
{
|
||||
$this->item()->topic_answered_pid = FALSE;
|
||||
$this->item()->save();
|
||||
$item->topic_answered_pid = FALSE;
|
||||
$item->save();
|
||||
}
|
||||
|
||||
parent::delete();
|
||||
|
||||
/* Deleting a post may make the item no longer popular */
|
||||
if( $item !== NULL )
|
||||
{
|
||||
$item->rebuildPopularTime();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Indefinite Article
|
||||
*
|
||||
* @param \IPS\Lang|NULL $language The language to use, or NULL for the language of the currently logged in member
|
||||
* @return string
|
||||
*/
|
||||
public function indefiniteArticle( \IPS\Lang $lang = NULL )
|
||||
{
|
||||
if ( $this->isFirst() AND $this->item()->container()->forums_bitoptions['bw_enable_answers'] )
|
||||
{
|
||||
$lang = $lang ?: \IPS\Member::loggedIn()->language();
|
||||
return $lang->addToStack( '__indefart_question', FALSE );
|
||||
}
|
||||
|
||||
return parent::indefiniteArticle( $lang );
|
||||
}
|
||||
|
||||
/**
|
||||
* Indefinite Article
|
||||
*
|
||||
* @param array $containerData Basic data about the container. Only includes columns returned by container::basicDataColumns()
|
||||
* @param \IPS\Lang $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 )
|
||||
{
|
||||
$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'] );
|
||||
@@ -340,6 +388,28 @@ class _Post extends \IPS\Content\Comment implements \IPS\Content\ReportCenter, \
|
||||
return parent::_indefiniteArticle( $containerData, $lang );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Definite Article
|
||||
*
|
||||
* @param array $containerData Basic data about the container. Only includes columns returned by container::basicDataColumns()
|
||||
* @param \IPS\Lang $lang The language to use, or NULL for the language of the currently logged in member
|
||||
* @return string
|
||||
*/
|
||||
public static function _definiteArticle( array $containerData = NULL, \IPS\Lang $lang = NULL )
|
||||
{
|
||||
$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_answer', FALSE );
|
||||
}
|
||||
else
|
||||
{
|
||||
return parent::_definiteArticle( $containerData, $lang );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get HTML
|
||||
@@ -353,4 +423,18 @@ class _Post extends \IPS\Content\Comment implements \IPS\Content\ReportCenter, \
|
||||
|
||||
return parent::html();
|
||||
}
|
||||
|
||||
/**
|
||||
* Force HTML posting abilities to TRUE for this comment
|
||||
* This is usually determined by the member group and Editor extension.
|
||||
* Here it can be overridden on a per comment basis
|
||||
*
|
||||
* @note Used currently in applications/core/extensions/core/Queue/RebuildPosts when rebuilding
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function htmlParsingEnforced()
|
||||
{
|
||||
return (boolean) $this->post_htmlstate > 0;
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,10 @@
|
||||
/**
|
||||
* @brief Topic Model
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Social Suite
|
||||
* @subpackage Board
|
||||
* @package IPS Community Suite
|
||||
* @subpackage Forums
|
||||
* @since 8 Jan 2014
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
@@ -144,6 +144,20 @@ class _Topic extends \IPS\Content\Item implements
|
||||
* @brief Hover preview
|
||||
*/
|
||||
public $tableHoverUrl = TRUE;
|
||||
|
||||
/**
|
||||
* Callback from \IPS\Http\Url\Inernal::correctUrlFromVerifyClass()
|
||||
*
|
||||
* This is called when verifying the *the URL currently being viewed* is correct, before calling self::loadFromUrl()
|
||||
* Can be used if there is a more effecient way to load and cache the objects that will be used later on that page
|
||||
*
|
||||
* @param \IPS\Http\Url $url The URL of the page being viewed, which belongs to this class
|
||||
* @return void
|
||||
*/
|
||||
public static function preCorrectUrlFromVerifyClass( \IPS\Http\Url $url )
|
||||
{
|
||||
\IPS\forums\Forum::loadIntoMemory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the request for legacy parameters we may need to redirect to
|
||||
@@ -168,6 +182,7 @@ class _Topic extends \IPS\Content\Item implements
|
||||
/* p=123 needs to go to comment=123 */
|
||||
if( isset( \IPS\Request::i()->p ) )
|
||||
{
|
||||
$paramsToSet['do'] = 'findComment';
|
||||
$paramsToSet['comment'] = \IPS\Request::i()->p;
|
||||
$paramsToUnset[] = 'p';
|
||||
}
|
||||
@@ -233,13 +248,13 @@ class _Topic extends \IPS\Content\Item implements
|
||||
}
|
||||
|
||||
/**
|
||||
* Get items with permisison check
|
||||
* Get items with permission check
|
||||
*
|
||||
* @param array $where Where clause
|
||||
* @param string $order MySQL ORDER BY clause (NULL to order by date)
|
||||
* @param int|array $limit Limit clause
|
||||
* @param string|NULL $permissionKey A key which has a value in the permission map (either of the container or of this class) matching a column ID in core_permission_index or NULL to ignore permissions
|
||||
* @param bool|NULL $includeHiddenItems Include hidden files? Boolean or NULL to detect if currently logged member has permission
|
||||
* @param mixed $includeHiddenItems Include hidden items? NULL to detect if currently logged in member has permission, -1 to return public content only, TRUE to return unapproved content and FALSE to only return unapproved content the viewing member submitted
|
||||
* @param int $queryFlags Select bitwise flags
|
||||
* @param \IPS\Member $member The member (NULL to use currently logged in member)
|
||||
* @param bool $joinContainer If true, will join container data (set to TRUE if your $where clause depends on this data)
|
||||
@@ -251,12 +266,13 @@ class _Topic extends \IPS\Content\Item implements
|
||||
* @param bool $joinTags If true, will join the tags table
|
||||
* @param bool $joinAuthor If true, will join the members table for the author
|
||||
* @param bool $joinLastCommenter If true, will join the members table for the last commenter
|
||||
* @param bool $showMovedLinks If true, moved item links are included in the results
|
||||
* @return \IPS\Patterns\ActiveRecordIterator|int
|
||||
*/
|
||||
public static function getItemsWithPermission( $where=array(), $order=NULL, $limit=10, $permissionKey='read', $includeHiddenItems=NULL, $queryFlags=0, \IPS\Member $member=NULL, $joinContainer=FALSE, $joinComments=FALSE, $joinReviews=FALSE, $countOnly=FALSE, $joins=NULL, $skipPermission=FALSE, $joinTags=TRUE, $joinAuthor=TRUE, $joinLastCommenter=TRUE )
|
||||
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, $skipPermission=FALSE, $joinTags=TRUE, $joinAuthor=TRUE, $joinLastCommenter=TRUE, $showMovedLinks=FALSE )
|
||||
{
|
||||
$where = static::getItemsWithPermissionWhere( $where, $permissionKey, $member, $joinContainer, $skipPermission );
|
||||
return parent::getItemsWithPermission( $where, $order, $limit, $permissionKey, $includeHiddenItems, $queryFlags, $member, $joinContainer, $joinComments, $joinReviews, $countOnly, $joins, $skipPermission, $joinTags, $joinAuthor, $joinLastCommenter );
|
||||
return parent::getItemsWithPermission( $where, $order, $limit, $permissionKey, $includeHiddenItems, $queryFlags, $member, $joinContainer, $joinComments, $joinReviews, $countOnly, $joins, $skipPermission, $joinTags, $joinAuthor, $joinLastCommenter, $showMovedLinks );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -279,7 +295,7 @@ class _Topic extends \IPS\Content\Item implements
|
||||
* @param string $permissionKey A key which has a value in the permission map (either of the container or of this class) matching a column ID in core_permission_index
|
||||
* @param \IPS\Member $member The member (NULL to use currently logged in member)
|
||||
* @param bool $joinContainer If true, will join container data (set to TRUE if your $where clause depends on this data)
|
||||
* @param mixed $skipPermission If you are getting records from a specific container, pass the container to reduce the number of permission checks necessary or pass TRUE to skip conatiner-based permission. You must still specify this in the $where clause
|
||||
* @param mixed $skipPermission If you are getting records from a specific container, pass the container to reduce the number of permission checks necessary or pass TRUE to skip container-based permission. You must still specify this in the $where clause
|
||||
* @return array
|
||||
*/
|
||||
public static function getItemsWithPermissionWhere( $where, $permissionKey, $member, &$joinContainer, $skipPermission=FALSE )
|
||||
@@ -304,18 +320,16 @@ class _Topic extends \IPS\Content\Item implements
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't show links to moved topics */
|
||||
$where['item'][] = array( "( NULLIF(forums_topics.moved_to, '') IS NULL )" );
|
||||
|
||||
$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' ) ) )
|
||||
{
|
||||
$member = $member ?: \IPS\Member::loggedIn();
|
||||
|
||||
if ( !$member->modPermission( 'can_read_all_topics' ) or $member->modPermission( 'forums' ) !== -1 )
|
||||
|
||||
if ( !$member->modPermission( 'can_read_all_topics' ) or ( $member->modPermission( 'forums' ) !== TRUE and $member->modPermission( 'forums' ) !== -1 ) )
|
||||
{
|
||||
if ( $skipPermission instanceof \IPS\forums\Forum )
|
||||
{
|
||||
@@ -343,7 +357,7 @@ class _Topic extends \IPS\Content\Item implements
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* 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' ) ) )
|
||||
{
|
||||
@@ -496,17 +510,11 @@ class _Topic extends \IPS\Content\Item implements
|
||||
/* Moderator actions */
|
||||
if ( isset( $values['topic_create_state'] ) )
|
||||
{
|
||||
|
||||
if ( static::modPermission( 'feature', NULL, $this->container() ) )
|
||||
if ( static::modPermission( 'lock', NULL, $this->container() ) )
|
||||
{
|
||||
$this->state = ( in_array( 'lock', $values['topic_create_state'] ) ) ? 'closed' : 'open';
|
||||
}
|
||||
|
||||
if ( static::modPermission( 'hide', NULL, $this->container() ) )
|
||||
{
|
||||
$this->approved = ( in_array( 'hide', $values['topic_create_state'] ) ) ? -1 : 1;
|
||||
}
|
||||
|
||||
|
||||
if ( static::modPermission( 'pin', NULL, $this->container() ) )
|
||||
{
|
||||
$this->pinned = ( in_array( 'pin', $values['topic_create_state'] ) ) ? 1 : 0;
|
||||
@@ -521,17 +529,11 @@ class _Topic extends \IPS\Content\Item implements
|
||||
if ( static::modPermission( 'lock', NULL, $this->container() ) )
|
||||
{
|
||||
/* Set open/close time */
|
||||
if ( isset( $values['topic_open_time'] ) and $values['topic_open_time'] )
|
||||
$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'] ) )
|
||||
{
|
||||
$this->topic_open_time = $values['topic_open_time']->getTimestamp();
|
||||
}
|
||||
if ( isset( $values['topic_close_time'] ) and $values['topic_close_time'] )
|
||||
{
|
||||
$this->topic_close_time = $values['topic_close_time']->getTimestamp();
|
||||
}
|
||||
else if( isset( $values['topic_create_state'] ) and !in_array( 'lock', $values['topic_create_state'] ) )
|
||||
{
|
||||
$this->topic_close_time = 0;
|
||||
$this->state = 'open';
|
||||
}
|
||||
|
||||
@@ -541,8 +543,20 @@ class _Topic extends \IPS\Content\Item implements
|
||||
$this->state = 'closed';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Process created object AFTER the object has been created
|
||||
*
|
||||
* @param \IPS\Content\Comment|NULL $comment The first comment
|
||||
* @param array $values Values from form
|
||||
* @return void
|
||||
*/
|
||||
protected function processAfterCreate( $comment, $values )
|
||||
{
|
||||
$this->processAfterCreateOrEdit( $values );
|
||||
|
||||
parent::processAfterCreate( $comment, $values );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -553,11 +567,13 @@ class _Topic extends \IPS\Content\Item implements
|
||||
*/
|
||||
public function processAfterEdit( $values )
|
||||
{
|
||||
$this->processAfterCreateOrEdit( $values );
|
||||
|
||||
/* Initial Comment */
|
||||
parent::processAfterEdit( $values );
|
||||
|
||||
/* Topic changed? */
|
||||
if ( ! $this->hidden() and ( $this->tid === $this->container()->last_id ) and ( $this->title_seo !== $this->container()->seo_last_title ) )
|
||||
if ( ! $this->hidden() and ( $this->tid === $this->container()->last_id ) )
|
||||
{
|
||||
$this->container()->seo_last_title = $this->title_seo;
|
||||
$this->container()->last_title = $this->title;
|
||||
@@ -574,6 +590,32 @@ class _Topic extends \IPS\Content\Item implements
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process after the object has been edited or created on the front-end
|
||||
*
|
||||
* @param array $values Values from form
|
||||
* @return void
|
||||
*/
|
||||
protected function processAfterCreateOrEdit( $values )
|
||||
{
|
||||
/* Moderator actions */
|
||||
if ( isset( $values['topic_create_state'] ) )
|
||||
{
|
||||
if ( static::modPermission( 'hide', NULL, $this->container() ) )
|
||||
{
|
||||
if( in_array( 'hide', $values['topic_create_state'] ) )
|
||||
{
|
||||
$this->hide( NULL );
|
||||
}
|
||||
/* Only unhide if the topic is not pending approval */
|
||||
elseif( $this->hidden() !== 1 )
|
||||
{
|
||||
$this->unhide( NULL );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Cached URLs
|
||||
@@ -633,6 +675,35 @@ class _Topic extends \IPS\Content\Item implements
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the new popular time if needed
|
||||
*
|
||||
*/
|
||||
public function rebuildPopularTime( )
|
||||
{
|
||||
$popularNowSettings = json_decode( \IPS\Settings::i()->forums_popular_now, TRUE );
|
||||
|
||||
if ( $popularNowSettings['posts'] and $popularNowSettings['minutes'] )
|
||||
{
|
||||
$popularNowInterval = new \DateInterval( 'PT' . $popularNowSettings['minutes'] . 'M' );
|
||||
|
||||
$comments = $this->comments( NULL, NULL, 'date', 'desc', NULL, FALSE, \IPS\DateTime::create()->sub( $popularNowInterval ) );
|
||||
if ( count( $comments ) >= $popularNowSettings['posts'] )
|
||||
{
|
||||
$commentToBasePopularNowTimeOff = array_slice( $comments, ( $popularNowSettings['posts'] - 1 ), 1 );
|
||||
$commentToBasePopularNowTimeOff = array_pop( $commentToBasePopularNowTimeOff );
|
||||
|
||||
$this->popular_time = \IPS\DateTime::ts( $commentToBasePopularNowTimeOff->post_date )->add( $popularNowInterval )->getTimestamp();
|
||||
$this->save();
|
||||
}
|
||||
elseif ( $this->popular_time !== NULL )
|
||||
{
|
||||
$this->popular_time = NULL;
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set name
|
||||
@@ -643,7 +714,7 @@ class _Topic extends \IPS\Content\Item implements
|
||||
public function set_title( $title )
|
||||
{
|
||||
$this->_data['title'] = $title;
|
||||
$this->_data['title_seo'] = \IPS\Http\Url::seoTitle( $title );
|
||||
$this->_data['title_seo'] = \IPS\Http\Url\Friendly::seoTitle( $title );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -655,11 +726,11 @@ class _Topic extends \IPS\Content\Item implements
|
||||
{
|
||||
if( !$this->_data['title_seo'] )
|
||||
{
|
||||
$this->title_seo = \IPS\Http\Url::seoTitle( $this->title );
|
||||
$this->title_seo = \IPS\Http\Url\Friendly::seoTitle( $this->title );
|
||||
$this->save();
|
||||
}
|
||||
|
||||
return $this->_data['title_seo'] ?: \IPS\Http\Url::seoTitle( $this->title );
|
||||
return $this->_data['title_seo'] ?: \IPS\Http\Url\Friendly::seoTitle( $this->title );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -722,9 +793,9 @@ class _Topic extends \IPS\Content\Item implements
|
||||
*/
|
||||
public function searchIndexPermissions()
|
||||
{
|
||||
$return = parent::searchIndexPermissions();
|
||||
$return = $this->container()->searchIndexPermissions();
|
||||
|
||||
if ( !$this->container()->can_view_others )
|
||||
if ( $this->starter_id and !$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 )
|
||||
@@ -732,40 +803,16 @@ class _Topic extends \IPS\Content\Item implements
|
||||
return $return;
|
||||
}
|
||||
|
||||
$normal = $return;
|
||||
$return = array();
|
||||
$members = array();
|
||||
|
||||
foreach ( \IPS\Db::i()->select( '*', 'core_moderators' ) as $moderator )
|
||||
{
|
||||
if ( $moderator['perms'] === '*' or in_array( 'can_read_all_topics', explode( ',', $moderator['perms'] ) ) )
|
||||
{
|
||||
if( $moderator['type'] === 'g' )
|
||||
{
|
||||
$return[] = $moderator['id'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$members[] = "m{$moderator['id']}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$return = ( $normal == '*' ) ? array_unique( $return ) : array_intersect( explode( ',', $normal ), array_unique( $return ) );
|
||||
$return = $this->container()->permissionsThatCanAccessAllTopics();
|
||||
|
||||
if ( $this->starter_id )
|
||||
{
|
||||
$members[] = "m{$this->starter_id}";
|
||||
}
|
||||
|
||||
if( count( $members ) )
|
||||
{
|
||||
$return = array_merge( $return, $members );
|
||||
$return[] = "m{$this->starter_id}";
|
||||
}
|
||||
|
||||
$return = implode( ',', $return );
|
||||
}
|
||||
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
@@ -855,11 +902,11 @@ class _Topic extends \IPS\Content\Item implements
|
||||
|
||||
if ( !empty( $openIds ) )
|
||||
{
|
||||
\IPS\Db::i()->update( 'forums_topics', array( 'state' => 'open', 'topic_close_time' => 0 ), \IPS\Db::i()->in( 'tid', $openIds ) );
|
||||
\IPS\Db::i()->update( 'forums_topics', array( 'state' => 'open', 'topic_open_time' => 0 ), \IPS\Db::i()->in( 'tid', $openIds ) );
|
||||
}
|
||||
if ( !empty( $closeIds ) )
|
||||
{
|
||||
\IPS\Db::i()->update( 'forums_topics', array( 'state' => 'closed', 'topic_open_time' => 0 ), \IPS\Db::i()->in( 'tid', $closeIds ) );
|
||||
\IPS\Db::i()->update( 'forums_topics', array( 'state' => 'closed', 'topic_close_time' => 0 ), \IPS\Db::i()->in( 'tid', $closeIds ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -922,12 +969,123 @@ class _Topic extends \IPS\Content\Item implements
|
||||
public function delete()
|
||||
{
|
||||
parent::delete();
|
||||
|
||||
\IPS\forums\Topic\ArchivedPost::db()->delete( 'forums_archive_posts', array( 'archive_topic_id=?', $this->tid ) );
|
||||
|
||||
try
|
||||
{
|
||||
\IPS\forums\Topic\ArchivedPost::db()->delete( 'forums_archive_posts', array( 'archive_topic_id=?', $this->tid ) );
|
||||
}
|
||||
/* catch db exceptions if e.g. if the connection credentials didn't work or if the database doesn't exist anymore */
|
||||
catch ( \IPS\Db\Exception $e ){}
|
||||
|
||||
\IPS\Db::i()->delete( 'forums_question_ratings', array( 'topic=?', $this->tid ) );
|
||||
\IPS\Db::i()->delete( 'forums_answer_ratings', array( 'topic=?', $this->tid ) );
|
||||
|
||||
/* Delete any moved topic links that point to this topic */
|
||||
\IPS\Db::i()->delete( 'forums_topics', array( "state=? AND moved_to LIKE CONCAT( ?, '%' )", 'link', $this->tid . '&' ) );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Merge other items in (they will be deleted, this will be kept)
|
||||
*
|
||||
* @param array $items Items to merge in
|
||||
* @param bool $keepLinks Retain redirect links for the items that were merge in
|
||||
* @return void
|
||||
*/
|
||||
public function mergeIn( array $items, $keepLinks=FALSE )
|
||||
{
|
||||
/* If this is a QA forum we need to make sure we only have one best answer (at most) post-merge */
|
||||
if( $this->isQuestion() )
|
||||
{
|
||||
/* Does this topic already have a best answer? */
|
||||
if( $this->topic_answered_pid )
|
||||
{
|
||||
/* Then we need to make sure none of the items also has a best answer */
|
||||
foreach( $items as $item )
|
||||
{
|
||||
/* Reset best answer for this topic */
|
||||
if( $item->topic_answered_pid )
|
||||
{
|
||||
try
|
||||
{
|
||||
$post = \IPS\forums\Topic\Post::load( $item->topic_answered_pid );
|
||||
$post->post_bwoptions['best_answer'] = FALSE;
|
||||
$post->save();
|
||||
}
|
||||
catch( \OutOfRangeException $e ){}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* The topic doesn't have a best answer, but we still need to make sure we only have one best answer total post-merge */
|
||||
else
|
||||
{
|
||||
$bestAnswerSeen = FALSE;
|
||||
|
||||
foreach( $items as $item )
|
||||
{
|
||||
if( $item->topic_answered_pid )
|
||||
{
|
||||
/* Have we seen a best answer yet? If not, then we're ok. */
|
||||
if( $bestAnswerSeen === FALSE )
|
||||
{
|
||||
/* This topic had no best answer flag set, so set it now */
|
||||
$this->topic_answered_pid = $item->topic_answered_pid;
|
||||
$this->save();
|
||||
|
||||
$bestAnswerSeen = TRUE;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If we have though, reset any others */
|
||||
try
|
||||
{
|
||||
$post = \IPS\forums\Topic\Post::load( $item->topic_answered_pid );
|
||||
$post->post_bwoptions['best_answer'] = FALSE;
|
||||
$post->save();
|
||||
}
|
||||
catch( \OutOfRangeException $e ){}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parent::mergeIn( $items, $keepLinks );
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide
|
||||
*
|
||||
* @param \IPS\Member|NULL|FALSE $member The member doing the action (NULL for currently logged in member, FALSE for no member)
|
||||
* @param string $reason Reason
|
||||
* @return void
|
||||
*/
|
||||
public function hide( $member, $reason = NULL )
|
||||
{
|
||||
/* Hide any moved topic links that point to this topic */
|
||||
foreach( \IPS\Db::i()->select( '*', 'forums_topics', array( "state=? AND moved_to LIKE CONCAT( ?, '%' )", 'link', $this->tid . '&' ) ) as $link )
|
||||
{
|
||||
\IPS\forums\Topic::constructFromData( $link )->hide( $member, $reason );
|
||||
}
|
||||
|
||||
return parent::hide( $member, $reason );
|
||||
}
|
||||
|
||||
/**
|
||||
* Unhide
|
||||
*
|
||||
* @param \IPS\Member|NULL|FALSE $member The member doing the action (NULL for currently logged in member, FALSE for no member)
|
||||
* @return void
|
||||
*/
|
||||
public function unhide( $member )
|
||||
{
|
||||
/* Unhide any moved topic links that point to this topic */
|
||||
foreach( \IPS\Db::i()->select( '*', 'forums_topics', array( "state=? AND moved_to LIKE CONCAT( ?, '%' )", 'link', $this->tid . '&' ) ) as $link )
|
||||
{
|
||||
\IPS\forums\Topic::constructFromData( $link )->unhide( $member );
|
||||
}
|
||||
|
||||
return parent::unhide( $member );
|
||||
}
|
||||
|
||||
/* !Saved Actions */
|
||||
|
||||
/**
|
||||
@@ -1052,14 +1210,14 @@ class _Topic extends \IPS\Content\Item implements
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* Or if we're a moderator */
|
||||
if
|
||||
(
|
||||
$member->modPermission( 'can_set_best_answer' )
|
||||
and
|
||||
(
|
||||
$member->modPermission( \IPS\forums\Forum::$modPerm ) === -1
|
||||
( $member->modPermission( \IPS\forums\Forum::$modPerm ) === TRUE or $member->modPermission( \IPS\forums\Forum::$modPerm ) === -1 )
|
||||
or
|
||||
(
|
||||
is_array( $member->modPermission( \IPS\forums\Forum::$modPerm ) )
|
||||
@@ -1217,6 +1375,27 @@ 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
|
||||
* @return string
|
||||
*/
|
||||
public static function _definiteArticle( array $containerData = NULL, \IPS\Lang $lang = NULL )
|
||||
{
|
||||
$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 );
|
||||
}
|
||||
else
|
||||
{
|
||||
return parent::_definiteArticle( $containerData, $lang );
|
||||
}
|
||||
}
|
||||
|
||||
/* !Sitemap */
|
||||
|
||||
/**
|
||||
@@ -1327,38 +1506,6 @@ class _Topic extends \IPS\Content\Item implements
|
||||
return parent::canEdit( $member );
|
||||
}
|
||||
|
||||
/**
|
||||
* Can pin?
|
||||
*
|
||||
* @param \IPS\Member|NULL $member The member to check for (NULL for currently logged in member)
|
||||
* @return bool
|
||||
*/
|
||||
public function canPin( $member=NULL )
|
||||
{
|
||||
if ( $this->isArchived() )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return parent::canPin( $member );
|
||||
}
|
||||
|
||||
/**
|
||||
* Can unpin?
|
||||
*
|
||||
* @param \IPS\Member|NULL $member The member to check for (NULL for currently logged in member)
|
||||
* @return bool
|
||||
*/
|
||||
public function canUnpin( $member=NULL )
|
||||
{
|
||||
if ( $this->isArchived() )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return parent::canUnpin( $member );
|
||||
}
|
||||
|
||||
/**
|
||||
* Can feature?
|
||||
*
|
||||
@@ -1663,4 +1810,67 @@ class _Topic extends \IPS\Content\Item implements
|
||||
|
||||
return $metaDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get output for API
|
||||
*
|
||||
* @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 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
|
||||
* @apiresponse \IPS\forums\Topic\Post lastPost The last post in the topic
|
||||
* @apiresponse \IPS\forums\Topic\Post bestAnswer The best answer, if this is a question and there is one
|
||||
* @apiresponse bool locked Topic is locked
|
||||
* @apiresponse bool hidden Topic is hidden
|
||||
* @apiresponse bool pinned Topic is pinned
|
||||
* @apiresponse bool featured Topic is featured
|
||||
* @apiresponse bool archived Topic is archived
|
||||
* @apiresponse \IPS\Poll poll Poll data, if there is one
|
||||
* @apiresponse string url URL
|
||||
* @apiresponse float rating Average Rating
|
||||
*/
|
||||
public function apiOutput()
|
||||
{
|
||||
$firstPost = $this->comments( 1, 0, 'date', 'asc' );
|
||||
$lastPost = $this->comments( 1, 0, 'date', 'desc' );
|
||||
$bestAnswer = $this->bestAnswer();
|
||||
return array(
|
||||
'id' => $this->tid,
|
||||
'title' => $this->title,
|
||||
'forum' => $this->container()->apiOutput(),
|
||||
'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,
|
||||
'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,
|
||||
'url' => (string) $this->url(),
|
||||
'rating' => $this->averageRating(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content
|
||||
*
|
||||
* @return string
|
||||
* @throws \BadMethodCallException
|
||||
* @note This is overridden for performance reasons - selecting a post by a PID is more efficient than select * from posts order by date desc limit 1
|
||||
*/
|
||||
public function content()
|
||||
{
|
||||
$firstComment = $this->firstComment();
|
||||
return $firstComment ? $firstComment->content() : '';
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user