Файл: library/XenForo/Model/InlineMod/Post.php
Строк: 932
<?php
/**
* Model to handle inline mod-style actions on posts. Generally, these are simply
* bulk actions. They can be applied to other circumstances if desired.
*
* @package XenForo_Post
*/
class XenForo_Model_InlineMod_Post extends XenForo_Model
{
public $enableLogging = true;
/**
* Determines if the selected post IDs can be deleted.
*
* @param array $postIds List of post IDs check
* @param string $deleteType The type of deletion being requested (soft or hard)
* @param string $errorKey Modified by reference. If no permission, may include a key of a phrase that gives more info
* @param array|null $viewingUser
*
* @return boolean
*/
public function canDeletePosts(array $postIds, $deleteType = 'soft', &$errorKey = '', array $viewingUser = null)
{
list($posts, $threads, $forums) = $this->getPostsAndParentData($postIds, $viewingUser);
return $this->canDeletePostsData($posts, $deleteType, $threads, $forums, $errorKey, $viewingUser);
}
/**
* Determines if the selected posts can be deleted. This is a slightly more
* "internal" version of the canDeletePosts() function, as the required data
* must already be retrieved.
*
* @param array $posts List of information about posts to be deleted
* @param string $deleteType Type of deletion (soft or hard)
* @param array $threads List of information about threads the posts are in
* @param array $forums List of information about forums the threads/posts are in; must include unserialized permissions in 'nodePermissions' key
* @param string $errorKey Modified by reference. If no permission, may include a key of a phrase that gives more info
* @param array|null $viewingUser
*
* @return boolean
*/
public function canDeletePostsData(array $posts, $deleteType, array $threads, array $forums, &$errorKey = '', array $viewingUser = null)
{
// note: this cannot use _checkPermissionOnPosts because of extra param
if (!$posts)
{
return true;
}
$this->standardizeViewingUserReference($viewingUser);
$postModel = $this->_getPostModel();
foreach ($posts AS $post)
{
list($thread, $forum) = $this->_getThreadAndForumFromPost($post, $threads, $forums);
if (!$postModel->canDeletePost($post, $thread, $forum, $deleteType, $errorKey, $forum['nodePermissions'], $viewingUser))
{
return false;
}
}
return true;
}
/**
* Deletes the specified posts if permissions are sufficient.
*
* @param array $postIds List of post IDs to delete
* @param array $options Options that control the delete. Supports deleteType (soft or hard).
* @param string $errorKey Modified by reference. If no permission, may include a key of a phrase that gives more info
* @param array|null $viewingUser
*
* @return boolean True if permissions were ok
*/
public function deletePosts(array $postIds, array $options = array(), &$errorKey = '', array $viewingUser = null)
{
$options = array_merge(
array(
'deleteType' => '',
'reason' => ''
), $options
);
if (!$options['deleteType'])
{
throw new XenForo_Exception('No deletion type specified.');
}
list($posts, $threads, $forums) = $this->getPostsAndParentData($postIds, $viewingUser);
if (empty($options['skipPermissions']) && !$this->canDeletePostsData($posts, $options['deleteType'], $threads, $forums, $errorKey, $viewingUser))
{
return false;
}
$skipThreads = array();
foreach ($posts AS $post)
{
if (!empty($skipThreads[$post['thread_id']]))
{
continue;
}
list($thread, $forum) = $this->_getThreadAndForumFromPost($post, $threads, $forums);
$dw = XenForo_DataWriter::create('XenForo_DataWriter_DiscussionMessage_Post', XenForo_DataWriter::ERROR_SILENT);
$dw->setExistingData($post);
if (!$dw->get('post_id'))
{
// this may happen if the post was already removed
continue;
}
$dw->setExtraData(XenForo_DataWriter_DiscussionMessage_Post::DATA_FORUM, $forum);
if ($options['deleteType'] == 'hard')
{
$dw->delete();
if ($dw->discussionDeleted())
{
if ($this->enableLogging)
{
XenForo_Model_Log::logModeratorAction('thread', $thread, 'delete_hard');
}
// all posts in this thread have been removed, so skip them
$skipThreads[$post['thread_id']] = true;
}
else
{
if ($this->enableLogging)
{
XenForo_Model_Log::logModeratorAction('post', $post, 'delete_hard', array(), $thread);
}
}
}
else
{
$dw->setExtraData(XenForo_DataWriter_DiscussionMessage::DATA_DELETE_REASON, $options['reason']);
$dw->set('message_state', 'deleted');
$dw->save();
if ($this->enableLogging)
{
XenForo_Model_Log::logModeratorAction(
'post', $post, 'delete_soft', array('reason' => $options['reason']), $thread
);
}
}
}
return true;
}
/**
* Determines if the selected post IDs can be undeleted.
*
* @param array $postIds List of post IDs check
* @param string $errorKey Modified by reference. If no permission, may include a key of a phrase that gives more info
* @param array|null $viewingUser
*
* @return boolean
*/
public function canUndeletePosts(array $postIds, &$errorKey = '', array $viewingUser = null)
{
list($posts, $threads, $forums) = $this->getPostsAndParentData($postIds, $viewingUser);
return $this->canUndeletePostsData($posts, $threads, $forums, $errorKey, $viewingUser);
}
/**
* Determines if the selected posts can be undeleted. This is a slightly more
* "internal" version of the canUndeletePosts() function, as the required data
* must already be retrieved.
*
* @param array $posts List of information about posts to be checked
* @param array $threads List of information about threads the posts are in
* @param array $forums List of information about forums the threads/posts are in; must include unserialized permissions in 'nodePermissions' key
* @param string $errorKey Modified by reference. If no permission, may include a key of a phrase that gives more info
* @param array|null $viewingUser
*
* @return boolean
*/
public function canUndeletePostsData(array $posts, array $threads, array $forums, &$errorKey = '', array $viewingUser = null)
{
return $this->_checkPermissionOnPosts('canUndeletePost', $posts, $threads, $forums, $errorKey, $viewingUser);
}
/**
* Undeletes the specified posts if permissions are sufficient.
*
* @param array $postIds List of post IDs to undelete
* @param array $options Options that control the action. Nothing supported at this time.
* @param string $errorKey Modified by reference. If no permission, may include a key of a phrase that gives more info
* @param array|null $viewingUser
*
* @return boolean True if permissions were ok
*/
public function undeletePosts(array $postIds, array $options = array(), &$errorKey = '', array $viewingUser = null)
{
list($posts, $threads, $forums) = $this->getPostsAndParentData($postIds, $viewingUser);
if (empty($options['skipPermissions']) && !$this->canUndeletePostsData($posts, $threads, $forums, $errorKey, $viewingUser))
{
return false;
}
$this->_updatePostsMessageState($posts, $threads, $forums, 'visible', 'deleted');
return true;
}
/**
* Determines if the selected post IDs can be approved/unapproved.
*
* @param array $postIds List of post IDs check
* @param string $errorKey Modified by reference. If no permission, may include a key of a phrase that gives more info
* @param array|null $viewingUser
*
* @return boolean
*/
public function canApproveUnapprovePosts(array $postIds, &$errorKey = '', array $viewingUser = null)
{
list($posts, $threads, $forums) = $this->getPostsAndParentData($postIds, $viewingUser);
return $this->canApproveUnapprovePostsData($posts, $threads, $forums, $errorKey, $viewingUser);
}
/**
* Determines if the selected posts can be approved/unapproved. This is a slightly more
* "internal" version of the canApproveUnapprovePosts() function, as the required data
* must already be retrieved.
*
* @param array $posts List of information about posts to be checked
* @param array $threads List of information about threads the posts are in
* @param array $forums List of information about forums the threads/posts are in; must include unserialized permissions in 'nodePermissions' key
* @param string $errorKey Modified by reference. If no permission, may include a key of a phrase that gives more info
* @param array|null $viewingUser
*
* @return boolean
*/
public function canApproveUnapprovePostsData(array $posts, array $threads, array $forums, &$errorKey = '', array $viewingUser = null)
{
return $this->_checkPermissionOnPosts('canApproveUnapprovePost', $posts, $threads, $forums, $errorKey, $viewingUser);
}
/**
* Approves the specified posts if permissions are sufficient.
*
* @param array $postIds List of post IDs to approve
* @param array $options Options that control the action. Nothing supported at this time.
* @param string $errorKey Modified by reference. If no permission, may include a key of a phrase that gives more info
* @param array|null $viewingUser
*
* @return boolean True if permissions were ok
*/
public function approvePosts(array $postIds, array $options = array(), &$errorKey = '', array $viewingUser = null)
{
list($posts, $threads, $forums) = $this->getPostsAndParentData($postIds, $viewingUser);
if (empty($options['skipPermissions']) && !$this->canApproveUnapprovePostsData($posts, $threads, $forums, $errorKey, $viewingUser))
{
return false;
}
$this->_updatePostsMessageState($posts, $threads, $forums, 'visible', 'moderated');
return true;
}
/**
* Unapproves the specified posts if permissions are sufficient.
*
* @param array $postIds List of post IDs to unapprove
* @param array $options Options that control the action. Nothing supported at this time.
* @param string $errorKey Modified by reference. If no permission, may include a key of a phrase that gives more info
* @param array|null $viewingUser
*
* @return boolean True if permissions were ok
*/
public function unapprovePosts(array $postIds, array $options = array(), &$errorKey = '', array $viewingUser = null)
{
list($posts, $threads, $forums) = $this->getPostsAndParentData($postIds, $viewingUser);
if (empty($options['skipPermissions']) && !$this->canApproveUnapprovePostsData($posts, $threads, $forums, $errorKey, $viewingUser))
{
return false;
}
$this->_updatePostsMessageState($posts, $threads, $forums, 'moderated', 'visible');
return true;
}
/**
* Determines if the selected post IDs can be moved.
*
* @param array $postIds List of post IDs check
* @param string $errorKey Modified by reference. If no permission, may include a key of a phrase that gives more info
* @param array|null $viewingUser
*
* @return boolean
*/
public function canMovePosts(array $postIds, &$errorKey = '', array $viewingUser = null)
{
list($posts, $threads, $forums) = $this->getPostsAndParentData($postIds, $viewingUser);
return $this->canMovePostsData($posts, $threads, $forums, $errorKey, $viewingUser);
}
/**
* Determines if the selected posts can be moved. This is a slightly more
* "internal" version of the canMovePosts() function, as the required data
* must already be retrieved.
*
* @param array $posts List of information about posts to be checked
* @param array $threads List of information about threads the posts are in
* @param array $forums List of information about forums the threads/posts are in; must include unserialized permissions in 'nodePermissions' key
* @param string $errorKey Modified by reference. If no permission, may include a key of a phrase that gives more info
* @param array|null $viewingUser
*
* @return boolean
*/
public function canMovePostsData(array $posts, array $threads, array $forums, &$errorKey = '', array $viewingUser = null)
{
return $this->_checkPermissionOnPosts('canMovePost', $posts, $threads, $forums, $errorKey, $viewingUser);
}
/**
* Moves the specified posts if permissions are sufficient.
*
* @param array $postIds List of post IDs to move
* @param array $options Options that control the action. Nothing supported at this time.
* @param string $errorKey Modified by reference. If no permission, may include a key of a phrase that gives more info
* @param array|null $viewingUser
*
* @return array|false False if error; array of info about new thread otherwise
*/
public function movePosts(array $postIds, array $options = array(), &$errorKey = '', array $viewingUser = null)
{
list($posts, $threads, $forums) = $this->getPostsAndParentData($postIds, $viewingUser);
if (empty($options['skipPermissions']) && !$this->canMovePostsData($posts, $threads, $forums, $errorKey, $viewingUser))
{
return false;
}
return $this->_moveOrCopyPosts(
'move', $posts, $threads, $forums, $options, $errorKey
);
}
/**
* Determines if the selected post IDs can be copied.
*
* @param array $postIds List of post IDs check
* @param string $errorKey Modified by reference. If no permission, may include a key of a phrase that gives more info
* @param array|null $viewingUser
*
* @return boolean
*/
public function canCopyPosts(array $postIds, &$errorKey = '', array $viewingUser = null)
{
list($posts, $threads, $forums) = $this->getPostsAndParentData($postIds, $viewingUser);
return $this->canMovePostsData($posts, $threads, $forums, $errorKey, $viewingUser);
}
/**
* Determines if the selected posts can be copied. This is a slightly more
* "internal" version of the canCopyPosts() function, as the required data
* must already be retrieved.
*
* @param array $posts List of information about posts to be checked
* @param array $threads List of information about threads the posts are in
* @param array $forums List of information about forums the threads/posts are in; must include unserialized permissions in 'nodePermissions' key
* @param string $errorKey Modified by reference. If no permission, may include a key of a phrase that gives more info
* @param array|null $viewingUser
*
* @return boolean
*/
public function canCopyPostsData(array $posts, array $threads, array $forums, &$errorKey = '', array $viewingUser = null)
{
return $this->_checkPermissionOnPosts('canCopyPost', $posts, $threads, $forums, $errorKey, $viewingUser);
}
/**
* Copies the specified posts if permissions are sufficient.
*
* @param array $postIds List of post IDs to copy
* @param array $options Options that control the action. Nothing supported at this time.
* @param string $errorKey Modified by reference. If no permission, may include a key of a phrase that gives more info
* @param array|null $viewingUser
*
* @return array|false False if error; array of info about new thread otherwise
*/
public function copyPosts(array $postIds, array $options = array(), &$errorKey = '', array $viewingUser = null)
{
list($posts, $threads, $forums) = $this->getPostsAndParentData($postIds, $viewingUser);
if (empty($options['skipPermissions']) && !$this->canCopyPostsData($posts, $threads, $forums, $errorKey, $viewingUser))
{
return false;
}
return $this->_moveOrCopyPosts(
'copy', $posts, $threads, $forums, $options, $errorKey
);
}
protected function _moveOrCopyPosts($action, array $posts, array $threads, array $forums, array $options, &$errorKey = '')
{
$options = array_merge(
array(
'threadType' => 'new',
'threadNodeId' => 0,
'threadTitle' => '',
'threadPrefixId' => 0,
'threadId' => 0
),
$options
);
if ($options['threadType'] == 'existing')
{
$newThread = $this->_getThreadModel()->getThreadById($options['threadId']);
if (!$newThread)
{
return false;
}
}
else
{
$forum = $this->_getForumModel()->getForumById($options['threadNodeId']);
if (!$forum)
{
$errorKey = 'please_select_valid_forum';
return false;
}
$newThread = array(
'node_id' => $options['threadNodeId'],
'title' => $options['threadTitle'],
'prefix_id' => $options['threadPrefixId'],
);
}
$actionOptions = array('log' => $this->enableLogging);
if ($action == 'move')
{
return $this->_getPostModel()->movePosts($posts, $threads, $newThread, $actionOptions);
}
else if ($action == 'copy')
{
return $this->_getPostModel()->copyPosts($posts, $threads, $newThread, $actionOptions);
}
else
{
return false;
}
}
/**
* Determines if the selected post IDs can be merged.
*
* @param array $postIds List of post IDs check
* @param string $errorKey Modified by reference. If no permission, may include a key of a phrase that gives more info
* @param array|null $viewingUser
*
* @return boolean
*/
public function canMergePosts(array $postIds, &$errorKey = '', array $viewingUser = null)
{
list($posts, $threads, $forums) = $this->getPostsAndParentData($postIds, $viewingUser);
return $this->canMergePostsData($posts, $threads, $forums, $errorKey, $viewingUser);
}
/**
* Determines if the selected posts can be merged. This is a slightly more
* "internal" version of the canMergePosts() function, as the required data
* must already be retrieved.
*
* @param array $posts List of information about posts to be checked
* @param array $threads List of information about threads the posts are in
* @param array $forums List of information about forums the threads/posts are in; must include unserialized permissions in 'nodePermissions' key
* @param string $errorKey Modified by reference. If no permission, may include a key of a phrase that gives more info
* @param array|null $viewingUser
*
* @return boolean
*/
public function canMergePostsData(array $posts, array $threads, array $forums, &$errorKey = '', array $viewingUser = null)
{
if (count($posts) <= 1)
{
$errorKey = 'please_select_more_one_post_merge';
return false;
}
return $this->_checkPermissionOnPosts('canMergePost', $posts, $threads, $forums, $errorKey, $viewingUser);
}
/**
* Merges the specified posts if permissions are sufficient.
*
* @param array $postIds List of post IDs to merge
* @param array $options Options that control the action. Nothing supported at this time.
* @param string $errorKey Modified by reference. If no permission, may include a key of a phrase that gives more info
* @param array|null $viewingUser
*
* @return boolean True if permissions were ok
*/
public function mergePosts(array $postIds, array $options = array(), &$errorKey = '', array $viewingUser = null)
{
list($posts, $threads, $forums) = $this->getPostsAndParentData($postIds, $viewingUser);
if (empty($options['skipPermissions']) && !$this->canMergePostsData($posts, $threads, $forums, $errorKey, $viewingUser))
{
return false;
}
$options = array_merge(
array(
'targetPostId' => 0,
'newMessage' => '',
'log' => $this->enableLogging
),
$options
);
return $this->_getPostModel()->mergePosts($posts, $threads, $options['targetPostId'], $options['newMessage'], $options);
}
/**
* Checks a standard post permission against a collection of posts.
* True is returned only if the action is possible on all posts.
*
* @param string $permissionMethod Name of the permission method to call in the post model
* @param array $posts List of posts to check
* @param array $threads List of threads the posts are in
* @param array $forums List of forums the threads are in
* @param string $errorKey Modified by reference. If no permission, may include a key of a phrase that gives more info
* @param array|null $viewingUser
*
* @return boolean
*/
protected function _checkPermissionOnPosts($permissionMethod, array $posts, array $threads, array $forums, &$errorKey = '', array $viewingUser = null)
{
if (!$posts)
{
return true;
}
$this->standardizeViewingUserReference($viewingUser);
$postModel = $this->_getPostModel();
foreach ($posts AS $post)
{
list($thread, $forum) = $this->_getThreadAndForumFromPost($post, $threads, $forums);
if (!$postModel->$permissionMethod($post, $thread, $forum, $errorKey, $forum['nodePermissions'], $viewingUser))
{
return false;
}
}
return true;
}
/**
* Internal helper to update the message_state of a collection of posts.
*
* @param array $posts Information about the posts to update
* @param array $threads Information about the threads that the posts are in
* @param array $forums Information about the forums that the threads/posts are in
* @param string $newState New message state (visible, moderated, deleted)
* @param string|false $expectedOldState If specified, only updates if the old state matches
*/
protected function _updatePostsMessageState(array $posts, array $threads, array $forums, $newState, $expectedOldState = false)
{
switch ($newState)
{
case 'visible':
switch (strval($expectedOldState))
{
case 'visible': return;
case 'moderated': $logAction = 'approve'; break;
case 'deleted': $logAction = 'undelete'; break;
default: $logAction = 'undelete'; break;
}
break;
case 'moderated':
switch (strval($expectedOldState))
{
case 'visible': $logAction = 'unapprove'; break;
case 'moderated': return;
case 'deleted': $logAction = 'unapprove'; break;
default: $logAction = 'unapprove'; break;
}
break;
case 'deleted':
switch (strval($expectedOldState))
{
case 'visible': $logAction = 'delete_soft'; break;
case 'moderated': $logAction = 'delete_soft'; break;
case 'deleted': return;
default: $logAction = 'delete_soft'; break;
}
break;
default: return;
}
foreach ($posts AS $post)
{
list($thread, $forum) = $this->_getThreadAndForumFromPost($post, $threads, $forums);
if ($post['post_id'] == $thread['first_post_id'])
{
$dw = XenForo_DataWriter::create('XenForo_DataWriter_Discussion_Thread', XenForo_DataWriter::ERROR_SILENT);
$dw->setExistingData($thread);
if ($expectedOldState && $dw->get('discussion_state') != $expectedOldState)
{
continue;
}
$dw->set('discussion_state', $newState);
$dw->setExtraData(XenForo_DataWriter_Discussion_Thread::DATA_FORUM, $forum);
$dw->save();
if ($this->enableLogging)
{
XenForo_Model_Log::logModeratorAction('thread', $thread, $logAction);
}
}
else
{
if ($expectedOldState && $post['message_state'] != $expectedOldState)
{
continue;
}
$dw = XenForo_DataWriter::create('XenForo_DataWriter_DiscussionMessage_Post', XenForo_DataWriter::ERROR_SILENT);
$dw->setExistingData($post);
$dw->set('message_state', $newState);
$dw->setExtraData(XenForo_DataWriter_DiscussionMessage_Post::DATA_FORUM, $forum);
$dw->save();
if ($this->enableLogging)
{
XenForo_Model_Log::logModeratorAction('post', $post, $logAction, array(), $thread);
}
}
}
}
/**
* Gets information about the thread and forum a post belongs to,
* from the post's info.
*
* @param array $post Info about the post
* @param array $threads List of threads that the post could belong to
* @param array $forums List of forums that the post's thread could be long to
*
* @return array Format: [0] => thread, [1] => forum
*/
protected function _getThreadAndForumFromPost(array $post, array $threads, array $forums)
{
$thread = $threads[$post['thread_id']];
$forum = $forums[$thread['node_id']];
return array($thread, $forum);
}
/**
* From a list of post IDs, gets info about the posts, their threads, and
* the forums the threads are in.
*
* @param array $postIds List of post IDs
* @param array|null $viewingUser
*
* @return array Format: [0] => list of posts, [1] => list of threads, [2] => list of forums
*/
public function getPostsAndParentData(array $postIds, array $viewingUser = null)
{
$this->standardizeViewingUserReference($viewingUser);
return $this->_getPostModel()->getPostsAndParentData($postIds, $viewingUser['permission_combination_id']);
}
/**
* @return XenForo_Model_Post
*/
protected function _getPostModel()
{
return $this->getModelFromCache('XenForo_Model_Post');
}
/**
* @return XenForo_Model_Thread
*/
protected function _getThreadModel()
{
return $this->getModelFromCache('XenForo_Model_Thread');
}
/**
* @return XenForo_Model_Forum
*/
protected function _getForumModel()
{
return $this->getModelFromCache('XenForo_Model_Forum');
}
}