Merge "Introduce ResourceLoaderLessVarFileModule"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Thu, 26 Apr 2018 00:16:09 +0000 (00:16 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Thu, 26 Apr 2018 00:16:09 +0000 (00:16 +0000)
autoload.php
includes/resourceloader/ResourceLoaderLessVarFileModule.php [new file with mode: 0644]
tests/phpunit/includes/resourceloader/ResourceLoaderLessVarFileModuleTest.php [new file with mode: 0644]

index 881d0dd..12958ca 100644 (file)
@@ -1284,6 +1284,7 @@ $wgAutoloadLocalClasses = [
        'ResourceLoaderJqueryMsgModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderJqueryMsgModule.php',
        'ResourceLoaderLanguageDataModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderLanguageDataModule.php',
        'ResourceLoaderLanguageNamesModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderLanguageNamesModule.php',
+       'ResourceLoaderLessVarFileModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderLessVarFileModule.php',
        'ResourceLoaderMediaWikiUtilModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderMediaWikiUtilModule.php',
        'ResourceLoaderModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderModule.php',
        'ResourceLoaderOOUIFileModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderOOUIFileModule.php',
diff --git a/includes/resourceloader/ResourceLoaderLessVarFileModule.php b/includes/resourceloader/ResourceLoaderLessVarFileModule.php
new file mode 100644 (file)
index 0000000..17d00e0
--- /dev/null
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * Subclass with context specific LESS variables
+ */
+class ResourceLoaderLessVarFileModule extends ResourceLoaderFileModule {
+       protected $lessVariables = [
+               'collapsible-collapse',
+               'collapsible-expand',
+       ];
+
+       /**
+        * @inheritDoc
+        */
+       public function getMessages() {
+               // Overload so MessageBlobStore can detect updates to messages and purge as needed.
+               return array_merge( $this->messages, $this->lessVariables );
+       }
+
+       /**
+        * Exclude a set of messages from a JSON string representation
+        * @param string $blob
+        * @param array $exclusions
+        * @return array $blob
+        */
+       protected function excludeMessagesFromBlob( $blob, $exclusions ) {
+               $data = json_decode( $blob, true );
+               // unset the LESS variables so that they are not forwarded to JavaScript
+               foreach ( $exclusions as $key ) {
+                       unset( $data[$key] );
+               }
+               return $data;
+       }
+
+       /**
+        * @inheritDoc
+        */
+       protected function getMessageBlob( ResourceLoaderContext $context ) {
+               $blob = parent::getMessageBlob( $context );
+               return json_encode( $this->excludeMessagesFromBlob( $blob, $this->lessVariables ) );
+       }
+
+       /**
+        * Takes a message and wraps it in quotes for compatibility with LESS parser
+        * (ModifyVars) method so that the variable can be loaded and made available to stylesheets.
+        * Note this does not take care of CSS escaping. That will be taken care of as part
+        * of CSS Janus.
+        * @param string $msg
+        * @return string wrapped LESS variable definition
+        */
+       private static function wrapAndEscapeMessage( $msg ) {
+               return str_replace( "'", "\'", CSSMin::serializeStringValue( $msg ) );
+       }
+
+       /**
+        * @param \ResourceLoaderContext $context
+        * @return array LESS variables
+        */
+       protected function getLessVars( \ResourceLoaderContext $context ) {
+               $blob = parent::getMessageBlob( $context );
+               $lessMessages = $this->excludeMessagesFromBlob( $blob, $this->messages );
+
+               $vars = [];
+               foreach ( $lessMessages as $msgKey => $value ) {
+                       $vars['msg-' . $msgKey] = self::wrapAndEscapeMessage( $value );
+               }
+               return $vars;
+       }
+}
diff --git a/tests/phpunit/includes/resourceloader/ResourceLoaderLessVarFileModuleTest.php b/tests/phpunit/includes/resourceloader/ResourceLoaderLessVarFileModuleTest.php
new file mode 100644 (file)
index 0000000..a42e4be
--- /dev/null
@@ -0,0 +1,43 @@
+<?php
+
+/**
+ * @group ResourceLoader
+ */
+class ResourceLoaderLessVarFileModuleTest extends ResourceLoaderTestCase {
+
+       public static function providerWrapAndEscapeMessage() {
+               return [
+                       [
+                               "Foo", '"Foo"',
+                       ],
+                       [
+                               "Foo bananas", '"Foo bananas"',
+                       ],
+                       [
+                               "Who's that test? Who's that test? It's Jess!",
+                               '"Who\\\'s that test? Who\\\'s that test? It\\\'s Jess!"',
+                       ],
+                       [
+                               'Hello "he" said',
+                               '"Hello \"he\" said"',
+                       ],
+                       [
+                               'boo";-o-link:javascript:alert(1);color:red;content:"',
+                               '"boo\";-o-link:javascript:alert(1);color:red;content:\""',
+                       ],
+                       [
+                               '"jon\'s"',
+                               '"\"jon\\\'s\""'
+                       ]
+               ];
+       }
+       /**
+        * @dataProvider providerWrapAndEscapeMessage
+        * @covers ResourceLoaderLessVarFileModule::wrapAndEscapeMessage
+        */
+       public function testEscapeMessage( $msg, $expected ) {
+               $method = new ReflectionMethod( ResourceLoaderLessVarFileModule::class, 'wrapAndEscapeMessage' );
+               $method->setAccessible( true );
+               $this->assertEquals( $expected, $method->invoke( ResourceLoaderLessVarFileModule::class, $msg ) );
+       }
+}