/** * Generate a path to the mercurial repo for the file * * @param string $locale locale code * @param string $repo repository name * @param string $path Entity name representing the local file * @return string Path to the file in remote mercurial repository */ public static function hgPath($locale, $repo, $path) { $url = 'https://hg.mozilla.org'; // remove entity from path and store it in a variable $path = explode(':', $path); $path = $path[0]; $path = explode('/', $path); $entity_file = array_pop($path); $path = implode('/', $path); $exploded_path = explode('/', $path); $base_folder = $exploded_path[0]; if (Strings::startsWith($repo, 'gaia') || in_array($base_folder, ['apps', 'shared', 'showcase_apps', 'test_apps', 'test_external_apps'])) { $locale = Project::getLocaleInContext($locale, $repo); if ($repo == 'gaia') { $url .= '/gaia-l10n/' . $locale . '/file/default/'; } else { $version = str_replace('gaia_', '', $repo); $url .= '/releases/gaia-l10n/v' . $version . '/' . $locale . '/file/default/'; } return $url . $path . '/' . $entity_file; } $en_US_Folder_Mess = ['b2g/branding/official/', 'b2g/branding/unofficial/', 'b2g/', 'netwerk/', 'embedding/android/', 'testing/extensions/community/chrome/', 'intl/', 'extensions/spellcheck/', 'services/sync/', 'mobile/android/branding/aurora/', 'mobile/android/branding/official/', 'mobile/android/branding/nightly/', 'mobile/android/branding/unofficial/', 'mobile/android/branding/beta/', 'mobile/android/base/', 'mobile/android/', 'mobile/', 'security/manager/', 'toolkit/content/tests/fennec-tile-testapp/chrome/', 'toolkit/', 'browser/branding/aurora/', 'browser/branding/official/', 'browser/branding/nightly/', 'browser/branding/unofficial/', 'browser/', 'devtools/client/', 'devtools/shared/', 'layout/tools/layout-debug/ui/', 'dom/', 'webapprt/', 'chat/', 'suite/', 'other-licenses/branding/thunderbird/', 'mail/branding/aurora/', 'mail/branding/nightly/', 'mail/', 'mail/test/resources/mozmill/mozmill/extension/', 'editor/ui/', 'calendar/']; // Destop repos if ($locale != 'en-US') { if ($repo == 'central') { $url .= '/l10n-central/' . $locale . '/file/default/'; } else { $url .= '/releases/l10n/mozilla-' . $repo . '/' . $locale . '/file/default/'; } } else { if (in_array($base_folder, ['calendar', 'chat', 'editor', 'ldap', 'mail', 'mailnews', 'suite'])) { $repo_base = 'comm'; } else { $repo_base = 'mozilla'; } if ($repo == 'central') { $url .= "/{$repo_base}-central/file/default/"; } else { $url .= "/releases/{$repo_base}-{$repo}/file/default/"; } $loop = true; while ($loop && count($exploded_path) > 0) { if (in_array(implode('/', $exploded_path) . '/', $en_US_Folder_Mess)) { $path_part1 = implode('/', $exploded_path) . '/locales/en-US'; $pattern = preg_quote(implode('/', $exploded_path), '/'); $path = preg_replace('/' . $pattern . '/', $path_part1, $path, 1); $loop = false; break; } else { array_pop($exploded_path); } } if ($loop == true) { $path = explode('/', $path); $category = array_shift($path); $path = $category . '/locales/en-US/' . implode('/', $path); } } return $url . $path . '/' . $entity_file; }
/** * * Loads strings from a .lang file into an array. File format is: * ;String in english * translated string * * @param string $file .lang file to load * @param boolean $reference_locale True if the current locale is the reference locale * @return array Array of strings as [original => translation] */ public static function getStrings($file, $reference_locale) { // get all the lines in an array $f = self::getFile($file); $strings = []; for ($i = 0, $lines = count($f); $i < $lines; $i++) { // skip comments and metadata if (Strings::startsWith($f[$i], '#')) { continue; } if (Strings::startsWith($f[$i], ';') && !empty($f[$i + 1])) { $english = trim(substr($f[$i], 1)); if (Strings::startsWith($f[$i + 1], '#') || Strings::startsWith($f[$i + 1], ';')) { /* String is not translated, next line is a comment or * the next source string. I consider the string untranslated */ $translation = $english; } else { // Next line is an actual translation $translation = trim($f[$i + 1]); } // If untranslated, I need to store an empty string as translation // unless it's the reference locale if ($reference_locale) { $strings[$english] = $english; } else { $strings[$english] = $translation == $english ? '' : $translation; } $i++; } } return $strings; }
/** * Contains returns true if a base path contains a needle. * * Note that `realpath` is used on both base and needle: they need to exist or false is returned. * * Use this for avoiding directory traversal outside of a base path. * * @param string $base Path to base directory. * @param string $needle Needle that must exist within the base directory. * * @return bool True if both exist and needle does not escape the base folder. */ public static function contains($base, $needle) { $realBase = realpath($base); $needle = realpath($needle); if ($realBase === false || $needle === false) { return false; } return Strings::startsWith($needle, $base); }
/** * Return the list of files in a folder as an array. * Hidden files starting with a dot (.svn, .htaccess...) are ignored. * * @param string $folder the directory we want to scan * @param array $excluded_files Files to exclude from results * @return array Files in the folder */ public static function getFilenamesInFolder($folder, $excluded_files = []) { /* Here we exclude by default hidden files starting with a dot and the . and .. symbols for directories */ $files = array_filter(scandir($folder), function ($item) { return !Strings::startsWith($item, '.'); }); return array_diff($files, $excluded_files); }
public function login() { if (isset($_GET['returnto']) && Strings::startsWith($_GET['returnto'], '/')) { $returnto = $_GET['returnto']; } else { $returnto = (string) new URL(); } if ($_SERVER['REQUEST_METHOD'] === 'POST') { $query = db()->table('user')->getAll(); $query->group()->addRestriction('email', $_POST['username'])->addRestriction('usernames', db()->table('username')->get('name', $_POST['username'])->addRestriction('expires', NULL, 'IS'))->endGroup(); $user = $query->fetch(); if ($user && $user->checkPassword($_POST['password'])) { $session = Session::getInstance(); $session->lock($user->_id); return $this->response->getHeaders()->redirect($returnto); } else { $this->view->set('message', 'Username or password did not match'); } } $this->view->set('returnto', $returnto); }
/** * Search for strings with variables differences * @param array $tmx_source TMX file as reference * @param array $tmx_target TMX file for the locale to compare * @param string $repo Name of the repo to determine the right set of regexps * @param array $ignored_strings Optional list of ignored strings, default empty * @return array List of entity names not matching source */ public static function differences($tmx_source, $tmx_target, $repo, $ignored_strings = []) { $pattern_mismatch = []; switch ($repo) { case Strings::startsWith($repo, 'gaia'): $patterns = ['l10njs' => '/\\{\\{\\s*([a-z0-9_]+)\\s*\\}\\}/iu']; break; case 'firefox_ios': $patterns = ['ios' => '/(%(?:[0-9]+\\$){0,1}@)/i']; break; default: $patterns = ['dtd' => '/&([a-z0-9\\.]+);/i', 'printf' => '/(%(?:[0-9]+\\$){0,1}(?:[0-9].){0,1}(S))/i', 'properties' => '/(?<!%[0-9])\\$[a-z0-9\\.]+\\b/i', 'l10njs' => '/\\{\\{\\s*([a-z0-9_]+)\\s*\\}\\}/iu']; break; } foreach ($patterns as $pattern_name => $pattern) { foreach ($tmx_source as $key => $source) { if (isset($tmx_target[$key]) && $tmx_target[$key] != '' && !in_array($key, $ignored_strings)) { // Check variables only if the translation exists and // the string is not in the list of strings to ignore $translation = $tmx_target[$key]; $wrong_variable = false; preg_match_all($pattern, $source, $matches_source); preg_match_all($pattern, $translation, $matches_translation); if (count($matches_source[0]) > 0) { foreach ($matches_source[0] as $var_source) { if (!in_array($var_source, $matches_translation[0])) { $wrong_variable = true; } // For l10n.js {{n}} == {{ n }} if ($pattern_name == 'l10njs' && $wrong_variable) { // Trim whitespaces and sort variables alphabetically $trimmed_source_vars = array_map('trim', $matches_source[1]); sort($trimmed_source_vars); $trimmed_source_trans = array_map('trim', $matches_translation[1]); sort($trimmed_source_trans); if ($trimmed_source_vars === $trimmed_source_trans) { $wrong_variable = false; } } } } if ($pattern_name == 'printf') { /* * Check ordered vs unordered variables. The pattern * regular expression returns "S" or "s" as second group * for each variable, I can use it to check for case * differences ("s" vs "S") which are not allowed. */ preg_match_all('/%(?:[0-9]+\\$){1}(?:[0-9].){0,1}S/i', $translation, $matches_ordered_trans); preg_match_all('/%(?:0.){0,1}S/i', $translation, $matches_unordered_trans); if (count($matches_ordered_trans[0]) > 0 && count($matches_unordered_trans[0]) > 0) { // Strings can't mix ordered and unordered variables $wrong_variable = true; } else { if (count($matches_translation[0]) == count($matches_source[0]) && $matches_translation[2] === $matches_source[2]) { // Same number of variables and case, I assume the string is OK $wrong_variable = false; } } } if ($wrong_variable) { $pattern_mismatch[] = $key; } } } } return $pattern_mismatch; }
/** * Handles CONTEXT_TAG. */ private function contextTag() { $matches = $this->match('~ (?P<end>\\ ?/?>)([ \\t]*\\n)?| ## end of HTML tag ' . $this->macroRe . '| ## macro tag \\s*(?P<attr>[^\\s/>={]+)(?:\\s*=\\s*(?P<value>["\']|[^\\s/>{]+))? ## begin of HTML attribute ~xsi'); if (!empty($matches['end'])) { // end of HTML tag /> $this->addToken(LatteToken::HTML_TAG_END, $matches[0]); $this->setContext(!$this->xmlMode && in_array($this->lastHtmlTag, array('script', 'style')) ? self::CONTEXT_CDATA : self::CONTEXT_TEXT); } elseif (isset($matches['attr']) && $matches['attr'] !== '') { // HTML attribute $token = $this->addToken(LatteToken::HTML_ATTRIBUTE, $matches[0]); $token->name = $matches['attr']; $token->value = isset($matches['value']) ? $matches['value'] : ''; if ($token->value === '"' || $token->value === "'") { // attribute = "' if (Strings::startsWith($token->name, self::N_PREFIX)) { $token->value = ''; if ($m = $this->match('~(.*?)' . $matches['value'] . '~xsi')) { $token->value = $m[1]; $token->text .= $m[0]; } } else { $this->setContext(self::CONTEXT_ATTRIBUTE, $matches['value']); } } } return $matches; }
/** * Ověří, zda soubor je v zadaném adresáři. * @param string $file * @param string $dir * @return bool */ static function isFileInDir($file, $dir) { if (!Strings::endsWith($dir, "/")) { $dir .= "/"; } return Strings::startsWith($file, $dir); }
public static function convertFileName($fileName, $pattern) { if (empty($pattern)) { return $fileName; } $new = preg_replace_callback("({[\\w\\s\\[\\],-_]+})", function ($p) use($fileName) { $a = $p[0]; if ($a == '{md5}') { return md5($fileName); } else { if ($a == '{ext}') { return substr(strrchr($fileName, '.'), 1); } else { if ($a == '{origin}') { $ext = strrchr($fileName, '.'); return substr($fileName, 0, strlen($fileName) - strlen($ext)); } else { if (Strings::startsWith($a, '{date')) { preg_match('(\\[.*\\])', $a, $p); $pattern = trim($p[0], "[]"); return date($pattern, time()); } else { if ($a == '{unix_timestamp}') { return time(); } else { if (Strings::startsWith($a, '{random')) { preg_match('(\\[.*\\])', $a, $p); $pattern = trim($p[0], "[]"); $r = explode(',', $pattern); return rand($r[0], $r[1]); } } } } } } return $a; }, $pattern); return $new; }
/** * Adds prefix to the string, if string does not start with the prefix already. * * Example: * <code> * $string = 'Daenerys Targaryen'; * $stringWithPrefix = Strings::appendMissingPrefix($string, 'Daenerys '); * </code> * Result: * <code> * Daenerys Targaryen * </code> * * @param string $string * @param string $prefix * @return string */ public static function prependIfMissing($string, $prefix) { if (Strings::startsWith($string, $prefix)) { return $string; } return Strings::appendPrefix($string, $prefix); }
/** * Return the correct locale code based on context * For example: given "es", returns "es-ES" for Bugzilla, * "es" for Gaia, "es-ES" for other repos. * * @param string $locale Name of the current locale * @param string $context The context we need to use this locale in * @return string Locale code to use in the requested context */ public static function getLocaleInContext($locale, $context) { $locale_mappings = []; // Bugzilla locales $locale_mappings['bugzilla'] = ['es' => 'es-ES', 'gu' => 'gu-IN', 'pa' => 'pa-IN', 'sr-Cyrl' => 'sr', 'sr-Latn' => 'sr']; // Gaia locales $locale_mappings['gaia'] = ['es-AR' => 'es', 'es-CL' => 'es', 'es-ES' => 'es', 'es-MX' => 'es', 'gu-IN' => 'gu', 'pa-IN' => 'pa', 'sr' => 'sr-Cyrl']; // Use Gaia mapping for all Gaia repositories if (Strings::startsWith($context, 'gaia')) { $context = 'gaia'; } // Firefox for iOS $locale_mappings['firefox_ios'] = ['es-AR' => 'es', 'es-ES' => 'es']; // For other contexts use the same as Bugzilla $locale_mappings['other'] = $locale_mappings['bugzilla']; // Fallback to 'other' if context doesn't exist in $locale_mappings $context = array_key_exists($context, $locale_mappings) ? $context : 'other'; $locale = array_key_exists($locale, $locale_mappings[$context]) ? $locale_mappings[$context][$locale] : $locale; return $locale; }
public static function isearchStartsWith($needle, array $haystack) { foreach ($haystack as $key => $val) { if (Strings::startsWith(strtolower($val), strtolower($needle))) { return $key; } } return false; }
/** * @return null|string */ private function readMercurial() { $hgDir = Path::combine($this->repositoryRoot, '.hg'); if (!is_dir($hgDir) || !OS::hasBinary('hg')) { return null; } $hg = Exec::create('hg', '--repository', $this->repositoryRoot); // Removes everything but the tag if distance is zero. $hg->run('log', '-r', '.', '--template', '{latesttag}{sub(\'^-0-m.*\', \'\', \'-{latesttagdistance}-m{node|short}\')}'); $tag = Dot::get($hg->getOutput(), 0); // Actual null if no lines were returned or `hg log` returned actual "null". // Either way, need to fall back to the revision id. if ($tag === null || $tag === 'null' || Strings::startsWith($tag, 'null-')) { $hg->run('id', '-i'); $tag = Dot::get($hg->getOutput(), 0); // Remove 'dirty' plus from revision id $tag = rtrim($tag, '+'); } $summary = $hg->run('summary')->getOutput(); $isDirty = 0 === count(array_filter($summary, function ($line) { return preg_match('/^commit: .*\\(clean\\)$/', $line) === 1; })); if ($isDirty) { $tag .= '-dirty'; } return $tag; }
/** * startsWith() をテストします. 以下を確認します. * * - 第 1 引数の文字列の先頭が第 2 引数の文字列に合致した場合に true, それ以外は false を返す * - 第 2 引数が空文字列の場合は true を返す * - 引数が文字列型でない場合は文字列に変換してから適用する * * @covers Peach\Util\Strings::startsWith */ public function testStartsWith() { $this->assertSame(true, Strings::startsWith("The quick brown fox", "The")); $this->assertSame(false, Strings::startsWith("Hogehoge", "hoge")); $this->assertSame(true, Strings::startsWith("something", "")); $prefix = new StringsTest_Object("TEST"); $subject = new StringsTest_Object("TEST object"); $other = new StringsTest_Object("fuga"); $this->assertSame(true, Strings::startsWith($subject, $prefix)); $this->assertSame(false, Strings::startsWith($subject, $other)); }
public static function startsWith($prefix) { return function ($string) use($prefix) { return Strings::startsWith($string, $prefix); }; }
/** * Generates code for {macro ...} to the output. * @param string * @param string * @param string * @param bool * @return MacroNode */ public function writeMacro($name, $args = NULL, $modifiers = NULL, $isRightmost = FALSE, HtmlNode $htmlNode = NULL, $prefix = NULL) { if ($name[0] === '/') { // closing $node = end($this->macroNodes); if (!$node || "/{$node->name}" !== $name && '/' !== $name || $modifiers || $args && $node->args && !Strings::startsWith("{$node->args} ", "{$args} ")) { $name .= $args ? ' ' : ''; throw new CompileException("Unexpected macro {{$name}{$args}{$modifiers}}" . ($node ? ", expecting {/{$node->name}}" . ($args && $node->args ? " or eventually {/{$node->name} {$node->args}}" : '') : '')); } array_pop($this->macroNodes); if (!$node->args) { $node->setArgs($args); } $isLeftmost = $node->content ? trim(substr($this->output, strrpos("\n{$this->output}", "\n"))) === '' : FALSE; $node->closing = TRUE; $node->macro->nodeClosed($node); $this->output =& $node->saved[0]; $this->writeCode($node->openingCode, $this->output, $node->saved[1]); $this->writeCode($node->closingCode, $node->content, $isRightmost, $isLeftmost); $this->output .= $node->content; } else { // opening $node = $this->expandMacro($name, $args, $modifiers, $htmlNode, $prefix); if ($node->isEmpty) { $this->writeCode($node->openingCode, $this->output, $isRightmost); } else { $this->macroNodes[] = $node; $node->saved = array(&$this->output, $isRightmost); $this->output =& $node->content; } } return $node; }