* Invision Power Services * IP.Board v3.2.3 * Moderator actions * Last Updated: $Date: 2011-10-11 11:50:01 -0400 (Tue, 11 Oct 2011) $ * * * @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 "

Incorrect access

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'] = '' . $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'] = ''; } 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; } }