/** * Check if a user is allowed to perform a certain action. * * @param AbstractRevision $revision * @param string $action * @return bool */ public function isAllowed(AbstractRevision $revision, $action) { // check if we're allowed to $action on this revision if (!$this->isRevisionAllowed($revision, $action)) { return false; } // see if we're allowed to perform $action on anything inside this root $root = $this->getRoot($revision); if (!$revision->getRevisionId()->equals($root->getRevisionId()) && !$this->isRootAllowed($root, $action)) { return false; } // see if we're allowed to perform $action on anything inside this board $collection = $revision->getCollection(); $workflow = $collection->getBoardWorkflow(); if (!$this->isBoardAllowed($workflow, $action)) { return false; } // Also check if the user would be allowed to perform this // against the most recent revision - the last revision is the // current state of an object, so checking against a revision at // one point in time alone isn't enough. /** @var CollectionCache $cache */ $cache = Container::get('collection.cache'); $last = $cache->getLastRevisionFor($revision); $isLastRevision = $last->getRevisionId()->equals($revision->getRevisionId()); if (!$isLastRevision && !$this->isRevisionAllowed($last, $action)) { return false; } return true; }
public function testSortByOption() { $user = User::newFromId(1); $user->setOption('flow-topiclist-sortby', ''); // reset flow state, so everything ($container['permissions']) // uses this particular $user \FlowHooks::resetFlowExtension(); Container::reset(); $container = Container::getContainer(); $container['user'] = $user; $ctx = $this->getMock('IContextSource'); $ctx->expects($this->any())->method('getUser')->will($this->returnValue($user)); $workflow = Workflow::create('discussion', Title::newFromText('Talk:Flow_QA')); $block = new TopicListBlock($workflow, Container::get('storage')); $block->init($ctx, 'view'); $res = $block->renderApi(array()); $this->assertEquals('newest', $res['sortby'], 'With no sortby defaults to newest'); $res = $block->renderApi(array('sortby' => 'foo')); $this->assertEquals('newest', $res['sortby'], 'With invalid sortby defaults to newest'); $res = $block->renderApi(array('sortby' => 'updated')); $this->assertEquals('updated', $res['sortby'], 'With sortby updated output changes to updated'); $res = $block->renderApi(array()); $this->assertEquals('newest', $res['sortby'], 'Sort still defaults to newest'); $res = $block->renderApi(array('sortby' => 'updated', 'savesortby' => '1')); $this->assertEquals('updated', $res['sortby'], 'Request saving sortby option'); $res = $block->renderApi(array()); $this->assertEquals('updated', $res['sortby'], 'Default sortby now changed to updated'); $res = $block->renderApi(array('sortby' => '')); $this->assertEquals('updated', $res['sortby'], 'Default sortby with blank sortby still uses user default'); }
public function renderApi(array $options) { global $wgRequest; if ($this->workflow->isNew()) { return array('type' => $this->getName(), 'revisions' => array(), 'links' => array()); } /** @var BoardHistoryQuery $query */ $query = Container::get('query.board-history'); /** @var RevisionFormatter $formatter */ $formatter = Container::get('formatter.revision'); $formatter->setIncludeHistoryProperties(true); list($limit, ) = $wgRequest->getLimitOffset(); // don't use offset from getLimitOffset - that assumes an int, which our // UUIDs are not $offset = $wgRequest->getText('offset'); $offset = $offset ? UUID::create($offset) : null; $pager = new HistoryPager($query, $this->workflow->getId()); $pager->setLimit($limit); $pager->setOffset($offset); $pager->doQuery(); $history = $pager->getResult(); $revisions = array(); foreach ($history as $row) { $serialized = $formatter->formatApi($row, $this->context, 'history'); if ($serialized) { $revisions[$serialized['revisionId']] = $serialized; } } return array('type' => $this->getName(), 'revisions' => $revisions, 'navbar' => $pager->getNavigationBar(), 'links' => array()); }
protected function processPosts($recorder) { $storage = Container::get('storage.post'); $count = $this->mBatchSize; $id = ''; $dbr = Container::get('db.factory')->getDB(DB_SLAVE); while ($count === $this->mBatchSize) { $count = 0; $res = $dbr->select(array('flow_tree_revision'), array('tree_rev_id'), array('tree_parent_id IS NOT NULL', 'tree_rev_id > ' . $dbr->addQuotes($id)), __METHOD__, array('ORDER BY' => 'tree_rev_id ASC', 'LIMIT' => $this->mBatchSize)); if (!$res) { throw new \MWException('SQL error in maintenance script ' . __METHOD__); } foreach ($res as $row) { $count++; $id = $row->tree_rev_id; $uuid = UUID::create($id); $alpha = $uuid->getAlphadecimal(); $post = $storage->get($uuid); if ($post) { echo "Processing post {$alpha}\n"; $recorder->onAfterInsert($post, array(), array('workflow' => $post->getCollection()->getWorkflow())); } } } }
protected function doDBUpdates() { $dbf = Container::get('db.factory'); $dbw = $dbf->getDB(DB_MASTER); $hasRun = false; $runUpdate = function ($callback) use($dbf, $dbw, &$hasRun) { $hasRun = true; $continue = ""; do { $continue = call_user_func($callback, $dbw, $continue); $dbf->waitForSlaves(); } while ($continue !== null); }; // run updates only if we have the required source data if ($dbw->fieldExists('flow_workflow', 'workflow_user_text')) { $runUpdate(array($this, 'updateWorkflow')); } if ($dbw->fieldExists('flow_tree_revision', 'tree_orig_user_text')) { $runUpdate(array($this, 'updateTreeRevision')); } if ($dbw->fieldExists('flow_revision', 'rev_user_text') && $dbw->fieldExists('flow_revision', 'rev_mod_user_text') && $dbw->fieldExists('flow_revision', 'rev_edit_user_text')) { $runUpdate(array($this, 'updateRevision')); } if ($hasRun) { $dbw->sourceFile(__DIR__ . '/../db_patches/patch-remove_usernames_2.sql'); } return true; }
public function updateTreeRevision(DatabaseBase $dbw, $continue = null) { $rows = $dbw->select('flow_tree_revision', array('tree_rev_id'), array('tree_rev_id > ' . $dbw->addQuotes($continue), 'tree_orig_user_ip IS NOT NULL', 'tree_orig_user_id > 0'), __METHOD__, array('LIMIT' => $this->mBatchSize, 'ORDER BY' => 'tree_rev_id')); $om = Container::get('storage')->getStorage('PostRevision'); $objs = $ids = array(); foreach ($rows as $row) { $id = UUID::create($row->tree_rev_id); $found = $om->get($id); if ($found) { $ids[] = $row->tree_rev_id; $objs[] = $found; } else { $this->error(__METHOD__ . ': Failed loading Flow\\Model\\PostRevision: ' . $id->getAlphadecimal()); } } if (!$ids) { return null; } $dbw->update('flow_tree_revision', array('tree_orig_user_ip' => null), array('tree_rev_id' => $ids), __METHOD__); foreach ($objs as $obj) { $om->cachePurge($obj); } $this->completeCount += count($ids); return end($ids); }
public function testSomething() { // meaningless set of ids used for repeatability $ids = array_map(array('Flow\\Model\\UUID', 'create'), array("s3z44zhp93j5vvc8", "s3z44zhqt7yt8220", "s46w00pmmw0otc0q", "s3qvc7cnor86wvb4", "s3qvc7bbcxr3f340", "s3gre9r27pobtg0n", "s3cdl3dfqf8brx18", "s3cdl3dhajnz43r0")); // Use 2 repos with 2 caches, the one you insert with reads from cache // the other reads from db due to different cache $cache[] = new BufferedCache(new BufferedBagOStuff(new \HashBagOStuff()), 600); $cache[] = new BufferedCache(new BufferedBagOStuff(new \HashBagOStuff()), 600); $dbf = Container::get('db.factory'); $repo[] = new TreeRepository($dbf, $cache[0]); $repo[] = new TreeRepository($dbf, $cache[1]); // id0 as new root wfDebugLog('Flow', "\n\n************** id0 as new root ************"); $repo[0]->insert($ids[0]); $this->assertEquals(array($ids[0]), $repo[0]->findRootPath($ids[0])); $this->assertEquals(array($ids[0]), $repo[1]->findRootPath($ids[0])); // id1 as child of id0 wfDebugLog('Flow', "\n\n************** id1 as child of id0 ************"); $repo[0]->insert($ids[1], $ids[0]); $this->assertEquals(array($ids[0], $ids[1]), $repo[0]->findRootPath($ids[1])); $this->assertEquals(array($ids[0], $ids[1]), $repo[1]->findRootPath($ids[1])); // id2 as child of id0 wfDebugLog('Flow', "\n\n************** id2 as child of id0 ************"); $repo[0]->insert($ids[2], $ids[0]); $this->assertEquals(array($ids[0], $ids[2]), $repo[0]->findRootPath($ids[2])); $this->assertEquals(array($ids[0], $ids[2]), $repo[1]->findRootPath($ids[2])); // id3 as child of id1 wfDebugLog('Flow', "\n\n************** id3 as child of id1 ************"); $repo[0]->insert($ids[3], $ids[1]); $this->assertEquals(array($ids[0], $ids[1], $ids[3]), $repo[0]->findRootPath($ids[3])); $this->assertEquals(array($ids[0], $ids[1], $ids[3]), $repo[1]->findRootPath($ids[3])); }
/** * This is a horrible test, it basically runs the whole thing * and sees if it falls over. */ public function testImportDoesntCompletelyFail() { $workflow = Workflow::create('discussion', Title::newFromText('TalkpageImportOperationTest')); $storage = $this->getMockBuilder('Flow\\Data\\ManagerGroup')->disableOriginalConstructor()->getMock(); $stored = array(); $storage->expects($this->any())->method('put')->will($this->returnCallback(function ($obj) use(&$stored) { $stored[] = $obj; })); $storage->expects($this->any())->method('multiPut')->will($this->returnCallback(function ($objs) use(&$stored) { $stored = array_merge($stored, $objs); })); $now = time(); $source = new MockImportSource(new MockImportHeader(array(new MockImportRevision(array('createdTimestamp' => $now)))), array(new MockImportTopic(new MockImportSummary(array(new MockImportRevision(array('createdTimestamp' => $now - 250)))), array(new MockImportRevision(array('createdTimestamp' => $now - 1000))), array(new MockImportPost(array(new MockImportRevision(array('createdTimestmap' => $now - 1000))), array(new MockImportPost(array(new MockImportRevision(array('createdTimestmap' => $now - 500, 'user' => User::newFromNAme('10.0.0.2', false)))), array()))))))); $op = new TalkpageImportOperation($source, Container::get('occupation_controller')); $store = new NullImportSourceStore(); $op->import(new PageImportState($workflow, $storage, $store, new NullLogger(), $this->getMockBuilder('Flow\\Data\\BufferedCache')->disableOriginalConstructor()->getMock(), Container::get('db.factory'), new ProcessorGroup(), new SplQueue())); // Count what actually came through $storedHeader = $storedDiscussion = $storedTopics = $storedTopicListEntry = $storedSummary = $storedPosts = 0; foreach ($stored as $obj) { if ($obj instanceof Workflow) { if ($obj->getType() === 'discussion') { $this->assertSame($workflow, $obj); $storedDiscussion++; } else { $alpha = $obj->getId()->getAlphadecimal(); if (!isset($seenWorkflow[$alpha])) { $seenWorkflow[$alpha] = true; $this->assertEquals('topic', $obj->getType()); $storedTopics++; $topicWorkflow = $obj; } } } elseif ($obj instanceof PostSummary) { $storedSummary++; } elseif ($obj instanceof PostRevision) { $storedPosts++; if ($obj->isTopicTitle()) { $topicTitle = $obj; } } elseif ($obj instanceof TopicListEntry) { $storedTopicListEntry++; } elseif ($obj instanceof Header) { $storedHeader++; } else { $this->fail('Unexpected object stored:' . get_class($obj)); } } // Verify we wrote the expected objects to storage $this->assertEquals(1, $storedHeader); $this->assertEquals(1, $storedDiscussion); $this->assertEquals(1, $storedTopics); $this->assertEquals(1, $storedTopicListEntry); $this->assertEquals(1, $storedSummary); $this->assertEquals(3, $storedPosts); // This total expected number of insertions should match the sum of the left assertEquals parameters above. $this->assertCount(8, array_unique(array_map('spl_object_hash', $stored))); // Other special cases we need to check $this->assertTrue($topicTitle->getPostId()->equals($topicWorkflow->getId()), 'Root post id must match its workflow'); }
protected function renderTemplate($templateName, array $args = array()) { $lc = Container::get('lightncandy'); $filenames = $lc->getTemplateFilenames($templateName); $phpCode = $lc::compile(file_get_contents($filenames['template']), Container::get('lightncandy.template_dir')); $renderer = LightnCandy::prepare($phpCode); return new Crawler($renderer($args)); }
/** * @fixme this looks slow, likely a better way */ protected function serializeRcRevision(RecentChange $rc, IContextSource $ctx) { /** @var RecentChangesQuery $query */ $query = Container::get('query.recentchanges'); $query->loadMetadataBatch(array((object) $rc->mAttribs)); $rcRow = $query->getResult(null, $rc); $this->serializer->setIncludeHistoryProperties(true); return $this->serializer->formatApi($rcRow, $ctx, 'recentchanges'); }
/** * In the future we can add more Flow related info here * @param Title $title * @return array */ protected function getPageInfo(Title $title) { /** @var \Flow\TalkpageManager $manager */ $manager = Container::get('occupation_controller'); $result = array('flow' => array()); if ($manager->isTalkpageOccupied($title)) { $result['flow']['enabled'] = ''; } return $result; }
/** * @return UUID * @throws \Flow\Exception\DataModelException */ public function getWorkflowId() { // the root post (topic title) has the same id as the workflow if (!$this->rootId) { /** @var \Flow\Repository\TreeRepository $treeRepo */ $treeRepo = Container::get('repository.tree'); $this->rootId = $treeRepo->findRoot($this->getId()); } return $this->rootId; }
public function execute() { global $wgFlowSearchMaintenanceTimeout; // Set the timeout for maintenance actions Connection::getSingleton()->setTimeout2($wgFlowSearchMaintenanceTimeout); /** @var Updater[] $updaters */ $updaters = Container::get('searchindex.updaters'); foreach ($updaters as $updaterType => $updater) { $fromId = $this->getOption('fromId', null); $fromId = $fromId ? UUID::create($fromId) : null; $toId = $this->getOption('toId', null); $toId = $toId ? UUID::create($toId) : null; $namespace = $this->getOption('namespace', null); $numRevisionsToIndex = $this->getOption('limit', null); $total = 0; while (true) { // if a limit was provided, we should make sure to not fetch // more revisions than asked for $options = array('LIMIT' => $this->mBatchSize); if ($numRevisionsToIndex) { $options['LIMIT'] = min($numRevisionsToIndex, $this->mBatchSize); // since we do this in batches, we'll subtract the size of // each batch until $numRevisionsToIndex is reached $numRevisionsToIndex -= $this->mBatchSize; if ($options['LIMIT'] <= 0) { break; } } $conditions = $updater->buildQueryConditions($fromId, $toId, $namespace); $revisions = $updater->getRevisions($conditions, $options); // stop if we're all out of revisions if (!$revisions) { break; } $total += $updater->updateRevisions($revisions, null, null); $this->output("Indexed {$total} {$updaterType} document(s)\n"); // prepare for next batch, starting at the next id // prevFromId will default to around unix epoch - there can be // no data before that $prevFromId = $fromId ?: UUID::getComparisonUUID('1'); $fromId = $this->getNextFromId($revisions); // make sure we don't get stuck in an infinite loop $diff = $prevFromId->getTimestampObj()->diff($fromId->getTimestampObj()); // invert will be 1 if the diff is a negative time period from // $prevFromId to $fromId, which means that the new $timestamp is // more recent than our current $result if ($diff->invert) { $this->error('Got stuck in an infinite loop.' . "\n" . 'workflow_last_update_timestamp is likely incorrect ' . 'for some workflows.' . "\n" . 'Run maintenance/FlowFixWorkflowLastUpdateTimestamp.php ' . 'to automatically fix those.', 1); } // prevent memory from being filled up Container::get('storage')->clear(); } } }
/** * Ensures Flow is reset before passing control on * to parent::doApiRequest. Defaults all requests to * the sysop user if not specified. */ protected function doApiRequest(array $params, array $session = null, $appendModule = false, User $user = null) { if ($user === null) { $user = self::$users['sysop']->user; } // reset flow state before each request FlowHooks::resetFlowExtension(); Container::reset(); $container = Container::getContainer(); $container['user'] = $user; return parent::doApiRequest($params, $session, $appendModule, $user); }
/** * Returns list of rev_change_type values that warrant an editcount increase. * * @return array */ protected function getCountableActions() { $allowedActions = array(); /** @var FlowActions $actions */ $actions = \Flow\Container::get('flow_actions'); foreach ($actions->getActions() as $action) { if ($actions->getValue($action, 'editcount')) { $allowedActions[] = $action; } } return $allowedActions; }
public function execute() { global $wgFlowCluster; $dbFactory = Container::get('db.factory'); $storage = Container::get('storage'); $rootPostLoader = Container::get('loader.root_post'); $iterator = new EchoBatchRowIterator($dbFactory->getDB(DB_SLAVE), 'flow_workflow', 'workflow_id', $this->mBatchSize); $iterator->setFetchColumns(array('workflow_id', 'workflow_type', 'workflow_last_update_timestamp')); $iterator->addConditions(array('workflow_wiki' => wfWikiId())); $updater = new EchoBatchRowUpdate($iterator, new UpdateWorkflowLastUpdateTimestampWriter($storage, $wgFlowCluster), new UpdateWorkflowLastUpdateTimestampGenerator($storage, $rootPostLoader)); $updater->setOutput(array($this, 'output')); $updater->execute(); }
/** * {@inheritDoc} */ public function getRevisions(array $conditions = array(), array $options = array()) { $dbr = $this->dbFactory->getDB(DB_SLAVE); // get the current (=most recent, =max) revision id for all headers $rows = $dbr->select(array('flow_revision', 'flow_workflow'), array('rev_id' => 'MAX(rev_id)'), $conditions, __METHOD__, array('ORDER BY' => 'rev_id ASC', 'GROUP BY' => 'rev_type_id') + $options, array('flow_workflow' => array('INNER JOIN', array('workflow_id = rev_type_id', 'rev_type' => 'header')))); $uuids = array(); foreach ($rows as $row) { $uuids[] = UUID::create($row->rev_id); } /** @var ManagerGroup $storage */ $storage = Container::get('storage'); return $storage->getStorage('Header')->getMulti($uuids); }
/** * @dataProvider objectManagerKeyProvider */ public function testSomething($key) { $c = Container::getContainer(); $this->assertNotNull($c[$key]); foreach ($c["{$key}.indexes"] as $pos => $index) { $this->assertInstanceOf('Flow\\Data\\Index', $index, "At {$key}.indexes[{$pos}]"); } if (isset($c["{$key}.listeners"])) { foreach ($c["{$key}.listeners"] as $pos => $listener) { $this->assertInstanceOf("Flow\\Data\\LifecycleHandler", $listener, "At {$key}.listeners[{$pos}]"); } } }
/** * Formats an activity log entry. * * @return string The log entry */ protected function getActionMessage() { global $wgContLang; /* * At this point, all log entries will already have been created & we've * gathered all uuids in constructor: we can now batch-load all of them. * We won't directly be using that batch-loaded data (nothing will even * be returned) but it'll ensure that everything we need will be * retrieved from cache/storage efficiently & waiting in memory for when * we request it again. */ static $loaded = false; if (!$loaded) { /** @var ManagerGroup storage */ $storage = Container::get('storage'); /** @var TreeRepository $treeRepository */ $treeRepository = Container::get('repository.tree'); $query = new LogQuery($storage, $treeRepository); $query->loadMetadataBatch(static::$uuids); $loaded = true; } $root = $this->getRoot(); if (!$root) { // failed to load required data return ''; } $type = $this->entry->getType(); $action = $this->entry->getSubtype(); $title = $this->entry->getTarget(); $skin = $this->plaintext ? null : $this->context->getSkin(); $params = $this->entry->getParameters(); // @todo: we should probably check if user isAllowed( <this-revision>, 'log' ) // unlike RC, Contributions, ... this one does not batch-load all Flow // revisions & does not use the same Formatter, i18n message text, etc if (isset($params['postId'])) { /** @var UrlGenerator $urlGenerator */ $urlGenerator = Container::get('url_generator'); // generate link that highlights the post $anchor = $urlGenerator->postLink($title, UUID::create($params['topicId']), UUID::create($params['postId'])); $title = $anchor->resolveTitle(); } // Give grep a chance to find the usages: // logentry-delete-flow-delete-post, logentry-delete-flow-restore-post, // logentry-suppress-flow-restore-post, logentry-suppress-flow-suppress-post, // logentry-delete-flow-delete-topic, logentry-delete-flow-restore-topic, // logentry-suppress-flow-restore-topic, logentry-suppress-flow-suppress-topic, $language = $skin === null ? $wgContLang : $skin->getLanguage(); $message = wfMessage("logentry-{$type}-{$action}")->params(array(Message::rawParam($this->getPerformerElement()), $this->entry->getPerformer()->getName(), $title, $title->getFullUrl(), $root->getLastRevision()->getContent(), $root->getWorkflow()->getOwnerTitle()))->inLanguage($language)->parse(); return \Html::rawElement('span', array('class' => 'plainlinks'), $message); }
protected function setUp() { parent::setUp(); // We don't want local config getting in the way of testing whether or // not our permissions implementation works well. // This will load default $wgGroupPermissions + Flow settings, so we can // test if permissions work well, regardless of any custom config. global $IP, $wgFlowGroupPermissions; $wgGroupPermissions = array(); require "{$IP}/includes/DefaultSettings.php"; $wgGroupPermissions = array_merge_recursive($wgGroupPermissions, $wgFlowGroupPermissions); $this->setMwGlobals('wgGroupPermissions', $wgGroupPermissions); // load actions object $this->actions = Container::get('flow_actions'); }
protected function createFormatter($class) { $permissions = $this->getMockBuilder('Flow\\RevisionActionPermissions')->disableOriginalConstructor()->getMock(); $permissions->expects($this->any())->method('isAllowed')->will($this->returnValue(true)); $permissions->expects($this->any())->method('getActions')->will($this->returnValue(Container::get('flow_actions'))); $templating = $this->getMockBuilder('Flow\\Templating')->disableOriginalConstructor()->getMock(); $occupier = $this->getMockBuilder('Flow\\OccupationController')->disableOriginalConstructor()->getMock(); $workflowMapper = $this->getMockBuilder('Flow\\Data\\Mapper\\CachingObjectMapper')->disableOriginalConstructor()->getMock(); $urlGenerator = new UrlGenerator($workflowMapper, $occupier); $templating->expects($this->any())->method('getUrlGenerator')->will($this->returnValue($urlGenerator)); $usernames = $this->getMockBuilder('Flow\\Repository\\UserNameBatch')->disableOriginalConstructor()->getMock(); global $wgFlowMaxThreadingDepth; $serializer = new RevisionFormatter($permissions, $templating, $usernames, $wgFlowMaxThreadingDepth); return new $class($permissions, $serializer); }
/** * Creates a flow board. * Archives any pre-existing wikitext talk page. * * @param array $data Form data * @return Status Status indicating result */ public function onSubmit(array $data) { $page = $data['page']; $title = Title::newFromText($page); if (!$title) { return Status::newFatal('flow-special-enableflow-invalid-title', $page); } // Canonicalize so the error or confirmation message looks nicer (no underscores). $page = $title->getPrefixedText(); if ($this->occupationController->isTalkpageOccupied($title, true)) { return Status::newFatal('flow-special-enableflow-board-already-exists', $page); } $status = Status::newGood(); if ($title->exists(Title::GAID_FOR_UPDATE)) { if (class_exists('LqtDispatch') && \LqtDispatch::isLqtPage($title)) { return Status::newFatal('flow-special-enableflow-page-is-liquidthreads', $page); } $logger = Container::get('default_logger'); $converter = new Converter(wfGetDB(DB_MASTER), Container::get('importer'), $logger, $this->occupationController->getTalkpageManager(), new EnableFlowWikitextConversionStrategy(Container::get('parser'), new NullImportSourceStore(), $logger, array(), $data['header'])); try { $converter->convert($title); } catch (\Exception $e) { \MWExceptionHandler::logException($e); $status->fatal('flow-error-external', $e->getMessage()); } } else { $allowCreationStatus = $this->occupationController->allowCreation($title, $this->getUser(), false); if (!$allowCreationStatus->isGood()) { return Status::newFatal('flow-special-enableflow-board-creation-not-allowed', $page); } $loader = $this->loaderFactory->createWorkflowLoader($title); $blocks = $loader->getBlocks(); $action = 'edit-header'; $params = array('header' => array('content' => $data['header'], 'format' => 'wikitext')); $blocksToCommit = $loader->handleSubmit($this->getContext(), $action, $params); foreach ($blocks as $block) { if ($block->hasErrors()) { $errors = $block->getErrors(); foreach ($errors as $errorKey) { $status->fatal($block->getErrorMessage($errorKey)); } } } $loader->commit($blocksToCommit); } $this->page = $data['page']; return $status; }
/** * @param string $key * @param array $ids * @param callable $loadCallback * @return array * @throws InvalidInputException */ public function get($key, array $ids, $loadCallback) { $key = implode(':', (array) $key); $cacheKeys = array(); foreach ($ids as $id) { if ($id instanceof UUID) { $cacheId = $id->getAlphadecimal(); } elseif (!is_scalar($id)) { $type = is_object($id) ? get_class($id) : gettype($id); throw new InvalidInputException('Not scalar:' . $type, 'invalid-input'); } else { $cacheId = $id; } $cacheKeys[wfForeignMemcKey('flow', '', $key, $cacheId, Container::get('cache.version'))] = $id; } return $this->getByKey($cacheKeys, $loadCallback); }
protected function doDBUpdates() { $container = Container::getContainer(); $dbFactory = $container['db.factory']; $dbw = $dbFactory->getDb(DB_MASTER); $storage = $container['storage']; $moderationLoggingListener = $container['storage.post.listeners.moderation_logging']; $rowIterator = new EchoBatchRowIterator($dbw, 'flow_revision', 'rev_id', $this->mBatchSize); $rowIterator->setFetchColumns(array('rev_id', 'rev_type')); // Fetch rows that are a moderation action $rowIterator->addConditions(array('rev_change_type' => ModerationLoggingListener::getModerationChangeTypes(), 'rev_user_wiki' => wfWikiID())); $start = $this->getOption('start'); $startId = UUID::create($start); $rowIterator->addConditions(array('rev_id > ' . $dbw->addQuotes($startId->getBinary()))); $stop = $this->getOption('stop'); $stopId = UUID::create($stop); $rowIterator->addConditions(array('rev_id < ' . $dbw->addQuotes($stopId->getBinary()))); $total = $fail = 0; foreach ($rowIterator as $batch) { $dbw->begin(); foreach ($batch as $row) { $total++; $objectManager = $storage->getStorage($row->rev_type); $revId = UUID::create($row->rev_id); $obj = $objectManager->get($revId); if (!$obj) { $this->error('Could not load revision: ' . $revId->getAlphadecimal()); $fail++; continue; } $workflow = $obj->getCollection()->getWorkflow(); $moderationLoggingListener->onAfterInsert($obj, array(), array('workflow' => $workflow)); } $dbw->commit(); $storage->clear(); $dbFactory->waitForSlaves(); } $this->output("Processed a total of {$total} moderation revisions.\n"); if ($fail !== 0) { $this->error("Errors were encountered while processing {$fail} of them.\n"); } return true; }
/** * @param array $params * @return Title */ protected function getPage($params) { $page = Title::newFromText($params['page']); if (!$page) { $this->dieUsage('Invalid page provided', 'invalid-page'); } /** @var \Flow\TalkpageManager $controller */ $controller = Container::get('occupation_controller'); if (!$controller->isTalkpageOccupied($page)) { // just check for permissions, nothing else to do. if the commit // is successful the OccupationListener will see the new revision // and put the flow board in place. $status = $controller->allowCreation($page, $this->getUser()); if (!$status->isGood()) { $this->dieUsage("Page provided does not have Flow enabled and allowCreation failed with: " . $status->getMessage()->parse(), 'invalid-page'); } } return $page; }
public function setUp() { parent::setUp(); // create a workflow & revision associated with it $this->revision = $this->generateObject(); $this->workflow = $this->workflows[$this->revision->getCollectionId()->getAlphadecimal()]; $this->storage = Container::get('storage'); $this->extractor = Container::get('reference.extractor'); $this->recorder = Container::get('reference.recorder'); $this->updater = Container::get('reference.updater.links-tables'); // Check for Parsoid try { Utils::convert('html', 'wikitext', 'Foo', $this->workflow->getOwnerTitle()); } catch (WikitextException $excep) { $this->markTestSkipped('Parsoid not enabled'); } // These tests don't provide sufficient data to properly run all listeners $this->clearExtraLifecycleHandlers(); }
public function execute() { $logger = new MaintenanceDebugLogger($this); if ($this->getOption('debug')) { $logger->setMaximumLevel(LogLevel::DEBUG); } else { $logger->setMaximumLevel(LogLevel::INFO); } $importer = Flow\Container::get('importer'); $talkpageManagerUser = FlowHooks::getOccupationController()->getTalkpageManager(); $dbw = wfGetDB(DB_MASTER); $strategy = new ConversionStrategy($dbw, new FileImportSourceStore($this->getOption('logfile')), new LocalApiBackend($talkpageManagerUser), Container::get('url_generator'), $talkpageManagerUser, Container::get('controller.notification')); $converter = new \Flow\Import\Converter($dbw, $importer, $logger, $talkpageManagerUser, $strategy); $startId = $this->getOption('startId'); $stopId = $this->getOption('stopId'); $logger->info("Starting full wiki LQT conversion of all pages with use-liquid-threads property"); $titles = new PagesWithPropertyIterator($dbw, 'use-liquid-threads', $startId, $stopId); $converter->convertAll($titles); $logger->info("Finished conversion"); }
/** * @dataProvider somethingProvider */ public function testSomething($message, $expect, $init) { $actions = Container::get('flow_actions'); $usernames = $this->getMockBuilder('Flow\\Repository\\UserNameBatch')->disableOriginalConstructor()->getMock(); $rcFactory = $this->getMockBuilder('Flow\\Data\\Utils\\RecentChangeFactory')->disableOriginalConstructor()->getMock(); $ircFormatter = $this->getMockBuilder('Flow\\Formatter\\IRCLineUrlFormatter')->disableOriginalConstructor()->getMock(); $rc = new RecentChangesListener($actions, $usernames, $rcFactory, $ircFormatter); $change = $this->getMock('RecentChange'); $rcFactory->expects($this->once())->method('newFromRow')->will($this->returnCallback(function ($obj) use(&$ref, $change) { $ref = $obj; return $change; })); $title = Title::newMainPage(); $user = User::newFromName('127.0.0.1', false); $workflow = Workflow::create('topic', $title); $revision = $init($workflow, $user); $rc->onAfterInsert($revision, array('rev_user_id' => 0, 'rev_user_ip' => '127.0.0.1'), array('workflow' => $workflow)); $this->assertNotNull($ref); $this->assertEquals($expect, $ref->rc_namespace, $message); }
/** * @dataProvider referenceExtractorProvider */ public function testReferenceExtractor($description, $wikitext, $expectedClass, $expectedType, $expectedTarget, $page = 'UTPage') { $referenceExtractor = Container::get('reference.extractor'); $workflow = $this->getMock('Flow\\Model\\Workflow'); $workflow->expects($this->any())->method('getId')->will($this->returnValue(UUID::create())); $workflow->expects($this->any())->method('getArticleTitle')->will($this->returnValue(Title::newMainPage())); $factory = new ReferenceFactory($workflow, 'foo', UUID::create()); $reflMethod = new ReflectionMethod($referenceExtractor, 'extractReferences'); $reflMethod->setAccessible(true); $reflProperty = new \ReflectionProperty($referenceExtractor, 'extractors'); $reflProperty->setAccessible(true); $extractors = $reflProperty->getValue($referenceExtractor); $html = Utils::convert('wt', 'html', $wikitext, Title::newFromText($page)); $result = $reflMethod->invoke($referenceExtractor, $factory, $extractors['post'], $html); $this->assertCount(1, $result, $html); $result = reset($result); $this->assertInstanceOf($expectedClass, $result, $description); $this->assertEquals($expectedType, $result->getType(), $description); $this->assertEquals($expectedTarget, $result->getTargetIdentifier(), $description); }
/** * Returns an array of user ids to subscribe to the title. * * @param string $changeType * @param string $watchType Key of the corresponding 'watch' array in FlowActions.php * @param WatchedTopicItems[] $params Params to feed to callback function that will return * an array of users to subscribe * @return User[] */ public static function getUsersToSubscribe($changeType, $watchType, array $params = array()) { /** @var FlowActions $actions */ $actions = Container::get('flow_actions'); // Find users defined for this action, in FlowActions.php try { $users = $actions->getValue($changeType, 'watch', $watchType); } catch (\Exception $e) { return array(); } // Null will be returned if nothing is defined for this changeType if (!$users) { return array(); } // Some actions may have more complex logic to determine watching users if (is_callable($users)) { $users = call_user_func_array($users, $params); } return $users; }