/**
  * @see Taggable::getObjectsByTagID()
  */
 public function getObjectsByTagID($tagID, $limit = 0, $offset = 0)
 {
     $accessibleBoardIDArray = Board::getAccessibleBoardIDArray();
     if (count($accessibleBoardIDArray) == 0) {
         return array();
     }
     $sqlThreadVisitSelect = $sqlThreadVisitJoin = $sqlSubscriptionSelect = $sqlSubscriptionJoin = $sqlOwnPostsSelect = $sqlOwnPostsJoin = '';
     if (WCF::getUser()->userID != 0) {
         $sqlThreadVisitSelect = ', thread_visit.lastVisitTime';
         $sqlThreadVisitJoin = " LEFT JOIN \twbb" . WBB_N . "_thread_visit thread_visit \n\t\t\t\t\t\tON \t\t(thread_visit.threadID = thread.threadID\n\t\t\t\t\t\t\t\tAND thread_visit.userID = " . WCF::getUser()->userID . ")";
         $sqlSubscriptionSelect = ', IF(thread_subscription.userID IS NOT NULL, 1, 0) AS subscribed';
         $sqlSubscriptionJoin = " LEFT JOIN \twbb" . WBB_N . "_thread_subscription thread_subscription \n\t\t\t\t\t\tON \t\t(thread_subscription.userID = " . WCF::getUser()->userID . "\n\t\t\t\t\t\t\t\tAND thread_subscription.threadID = thread.threadID)";
         if (BOARD_THREADS_ENABLE_OWN_POSTS) {
             $sqlOwnPostsSelect = "DISTINCT post.userID AS ownPosts,";
             $sqlOwnPostsJoin = "\tLEFT JOIN\twbb" . WBB_N . "_post post\n\t\t\t\t\t\t\tON \t\t(post.threadID = thread.threadID\n\t\t\t\t\t\t\t\t\tAND post.userID = " . WCF::getUser()->userID . ")";
         }
     }
     $threads = array();
     $sql = "SELECT\t\t" . $sqlOwnPostsSelect . "\n\t\t\t\t\tthread.*,\n\t\t\t\t\tboard.boardID, board.title\n\t\t\t\t\t" . $sqlThreadVisitSelect . "\n\t\t\t\t\t" . $sqlSubscriptionSelect . "\n\t\t\tFROM\t\twcf" . WCF_N . "_tag_to_object tag_to_object\n\t\t\tLEFT JOIN\twbb" . WBB_N . "_thread thread\n\t\t\tON\t\t(thread.threadID = tag_to_object.objectID)\n\t\t\tLEFT JOIN \twbb" . WBB_N . "_board board\n\t\t\tON \t\t(board.boardID = thread.boardID)\n\t\t\t" . $sqlOwnPostsJoin . "\n\t\t\t" . $sqlThreadVisitJoin . "\n\t\t\t" . $sqlSubscriptionJoin . "\n\t\t\tWHERE\t\ttag_to_object.tagID = " . $tagID . "\n\t\t\t\t\tAND tag_to_object.taggableID = " . $this->getTaggableID() . "\n\t\t\t\t\tAND thread.boardID IN (" . implode(',', $accessibleBoardIDArray) . ")\n\t\t\t\t\tAND thread.isDeleted = 0\n\t\t\t\t\tAND thread.isDisabled = 0\n\t\t\tORDER BY\tthread.lastPostTime DESC";
     $result = WCF::getDB()->sendQuery($sql, $limit, $offset);
     while ($row = WCF::getDB()->fetchArray($result)) {
         $row['taggable'] = $this;
         $threads[] = new TaggedThread(null, $row);
     }
     return $threads;
 }
    /**
     * @see EventListener::execute()
     */
    public function execute($eventObj, $className, $eventName)
    {
        if ($eventName == 'init') {
            $eventObj->sqlSelects .= 'wbb_user.posts,';
            $eventObj->sqlJoins .= ' LEFT JOIN wbb' . WBB_N . '_user wbb_user
						ON (wbb_user.userID = user.userID) ';
        } else {
            if ($eventName == 'assignVariables') {
                $user = $eventObj->frame->getUser();
                $eventObj->generalInformation[] = array('icon' => StyleManager::getStyle()->getIconPath('postM.png'), 'title' => WCF::getLanguage()->get('wcf.user.posts'), 'value' => '<a href="index.php?form=Search&amp;types[]=post&amp;userID=' . $user->userID . SID_ARG_2ND . '" title="' . WCF::getLanguage()->get('wcf.user.profile.search', array('$username' => StringUtil::encodeHTML($user->username))) . '">' . StringUtil::formatInteger(intval($user->posts)) . ($user->getProfileAge() > 1 ? ' ' . WCF::getLanguage()->get('wcf.user.postsPerDay', array('$posts' => StringUtil::formatDouble($user->posts / $user->getProfileAge()))) : '') . '</a>');
                // show last 5 posts
                if (PROFILE_SHOW_LAST_POSTS) {
                    require_once WBB_DIR . 'lib/data/post/ViewablePost.class.php';
                    require_once WBB_DIR . 'lib/data/board/Board.class.php';
                    $boardIDArray = Board::getAccessibleBoardIDArray(array('canViewBoard', 'canEnterBoard', 'canReadThread'));
                    if (count($boardIDArray)) {
                        $posts = array();
                        $sql = "SELECT\t\tpost.postID, post.time,\n\t\t\t\t\t\t\t\tCASE WHEN post.subject <> '' THEN post.subject ELSE thread.topic END AS subject\n\t\t\t\t\t\tFROM\t\twbb" . WBB_N . "_user_last_post user_last_post\n\t\t\t\t\t\tLEFT JOIN\twbb" . WBB_N . "_post post\n\t\t\t\t\t\tON\t\t(post.postID = user_last_post.postID)\n\t\t\t\t\t\tLEFT JOIN\twbb" . WBB_N . "_thread thread\n\t\t\t\t\t\tON\t\t(thread.threadID = post.threadID)\n\t\t\t\t\t\tWHERE\t\tuser_last_post.userID = " . $user->userID . "\n\t\t\t\t\t\t\t\tAND post.isDeleted = 0\n\t\t\t\t\t\t\t\tAND post.isDisabled = 0\n\t\t\t\t\t\t\t\tAND thread.boardID IN (" . implode(',', $boardIDArray) . ")\n\t\t\t\t\t\t\t\t" . (count(WCF::getSession()->getVisibleLanguageIDArray()) ? "AND thread.languageID IN (" . implode(',', WCF::getSession()->getVisibleLanguageIDArray()) . ")" : "") . "\n\t\t\t\t\t\tORDER BY\tuser_last_post.time DESC";
                        $result = WCF::getDB()->sendQuery($sql, 5);
                        while ($row = WCF::getDB()->fetchArray($result)) {
                            $posts[] = new ViewablePost(null, $row);
                        }
                        if (count($posts)) {
                            WCF::getTPL()->assign(array('posts' => $posts, 'user' => $user));
                            WCF::getTPL()->append('additionalContent2', WCF::getTPL()->fetch('userProfileLastPosts'));
                        }
                    }
                }
            }
        }
    }
 /**
  * Gets the posts for the feed.
  */
 protected function readPosts()
 {
     // accessible boards
     $accessibleBoardIDArray = Board::getAccessibleBoardIDArray(array('canViewBoard', 'canEnterBoard', 'canReadThread'));
     if (!count($accessibleBoardIDArray)) {
         throw new PermissionDeniedException();
     }
     // get posts
     $attachmentPostIDArray = array();
     $sql = "SELECT\t\tpost.*\n\t\t\tFROM\t\twbb" . WBB_N . "_post post\n\t\t\tWHERE\t\tpost.threadID IN (" . implode(',', $this->threadIDArray) . ")\n\t\t\t\t\tAND post.threadID IN (SELECT threadID FROM wbb" . WBB_N . "_thread WHERE boardID IN (" . implode(',', $accessibleBoardIDArray) . "))\n\t\t\t\t\tAND post.isDeleted = 0\n\t\t\t\t\tAND post.isDisabled = 0\n\t\t\t\t\t" . ($this->hours ? "AND post.time > " . (TIME_NOW - $this->hours * 3600) : '') . "\n\t\t\tORDER BY\tpost.time DESC";
     $result = WCF::getDB()->sendQuery($sql, $this->limit);
     while ($row = WCF::getDB()->fetchArray($result)) {
         $this->posts[] = new FeedPost(null, $row);
         // attachments
         if ($row['attachments'] != 0) {
             $attachmentPostIDArray[] = $row['postID'];
         }
     }
     // read attachments
     if (MODULE_ATTACHMENT == 1 && count($attachmentPostIDArray) > 0 && (WCF::getUser()->getPermission('user.board.canViewAttachmentPreview') || WCF::getUser()->getPermission('user.board.canDownloadAttachment'))) {
         require_once WCF_DIR . 'lib/data/attachment/MessageAttachmentList.class.php';
         $attachmentList = new MessageAttachmentList($attachmentPostIDArray, 'post');
         $attachmentList->readObjects();
         $attachments = $attachmentList->getSortedAttachments();
         // set embedded attachments
         require_once WCF_DIR . 'lib/data/message/bbcode/AttachmentBBCode.class.php';
         AttachmentBBCode::setAttachments($attachments);
     }
 }
 /**
  * Gets the threads for the feed.
  */
 protected function readThreads()
 {
     // accessible boards
     $accessibleBoardIDArray = Board::getAccessibleBoardIDArray(array('canViewBoard', 'canEnterBoard', 'canReadThread'));
     // get threads
     if (count($accessibleBoardIDArray)) {
         $sql = "SELECT\t\tpost.*, thread.*\n\t\t\t\tFROM\t\twbb" . WBB_N . "_thread_subscription subscription\n\t\t\t\tLEFT JOIN\twbb" . WBB_N . "_thread thread\n\t\t\t\tON\t\t(thread.threadID = subscription.threadID)\n\t\t\t\tLEFT JOIN\twbb" . WBB_N . "_post post\n\t\t\t\tON\t\t(post.postID = thread.firstPostID)\n\t\t\t\tWHERE\t\tsubscription.userID = " . WCF::getUser()->userID . "\n\t\t\t\t\t\tAND thread.boardID IN (" . implode(',', $accessibleBoardIDArray) . ")\n\t\t\t\t\t\tAND thread.isDeleted = 0\n\t\t\t\t\t\tAND thread.isDisabled = 0\n\t\t\t\t\t\tAND thread.movedThreadID = 0\n\t\t\t\t\t\tAND thread.time > " . ($this->hours ? TIME_NOW - $this->hours * 3600 : TIME_NOW - 30 * 86400) . "\n\t\t\t\tORDER BY\tthread.time DESC";
         $result = WCF::getDB()->sendQuery($sql, $this->limit);
         while ($row = WCF::getDB()->fetchArray($result)) {
             $this->threads[] = new FeedThread($row);
         }
     }
 }
 /**
  * Gets the threads for the feed.
  */
 protected function readThreads()
 {
     $boardIDArray = $this->boardIDArray;
     // include subboards
     if (count($boardIDArray)) {
         $boardIDArray = array_merge($boardIDArray, Board::getSubBoardIDArray($boardIDArray));
     }
     // accessible boards
     $accessibleBoardIDArray = Board::getAccessibleBoardIDArray(array('canViewBoard', 'canEnterBoard', 'canReadThread'));
     if (count($boardIDArray)) {
         $boardIDArray = array_intersect($boardIDArray, $accessibleBoardIDArray);
     } else {
         $boardIDArray = $accessibleBoardIDArray;
         foreach ($boardIDArray as $key => $boardID) {
             if (WCF::getUser()->isIgnoredBoard($boardID)) {
                 unset($boardIDArray[$key]);
             }
         }
     }
     // get threads
     $attachmentPostIDArray = array();
     if (count($boardIDArray)) {
         $sql = "SELECT\t\tpost.*, thread.*, post.attachments\n\t\t\t\tFROM\t\twbb" . WBB_N . "_thread thread\n\t\t\t\tLEFT JOIN\twbb" . WBB_N . "_post post\n\t\t\t\tON\t\t(post.postID = thread.firstPostID)\n\t\t\t\tWHERE\t\tthread.boardID IN (" . implode(',', $boardIDArray) . ")\n\t\t\t\t\t\tAND thread.isDeleted = 0\n\t\t\t\t\t\tAND thread.isDisabled = 0\n\t\t\t\t\t\tAND thread.movedThreadID = 0\n\t\t\t\t\t\tAND thread.time > " . ($this->hours ? TIME_NOW - $this->hours * 3600 : TIME_NOW - 30 * 86400) . "\n\t\t\t\tORDER BY\tthread.time DESC";
         $result = WCF::getDB()->sendQuery($sql, $this->limit);
         while ($row = WCF::getDB()->fetchArray($result)) {
             $this->threads[] = new FeedThread($row);
             // attachments
             if ($row['attachments'] != 0) {
                 $attachmentPostIDArray[] = $row['postID'];
             }
         }
     }
     // read attachments
     if (MODULE_ATTACHMENT == 1 && count($attachmentPostIDArray) > 0 && (WCF::getUser()->getPermission('user.board.canViewAttachmentPreview') || WCF::getUser()->getPermission('user.board.canDownloadAttachment'))) {
         require_once WCF_DIR . 'lib/data/attachment/MessageAttachmentList.class.php';
         $attachmentList = new MessageAttachmentList($attachmentPostIDArray, 'post');
         $attachmentList->readObjects();
         $attachments = $attachmentList->getSortedAttachments();
         // set embedded attachments
         require_once WCF_DIR . 'lib/data/message/bbcode/AttachmentBBCode.class.php';
         AttachmentBBCode::setAttachments($attachments);
     }
 }
    /**
     * @see EventListener::execute()
     */
    public function execute($eventObj, $className, $eventName)
    {
        if ($eventName == 'readParameters') {
            // handle special search options here
            $action = '';
            if (isset($_REQUEST['action'])) {
                $action = $_REQUEST['action'];
            }
            if (empty($action)) {
                return;
            }
            // get accessible board ids
            require_once WBB_DIR . 'lib/data/board/Board.class.php';
            $boardIDArray = Board::getAccessibleBoardIDArray(array('canViewBoard', 'canEnterBoard', 'canReadThread'));
            foreach ($boardIDArray as $key => $boardID) {
                if (WCF::getUser()->isIgnoredBoard($boardID)) {
                    unset($boardIDArray[$key]);
                } else {
                    if (!Board::getBoard($boardID)->searchable) {
                        unset($boardIDArray[$key]);
                    }
                }
            }
            if (!count($boardIDArray)) {
                return;
            }
            switch ($action) {
                case 'unread':
                    $sql = "SELECT\t\tthread.threadID\n\t\t\t\t\t\tFROM\t\twbb" . WBB_N . "_thread thread\n\t\t\t\t\t\tWHERE\t\tthread.boardID IN (" . implode(',', $boardIDArray) . ")\n\t\t\t\t\t\t\t\tAND thread.lastPostTime > " . WCF::getUser()->getLastMarkAllAsReadTime() . "\n\t\t\t\t\t\t\t\t" . (WCF::getUser()->userID ? "\n\t\t\t\t\t\t\t\tAND thread.lastPostTime > IFNULL((\n\t\t\t\t\t\t\t\t\tSELECT\tlastVisitTime\n\t\t\t\t\t\t\t\t\tFROM \twbb" . WBB_N . "_thread_visit\n\t\t\t\t\t\t\t\t\tWHERE \tthreadID = thread.threadID\n\t\t\t\t\t\t\t\t\t\tAND userID = " . WCF::getUser()->userID . "\n\t\t\t\t\t\t\t\t), 0)\n\t\t\t\t\t\t\t\tAND thread.lastPostTime > IFNULL((\n\t\t\t\t\t\t\t\t\tSELECT\tlastVisitTime\n\t\t\t\t\t\t\t\t\tFROM \twbb" . WBB_N . "_board_visit\n\t\t\t\t\t\t\t\t\tWHERE \tboardID = thread.boardID\n\t\t\t\t\t\t\t\t\t\tAND userID = " . WCF::getUser()->userID . "\n\t\t\t\t\t\t\t\t), 0)\n\t\t\t\t\t\t\t\t" : '') . "\n\t\t\t\t\t\t\t\tAND thread.isDeleted = 0\n\t\t\t\t\t\t\t\tAND thread.isDisabled = 0\n\t\t\t\t\t\t\t\t" . (count(WCF::getSession()->getVisibleLanguageIDArray()) ? " AND languageID IN (" . implode(',', WCF::getSession()->getVisibleLanguageIDArray()) . ")" : "") . "\n\t\t\t\t\t\t\t\tAND thread.movedThreadID = 0\n\t\t\t\t\t\tORDER BY\tthread.lastPostTime DESC";
                    break;
                case 'newPostsSince':
                    $since = TIME_NOW;
                    if (isset($_REQUEST['since'])) {
                        $since = intval($_REQUEST['since']);
                    }
                    $sql = "SELECT\t\tthread.threadID\n\t\t\t\t\t\tFROM\t\twbb" . WBB_N . "_thread thread\n\t\t\t\t\t\tWHERE\t\tthread.boardID IN (" . implode(',', $boardIDArray) . ")\n\t\t\t\t\t\t\t\tAND thread.lastPostTime > " . $since . "\n\t\t\t\t\t\t\t\tAND thread.isDeleted = 0\n\t\t\t\t\t\t\t\tAND thread.isDisabled = 0\n\t\t\t\t\t\t\t\t" . (count(WCF::getSession()->getVisibleLanguageIDArray()) ? " AND languageID IN (" . implode(',', WCF::getSession()->getVisibleLanguageIDArray()) . ")" : "") . "\n\t\t\t\t\t\t\t\tAND thread.movedThreadID = 0\n\t\t\t\t\t\tORDER BY\tthread.lastPostTime DESC";
                    break;
                case 'unreplied':
                    $sql = "SELECT\t\tthreadID\n\t\t\t\t\t\tFROM\t\twbb" . WBB_N . "_thread\n\t\t\t\t\t\tWHERE\t\tboardID IN (" . implode(',', $boardIDArray) . ")\n\t\t\t\t\t\t\t\tAND isDeleted = 0\n\t\t\t\t\t\t\t\tAND isDisabled = 0\n\t\t\t\t\t\t\t\tAND movedThreadID = 0\n\t\t\t\t\t\t\t\t" . (count(WCF::getSession()->getVisibleLanguageIDArray()) ? " AND languageID IN (" . implode(',', WCF::getSession()->getVisibleLanguageIDArray()) . ")" : "") . "\n\t\t\t\t\t\t\t\tAND replies = 0\n\t\t\t\t\t\tORDER BY\tlastPostTime DESC";
                    break;
                case '24h':
                    $sql = "SELECT\t\tthreadID\n\t\t\t\t\t\tFROM\t\twbb" . WBB_N . "_thread\n\t\t\t\t\t\tWHERE\t\tboardID IN (" . implode(',', $boardIDArray) . ")\n\t\t\t\t\t\t\t\tAND lastPostTime > " . (TIME_NOW - 86400) . "\n\t\t\t\t\t\t\t\tAND isDeleted = 0\n\t\t\t\t\t\t\t\tAND isDisabled = 0\n\t\t\t\t\t\t\t\t" . (count(WCF::getSession()->getVisibleLanguageIDArray()) ? " AND languageID IN (" . implode(',', WCF::getSession()->getVisibleLanguageIDArray()) . ")" : "") . "\n\t\t\t\t\t\t\t\tAND movedThreadID = 0\n\t\t\t\t\t\tORDER BY\tlastPostTime DESC";
                    break;
                default:
                    return;
            }
            // build search hash
            $searchHash = StringUtil::getHash($sql);
            // execute query
            $matches = array();
            $result = WCF::getDB()->sendQuery($sql, 1000);
            while ($row = WCF::getDB()->fetchArray($result)) {
                $matches[] = array('messageID' => $row['threadID'], 'messageType' => 'post');
            }
            // result is empty
            if (count($matches) == 0) {
                throw new NamedUserException(WCF::getLanguage()->get('wbb.search.error.noMatches'));
            }
            // save result in database
            $searchData = array('packageID' => PACKAGE_ID, 'query' => '', 'result' => $matches, 'additionalData' => array('post' => array('findThreads' => 1)), 'sortOrder' => 'DESC', 'sortField' => 'time', 'types' => array('post'));
            $searchData = serialize($searchData);
            $sql = "INSERT INTO\twcf" . WCF_N . "_search\n\t\t\t\t\t\t(userID, searchData, searchDate, searchType, searchHash)\n\t\t\t\tVALUES\t\t(" . WCF::getUser()->userID . ",\n\t\t\t\t\t\t'" . escapeString($searchData) . "',\n\t\t\t\t\t\t" . TIME_NOW . ",\n\t\t\t\t\t\t'messages',\n\t\t\t\t\t\t'" . $searchHash . "')";
            WCF::getDB()->sendQuery($sql);
            $searchID = WCF::getDB()->getInsertID();
            // forward to result page
            HeaderUtil::redirect('index.php?form=Search&searchID=' . $searchID . SID_ARG_2ND_NOT_ENCODED);
            exit;
        } else {
            if ($eventName == 'readFormParameters') {
                if (isset($_POST['findThreads'])) {
                    $this->findThreads = intval($_POST['findThreads']);
                }
                if (isset($_REQUEST['findUserThreads'])) {
                    $this->findUserThreads = intval($_REQUEST['findUserThreads']);
                }
                if ($this->findUserThreads == 1) {
                    $this->findThreads = 1;
                }
                // handle findThreads option
                if ($this->findThreads == 1 && (!count($eventObj->types) || in_array('post', $eventObj->types))) {
                    // remove all other searchable message types
                    // findThreads only supports post search
                    $eventObj->types = array('post');
                } else {
                    $this->findThreads = 0;
                }
            } else {
                if ($eventName == 'assignVariables') {
                    if ($eventObj instanceof SearchResultPage) {
                        $html = '<div class="floatedElement">
						<label for="findThreads">' . WCF::getLanguage()->get('wbb.search.results.display') . '</label>
						<select name="findThreads" id="findThreads">
							<option value="0">' . WCF::getLanguage()->get('wbb.search.results.display.post') . '</option>
							<option value="1"' . ($eventObj->additionalData['post']['findThreads'] == 1 ? ' selected="selected"' : '') . '>' . WCF::getLanguage()->get('wbb.search.results.display.thread') . '</option>
						</select>
					</div>';
                        WCF::getTPL()->append('additionalDisplayOptions', $html);
                    } else {
                        $html = '<div class="floatedElement">
						<label for="findThreads">' . WCF::getLanguage()->get('wbb.search.results.display') . '</label>
						<select name="findThreads" id="findThreads">
							<option value="0"' . ($this->findThreads == 0 ? ' selected="selected"' : '') . '>' . WCF::getLanguage()->get('wbb.search.results.display.post') . '</option>
							<option value="1"' . ($this->findThreads == 1 ? ' selected="selected"' : '') . '>' . WCF::getLanguage()->get('wbb.search.results.display.thread') . '</option>
						</select>
					</div>';
                        WCF::getTPL()->append('additionalDisplayOptions', $html);
                        WCF::getTPL()->append('additionalAuthorOptions', '<label><input type="checkbox" name="findUserThreads" value="1"' . ($this->findUserThreads == 1 ? ' checked="checked"' : '') . '/> ' . WCF::getLanguage()->get('wbb.search.findUserThreads') . '</label>');
                    }
                }
            }
        }
    }