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;
	}