public function execute(array $deferred, array $data, $targetRunTime, &$status)
 {
     $data = array_merge(array('contentType' => '', 'contentIds' => false), $data);
     $status = sprintf('Search Index (Partial)');
     if (!$data['contentType'] || !$data['contentIds'] || !is_array($data['contentIds'])) {
         return false;
     }
     $limitTime = $targetRunTime > 0;
     do {
         $s = microtime(true);
         $i = 0;
         $contentIds = array();
         foreach ($data['contentIds'] as $key => $id) {
             $contentIds[] = $id;
             unset($data['contentIds'][$key]);
             if (++$i >= 10) {
                 break;
             }
         }
         $indexer = new XenForo_Search_Indexer();
         $indexer->quickIndex($data['contentType'], $contentIds);
         $targetRunTime -= microtime(true) - $s;
     } while ((!$limitTime || $targetRunTime > 0) && $data['contentIds']);
     if (!$data['contentIds']) {
         return false;
     }
     return $data;
 }
 public function execute(array $deferred, array $data, $targetRunTime, &$status)
 {
     $inputHandler = new XenForo_Input($data);
     $input = $inputHandler->filter(array('batch' => XenForo_Input::UINT, 'start' => XenForo_Input::UINT, 'extra_data' => XenForo_Input::ARRAY_SIMPLE, 'delay' => XenForo_Input::UNUM, 'content_type' => XenForo_Input::STRING, 'delete_index' => XenForo_Input::UINT));
     if ($input['delay'] >= 0.01) {
         usleep($input['delay'] * 1000000);
     }
     /* @var $searchModel XenForo_Model_Search */
     $searchModel = XenForo_Model::create('XenForo_Model_Search');
     $searchContentTypes = $searchModel->getSearchContentTypes();
     $extraData = $input['extra_data'];
     if (!isset($extraData['content_types']) || !is_array($extraData['content_types'])) {
         if ($input['content_type'] && isset($searchContentTypes[$input['content_type']])) {
             $extraData['content_types'] = array($input['content_type']);
         } else {
             $extraData['content_types'] = array_keys($searchContentTypes);
         }
     }
     if (empty($extraData['current_type'])) {
         $extraData['current_type'] = array_shift($extraData['content_types']);
     }
     if (empty($extraData['type_start'])) {
         $extraData['type_start'] = 0;
     }
     $originalExtraData = $extraData;
     while (!isset($searchContentTypes[$extraData['current_type']])) {
         if (!$extraData['content_types']) {
             return false;
         }
         $extraData['current_type'] = array_shift($extraData['content_types']);
     }
     if ($input['delete_index']) {
         $source = XenForo_Search_SourceHandler_Abstract::getDefaultSourceHandler();
         $source->deleteIndex($input['content_type'] ? $input['content_type'] : null);
     }
     $dataHandler = false;
     $searchHandler = $searchContentTypes[$extraData['current_type']];
     if (class_exists($searchHandler)) {
         $dataHandler = XenForo_Search_DataHandler_Abstract::create($searchHandler);
         $indexer = new XenForo_Search_Indexer();
         $indexer->setIsRebuild(true);
         $nextStart = $dataHandler->rebuildIndex($indexer, $extraData['type_start'], $input['batch']);
         $indexer->finalizeRebuildSet();
     } else {
         $nextStart = false;
     }
     if ($nextStart === false) {
         // move on to next type
         $extraData['current_type'] = '';
         $extraData['type_start'] = 0;
     } else {
         $extraData['type_start'] = $nextStart;
     }
     $data = array('batch' => $input['batch'], 'start' => $input['start'] + 1, 'extra_data' => $extraData, 'delay' => $input['delay']);
     $actionPhrase = new XenForo_Phrase('rebuilding');
     $typePhrase = new XenForo_Phrase('search_index');
     $text = $dataHandler ? $dataHandler->getSearchContentTypePhrase() : new XenForo_Phrase($originalExtraData['current_type']);
     $status = sprintf('%s... %s (%s)', $actionPhrase, $typePhrase, "{$text} " . XenForo_Locale::numberFormat($originalExtraData['type_start']));
     return $data;
 }
Example #3
0
 /**
  * Deletes one or more records from the index.
  *
  * @see XenForo_Search_DataHandler_Abstract::_deleteFromIndex()
  */
 protected function _deleteFromIndex(XenForo_Search_Indexer $indexer, array $dataList)
 {
     $ids = array();
     foreach ($dataList as $data) {
         $ids[] = is_array($data) ? $data['node_id'] : $data;
     }
     $indexer->deleteFromIndex('page', $ids);
 }
Example #4
0
 protected function _deleteFromIndex(XenForo_Search_Indexer $indexer, array $dataList)
 {
     $clientContentIds = array();
     foreach ($dataList as $data) {
         $clientContentIds[] = is_array($data) ? $data['client_content_id'] : $data;
     }
     $indexer->deleteFromIndex(self::CONTENT_TYPE, $clientContentIds);
 }
Example #5
0
 protected function _deleteFromIndex(XenForo_Search_Indexer $indexer, array $dataList)
 {
     $mediaIDs = array();
     foreach ($dataList as $data) {
         $mediaIDs[] = $data['media_id'];
     }
     $indexer->deleteFromIndex('media', $mediaIDs);
 }
 /**
  * Deletes one or more records from the index.
  *
  * @see XenForo_Search_DataHandler_Abstract::_deleteFromIndex()
  */
 protected function _deleteFromIndex(XenForo_Search_Indexer $indexer, array $dataList)
 {
     $conversationIds = array();
     foreach ($dataList as $data) {
         $conversationIds[] = $data['conversation_id'];
     }
     $indexer->deleteFromIndex('conversation', $conversationIds);
 }
Example #7
0
 /**
  * Deletes one or more records from the index.
  *
  * @see XenForo_Search_DataHandler_Abstract::_deleteFromIndex()
  */
 protected function _deleteFromIndex(XenForo_Search_Indexer $indexer, array $dataList)
 {
     $profilePostIds = array();
     foreach ($dataList as $data) {
         $profilePostIds[] = is_array($data) ? $data['profile_post_id'] : $data;
     }
     $indexer->deleteFromIndex('profile_post', $profilePostIds);
 }
Example #8
0
 /**
  * Deletes one or more records from the index.
  *
  * @see XenForo_Search_DataHandler_Abstract::_deleteFromIndex()
  */
 protected function _deleteFromIndex(XenForo_Search_Indexer $indexer, array $dataList)
 {
     $verseIds = array();
     foreach ($dataList as $data) {
         $verseIds[] = is_array($data) ? $data['verse_id'] : $data;
     }
     $indexer->deleteFromIndex('bible_verse', $verseIds);
 }
Example #9
0
 /**
  * Deletes one or more records from the index.
  *
  * @see XenForo_Search_DataHandler_Abstract::_deleteFromIndex()
  */
 protected function _deleteFromIndex(XenForo_Search_Indexer $indexer, array $dataList)
 {
     $checkOutIds = array();
     foreach ($dataList as $data) {
         $checkOutIds[] = $data['resource_check_out_id'];
     }
     $indexer->deleteFromIndex('resource_check_out', $checkOutIds);
 }
Example #10
0
 protected function _deleteFromIndex(XenForo_Search_Indexer $indexer, array $dataList)
 {
     $pageIDs = array();
     foreach ($dataList as $data) {
         $pageIDs[] = $data['page_id'];
     }
     $indexer->deleteFromIndex('wiki', $pageIDs);
 }
Example #11
0
 protected function _deleteFromIndex(XenForo_Search_Indexer $indexer, array $dataList)
 {
     $ids = array();
     foreach ($dataList as $data) {
         $ids[] = $data['node_id'];
     }
     $indexer->deleteFromIndex(Tinhte_XenTag_Constants::CONTENT_TYPE_FORUM, $ids);
 }
Example #12
0
 /**
  * Deletes one or more records from the index.
  *
  * @see XenForo_Search_DataHandler_Abstract::_deleteFromIndex()
  */
 protected function _deleteFromIndex(XenForo_Search_Indexer $indexer, array $dataList)
 {
     $postIds = array();
     foreach ($dataList as $data) {
         $postIds[] = $data['post_id'];
     }
     $indexer->deleteFromIndex('post', $postIds);
 }
Example #13
0
 /**
  * Deletes one or more records from the index.
  *
  * @see XenForo_Search_DataHandler_Abstract::_deleteFromIndex()
  */
 protected function _deleteFromIndex(XenForo_Search_Indexer $indexer, array $dataList)
 {
     $mediaIds = array();
     foreach ($dataList as $data) {
         if (!empty($data['media_id'])) {
             $mediaIds[] = $data['media_id'];
         }
     }
     $indexer->deleteFromIndex('xengallery_media', $mediaIds);
 }
Example #14
0
 /**
  * Deletes one or more records from the index.
  *
  * @see XenForo_Search_DataHandler_Abstract::_deleteFromIndex()
  *
  * @param XenForo_Search_Indexer $indexer
  * @param array $dataList
  */
 protected function _deleteFromIndex(XenForo_Search_Indexer $indexer, array $dataList)
 {
     $videoIds = array();
     foreach ($dataList as $data) {
         if (!is_array($data)) {
             $videoIds[] = $data['content_id'];
         } else {
             $videoIds[] = $data;
         }
     }
     $indexer->deleteFromIndex('sonnb_xengallery_video', $videoIds);
 }
Example #15
0
 /**
  * Deletes one or more records from the index.
  *
  * @see XenForo_Search_DataHandler_Abstract::_deleteFromIndex()
  */
 protected function _deleteFromIndex(XenForo_Search_Indexer $indexer, array $dataList)
 {
     $updateIds = array();
     foreach ($dataList as $data) {
         if (is_array($data)) {
             $updateIds[] = $data['resource_update_id'];
         } else {
             $updateIds[] = $data;
         }
     }
     $indexer->deleteFromIndex('resource_update', $updateIds);
 }
Example #16
0
 /**
  * Rebuilds the data.
  *
  * @see XenForo_CacheRebuilder_Abstract::rebuild()
  */
 public function rebuild($position = 0, array &$options = array(), &$detailedMessage = '')
 {
     $inputHandler = new XenForo_Input($options);
     $input = $inputHandler->filter(array('batch' => XenForo_Input::UINT, 'start' => XenForo_Input::UINT, 'extra_data' => XenForo_Input::ARRAY_SIMPLE, 'delay' => XenForo_Input::UNUM));
     if ($input['delay'] >= 0.01) {
         usleep($input['delay'] * 1000000);
     }
     /* @var $searchModel XenForo_Model_Search */
     $searchModel = XenForo_Model::create('XenForo_Model_Search');
     $searchContentTypes = $searchModel->getSearchContentTypes();
     // TODO: potentially look at truncating the table (user option?)
     $extraData = $input['extra_data'];
     if (!isset($extraData['content_types']) || !is_array($extraData['content_types'])) {
         $extraData['content_types'] = array_keys($searchContentTypes);
     }
     if (empty($extraData['current_type'])) {
         $extraData['current_type'] = array_shift($extraData['content_types']);
     }
     if (empty($extraData['type_start'])) {
         $extraData['type_start'] = 0;
     }
     $originalExtraData = $extraData;
     while (!isset($searchContentTypes[$extraData['current_type']])) {
         if (!$extraData['content_types']) {
             return true;
         }
         $extraData['current_type'] = array_shift($extraData['content_types']);
     }
     $searchHandler = $searchContentTypes[$extraData['current_type']];
     $dataHandler = XenForo_Search_DataHandler_Abstract::create($searchHandler);
     $indexer = new XenForo_Search_Indexer();
     $indexer->setIsRebuild(true);
     $nextStart = $dataHandler->rebuildIndex($indexer, $extraData['type_start'], $input['batch']);
     $indexer->finalizeRebuildSet();
     if ($nextStart === false) {
         // move on to next type
         $extraData['current_type'] = '';
         $extraData['type_start'] = 0;
     } else {
         $extraData['type_start'] = $nextStart;
     }
     $options = array('batch' => $input['batch'], 'start' => $input['start'] + 1, 'extra_data' => $extraData, 'delay' => $input['delay']);
     $detailedMessage = "({$originalExtraData['current_type']} " . XenForo_Locale::numberFormat($originalExtraData['type_start']) . ")";
     return 1;
 }
Example #17
0
 protected function _deleteFromSearchIndex()
 {
     $contentType = $this->_getXfContentType($this->get('content_type'));
     $contentId = $this->get('content_id');
     $indexer = new XenForo_Search_Indexer();
     $indexer->deleteFromIndex($contentType, $contentId);
 }
Example #18
0
 /**
  * Post-delete handling.
  */
 protected function _postDelete()
 {
     $this->getModelFromCache('XenForo_Model_Alert')->deleteAlerts('resource_update', $this->get('resource_update_id'));
     $this->_deleteAttachments();
     if ($this->get('message_state') == 'visible') {
         $resourceDw = XenForo_DataWriter::create('XenResource_DataWriter_Resource', XenForo_DataWriter::ERROR_SILENT);
         if ($resourceDw->setExistingData($this->get('resource_id'))) {
             $resourceDw->updateLastUpdate();
             $resourceDw->updateUpdateCount(-1);
             $resourceDw->save();
         }
     }
     $indexer = new XenForo_Search_Indexer();
     $indexer->deleteFromIndex('resource_update', $this->get('resource_update_id'));
     $this->_updateDeletionLog(true);
     $this->getModelFromCache('XenForo_Model_ModerationQueue')->deleteFromModerationQueue('resource_update', $this->get('resource_update_id'));
 }
Example #19
0
    /**
     * Merge multiple threads into a single thread
     *
     * @param array $threads
     * @param integer $targetThreadId
     * @param array $options
     *
     * @return boolean|array False if failure, otherwise thread array of merged thread
     */
    public function mergeThreads(array $threads, $targetThreadId, array $options = array())
    {
        if (!isset($threads[$targetThreadId])) {
            return false;
        }
        $targetThread = $threads[$targetThreadId];
        unset($threads[$targetThreadId]);
        $mergeFromThreadIds = array_keys($threads);
        if (!$mergeFromThreadIds) {
            return false;
        }
        $options = array_merge(array('redirect' => false, 'redirectExpiry' => 0), $options);
        $postModel = $this->_getPostModel();
        $db = $this->_getDb();
        $movePosts = $this->fetchAllKeyed('
			SELECT post_id, thread_id, user_id, message_state
			FROM xf_post
			WHERE thread_id IN (' . $db->quote($mergeFromThreadIds) . ')
		', 'post_id');
        $movePostIds = array_keys($movePosts);
        XenForo_Db::beginTransaction($db);
        $db->update('xf_post', array('thread_id' => $targetThreadId), 'post_id IN (' . $db->quote($movePostIds) . ')');
        $newCounters = $postModel->recalculatePostPositionsInThread($targetThreadId);
        if (!$newCounters['firstPostId']) {
            XenForo_Db::rollback($db);
            return false;
        }
        // TODO: user message counts will go off if merging from a visible thread into a hidden one or vice versa
        $threadDw = XenForo_DataWriter::create('XenForo_DataWriter_Discussion_Thread');
        $threadDw->setExistingData($targetThreadId);
        $threadDw->rebuildDiscussionCounters($newCounters['visibleCount'] - 1, $newCounters['firstPostId'], $newCounters['lastPostId']);
        $threadDw->save();
        if ($options['redirect']) {
            $targetUrl = XenForo_Link::buildPublicLink('threads', $targetThread);
            $redirectKey = "thread-{$targetThread['thread_id']}-";
            foreach ($threads as $thread) {
                $redirectDw = XenForo_DataWriter::create('XenForo_DataWriter_Discussion_Thread');
                $redirectDw->setExistingData($thread, true);
                $redirectDw->set('discussion_type', 'redirect');
                $redirectDw->save();
                $this->getModelFromCache('XenForo_Model_ThreadRedirect')->insertThreadRedirect($thread['thread_id'], $targetUrl, $redirectKey, $options['redirectExpiry']);
            }
            $idsQuoted = $db->quote($mergeFromThreadIds);
            $db->delete('xf_thread_watch', "thread_id IN ({$idsQuoted})");
            $db->delete('xf_thread_user_post', "thread_id IN ({$idsQuoted})");
        } else {
            foreach ($threads as $thread) {
                $deleteDw = XenForo_DataWriter::create('XenForo_DataWriter_Discussion_Thread');
                $deleteDw->setExistingData($thread, true);
                $deleteDw->delete();
            }
        }
        $forumIds = array();
        foreach ($threads as $thread) {
            $forumIds[$thread['node_id']] = $thread['node_id'];
        }
        foreach ($forumIds as $forumId) {
            $forumDw = XenForo_DataWriter::create('XenForo_DataWriter_Forum', XenForo_DataWriter::ERROR_SILENT);
            $forumDw->setExistingData($forumId);
            $forumDw->rebuildCounters();
            $forumDw->save();
        }
        $this->replaceThreadUserPostCounters($targetThreadId, $newCounters['userPosts']);
        $indexer = new XenForo_Search_Indexer();
        $indexer->quickIndex('post', $movePostIds);
        XenForo_Db::commit($db);
        return $threadDw->getMergedData();
    }
Example #20
0
 public function insertIntoIndex($contentType, $contentId, $title, $message, $itemDate, $userId, $discussionId = 0, array $metadata = array())
 {
     $metadata = XenForo_Application::mapMerge($metadata, $this->_extraMetadata);
     return parent::insertIntoIndex($contentType, $contentId, $title, $message, $itemDate, $userId, $discussionId, $metadata);
 }
Example #21
0
 protected function _postDelete()
 {
     $albumId = $this->get('album_id');
     $albumModel = $this->_getAlbumModel();
     $contentType = sonnb_XenGallery_Model_Album::$contentType;
     $this->_logIpExtend('delete');
     if ($this->get('category_id') && $this->get('album_state') === 'visible') {
         $albumModel->modifyAlbumCount($this->getExisting('category_id'), -1);
     }
     if ($this->get('collection_id') && $this->get('album_state') === 'visible') {
         $albumModel->modifyCollectionCount($this->getExisting('collection_id'), -1, $this->getMergedData());
     }
     if ($this->get('content_count')) {
         $contents = $this->_getContentModel()->getContentsByAlbumId($this->getExisting('album_id'));
         if ($contents) {
             foreach ($contents as $contentId => $content) {
                 $contentDw = $this->_getXfContentDw($content['content_type'], XenForo_DataWriter::ERROR_SILENT);
                 $contentDw->setExistingData($contentId);
                 $contentDw->delete();
             }
         }
     }
     $this->_db->delete('sonnb_xengallery_watch', "content_id = {$albumId} AND content_type='{$contentType}'");
     $this->_db->delete('sonnb_xengallery_location', "content_id = {$albumId} AND content_type='{$contentType}'");
     $this->_db->delete('sonnb_xengallery_tag', "content_id = {$albumId} AND content_type='{$contentType}'");
     $this->_db->delete('sonnb_xengallery_stream', "content_id = {$albumId} AND content_type='{$contentType}'");
     $this->rebuildUserAlbumCount();
     $this->_deleteFromNewsFeed();
     $this->_deleteFromAlert();
     $this->_updateDeletionLog(true);
     $this->_updateModerationQueue(true);
     if ($this->get('comment_count')) {
         $comments = $this->_getCommentModel()->getCommentsByContentId(sonnb_XenGallery_Model_Album::$contentType, $albumId);
         if ($comments) {
             foreach ($comments as $comment) {
                 $dw = XenForo_DataWriter::create('sonnb_XenGallery_DataWriter_Comment', XenForo_DataWriter::ERROR_SILENT);
                 $dw->setExistingData($comment, true);
                 $dw->delete();
             }
         }
     }
     $indexer = new XenForo_Search_Indexer();
     $indexer->deleteFromIndex('sonnb_xengallery_album', $albumId);
     $this->_getFieldModel()->deleteFieldValueByContentId(sonnb_XenGallery_Model_Album::$contentType, $albumId);
     //TODO: Write to history
 }
Example #22
0
    /**
     * Post-delete handling.
     */
    protected function _postDelete()
    {
        $versionIds = $this->_db->fetchCol('
			SELECT resource_version_id
			FROM xf_resource_version
			WHERE resource_id = ?
		', $this->get('resource_id'));
        $this->getModelFromCache('XenForo_Model_Attachment')->deleteAttachmentsFromContentIds('resource_version', $versionIds);
        if ($versionIds) {
            $this->_db->delete('xf_resource_download', 'resource_version_id IN (' . $this->_db->quote($versionIds) . ')');
        }
        $updateIds = $this->_db->fetchCol('
			SELECT resource_update_id
			FROM xf_resource_update
			WHERE resource_id = ?
		', $this->get('resource_id'));
        $this->getModelFromCache('XenForo_Model_Attachment')->deleteAttachmentsFromContentIds('resource_update', $updateIds);
        $this->getModelFromCache('XenForo_Model_Alert')->deleteAlerts('resource_update', $updateIds);
        $ratingIds = $this->_db->fetchCol('
			SELECT resource_rating_id
			FROM xf_resource_rating
			WHERE resource_id = ?
		', $this->get('resource_id'));
        $this->getModelFromCache('XenForo_Model_Alert')->deleteAlerts('resource_rating', $ratingIds);
        $idQuoted = $this->_db->quote($this->get('resource_id'));
        $this->_db->delete('xf_resource_feature', 'resource_id = ' . $idQuoted);
        $this->_db->delete('xf_resource_field_value', 'resource_id = ' . $idQuoted);
        $this->_db->delete('xf_resource_rating', 'resource_id = ' . $idQuoted);
        $this->_db->delete('xf_resource_update', 'resource_id = ' . $idQuoted);
        $this->_db->delete('xf_resource_version', 'resource_id = ' . $idQuoted);
        $this->_db->delete('xf_resource_watch', 'resource_id = ' . $idQuoted);
        $indexer = new XenForo_Search_Indexer();
        $indexer->deleteFromIndex('resource_update', $updateIds);
        if ($this->getExisting('resource_state') == 'visible') {
            $this->_resourceRemoved();
        }
        $this->_updateDeletionLog(true);
        $this->getModelFromCache('XenForo_Model_ModerationQueue')->deleteFromModerationQueue('resource', $this->get('resource_id'));
        $filePath = $this->_getResourceModel()->getResourceIconFilePath($this->get('resource_id'));
        @unlink($filePath);
    }
Example #23
0
 /**
  * Moves the specified posts (in the given threads) to a new thread. The
  * new thread will be created in this function.
  *
  * @param array $posts
  * @param array $sourceThreads
  * @param array $newThread Information about the new thread; first/last/poster info filled in automatically
  *
  * @return array|false New thread ID or false
  */
 public function movePostsToNewThread(array $posts, array $sourceThreads, array $newThread)
 {
     if (!$posts) {
         return false;
     }
     $firstPostId = 0;
     $firstPostDate = PHP_INT_MAX;
     foreach ($posts as $postId => $post) {
         $sourceThreadIds[$post['thread_id']] = true;
         if ($post['post_date'] < $firstPostDate || $post['post_date'] == $firstPostDate && $post['post_id'] < $firstPostId) {
             $firstPostId = $postId;
             $firstPostDate = $post['post_date'];
         }
     }
     if (!isset($posts[$firstPostId])) {
         return false;
     }
     $firstPost = $posts[$firstPostId];
     $postIds = array_keys($posts);
     // TODO: consider user message count issues if moving from invisible to visible, etc
     unset($newThread['thread_id']);
     $newThread['first_post_id'] = $firstPostId;
     $newThread['post_date'] = $firstPost['post_date'];
     $newThread['user_id'] = $firstPost['user_id'];
     $newThread['username'] = $firstPost['username'];
     $newThread['discussion_state'] = $firstPost['message_state'];
     $db = $this->_getDb();
     XenForo_Db::beginTransaction($db);
     $threadDw = XenForo_DataWriter::create('XenForo_DataWriter_Discussion_Thread');
     $threadDw->setOption(XenForo_DataWriter_Discussion::OPTION_REQUIRE_INSERT_FIRST_MESSAGE, false);
     $threadDw->bulkSet($newThread);
     $threadDw->save();
     $newThread = $threadDw->getMergedData();
     $db->update('xf_post', array('thread_id' => $newThread['thread_id']), 'post_id IN (' . $db->quote($postIds) . ')');
     $firstPostDw = XenForo_DataWriter::create('XenForo_DataWriter_DiscussionMessage_Post', XenForo_DataWriter::ERROR_SILENT);
     $firstPostDw->setExistingData($firstPostId);
     $firstPostDw->set('message_state', 'visible');
     $firstPostDw->save();
     $newCounters = $this->recalculatePostPositionsInThread($newThread['thread_id']);
     $threadDw = XenForo_DataWriter::create('XenForo_DataWriter_Discussion_Thread');
     $threadDw->setExistingData($newThread, true);
     $threadDw->set('reply_count', $newCounters['visibleCount'] - 1);
     $threadDw->updateLastPost();
     $threadDw->save();
     $this->_getThreadModel()->replaceThreadUserPostCounters($newThread['thread_id'], $newCounters['userPosts']);
     $reindexPostIds = $postIds;
     foreach ($sourceThreads as $sourceThread) {
         $threadDw = XenForo_DataWriter::create('XenForo_DataWriter_Discussion_Thread');
         $threadDw->setExistingData($sourceThread, true);
         $newSourceCounters = $this->recalculatePostPositionsInThread($sourceThread['thread_id']);
         if (!$newSourceCounters['firstPostId']) {
             // all posts removed -> delete
             $threadDw->delete();
         } else {
             if ($newSourceCounters['firstPostId'] != $sourceThread['first_post_id']) {
                 $sourceFirstPost = $this->getPostById($newSourceCounters['firstPostId']);
                 $threadDw->set('first_post_id', $sourceFirstPost['post_id']);
                 $threadDw->set('post_date', $sourceFirstPost['post_date']);
                 $threadDw->set('user_id', $sourceFirstPost['user_id']);
                 $threadDw->set('username', $sourceFirstPost['username']);
                 // TODO: possible situation where new first post is moderated
                 $reindexPostIds[] = $sourceFirstPost['post_id'];
             }
             $threadDw->set('reply_count', $newSourceCounters['visibleCount'] - 1);
             if ($newSourceCounters['lastPostId'] != $sourceThread['last_post_id']) {
                 $threadDw->updateLastPost();
             }
             $threadDw->save();
             $this->_getThreadModel()->replaceThreadUserPostCounters($sourceThread['thread_id'], $newSourceCounters['userPosts']);
         }
     }
     $indexer = new XenForo_Search_Indexer();
     $indexer->quickIndex('post', $reindexPostIds);
     XenForo_Db::commit($db);
     return $newThread;
 }