railpage/railpagecore

View on GitHub
lib/Forums/Forum.php

Summary

Maintainability
F
4 days
Test Coverage
<?php

/**
 * Forums API
 * @since Version 3.0.1
 * @version 3.2
 * @package Railpage
 * @author James Morgan, Michael Greenhill
 */
 
namespace Railpage\Forums;

use Railpage\Module;
use Railpage\Url;
use Railpage\Users\User;
use Railpage\Users\Factory as UserFactory;
use Railpage\Debug;
use Zend_Acl;
use Zend_Db_Expr;
use DateTime;
use Exception;
use InvalidArgumentException;
use stdClass;

/** 
 * phpBB Forum class
 * @since Version 3.0.1
 * @version 3.0.1
 * @author James Morgan
 */

class Forum extends Forums {
    
    /**
     * Forum status: unlocked
     * @since Version 3.9.1
     * @const int FORUM_UNLOCKED
     */
    
    const FORUM_UNLOCKED = 0; 
    
    /**
     * Forum status: locked
     * @since Version 3.9.1
     * @const int FORUM_LOCKED
     */
    
    const FORUM_LOCKED = 1;
    
    /**
     * Forum ID
     * @since Version 3.0.1
     * @version 3.0.1
     * @var int $id
     */
    
    public $id;
    
    /**
     * Category ID
     * @since Version 3.0.1
     * @version 3.0.1
     * @var int $catid
     */
    
    public $catid;
    
    /**
     * Forum name
     * @since Version 3.0.1
     * @version 3.0.1
     * @var string $name
     */
    
    public $name;
    
    /**
     * Forum description
     * @since Version 3.0.1
     * @version 3.0.1
     * @var string $description
     */
    
    public $description;
    
    /**
     * Forum status
     * @since Version 3.0.1
     * @version 3.0.1
     * @var int $status
     */
    
    public $status;
    
    /**
     * Forum order
     * @since Version 3.0.1
     * @version 3.0.1
     * @var int $order
     */
    
    public $order;
    
    /**
     * Number of posts in this forum
     * @since Version 3.0.1
     * @version 3.0.1
     * @var int $posts
     */
    
    public $posts;
    
    /**
     * Number of topics in this forum
     * @since Version 3.0.1
     * @version 3.0.1
     * @var int $topics
     */
    
    public $topics;
    
    /**
     * Last post ID
     * @since Version 3.0.1
     * @version 3.0.1
     * @var int $last_post
     */
    
    public $last_post;
    
    /**
     * Category object
     * @since Version 3.2
     * @version 3.2
     * @var object $category
     */
    
    public $category;
    
    /**
     * Parent forum
     * @since Version 3.9.1
     * @var \Railpage\Forums\Forum|null $Parent
     */
    
    public $Parent;
    
    /**
     * Constructor
     * @since Version 3.0.1
     * @version 3.0.1
     * @param int $forumid
     * @param object $database
     */
    
    public function __construct($forumid = false, $getParent = true) {
        
        parent::__construct();
        
        $timer = Debug::GetTimer(); 
        
        $this->Module = new Module("forums");
        
        if (filter_var($forumid, FILTER_VALIDATE_INT)) {
            $this->load($forumid, $getParent);
        } elseif ($shortname = filter_var($forumid, FILTER_SANITIZE_STRING)) {
            if (!is_null($shortname)) {
                $this->load($shortname, $getParent);
            }
        }
        
        Debug::LogEvent(__METHOD__, $timer); 
        
    }
    
    /**
     * Load the forum
     * @since Version 3.9.1
     * @return \Railpage\Forums\Forum
     * @param int|string $id
     * @param boolean $getParent
     */
    
    public function load($id = false, $getParent = false) {
        if ($id === false) {
            throw new InvalidArgumentException("No valid forum ID or shortname was provided");
        }
        
        $this->url = new Url(sprintf("/f-f%d.htm", $id));
        
        if (filter_var($id, FILTER_VALIDATE_INT)) {
            #$query = "SELECT * FROM nuke_bbforums f LEFT JOIN (nuke_bbtopics t, nuke_bbposts p, nuke_bbposts_text pt) ON (f.forum_last_post_id = p.post_id AND p.topic_id = t.topic_id AND pt.post_id = p.post_id) WHERE f.forum_id = ? LIMIT 1";
            
            $query = "SELECT f.*, p.post_time, p.poster_id, p.post_username, pt.post_subject, pt.post_text, pt.bbcode_uid,
                            t.topic_id, t.topic_title, t.topic_time,
                            f.forum_name, f.forum_desc
                        FROM nuke_bbforums AS f
                            LEFT JOIN nuke_bbposts AS p ON f.forum_last_post_id = p.post_id
                            LEFT JOIN nuke_bbtopics AS t ON p.topic_id = t.topic_id
                            LEFT JOIN nuke_bbposts_text AS pt ON pt.post_id = p.post_id
                        WHERE f.forum_id = ?";
            
            $row = $this->db->fetchRow($query, $id);
        }
        
        if (isset($row) && is_array($row)) {
            $this->id           = $row['forum_id'];
            $this->catid        = $row["cat_id"];
            $this->name         = function_exists("html_entity_decode_utf8") ? html_entity_decode_utf8($row["forum_name"]) : $row['forum_name'];
            $this->description  = function_exists("html_entity_decode_utf8") ? html_entity_decode_utf8($row["forum_desc"]) : $row['forum_desc'];
            $this->status       = $row["forum_status"];
            $this->order        = $row["forum_order"];
            $this->posts        = $row["forum_posts"];
            $this->topics       = $row["forum_topics"];
            $this->last_post    = $row["forum_last_post_id"];
            
            $this->last_post_id         = $this->last_post;
            $this->last_post_time       = $row['post_time'];
            $this->last_post_user_id    = $row['poster_id'];
            $this->last_post_username   = $row['post_username'];
            $this->last_post_subject    = $row['post_subject'];
            $this->last_post_text       = $row['post_text'];
            $this->last_post_bbcodeuid  = $row['bbcode_uid'];
            
            $this->last_post_topic_id       = $row['topic_id'];
            $this->last_post_topic_title    = $row['topic_title'];
            $this->last_post_topic_time     = $row['topic_time'];
            
            $this->acl_resource = sprintf("railpage.forums.forum:%d", $this->id);
            
            if ($getParent) {
                $this->category = ForumsFactory::CreateCategory($this->catid);
            }
            
            if (filter_var($row['forum_parent'], FILTER_VALIDATE_INT) && $row['forum_parent'] > 0) {
                $this->Parent = new Forum($row['forum_parent']);
            }
        }

    }
    
    /**
     * Set the category for this forum
     * @since Version 3.9.1
     * @return \Railpage\Forums\Forum
     * @param \Railpage\Forms\Category $Category
     */
    
    public function setCategory(Category $Category) {
        
        $this->catid = $Category->id;
        $this->category = $Category;
        
        return $this;
    }
    
    /**
     * Validate changes to this forum
     * @since Version 3.9.1
     * @return boolean
     */
    
    private function validate() {
        
        if (!filter_var($this->catid, FILTER_VALIDATE_INT) && !$this->category instanceof Category) {
            throw new Exception("No valid forum category has been set (hint: Forum::setCategory");
        }
        
        /**
         * Sanitize
         */
        
        $vars = array(
            "name",
            "description"
        );
        
        foreach ($vars as $var) {
            $this->$var = filter_var($this->$var, FILTER_SANITIZE_STRING); 
        }
        
        if (empty($this->name)) {
            throw new Exception("No forum name has been set");
        }
        
        if (!filter_var($this->status, FILTER_VALIDATE_INT)) {
            $this->status = self::FORUM_UNLOCKED;
        }
        
        /**
         * Set some default ints
         */
        
        $vars = array(
            "order", 
            "posts",
            "topics", 
            "last_post",
        );
        
        foreach ($vars as $var) {
            if (!filter_var($this->$var, FILTER_VALIDATE_INT)) {
                $this->$var = 0;
            }
        }
        
        return true;
    }
    
    /**
     * Commit changes to this forum
     * @since Version 3.9.1
     * @return \Railpage\Forums\Forum
     */
    
    public function commit() {
        
        $this->validate(); 
        
        $data = array(
            "cat_id" => $this->catid,
            "forum_name" => $this->name,
            "forum_desc" => $this->description,
            "forum_status" => $this->status,
            "forum_order" => $this->order,
            "forum_posts" => $this->posts,
            "forum_topics" => $this->topics,
            "forum_last_post_id" => $this->last_post,
            "forum_parent" => $this->Parent instanceof Forum ? $this->Parent->id : 0
        );
        
        if (filter_var($this->id, FILTER_VALIDATE_INT)) {
            $where = array(
                "forum_id = ?" => $this->id
            );
            
            $this->db->update("nuke_bbforums", $data, $where); 
        } else {
            $this->db->insert("nuke_bbforums", $data); 
            $this->id = intval($this->db->lastInsertId());
        }
        
        return $this;
    }
    
    /**
     * Tell the forum that there's a new post
     * @since Version 3.0.1
     * @version 3.0.1
     * @param int $postID
     * @return boolean
     */
    
    public function addPost($postID = false) {
        if (empty($postID) || !$postID) {
            throw new \Exception("No post ID specified for " . __CLASS__ . "::" . __FUNCTION__ . "()"); 
            return false;
        }
        
        $data = array(
            "forum_posts" => new Zend_Db_Expr("forum_posts + 1"),
            "forum_last_post_id" => $postID
        );
        
        $where = array(
            "forum_id = ?" => $this->id
        );
        
        return $this->db->update("nuke_bbforums", $data, $where); 
    }
    
    /**
     * Tell the forum that there's a new thread
     * @since Version 3.0.1
     * @version 3.0.1
     * @param int $topicID
     * @return boolean
     */ 
    
    public function addTopic() {
        $data = array(
            "forum_topics" => new Zend_Db_Expr("forum_topics + 1"),
        );
        
        $where = array(
            "forum_id = ?" => $this->id
        );
        
        return $this->db->update("nuke_bbforums", $data, $where); 
    }
    
    /**
     * Reload the forum data - eg when a new post or topic has been created
     * @since Version 3.0.1
     * @version 3.0.1
     */
    
    public function refresh() {
        $query = "SELECT * FROM nuke_bbforums WHERE forum_id = ? LIMIT 1";
        
        $row = $this->db->fetchRow($query, $this->id); 
        
        $this->catid        = $row["cat_id"];
        $this->name         = $row["forum_name"];
        $this->description  = $row["forum_desc"];
        $this->status       = $row["forum_status"];
        $this->order        = $row["forum_order"];
        $this->posts        = $row["forum_posts"];
        $this->topics       = $row["forum_topics"];
        $this->last_post    = $row["forum_last_post_id"];
        
        return true;
    }
    
    /**
     * Permissions check
     * @since Version 3.2
     * @version 3.2
     * @param object $permissions
     * @return boolean
     */
     
    public function get_permission($permission_name = false, $permissions_object = false) {
        if ($permissions_object) {
            $this->permissions = $permissions_object;
        }
        
        if (!$permission_name || !$this->permissions || !$this->id) {
            return false;
        }
        
        $object = $this->permissions->forums[$this->id];
        
        switch ($permission_name) {
            case "view":
                if ($object['auth_view'] == 0 || $object['auth_read'] == 0) {
                    return true;
                    
                } elseif ($this->permissions->user->id) {
                    // Logged in
                    
                    if ($object['auth_mod'] > 0) {
                        // User is a moderator
                        return true;
                    }
                    
                    if ($this->permissions->user->level > 1) {
                        return true;
                    }
                }
                
                break;
        }
        
        return false;
    }
    
    /**
     * Get topics from this forum
     * @since Version 3.2
     * @version 3.2
     * @return array
     * @param int $items_per_page
     * @param int $page_num
     * @param string $sort
     */
    
    public function topics($items_per_page = 25, $page_num = 1, $sort = "DESC") {
        
        $query = "SELECT 
                        SQL_CALC_FOUND_ROWS 
                        t.*, 
                        ufirst.username AS first_post_username, 
                        ufirst.user_id AS first_post_user_id, 
                        ulast.username AS last_post_username, 
                        ulast.user_id AS last_post_user_id,
                        pfirst_text.post_text AS first_post_text,
                        pfirst_text.bbcode_uid AS first_post_bbcode_uid,
                        pfirst.post_time AS first_post_time,
                        plast_text.post_text AS last_post_text,
                        plast_text.bbcode_uid AS last_post_bbcode_uid,
                        plast.post_time AS last_post_time
                        
                    FROM nuke_bbtopics AS t
                    
                    LEFT JOIN nuke_bbposts AS pfirst ON pfirst.post_id = t.topic_first_post_id
                    LEFT JOIN nuke_bbposts AS plast ON plast.post_id = t.topic_last_post_id
                    LEFT JOIN nuke_bbposts_text AS pfirst_text ON pfirst.post_id = pfirst_text.post_id
                    LEFT JOIN nuke_bbposts_text AS plast_text ON plast.post_id = plast_text.post_id
                    LEFT JOIN nuke_users AS ufirst ON ufirst.user_id = pfirst.poster_id
                    LEFT JOIN nuke_users AS ulast ON ulast.user_id = plast.poster_id
                    
                    WHERE t.forum_id = ?
                    ORDER BY t.topic_type DESC, plast.post_time " . $sort . "
                    LIMIT ?, ?";
        
        $params = array(
            $this->id,
            ($page_num - 1) * $items_per_page,
            $items_per_page
        );
        
        $result = $this->db->fetchAll($query, $params);
        
        $topics = array();
        $topics['total_topics'] = $this->db->fetchOne("SELECT FOUND_ROWS() AS total"); 
        $topics['total_pages'] = ceil($total['total'] / $items_per_page);
        $topics['page_num'] = $page_num;
        $topics['items_per_page'] = $items_per_page;
        
        foreach ($result as $row) {
            $topics['topics'][$row['topic_id']] = $row;
        }
        
        return $topics;
    }
    
    /**
     * Get threads within this forum
     * @since Version 3.8.7
     * @yield \Railpage\Forums\Thread
     * @param int $items_per_page
     * @param int $page
     */
    
    public function getThreads($items_per_page = 25, $page = 1) {
        $query = "SELECT topic_id FROM nuke_bbtopics WHERE forum_id = ? LIMIT ?, ?";
        
        foreach ($this->db->fetchAll($query, array($this->id, ($page - 1) * $items_per_page, $items_per_page)) as $row) {
            yield new Thread($row['topic_id']);
        }
    }
    
    /**
     * Check various forums permissions 
     * @since Version 3.8.7
     * @param string $permission
     * @return boolean
     */
    
    public function isAllowed($permission) {
        if (!$this->User instanceof User) {
            throw new Exception("Cannot check forum ACL because no valid user has been set (hint: setUser(\$User)");
        }
        
        $this->getACL();
        
        return $this->ZendACL->isAllowed("forums_viewer", $this->acl_resource, $permission);
    }
    
    /**
     * Refresh forum data
     * @since Version 3.9.1
     * @return \Railpage\Forums\Forum
     */
    
    public function refreshForumStats() {
        $query = "SELECT 
            (SELECT COUNT(post_id) AS forum_posts FROM nuke_bbposts WHERE forum_id = ?) AS forum_posts,
            (SELECT COUNT(topic_id) AS forum_topics FROM nuke_bbtopics WHERE forum_id = ?) AS forum_topics,
            (SELECT post_id AS forum_last_post_id FROM nuke_bbposts WHERE forum_id = ? ORDER BY post_time DESC LIMIT 1) AS forum_last_post_id";
        
        $where = array(
            $this->id,
            $this->id,
            $this->id
        );
        
        $stats = $this->db->fetchAll($query, $where);
        
        if (isset($stats[0])) {
            $data = $stats[0];
            
            $where = array(
                "forum_id = ?" => $this->id
            );
            
            $this->db->update("nuke_bbforums", $data, $where);
        }
        
        return $this;
    }
}