/** * @param FormatterRow $row * @return array * @throws FlowException */ public function buildActions(FormatterRow $row) { $user = $this->permissions->getUser(); $workflow = $row->workflow; $title = $workflow->getArticleTitle(); // If a user is blocked from performing actions on this page return // an empty array of actions. // // We only check actual users and not anon's because the anonymous // version can be cached and served to many different ip addresses // which will not all be blocked. if (!$user->isAnon() && ($user->isBlockedFrom($title, true) || !$title->quickUserCan('edit', $user))) { return array(); } $revision = $row->revision; $action = $revision->getChangeType(); $workflowId = $workflow->getId(); $revId = $revision->getRevisionId(); $postId = method_exists($revision, 'getPostId') ? $revision->getPostId() : null; $actionTypes = $this->permissions->getActions()->getValue($action, 'actions'); if ($actionTypes === null) { wfDebugLog('Flow', __METHOD__ . ": No actions defined for action: {$action}"); return array(); } // actions primarily vary by revision type... $links = array(); foreach ($actionTypes as $type) { if (!$this->permissions->isAllowed($revision, $type)) { continue; } switch ($type) { case 'thank': if (class_exists('ThanksHooks') && !$user->isAnon() && $revision instanceof PostRevision && $revision->getCreatorId() > 0 && $user->getId() !== $revision->getCreatorId()) { $links['thank'] = $this->urlGenerator->thankAction($postId); } break; case 'reply': if (!$postId) { throw new FlowException("{$type} called without \$postId"); } elseif (!$revision instanceof PostRevision) { throw new FlowException("{$type} called without PostRevision object"); } /* * If the post being replied to is the most recent post * of its depth, the reply link should point to parent */ $replyToId = $postId; $replyToRevision = $revision; if ($row->isLastReply) { $replyToId = $replyToRevision->getReplyToId(); $replyToRevision = PostCollection::newFromId($replyToId)->getLastRevision(); } /* * If the post being replied to is at or exceeds the max * threading depth, the reply link should point to parent. */ while ($replyToRevision->getDepth() >= $this->maxThreadingDepth) { $replyToId = $replyToRevision->getReplyToId(); $replyToRevision = PostCollection::newFromId($replyToId)->getLastRevision(); } $links['reply'] = $this->urlGenerator->replyAction($title, $workflowId, $replyToId, $revision->isTopicTitle()); break; case 'edit-header': $links['edit'] = $this->urlGenerator->editHeaderAction($title, $workflowId, $revId); break; case 'edit-title': if (!$postId) { throw new FlowException("{$type} called without \$postId"); } $links['edit'] = $this->urlGenerator->editTitleAction($title, $workflowId, $postId, $revId); break; case 'edit-post': if (!$postId) { throw new FlowException("{$type} called without \$postId"); } $links['edit'] = $this->urlGenerator->editPostAction($title, $workflowId, $postId, $revId); break; case 'undo-edit-header': case 'undo-edit-post': case 'undo-edit-topic-summary': if (!$revision->isFirstRevision()) { $links['undo'] = $this->urlGenerator->undoAction($revision, $title, $workflowId); } break; case 'hide-post': if (!$postId) { throw new FlowException("{$type} called without \$postId"); } $links['hide'] = $this->urlGenerator->hidePostAction($title, $workflowId, $postId); break; case 'delete-topic': $links['delete'] = $this->urlGenerator->deleteTopicAction($title, $workflowId); break; case 'delete-post': if (!$postId) { throw new FlowException("{$type} called without \$postId"); } $links['delete'] = $this->urlGenerator->deletePostAction($title, $workflowId, $postId); break; case 'suppress-topic': $links['suppress'] = $this->urlGenerator->suppressTopicAction($title, $workflowId); break; case 'suppress-post': if (!$postId) { throw new FlowException("{$type} called without \$postId"); } $links['suppress'] = $this->urlGenerator->suppressPostAction($title, $workflowId, $postId); break; case 'lock-topic': // lock topic link is only available to topics if (!$revision instanceof PostRevision || !$revision->isTopicTitle()) { continue; } $links['lock'] = $this->urlGenerator->lockTopicAction($title, $workflowId); break; case 'restore-topic': $moderateAction = $flowAction = null; switch ($revision->getModerationState()) { case AbstractRevision::MODERATED_LOCKED: $moderateAction = 'unlock'; $flowAction = 'lock-topic'; break; case AbstractRevision::MODERATED_HIDDEN: case AbstractRevision::MODERATED_DELETED: case AbstractRevision::MODERATED_SUPPRESSED: $moderateAction = 'un' . $revision->getModerationState(); $flowAction = 'moderate-topic'; break; } if (isset($moderateAction) && $moderateAction) { $links[$moderateAction] = $this->urlGenerator->restoreTopicAction($title, $workflowId, $moderateAction, $flowAction); } break; case 'restore-post': if (!$postId) { throw new FlowException("{$type} called without \$postId"); } $moderateAction = $flowAction = null; switch ($revision->getModerationState()) { case AbstractRevision::MODERATED_HIDDEN: case AbstractRevision::MODERATED_DELETED: case AbstractRevision::MODERATED_SUPPRESSED: $moderateAction = 'un' . $revision->getModerationState(); $flowAction = 'moderate-post'; break; } if ($moderateAction) { $links[$moderateAction] = $this->urlGenerator->restorePostAction($title, $workflowId, $postId, $moderateAction, $flowAction); } break; case 'hide-topic': $links['hide'] = $this->urlGenerator->hideTopicAction($title, $workflowId); break; // Need to use 'edit-topic-summary' to match FlowActions // Need to use 'edit-topic-summary' to match FlowActions case 'edit-topic-summary': // summarize link is only available to topic workflow if (!in_array($workflow->getType(), array('topic', 'topicsummary'))) { continue; } $links['summarize'] = $this->urlGenerator->editTopicSummaryAction($title, $workflowId); break; default: wfDebugLog('Flow', __METHOD__ . ': unkown action link type: ' . $type); break; } } return $links; }
public function thankAction(UUID $postId) { $sender = RequestContext::getMain()->getUser(); $recipient = $sender; // Default to current user's gender if we can't find the recipient $postCollection = PostCollection::newFromId($postId); $postRevision = $postCollection->getLastRevision(); $recipient = $postRevision->getCreatorTuple()->createUser(); return new Anchor(wfMessage('flow-thank-link', $sender, $recipient)->text(), SpecialPage::getTitleFor('Thanks', 'Flow/' . $postId->getAlphadecimal()), array(), null, wfMessage('flow-thank-link-title', $sender, $recipient)->text()); }
/** * Get the post collection for this summary * @return PostCollection */ public function getPost() { return PostCollection::newFromId($this->uuid); }
/** * Saves a PostRevision to storage. * Be sure to add the required tables to $tablesUsed and add @group Database * to the class' phpDoc. * * @param PostRevision $revision */ protected function store(PostRevision $revision) { if ($revision->isTopicTitle()) { $root = $revision; } else { /** @var PostCollection $parentCollection */ $parentCollection = PostCollection::newFromId($revision->getReplyToId()); $root = $parentCollection->getRoot()->getLastRevision(); } $topicWorkflow = $this->workflows[$root->getCollectionId()->getAlphadecimal()]; $boardWorkflow = Container::get('factory.loader.workflow')->createWorkflowLoader($topicWorkflow->getOwnerTitle())->getWorkflow(); $metadata = array('workflow' => $topicWorkflow, 'board-workflow' => $boardWorkflow); // check if this topic (+ workflow + board workflow + board page) have // already been inserted or do so now $found = $this->getStorage()->find('TopicListEntry', array('topic_id' => $topicWorkflow->getId())); if (!$found) { $title = $boardWorkflow->getOwnerTitle(); $user = User::newFromName('127.0.0.1', false); /** @var OccupationController $occupationController */ $occupationController = Container::get('occupation_controller'); // make sure user has rights to create board $user->mRights = array_merge($user->getRights(), array('flow-create-board')); $occupationController->allowCreation($title, $user); $occupationController->ensureFlowRevision(new \Article($title), $boardWorkflow); $topicListEntry = TopicListEntry::create($boardWorkflow, $topicWorkflow); $this->getStorage()->put($boardWorkflow, $metadata); $this->getStorage()->put($topicWorkflow, $metadata); $this->getStorage()->put($topicListEntry, $metadata); } $this->getStorage()->put($revision, $metadata); /** @var SplQueue $deferredQueue */ $deferredQueue = Container::get('deferred_queue'); while (!$deferredQueue->isEmpty()) { try { DeferredUpdates::addCallableUpdate($deferredQueue->dequeue()); // doing updates 1 by 1 so an exception doesn't break others in // the queue DeferredUpdates::doUpdates(); } catch (\MWException $e) { // ignoring exceptions for now, not all are phpunit-proof yet } } // save for removal at end of tests $this->revisions[] = $revision; }
public function testNewFromRevision() { $revision = $this->revisions[0]; $collection = PostCollection::newFromRevision($revision); $this->assertInstanceOf('Flow\\Collection\\PostCollection', $collection); }
/** * @param array $tools Extra links * @param Title $title * @param bool $redirect Whether the page is a redirect * @param Skin $skin * @param string $link * @return bool */ public static function onWatchlistEditorBuildRemoveLine(&$tools, $title, $redirect, $skin, &$link = '') { if ($title->getNamespace() !== NS_TOPIC) { // Leave all non Flow topics alone! return true; } /* * Link to talk page is no applicable for Flow topics * Note that key 'talk' doesn't exist prior to * https://gerrit.wikimedia.org/r/#/c/156522/, so on old MW's, the link * to talk page will still be present. */ unset($tools['talk']); if (!$link) { /* * https://gerrit.wikimedia.org/r/#/c/156118/ adds argument $link. * Prior to that patch, it was impossible to change the link, so * let's quit early if it doesn't exist. */ return true; } try { // Find the title text of this specific topic $uuid = WorkflowLoaderFactory::uuidFromTitle($title); $collection = PostCollection::newFromId($uuid); $revision = $collection->getLastRevision(); } catch (Exception $e) { wfWarn(__METHOD__ . ': Failed to locate revision for: ' . $title->getDBKey()); return true; } // Titles are never parsed, so request as wikitext $content = $revision->getContent('wikitext'); $link = Linker::link($title, htmlspecialchars($content)); return true; }
/** * @return PostCollection */ public function getCollection() { return PostCollection::newFromRevision($this); }
/** * @param UUID $postId * @return PostCollection */ protected function loadPost(UUID $postId) { try { $collection = PostCollection::newFromId($postId); // validate collection by attempting to fetch latest revision - if // this fails (likely will for old data), catch will be invoked $collection->getLastRevision(); return $collection; } catch (InvalidDataException $e) { // posts used to mistakenly store revision ID instead of post ID /** @var ManagerGroup $storage */ $storage = Container::get('storage'); $result = $storage->find('PostRevision', array('rev_id' => $postId), array('LIMIT' => 1)); if ($result) { /** @var PostRevision $revision */ $revision = reset($result); // now build collection from real post ID return $this->loadPost($revision->getPostId()); } } return false; }
/** * @param AbstractRevision $revision * @return AbstractRevision */ protected function getRoot(AbstractRevision $revision) { if ($revision instanceof PostSummary) { $topicId = $revision->getSummaryTargetId(); } elseif ($revision instanceof PostRevision && !$revision->isTopicTitle()) { try { $topicId = $revision->getCollection()->getWorkflowId(); } catch (DataModelException $e) { // failed to locate root post (most likely in unit tests, where // we didn't store the tree) return $revision; } } else { // if we can't the revision it back to a root, this revision is root return $revision; } $collection = PostCollection::newFromId($topicId); return $collection->getLastRevision(); }
/** * @return PostCollection|bool */ protected function getRoot() { $params = $this->entry->getParameters(); try { if (!isset($params['topicId'])) { // failed finding the expected data in storage wfWarn(__METHOD__ . ': Failed to locate topicId in log_params for: ' . serialize($params) . ' (forgot to run FlowFixLog.php?)'); return false; } $uuid = UUID::create($params['topicId']); $collection = PostCollection::newFromId($uuid); // see if this post is valid $collection->getLastRevision(); return $collection; } catch (\Exception $e) { // failed finding the expected data in storage wfWarn(__METHOD__ . ': Failed to locate root for: ' . serialize($params) . ' (potentially storage issue)'); return false; } }