public function testInsertDelayed() { global $wgTranslateDelayedMessageIndexRebuild; $wgTranslateDelayedMessageIndexRebuild = true; MessageIndexRebuildJob::newJob()->insert(); $job = JobQueueGroup::singleton()->get('MessageIndexRebuildJob')->pop(); $this->assertInstanceOf('MessageIndexRebuildJob', $job, 'There is a job in the JobQueue'); $this->assertTrue($job->run(), 'Job is executed succesfully'); }
protected function performAction() { $jobs = array(); $target = $this->title; $base = $this->title->getPrefixedText(); $translationPages = $this->getTranslationPages(); foreach ( $translationPages as $old ) { $jobs[$old->getPrefixedText()] = DeleteJob::newJob( $old, $base, !$this->singleLanguage(), $this->user ); } $sectionPages = $this->getSectionPages(); foreach ( $sectionPages as $old ) { $jobs[$old->getPrefixedText()] = DeleteJob::newJob( $old, $base, !$this->singleLanguage(), $this->user ); } if ( !$this->doSubpages ) { $subpages = $this->getSubpages(); foreach ( $subpages as $old ) { if ( TranslatablePage::isTranslationPage( $old ) ) { continue; } $jobs[$old->getPrefixedText()] = DeleteJob::newJob( $old, $base, !$this->singleLanguage(), $this->user ); } } Job::batchInsert( $jobs ); $cache = wfGetCache( CACHE_DB ); $cache->set( wfMemcKey( 'pt-base', $target->getPrefixedText() ), array_keys( $jobs ), 60 * 60 * 6 ); if ( !$this->singleLanguage() ) { $this->page->removeTags(); } MessageGroups::clearCache(); MessageIndexRebuildJob::newJob()->insert(); global $wgOut; $wgOut->addWikiMsg( 'pt-deletepage-started' ); }
protected function performAction() { $jobs = array(); $target = $this->title; $base = $this->title->getPrefixedText(); $translationPages = $this->getTranslationPages(); $user = $this->getUser(); foreach ($translationPages as $old) { $jobs[$old->getPrefixedText()] = TranslateDeleteJob::newJob($old, $base, !$this->singleLanguage(), $user); } $sectionPages = $this->getSectionPages(); foreach ($sectionPages as $old) { $jobs[$old->getPrefixedText()] = TranslateDeleteJob::newJob($old, $base, !$this->singleLanguage(), $user); } if (!$this->doSubpages) { $subpages = $this->getSubpages(); foreach ($subpages as $old) { if (TranslatablePage::isTranslationPage($old)) { continue; } $jobs[$old->getPrefixedText()] = TranslateDeleteJob::newJob($old, $base, !$this->singleLanguage(), $user); } } JobQueueGroup::singleton()->push($jobs); $cache = wfGetCache(CACHE_DB); $cache->set(wfMemcKey('pt-base', $target->getPrefixedText()), array_keys($jobs), 60 * 60 * 6); if (!$this->singleLanguage()) { $this->page->unmarkTranslatablePage(); } $this->clearMetadata(); MessageGroups::singleton()->recache(); MessageIndexRebuildJob::newJob()->insert(); $this->getOutput()->addWikiMsg('pt-deletepage-started'); }
protected function performAction() { $jobs = array(); $target = $this->newTitle; $base = $this->oldTitle->getPrefixedText(); $oldLatest = $this->oldTitle->getLatestRevId(); $params = array( 'base-source' => $this->oldTitle->getPrefixedText(), 'base-target' => $this->newTitle->getPrefixedText(), ); $translationPages = $this->getTranslationPages(); foreach ( $translationPages as $old ) { $to = $this->newPageTitle( $base, $old, $target ); $jobs[$old->getPrefixedText()] = MoveJob::newJob( $old, $to, $params, $this->user ); } $sectionPages = $this->getSectionPages(); foreach ( $sectionPages as $old ) { $to = $this->newPageTitle( $base, $old, $target ); $jobs[$old->getPrefixedText()] = MoveJob::newJob( $old, $to, $params, $this->user ); } if ( $this->moveSubpages ) { $subpages = $this->getSubpages(); foreach ( $subpages as $old ) { if ( TranslatablePage::isTranslationPage( $old ) ) { continue; } $to = $this->newPageTitle( $base, $old, $target ); $jobs[$old->getPrefixedText()] = MoveJob::newJob( $old, $to, $params, $this->user ); } } // This is used by MoveJob wfGetCache( CACHE_ANYTHING )->set( wfMemcKey( 'translate-pt-move', $base ), count( $jobs ) ); Job::batchInsert( $jobs ); MoveJob::forceRedirects( false ); $errors = $this->oldTitle->moveTo( $this->newTitle, true, $this->reason, false ); if ( is_array( $errors ) ) { $this->showErrors( $errors ); } MoveJob::forceRedirects( true ); $newTpage = TranslatablePage::newFromTitle( $this->newTitle ); $newTpage->addReadyTag( $this->newTitle->getLatestRevId( GAID_FOR_UPDATE ) ); if ( $newTpage->getMarkedTag() === $oldLatest ) { $newTpage->addMarkedTag( $this->newTitle->getLatestRevId( GAID_FOR_UPDATE ) ); } MessageGroups::clearCache(); MessageIndexRebuildJob::newJob()->insert(); global $wgOut; $wgOut->addWikiMsg( 'pt-movepage-started' ); }
protected function processSubmit() { $req = $this->getRequest(); $out = $this->getOutput(); $jobs = array(); $jobs[] = MessageIndexRebuildJob::newJob(); $changefile = TranslateUtils::cacheFile(self::CHANGEFILE); $reader = CdbReader::open($changefile); $groups = unserialize($reader->get('#keys')); $postponed = array(); foreach ($groups as $groupId) { $group = MessageGroups::getGroup($groupId); $changes = unserialize($reader->get($groupId)); foreach ($changes as $code => $subchanges) { foreach ($subchanges as $type => $messages) { foreach ($messages as $index => $params) { $id = self::changeId($groupId, $code, $type, $params['key']); if ($req->getVal($id) === null) { // We probably hit the limit with number of post parameters. $postponed[$groupId][$code][$type][$index] = $params; continue; } if ($type === 'deletion' || $req->getCheck("i/{$id}")) { continue; } $fuzzy = $req->getCheck("f/{$id}") ? 'fuzzy' : false; $key = $params['key']; $title = Title::makeTitleSafe($group->getNamespace(), "{$key}/{$code}"); $jobs[] = MessageUpdateJob::newJob($title, $params['content'], $fuzzy); } } if (!isset($postponed[$groupId][$code])) { $cache = new MessageGroupCache($groupId, $code); $cache->create(); } } } JobQueueGroup::singleton()->push($jobs); $reader->close(); rename($changefile, $changefile . '-' . wfTimestamp()); if (count($postponed)) { $changefile = TranslateUtils::cacheFile(self::CHANGEFILE); $writer = CdbWriter::open($changefile); $keys = array_keys($postponed); $writer->set('#keys', serialize($keys)); foreach ($postponed as $groupId => $changes) { $writer->set($groupId, serialize($changes)); } $writer->close(); $this->showChanges(true, $this->getLimit()); } else { $out->addWikiMsg('translate-smg-submitted'); } }
/** * This function does the heavy duty of marking a page. * - Updates the source page with section markers. * - Updates translate_sections table * - Updates revtags table * - Setups renderjobs to update the translation pages * - Invalidates caches * @param TranslatablePage $page * @param array $sections * @return array|bool */ public function markForTranslation(TranslatablePage $page, array $sections) { // Add the section markers to the source page $wikiPage = WikiPage::factory($page->getTitle()); $content = ContentHandler::makeContent($page->getParse()->getSourcePageText(), $page->getTitle()); $status = $wikiPage->doEditContent($content, $this->msg('tpt-mark-summary')->inContentLanguage()->text(), EDIT_FORCE_BOT | EDIT_UPDATE); if (!$status->isOK()) { return array('tpt-edit-failed', $status->getWikiText()); } $newrevision = $status->value['revision']; // In theory it is either null or Revision object, // never revision object with null id, but who knows if ($newrevision instanceof Revision) { $newrevision = $newrevision->getId(); } if ($newrevision === null) { // Probably a no-change edit, so no new revision was assigned. // Get the latest revision manually $newrevision = $page->getTitle()->getLatestRevId(); } $inserts = array(); $changed = array(); $maxid = intval(TranslateMetadata::get($page->getMessageGroupId(), 'maxid')); $pageId = $page->getTitle()->getArticleID(); /** * @var TPSection $s */ foreach (array_values($sections) as $index => $s) { $maxid = max($maxid, intval($s->name)); $changed[] = $s->name; if ($this->getRequest()->getCheck("tpt-sect-{$s->id}-action-nofuzzy")) { // This will be checked by getTranslationUnitJobs $s->type = 'old'; } $inserts[] = array('trs_page' => $pageId, 'trs_key' => $s->name, 'trs_text' => $s->getText(), 'trs_order' => $index); } $dbw = wfGetDB(DB_MASTER); $dbw->delete('translate_sections', array('trs_page' => $page->getTitle()->getArticleID()), __METHOD__); $dbw->insert('translate_sections', $inserts, __METHOD__); TranslateMetadata::set($page->getMessageGroupId(), 'maxid', $maxid); $page->addMarkedTag($newrevision); MessageGroups::singleton()->recache(); $jobs = self::getRenderJobs($page); JobQueueGroup::singleton()->push($jobs); $jobs = self::getTranslationUnitJobs($page, $sections); JobQueueGroup::singleton()->push($jobs); // Logging $this->handlePriorityLanguages($this->getRequest(), $page); $entry = new ManualLogEntry('pagetranslation', 'mark'); $entry->setPerformer($this->getUser()); $entry->setTarget($page->getTitle()); $entry->setParameters(array('revision' => $newrevision, 'changed' => count($changed))); $logid = $entry->insert(); $entry->publish($logid); // Clear more caches $page->getTitle()->invalidateCache(); MessageIndexRebuildJob::newJob()->insert(); return false; }
protected function performAction() { $jobs = array(); $user = $this->getUser(); $target = $this->newTitle; $base = $this->oldTitle->getPrefixedText(); $oldLatest = $this->oldTitle->getLatestRevId(); $params = array('base-source' => $this->oldTitle->getPrefixedText(), 'base-target' => $this->newTitle->getPrefixedText()); $translationPages = $this->getTranslationPages(); foreach ($translationPages as $old) { $to = $this->newPageTitle($base, $old, $target); $jobs[$old->getPrefixedText()] = TranslateMoveJob::newJob($old, $to, $params, $user); } $sectionPages = $this->getSectionPages(); foreach ($sectionPages as $old) { $to = $this->newPageTitle($base, $old, $target); $jobs[$old->getPrefixedText()] = TranslateMoveJob::newJob($old, $to, $params, $user); } if ($this->moveSubpages) { $subpages = $this->getSubpages(); foreach ($subpages as $old) { if (TranslatablePage::isTranslationPage($old)) { continue; } $to = $this->newPageTitle($base, $old, $target); $jobs[$old->getPrefixedText()] = TranslateMoveJob::newJob($old, $to, $params, $user); } } // This is used by TranslateMoveJob wfGetCache(CACHE_ANYTHING)->set(wfMemcKey('translate-pt-move', $base), count($jobs)); JobQueueGroup::singleton()->push($jobs); TranslateMoveJob::forceRedirects(false); $errors = $this->oldTitle->moveTo($this->newTitle, true, $this->reason, false); if (is_array($errors)) { $this->showErrors($errors); } TranslateMoveJob::forceRedirects(true); $newTpage = TranslatablePage::newFromTitle($this->newTitle); $newTpage->addReadyTag($this->newTitle->getLatestRevId(Title::GAID_FOR_UPDATE)); if ($newTpage->getMarkedTag() === $oldLatest) { $newTpage->addMarkedTag($this->newTitle->getLatestRevId(Title::GAID_FOR_UPDATE)); } // remove the entries from metadata table. $oldGroupId = $this->page->getMessageGroupId(); $newGroupId = $newTpage->getMessageGroupId(); $this->moveMetadata($oldGroupId, $newGroupId); MessageGroups::singleton()->recache(); MessageIndexRebuildJob::newJob()->insert(); $this->getOutput()->addWikiMsg('pt-movepage-started'); }
public function execute() { if (!$this->getUser()->isAllowed(self::$right)) { $this->dieUsage('Permission denied', 'permissiondenied'); } $params = $this->extractRequestParams(); $action = $params['do']; $output = array(); if ($action === 'associate' || $action === 'dissociate') { // Group is mandatory only for these two actions if (!isset($params['group'])) { $this->dieUsageMsg(array('missingparam', 'group')); } if (!isset($params['aggregategroup'])) { $this->dieUsageMsg(array('missingparam', 'aggregategroup')); } $aggregateGroup = $params['aggregategroup']; $subgroups = TranslateMetadata::getSubgroups($aggregateGroup); if (count($subgroups) === 0) { // For newly created groups the subgroups value might be empty, // but check that. if (TranslateMetadata::get($aggregateGroup, 'name') === false) { $this->dieUsage('Invalid aggregate message group', 'invalidaggregategroup'); } $subgroups = array(); } $subgroupId = $params['group']; $group = MessageGroups::getGroup($subgroupId); // Add or remove from the list if ($action === 'associate') { if (!$group instanceof WikiPageMessageGroup) { $this->dieUsage('Group does not exist or invalid', 'invalidgroup'); } $subgroups[] = $subgroupId; $subgroups = array_unique($subgroups); } elseif ($action === 'dissociate') { // Allow removal of non-existing groups $subgroups = array_flip($subgroups); unset($subgroups[$subgroupId]); $subgroups = array_flip($subgroups); } TranslateMetadata::setSubgroups($aggregateGroup, $subgroups); $logParams = array('aggregategroup' => TranslateMetadata::get($aggregateGroup, 'name'), 'aggregategroup-id' => $aggregateGroup); /* Note that to allow removing no longer existing groups from * aggregate message groups, the message group object $group * might not always be available. In this case we need to fake * some title. */ $title = $group ? $group->getTitle() : Title::newFromText("Special:Translate/{$subgroupId}"); $entry = new ManualLogEntry('pagetranslation', $action); $entry->setPerformer($this->getUser()); $entry->setTarget($title); // @todo // $entry->setComment( $comment ); $entry->setParameters($logParams); $logid = $entry->insert(); $entry->publish($logid); } elseif ($action === 'remove') { if (!isset($params['aggregategroup'])) { $this->dieUsageMsg(array('missingparam', 'aggregategroup')); } TranslateMetadata::deleteGroup($params['aggregategroup']); // @todo Logging } elseif ($action === 'add') { if (!isset($params['groupname'])) { $this->dieUsageMsg(array('missingparam', 'groupname')); } $name = trim($params['groupname']); if (strlen($name) === 0) { $this->dieUsage('Invalid aggregate message group name', 'invalidaggregategroupname'); } if (!isset($params['groupdescription'])) { $this->dieUsageMsg(array('missingparam', 'groupdescription')); } $desc = trim($params['groupdescription']); $aggregateGroupId = self::generateAggregateGroupId($name); // Throw error if group already exists $nameExists = MessageGroups::labelExists($name); if ($nameExists) { $this->dieUsage('Message group already exists', 'duplicateaggregategroup'); } // ID already exists- Generate a new ID by adding a number to it. $idExists = MessageGroups::getGroup($aggregateGroupId); if ($idExists) { $i = 1; while ($idExists) { $tempId = $aggregateGroupId . "-" . $i; $idExists = MessageGroups::getGroup($tempId); $i++; } $aggregateGroupId = $tempId; } TranslateMetadata::set($aggregateGroupId, 'name', $name); TranslateMetadata::set($aggregateGroupId, 'description', $desc); TranslateMetadata::setSubgroups($aggregateGroupId, array()); // Once new aggregate group added, we need to show all the pages that can be added to that. $output['groups'] = self::getAllPages(); $output['aggregategroupId'] = $aggregateGroupId; // @todo Logging } elseif ($action === 'update') { if (!isset($params['groupname'])) { $this->dieUsageMsg(array('missingparam', 'groupname')); } $name = trim($params['groupname']); if (strlen($name) === 0) { $this->dieUsage('Invalid aggregate message group name', 'invalidaggregategroupname'); } $desc = trim($params['groupdescription']); $aggregateGroupId = $params['aggregategroup']; $oldName = TranslateMetadata::get($aggregateGroupId, 'name'); $oldDesc = TranslateMetadata::get($aggregateGroupId, 'description'); // Error if the label exists already $exists = MessageGroups::labelExists($name); if ($exists && $oldName !== $name) { $this->dieUsage('Message group name already exists', 'duplicateaggregategroup'); } if ($oldName === $name && $oldDesc === $desc) { $this->dieUsage('Invalid update', 'invalidupdate'); } TranslateMetadata::set($aggregateGroupId, 'name', $name); TranslateMetadata::set($aggregateGroupId, 'description', $desc); } // If we got this far, nothing has failed $output['result'] = 'ok'; $this->getResult()->addValue(null, $this->getModuleName(), $output); // Cache needs to be cleared after any changes to groups MessageGroups::singleton()->recache(); MessageIndexRebuildJob::newJob()->insert(); }
/** * Checks if the handle corresponds to a known message. * @since 2011-03-16 * @return bool */ public function isValid() { if (!$this->isMessageNamespace()) { return false; } $groups = $this->getGroupIds(); if (!$groups) { return false; } // Do another check that the group actually exists $group = $this->getGroup(); if (!$group) { $warning = "MessageIndex is out of date – refers to unknown group {$groups[0]}. "; $warning .= "Doing a rebuild."; wfWarn($warning); MessageIndexRebuildJob::newJob()->run(); return false; } return true; }
/** * This function does the heavy duty of marking a page. * - Updates the source page with section markers. * - Updates translate_sections table * - Updates revtags table * - Setups renderjobs to update the translation pages * - Invalidates caches * @param $page TranslatablePage * @param $sections array * @return array|bool */ public function markForTranslation( TranslatablePage $page, Array $sections ) { global $wgRequest; // Add the section markers to the source page $article = new Article( $page->getTitle(), 0 ); $status = $article->doEdit( $page->getParse()->getSourcePageText(), // Content wfMsgForContent( 'tpt-mark-summary' ), // Summary EDIT_FORCE_BOT | EDIT_UPDATE, // Flags $page->getRevision() // Based-on revision ); if ( !$status->isOK() ) { self::superDebug( __METHOD__, 'edit-fail', $this->user, $page->getTitle(), $status ); return array( 'tpt-edit-failed', $status->getWikiText() ); } $newrevision = $status->value['revision']; // In theory it is either null or Revision object, // never revision object with null id, but who knows if ( $newrevision instanceof Revision ) { $newrevision = $newrevision->getId(); } if ( $newrevision === null ) { // Probably a no-change edit, so no new revision was assigned. // Get the latest revision manually $newrevision = $page->getTitle()->getLatestRevId(); } self::superDebug( __METHOD__, 'latestrev', $page->getTitle(), $newrevision ); $inserts = array(); $changed = array(); $pageId = $page->getTitle()->getArticleId(); foreach ( array_values( $sections ) as $index => $s ) { if ( $s->type === 'changed' ) { // Allow silent changes to avoid fuzzying unnecessary. if ( !$wgRequest->getCheck( "tpt-sect-{$s->id}-action-nofuzzy" ) ) { $changed[] = $s->name; } } $inserts[] = array( 'trs_page' => $pageId, 'trs_key' => $s->name, 'trs_text' => $s->getText(), 'trs_order' => $index ); } // Don't add stuff if no changes, use the plain null instead for prettiness if ( !count( $changed ) ) { $changed = null; } $dbw = wfGetDB( DB_MASTER ); if ( !$dbw->fieldExists( 'translate_sections', 'trs_order', __METHOD__ ) ) { error_log( 'Field trs_order does not exist. Please run update.php.' ); foreach ( array_keys( $inserts ) as $index ) { unset( $inserts[$index]['trs_order'] ); } } $dbw->delete( 'translate_sections', array( 'trs_page' => $page->getTitle()->getArticleId() ), __METHOD__ ); $dbw->insert( 'translate_sections', $inserts, __METHOD__ ); /* Stores the names of changed sections in the database. * Used for calculating completion percentages for outdated messages */ $page->addMarkedTag( $newrevision, $changed ); $this->addFuzzyTags( $page, $changed ); global $wgUser; $logger = new LogPage( 'pagetranslation' ); $params = array( 'user' => $wgUser->getName(), 'revision' => $newrevision, 'changed' => count( $changed ), ); $logger->addEntry( 'mark', $page->getTitle(), null, array( serialize( $params ) ) ); $page->getTitle()->invalidateCache(); $this->setupRenderJobs( $page ); // Re-generate caches MessageGroups::clearCache(); MessageIndexRebuildJob::newJob()->insert(); return false; }