public function execute()
 {
     $groupIds = explode(',', trim($this->getOption('group')));
     $groupIds = MessageGroups::expandWildcards($groupIds);
     $groups = MessageGroups::getGroupsById($groupIds);
     if (!count($groups)) {
         $this->error("ESG2: No valid message groups identified.", 1);
     }
     $start = $this->getOption('start') ? strtotime($this->getOption('start')) : false;
     $end = $this->getOption('end') ? strtotime($this->getOption('end')) : false;
     $this->output("Conflict times: " . wfTimestamp(TS_ISO_8601, $start) . " - " . wfTimestamp(TS_ISO_8601, $end) . "\n");
     $codes = array_filter(array_map('trim', explode(',', $this->getOption('lang'))));
     $supportedCodes = array_keys(TranslateUtils::getLanguageNames('en'));
     ksort($supportedCodes);
     if ($codes[0] === '*') {
         $codes = $supportedCodes;
     }
     /** @var FileBasedMessageGroup $group */
     foreach ($groups as $groupId => &$group) {
         if ($group->isMeta()) {
             $this->output("Skipping meta message group {$groupId}.\n");
             continue;
         }
         $this->output("{$group->getLabel()} ", $group);
         foreach ($codes as $code) {
             // No sync possible for unsupported language codes.
             if (!in_array($code, $supportedCodes)) {
                 $this->output("Unsupported code " . $code . ": skipping.\n");
                 continue;
             }
             $file = $group->getSourceFilePath($code);
             if (!$file) {
                 continue;
             }
             if (!file_exists($file)) {
                 continue;
             }
             $cs = new ChangeSyncer($group, $this);
             $cs->setProgressCallback(array($this, 'myOutput'));
             $cs->interactive = !$this->hasOption('noask');
             $cs->nocolor = $this->hasOption('nocolor');
             $cs->norc = $this->hasOption('norc');
             # @todo FIXME: Make this auto detect.
             # Guess last modified date of the file from either git, svn or filesystem
             if ($this->hasOption('git')) {
                 $ts = $cs->getTimestampsFromGit($file);
             } else {
                 $ts = $cs->getTimestampsFromSvn($file);
             }
             if (!$ts) {
                 $ts = $cs->getTimestampsFromFs($file);
             }
             $this->output("Modify time for {$code}: " . wfTimestamp(TS_ISO_8601, $ts) . "\n");
             $cs->checkConflicts($code, $start, $end, $ts);
         }
         unset($group);
     }
     // Print timestamp if the user wants to store it
     $this->output(wfTimestamp(TS_RFC2822) . "\n");
 }
 public function getGroups()
 {
     if (!isset($this->groups)) {
         $groups = array();
         $ids = (array) $this->conf['GROUPS'];
         $ids = MessageGroups::expandWildcards($ids);
         foreach ($ids as $id) {
             // Do not try to include self and go to infinite loop.
             if ($id === $this->getId()) {
                 continue;
             }
             $group = MessageGroups::getGroup($id);
             if ($group === null) {
                 error_log("Invalid group id in {$this->getId()}: {$id}");
                 continue;
             }
             if (MessageGroups::getPriority($group) === 'discouraged') {
                 continue;
             }
             $groups[$id] = $group;
         }
         $this->groups = $groups;
     }
     return $this->groups;
 }
 /**
  * Gets list of message groups filtered by user input.
  * @return MessageGroup[]
  */
 protected function getGroups()
 {
     $groups = MessageGroups::getGroupsByType('FileBasedMessageGroup');
     // Include all if option not given
     $include = $this->getOption('group', '*');
     $include = explode(',', $include);
     $include = array_map('trim', $include);
     $include = MessageGroups::expandWildcards($include);
     // Exclude nothing if option not given
     $exclude = $this->getOption('skipgroup', '');
     $exclude = explode(',', $exclude);
     $exclude = array_map('trim', $exclude);
     $exclude = MessageGroups::expandWildcards($exclude);
     // Flip to allow isset
     $include = array_flip($include);
     $exclude = array_flip($exclude);
     $groups = array_filter($groups, function (MessageGroup $group) use($include, $exclude) {
         $id = $group->getId();
         return isset($include[$id]) && !isset($exclude[$id]);
     });
     return $groups;
 }
 public function execute()
 {
     $target = $this->getOption('target');
     if (!is_writable($target)) {
         $this->error("Target directory is not writable ({$target}).", 1);
     }
     $threshold = $this->getOption('threshold');
     $noFuzzy = $this->hasOption('no-fuzzy');
     $noLocation = '';
     if ($this->hasOption('no-location')) {
         $noLocation = '--no-location ';
     }
     $skip = array();
     if ($this->hasOption('skip')) {
         $skip = array_map('trim', explode(',', $this->getOption('skip')));
     }
     $reqLangs = TranslateUtils::parseLanguageCodes($this->getOption('lang'));
     $reqLangs = array_flip($reqLangs);
     foreach ($skip as $skipLang) {
         unset($reqLangs[$skipLang]);
     }
     $reqLangs = array_flip($reqLangs);
     $codemapOnly = $this->hasOption('codemaponly');
     $groupIds = explode(',', trim($this->getOption('group')));
     $groupIds = MessageGroups::expandWildcards($groupIds);
     $groups = MessageGroups::getGroupsById($groupIds);
     /** @var FileBasedMessageGroup $group */
     foreach ($groups as $groupId => $group) {
         if ($group->isMeta()) {
             $this->output("Skipping meta message group {$groupId}.\n");
             unset($groups[$groupId]);
             continue;
         }
         if (!$group instanceof FileBasedMessageGroup) {
             $this->output("EE2: Unexportable message group {$groupId}.\n");
             unset($groups[$groupId]);
             continue;
         }
     }
     if (!count($groups)) {
         $this->error("EE1: No valid message groups identified.", 1);
     }
     $changeFilter = false;
     $hours = $this->getOption('hours');
     if ($hours) {
         $namespaces = array();
         /** @var FileBasedMessageGroup $group */
         foreach ($groups as $group) {
             $namespaces[$group->getNamespace()] = true;
         }
         $namespaces = array_keys($namespaces);
         $bots = true;
         $changeFilter = array();
         $rows = TranslateUtils::translationChanges($hours, $bots, $namespaces);
         foreach ($rows as $row) {
             $title = Title::makeTitle($row->rc_namespace, $row->rc_title);
             $handle = new MessageHandle($title);
             $code = $handle->getCode();
             if (!$code) {
                 continue;
             }
             $groupIds = $handle->getGroupIds();
             foreach ($groupIds as $groupId) {
                 $changeFilter[$groupId][$code] = true;
             }
         }
     }
     $skipGroups = array();
     if ($this->hasOption('skipgroup')) {
         $skipGroups = array_map('trim', explode(',', $this->getOption('skipgroup')));
     }
     foreach ($groups as $groupId => $group) {
         if (in_array($groupId, $skipGroups)) {
             $this->output("Group {$groupId} is in skipgroup.\n");
             continue;
         }
         // No changes to this group at all
         if (is_array($changeFilter) && !isset($changeFilter[$groupId])) {
             $this->output("No recent changes to {$groupId}.\n");
             continue;
         }
         $langs = $reqLangs;
         if ($codemapOnly) {
             foreach ($langs as $index => $code) {
                 if ($group->mapCode($code) === $code) {
                     unset($langs[$index]);
                 }
             }
         }
         if ($threshold) {
             $stats = MessageGroupStats::forGroup($groupId);
             foreach ($langs as $index => $code) {
                 if (!isset($stats[$code])) {
                     unset($langs[$index]);
                     continue;
                 }
                 $total = $stats[$code][MessageGroupStats::TOTAL];
                 $translated = $stats[$code][MessageGroupStats::TRANSLATED];
                 if ($translated / $total * 100 < $threshold) {
                     unset($langs[$index]);
                 }
             }
         }
         // Filter out unchanged languages from requested languages
         if (is_array($changeFilter)) {
             $langs = array_intersect($langs, array_keys($changeFilter[$groupId]));
         }
         if (!count($langs)) {
             continue;
         }
         $this->output("Exporting {$groupId}...\n");
         $ffs = $group->getFFS();
         $ffs->setWritePath($target);
         $sourceLanguage = $group->getSourceLanguage();
         $collection = $group->initCollection($sourceLanguage);
         $definitionFile = false;
         if ($this->hasOption('ppgettext') && $ffs instanceof GettextFFS) {
             global $wgMaxShellMemory, $wgTranslateGroupRoot;
             // Need more shell memory for msgmerge.
             $wgMaxShellMemory = 402400;
             $path = $group->getSourceFilePath($sourceLanguage);
             $definitionFile = str_replace($wgTranslateGroupRoot, $this->getOption('ppgettext'), $path);
         }
         $whitelist = $group->getTranslatableLanguages();
         foreach ($langs as $lang) {
             // Do not export languges that are blacklisted (or not whitelisted).
             // Also check that whitelist is not null, which means that all
             // languages are allowed for translation and export.
             if (is_array($whitelist) && !isset($whitelist[$lang])) {
                 continue;
             }
             $collection->resetForNewLanguage($lang);
             $collection->loadTranslations();
             // Don't export ignored, unless it is the source language
             // or message documentation
             global $wgTranslateDocumentationLanguageCode;
             if ($lang !== $wgTranslateDocumentationLanguageCode && $lang !== $sourceLanguage) {
                 $collection->filter('ignored');
             }
             if ($noFuzzy) {
                 $collection->filter('fuzzy');
             }
             $ffs->write($collection);
             // Do post processing if requested.
             if ($definitionFile) {
                 if (is_file($definitionFile)) {
                     $targetFileName = $ffs->getWritePath() . "/" . $group->getTargetFilename($collection->code);
                     $cmd = "msgmerge --quiet " . $noLocation . "--output-file=" . $targetFileName . ' ' . $targetFileName . ' ' . $definitionFile;
                     wfShellExec($cmd, $ret);
                     // Report on errors.
                     if ($ret) {
                         $this->error("ERROR: {$ret}");
                     }
                 } else {
                     $this->error("{$definitionFile} does not exist.", 1);
                 }
             }
         }
     }
 }
 public function execute()
 {
     $codes = Language::fetchLanguageNames(false);
     // Exclude the documentation language code
     global $wgTranslateDocumentationLanguageCode;
     if ($wgTranslateDocumentationLanguageCode) {
         unset($codes[$wgTranslateDocumentationLanguageCode]);
     }
     $reqGroups = $this->getOption('group');
     if ($reqGroups) {
         $reqGroups = explode(',', $reqGroups);
         $reqGroups = array_map('trim', $reqGroups);
         $reqGroups = MessageGroups::expandWildcards($reqGroups);
     }
     $verbose = isset($options['verbose']);
     $groups = MessageGroups::singleton()->getGroups();
     /** @var $g MessageGroup */
     foreach ($groups as $g) {
         $id = $g->getId();
         $sourceLanguage = $g->getSourceLanguage();
         // Skip groups that are not requested
         if ($reqGroups && !in_array($id, $reqGroups)) {
             unset($g);
             continue;
         }
         $checker = $g->getChecker();
         if (!$checker) {
             unset($g);
             continue;
         }
         // Initialise messages, using unique definitions if appropriate
         $collection = $g->initCollection($sourceLanguage, true);
         if (!count($collection)) {
             continue;
         }
         $this->output("Working with {$id}: ", $id);
         // Skip source language code
         $langCodes = $codes;
         unset($langCodes[$sourceLanguage]);
         $langCodes = array_keys($langCodes);
         sort($langCodes);
         foreach ($langCodes as $code) {
             $this->output("{$code} ", $id);
             $problematic = array();
             $collection->resetForNewLanguage($code);
             $collection->loadTranslations();
             global $wgContLang;
             foreach ($collection as $key => $message) {
                 $prob = $checker->checkMessageFast($message, $code);
                 if ($prob) {
                     if ($verbose) {
                         // Print it
                         $nsText = $wgContLang->getNsText($g->namespaces[0]);
                         $this->output("# [[{$nsText}:{$key}/{$code}]]\n");
                     }
                     // Add it to the array
                     $problematic[] = array($g->namespaces[0], "{$key}/{$code}");
                 }
             }
             self::tagFuzzy($problematic);
         }
     }
 }