From fd7dc3f34a9605552866688ff9d3f8f75de70c3c Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Thu, 15 Jan 2015 20:05:40 -0800 Subject: [PATCH] CRM-15832 - CRM_Core_Resources - Move translateScript to class. Add HTML support. For the moment, it looks like the existing JS string-extractor actually works for Angular-style HTML partials. This is not entirely a coincidence (since our Angular convention was purposefully derived from our JS convention), but luck/circumstance is a big factor, and this may not work in the long-run. This commit makes it easy to use different string-extractors for different file-types. --- CRM/Core/Resources.php | 42 ++---- CRM/Core/Resources/Strings.php | 110 +++++++++++++++ .../CRM/Core/Resources/StringsTest.php | 63 +++++++++ tests/phpunit/CRM/Utils/HTMLTest.php | 125 ++++++++++++++++++ 4 files changed, 309 insertions(+), 31 deletions(-) create mode 100644 CRM/Core/Resources/Strings.php create mode 100644 tests/phpunit/CRM/Core/Resources/StringsTest.php create mode 100644 tests/phpunit/CRM/Utils/HTMLTest.php diff --git a/CRM/Core/Resources.php b/CRM/Core/Resources.php index dd0f937618..7707c12652 100644 --- a/CRM/Core/Resources.php +++ b/CRM/Core/Resources.php @@ -58,9 +58,9 @@ class CRM_Core_Resources { private $extMapper = NULL; /** - * @var CRM_Utils_Cache_Interface + * @var CRM_Core_Resources_Strings */ - private $cache = NULL; + private $strings = NULL; /** * @var array free-form data tree @@ -135,7 +135,7 @@ class CRM_Core_Resources { */ public function __construct($extMapper, $cache, $cacheCodeKey = NULL) { $this->extMapper = $extMapper; - $this->cache = $cache; + $this->strings = new CRM_Core_Resources_Strings($cache); $this->cacheCodeKey = $cacheCodeKey; if ($cacheCodeKey !== NULL) { $this->cacheCode = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, $cacheCodeKey); @@ -165,7 +165,9 @@ class CRM_Core_Resources { */ public function addScriptFile($ext, $file, $weight = self::DEFAULT_WEIGHT, $region = self::DEFAULT_REGION, $translate = TRUE) { if ($translate) { - $this->translateScript($ext, $file); + // For each extension, maintain one cache record which + // includes parsed (translatable) strings for all its files. + $this->addString($this->strings->get($ext, $this->getPath($ext, $file), 'text/javascript')); } // Look for non-minified version if we are in debug mode if (CRM_Core_Config::singleton()->debug && strpos($file, '.min.js') !== FALSE) { @@ -579,37 +581,15 @@ class CRM_Core_Resources { * @return CRM_Core_Resources */ public function flushStrings() { - $this->cache->flush(); + $this->strings->flush(); return $this; } /** - * Translate strings in a javascript file - * - * @param string $ext - * extension name. - * @param string $file - * file path. - * @return void - */ - private function translateScript($ext, $file) { - // For each extension, maintain one cache record which - // includes parsed (translatable) strings for all its JS files. - $stringsByFile = $this->cache->get($ext); // array($file => array(...strings...)) - if (!$stringsByFile) { - $stringsByFile = array(); - } - if (!isset($stringsByFile[$file])) { - $filePath = $this->getPath($ext, $file); - if ($filePath && is_readable($filePath)) { - $stringsByFile[$file] = CRM_Utils_JS::parseStrings(file_get_contents($filePath)); - } - else { - $stringsByFile[$file] = array(); - } - $this->cache->set($ext, $stringsByFile); - } - $this->addString($stringsByFile[$file]); + * @return CRM_Core_Resources_Strings + */ + public function getStrings() { + return $this->strings; } /** diff --git a/CRM/Core/Resources/Strings.php b/CRM/Core/Resources/Strings.php new file mode 100644 index 0000000000..2ac048ec0d --- /dev/null +++ b/CRM/Core/Resources/Strings.php @@ -0,0 +1,110 @@ +cache = $cache; + } + + /** + * Flush the cache of translated strings. + */ + public function flush() { + $this->cache->flush(); + } + + /** + * Get the strings from a file, using a cache if available. + * + * @param string $bucket + * The name of a cache-row which includes strings for this file. + * @param string $file + * File path. + * @param string $format + * Type of file (e.g. 'text/javascript', 'text/html'). + * @return array + * List of translatable strings. + */ + public function get($bucket, $file, $format) { + $stringsByFile = $this->cache->get($bucket); // array($file => array(...strings...)) + if (!$stringsByFile) { + $stringsByFile = array(); + } + if (!isset($stringsByFile[$file])) { + if ($file && is_readable($file)) { + $stringsByFile[$file] = $this->extract($file, $format); + } + else { + $stringsByFile[$file] = array(); + } + $this->cache->set($bucket, $stringsByFile); + } + return $stringsByFile[$file]; + } + + /** + * Extract a list of strings from a file. + * + * @param string $file + * File path. + * @param string $format + * Type of file (e.g. 'text/javascript', 'text/html'). + * @return array + * List of translatable strings. + * @throws Exception + */ + public function extract($file, $format) { + switch ($format) { + case 'text/javascript': + return CRM_Utils_JS::parseStrings(file_get_contents($file)); + + case 'text/html': + // Magic! The JS parser works with HTML! See CRM_Utils_HTMLTest. + return CRM_Utils_JS::parseStrings(file_get_contents($file)); + + default: + throw new Exception("Cannot extract strings: Unrecognized file type."); + } + } +} diff --git a/tests/phpunit/CRM/Core/Resources/StringsTest.php b/tests/phpunit/CRM/Core/Resources/StringsTest.php new file mode 100644 index 0000000000..730c2c992b --- /dev/null +++ b/tests/phpunit/CRM/Core/Resources/StringsTest.php @@ -0,0 +1,63 @@ +createExamples(); + $strings = new CRM_Core_Resources_Strings( + new CRM_Utils_Cache_Arraycache(NULL) + ); + $this->assertEquals( + array('Hello from Javascript'), + $strings->get('example', "$basedir/hello.js", "text/javascript") + ); + $this->assertEquals( + array('Hello from HTML'), + $strings->get('example', "$basedir/hello.html", "text/html") + ); + } + + /** + * @return string + * Path to the example dir. + */ + public function createExamples() { + $basedir = rtrim($this->createTempDir('ext-'), '/'); + file_put_contents("$basedir/hello.js", "alert(ts('Hello from Javascript'));"); + file_put_contents("$basedir/hello.html", "
{{ts('Hello from HTML')}}
"); + return $basedir; + } +} diff --git a/tests/phpunit/CRM/Utils/HTMLTest.php b/tests/phpunit/CRM/Utils/HTMLTest.php new file mode 100644 index 0000000000..7e24fa9046 --- /dev/null +++ b/tests/phpunit/CRM/Utils/HTMLTest.php @@ -0,0 +1,125 @@ +Hello world', + array(), + ); + $cases[] = array(// text, no arg + '
{{ts("Hello world")}}
', + array('Hello world'), + ); + $cases[] = array(// text, no arg, alternate text + '
{{ts("Good morning, Dave")}}
', + array('Good morning, Dave'), + ); + $cases[] = array(// text, with arg + '
{{ts("Hello world", {1: "whiz"})}}
', + array('Hello world'), + ); + $cases[] = array(// text, not really ts(), no arg + '
{{clients("Hello world")}}
', + array(), + ); + $cases[] = array(// text, not really ts(), with arg + '
{{clients("Hello world", {1: "whiz"})}}
', + array(), + ); + $cases[] = array(// two strings, duplicate + '
{{ts("Hello world")}}

{{ts("Hello world")}}

', + array('Hello world'), + ); + $cases[] = array(// two strings, addition + '
{{ts("Hello world") + "-" + ts("How do you do?")}}

', + array('Hello world', 'How do you do?'), + ); + $cases[] = array(// two strings, separate calls + '
{{ts("Hello world")}}

{{ts("How do you do?")}}

', + array('Hello world', 'How do you do?'), + ); + $cases[] = array(// single quoted + '
{{ts(\'Hello world\')}}
', + array('Hello world'), + ); + $cases[] = array(// unclear string + '
{{ts(message)}}
', + array(), + ); + $cases[] = array(// ts() within a string + '
{{ts("Does the ts(\'example\') notation work?")}}
', + array('Does the ts(\'example\') notation work?'), + ); + $cases[] = array(// attribute, no arg + '
', + array('Hello world'), + ); + $cases[] = array(// attribute, with arg + '
', + array('Hello world'), + ); + $cases[] = array(// attribute, two strings, with arg + '
', + array('Hello world', 'How do you do, %1?'), + ); + $cases[] = array(// trick question! Not used on Smarty templates. + '
{ts}Hello world{/ts}
', + array(), + ); + + return $cases; + } + + /** + * @param string $html + * Example HTML input. + * @param array $expectedStrings + * List of expected strings. + * @dataProvider translateExamples + */ + public function testParseStrings($html, $expectedStrings) { + // Magic! The JS parser works with HTML! + $actualStrings = CRM_Utils_JS::parseStrings($html); + sort($expectedStrings); + sort($actualStrings); + $this->assertEquals($expectedStrings, $actualStrings); + } +} -- 2.25.1