Files
IPB/upload/admin/applications/forums/sources/classes/topics.php
T
2025-12-19 00:34:03 -08:00

1533 lines
47 KiB
PHP

<?php
/**
* <pre>
* Invision Power Services
* IP.Board v3.2.3
* Moderator actions
* Last Updated: $Date: 2011-10-11 11:50:01 -0400 (Tue, 11 Oct 2011) $
* </pre>
*
* @author $Author: bfarber $
* @copyright (c) 2001 - 2009 Invision Power Services, Inc.
* @license This is NULLED!
* @package IP.Board
* @subpackage Forums
* @link http://hatynka.in
* @version $Revision: 9597 $
*/
if ( ! defined( 'IN_IPB' ) )
{
print "<h1>Incorrect access</h1>You cannot access this file directly.";
exit();
}
class app_forums_classes_topics
{
/**#@+
* Registry Object Shortcuts
*
* @var object
*/
protected $registry;
protected $DB;
protected $settings;
protected $request;
protected $lang;
protected $member;
protected $memberData;
protected $cache;
protected $caches;
/**#@-*/
protected $topicData = array();
protected $_memberData = array();
protected $moderatorData = null;
protected $permissionsData = array();
protected $errorMessage = array();
protected $_parsedMembers = array();
private $_topicCache = array();
private $_countPosts = 0;
private $_countTopics = 0;
/**
* Return errors, instead of printing
*
* @var bool
*/
public $return = true;
/**
* Constructor
*
* @param object Registry reference
* @return @e void
*/
public function __construct( ipsRegistry $registry )
{
/* Make objects */
$this->registry = $registry;
$this->DB = $this->registry->DB();
$this->settings =& $this->registry->fetchSettings();
$this->request =& $this->registry->fetchRequest();
$this->lang = $this->registry->getClass('class_localization');
$this->member = $this->registry->member();
$this->memberData =& $this->registry->member()->fetchMemberData();
$this->cache = $this->registry->cache();
$this->caches =& $this->registry->cache()->fetchCaches();
/* Set default member */
$this->setMemberData( $this->memberData );
/* Check for class_forums */
if ( ! $this->registry->isClassLoaded( 'class_forums' ) )
{
$classToLoad = IPSLib::loadLibrary( IPSLib::getAppDir( 'forums' ) . "/sources/classes/forums/class_forums.php", 'class_forums', 'forums' );
$this->registry->setClass( 'class_forums', new $classToLoad( $registry ) );
$this->registry->strip_invisible = 0;
$this->registry->class_forums->forumsInit();
}
/* Load tagging stuff */
if ( ! $this->registry->isClassLoaded('tags') )
{
require_once( IPS_ROOT_PATH . 'sources/classes/tags/bootstrap.php' );/*noLibHook*/
$this->registry->setClass( 'tags', classes_tags_bootstrap::run( 'forums', 'topics' ) );
}
/* Unpack reputation @todo this isn't ideal here but it'll do for now */
if ( $this->settings['reputation_enabled'] )
{
/* Load the class */
$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/class_reputation_cache.php', 'classReputationCache' );
$this->registry->setClass( 'repCache', new $classToLoad() );
/* Update the filter? */
if( isset( $this->request['rep_filter'] ) AND $this->request['rep_filter'] == 'update' )
{
/**
* Check secure key
* @link http://community.invisionpower.com/tracker/issue-22078-i-can-edit-your-treshold
*/
if( $this->request['secure_key'] != $this->member->form_hash )
{
$this->registry->output->showError( 'usercp_forums_bad_key', 1021522 );
}
$_mem_cache = IPSMember::unpackMemberCache( $this->memberData['members_cache'] );
if( $this->request['rep_filter_set'] == '*' )
{
$_mem_cache['rep_filter'] = '*';
}
else
{
$_mem_cache['rep_filter'] = intval( $this->request['rep_filter_set'] );
}
IPSMember::packMemberCache( $this->memberData['member_id'], $_mem_cache );
$this->memberData['_members_cache'] = $_mem_cache;
}
else
{
$this->memberData['_members_cache'] = isset($this->memberData['members_cache']) ? IPSMember::unpackMemberCache( $this->memberData['members_cache'] ) : array();
}
}
}
/**
* Auto populate the data and that. Populated topicData and forumData. Also does rudimentary access checks
*
* @param mixed $topic Array of topic data, or single topic id
* @param bool $return Return errors instead of printing
* @return @e void
*/
public function autoPopulate( $topic="", $return=true )
{
/* @todo Remove other calls to request['t'] - intvalled here because it's called in a million places */
$this->request['t'] = intval( $this->request['t'] );
$this->return = $return;
/* Sanitize */
$topicId = intval( $this->request['t'] );
if ( ! is_array( $topic ) )
{
if ( ! $topicId )
{
throw new Exception( 'EX_topics_no_tid' );
}
/* May have loaded topic data previously */
if ( empty( $this->registry->class_forums->topic_cache['tid'] ) )
{
$this->DB->build( array( 'select' => '*', 'from' => 'topics', 'where' => 'tid=' . $topicId ) );
$this->DB->execute();
$this->setTopicData( $this->DB->fetch() );
}
else
{
$this->setTopicData( $this->registry->class_forums->topic_cache );
}
}
else
{
$this->setTopicData( $topic );
}
$this->topicData['title'] = IPSText::stripAttachTag( $this->topicData['title'] );
/* @todo Remove other calls to request['f'] - intvalled here because it's called in a million places */
$this->request['f'] = intval( $this->topicData['forum_id'] );
/* Check to see if stuff is stuffable */
$result = $this->canView();
if ( $result === false )
{
throw new Exception( $this->getErrorMessage() );
}
if ( ! empty( $this->topicData['tag_cache_key'] ) )
{
$this->topicData['tags'] = $this->registry->tags->formatCacheJoinData( $this->topicData );
}
/* Set up */
$this->topicData = $this->setUpTopic( $this->topicData );
}
/**
* Sets up a topic
* @param array $topic If not passed then $this->topicData is used
* @return array
*/
public function setUpTopic( $topic=array() )
{
$topic = ( count( $topic ) ) ? $topic : $this->topicData;
/* Error out if the topic is not approved or soft deleted */
$approved = $this->registry->class_forums->fetchHiddenTopicType( $topic );
/* Deleted topic? */
$topic['_isDeleted'] = ( $approved == 'sdelete' || $approved == 'pdelete' ) ? true : false;
$topic['_isHidden'] = ( $approved == 'hidden' ) ? true : false;
/* Posts per day restrictions? */
$topic['_ppd_ok'] = $this->registry->getClass('class_forums')->checkGroupPostPerDay( $this->memberData, TRUE );
/* Got any unread posts? */
$topic['hasUnreadPosts'] = ( $this->registry->classItemMarking->isRead( array( 'forumID' => $topic['forum_id'], 'itemID' => $topic['tid'], 'itemLastUpdate' => $topic['last_post'] ), 'forums' ) ) ? false : true;
return $topic;
}
/**
* Load and return a topic by ID.
* @param intval $tid
* @return array Topic Data
*/
public function getTopicById( $tid )
{
if ( empty( $this->_topicCache[ $tid ] ) OR ! is_array( $this->_topicCache[ $tid ] ) )
{
$this->_topicCache[ $tid ] = $this->DB->buildAndFetch( array( 'select' => 't.*',
'from' => array( 'topics' => 't' ),
'where' => "t.tid=" . intval( $tid ),
'add_join' => array( array( 'select' => 'p.pid, p.start_date as poll_start_date, p.choices, p.starter_id as poll_starter_id, p.votes, p.poll_question, p.poll_only, p.poll_view_voters',
'type' => 'left',
'from' => array( 'polls' => 'p' ),
'where' => 'p.tid=t.tid' ) ) ) );
}
return $this->_topicCache[ $tid ];
}
/**
* Load and return a post by ID.
* @param intval $pid
* @return array Post Data
*/
public function getPostById( $pid )
{
$post = $this->DB->buildAndFetch( array( 'select' => 'p.*',
'from' => array( 'posts' => 'p' ),
'where' => "p.pid=" . intval( $pid ),
'add_join' => array( array( 'select' => 't.*, t.title as topic_title',
'type' => 'left',
'from' => array( 'topics' => 't' ),
'where' => 'p.topic_id=t.tid' ),
array( 'select' => 'm.*',
'type' => 'left',
'from' => array( 'members' => 'm' ),
'where' => 'm.member_id=p.author_id' ),
array( 'select' => 'pp.*',
'type' => 'left',
'from' => array( 'profile_portal' => 'pp' ),
'where' => 'pp.pp_member_id=p.author_id' ) ) ) );
if ( $post['author_id'] && $post['member_group_id'] )
{
$group = $this->caches['group_cache'][ $post['member_group_id'] ];
if ( is_array( $group ) && count( $group ) )
{
$post = array_merge( $group, $post );
}
}
return $post;
}
/**
* getTopics
* Fetches topics based on different critera
* @param array Filters (see below for specifics)
* @return array
*
* FILTERS:
* forumId Get posts matching the (array) topic ids, (int) forum ID
* memberData Set memberData (this->memberData is used otherwise)
* onlyViewable Set whether this member can view them or not. (default is true ) NOTE: Will not check to see if parent topic is viewable!
* onlyVisible Set whether to skip unapproved posts where permission allows (default is true)
* topicType array of 'sdelete', 'visible', 'hidden', 'pdeleted' (if you specify these, permission checks are NOT performed)
* sortOrder Sort key (date, pid, etc)
* sortDirection asc/desc
* skipForumCheck Skips the forum ID IN list check to ensure you have access to view (good for when using perms elsewhere)
* tidIsGreater Where PID is greater than x
* dateIsGreater Where DATE is greater than UNIX
* getFirstPost Return the first post of the topic
* parse Parses the first post of the topic
* limit, offset Limit the amount of results in the returned query
* getCount fetch count without limit
*/
public function getTopics( $filters )
{
/* init */
$filters = $this->_setTopicFilters( $filters );
$limit = null;
$topics = array();
$where = array();
$memberData = ( ! empty( $filters['memberData'] ) && is_array( $filters['memberData'] ) ) ? $filters['memberData'] : $this->memberData;
/* Forum Ids */
if ( ! empty( $filters['forumId'] ) )
{
$filters['forumId'] = ( ! is_array( $filters['forumId'] ) ) ? array( $filters['forumId'] ) : $filters['forumId'];
$where[] = "t.forum_id IN (" . implode( ',', $filters['forumId'] ) . ")";
}
/* PID is greater */
if ( ! empty( $filters['tidIsGreater'] ) )
{
$where[] = "t.tid > " . intval( $filters['tidIsGreater'] );
}
/* PID is greater */
if ( ! empty( $filters['dateIsGreater'] ) )
{
$where[] = "t.start_date > " . intval( $filters['dateIsGreater'] );
}
/* Visible / specific filters */
if ( ! empty( $filters['topicType'] ) && is_array( $filters['topicType'] ) )
{
$where[] = $this->registry->class_forums->fetchTopicHiddenQuery( $filters['topicType'], 't.' );
}
else
{
if ( isset( $filters['onlyViewable'] ) && $filters['onlyViewable'] === true && empty( $filters['onlyVisible'] ) )
{
$_perms = array( 'visible' );
if ( $this->registry->getClass('class_forums')->canSeeSoftDeletedTopics( false ) )
{
$_perms[] = 'sdelete';
}
if ( $this->registry->getClass('class_forums')->canQueuePosts( false ) )
{
$_perms[] = 'hidden';
}
$where[] = $this->registry->class_forums->fetchTopicHiddenQuery( $_perms, 't.' );
}
else
{
/* Show visible only */
$where[] = $this->registry->class_forums->fetchTopicHiddenQuery( array( 'visible' ), 't.' );
}
}
/* Forum ID check? */
if ( ( isset( $filters['onlyViewable'] ) && $filters['onlyViewable'] === true ) && ( empty($filters['skipForumCheck']) OR $filters['skipForumCheck'] === false ) )
{
if ( empty( $filters['forumId'] ) )
{
$forumIds = $this->registry->class_forums->fetchSearchableForumIds( $memberData['member_id'] );
if ( ! count( $forumIds ) )
{
return $where;
}
$where[] = "t.forum_id IN (" . implode( ",", $forumIds ) . ")";
}
}
/* Did we want a count also? */
if ( ! empty( $filters['getCount'] ) )
{
$count = $this->DB->buildAndFetch( array( 'select' => 'COUNT(*) as topics',
'from' => array( 'topics' => 't' ),
'where' => implode( ' AND ', $where ) ) );
$this->_countTopics = $count['topics'];
}
/* Offset, limit */
if ( isset( $filters['offset'] ) OR isset( $filters['limit'] ) )
{
if ( $filters['offset'] > 0 || $filters['limit'] > 0 )
{
$limit = array( intval( $filters['offset'] ), intval( $filters['limit'] ) );
}
}
/* Order */
if ( ! empty( $filters['sortField'] ) )
{
$order = 't.' . $filters['sortField'];
if ( isset( $filters['sortOrder'] ) )
{
$order .= ' ' . $filters['sortOrder'];
}
}
/* Set joins */
$joins = array( array( 'select' => 'm.*',
'type' => 'left',
'from' => array( 'members' => 'm' ),
'where' => 'm.member_id=t.starter_id' ),
array( 'select' => 'pp.*',
'type' => 'left',
'from' => array( 'profile_portal' => 'pp' ),
'where' => 'pp.pp_member_id=t.starter_id' ) );
/* Fetching first post ? */
if ( ! empty( $filters['getFirstPost'] ) )
{
$joins[] = array( 'select' => 'p.*',
'from' => array( 'posts' => 'p' ),
'where' => 'p.pid=t.topic_firstpost',
'type' => 'left' );
}
/* Fetch them */
$this->DB->build( array( 'select' => 't.*, t.title as real_title, t.posts as real_posts',
'from' => array( 'topics' => 't' ),
'where' => implode( ' AND ', $where ),
'limit' => $limit ? $limit : '',
'order' => $order ? $order : 't.tid asc',
'add_join' => $joins ) );
$o = $this->DB->execute();
while( $topic = $this->DB->fetch( $o ) )
{
if ( $topic['starter_id'] && $topic['member_group_id'] )
{
$group = $this->caches['group_cache'][ $topic['member_group_id'] ];
if ( is_array( $group ) && count( $group ) )
{
$topic = array_merge( $group, $topic );
}
}
/* Get the first post? */
if ( ! empty( $filters['getFirstPost'] ) )
{
if ( ! empty( $filters['parse'] ) )
{
$this->setTopicData( $topic );
$this->setPermissionData();
$topic['firstPostParsed'] = $this->parsePost( $topic );
}
$topic['_postType'] = $this->registry->class_forums->fetchHiddenType( $topic );
}
/* Member title overwrites topic */
$topic['title'] = $topic['real_title'];
$topic['posts'] = $topic['real_posts'];
$topic['_topicType'] = $this->registry->class_forums->fetchHiddenTopicType( $topic );
$topics[ $topic['tid'] ] = $topic;
}
return $topics;
}
/**
* getTopicsCount
* Fetches number of topics based on different critera (useful for pagination when combined with getPosts())
* @return int
*/
public function getTopicsCount()
{
return $this->_countTopics;
}
/**
* getPosts
* Fetches posts based on different critera
* @param array Filters (see below for specifics)
* @return array
*
* FILTERS:
* topicId Get posts matching the (array) topic ids, (int) topic ID
* forumId Get posts matching the (array) topic ids, (int) forum ID
* postId Get posts matching the (array) post ids, (int) post id
* memberData Set memberData (this->memberData is used otherwise)
* onlyViewable Set whether this member can view them or not. (default is true ) NOTE: Will not check to see if parent topic is viewable!
* onlyVisible Set whether to skip unapproved posts where permission allows (default is true)
* postType array of 'sdelete', 'visible', 'hidden', 'pdeleted' (if you specify these, permission checks are NOT performed)
* sortField Sort key (date, pid, etc)
* sortOrder asc/desc
* pidIsGreater Where PID is greater than x
* dateIsGreater Where DATE is greater than UNIX
* skipForumCheck Skips the forum ID IN list check to ensure you have access to view (good for when using perms elsewhere)
* parse Parses post content
* limit, offset Limit the amount of results in the returned query
* getCount fetch count without limit
*
*/
public function getPosts( $filters )
{
/* init */
$filters = $this->_setPostFilters( $filters );
$limit = null;
$posts = array();
$where = array();
$memberData = ( ! empty( $filters['memberData'] ) && is_array( $filters['memberData'] ) ) ? $filters['memberData'] : $this->memberData;
/* Posts */
if ( ! empty( $filters['postId'] ) )
{
$filters['postId'] = ( ! is_array( $filters['postId'] ) ) ? array( $filters['postId'] ) : $filters['postId'];
$where[] = "p.pid IN (" . implode( ',',$filters['postId'] ) . ")";
}
/* Topics */
if ( ! empty( $filters['topicId'] ) )
{
$filters['topicId'] = ( ! is_array( $filters['topicId'] ) ) ? array( $filters['topicId'] ) : $filters['topicId'];
$where[] = "p.topic_id IN (" . implode( ',',$filters['topicId'] ) . ")";
}
/* Forum Ids */
if ( ! empty( $filters['forumId'] ) )
{
$filters['forumId'] = ( ! is_array( $filters['forumId'] ) ) ? array( $filters['forumId'] ) : $filters['forumId'];
$where[] = "t.forum_id IN (" . implode( ',', $filters['forumId'] ) . ")";
}
/* PID is greater */
if ( ! empty( $filters['pidIsGreater'] ) )
{
$where[] = "p.pid > " . intval( $filters['pidIsGreater'] );
}
/* PID is less */
if ( ! empty( $filters['pidIsLess'] ) )
{
$where[] = "p.pid < " . intval( $filters['pidIsLess'] );
}
/* Date is greater */
if ( ! empty( $filters['dateIsGreater'] ) )
{
$where[] = "p.post_date > " . intval( $filters['dateIsGreater'] );
}
/* Visible / specific filters */
if ( ! empty( $filters['postType'] ) && is_array( $filters['postType'] ) )
{
$where[] = $this->registry->class_forums->fetchPostHiddenQuery( $filters['postType'], 'p.' );
}
else
{
if ( isset( $filters['onlyViewable'] ) && $filters['onlyViewable'] === true && empty( $filters['onlyVisible'] ) )
{
$_perms = array( 'visible' );
if ( $this->registry->getClass('class_forums')->canSeeSoftDeletedPosts( false ) )
{
$_perms[] = 'sdelete';
}
if ( $this->registry->getClass('class_forums')->canQueuePosts( false ) )
{
$_perms[] = 'hidden';
}
$where[] = $this->registry->class_forums->fetchPostHiddenQuery( $_perms, 'p.' );
}
else
{
/* Show visible only */
$where[] = $this->registry->class_forums->fetchPostHiddenQuery( array( 'visible' ), 'p.' );
}
}
/* Forum ID check? */
if ( ( isset( $filters['onlyViewable'] ) && $filters['onlyViewable'] === true ) && ( empty($filters['skipForumCheck']) OR $filters['skipForumCheck'] === false ) )
{
if ( empty( $filters['forumId'] ) )
{
$forumIds = $this->registry->class_forums->fetchSearchableForumIds( $memberData['member_id'] );
if ( ! count( $forumIds ) )
{
return $where;
}
$where[] = "t.forum_id IN (" . implode( ",", $forumIds ) . ")";
}
}
/* Did we want a count also? */
if ( ! empty( $filters['getCount'] ) )
{
$count = $this->DB->buildAndFetch( array( 'select' => 'COUNT(*) as posts',
'from' => array( 'posts' => 'p' ),
'where' => implode( ' AND ', $where ),
'add_join' => array( array( 'type' => 'left',
'from' => array( 'topics' => 't' ),
'where' => 'p.topic_id=t.tid' ) ) ) );
$this->_countPosts = $count['posts'];
}
/* Offset, limit */
if ( isset( $filters['offset'] ) OR isset( $filters['limit'] ) )
{
if ( $filters['offset'] > 0 || $filters['limit'] > 0 )
{
$limit = array( intval( $filters['offset'] ), intval( $filters['limit'] ) );
}
}
/* Order */
if ( ! empty( $filters['sortField'] ) )
{
$order = 'p.' . $filters['sortField'];
if ( isset( $filters['sortOrder'] ) )
{
$order .= ' ' . $filters['sortOrder'];
}
}
$_joins = array( array( 'select' => 't.*, t.title as real_title',
'type' => 'left',
'from' => array( 'topics' => 't' ),
'where' => 'p.topic_id=t.tid' ),
array( 'select' => 'm.*, m.title as member_title',
'type' => 'left',
'from' => array( 'members' => 'm' ),
'where' => 'm.member_id=p.author_id' ),
array( 'select' => 'pp.*',
'type' => 'left',
'from' => array( 'profile_portal' => 'pp' ),
'where' => 'pp.pp_member_id=p.author_id' ) );
/* Add custom fields join? */
if( $this->settings['custom_profile_topic'] == 1 )
{
$_joins[] = array(
'select' => 'pc.*',
'from' => array( 'pfields_content' => 'pc' ),
'where' => 'pc.member_id=p.author_id',
'type' => 'left'
);
}
/* Fetch them */
$this->DB->build( array( 'select' => 'p.*',
'from' => array( 'posts' => 'p' ),
'where' => implode( ' AND ', $where ),
'limit' => $limit ? $limit : '',
'order' => $order ? $order : 'p.pid asc',
'add_join' => $_joins ) );
$o = $this->DB->execute();
while( $post = $this->DB->fetch( $o ) )
{
if ( $post['author_id'] && $post['member_group_id'] )
{
$group = $this->caches['group_cache'][ $post['member_group_id'] ];
if ( is_array( $group ) && count( $group ) )
{
$post = array_merge( $group, $post );
}
}
/* Member title overwrites topic */
$post['title'] = $post['real_title'];
$pid = $post['pid'];
if ( ! empty( $filters['parse'] ) )
{
$this->setTopicData( $post );
$this->setPermissionData();
$post = $this->parsePost( $post );
}
$post['_postType'] = $this->registry->class_forums->fetchHiddenType( $post );
$post['_topicType'] = $this->registry->class_forums->fetchHiddenTopicType( $post );
$posts[ $pid ] = $post;
}
return $posts;
}
/**
* getPostsCount
* Fetches number of posts based on different critera (useful for pagination when combined with getPosts())
* @return int
*/
public function getPostsCount()
{
return $this->_countPosts;
}
/**
* @return the $errorMessage
*/
public function getErrorMessage()
{
return $this->errorMessage;
}
/**
* @param field_type $errorMessage
*/
public function setErrorMessage( $errorMessage )
{
$this->errorMessage = $errorMessage;
}
/**
* Sets up the permissions for this class
*
* @param string key
* @param string value
*/
public function setPermissionData( $k='', $v='' )
{
$this->registry->getClass('class_forums')->setMemberData( $this->getMemberData() );
if ( empty( $k ) and empty( $v ) )
{
/* Auto set up */
$this->permissionsData['softDelete'] = $this->registry->getClass('class_forums')->canSoftDeletePosts( $this->topicData['forum_id'], array() );
$this->permissionsData['softDeleteRestore'] = $this->registry->getClass('class_forums')->can_Un_SoftDeletePosts( $this->topicData['forum_id'] );
$this->permissionsData['softDeleteSee'] = $this->registry->getClass('class_forums')->canSeeSoftDeletedPosts( $this->topicData['forum_id'] );
$this->permissionsData['softDeleteReason'] = $this->registry->getClass('class_forums')->canSeeSoftDeleteReason( $this->topicData['forum_id'] );
$this->permissionsData['softDeleteContent'] = $this->registry->getClass('class_forums')->canSeeSoftDeleteContent( $this->topicData['forum_id'] );
$this->permissionsData['TopicSoftDelete'] = $this->registry->getClass('class_forums')->canSoftDeleteTopics( $this->topicData['forum_id'], $this->topicData );
$this->permissionsData['TopicSoftDeleteRestore'] = $this->registry->getClass('class_forums')->can_Un_SoftDeleteTopics( $this->topicData['forum_id'] );
$this->permissionsData['TopicSoftDeleteSee'] = $this->registry->getClass('class_forums')->canSeeSoftDeletedTopics( $this->topicData['forum_id'] );
}
else if ( isset( $k['tid'] ) && isset( $k['forum_id'] ) )
{
/* Auto set up */
$this->permissionsData['softDelete'] = $this->registry->getClass('class_forums')->canSoftDeletePosts( $k['forum_id'], array() );
$this->permissionsData['softDeleteRestore'] = $this->registry->getClass('class_forums')->can_Un_SoftDeletePosts( $k['forum_id'] );
$this->permissionsData['softDeleteSee'] = $this->registry->getClass('class_forums')->canSeeSoftDeletedPosts( $k['forum_id'] );
$this->permissionsData['softDeleteReason'] = $this->registry->getClass('class_forums')->canSeeSoftDeleteReason( $k['forum_id'] );
$this->permissionsData['softDeleteContent'] = $this->registry->getClass('class_forums')->canSeeSoftDeleteContent( $k['forum_id'] );
$this->permissionsData['TopicSoftDelete'] = $this->registry->getClass('class_forums')->canSoftDeleteTopics( $k['forum_id'], $k );
$this->permissionsData['TopicSoftDeleteRestore'] = $this->registry->getClass('class_forums')->can_Un_SoftDeleteTopics( $k['forum_id'] );
$this->permissionsData['TopicSoftDeleteSee'] = $this->registry->getClass('class_forums')->canSeeSoftDeletedTopics( $k['forum_id'] );
}
else if ( is_array( $k ) )
{
$this->permissionsData = $k;
}
else if ( ! empty( $k ) )
{
$this->permissionsData[ $k ] = $v;
}
}
/**
* @return the $permissionData
*/
public function getPermissionData( $k='' )
{
return ( ! empty( $k ) ) ? $this->permissionsData[ $k ] : $this->permissionsData;
}
/**
* @return the $topicData
*/
public function getTopicData( $k='' )
{
return ( ! empty( $k ) ) ? $this->topicData[ $k ] : $this->topicData;
}
/**
* @param string key
* @param string value
*/
public function setTopicData( $k, $v='' )
{
if ( is_integer( $k ) )
{
$this->topicData = $this->getTopicById( $k );
}
else if ( is_array( $k ) )
{
$this->topicData = $k;
}
else if ( ! empty( $k ) )
{
$this->topicData[ $k ] = $v;
}
}
/**
* @return the $_memberData
*/
public function getMemberData( $k='' )
{
return ( ! empty( $k ) ) ? $this->_memberData[ $k ] : $this->_memberData;
}
/**
* @param string key
* @param string value
*/
public function setMemberData( $k, $v='' )
{
if ( is_integer( $k ) )
{
$this->_memberData = IPSMember::load( $k );
}
else if ( is_array( $k ) )
{
$this->_memberData = $k;
}
else if ( ! empty( $k ) )
{
$this->_memberData[ $k ] = $v;
}
}
/**
* Loads and fetches the moderator data
*/
public function getModeratorData()
{
$forumData = $this->registry->getClass('class_forums')->getForumbyId( $this->topicData['forum_id'] );
if ( $this->moderatorData === null AND $this->memberData['member_id'] AND ! $this->memberData['g_is_supmod'] )
{
$other_mgroups = array();
$_mgroup_others = IPSText::cleanPermString( $this->memberData['mgroup_others'] );
if( $_mgroup_others )
{
$other_mgroups = explode( ",", $_mgroup_others );
}
$other_mgroups[] = $this->memberData['member_group_id'];
$member_group_ids = implode( ",", $other_mgroups );
$this->moderatorData = $this->DB->buildAndFetch( array( 'select' => '*',
'from' => 'moderators',
'where' => "forum_id LIKE '%,{$forumData['id']},%' AND (member_id={$this->memberData['member_id']} OR (is_group=1 AND group_id IN({$member_group_ids})))" ) );
$this->moderatorData = ( is_array( $this->moderatorData ) ) ? $this->moderatorData : array();
}
return $this->moderatorData;
}
/**
* Fetch the next unread topicID
* @param array or null $topicData
*/
public function getNextUnreadTopicId( $topicData=false)
{
$topicData = ( ! is_array( $topicData ) ) ? $this->getTopicData() : $topicData;
$readItems = $this->registry->classItemMarking->fetchReadIds( array( 'forumID' => $topicData['forum_id'] ) );
$lastMarked = $this->registry->classItemMarking->fetchTimeLastMarked( array( 'forumID' => $topicData['forum_id'] ) );
$approved = $this->memberData['is_mod'] ? ' AND ' . $this->registry->getClass('class_forums')->fetchTopicHiddenQuery( array( 'visible', 'hidden' ), '' ) . ' ' : ' AND ' . $this->registry->getClass('class_forums')->fetchTopicHiddenQuery( array( 'visible' ), '' ) . ' ';
/* Add in this topic ID to be sure */
$readItems[ $topicData['tid'] ] = $topicData['tid'];
/* First, attempt to fetch a topic older than this one */
$tid = $this->DB->buildAndFetch( array( 'select' => 'tid',
'from' => 'topics',
'where' => "forum_id=" . intval( $topicData['forum_id'] ) . " {$approved} AND tid NOT IN(".implode(",",array_values($readItems)).") AND last_post < " . intval($topicData['last_post']) . " AND last_post > " . $lastMarked . " AND state != 'link'",
'order' => 'last_post DESC',
'limit' => array( 0, 1 ) ) );
if ( ! $tid )
{
$tid = $this->DB->buildAndFetch( array( 'select' => 'tid',
'from' => 'topics',
'where' => "forum_id=" . intval( $topicData['forum_id'] ) . " {$approved} AND tid NOT IN(".implode(",",array_values($readItems)).") AND last_post > " . intval($topicData['last_post']) . " AND state != 'link'",
'order' => 'last_post DESC',
'limit' => array( 0, 1 ) ) );
}
return intval( $tid['tid'] );
}
/**
* Determines if we're on the last page or not ...
* @param array or null $topicData
* @return boolean
*/
public function isOnLastPage( $topicData=false )
{
$topicData = ( ! is_array( $topicData ) ) ? $this->getTopicData() : $topicData;
$st = intval( $this->request['st'] );
$perPage = $this->settings['display_max_posts'];
$posts = intval( $topicData['posts'] ) + 1;
if ( $posts <= $perPage )
{
return true;
}
$maxSt = ( ceil( $posts / $perPage ) - 1 ) * $perPage;
if ( $st == $maxSt )
{
return true;
}
return false;
}
/**
* Return whether we can see the IP address or not
*
* @return bool
*/
public function canSeeIp()
{
$moderator = $this->getModeratorData();
if ( ! $this->memberData['g_is_supmod'] && empty( $moderator['view_ip'] ) )
{
return FALSE;
}
else
{
return TRUE;
}
}
/**
* Can view or cannot view
* @param mixed Nothing or inline topicData
* @param mixed Nothing or inline memberData
*/
public function canView( $topicData=false, $memberData=false )
{
if ( is_array( $topicData ) && isset( $topicData['tid'] ) )
{
$this->setTopicData( $topicData );
}
if ( is_array( $memberData ) && isset( $memberData['member_id'] ) )
{
$this->setMemberData( $memberData );
}
/* get member/topic data */
$memberData = $this->getMemberData();
$topicData = $this->getTopicData();
/* Basic checks */
if ( ! $topicData['tid'] )
{
$this->setErrorMessage( 'EX_topics_no_tid' );
return false;
}
if ( ! $this->registry->getClass('class_forums')->getForumbyId( $topicData['forum_id'] ) )
{
$this->setErrorMessage( 'EX_topics_no_fid' );
return false;
}
/* Set up member ID */
$this->registry->class_forums->setMemberData( $memberData );
/* Test */
if ( ! $this->registry->class_forums->forumsCheckAccess( $topicData['forum_id'], 1, 'topic', $topicData, $this->return ) )
{
$this->setErrorMessage( 'EX_topic_not_approved' );
return false;
}
/* Error out if the topic is not approved or soft deleted */
$approved = $this->registry->class_forums->fetchHiddenTopicType( $topicData );
if ( ! $this->registry->class_forums->canQueuePosts( $topicData['forum_id'] ) )
{
if ( $approved == 'hidden' )
{
$this->setErrorMessage( 'EX_topic_not_approved' );
return false;
}
}
/* Set up permissions */
if ( ! count( $this->permissionsData ) )
{
$this->setPermissionData();
}
/* Soft deleted? */
if ( $approved == 'sdelete' AND ! $this->permissionsData['softDeleteContent'] )
{
$this->setErrorMessage( 'EX_topic_not_approved' );
return false;
}
/* Hard deleted? */
if ( $approved == 'pdelete' AND ! $memberData['g_is_supmod'] )
{
$this->setErrorMessage( 'EX_topic_not_approved' );
return false;
}
return true;
}
/**
* Return whether or not we have permission to delete the post (_getDeleteButtonData)
*
* @return bool
*/
public function canDeletePost( $poster )
{
$moderator = $this->getModeratorData();
if ( ! $this->memberData['member_id'] )
{
return FALSE;
}
if ( $this->memberData['g_is_supmod'] OR $moderator['delete_post'] )
{
return TRUE;
}
if ( $poster['member_id'] == $this->memberData['member_id'] and ( $this->memberData['g_delete_own_posts'] ) )
{
return TRUE;
}
return FALSE;
}
/**
* Return whether or not we can edit this post
*
* @param array Array of post data
* @return bool
*/
public function canEditPost( $poster=array() )
{
$moderator = $this->getModeratorData();
$topicData = $this->getTopicData();
if ( $this->memberData['member_id'] == "" or $this->memberData['member_id'] == 0 )
{
return FALSE;
}
if ( $this->memberData['g_is_supmod'] )
{
return TRUE;
}
if ( $moderator['edit_post'] )
{
return TRUE;
}
if ( ( $topicData['state'] != 'open' ) and ( ! $this->memberData['g_is_supmod'] AND ! $moderator['edit_post'] ) )
{
if ( $this->memberData['g_post_closed'] != 1 )
{
return FALSE;
}
}
if ( $poster['member_id'] == $this->memberData['member_id'] and ($this->memberData['g_edit_posts']) )
{
// Have we set a time limit?
if ($this->memberData['g_edit_cutoff'] > 0)
{
if ( $poster['post_date'] > ( time() - ( intval($this->memberData['g_edit_cutoff']) * 60 ) ) )
{
return TRUE;
}
else
{
return FALSE;
}
}
else
{
return TRUE;
}
}
return FALSE;
}
/**
* Can make a reply to this topic
* @return string locked / moved / no_reply / reply
*/
public function getReplyStatus()
{
/* Init */
$topicData = $this->getTopicData();
$forumData = $this->registry->getClass('class_forums')->getForumbyId( $topicData['forum_id'] );
$status = '';
if ($topicData['state'] == 'closed' OR ( $topicData['poll_state'] AND $topicData['poll_only'] ) )
{
/* Do we have the ability to post in closed topics or is this a poll only?*/
if ( $this->memberData['g_post_closed'] == 1)
{
$status = 'locked';
}
else
{
$status = "locked";
}
}
else
{
if ( $topicData['state'] == 'moved' )
{
$status = "moved";
}
else if ( $topicData['_isDeleted'] )
{
$status = 'no_reply';
}
else
{
if ( $forumData['min_posts_post'] && $forumData['min_posts_post'] > $this->memberData['posts'] && !$this->memberData['g_is_supmod'] )
{
$status = "locked";
}
else
{
if ( $this->memberData['member_id'] AND ( ( ( $this->memberData['member_id'] == $topicData['starter_id'] ) AND ! $this->memberData['g_reply_own_topics'] ) OR ( ( $this->memberData['member_id'] != $topicData['starter_id'] ) AND ! $this->memberData['g_reply_other_topics'] ) ) )
{
$status = "no_reply";
}
else if ( $this->registry->permissions->check( 'reply', $forumData ) == TRUE )
{
$status = "reply";
}
else
{
$status = "no_reply";
}
}
}
}
return $status;
}
/**
* Get multimoderation data
*
* @return array
*/
public function getMultiModerationData()
{
$return_array = array();
$mm_array = $this->registry->class_forums->getMultimod( $this->topicData['forum_id'] );
//-----------------------------------------
// Print and show
//-----------------------------------------
if ( is_array( $mm_array ) and count( $mm_array ) )
{
foreach( $mm_array as $m )
{
$return_array[] = $m;
}
}
return $return_array;
}
/**
* Parses and caches members
* @param array $member
*/
public function parseMember( array $member )
{
/* Not cached? */
if ( ! isset( $this->_parsedMembers[ $member['author_id'] ] ) )
{
$member['member_id'] = !empty($member['mid']) ? $member['mid'] : $member['member_id'];
/* Unset any post data */
unset( $member['pid'], $member['append_edit'], $member['edit_time'], $member['use_sig'], $member['use_emo'], $member['post_edit_reason'],
$member['post_date'], $member['icon_id'], $member['post'], $member['queued'], $member['topic_id'], $member['post_htmlstate'],
$member['post_title'], $member['new_topic'], $member['edit_name'], $member['post_key'], $member['title'] );
/* Do we have a cached signature? */
if ( isset( $member['cache_content_sig'] ) )
{
$member['cache_content'] = $member['cache_content_sig'];
$member['cache_updated'] = $member['cache_updated_sig'];
}
else
{
unset( $member['cache_content'], $member['cache_updated'] );
}
/**
* Add group data and setup secondary groups as well
* @link http://community.invisionpower.com/tracker/issue-29142-can-post-html-in-secondary-groups/
*/
if( !empty($this->caches['group_cache'][ $member['member_group_id'] ]) )
{
$member = array_merge( $member, $this->caches['group_cache'][ $member['member_group_id'] ] );
$member = $this->member->setUpSecondaryGroups( $member );
}
$member = IPSMember::buildDisplayData( $member, array( 'signature' => 1, 'customFields' => 1, 'warn' => 1, 'checkFormat' => 1, 'cfLocation' => 'topic', 'photoTagSize' => array( 'thumb', 'small' ) ) );
//-----------------------------------------
// Add it to the cached list
//-----------------------------------------
$this->_parsedMembers[ $member['author_id'] ] = $member;
}
return $this->_parsedMembers[ $member['author_id'] ];
}
/**
* Builds an array of post data for output
*
* @param array $row Array of post data
* @return array
*/
public function parsePost( array $post )
{
/* Init */
$topicData = $this->getTopicData();
$forumData = $this->registry->getClass('class_forums')->getForumById( $topicData['forum_id'] );
$permissionData = $this->getPermissionData();
/* Start memory debug */
$_NOW = IPSDebug::getMemoryDebugFlag();
$poster = array();
/* Bitwise options */
$_tmp = IPSBWOptions::thaw( $post['post_bwoptions'], 'post', 'forums' );
if ( count( $_tmp ) )
{
foreach( $_tmp as $k => $v )
{
$post[ $k ] = $v;
}
}
/* Is this a member? */
if ( $post['author_id'] != 0 )
{
$poster = $this->parseMember( $post );
}
else
{
/* Sort out guest */
$post['author_name'] = $this->settings['guest_name_pre'] . $post['author_name'] . $this->settings['guest_name_suf'];
$poster = IPSMember::setUpGuest( $post['author_name'] );
$poster['members_display_name'] = $post['author_name'];
$poster['_members_display_name'] = $post['author_name'];
$poster['custom_fields'] = "";
$poster['warn_img'] = "";
$poster = IPSMember::buildProfilePhoto( $poster );
}
/* Memory debug */
IPSDebug::setMemoryDebugFlag( "PID: ".$post['pid'] . " - Member Parsed", $_NOW );
/* Update permission */
$this->registry->getClass('class_forums')->setMemberData( $this->getMemberData() );
$permissionData['softDelete'] = $this->registry->getClass('class_forums')->canSoftDeletePosts( $topicData['forum_id'], $post );
/* Soft delete */
$post['_softDelete'] = ( $post['pid'] != $topicData['topic_firstpost'] ) ? $permissionData['softDelete'] : FALSE;
$post['_softDeleteRestore'] = $permissionData['softDeleteRestore'];
$post['_softDeleteSee'] = $permissionData['softDeleteSee'];
$post['_softDeleteReason'] = $permissionData['softDeleteReason'];
$post['_softDeleteContent'] = $permissionData['softDeleteContent'];
$post['_isVisible'] = ( $this->registry->getClass('class_forums')->fetchHiddenType( $post ) == 'visible' ) ? true : false;
$post['_isHidden'] = ( $this->registry->getClass('class_forums')->fetchHiddenType( $post ) == 'hidden' ) ? true : false;
$post['_isDeleted'] = ( $this->registry->getClass('class_forums')->fetchHiddenType( $post ) == 'sdelete' ) ? true : false;
/* Queued */
if ( $topicData['topic_firstpost'] == $post['pid'] and ( $post['_isHidden'] OR $topicData['_isHidden'] ) )
{
$post['queued'] = 1;
$post['_isHidden'] = true;
}
/* Edited stuff */
$post['edit_by'] = "";
if ( ( $post['append_edit'] == 1 ) and ( $post['edit_time'] != "" ) and ( $post['edit_name'] != "" ) )
{
$e_time = $this->registry->class_localization->getDate( $post['edit_time'] , 'LONG' );
$post['edit_by'] = sprintf( $this->lang->words['edited_by'], $post['edit_name'], $e_time );
}
/* Now parse the post */
if ( !isset($post['cache_content']) OR !$post['cache_content'] )
{
$_NOW2 = IPSDebug::getMemoryDebugFlag();
IPSText::getTextClass('bbcode')->parse_smilies = $post['use_emo'];
IPSText::getTextClass('bbcode')->parse_html = ( $forumData['use_html'] and $poster['g_dohtml'] and $post['post_htmlstate'] ) ? 1 : 0;
IPSText::getTextClass('bbcode')->parse_nl2br = $post['post_htmlstate'] == 2 ? 1 : 0;
IPSText::getTextClass('bbcode')->parse_bbcode = $forumData['use_ibc'];
IPSText::getTextClass('bbcode')->parsing_section = 'topics';
IPSText::getTextClass('bbcode')->parsing_mgroup = $post['member_group_id'];
IPSText::getTextClass('bbcode')->parsing_mgroup_others = $post['mgroup_others'];
$post['post'] = IPSText::getTextClass('bbcode')->preDisplayParse( $post['post'] );
IPSDebug::setMemoryDebugFlag( "topics::parsePostRow - bbcode parse - Completed", $_NOW2 );
IPSContentCache::update( $post['pid'], 'post', $post['post'] );
}
else
{
$post['post'] = '<!--cached-' . gmdate( 'r', $post['cache_updated'] ) . '-->' . $post['cache_content'];
}
/* Buttons */
$post['_can_delete'] = $post['pid'] != $topicData['topic_firstpost']
? $this->canDeletePost( $post )
: FALSE;
$post['_can_edit'] = $this->canEditPost( $post );
$post['_show_ip'] = $this->canSeeIp();
$post['_canReply'] = ( $this->getReplyStatus() == 'reply' ) ? true : false;
/* Signatures */
$post['signature'] = "";
if ( ! empty( $poster['signature'] ) )
{
if ( $post['use_sig'] == 1 )
{
if ( ! $this->memberData['view_sigs'] || ( $poster['author_id'] && $this->memberData['member_id'] && ! empty( $this->member->ignored_users[ $poster['author_id'] ]['ignore_signatures'] ) && IPSMember::isIgnorable( $poster['member_group_id'], $poster['mgroup_others'] ) ) )
{
$post['signature'] = '<!--signature.hidden.' . $post['pid'] . '-->';
}
else
{
$post['signature'] = $this->registry->output->getTemplate( 'global' )->signature_separator( $poster['signature'], $poster['author_id'], IPSMember::isIgnorable( $poster['member_group_id'], $poster['mgroup_others'] ) );
}
}
}
$post['forum_id'] = $topicData['forum_id'];
/* Reputation */
if ( $this->settings['reputation_enabled'] )
{
/* Load the class */
if ( ! $this->registry->isClassLoaded( 'repCache' ) )
{
$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/class_reputation_cache.php', 'classReputationCache' );
$this->registry->setClass( 'repCache', new $classToLoad() );
}
$this->memberData['_members_cache']['rep_filter'] = isset( $this->memberData['_members_cache']['rep_filter'] ) ? $this->memberData['_members_cache']['rep_filter'] : '*';
$post['pp_reputation_points'] = $post['pp_reputation_points'] ? $post['pp_reputation_points'] : 0;
$post['has_given_rep'] = $post['has_given_rep'] ? $post['has_given_rep'] : 0;
$post['rep_points'] = $post['rep_points'] ? $post['rep_points'] : 0;
$post['_repignored'] = 0;
if ( ! ( $this->settings['reputation_protected_groups'] &&
in_array( $this->memberData['member_group_id'], explode( ',', $this->settings['reputation_protected_groups'] ) )
) &&
$this->memberData['_members_cache']['rep_filter'] !== '*'
)
{
if ( $this->settings['reputation_show_content'] && $post['rep_points'] < $this->memberData['_members_cache']['rep_filter'] && $this->settings['reputation_point_types'] != 'like' )
{
$post['_repignored'] = 1;
}
}
$post['like'] = $this->registry->repCache->getLikeFormatted( array( 'app' => 'forums', 'type' => 'pid', 'id' => $post['pid'], 'rep_like_cache' => $post['rep_like_cache'] ) );
}
/* Memory debug */
IPSDebug::setMemoryDebugFlag( "PID: ".$post['pid']. " - Completed", $_NOW );
return array( 'post' => $post, 'author' => $poster );
}
/**
* Set post filters
* Takes user input and cleans it up a bit
*
* @param array Incoming filters
* @return array
*/
protected function _setPostFilters( $filters )
{
$filters['sortOrder'] = ( isset( $filters['sortOrder'] ) ) ? $filters['sortOrder'] : '';
$filters['sortField'] = ( isset( $filters['sortField'] ) ) ? $filters['sortField'] : '';
$filters['offset'] = ( isset( $filters['offset'] ) ) ? $filters['offset'] : '';
$filters['limit'] = ( isset( $filters['limit'] ) ) ? $filters['limit'] : '';
$filters['isVisible'] = ( isset( $filters['isVisible'] ) ) ? $filters['isVisible'] : '';
switch( $filters['sortOrder'] )
{
default:
case 'desc':
case 'descending':
case 'z-a':
$filters['sortOrder'] = 'desc';
break;
case 'asc':
case 'ascending':
case 'a-z':
$filters['sortOrder'] = 'asc';
break;
}
/* Do some set up */
switch( $filters['sortField'] )
{
case 'date':
case 'time':
$filters['sortField'] = 'post_date';
break;
case 'pid':
case 'id':
$filters['sortField'] = 'pid';
break;
}
/* Others */
$filters['offset'] = intval( $filters['offset'] );
$filters['limit'] = intval( $filters['limit'] );
$filters['unixCutOff'] = ( ! empty( $filters['unixCutOff'] ) ) ? intval( $filters['unixCutOff'] ) : 0;
/* So we don't have to do this twice */
$filters['_cleaned'] = true;
return $filters;
}
/**
* Set topic filters
* Takes user input and cleans it up a bit
*
* @param array Incoming filters
* @return array
*/
protected function _setTopicFilters( $filters )
{
$filters['sortOrder'] = ( isset( $filters['sortOrder'] ) ) ? $filters['sortOrder'] : '';
$filters['sortField'] = ( isset( $filters['sortField'] ) ) ? $filters['sortField'] : '';
$filters['offset'] = ( isset( $filters['offset'] ) ) ? $filters['offset'] : '';
$filters['limit'] = ( isset( $filters['limit'] ) ) ? $filters['limit'] : '';
$filters['isVisible'] = ( isset( $filters['isVisible'] ) ) ? $filters['isVisible'] : '';
switch( $filters['sortOrder'] )
{
default:
case 'desc':
case 'descending':
case 'z-a':
$filters['sortOrder'] = 'desc';
break;
case 'asc':
case 'ascending':
case 'a-z':
$filters['sortOrder'] = 'asc';
break;
}
/* Do some set up */
switch( $filters['sortField'] )
{
case 'date':
case 'time':
case 'start_date':
$filters['sortField'] = 'start_date';
break;
case 'lastDate':
case 'lastTime':
case 'last_post':
$filters['sortField'] = 'last_post';
break;
case 'tid':
case 'id':
$filters['sortField'] = 'tid';
break;
}
/* Others */
$filters['offset'] = intval( $filters['offset'] );
$filters['limit'] = intval( $filters['limit'] );
$filters['unixCutOff'] = ( ! empty( $filters['unixCutOff'] ) ) ? intval( $filters['unixCutOff'] ) : 0;
/* So we don't have to do this twice */
$filters['_cleaned'] = true;
return $filters;
}
}