jenkins-bot has submitted this change and it was merged.

Change subject: Migrated to single page config
......................................................................


Migrated to single page config

* All settings are loaded from the common JSON page
* Config data is validated and cached
* Removed lots of legacy code to load and parse config pages
* Added sample json configuration page

Change-Id: Ie61a3675ec6185cfecd5ff3468069977ebe95a33
---
A SampleConfigPage.json
M ZeroRatedMobileAccess.php
A includes/CarrierConfig.php
M includes/PageRenderingHooks.php
4 files changed, 505 insertions(+), 468 deletions(-)

Approvals:
  Dr0ptp4kt: Verified; Looks good to me, approved
  jenkins-bot: Verified



diff --git a/SampleConfigPage.json b/SampleConfigPage.json
new file mode 100644
index 0000000..c471129
--- /dev/null
+++ b/SampleConfigPage.json
@@ -0,0 +1,65 @@
+{
+  "enabled":true,
+  "partnerId":1019,
+  "messageId":"dialog-sri-lanka",
+  "showLangs":[
+    "en",
+    "kg"
+  ],
+  "langNameOverrides":{
+    "kg":"Kikongo"
+  },
+  "whitelistedLangs":[
+    "en",
+    "ru",
+    "fr"
+  ],
+  "foreground":"#735005",
+  "background":"#F4A83D",
+  "fontSize":"0.9em",
+  "bannerWarning":true,
+  "showImages":false,
+  "showZeroPage":true,
+  "bannerUrl":"http://www.dialog.lk/personal/broadband/hspa/";,
+  "ips":[
+    "192.168.0.0/16"
+  ],
+  "name":{
+    "en": "Dialog Sri Lanka",
+    "id": "Dialog Sri Lanka",
+    "ja": "ダイアログ スリランカ",
+    "mk": "Dialog Шри Ланка",
+    "si": "ඩයලොග්",
+    "ta": "Dialog Sri Lanka"
+  },
+  "banner":{
+    "ar": "{{SITENAME}} بلا مقابل من $1",
+    "ast": "{{SITENAME}} @ TARIFA CERO dende $1",
+    "cs": "{{SITENAME}} BEZPLATNĚ od $1",
+    "de": "{{SITENAME}} ohne Gebühren von $1",
+    "en": "{{SITENAME}} @ ZERO CHARGE from $1",
+    "es": "{{SITENAME}} gratis desde $1",
+    "fi": "{{SITENAME}} MAKSUTTA, tarjoaa $1",
+    "fr": "{{SITENAME}} @ ZÉRO CHARGE depuis $1",
+    "frp": "{{SITENAME}} @ ZÉRÔ CHARGE dês $1",
+    "gl": "{{SITENAME}} gratis desde $1",
+    "he": "{{SITENAME}} חינם־חינם מבית $1",
+    "hsb": "{{SITENAME}} bjez popłatkow wot $1",
+    "id": "{{SITENAME}} BEBAS PULSA dari $1",
+    "it": "{{SITENAME}} gratuito da $1",
+    "ja": "{{SITENAME}} @ ZERO CHARGE、$1 から",
+    "ko": "$1에서 {{SITENAME}} @ ZERO CHARGE",
+    "ksh": "{{SITENAME}} <span style='text-convsert:uppercase'>der ohne 
Koßte</span> vun $1",
+    "mk": "{{SITENAME}} без наплата од $1",
+    "ml": "{{SITENAME}} @ $1 നൽകുന്ന സീറോ ചാർജ്",
+    "ms": "{{SITENAME}} PERCUMA mulai $1",
+    "nl": "{{SITENAME}} ZONDER KOSTEN van $1",
+    "pms": "{{SITENAME}} @ TARIFA ZERO da $1",
+    "ro": "{{SITENAME}} @ FĂRĂ COSTURI de la $1",
+    "si": "$1 සමගින් නොමිලේ විකිපීඩියා බලන්න",
+    "ta": "$1 Wikipedia க்கு கட்டணங்கள் இல்லை",
+    "vi": "{{SITENAME}} MIỄN PHÍ từ $1",
+    "zh-hans": "由$1提供的免费{{SITENAME}}访问",
+    "zh-hant": "$1提供{{SITENAME}}免費訪問連線"
+  }
+}
diff --git a/ZeroRatedMobileAccess.php b/ZeroRatedMobileAccess.php
index 3b69088..857a96b 100644
--- a/ZeroRatedMobileAccess.php
+++ b/ZeroRatedMobileAccess.php
@@ -33,6 +33,7 @@
 $autoloadClasses = array (
        'PageRenderingHooks' => 'PageRenderingHooks',
        'ZeroSpecialPage' => 'ZeroSpecialPage',
+       'CarrierConfig' => 'CarrierConfig',
 );
 
 $ns = 'Extensions\ZeroRatedMobileAccess\\';
@@ -65,6 +66,7 @@
 
 $wgEnableZeroRatedMobileAccessTesting = false;
 $wgZeroRatedMobileAccessConfigIndexUri = false;
+$wgZeroRatedMobileAccessDisableCache = false;
 
 $wgHooks['ResourceLoaderTestModules'][] = $ns . 
'PageRenderingHooks::onResourceLoaderTestModules';
 $wgHooks['BeforePageDisplayMobile'][] = $ns . 
'PageRenderingHooks::onBeforePageDisplay';
diff --git a/includes/CarrierConfig.php b/includes/CarrierConfig.php
new file mode 100644
index 0000000..2aa44fc
--- /dev/null
+++ b/includes/CarrierConfig.php
@@ -0,0 +1,379 @@
+<?php
+
+namespace Extensions\ZeroRatedMobileAccess;
+use FormatJson;
+use Http;
+use ObjectCache;
+use stdClass;
+
+/**
+ * Represents a json blob on a remote wiki.
+ * Handles retrieval (via HTTP) and local caching.
+ * This code was adapted from extensions/EventLogging/includes/RemoteSchema.php
+ * @note When we switch to PHP 5.4, add 'implements JsonSerializable'
+ */
+class CarrierConfig {
+
+       const LOCK_TIMEOUT = 20;
+
+       var $carrierId;
+       var $cache;
+       var $http;
+       var $key;
+       var $content = false;
+
+
+       /**
+        * Constructor.
+        * @param string $carrierId Telecom ID (X-CS Header)
+        * @param ObjectCache $cache: (optional) cache client.
+        * @param Http $http: (optional) HTTP client.
+        */
+       function __construct( $carrierId, $cache = NULL, $http = NULL ) {
+               $this->carrierId = $carrierId;
+               $this->cache = $cache ?: wfGetCache( CACHE_ANYTHING );
+               $this->http = $http ?: new Http();
+               $this->key = 'ZeroRatedMobileAccess:' . $carrierId;
+       }
+
+
+       /**
+        * Retrieves content.
+        * @return array|bool: Content array or false if irretrievable.
+        */
+       public function get() {
+               global $wgZeroRatedMobileAccessDisableCache;
+
+               if ( $this->content ) {
+                       return $this->content;
+               }
+
+               if ( !$wgZeroRatedMobileAccessDisableCache ) {
+                       $this->content = $this->memcGet();
+                       if ( $this->content ) {
+                               return $this->content;
+                       }
+               }
+
+               $this->content = $this->httpGetAndValidate();
+
+               if ( !$wgZeroRatedMobileAccessDisableCache && $this->content ) {
+                       $this->memcSet();
+               }
+
+               return $this->content;
+       }
+
+
+       /**
+        * Retrieves content from memcached.
+        * @return array|bool Carrier config or false if not in cache.
+        */
+       private function memcGet() {
+               return $this->cache->get( $this->key );
+       }
+
+
+       /**
+        * Store content in memcached.
+        */
+       private function memcSet() {
+               return $this->cache->set( $this->key, $this->content );
+       }
+
+
+       /**
+        * Delete any cached information related to this config
+        */
+       public function resetCache() {
+               $this->cache->delete( $this->key );
+               $this->cache->delete( $this->key . ':lock' );
+       }
+
+
+       /**
+        * Acquire a mutex lock for HTTP retrieval.
+        * @return bool: Whether lock was successfully acquired.
+        */
+       private function lock() {
+               return $this->cache->add( $this->key . ':lock', 1, 
self::LOCK_TIMEOUT );
+       }
+
+
+       /**
+        * Constructs URI for retrieving configuration from remote wiki.
+        * @return string: URI.
+        */
+       private function getUri() {
+               global $wgZeroRatedMobileAccessConfigIndexUri;
+
+               if ( $wgZeroRatedMobileAccessConfigIndexUri === false ) {
+                       wfWarn( '$wgZeroRatedMobileAccessConfigIndexUri is not 
set' );
+                       return false;
+               }
+
+               $q = array(
+                       'title'  =>  'Zero:' . $this->carrierId,
+                       'action' =>  'raw',
+               );
+
+               return wfAppendQuery( $wgZeroRatedMobileAccessConfigIndexUri, 
$q );
+       }
+
+
+       /**
+        * Returns an object containing serializable properties.
+        * @implements JsonSerializable
+        */
+       public function jsonSerialize() {
+               return array(
+                       'Zero'  => $this->get() ?: new stdClass(),
+               );
+       }
+
+
+       /**
+        * Retrieves the config using HTTP.
+        * Uses a memcached lock to avoid cache stampedes.
+        * @return array|boolean: config or false if unable to fetch.
+        */
+       private function httpGetAndValidate() {
+               if ( !$this->lock() ) {
+                       return false;
+               }
+               $uri = $this->getUri();
+               if ( $uri === false ) {
+                       return false;
+               }
+               $raw = $this->http->get( $uri, self::LOCK_TIMEOUT * 0.8 );
+               list ( $content, $issues ) = $this->validateConfig( $raw );
+               if ( array_key_exists( 'error', $issues ) ) {
+                       return false;
+               }
+               return $content;
+       }
+
+
+       static function isArray( $arr, $isAssoc ) {
+               if ( !is_array( $arr ) ) {
+                       return false;
+               }
+               $strCount = count( array_filter( array_keys( $arr ), 
'is_string') );
+               return ( $isAssoc && $strCount === count( $arr ) ) ||
+                       ( !$isAssoc && $strCount === 0 );
+       }
+
+
+       static function isArrayOfStrings( $arr ) {
+               return count( $arr ) === count( array_filter( $arr, 'is_string' 
) );
+       }
+
+
+       static function isArrayOfLangs( $arr ) {
+               $filter = function( $v ) { return \Language::isValidCode( $v ); 
};
+               return count( $arr ) === count( array_filter( $arr, $filter ) );
+       }
+
+       public function validateConfig( $raw ) {
+
+               $config = array();
+               $issues = array();
+
+               $json = FormatJson::decode( $raw, true ) ?: false;
+               if ( !is_array( $json ) ) {
+                       $issues['error'] = 'Unable to parse JSON configuration. 
Please check the syntax.';
+                       return array( $config, $issues );
+               }
+
+               $validateBool = function( $v ) {
+                       return is_bool( $v ) ? null : 'Must be true or false 
(optional)';
+               };
+               $validateStr = function( $v ) {
+                       return is_string( $v ) ? null : 'Must be a string 
(optional)';
+               };
+
+
+               //'enabled' => true,          // Config is enabled
+               $this->check( $config, $json, 'enabled', true, $issues, 
$validateBool );
+
+               //'name' => null,             // Map of localized partner names
+               $this->check( $config, $json, 'name', null, $issues,
+                       function( $v ) {
+                               return CarrierConfig::isArray( $v, true )
+                                       && CarrierConfig::isArrayOfLangs( 
array_keys( $v ) )
+                                       && CarrierConfig::isArrayOfStrings( $v )
+                                       ? null : 'Must be a dictionary of valid 
language codes to strings';
+                       } );
+
+               // Integer Carrier ID
+               $this->check( $config, $json, 'partnerId', 0, $issues,
+                       function( $v ) {
+                               return is_int( $v ) ? null : 'Must be an 
integer (optional)';
+                       } );
+
+               // Integer Carrier ID
+               $this->check( $config, $json, 'messageId', null, $issues,
+                       function( $v ) {
+                               return is_string( $v )
+                                       && strlen( $v ) > 0
+                                       && strtolower( $v ) === $v
+                                       && strpos( $v, ' ' ) === false
+                                       ? null
+                                       : "Must be a nonempty lowercase string 
with no spaces";
+                       } );
+
+               //'showLangs' => null,        // List of language codes to show 
on Zero page
+               $this->check( $config, $json, 'showLangs', null, $issues,
+                       function( $v ) {
+                               return CarrierConfig::isArray( $v, false )
+                                       && CarrierConfig::isArrayOfLangs( $v )
+                                       && count( $v ) > 0
+                                       ? null : 'Must be a non-empty list of 
valid language codes';
+                       } );
+
+               // Orange Congo wanted to be able to override the 'kg' language 
name to 'Kikongo'
+               $this->check( $config, $json, 'langNameOverrides', array(), 
$issues,
+                       function( $v ) {
+                               return CarrierConfig::isArray( $v, true )
+                                        && CarrierConfig::isArrayOfLangs( 
array_keys( $v ) )
+                                        && CarrierConfig::isArrayOfStrings( $v 
)
+                                       ? null : 'Optional, must be a 
dictionary of valid language codes to strings';
+                       } );
+
+               //'whitelistedLangs' => null, // List of language codes to show 
banner on, or empty list to allow on all languages
+               $this->check( $config, $json, 'whitelistedLangs', null, $issues,
+                       function( $v ) {
+                               return CarrierConfig::isArray( $v, false )
+                                       && CarrierConfig::isArrayOfLangs( $v )
+                                       ? null : 'Must be a list of valid 
language codes (could be empty)';
+                       } );
+
+               //'banner' => null,           // Map of localized banner texts 
with {{PARTNER}} placeholder
+               $this->check( $config, $json, 'banner', null, $issues,
+                       function( $v ) {
+                               return CarrierConfig::isArray( $v, true )
+                                       && CarrierConfig::isArrayOfLangs( 
array_keys( $v ) )
+                                       && CarrierConfig::isArrayOfStrings( $v )
+                                       ? null : 'Must be a dictionary of valid 
language codes to strings';
+                       } );
+
+               //'bannerWarning' => true,    // Show "non-zero navigation" 
warning when clicking the banner
+               $this->check( $config, $json, 'bannerWarning', true, $issues, 
$validateBool );
+
+               // Background banner color
+               $this->check( $config, $json, 'background', '#E31230', $issues, 
$validateStr );
+
+               // Foreground banner color
+               $this->check( $config, $json, 'foreground', '#551011', $issues, 
$validateStr );
+
+               // Banner font size override
+               $this->check( $config, $json, 'fontSize', '', $issues, 
$validateStr );
+
+               //
+               $this->check( $config, $json, 'showImages', true, $issues, 
$validateBool );
+
+               // Partner URL, do not link by default
+               $this->check( $config, $json, 'bannerUrl', '', $issues,
+                       function( $v ) {
+                               return
+                                       $v === '' || false !== filter_var( $v, 
FILTER_VALIDATE_URL )
+                                       ? null : 'Optional, must be a valid 
URL';
+                       } );
+
+               //'sites' => 'both',          // Which sites are whitelisted? 
Could be zero, m, both, redirect
+               $this->check( $config, $json, 'sites', 'both', $issues,
+                       function( $v ) {
+                               $validValues = array( 'zero', 'm', 'both', 
'redirect' );
+                               return is_string( $v )
+                                       && in_array( strtolower( $v ), 
$validValues )
+                                       ? null
+                                       : "Optional, must be one of these 
values: '" . implode( "', '", $validValues ) . "'";
+                       } );
+               $config['sites'] = strtolower( $config['sites'] );
+
+               //'ips' => null,
+               $this->check( $config, $json, 'ips', '', $issues,
+                       function( $v ) {
+                               $msg = 'Must be an array of valid CIDR blocks';
+                               if ( !CarrierConfig::isArray( $v, false )
+                                       || !CarrierConfig::isArrayOfStrings( $v 
) ) {
+                                       return $msg;
+                               }
+                               foreach ( $v as $cidr ) {
+                                       $parts = explode( '/', $cidr, 3 );
+                                       if ( count( $parts ) > 2 ) {
+                                               return $msg;
+                                       }
+                                       // @TODO: Finish CIDR validation
+                               }
+                               return null;
+                       } );
+
+               // default = ( count( 'showLangs' ) > 1 )
+               $this->check( $config, $json, 'showZeroPage', true, $issues, 
$validateBool );
+               if ( isset( $issues['defaults']['showZeroPage'] ) ) {
+                       $config['showZeroPage'] = count( $config['showLangs'] ) 
> 1;
+               }
+
+               if ( array_key_exists( 'errors', $issues ) ) {
+                       $errCount = count( $issues['errors'] );
+                       $issues['error'] =
+                               $errCount .
+                               ( $errCount > 1 ? ' errors were' : ' error was' 
) .
+                               ' found in configuration data';
+               }
+
+               return array( $config, $issues );
+       }
+
+
+       private function check( &$config, $json, $field, $default, &$issues, 
$validator ) {
+               $res = $this->getValue( $json, $field, $issues );
+               $useDflt = $res === null;
+               if ( $useDflt ) {
+                       $res = $default;
+               }
+               $err = call_user_func( $validator, $res );
+               if ( $err !== null ) {
+                       // Invalid value
+                       $issues['valid'] = false;
+                       $issues['errors'][$field] = $err;
+               } else {
+                       $config[$field] = $res;
+                       if ( $useDflt ) {
+                               // default value was used
+                               $issues['defaults'][$field] = true;
+                       }
+               }
+       }
+
+
+       private function getValue( $json, $field, &$issues ) {
+               $value = null;
+               $dupl = array();
+
+               // check for exact field name match
+               if ( array_key_exists( $field, $json ) ) {
+                       $dupl[] = $field;
+                       $value = $json[$field];
+                       unset( $json[$field] );
+               }
+
+               // check for other casing of the field name
+               foreach ( $json as $k => $v ) {
+                       if ( 0 === strcasecmp( $k, $field ) ) {
+                               $dupl[] = $k;
+                               $value = $json[$k];
+                               unset( $json[$k] );
+                               $issues['normalized'][$k] = $field;
+                       }
+               }
+
+               if ( count( $dupl ) > 1 ) {
+                       $issues['duplicates'] = array_merge( 
$issues['duplicates'], $dupl );
+                       return null;
+               } else {
+                       return $value;
+               }
+       }
+}
diff --git a/includes/PageRenderingHooks.php b/includes/PageRenderingHooks.php
index 3bd5493..e64e6c7 100644
--- a/includes/PageRenderingHooks.php
+++ b/includes/PageRenderingHooks.php
@@ -4,9 +4,6 @@
 use DOMDocument;
 use DOMElement;
 use DOMXPath;
-use DateTimeZone;
-use DateTime;
-use Http;
 use Html;
 use FormatJson;
 use Language;
@@ -33,6 +30,7 @@
        private static $isFilePage = false;
        public static $isMainPage = false;
        private static $acceptBilling;
+       private static $carrierId;
        private static $carrier;
        private static $renderZeroRatedRedirect;
        private static $forceClickToViewImages;
@@ -56,8 +54,7 @@
         * @return bool
         */
        public static function getMobileUrl( &$subdomainTokenReplacement ) {
-               global $wgRequest, $wgZeroDisableImages;
-               $carrier = $wgRequest->getHeader( 'X-CARRIER' );
+               global $wgZeroDisableImages;
                $xSubDomain = isset( $_SERVER['HTTP_X_SUBDOMAIN'] ) ? 
$_SERVER['HTTP_X_SUBDOMAIN'] : '';
                if ( $xSubDomain === 'ZERO' ) {
                        if ( $wgZeroDisableImages === 1 ) {
@@ -106,15 +103,17 @@
                                self::$isMainPage = true;
                        }
 
-                       $carrier = $wgRequest->getHeader( 'X-CARRIER' );
-
-                       if ( $carrier !== '(null)' && $carrier ) {
+                       $carrierId = $wgRequest->getHeader( 'X-CS' );
+                       if ( $carrierId !== '(null)' && $carrierId ) {
                                self::$renderZeroRatedBanner = true;
+                               self::$carrierId = $carrierId;
+                       } else {
+                               self::$carrierId = '';
                        }
 
                        $output = '';
 
-                       if ( $xSubDomain === 'ZERO' && empty( $carrier ) ) {
+                       if ( $xSubDomain === 'ZERO' && self::$carrierId === '' 
) {
                                $ip = $wgRequest->getVal( 'ip', 
$wgRequest->getIP() );
                                // @todo FIXME: Unescaped UI text output as 
HTML in next 3 lines.
                                $bannerText = wfMessage( 
'zero-rated-mobile-access-sorry' )->text();
@@ -169,14 +168,14 @@
                        }
 
                        if ( self::$renderZeroRatedBanner === true ) {
-                               self::$carrier = $this->lookupCarrier( $carrier 
);
-                               if ( self::$isFilePage && isset( 
self::$carrier['images'] ) && self::$carrier['images'] ) {
+                               self::$carrier = $this->lookupCarrier( 
self::$carrierId );
+                               if ( self::$isFilePage && 
self::$carrier['showImages'] ) {
                                        self::$renderWarning = false;
                                }
 
                                $options = array();
                                $options['toggle_view_desktop'] = 
'&renderZeroRatedBanner=true&renderwarning=yes&returnto=';
-                               $options['supported_languages'] = isset( 
self::$carrier['languages'] ) ? self::$carrier['languages'] : array();
+                               $options['supported_languages'] = 
self::$carrier['showLangs'];
                        }
 
                        if ( self::$renderWarning && self::$acceptBilling !== 
'yes' && self::$renderZeroRatedBanner === true ) {
@@ -243,8 +242,10 @@
                                        $parsedHtml = 
$this->parseLinksForZeroQueryString( $html );
                                        $out->clearHTML();
                                        $out->addHTML( $parsedHtml );
-                                       $carrierLink = ( isset( 
self::$carrier['link'] ) ) ? self::$carrier['link'] : '';
-                                       if ( isset( 
self::$carrier['bannerWarning'] ) && !self::$carrier['bannerWarning'] && 
!empty( $carrierLink ) ) {
+                                       $carrierLink = self::getCarrierLink();
+
+
+                                       if ( !self::$carrier['bannerWarning'] 
&& !empty( $carrierLink ) ) {
                                                preg_match( '/<a 
href="(.+)">/', $carrierLink, $match );
                                                if ( isset( $match[1] ) ) {
                                                        $originalHref = 
$match[1];
@@ -258,17 +259,13 @@
                                                        }
                                                }
                                        }
-                                       $customStyle = '';
-                                       $customStyleNotifyClose = '';
-                                       if ( isset( 
self::$carrier['bannerPalette']['foreground'] ) && isset( 
self::$carrier['bannerPalette']['background'] ) ) {
-                                               $customStyle = 'background:' . 
self::$carrier['bannerPalette']['background'] .
-                                                       ';color:' . 
self::$carrier['bannerPalette']['foreground'] . ';';
-                                               $customStyleFontSize = ( 
$carrier === 'Saudi Telecom' ) ? ' font-size:0.9em;' : '';
-                                               $carrierLink = str_replace( '<a 
href="', '<a style="color:' . self::$carrier['bannerPalette']['foreground'] .
-                                                       ';" href="', 
$carrierLink );
-                                               $customStyleNotifyClose = 
'background-color:' . self::$carrier['bannerPalette']['background'] .
-                                                       ';border: 2px solid ' . 
self::$carrier['bannerPalette']['foreground'] . ';';
-                                       }
+                                       $customStyle = 'background:' . 
self::$carrier['background'] .
+                                               ';color:' . 
self::$carrier['foreground'] . ';';
+                                       $customStyleFontSize = 
self::$carrier['fontSize'] !== '' ? ' font-size:' . self::$carrier['fontSize'] 
. ';' : '';
+                                       $carrierLink = str_replace( '<a 
href="', '<a style="color:' . self::$carrier['foreground'] .
+                                               ';" href="', $carrierLink );
+                                       $customStyleNotifyClose = 
'background-color:' . self::$carrier['background'] .
+                                               ';border: 2px solid ' . 
self::$carrier['foreground'] . ';';
                                        $bannerText = Html::rawElement( 'span',
                                                array(
                                                        'class' => 
'mw-mf-message',
@@ -291,18 +288,20 @@
                                $out->clearHTML();
                                $out->setPageTitle( null );
                                $languageNames = Language::fetchLanguageNames();
-                               $languageNames['kg'] = 'Kikongo'; // 
special-case as requested by Orange Congo
 
-                               $languageOptionsForCarriers = 
self::createLanguageOptionsFromWikiTextForCarrier();
-                               $carrierName = strtoupper( 
self::$carrier['name'] );
-                               $languageOptionsForCarrier = ( isset( 
$languageOptionsForCarriers[$carrierName] ) ) ?
-                                       
$languageOptionsForCarriers[$carrierName] : null;
+                               if ( self::$carrier ) {
+                                       $overrides = 
self::$carrier['langNameOverrides'];
+                                       // Allow language name overrides, as 
requested by Orange Congo ('kg' => 'Kikongo)
+                                       // Do it one at a time to keep the 
original order of the array
+                                       foreach ( $overrides as $l => $n ) {
+                                               $languageNames[$l] = $n;
+                                       }
 
-                               if ( is_array( $languageOptionsForCarrier ) ) {
-                                       $sizeOfLanguagesForCountry = sizeof( 
$languageOptionsForCarrier );
-                                       for ( $i = 0; $i < 
$sizeOfLanguagesForCountry; $i++ ) {
-                                               $languageName = 
$languageNames[$languageOptionsForCarrier[$i]['language']];
-                                               $languageCode = 
$languageOptionsForCarrier[$i]['language'];
+                                       foreach ( self::$carrier['showLangs'] 
as $languageCode ) {
+                                               if ( !array_key_exists( 
$languageCode, $languageNames ) ) {
+                                                       continue;
+                                               }
+                                               $languageName = 
$languageNames[$languageCode];
                                                $languageUrl = sprintf( 
self::$formatMobileUrl, $languageCode );
                                                $languageLink = Html::element( 
'a',
                                                        array(  'id' => 'lang_' 
. $languageCode,
@@ -310,7 +309,7 @@
                                                        wfMessage( 
'zero-rated-mobile-access-home-page-selection',
                                                                ucfirst( 
$languageName ) )->inLanguage( $languageCode )
                                                );
-                                       if ( $wgZeroDisableImages === 1 ) {
+                                               if ( $wgZeroDisableImages === 1 
) {
                                                        $languageLink = 
str_replace( '.m.wikipedia.org', '.zero.wikipedia.org', $languageLink );
                                                } else {
                                                        $languageLink = 
str_replace( '.zero.wikipedia.org', '.m.wikipedia.org', $languageLink );
@@ -354,6 +353,25 @@
                return true;
        }
 
+       private static function getCarrierLink() {
+               global $wgRequest;
+               $url = self::$carrier['bannerUrl'];
+               $messageId = self::$carrier['messageId'];
+
+               $name = ucwords(
+                       wfMessage( 
'zero-rated-mobile-access-banner-carrier-name-' . $messageId )->escaped()
+               );
+               $linkText = wfMessage( 'zero-rated-mobile-access-banner-text-' 
. $messageId )
+                       ->rawParams( $name )->escaped();
+               $billingURL = $wgRequest->appendQuery(
+                       
'renderZeroRatedBanner=true&renderwarning=yes&returnto=' . urlencode( $url )
+               );
+               $carrierLink = Html::rawElement( 'a',
+                       array( 'href' => $billingURL ),
+                       $linkText );
+               return $carrierLink;
+       }
+
        /**
         * ResourceLoaderTestModules hook handler
         * @see 
https://www.mediawiki.org/wiki/Manual:Hooks/ResourceLoaderTestModules
@@ -377,52 +395,21 @@
                return true;
        }
 
-       /**
-        * @return Array
-        */
-       private function mergeCarrierData() {
-               $allCarrierLinkData = $this->createCarrierOptionsFromWikiText();
-               $allCarrierSupportedLanguageData = 
$this->getLanguageOptionForWikiFromWikiText();
-
-               if ( is_array( $allCarrierLinkData ) && is_array( 
$allCarrierSupportedLanguageData ) ) {
-                       foreach ( $allCarrierLinkData as $key => $value ) {
-                               if ( is_array( $value ) && array_key_exists( 
'partnerId', $value ) ) {
-                                       foreach ( $value as $subKey => 
$subValue ) {
-                                               if ( $subKey !== 'partnerId' ) {
-                                                       continue;
-                                               }
-                                               if ( isset( 
$allCarrierSupportedLanguageData[$subValue][0] ) &&
-                                                       is_array( 
$allCarrierSupportedLanguageData[$subValue][0] ) ) {
-                                                       
$allCarrierLinkData[$key]['languages'] = 
$allCarrierSupportedLanguageData[$subValue][0];
-                                               } else {
-                                                       
$allCarrierLinkData[$key]['languages'] = 'all';
-                                               }
-                                       }
-                               }
-                       }
-               }
-               return $allCarrierLinkData;
-       }
 
        /**
        * Returns information about carrier
        *
-       * @param String $carrier: Name of carrier e.g., "Verizon Wireless"
+       * @param String $carrier: carrier ID e.g., "250-99"
        * @return Array
        */
        private function lookupCarrier( $carrier ) {
                wfProfileIn( __METHOD__ );
-               $carrierLinkData = array();
-               $carrier = strtoupper( $carrier );
 
-               $allCarrierLinkData = $this->mergeCarrierData();
-
-               if ( is_array( $allCarrierLinkData ) && isset( 
$allCarrierLinkData[$carrier] ) ) {
-                       $carrierLinkData = $allCarrierLinkData[$carrier];
-               }
+               $conf = new CarrierConfig( $carrier );
+               $data = $conf->get();
 
                wfProfileOut( __METHOD__ );
-               return $carrierLinkData;
+               return $data;
        }
 
        /**
@@ -478,7 +465,7 @@
                                $zeroPartnerUrl = wfAppendQuery( 
$zeroRatedLinkHref,
                                        array(  'zeropartner' => $partnerId, 
'renderZeroRatedBanner' => 'true' ) );
                                if ( $zeroPartnerUrl ) {
-                                       if ( isset( self::$carrier['images'] ) 
&& !self::$carrier['images'] ) {
+                                       if ( !self::$carrier['showImages'] ) {
                                                $zeroRatedLink->setAttribute( 
'href', $zeroPartnerUrl );
                                        }
                                }
@@ -503,402 +490,6 @@
                $output = $doc->saveXML( null, LIBXML_NOEMPTYTAG );
                wfProfileOut( __METHOD__ );
                return $output;
-       }
-
-       /**
-        * @param $formatter array
-        * @param $wikiText string
-        * @param $nChild bool
-        * @return array
-        */
-       public function parseWikiTextToArray( Array $formatter, $wikiText, 
$nChild = false ) {
-               $options = array();
-               if ( !is_array( $formatter ) ) {
-                       return $options;
-               }
-               wfProfileIn( __METHOD__ );
-               $data = explode( PHP_EOL, $wikiText );
-               if ( $nChild ) {
-                       $arrayKeys = array_keys( $formatter );
-                       $keyCount = count( $arrayKeys );
-                       $index = 0;
-                       foreach ( $data as $key => $rawData ) {
-                               $index = ( intval( $key ) % $keyCount === 0 ) ? 
0 : $index + 1;
-                               if ( strpos( $rawData, '*' ) === 0 && strpos( 
$rawData, '**' ) !== 0 && $key >= 0 ) {
-                                       $data = trim( str_replace( '*', '', 
$rawData ) );
-                                       $prefixName = strtoupper( $data );
-                                       $options[$prefixName] = '';
-                               } elseif ( strpos( $rawData, '**' ) === 0 && 
$key > 0 ) {
-                                       $data = trim( str_replace( '*', '', 
$rawData ) );
-                                       if ( !is_array( $formatter ) ) {
-                                               $options[$prefixName][] = $data;
-                                               continue;
-                                       }
-                                       if ( !isset( 
$formatter[$index]['callback'] ) ) {
-                                               continue;
-                                       }
-                                       $callback = 
$formatter[$index]['callback'];
-                                       if ( method_exists( $this, $callback ) 
) {
-                                               $data = $this->$callback( $data 
);
-                                               if ( $data ) {
-                                                       $options[$prefixName][] 
= $data;
-                                               }
-                                       }
-                               }
-                       }
-                       wfProfileOut( __METHOD__ );
-                       return $options;
-               }
-
-               $arrayKeys = array_keys( $formatter );
-               $keyCount = count( $arrayKeys );
-               $index = 0;
-               foreach ( $data as $key => $rawData ) {
-                       $index = ( intval( $key ) % $keyCount === 0 ) ? 0 : 
$index + 1;
-                       if ( !in_array( $index, $arrayKeys ) ) {
-                               continue;
-                       }
-                       $data = trim( str_replace( '*', '', $rawData ) );
-                       if ( is_array( $formatter[$index] ) ) {
-                               $name = $formatter[$index]['name'];
-                               if ( !isset( $formatter[$index]['callback'] ) ) 
{
-                                       continue;
-                               }
-                               $callback = $formatter[$index]['callback'];
-                               if ( !method_exists( $this, $callback ) ) {
-                                       continue;
-                               }
-                               if ( isset( $formatter[$index]['parameters'] ) 
) {
-                                       if ( is_array( 
$formatter[$index]['parameters'] ) ) {
-                                               $parameters = array();
-                                               foreach ( 
$formatter[$index]['parameters'] as $parameter ) {
-                                                       if ( isset( 
$options[$prefixName][$parameter] ) ) {
-                                                               
$parameters[$parameter] = $options[$prefixName][$parameter];
-                                                       }
-                                               }
-                                               $data = $this->$callback( 
$data, $parameters );
-                                       } else {
-                                               $parameter = 
$formatter[$index]['parameters'];
-                                               if ( isset( 
$options[$prefixName][$parameter] ) ) {
-                                                       $parameterValue = 
$options[$prefixName][$parameter];
-                                                       $data = 
$this->$callback( $data, $parameterValue );
-                                               }
-                                       }
-                               } else {
-                                       $data = $this->$callback( $data );
-                               }
-                       } else {
-                               $name = $formatter[$index];
-                       }
-                       if ( $index === 0 ) {
-                               $prefixName = strtoupper( $data );
-                       }
-                       $options[$prefixName][$name] = $data;
-               }
-               wfProfileOut( __METHOD__ );
-               return $options;
-       }
-
-       /**
-        * @param $data array
-        * @return array
-        */
-       public function commaSeparatedCallback( $data ) {
-               return explode( ',', str_replace( ' ', '', $data ) );
-       }
-
-       /**
-        * @param $url string
-        * @param $name string
-        * @return string
-        */
-       public function createUrlCallback( $url, $name ) {
-               global $wgRequest;
-               $carrier = strtolower( $name );
-               $posSpace = strpos( $carrier, ' ' );
-               if ( $posSpace === false ) {
-               } else {
-                       $carrier = str_replace( ' ', '-', $carrier );
-               }
-
-               $name = ucwords(
-                       wfMessage( 
'zero-rated-mobile-access-banner-carrier-name-' . $carrier )->escaped()
-               );
-               $linkText = wfMessage( 'zero-rated-mobile-access-banner-text-' 
. $carrier )
-                       ->rawParams( $name )->escaped();
-               $billingURL = $wgRequest->appendQuery(
-                       
'renderZeroRatedBanner=true&renderwarning=yes&returnto=' . urlencode( $url )
-               );
-               $carrierLink = Html::rawElement( 'a',
-                       array( 'href' => $billingURL ),
-                               $linkText );
-               return $carrierLink;
-       }
-
-       /**
-        * @param $int string|int
-        * @return int
-        */
-       public function intValCallback( $int ) {
-               return intval( $int );
-       }
-
-       /**
-        * @param $images string
-        * @return boolean
-        */
-       public function booleanImagesOnOff( $images ) {
-               if ( $images === 'IMAGES_ON' ) {
-                       return true;
-               }
-               return false;
-       }
-
-       /**
-        * @param $colors string
-        * @return array
-        */
-       public function arrayBannerPalette( $colors ) {
-               $tempColorArray = explode( ':' , $colors );
-               if ( isset( $tempColorArray[0] ) && isset( $tempColorArray[1] ) 
) {
-                       $colorArray = array(
-                               'foreground' => $tempColorArray[0],
-                               'background' => $tempColorArray[1],
-                       );
-               } else {
-                       $colorArray = array();
-               }
-
-               return $colorArray;
-       }
-
-       /**
-        * @param $banner
-        * @return boolean
-        */
-       public function booleanBannerWarning( $banner ) {
-               if ( $banner === 'BANNER_WARNING_ON' ) {
-                       return true;
-               }
-               return false;
-       }
-
-       /**
-       * Returns the carrier options array parsed from a valid wiki page
-       *
-       * @return Array
-       */
-       private function createCarrierOptionsFromWikiText() {
-               wfProfileIn( __METHOD__ );
-               $carrierOptionsWikiPage = wfMessage( 
'zero-rated-mobile-access-carrier-options-wiki-page' )
-                       ->inContentLanguage()->text();
-               list( $revId, $rev ) = self::getOptionsFromForeignWiki( 
$carrierOptionsWikiPage );
-               $carrierOptions = null;
-
-               if ( !$carrierOptions ) {
-                       if ( $rev ) {
-                               $formatter = array(
-                                       0 => 'name',
-                                       1 => array( 'name' => 'link',
-                                                                'callback' => 
'createUrlCallback',
-                                                                'parameters' 
=> 'name',
-                                               ),
-                                       2 => array( 'name' => 'partnerId',
-                                                                'callback' => 
'intValCallback',
-                                               ),
-                                       3 => array( 'name' => 'images',
-                                                               'callback' => 
'booleanImagesOnOff',
-                                               ),
-                                       4 => array( 'name' => 'bannerPalette',
-                                                               'callback' => 
'arrayBannerPalette',
-                                               ),
-                                       5 => array( 'name' => 'bannerWarning',
-                                                               'callback' => 
'booleanBannerWarning',
-                                               ),
-
-                               );
-                                $carrierOptions = $this->parseWikiTextToArray( 
$formatter, $rev );
-                       }
-               }
-               wfProfileOut( __METHOD__ );
-               return $carrierOptions;
-       }
-
-       /**
-        * Returns the foreign wiki options array from a valid wiki page
-        *
-        * @param $pageName string
-        * @return Array
-        */
-       private static function getOptionsFromForeignWiki( $pageName ) {
-               global $wgMemc;
-               wfProfileIn( __METHOD__ );
-
-               $key = null;
-               $rev = null;
-
-               if ( $pageName ) {
-                       $day = gmdate( 'Ymd' );
-                       $memcKey = wfMemcKey( 
'zero-rated-mobile-access-foreign-options-', md5( $pageName ), $day );
-                       $foreignOptions = $wgMemc->get( $memcKey );
-
-                       if ( !$foreignOptions ) {
-                               $url = 
'http://en.wikipedia.org/w/api.php?action=query&prop=revisions&&rvlimit=1&rvprop=content&format=json&titles=MediaWiki:'
 . $pageName;
-                               $ret = Http::get( $url );
-
-                               if ( !$ret ) {
-                                       wfProfileOut( __METHOD__ );
-                                       return array( $key, $rev );
-                               }
-
-                               $jsonData = FormatJson::decode( $ret, true );
-
-                               if ( isset( $jsonData['query']['pages'] ) ) {
-                                       $key = key( $jsonData['query']['pages'] 
);
-                                       if ( !is_int( $key ) ) {
-                                               $key = null;
-                                       }
-
-                                       foreach ( $jsonData['query']['pages'] 
as $pages ) {
-                                               if ( isset( 
$pages['revisions'][0]['*'] ) ) {
-                                                       $rev = 
$pages['revisions'][0]['*'];
-                                               }
-                                       }
-                               }
-
-                               if ( $key && $rev ) {
-                                       $wgMemc->set( $memcKey, array( $key, 
$rev ), self::getMaxAge() );
-                               }
-                       } else {
-                               list ( $key, $rev ) = $foreignOptions;
-                       }
-               }
-
-               wfProfileOut( __METHOD__ );
-               return array( $key, $rev );
-       }
-
-       /**
-        * @param $data array
-        * @return array|string
-        */
-       public function languagePercentageCallback( $data ) {
-               $languageArray = array();
-               $lineParts = explode( '#', $data );
-               $language = ( isset( $lineParts[0] ) ) ? trim( $lineParts[0] ) 
: trim( $data );
-               if ( $language !== 'portal' && $language !== 'other' ) {
-                       $languageArray = ( isset( $lineParts[1] ) ) ?
-                               array(  'language'  =>  $language,
-                                               'percentage'  =>  intval( 
str_replace( '%', '', trim( $lineParts[1] ) ) ) ) :
-                               $language;
-               }
-               return $languageArray;
-       }
-
-/**
-       * Returns the language options array parsed from a valid wiki page
-       *
-       * @return Array
-       */
-       private function createLanguageOptionsFromWikiTextForCarrier() {
-               global $wgMemc;
-               wfProfileIn( __METHOD__ );
-               $languageOptionsWikiPage = wfMessage( 
'zero-rated-mobile-access-language-options-wiki-page-for-carrier' 
)->inContentLanguage()->text();
-               list( $revId, $rev ) = self::getOptionsFromForeignWiki( 
$languageOptionsWikiPage );
-               if ( $rev ) {
-                       $key = wfMemcKey( 
'zero-rated-mobile-access-language-options-for-carrier', $revId );
-                       $languageOptions = $wgMemc->get( $key );
-               } else {
-                       $languageOptions = null;
-               }
-
-               if ( !$languageOptions ) {
-                       $languageOptions = array();
-                       if ( $rev ) {
-                               $formatter = array(
-                                       0 => array( 'name' => 'partnerId',
-                                                                'callback' => 
'languagePercentageCallback'
-                                       ),
-                               );
-                               $languageOptions = $this->parseWikiTextToArray( 
$formatter, $rev, true );
-                       }
-                       $wgMemc->set( $key, $languageOptions, self::getMaxAge() 
);
-               }
-               wfProfileOut( __METHOD__ );
-               return $languageOptions;
-       }
-
-       /**
-        * @return array|mixed|null
-        */
-       private function getLanguageOptionForWikiFromWikiText() {
-               global $wgMemc;
-               wfProfileIn( __METHOD__ );
-               $languageOptionsWikiPage = wfMessage( 
'zero-rated-mobile-access-carrier-options-supported-wikis-wiki-page' )
-                       ->inContentLanguage()->text();
-
-               list( $revId, $rev ) = self::getOptionsFromForeignWiki( 
$languageOptionsWikiPage );
-
-               if ( $rev ) {
-                       $key = wfMemcKey( 
'zero-rated-mobile-access-carrier-options-supported-wikis-wiki-page', $revId );
-                       $languageOptions = $wgMemc->get( $key );
-               } else {
-                       $languageOptions = null;
-               }
-
-               if ( !$languageOptions ) {
-                       $languageOptions = array();
-                       if ( $rev ) {
-                               $formatter = array(
-                                       0 => array( 'name' => 'partnerId',
-                                                               'callback' => 
'intValCallback'
-                                               ),
-                                       1 => array( 'name' => 'languages',
-                                                               'callback' => 
'commaSeparatedCallback'
-                                               ),
-                               );
-                               $languageOptions = $this->parseWikiTextToArray( 
$formatter, $rev, true );
-                       }
-                       $wgMemc->set( $key, $languageOptions, self::getMaxAge() 
);
-               }
-               wfProfileOut( __METHOD__ );
-               return $languageOptions;
-       }
-
-       /**
-        * Returns the Unix timestamp of current day's first second
-        *
-        * @return int: Timestamp
-        */
-       private static function todaysStart() {
-               wfProfileIn( __METHOD__ );
-               static $time = false;
-               if ( !$time ) {
-                       global $wgLocaltimezone;
-                       if ( isset( $wgLocaltimezone ) ) {
-                               $tz = new DateTimeZone( $wgLocaltimezone );
-                       } else {
-                               $tz = new DateTimeZone( 
date_default_timezone_get() );
-                       }
-                       $dt = new DateTime( 'now', $tz );
-                       $dt->setTime( 0, 0, 0 );
-                       $time = $dt->getTimestamp();
-               }
-               wfProfileOut( __METHOD__ );
-               return $time;
-       }
-
-       /**
-       * Returns the number of seconds an item should stay in cache
-       *
-       * @return int: Time in seconds
-       */
-       private static function getMaxAge() {
-               wfProfileIn( __METHOD__ );
-               // add 10 seconds to cater for the time deviation between 
servers
-               $expiry = self::todaysStart() + 24 * 3600 - wfTimestamp() + 10;
-               wfProfileOut( __METHOD__ );
-               return min( $expiry, 900 );
        }
 
        public function getVersion() {

-- 
To view, visit https://gerrit.wikimedia.org/r/58252
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: Ie61a3675ec6185cfecd5ff3468069977ebe95a33
Gerrit-PatchSet: 3
Gerrit-Project: mediawiki/extensions/ZeroRatedMobileAccess
Gerrit-Branch: master
Gerrit-Owner: Yurik <yu...@wikimedia.org>
Gerrit-Reviewer: Brion VIBBER <br...@wikimedia.org>
Gerrit-Reviewer: Dfoy <d...@wikimedia.org>
Gerrit-Reviewer: Dr0ptp4kt <ab...@wikimedia.org>
Gerrit-Reviewer: Jdlrobson <jrob...@wikimedia.org>
Gerrit-Reviewer: jenkins-bot

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to