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"); }
/** * @param AggregateMessageGroup $parent * @return string */ protected function listSubgroups(AggregateMessageGroup $parent) { $out = ''; $id = $this->htmlIdForGroup($parent, 'mw-tpa-grouplist-'); $out = Html::openElement('ol', array('id' => $id)); // Not calling $parent->getGroups() because it has done filtering already $subgroupIds = TranslateMetadata::getSubgroups($parent->getId()); // Get the respective groups and sort them $subgroups = MessageGroups::getGroupsById($subgroupIds); uasort($subgroups, array('MessageGroups', 'groupLabelSort')); // Add missing invalid group ids back, not returned by getGroupsById foreach ($subgroupIds as $id) { if (!isset($subgroups[$id])) { $subgroups[$id] = null; } } foreach ($subgroups as $id => $group) { $remove = ''; if ($this->hasPermission) { $remove = Html::element('span', array('class' => 'tp-aggregate-remove-button', 'data-groupid' => $id)); } if ($group) { $text = Linker::linkKnown($group->getTitle()); $note = MessageGroups::getPriority($id); } else { $text = htmlspecialchars($id); $note = $this->msg('tpt-aggregategroup-invalid-group')->escaped(); } $out .= Html::rawElement('li', array(), "{$text}{$remove} {$note}"); } $out .= Html::closeElement('ol'); return $out; }
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); } } } } }