r109930 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r109929‎ | r109930 | r109931 >
Date:14:47, 24 January 2012
Author:reedy
Status:ok
Tags:
Comment:
Modified paths:
  • /branches/wmf/1.18wmf1/extensions/MobileFrontend (modified) (history)
  • /branches/wmf/1.18wmf1/extensions/MobileFrontend/MobileFrontend.body.php (added) (history)
  • /branches/wmf/1.18wmf1/extensions/MobileFrontend/MobileFrontend.php (modified) (history)

Diff [purge]

Index: branches/wmf/1.18wmf1/extensions/MobileFrontend/MobileFrontend.body.php
@@ -0,0 +1,1665 @@
 2+<?php
 3+
 4+class ExtMobileFrontend {
 5+ const VERSION = '0.6.1';
 6+
 7+ /**
 8+ * @var DOMDocument
 9+ */
 10+ private $doc, $mainPage;
 11+ public $contentFormat = '';
 12+ public $WMLSectionSeperator = '***************************************************************************';
 13+
 14+ /**
 15+ * @var Title
 16+ */
 17+ public static $title;
 18+ public static $messages = array();
 19+ public static $htmlTitle;
 20+ public static $dir;
 21+ public static $code;
 22+ public static $device;
 23+ public static $headings;
 24+ public static $mainPageUrl;
 25+ public static $randomPageUrl;
 26+ public static $requestedSegment;
 27+ public static $format;
 28+ public static $search;
 29+ public static $callback;
 30+ public static $useFormat;
 31+ public static $disableImages;
 32+ public static $enableImages;
 33+ public static $isMainPage = false;
 34+ public static $searchField;
 35+ public static $disableImagesURL;
 36+ public static $enableImagesURL;
 37+ public static $disableMobileSiteURL;
 38+ public static $viewNormalSiteURL;
 39+ public static $currentURL;
 40+ public static $displayNoticeId;
 41+ public static $leaveFeedbackURL;
 42+ public static $mobileRedirectFormAction;
 43+ public static $isBetaGroupMember = false;
 44+ public static $hideSearchBox = false;
 45+ public static $hideLogo = false;
 46+ public static $hideFooter = false;
 47+ public static $languageUrls;
 48+ public static $wsLoginToken = '';
 49+ public static $wsLoginFormAction = '';
 50+ public static $isFilePage;
 51+ public static $logoutHtml;
 52+ public static $loginHtml;
 53+ public static $zeroRatedBanner;
 54+
 55+ public static $messageKeys = array(
 56+ 'mobile-frontend-show-button',
 57+ 'mobile-frontend-hide-button',
 58+ 'mobile-frontend-back-to-top-of-section',
 59+ 'mobile-frontend-regular-site',
 60+ 'mobile-frontend-perm-stop-redirect',
 61+ 'mobile-frontend-home-button',
 62+ 'mobile-frontend-random-button',
 63+ 'mobile-frontend-are-you-sure',
 64+ 'mobile-frontend-explain-disable',
 65+ 'mobile-frontend-disable-button',
 66+ 'mobile-frontend-back-button',
 67+ 'mobile-frontend-opt-in-message',
 68+ 'mobile-frontend-opt-in-yes-button',
 69+ 'mobile-frontend-opt-in-no-button',
 70+ 'mobile-frontend-opt-in-title',
 71+ 'mobile-frontend-opt-out-message',
 72+ 'mobile-frontend-opt-out-yes-button',
 73+ 'mobile-frontend-opt-out-no-button',
 74+ 'mobile-frontend-opt-out-title',
 75+ 'mobile-frontend-opt-in-explain',
 76+ 'mobile-frontend-opt-out-explain',
 77+ 'mobile-frontend-disable-images',
 78+ 'mobile-frontend-wml-continue',
 79+ 'mobile-frontend-wml-back',
 80+ 'mobile-frontend-enable-images',
 81+ 'mobile-frontend-featured-article',
 82+ 'mobile-frontend-news-items',
 83+ 'mobile-frontend-leave-feedback-title',
 84+ 'mobile-frontend-leave-feedback-notice',
 85+ 'mobile-frontend-leave-feedback-subject',
 86+ 'mobile-frontend-leave-feedback-message',
 87+ 'mobile-frontend-leave-feedback-cancel',
 88+ 'mobile-frontend-leave-feedback-submit',
 89+ 'mobile-frontend-leave-feedback-link-text',
 90+ 'mobile-frontend-leave-feedback',
 91+ 'mobile-frontend-feedback-page',
 92+ 'mobile-frontend-leave-feedback-thanks',
 93+ 'mobile-frontend-search-submit',
 94+ 'mobile-frontend-language',
 95+ 'mobile-frontend-username',
 96+ 'mobile-frontend-password',
 97+ 'mobile-frontend-login',
 98+ 'mobile-frontend-placeholder',
 99+ 'mobile-frontend-dismiss-notification',
 100+ 'mobile-frontend-sopa-notice',
 101+ );
 102+
 103+ public $itemsToRemove = array(
 104+ '#contentSub',
 105+ 'div.messagebox',
 106+ '#siteNotice',
 107+ '#siteSub',
 108+ '#jump-to-nav',
 109+ 'div.editsection',
 110+ 'div.infobox',
 111+ 'table.toc',
 112+ '#catlinks',
 113+ 'div.stub',
 114+ 'form',
 115+ 'div.sister-project',
 116+ 'script',
 117+ 'div.magnify',
 118+ '.editsection',
 119+ 'span.t',
 120+ 'sup[style*="help"]',
 121+ '.portal',
 122+ '#protected-icon',
 123+ '.printfooter',
 124+ '.boilerplate',
 125+ '#id-articulo-destacado',
 126+ '#coordinates',
 127+ '#top',
 128+ '.hiddenStructure',
 129+ '.noprint',
 130+ '.medialist',
 131+ '.mw-search-createlink',
 132+ '#ogg_player_1',
 133+ '.nomobile',
 134+ );
 135+
 136+ /**
 137+ * Work out the site and language name from a database name
 138+ * @param $site string
 139+ * @param $lang string
 140+ * @return string
 141+ */
 142+ public function getSite( &$site, &$lang ) {
 143+ global $wgConf;
 144+ wfProfileIn( __METHOD__ );
 145+ $DB = wfGetDB( DB_MASTER );
 146+ $DBName = $DB->getDBname();
 147+ list( $site, $lang ) = $wgConf->siteFromDB( $DBName );
 148+ wfProfileOut( __METHOD__ );
 149+ return true;
 150+ }
 151+
 152+ /**
 153+ * @param $request WebRequest
 154+ * @param $title Title
 155+ * @param $output OutputPage
 156+ * @return bool
 157+ * @throws HttpError
 158+ */
 159+ public function testCanonicalRedirect( $request, $title, $output ) {
 160+ global $wgUsePathInfo, $wgMobileDomain;
 161+ $xDevice = isset( $_SERVER['HTTP_X_DEVICE'] ) ? $_SERVER['HTTP_X_DEVICE'] : '';
 162+ if ( empty( $xDevice ) ) {
 163+ return true; // Let the redirect happen
 164+ } else {
 165+ if ( $title->getNamespace() == NS_SPECIAL ) {
 166+ list( $name, $subpage ) = SpecialPage::resolveAlias( $title->getDBkey() );
 167+ if ( $name ) {
 168+ $title = SpecialPage::getTitleFor( $name, $subpage );
 169+ }
 170+ }
 171+ $targetUrl = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
 172+ // Redirect to canonical url, make it a 301 to allow caching
 173+ if ( $targetUrl == $request->getFullRequestURL() ) {
 174+ $message = "Redirect loop detected!\n\n" .
 175+ "This means the wiki got confused about what page was " .
 176+ "requested; this sometimes happens when moving a wiki " .
 177+ "to a new server or changing the server configuration.\n\n";
 178+
 179+ if ( $wgUsePathInfo ) {
 180+ $message .= "The wiki is trying to interpret the page " .
 181+ "title from the URL path portion (PATH_INFO), which " .
 182+ "sometimes fails depending on the web server. Try " .
 183+ "setting \"\$wgUsePathInfo = false;\" in your " .
 184+ "LocalSettings.php, or check that \$wgArticlePath " .
 185+ "is correct.";
 186+ } else {
 187+ $message .= "Your web server was detected as possibly not " .
 188+ "supporting URL path components (PATH_INFO) correctly; " .
 189+ "check your LocalSettings.php for a customized " .
 190+ "\$wgArticlePath setting and/or toggle \$wgUsePathInfo " .
 191+ "to true.";
 192+ }
 193+ throw new HttpError( 500, $message );
 194+ } else {
 195+ $parsedUrl = wfParseUrl( $targetUrl );
 196+ if ( stristr( $parsedUrl['host'], $wgMobileDomain ) === false ) {
 197+ $hostParts = explode( '.', $parsedUrl['host'] );
 198+ $parsedUrl['host'] = $hostParts[0] . $wgMobileDomain . $hostParts[1] . '.' . $hostParts[2];
 199+ }
 200+ $fragmentDelimiter = ( !empty( $parsedUrl['fragment'] ) ) ? '#' : '';
 201+ $queryDelimiter = ( !empty( $parsedUrl['query'] ) ) ? '?' : '';
 202+ $targetUrl = $parsedUrl['scheme'] . '://' . $parsedUrl['host'] . $parsedUrl['path']
 203+ . $queryDelimiter . $parsedUrl['query'] . $fragmentDelimiter . $parsedUrl['fragment'];
 204+ $output->setSquidMaxage( 1200 );
 205+ $output->redirect( $targetUrl, '301' );
 206+ }
 207+ return false; // Prevent the redirect from occuring
 208+ }
 209+ }
 210+
 211+ /**
 212+ * @param $obj Article
 213+ * @param $tpl
 214+ * @return bool
 215+ */
 216+ public function addMobileFooter( &$obj, &$tpl ) {
 217+ global $wgRequest;
 218+ wfProfileIn( __METHOD__ );
 219+
 220+ $title = $obj->getTitle();
 221+ $isSpecial = $title->isSpecialPage();
 222+
 223+ if ( ! $isSpecial ) {
 224+ $footerlinks = $tpl->data['footerlinks'];
 225+ $mobileViewUrl = $wgRequest->escapeAppendQuery( 'useformat=mobile' );
 226+
 227+ $tpl->set( 'mobileview', "<a href='{$mobileViewUrl}' class='noprint'>" . wfMsg( 'mobile-frontend-view' ) . "</a>" );
 228+ $footerlinks['places'][] = 'mobileview';
 229+ $tpl->set( 'footerlinks', $footerlinks );
 230+ }
 231+ wfProfileOut( __METHOD__ );
 232+ return true;
 233+ }
 234+
 235+ /**
 236+ * @param $url string
 237+ * @param $field string
 238+ * @return string
 239+ */
 240+ private function removeQueryStringParameter( $url, $field ) {
 241+ wfProfileIn( __METHOD__ );
 242+ $url = preg_replace( '/(.*)(\?|&)' . $field . '=[^&]+?(&)(.*)/i', '$1$2$4', $url . '&' );
 243+ $url = substr( $url, 0, -1 );
 244+ wfProfileOut( __METHOD__ );
 245+ return $url;
 246+ }
 247+
 248+ public function getMsg() {
 249+ global $wgUser, $wgContLang, $wgRequest, $wgServer, $wgMobileRedirectFormAction, $wgMobileDomain, $wgOut, $wgLanguageCode;
 250+ wfProfileIn( __METHOD__ );
 251+
 252+ self::$disableImagesURL = $wgRequest->escapeAppendQuery( 'disableImages=1' );
 253+ self::$enableImagesURL = $wgRequest->escapeAppendQuery( 'enableImages=1' );
 254+ self::$disableMobileSiteURL = $wgRequest->escapeAppendQuery( 'mobileaction=disable_mobile_site' );
 255+ self::$viewNormalSiteURL = $wgRequest->escapeAppendQuery( 'mobileaction=view_normal_site' );
 256+ self::$currentURL = $wgRequest->getFullRequestURL();
 257+ self::$leaveFeedbackURL = $wgRequest->escapeAppendQuery( 'mobileaction=leave_feedback' );
 258+
 259+ $skin = $wgUser->getSkin();
 260+ $copyright = $skin->getCopyright();
 261+ if ( stristr( $copyright, '<li class="noprint">' ) !== false ) {
 262+ $copyright = '<ul id="footer-info"><li>' . $copyright . '</li></ul>';
 263+ }
 264+
 265+ // Need to stash the results of the "wfMsg" call before the Output Buffering handler
 266+ // because at this point the database connection is shut down, etc.
 267+
 268+ self::$messages['mobile-frontend-copyright'] = $copyright;
 269+
 270+ foreach ( self::$messageKeys as $messageKey ) {
 271+
 272+ if ( $messageKey == 'mobile-frontend-leave-feedback-notice' ) {
 273+ $linkText = wfMsg( 'mobile-frontend-leave-feedback-link-text' );
 274+ $linkTarget = wfMsgNoTrans( 'mobile-frontend-feedback-page' );
 275+ self::$messages[$messageKey] = wfMsgExt( $messageKey, array( 'replaceafter' ), Html::element( 'a', array( 'href' => Title::newFromText( $linkTarget )->getFullURL(), 'target' => '_blank' ), $linkText ) );
 276+ } elseif ( $messageKey == 'mobile-frontend-feedback-page' ) {
 277+ self::$messages[$messageKey] = wfMsgNoTrans( $messageKey );
 278+ } else {
 279+ self::$messages[$messageKey] = wfMsg( $messageKey );
 280+ }
 281+ }
 282+
 283+ self::$dir = $wgContLang->getDir();
 284+ self::$code = $wgContLang->getCode();
 285+
 286+ $languageUrls = array();
 287+
 288+ $languageUrls[] = array(
 289+ 'href' => self::$currentURL,
 290+ 'text' => self::$htmlTitle,
 291+ 'language' => $wgContLang->getLanguageName( $wgLanguageCode ),
 292+ 'class' => 'interwiki-' . $wgLanguageCode,
 293+ 'lang' => $wgLanguageCode,
 294+ );
 295+
 296+ foreach ( $wgOut->getLanguageLinks() as $l ) {
 297+ $tmp = explode( ':', $l, 2 );
 298+ $class = 'interwiki-' . $tmp[0];
 299+ $lang = $tmp[0];
 300+ unset( $tmp );
 301+ $nt = Title::newFromText( $l );
 302+ if ( $nt ) {
 303+ $parsedUrl = wfParseUrl( $nt->getFullURL() );
 304+ if ( stristr( $parsedUrl['host'], $wgMobileDomain ) === false ) {
 305+ $hostParts = explode( '.', $parsedUrl['host'] );
 306+ $parsedUrl['host'] = $hostParts[0] . $wgMobileDomain . $hostParts[1] . '.' . $hostParts[2];
 307+ }
 308+ $fragmentDelimiter = ( isset( $parsedUrl['fragment'] ) && $parsedUrl['fragment'] !== null ) ? '#' : '';
 309+ $queryDelimiter = ( isset( $parsedUrl['query'] ) && $parsedUrl['query'] !== null ) ? '?' : '';
 310+
 311+ $languageUrl = $parsedUrl['scheme'] . $parsedUrl['delimiter'] . $parsedUrl['host'] . $parsedUrl['path'];
 312+ if ( isset( $parsedUrl['query'] ) ) {
 313+ $languageUrl .= $queryDelimiter . $parsedUrl['query'];
 314+ }
 315+ if ( isset( $parsedUrl['fragment'] ) ) {
 316+ $languageUrl .= $fragmentDelimiter . $parsedUrl['fragment'];
 317+ }
 318+ $languageUrls[] = array(
 319+ 'href' => $languageUrl,
 320+ 'text' => ( $wgContLang->getLanguageName( $nt->getInterwiki() ) != ''
 321+ ? $wgContLang->getLanguageName( $nt->getInterwiki() )
 322+ : $l ),
 323+ 'language' => $wgContLang->getLanguageName( $lang ),
 324+ 'class' => $class,
 325+ 'lang' => $lang,
 326+ );
 327+ }
 328+ }
 329+
 330+ self::$languageUrls = $languageUrls;
 331+
 332+ $nonMobileServerBaseURL = str_replace( $wgMobileDomain, '.', $wgServer );
 333+ self::$mobileRedirectFormAction = ( $wgMobileRedirectFormAction !== false )
 334+ ? $wgMobileRedirectFormAction
 335+ : "{$nonMobileServerBaseURL}/w/mobileRedirect.php";
 336+
 337+ self::$mainPageUrl = Title::newMainPage()->getLocalUrl();
 338+ self::$randomPageUrl = $this->getRelativeURL( SpecialPage::getTitleFor( 'Randompage' )->getLocalUrl() );
 339+ wfProfileOut( __METHOD__ );
 340+ return true;
 341+ }
 342+
 343+ /**
 344+ * @param $parsedUrl wfParseUrl Array
 345+ * @return string
 346+ */
 347+ public function parsePageRedirect( $parsedUrl ) {
 348+ global $wgMobileDomain;
 349+ wfProfileIn( __METHOD__ );
 350+ $redirect = '';
 351+ $hostParts = explode( '.', $parsedUrl['host'] );
 352+ $parsedUrl['host'] = $hostParts[0] . $wgMobileDomain . $hostParts[1] . '.' . $hostParts[2];
 353+ $fragmentDelimiter = ( !empty( $parsedUrl['fragment'] ) ) ? '#' : '';
 354+ $queryDelimiter = ( !empty( $parsedUrl['query'] ) ) ? '?' : '';
 355+ $redirect = $parsedUrl['scheme'] . '://' . $parsedUrl['host'] . $parsedUrl['path'];
 356+ if ( isset( $parsedUrl['query'] ) ) {
 357+ $redirect .= $queryDelimiter . $parsedUrl['query'];
 358+ }
 359+ if ( isset( $parsedUrl['fragment'] ) ) {
 360+ $redirect .= $fragmentDelimiter . $parsedUrl['fragment'];
 361+ }
 362+ wfProfileOut( __METHOD__ );
 363+ return $redirect;
 364+ }
 365+
 366+ /**
 367+ * @param $out OutputPage
 368+ * @param $redirect
 369+ * @param $code
 370+ * @return bool
 371+ */
 372+ public function beforePageRedirect( $out, &$redirect, &$code ) {
 373+ global $wgMobileDomain;
 374+ wfProfileIn( __METHOD__ );
 375+ if ( $out->getTitle()->isSpecial( 'Userlogin' ) ) {
 376+ $xDevice = isset( $_SERVER['HTTP_X_DEVICE'] ) ? $_SERVER['HTTP_X_DEVICE'] : '';
 377+ if ( $xDevice ) {
 378+ $parsedUrl = wfParseUrl( $redirect );
 379+ if ( stristr( $parsedUrl['host'], $wgMobileDomain ) === false ) {
 380+ $hostParts = explode( '.', $parsedUrl['host'] );
 381+ $parsedUrl['host'] = $hostParts[0] . $wgMobileDomain . $hostParts[1] . '.' . $hostParts[2];
 382+ }
 383+ if ( $parsedUrl['scheme'] == 'http' ) {
 384+ $parsedUrl['scheme'] = 'https';
 385+ }
 386+
 387+ $redirect = $this->parsePageRedirect( $parsedUrl );
 388+ }
 389+ } else if ( $out->getTitle()->isSpecial( 'Randompage' ) ) {
 390+ $xDevice = isset( $_SERVER['HTTP_X_DEVICE'] ) ? $_SERVER['HTTP_X_DEVICE'] : '';
 391+ if ( $xDevice ) {
 392+ $parsedUrl = wfParseUrl( $redirect );
 393+ if ( stristr( $parsedUrl['host'], $wgMobileDomain ) === false ) {
 394+ $redirect = $this->parsePageRedirect( $parsedUrl );
 395+ }
 396+ }
 397+ }
 398+ wfProfileOut( __METHOD__ );
 399+ return true;
 400+ }
 401+
 402+ /**
 403+ * @param $out OutputPage
 404+ * @param $text String
 405+ * @return bool
 406+ */
 407+ public function beforePageDisplayHTML( &$out, &$text ) {
 408+ global $wgContLang, $wgRequest, $wgMemc, $wgUser;
 409+ wfProfileIn( __METHOD__ );
 410+
 411+ // Note: The WebRequest Class calls are made in this block because
 412+ // since PHP 5.1.x, all objects have their destructors called
 413+ // before the output buffer callback function executes.
 414+ // Thus, globalized objects will not be available as expected in the function.
 415+ // This is stated to be intended behavior, as per the following: [http://bugs.php.net/bug.php?id=40104]
 416+
 417+ $xDevice = isset( $_SERVER['HTTP_X_DEVICE'] ) ? $_SERVER['HTTP_X_DEVICE'] : '';
 418+ self::$useFormat = $wgRequest->getText( 'useformat' );
 419+ $mobileAction = $wgRequest->getText( 'mobileaction' );
 420+ $action = $wgRequest->getText( 'action' );
 421+
 422+ if ( self::$useFormat !== 'mobile' && self::$useFormat !== 'mobile-wap' &&
 423+ !$xDevice ) {
 424+ wfProfileOut( __METHOD__ );
 425+ return true;
 426+ }
 427+ if ( $action === 'edit' ||
 428+ $mobileAction === 'view_normal_site' ) {
 429+ wfProfileOut( __METHOD__ );
 430+ return true;
 431+ }
 432+
 433+ $userAgent = $_SERVER['HTTP_USER_AGENT'];
 434+ $acceptHeader = isset( $_SERVER["HTTP_ACCEPT"] ) ? $_SERVER["HTTP_ACCEPT"] : '';
 435+ $uAmd5 = md5( $userAgent );
 436+
 437+ $key = wfMemcKey( 'mobile', 'ua', $uAmd5 );
 438+
 439+ $props = null;
 440+ try {
 441+ $props = $wgMemc->get( $key );
 442+ if ( !$props ) {
 443+ $wurflConfigFile = RESOURCES_DIR . 'wurfl-config.xml';
 444+ $wurflConfig = new WURFL_Configuration_XmlConfig( $wurflConfigFile );
 445+ $wurflManagerFactory = new WURFL_WURFLManagerFactory( $wurflConfig );
 446+ $wurflManager = $wurflManagerFactory->create();
 447+ $device = $wurflManager->getDeviceForHttpRequest( $_SERVER );
 448+
 449+ if ( $device->isSpecific() === true ) {
 450+ $props = $device->getAllCapabilities();
 451+ $wgMemc->set( $key, $props, 86400 );
 452+ } else {
 453+ $wgMemc->set( $key, 'generic', 86400 );
 454+ $props = 'generic';
 455+ }
 456+ }
 457+ } catch ( Exception $e ) {
 458+ // echo $e->getMessage();
 459+ }
 460+
 461+ self::$title = $out->getTitle();
 462+
 463+ if ( self::$title->isMainPage() ) {
 464+ self::$isMainPage = true;
 465+ }
 466+ if ( self::$title->getNamespace() == NS_FILE ) {
 467+ self::$isFilePage = true;
 468+ }
 469+
 470+ self::$htmlTitle = $out->getHTMLTitle();
 471+ self::$disableImages = $wgRequest->getText( 'disableImages', 0 );
 472+ self::$enableImages = $wgRequest->getText( 'enableImages', 0 );
 473+ self::$displayNoticeId = $wgRequest->getText( 'noticeid', '' );
 474+
 475+ if ( self::$disableImages == 1 ) {
 476+ $wgRequest->response()->setcookie( 'disableImages', 1 );
 477+ $location = str_replace( '?disableImages=1', '', str_replace( '&disableImages=1', '', $wgRequest->getFullRequestURL() ) );
 478+ $location = str_replace( '&mfi=1', '', str_replace( '&mfi=0', '', $location ) );
 479+ $location = $this->getRelativeURL( $location );
 480+ $wgRequest->response()->header( 'Location: ' . $location . '&mfi=0' );
 481+ } elseif ( self::$disableImages == 0 ) {
 482+ $disableImages = $wgRequest->getCookie( 'disableImages' );
 483+ if ( $disableImages ) {
 484+ self::$disableImages = $disableImages;
 485+ }
 486+ }
 487+
 488+ if ( self::$enableImages == 1 ) {
 489+ $disableImages = $wgRequest->getCookie( 'disableImages' );
 490+ if ( $disableImages ) {
 491+ $wgRequest->response()->setcookie( 'disableImages', '' );
 492+ }
 493+ $location = str_replace( '?enableImages=1', '', str_replace( '&enableImages=1', '', $wgRequest->getFullRequestURL() ) );
 494+ $location = str_replace( '&mfi=1', '', str_replace( '&mfi=0', '', $location ) );
 495+ $location = $this->getRelativeURL( $location );
 496+ $wgRequest->response()->header( 'Location: ' . $location . '&mfi=1' );
 497+ }
 498+
 499+ self::$format = $wgRequest->getText( 'format' );
 500+ self::$callback = $wgRequest->getText( 'callback' );
 501+ self::$requestedSegment = $wgRequest->getText( 'seg', 0 );
 502+ self::$search = $wgRequest->getText( 'search' );
 503+ self::$searchField = $wgRequest->getText( 'search', '' );
 504+
 505+ $device = new DeviceDetection();
 506+
 507+ if ( $xDevice ) {
 508+ $formatName = $xDevice;
 509+ } else {
 510+ $formatName = $device->formatName( $userAgent, $acceptHeader );
 511+ }
 512+
 513+ self::$device = $device->format( $formatName );
 514+
 515+ if ( self::$device['view_format'] === 'wml' ) {
 516+ $this->contentFormat = 'WML';
 517+ } elseif ( self::$device['view_format'] === 'html' ) {
 518+ $this->contentFormat = 'XHTML';
 519+ }
 520+
 521+ if ( self::$useFormat === 'mobile-wap' ) {
 522+ $this->contentFormat = 'WML';
 523+ }
 524+
 525+ if ( $mobileAction == 'leave_feedback' ) {
 526+ echo $this->renderLeaveFeedbackXHTML();
 527+ wfProfileOut( __METHOD__ );
 528+ exit();
 529+ }
 530+
 531+ if ( $mobileAction == 'leave_feedback_post' ) {
 532+
 533+ $this->getMsg();
 534+
 535+ $subject = $wgRequest->getText( 'subject', '' );
 536+ $message = $wgRequest->getText( 'message', '' );
 537+ $token = $wgRequest->getText( 'edittoken', '' );
 538+
 539+ $title = Title::newFromText( self::$messages['mobile-frontend-feedback-page'] );
 540+
 541+ if ( $title->userCan( 'edit' ) &&
 542+ !$wgUser->isBlockedFrom( $title ) &&
 543+ $wgUser->matchEditToken( $token ) ) {
 544+ $article = new Article( $title, 0 );
 545+ $rawtext = $article->getRawText();
 546+ $rawtext .= "\n== {$subject} == \n {$message} ~~~~ \n <small>User agent: {$userAgent}</small> ";
 547+ $article->doEdit( $rawtext, '' );
 548+ }
 549+
 550+ $location = str_replace( '&mobileaction=leave_feedback_post', '', $wgRequest->getFullRequestURL() . '&noticeid=1&useformat=mobile' );
 551+ $location = $this->getRelativeURL( $location );
 552+ $wgRequest->response()->header( 'Location: ' . $location );
 553+ wfProfileOut( __METHOD__ );
 554+ exit();
 555+ }
 556+
 557+ if ( $mobileAction == 'disable_mobile_site' && $this->contentFormat == 'XHTML' ) {
 558+ echo $this->renderDisableMobileSiteXHTML();
 559+ wfProfileOut( __METHOD__ );
 560+ exit();
 561+ }
 562+
 563+ if ( $mobileAction == 'opt_in_mobile_site' && $this->contentFormat == 'XHTML' ) {
 564+ echo $this->renderOptInMobileSiteXHTML();
 565+ wfProfileOut( __METHOD__ );
 566+ exit();
 567+ }
 568+
 569+ if ( $mobileAction == 'opt_out_mobile_site' && $this->contentFormat == 'XHTML' ) {
 570+ echo $this->renderOptOutMobileSiteXHTML();
 571+ wfProfileOut( __METHOD__ );
 572+ exit();
 573+ }
 574+
 575+ if ( $mobileAction == 'opt_in_cookie' ) {
 576+ wfIncrStats( 'mobile.opt_in_cookie_set' );
 577+ $this->setOptInOutCookie( '1' );
 578+ $this->disableCaching();
 579+ $location = wfExpandUrl( Title::newMainPage()->getFullURL(), PROTO_CURRENT );
 580+ $wgRequest->response()->header( 'Location: ' . $location );
 581+ }
 582+
 583+ if ( $mobileAction == 'opt_out_cookie' ) {
 584+ $this->setOptInOutCookie( '' );
 585+ }
 586+
 587+ $this->getMsg();
 588+ $this->disableCaching();
 589+ $this->sendXDeviceVaryHeader();
 590+ $this->sendApplicationVersionVaryHeader();
 591+ $this->checkUserStatus();
 592+ $this->checkUserLoggedIn();
 593+
 594+ if ( self::$title->isSpecial( 'Userlogin' ) && self::$isBetaGroupMember ) {
 595+ self::$wsLoginToken = $wgRequest->getSessionData( 'wsLoginToken' );
 596+ $q = array( 'action' => 'submitlogin', 'type' => 'login' );
 597+ $returnToVal = $wgRequest->getVal( 'returnto' );
 598+
 599+ if ( $returnToVal ) {
 600+ $q['returnto'] = $returnToVal;
 601+ }
 602+
 603+ self::$wsLoginFormAction = self::$title->getLocalURL( $q );
 604+ }
 605+
 606+ $this->setDefaultLogo();
 607+ ob_start( array( $this, 'DOMParse' ) );
 608+
 609+ wfProfileOut( __METHOD__ );
 610+ return true;
 611+ }
 612+
 613+ /**
 614+ * @return bool
 615+ */
 616+ private function checkUserLoggedIn() {
 617+ global $wgUser, $wgCookieDomain, $wgRequest, $wgCookiePrefix;
 618+ wfProfileIn( __METHOD__ );
 619+ $tempWgCookieDomain = $wgCookieDomain;
 620+ $wgCookieDomain = $this->getBaseDomain();
 621+ $tempWgCookiePrefix = $wgCookiePrefix;
 622+ $wgCookiePrefix = '';
 623+
 624+ if ( $wgUser->isLoggedIn() ) {
 625+ $wgRequest->response()->setcookie( 'mfsecure', '1', 0, '' );
 626+ } else {
 627+ $mfSecure = $wgRequest->getCookie( 'mfsecure', '' );
 628+ if ( $mfSecure && $mfSecure == '1' ) {
 629+ $wgRequest->response()->setcookie( 'mfsecure', '', 0, '' );
 630+ }
 631+ }
 632+
 633+ $wgCookieDomain = $tempWgCookieDomain;
 634+ $wgCookiePrefix = $tempWgCookiePrefix;
 635+ wfProfileOut( __METHOD__ );
 636+ return true;
 637+ }
 638+
 639+ private function checkUserStatus() {
 640+ global $wgRequest;
 641+ wfProfileIn( __METHOD__ );
 642+
 643+ $hideSearchBox = $wgRequest->getInt( 'hidesearchbox' );
 644+
 645+ if ( $hideSearchBox === 1 ) {
 646+ self::$hideSearchBox = true;
 647+ }
 648+
 649+ $hideLogo = $wgRequest->getInt( 'hidelogo' );
 650+
 651+ if ( $hideLogo === 1 ) {
 652+ self::$hideLogo = true;
 653+ }
 654+
 655+ if ( !empty( $_SERVER['HTTP_APPLICATION_VERSION'] ) &&
 656+ strpos( $_SERVER['HTTP_APPLICATION_VERSION'], 'Wikipedia Mobile' ) !== false ) {
 657+ self::$hideSearchBox = true;
 658+ if ( strpos( $_SERVER['HTTP_APPLICATION_VERSION'], 'Android' ) !== false ) {
 659+ self::$hideLogo = true;
 660+ }
 661+ }
 662+
 663+ if ( self::$hideLogo == true ) {
 664+ self::$hideFooter = true;
 665+ }
 666+
 667+ $optInCookie = $this->getOptInOutCookie();
 668+ if ( !empty( $optInCookie ) &&
 669+ $optInCookie == 1 ) {
 670+ self::$isBetaGroupMember = true;
 671+ }
 672+ wfProfileOut( __METHOD__ );
 673+ return true;
 674+ }
 675+
 676+ /**
 677+ * @param $value string
 678+ */
 679+ private function setOptInOutCookie( $value ) {
 680+ global $wgCookieDomain, $wgRequest, $wgCookiePrefix;
 681+ wfProfileIn( __METHOD__ );
 682+ $tempWgCookieDomain = $wgCookieDomain;
 683+ $wgCookieDomain = $this->getBaseDomain();
 684+ $tempWgCookiePrefix = $wgCookiePrefix;
 685+ $wgCookiePrefix = '';
 686+ $wgRequest->response()->setcookie( 'optin', $value, 0, '' );
 687+ $wgCookieDomain = $tempWgCookieDomain;
 688+ $wgCookiePrefix = $tempWgCookiePrefix;
 689+ wfProfileOut( __METHOD__ );
 690+ return true;
 691+ }
 692+
 693+ /**
 694+ * @return Mixed
 695+ */
 696+ private function getOptInOutCookie() {
 697+ global $wgRequest;
 698+ wfProfileIn( __METHOD__ );
 699+ $optInCookie = $wgRequest->getCookie( 'optin', '' );
 700+ wfProfileOut( __METHOD__ );
 701+ return $optInCookie;
 702+ }
 703+
 704+ /**
 705+ * @return string
 706+ */
 707+ private function getBaseDomain() {
 708+ wfProfileIn( __METHOD__ );
 709+ // Validates value as IP address
 710+ if ( !IP::isValid( $_SERVER['HTTP_HOST'] ) ) {
 711+ $domainParts = explode( '.', $_SERVER['HTTP_HOST'] );
 712+ $domainParts = array_reverse( $domainParts );
 713+ // Although some browsers will accept cookies without the initial ., » RFC 2109 requires it to be included.
 714+ wfProfileOut( __METHOD__ );
 715+ return '.' . $domainParts[1] . '.' . $domainParts[0];
 716+ }
 717+ wfProfileOut( __METHOD__ );
 718+ return $_SERVER['HTTP_HOST'];
 719+ }
 720+
 721+ /**
 722+ * @param $url string
 723+ * @return string
 724+ */
 725+ private function getRelativeURL( $url ) {
 726+ wfProfileIn( __METHOD__ );
 727+ $parsedUrl = parse_url( $url );
 728+ // Validates value as IP address
 729+ if ( !empty( $parsedUrl['host'] ) && !IP::isValid( $parsedUrl['host'] ) ) {
 730+ $baseUrl = $parsedUrl['scheme'] . '://' . $parsedUrl['host'];
 731+ $baseUrl = str_replace( $baseUrl, '', $url );
 732+ wfProfileOut( __METHOD__ );
 733+ return $baseUrl;
 734+ }
 735+ wfProfileOut( __METHOD__ );
 736+ return $url;
 737+ }
 738+
 739+ private function disableCaching() {
 740+ global $wgRequest;
 741+ wfProfileIn( __METHOD__ );
 742+ if ( isset( $_SERVER['HTTP_VIA'] ) &&
 743+ stripos( $_SERVER['HTTP_VIA'], '.wikimedia.org:3128' ) !== false ) {
 744+ $wgRequest->response()->header( 'Cache-Control: no-cache, must-revalidate' );
 745+ $wgRequest->response()->header( 'Expires: Sat, 26 Jul 1997 05:00:00 GMT' );
 746+ $wgRequest->response()->header( 'Pragma: no-cache' );
 747+ }
 748+ wfProfileOut( __METHOD__ );
 749+ return true;
 750+ }
 751+
 752+ private function sendXDeviceVaryHeader() {
 753+ global $wgOut, $wgRequest;
 754+ wfProfileIn( __METHOD__ );
 755+ if ( isset( $_SERVER['HTTP_X_DEVICE'] ) ) {
 756+ $wgRequest->response()->header( 'X-Device: ' . $_SERVER['HTTP_X_DEVICE'] );
 757+ $wgOut->addVaryHeader( 'X-Device' );
 758+ }
 759+ $wgOut->addVaryHeader( 'Cookie' );
 760+ $wgOut->addVaryHeader( 'X-Carrier' );
 761+ wfProfileOut( __METHOD__ );
 762+ return true;
 763+ }
 764+
 765+ private function sendApplicationVersionVaryHeader() {
 766+ global $wgOut, $wgRequest;
 767+ wfProfileIn( __METHOD__ );
 768+ $wgOut->addVaryHeader( 'Application_Version' );
 769+ if ( isset( $_SERVER['HTTP_APPLICATION_VERSION'] ) ) {
 770+ $wgRequest->response()->header( 'Application_Version: ' . $_SERVER['HTTP_APPLICATION_VERSION'] );
 771+ } else {
 772+ if ( isset( $_SERVER['HTTP_X_DEVICE'] ) ) {
 773+ if ( stripos( $_SERVER['HTTP_X_DEVICE'], 'iphone' ) !== false ||
 774+ stripos( $_SERVER['HTTP_X_DEVICE'], 'android' ) !== false ) {
 775+ $wgRequest->response()->header( 'Application_Version: ' . $_SERVER['HTTP_X_DEVICE'] );
 776+ }
 777+ }
 778+ }
 779+ wfProfileOut( __METHOD__ );
 780+ return true;
 781+ }
 782+
 783+ /**
 784+ * @return string
 785+ */
 786+ private function renderLeaveFeedbackXHTML() {
 787+ global $wgRequest, $wgUser;
 788+ wfProfileIn( __METHOD__ );
 789+ if ( $this->contentFormat == 'XHTML' ) {
 790+ $this->getMsg();
 791+ $searchTemplate = $this->getSearchTemplate();
 792+ $searchWebkitHtml = $searchTemplate->getHTML();
 793+ $footerTemplate = $this->getFooterTemplate();
 794+ $footerHtml = $footerTemplate->getHTML();
 795+ $leaveFeedbackTemplate = new LeaveFeedbackTemplate();
 796+ $options = array(
 797+ 'feedbackPostURL' => str_replace( '&mobileaction=leave_feedback', '', $wgRequest->getFullRequestURL() ) . '&mobileaction=leave_feedback_post',
 798+ 'editToken' => $wgUser->editToken(),
 799+ 'title' => self::$messages['mobile-frontend-leave-feedback-title'],
 800+ 'notice' => self::$messages['mobile-frontend-leave-feedback-notice'],
 801+ 'subject' => self::$messages['mobile-frontend-leave-feedback-subject'],
 802+ 'message' => self::$messages['mobile-frontend-leave-feedback-message'],
 803+ 'cancel' => self::$messages['mobile-frontend-leave-feedback-cancel'],
 804+ 'submit' => self::$messages['mobile-frontend-leave-feedback-submit'],
 805+ );
 806+ $leaveFeedbackTemplate->setByArray( $options );
 807+ $leaveFeedbackHtml = $leaveFeedbackTemplate->getHTML();
 808+ $contentHtml = $leaveFeedbackHtml;
 809+ $applicationTemplate = $this->getApplicationTemplate();
 810+ $options = array(
 811+ 'htmlTitle' => self::$messages['mobile-frontend-leave-feedback'],
 812+ 'searchWebkitHtml' => $searchWebkitHtml,
 813+ 'contentHtml' => $contentHtml,
 814+ 'footerHtml' => $footerHtml,
 815+ );
 816+ $applicationTemplate->setByArray( $options );
 817+ $applicationHtml = $applicationTemplate->getHTML();
 818+ wfProfileOut( __METHOD__ );
 819+ return $applicationHtml;
 820+ }
 821+ wfProfileOut( __METHOD__ );
 822+ return '';
 823+ }
 824+
 825+ /**
 826+ * @return string
 827+ */
 828+ private function renderOptInMobileSiteXHTML() {
 829+ wfProfileIn( __METHOD__ );
 830+ if ( $this->contentFormat == 'XHTML' ) {
 831+ $this->getMsg();
 832+ $searchTemplate = $this->getSearchTemplate();
 833+ $searchWebkitHtml = $searchTemplate->getHTML();
 834+ $footerTemplate = $this->getFooterTemplate();
 835+ $footerHtml = $footerTemplate->getHTML();
 836+ $optInTemplate = new OptInTemplate();
 837+ $options = array(
 838+ 'explainOptIn' => self::$messages['mobile-frontend-opt-in-explain'],
 839+ 'optInMessage' => self::$messages['mobile-frontend-opt-in-message'],
 840+ 'yesButton' => self::$messages['mobile-frontend-opt-in-yes-button'],
 841+ 'noButton' => self::$messages['mobile-frontend-opt-in-no-button'],
 842+ 'formAction' => wfExpandUrl( Title::newMainPage()->getFullURL(), PROTO_CURRENT ),
 843+ );
 844+ $optInTemplate->setByArray( $options );
 845+ $optInHtml = $optInTemplate->getHTML();
 846+ $contentHtml = $optInHtml;
 847+ $applicationTemplate = $this->getApplicationTemplate();
 848+ $options = array(
 849+ 'htmlTitle' => self::$messages['mobile-frontend-opt-in-title'],
 850+ 'searchWebkitHtml' => $searchWebkitHtml,
 851+ 'contentHtml' => $contentHtml,
 852+ 'footerHtml' => $footerHtml,
 853+ );
 854+ $applicationTemplate->setByArray( $options );
 855+ $applicationHtml = $applicationTemplate->getHTML();
 856+ wfProfileOut( __METHOD__ );
 857+ return $applicationHtml;
 858+ }
 859+ wfProfileOut( __METHOD__ );
 860+ return '';
 861+ }
 862+
 863+ /**
 864+ * @return string
 865+ */
 866+ private function renderOptOutMobileSiteXHTML() {
 867+ wfProfileIn( __METHOD__ );
 868+ if ( $this->contentFormat == 'XHTML' ) {
 869+ $this->getMsg();
 870+ $searchTemplate = $this->getSearchTemplate();
 871+ $searchWebkitHtml = $searchTemplate->getHTML();
 872+ $footerTemplate = $this->getFooterTemplate();
 873+ $footerHtml = $footerTemplate->getHTML();
 874+ $optOutTemplate = new OptOutTemplate();
 875+ $options = array(
 876+ 'explainOptOut' => self::$messages['mobile-frontend-opt-out-explain'],
 877+ 'optOutMessage' => self::$messages['mobile-frontend-opt-out-message'],
 878+ 'yesButton' => self::$messages['mobile-frontend-opt-out-yes-button'],
 879+ 'noButton' => self::$messages['mobile-frontend-opt-out-no-button'],
 880+ 'formAction' => wfExpandUrl( Title::newMainPage()->getFullURL(), PROTO_CURRENT ),
 881+ );
 882+ $optOutTemplate->setByArray( $options );
 883+ $optOutHtml = $optOutTemplate->getHTML();
 884+ $contentHtml = $optOutHtml;
 885+ $applicationTemplate = $this->getApplicationTemplate();
 886+ $options = array(
 887+ 'htmlTitle' => self::$messages['mobile-frontend-opt-out-title'],
 888+ 'searchWebkitHtml' => $searchWebkitHtml,
 889+ 'contentHtml' => $contentHtml,
 890+ 'footerHtml' => $footerHtml,
 891+ );
 892+ $applicationTemplate->setByArray( $options );
 893+ $applicationHtml = $applicationTemplate->getHTML();
 894+ wfProfileOut( __METHOD__ );
 895+ return $applicationHtml;
 896+ }
 897+ wfProfileOut( __METHOD__ );
 898+ return '';
 899+ }
 900+
 901+ /**
 902+ * @return string
 903+ */
 904+ private function renderDisableMobileSiteXHTML() {
 905+ wfProfileIn( __METHOD__ );
 906+ if ( $this->contentFormat == 'XHTML' ) {
 907+ $this->getMsg();
 908+ $areYouSure = self::$messages['mobile-frontend-are-you-sure'];
 909+ $explainDisable = self::$messages['mobile-frontend-explain-disable'];
 910+ $disableButton = self::$messages['mobile-frontend-disable-button'];
 911+ $backButton = self::$messages['mobile-frontend-back-button'];
 912+ $htmlTitle = $areYouSure;
 913+ $title = $areYouSure;
 914+ $searchTemplate = $this->getSearchTemplate();
 915+ $searchWebkitHtml = $searchTemplate->getHTML();
 916+ $footerTemplate = $this->getFooterTemplate();
 917+ $footerHtml = $footerTemplate->getHTML();
 918+ $disableTemplate = new DisableTemplate();
 919+ $options = array(
 920+ 'currentURL' => self::$currentURL,
 921+ 'mobileRedirectFormAction' => self::$mobileRedirectFormAction,
 922+ 'areYouSure' => $areYouSure,
 923+ 'explainDisable' => $explainDisable,
 924+ 'disableButton' => $disableButton,
 925+ 'backButton' => $backButton,
 926+ 'htmlTitle' => $htmlTitle,
 927+ 'title' => $title,
 928+ );
 929+ $disableTemplate->setByArray( $options );
 930+ $disableHtml = $disableTemplate->getHTML();
 931+
 932+ $contentHtml = $disableHtml;
 933+ $applicationTemplate = $this->getApplicationTemplate();
 934+ $options = array(
 935+ 'htmlTitle' => $htmlTitle,
 936+ 'searchWebkitHtml' => $searchWebkitHtml,
 937+ 'contentHtml' => $contentHtml,
 938+ 'footerHtml' => $footerHtml,
 939+ );
 940+ $applicationTemplate->setByArray( $options );
 941+ $applicationHtml = $applicationTemplate->getHTML();
 942+ wfProfileOut( __METHOD__ );
 943+ return $applicationHtml;
 944+ }
 945+ wfProfileOut( __METHOD__ );
 946+ return '';
 947+ }
 948+
 949+ /**
 950+ * @param $matches array
 951+ * @return string
 952+ */
 953+ private function headingTransformCallbackWML( $matches ) {
 954+ wfProfileIn( __METHOD__ );
 955+ static $headings = 0;
 956+ ++$headings;
 957+
 958+ $base = $this->WMLSectionSeperator .
 959+ "<h2 class='section_heading' id='section_{$headings}'>{$matches[2]}</h2>";
 960+
 961+ self::$headings = $headings;
 962+ wfProfileOut( __METHOD__ );
 963+ return $base;
 964+ }
 965+
 966+ /**
 967+ * @param $matches array
 968+ * @return string
 969+ */
 970+ private function headingTransformCallbackXHTML( $matches ) {
 971+ wfProfileIn( __METHOD__ );
 972+ if ( isset( $matches[0] ) ) {
 973+ preg_match( '/id="([^"]*)"/', $matches[0], $headlineMatches );
 974+ }
 975+
 976+ $headlineId = ( isset( $headlineMatches[1] ) ) ? $headlineMatches[1] : '';
 977+
 978+ static $headings = 0;
 979+ $show = self::$messages['mobile-frontend-show-button'];
 980+ $hide = self::$messages['mobile-frontend-hide-button'];
 981+ $backToTop = self::$messages['mobile-frontend-back-to-top-of-section'];
 982+ ++$headings;
 983+ // Back to top link
 984+ $base = Html::openElement( 'div',
 985+ array( 'id' => 'anchor_' . intval( $headings - 1 ),
 986+ 'class' => 'section_anchors', )
 987+ ) .
 988+ Html::rawElement( 'a',
 989+ array( 'href' => '#section_' . intval( $headings - 1 ),
 990+ 'class' => 'back_to_top' ),
 991+ '&#8593;' . $backToTop ) .
 992+ Html::closeElement( 'div' );
 993+ // generate the HTML we are going to inject
 994+ $buttons = Html::element( 'button',
 995+ array( 'class' => 'section_heading show',
 996+ 'section_id' => $headings ),
 997+ $show ) .
 998+ Html::element( 'button',
 999+ array( 'class' => 'section_heading hide',
 1000+ 'section_id' => $headings ),
 1001+ $hide );
 1002+ if ( self::$device['supports_javascript'] ) {
 1003+ $h2OnClick = 'javascript:wm_toggle_section(' . $headings . ');';
 1004+ $base .= Html::openElement( 'h2',
 1005+ array( 'class' => 'section_heading',
 1006+ 'id' => 'section_' . $headings,
 1007+ 'onclick' => $h2OnClick ) );
 1008+ } else {
 1009+ $base .= Html::openElement( 'h2',
 1010+ array( 'class' => 'section_heading',
 1011+ 'id' => 'section_' . $headings ) );
 1012+ }
 1013+ $base .= $buttons .
 1014+ Html::rawElement( 'span',
 1015+ array( 'id' => $headlineId ),
 1016+ $matches[2] ) .
 1017+ Html::closeElement( 'h2' ) .
 1018+ Html::openElement( 'div',
 1019+ array( 'class' => 'content_block',
 1020+ 'id' => 'content_' . $headings ) );
 1021+
 1022+ if ( $headings > 1 ) {
 1023+ // Close it up here
 1024+ $base = Html::closeElement( 'div' ) . $base;
 1025+ }
 1026+
 1027+ self::$headings = $headings;
 1028+ wfProfileOut( __METHOD__ );
 1029+ return $base;
 1030+ }
 1031+
 1032+ /**
 1033+ * @param $s string
 1034+ * @return string
 1035+ */
 1036+ public function headingTransform( $s ) {
 1037+ wfProfileIn( __METHOD__ );
 1038+ $callback = 'headingTransformCallback';
 1039+ $callback .= $this->contentFormat;
 1040+
 1041+ // Closures are a PHP 5.3 feature.
 1042+ // MediaWiki currently requires PHP 5.2.3 or higher.
 1043+ // So, using old style for now.
 1044+ $s = preg_replace_callback(
 1045+ '/<h2(.*)<span class="mw-headline" [^>]*>(.+)<\/span>\w*<\/h2>/',
 1046+ array( $this, $callback ),
 1047+ $s
 1048+ );
 1049+
 1050+ // if we had any, make sure to close the whole thing!
 1051+ if ( isset( self::$headings ) && self::$headings > 0 ) {
 1052+ $s = str_replace(
 1053+ '<div class="visualClear">',
 1054+ '</div><div class="visualClear">',
 1055+ $s
 1056+ );
 1057+ }
 1058+ wfProfileOut( __METHOD__ );
 1059+ return $s;
 1060+ }
 1061+
 1062+ /**
 1063+ * @param $s string
 1064+ * @return string
 1065+ */
 1066+ private function createWMLCard( $s ) {
 1067+ wfProfileIn( __METHOD__ );
 1068+ $segments = explode( $this->WMLSectionSeperator, $s );
 1069+ $card = '';
 1070+ $idx = 0;
 1071+ $requestedSegment = htmlspecialchars( self::$requestedSegment );
 1072+ $title = htmlspecialchars( self::$title->getText() );
 1073+
 1074+ $card .= "<card id='s{$idx}' title='{$title}'><p>{$segments[$requestedSegment]}</p>";
 1075+ $idx = $requestedSegment + 1;
 1076+ $segmentsCount = count( $segments );
 1077+ $card .= "<p>" . $idx . "/" . $segmentsCount . "</p>";
 1078+
 1079+ $useFormatParam = ( isset( self::$useFormat ) ) ? '&amp;' . 'useformat=' . self::$useFormat : '';
 1080+
 1081+ // Title::getLocalUrl doesn't work at this point since PHP 5.1.x, all objects have their destructors called
 1082+ // before the output buffer callback function executes.
 1083+ // Thus, globalized objects will not be available as expected in the function.
 1084+ // This is stated to be intended behavior, as per the following: [http://bugs.php.net/bug.php?id=40104]
 1085+ $mDefaultQuery = $_GET;
 1086+ unset( $mDefaultQuery['seg'] );
 1087+ unset( $mDefaultQuery['useformat'] );
 1088+
 1089+ $qs = wfArrayToCGI( $mDefaultQuery );
 1090+ $delimiter = ( !empty( $qs ) ) ? '?' : '';
 1091+ $basePageParts = wfParseUrl( self::$currentURL );
 1092+ $basePage = $basePageParts['scheme'] . $basePageParts['delimiter'] . $basePageParts['host'] . $basePageParts['path'] . $delimiter . $qs;
 1093+ $appendDelimiter = ( $delimiter === '?' ) ? '&amp;' : '?';
 1094+
 1095+ if ( $idx < $segmentsCount ) {
 1096+ $card .= "<p><a href=\"{$basePage}{$appendDelimiter}seg={$idx}{$useFormatParam}\">" . self::$messages['mobile-frontend-wml-continue'] . "</a></p>";
 1097+ }
 1098+
 1099+ if ( $idx > 1 ) {
 1100+ $back_idx = $requestedSegment - 1;
 1101+ $card .= "<p><a href=\"{$basePage}{$appendDelimiter}seg={$back_idx}{$useFormatParam}\">" . self::$messages['mobile-frontend-wml-back'] . "</a></p>";
 1102+ }
 1103+
 1104+ $card .= '</card>';
 1105+ wfProfileOut( __METHOD__ );
 1106+ return $card;
 1107+ }
 1108+
 1109+ /**
 1110+ * @return array
 1111+ */
 1112+ private function parseItemsToRemove() {
 1113+ global $wgMFRemovableClasses;
 1114+ wfProfileIn( __METHOD__ );
 1115+ $itemToRemoveRecords = array();
 1116+
 1117+ foreach ( array_merge( $this->itemsToRemove, $wgMFRemovableClasses ) as $itemToRemove ) {
 1118+ $type = '';
 1119+ $rawName = '';
 1120+ CssDetection::detectIdCssOrTag( $itemToRemove, $type, $rawName );
 1121+ $itemToRemoveRecords[$type][] = $rawName;
 1122+ }
 1123+
 1124+ wfProfileOut( __METHOD__ );
 1125+ return $itemToRemoveRecords;
 1126+ }
 1127+
 1128+ /**
 1129+ * @param $html string
 1130+ * @return string
 1131+ */
 1132+ public function DOMParseMainPage( $html ) {
 1133+ wfProfileIn( __METHOD__ );
 1134+ $html = mb_convert_encoding( $html, 'HTML-ENTITIES', "UTF-8" );
 1135+ libxml_use_internal_errors( true );
 1136+ $this->mainPage = new DOMDocument();
 1137+ // It seems that loadhtml() does not "attach" the html dtd that defines id as an id-attribute to the DOM.
 1138+ $this->mainPage->loadHTML( '<?xml encoding="UTF-8"><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
 1139+ <html><head><title></title></head><body>' . $html . '</body></html>' );
 1140+ libxml_use_internal_errors( false );
 1141+ $this->mainPage->preserveWhiteSpace = false;
 1142+ $this->mainPage->strictErrorChecking = false;
 1143+ $this->mainPage->encoding = 'UTF-8';
 1144+
 1145+ $zeroLandingPage = $this->mainPage->getElementById( 'zero-landing-page' );
 1146+ $featuredArticle = $this->mainPage->getElementById( 'mp-tfa' );
 1147+ $newsItems = $this->mainPage->getElementById( 'mp-itn' );
 1148+
 1149+ $xpath = new DOMXpath( $this->mainPage );
 1150+ $elements = $xpath->query( '//*[starts-with(@id, "mf-")]' );
 1151+
 1152+ $commonAttributes = array( 'mp-tfa', 'mp-itn' );
 1153+
 1154+ $content = $this->mainPage->createElement( 'div' );
 1155+ $content->setAttribute( 'id', 'content' );
 1156+
 1157+ if ( $zeroLandingPage ) {
 1158+ $content->appendChild( $zeroLandingPage );
 1159+ }
 1160+
 1161+ if ( $featuredArticle ) {
 1162+ $h2FeaturedArticle = $this->mainPage->createElement( 'h2', self::$messages['mobile-frontend-featured-article'] );
 1163+ $content->appendChild( $h2FeaturedArticle );
 1164+ $content->appendChild( $featuredArticle );
 1165+ }
 1166+
 1167+ if ( $newsItems ) {
 1168+ $h2NewsItems = $this->mainPage->createElement( 'h2', self::$messages['mobile-frontend-news-items'] );
 1169+ $content->appendChild( $h2NewsItems );
 1170+ $content->appendChild( $newsItems );
 1171+ }
 1172+
 1173+ foreach ( $elements as $element ) {
 1174+ if ( $element->hasAttribute( 'id' ) ) {
 1175+ $id = $element->getAttribute( 'id' );
 1176+ if ( !in_array( $id, $commonAttributes ) ) {
 1177+ $elementTitle = $element->hasAttribute( 'title' ) ? $element->getAttribute( 'title' ) : '';
 1178+ $h2UnknownMobileSection = $this->mainPage->createElement( 'h2', $elementTitle );
 1179+ $br = $this->mainPage->createElement( 'br' );
 1180+ $br->setAttribute( 'CLEAR', 'ALL' );
 1181+ $content->appendChild( $h2UnknownMobileSection );
 1182+ $content->appendChild( $element );
 1183+ $content->appendChild( $br );
 1184+ }
 1185+ }
 1186+ }
 1187+
 1188+ $contentHtml = $this->mainPage->saveXML( $content, LIBXML_NOEMPTYTAG );
 1189+ wfProfileOut( __METHOD__ );
 1190+ return $contentHtml;
 1191+ }
 1192+
 1193+ /**
 1194+ * @return DomElement
 1195+ */
 1196+ public function renderLogin() {
 1197+ wfProfileIn( __METHOD__ );
 1198+ $form = Html::openElement( 'form',
 1199+ array( 'name' => 'userlogin',
 1200+ 'method' => 'post',
 1201+ 'action' => self::$wsLoginFormAction ) ) .
 1202+ Html::openElement( 'table',
 1203+ array( 'class' => 'user-login' ) ) .
 1204+ Html::openElement( 'tbody' ) .
 1205+ Html::openElement( 'tr' ) .
 1206+ Html::openElement( 'td',
 1207+ array( 'class' => 'mw-label' ) ) .
 1208+ Html::element( 'label',
 1209+ array( 'for' => 'wpName1' ), self::$messages['mobile-frontend-username'] ) .
 1210+ Html::closeElement( 'td' ) .
 1211+ Html::closeElement( 'tr' ) .
 1212+ Html::openElement( 'tr' ) .
 1213+ Html::openElement( 'td' ) .
 1214+ Html::input( 'wpName', null, 'text',
 1215+ array( 'class' => 'loginText',
 1216+ 'id' => 'wpName1',
 1217+ 'tabindex' => '1',
 1218+ 'size' => '20',
 1219+ 'required' ) ) .
 1220+ Html::closeElement( 'td' ) .
 1221+ Html::closeElement( 'tr' ) .
 1222+ Html::openElement( 'tr' ) .
 1223+ Html::openElement( 'td',
 1224+ array( 'class' => 'mw-label' ) ) .
 1225+ Html::element( 'label',
 1226+ array( 'for' => 'wpPassword1' ), self::$messages['mobile-frontend-password'] ) .
 1227+ Html::closeElement( 'td' ) .
 1228+ Html::closeElement( 'tr' ) .
 1229+ Html::openElement( 'tr' ) .
 1230+ Html::openElement( 'td',
 1231+ array( 'class' => 'mw-input' ) ) .
 1232+ Html::input( 'wpPassword', null, 'password',
 1233+ array( 'class' => 'loginPassword',
 1234+ 'id' => 'wpPassword1',
 1235+ 'tabindex' => '2',
 1236+ 'size' => '20' ) ) .
 1237+ Html::closeElement( 'td' ) .
 1238+ Html::closeElement( 'tr' ) .
 1239+ Html::openElement( 'tr' ) .
 1240+ Html::element( 'td' ) .
 1241+ Html::closeElement( 'tr' ) .
 1242+ Html::openElement( 'tr' ) .
 1243+ Html::openElement( 'td',
 1244+ array( 'class' => 'mw-submit' ) ) .
 1245+ Html::input( 'wpLoginAttempt', self::$messages['mobile-frontend-login'], 'submit',
 1246+ array( 'id' => 'wpLoginAttempt',
 1247+ 'tabindex' => '3' ) ) .
 1248+ Html::closeElement( 'td' ) .
 1249+ Html::closeElement( 'tr' ) .
 1250+ Html::closeElement( 'tbody' ) .
 1251+ Html::closeElement( 'table' ) .
 1252+ Html::input( 'wpLoginToken', self::$wsLoginToken, 'hidden' ) .
 1253+ Html::closeElement( 'form' );
 1254+ wfProfileOut( __METHOD__ );
 1255+ return $this->getDomDocumentNodeByTagName( $form, 'form' );
 1256+ }
 1257+
 1258+ /**
 1259+ * @param $html string
 1260+ * @param $tagName string
 1261+ * @return DomElement
 1262+ */
 1263+ private function getDomDocumentNodeByTagName( $html, $tagName ) {
 1264+ wfProfileIn( __METHOD__ );
 1265+ libxml_use_internal_errors( true );
 1266+ $dom = new DOMDocument();
 1267+ $dom->loadHTML( $html );
 1268+ libxml_use_internal_errors( false );
 1269+ $dom->preserveWhiteSpace = false;
 1270+ $dom->strictErrorChecking = false;
 1271+ $dom->encoding = 'UTF-8';
 1272+ $node = $dom->getElementsByTagName( $tagName )->item( 0 );
 1273+ wfProfileOut( __METHOD__ );
 1274+ return $node;
 1275+ }
 1276+
 1277+ /**
 1278+ * @param $html string
 1279+ * @return string
 1280+ */
 1281+ public function DOMParse( $html ) {
 1282+ global $wgScript;
 1283+ wfProfileIn( __METHOD__ );
 1284+ $html = mb_convert_encoding( $html, 'HTML-ENTITIES', "UTF-8" );
 1285+ libxml_use_internal_errors( true );
 1286+ $this->doc = new DOMDocument();
 1287+ $this->doc->loadHTML( '<?xml encoding="UTF-8">' . $html );
 1288+ libxml_use_internal_errors( false );
 1289+ $this->doc->preserveWhiteSpace = false;
 1290+ $this->doc->strictErrorChecking = false;
 1291+ $this->doc->encoding = 'UTF-8';
 1292+
 1293+ $itemToRemoveRecords = $this->parseItemsToRemove();
 1294+
 1295+ $zeroRatedBannerElement = $this->doc->getElementById( 'zero-rated-banner' );
 1296+
 1297+ if ( !$zeroRatedBannerElement ) {
 1298+ $zeroRatedBannerElement = $this->doc->getElementById( 'zero-rated-banner-red' );
 1299+ }
 1300+
 1301+ if ( $zeroRatedBannerElement ) {
 1302+ self::$zeroRatedBanner = $this->doc->saveXML( $zeroRatedBannerElement, LIBXML_NOEMPTYTAG );
 1303+ }
 1304+
 1305+ if ( self::$isBetaGroupMember ) {
 1306+ $ptLogout = $this->doc->getElementById( 'pt-logout' );
 1307+
 1308+ if ( $ptLogout ) {
 1309+ $ptLogoutLink = $ptLogout->firstChild;
 1310+ self::$logoutHtml = $this->doc->saveXML( $ptLogoutLink, LIBXML_NOEMPTYTAG );
 1311+ }
 1312+ $ptAnonLogin = $this->doc->getElementById( 'pt-anonlogin' );
 1313+
 1314+ if ( !$ptAnonLogin ) {
 1315+ $ptAnonLogin = $this->doc->getElementById( 'pt-login' );
 1316+ }
 1317+
 1318+ if ( $ptAnonLogin ) {
 1319+ $ptAnonLoginLink = $ptAnonLogin->firstChild;
 1320+ if ( $ptAnonLoginLink && $ptAnonLoginLink->hasAttributes() ) {
 1321+ $ptAnonLoginLinkHref = $ptAnonLoginLink->getAttributeNode( 'href' );
 1322+ $ptAnonLoginLinkTitle = $ptAnonLoginLink->getAttributeNode( 'title' );
 1323+ if ( $ptAnonLoginLinkTitle ) {
 1324+ $ptAnonLoginLinkTitle->nodeValue = self::$messages['mobile-frontend-login'];
 1325+ }
 1326+ if ( $ptAnonLoginLinkHref ) {
 1327+ $ptAnonLoginLinkHref->nodeValue = str_replace( "&", "&amp;", $ptAnonLoginLinkHref->nodeValue );
 1328+ }
 1329+ $ptAnonLoginLinkText = $ptAnonLoginLink->firstChild;
 1330+ if ( $ptAnonLoginLinkText ) {
 1331+ $ptAnonLoginLinkText->nodeValue = self::$messages['mobile-frontend-login'];
 1332+ }
 1333+ }
 1334+ self::$loginHtml = $this->doc->saveXML( $ptAnonLoginLink, LIBXML_NOEMPTYTAG );
 1335+ }
 1336+ }
 1337+
 1338+ if ( self::$title->isSpecial( 'Userlogin' ) && self::$isBetaGroupMember ) {
 1339+ $userlogin = $this->doc->getElementById( 'userloginForm' );
 1340+
 1341+ if ( $userlogin && get_class( $userlogin ) === 'DOMElement' ) {
 1342+ $firstHeading = $this->doc->getElementById( 'firstHeading' );
 1343+ if ( $firstHeading ) {
 1344+ $firstHeading->nodeValue = '';
 1345+ }
 1346+ }
 1347+ }
 1348+
 1349+ // Tags
 1350+
 1351+ // You can't remove DOMNodes from a DOMNodeList as you're iterating
 1352+ // over them in a foreach loop. It will seemingly leave the internal
 1353+ // iterator on the foreach out of wack and results will be quite
 1354+ // strange. Though, making a queue of items to remove seems to work.
 1355+ // For example:
 1356+
 1357+ if ( self::$disableImages == 1 ) {
 1358+ $itemToRemoveRecords['TAG'][] = "img";
 1359+ $itemToRemoveRecords['TAG'][] = "audio";
 1360+ $itemToRemoveRecords['TAG'][] = "video";
 1361+ $itemToRemoveRecords['CLASS'][] = "thumb tright";
 1362+ $itemToRemoveRecords['CLASS'][] = "thumb tleft";
 1363+ $itemToRemoveRecords['CLASS'][] = "thumbcaption";
 1364+ $itemToRemoveRecords['CLASS'][] = "gallery";
 1365+ }
 1366+
 1367+ $tagToRemoveNodeIdAttributeValues = array( 'zero-language-search' );
 1368+
 1369+ $domElemsToRemove = array();
 1370+ foreach ( $itemToRemoveRecords['TAG'] as $tagToRemove ) {
 1371+ $tagToRemoveNodes = $this->doc->getElementsByTagName( $tagToRemove );
 1372+ foreach ( $tagToRemoveNodes as $tagToRemoveNode ) {
 1373+ $tagToRemoveNodeIdAttributeValue = '';
 1374+ if ( $tagToRemoveNode ) {
 1375+ $tagToRemoveNodeIdAttribute = $tagToRemoveNode->getAttributeNode( 'id' );
 1376+ if ( $tagToRemoveNodeIdAttribute ) {
 1377+ $tagToRemoveNodeIdAttributeValue = $tagToRemoveNodeIdAttribute->value;
 1378+ }
 1379+ if ( !in_array( $tagToRemoveNodeIdAttributeValue, $tagToRemoveNodeIdAttributeValues ) ) {
 1380+ $domElemsToRemove[] = $tagToRemoveNode;
 1381+ }
 1382+ }
 1383+ }
 1384+ }
 1385+
 1386+ foreach ( $domElemsToRemove as $domElement ) {
 1387+ $domElement->parentNode->removeChild( $domElement );
 1388+ }
 1389+
 1390+ // Elements with named IDs
 1391+ foreach ( $itemToRemoveRecords['ID'] as $itemToRemove ) {
 1392+ $itemToRemoveNode = $this->doc->getElementById( $itemToRemove );
 1393+ if ( $itemToRemoveNode ) {
 1394+ $itemToRemoveNode->parentNode->removeChild( $itemToRemoveNode );
 1395+ }
 1396+ }
 1397+
 1398+ // CSS Classes
 1399+ $xpath = new DOMXpath( $this->doc );
 1400+ foreach ( $itemToRemoveRecords['CLASS'] as $classToRemove ) {
 1401+ $elements = $xpath->query( '//*[@class="' . $classToRemove . '"]' );
 1402+
 1403+ foreach ( $elements as $element ) {
 1404+ $element->parentNode->removeChild( $element );
 1405+ }
 1406+ }
 1407+
 1408+ // Tags with CSS Classes
 1409+ foreach ( $itemToRemoveRecords['TAG_CLASS'] as $classToRemove ) {
 1410+ $parts = explode( '.', $classToRemove );
 1411+
 1412+ $elements = $xpath->query(
 1413+ '//' . $parts[0] . '[@class="' . $parts[1] . '"]'
 1414+ );
 1415+
 1416+ foreach ( $elements as $element ) {
 1417+ $removedElement = $element->parentNode->removeChild( $element );
 1418+ }
 1419+ }
 1420+
 1421+ // Handle red links with action equal to edit
 1422+ $redLinks = $xpath->query( '//a[@class="new"]' );
 1423+ foreach ( $redLinks as $redLink ) {
 1424+ // PHP Bug #36795 — Inappropriate "unterminated entity reference"
 1425+ $spanNode = $this->doc->createElement( "span", str_replace( "&", "&amp;", $redLink->nodeValue ) );
 1426+
 1427+ if ( $redLink->hasAttributes() ) {
 1428+ $attributes = $redLink->attributes;
 1429+ foreach ( $attributes as $i => $attribute ) {
 1430+ if ( $attribute->name != 'href' ) {
 1431+ $spanNode->setAttribute( $attribute->name, $attribute->value );
 1432+ }
 1433+ }
 1434+ }
 1435+
 1436+ $redLink->parentNode->replaceChild( $spanNode, $redLink );
 1437+ }
 1438+
 1439+ if ( self::$title->isSpecial( 'Userlogin' ) && self::$isBetaGroupMember ) {
 1440+ if ( $userlogin && get_class( $userlogin ) === 'DOMElement' ) {
 1441+ $login = $this->renderLogin();
 1442+ $loginNode = $this->doc->importNode( $login, true );
 1443+ $userlogin->appendChild( $loginNode );
 1444+ }
 1445+ }
 1446+
 1447+ $content = $this->doc->getElementById( 'content' );
 1448+
 1449+ $contentHtml = $this->doc->saveXML( $content, LIBXML_NOEMPTYTAG );
 1450+
 1451+ if ( self::$isMainPage ) {
 1452+ $contentHtml = $this->DOMParseMainPage( $contentHtml );
 1453+ }
 1454+
 1455+ $title = htmlspecialchars( self::$title->getText() );
 1456+ $htmlTitle = htmlspecialchars( self::$htmlTitle );
 1457+
 1458+ if ( strlen( $contentHtml ) > 4000 && $this->contentFormat == 'XHTML'
 1459+ && self::$device['supports_javascript'] === true
 1460+ && empty( self::$search ) && !self::$isMainPage ) {
 1461+ $contentHtml = $this->headingTransform( $contentHtml );
 1462+ } elseif ( $this->contentFormat == 'WML' ) {
 1463+ header( 'Content-Type: text/vnd.wap.wml' );
 1464+ $contentHtml = $this->headingTransform( $contentHtml );
 1465+
 1466+ // Content removal for WML rendering
 1467+ $elements = array( 'span', 'div', 'sup', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'sup', 'sub' );
 1468+ foreach ( $elements as $element ) {
 1469+ $contentHtml = preg_replace( '#</?' . $element . '[^>]*>#is', '', $contentHtml );
 1470+ }
 1471+
 1472+ // Wml for searching
 1473+ $searchWml = '<p><input emptyok="true" format="*M" type="text" name="search" value="" size="16" />' .
 1474+ '<do type="accept" label="' . self::$messages['mobile-frontend-search-submit'] . '">' .
 1475+ '<go href="' . $wgScript . '?title=Special%3ASearch&amp;search=$(search)"></go></do></p>';
 1476+ $contentHtml = $searchWml . $contentHtml;
 1477+ // Content wrapping
 1478+ $contentHtml = $this->createWMLCard( $contentHtml );
 1479+ $applicationWmlTemplate = new ApplicationWmlTemplate();
 1480+ $options = array(
 1481+ 'mainPageUrl' => self::$mainPageUrl,
 1482+ 'randomPageUrl' => self::$randomPageUrl,
 1483+ 'dir' => self::$dir,
 1484+ 'code' => self::$code,
 1485+ 'contentHtml' => $contentHtml,
 1486+ 'homeButton' => self::$messages['mobile-frontend-home-button'],
 1487+ 'randomButton' => self::$messages['mobile-frontend-random-button'],
 1488+ );
 1489+ $applicationWmlTemplate->setByArray( $options );
 1490+ $applicationHtml = $applicationWmlTemplate->getHTML();
 1491+ }
 1492+
 1493+ if ( $this->contentFormat == 'XHTML' && self::$format != 'json' ) {
 1494+ if ( !empty( self::$displayNoticeId ) ) {
 1495+ if ( intval( self::$displayNoticeId ) === 1 ) {
 1496+ $thanksNoticeTemplate = new ThanksNoticeTemplate();
 1497+ $thanksNoticeTemplate->set( 'messages', self::$messages );
 1498+ $noticeHtml = $thanksNoticeTemplate->getHTML();
 1499+ }
 1500+ }
 1501+
 1502+ if ( !empty( self::$displayNoticeId ) ) {
 1503+ if ( intval( self::$displayNoticeId ) === 2 ) {
 1504+ $sopaNoticeTemplate = new SopaNoticeTemplate();
 1505+ $sopaNoticeTemplate->set( 'messages', self::$messages );
 1506+ $noticeHtml = $sopaNoticeTemplate->getHTML();
 1507+ }
 1508+ }
 1509+
 1510+ // header( 'Content-Type: application/xhtml+xml; charset=utf-8' );
 1511+ $searchTemplate = $this->getSearchTemplate();
 1512+ $searchWebkitHtml = $searchTemplate->getHTML();
 1513+ $footerTemplate = $this->getFooterTemplate();
 1514+ $footerHtml = $footerTemplate->getHTML();
 1515+ $noticeHtml = ( !empty( $noticeHtml ) ) ? $noticeHtml : '';
 1516+ $applicationTemplate = $this->getApplicationTemplate();
 1517+ $options = array(
 1518+ 'noticeHtml' => $noticeHtml,
 1519+ 'htmlTitle' => $htmlTitle,
 1520+ 'searchWebkitHtml' => $searchWebkitHtml,
 1521+ 'contentHtml' => $contentHtml,
 1522+ 'footerHtml' => $footerHtml,
 1523+ );
 1524+ $applicationTemplate->setByArray( $options );
 1525+ $applicationHtml = $applicationTemplate->getHTML();
 1526+ }
 1527+
 1528+ if ( self::$format === 'json' ) {
 1529+ header( 'Content-Type: application/javascript' );
 1530+ header( 'Content-Disposition: attachment; filename="data.js";' );
 1531+ $json_data = array();
 1532+ $json_data['title'] = htmlspecialchars ( self::$title->getText() );
 1533+ $json_data['html'] = $contentHtml;
 1534+
 1535+ $json = FormatJson::encode( $json_data );
 1536+
 1537+ if ( !empty( self::$callback ) ) {
 1538+ $json = urlencode( htmlspecialchars( self::$callback ) ) . '(' . $json . ')';
 1539+ }
 1540+
 1541+ wfProfileOut( __METHOD__ );
 1542+ return $json;
 1543+ }
 1544+
 1545+ wfProfileOut( __METHOD__ );
 1546+ return $applicationHtml;
 1547+ }
 1548+
 1549+ public function getFooterTemplate() {
 1550+ wfProfileIn( __METHOD__ );
 1551+ $footerTemplate = new FooterTemplate();
 1552+ $logoutHtml = ( self::$logoutHtml ) ? self::$logoutHtml : '';
 1553+ $loginHtml = ( self::$loginHtml ) ? self::$loginHtml : '';
 1554+ $options = array(
 1555+ 'messages' => self::$messages,
 1556+ 'leaveFeedbackURL' => self::$leaveFeedbackURL,
 1557+ 'disableMobileSiteURL' => self::$disableMobileSiteURL,
 1558+ 'viewNormalSiteURL' => self::$viewNormalSiteURL,
 1559+ 'disableImages' => self::$disableImages,
 1560+ 'disableImagesURL' => self::$disableImagesURL,
 1561+ 'enableImagesURL' => self::$enableImagesURL,
 1562+ 'logoutHtml' => $logoutHtml,
 1563+ 'loginHtml' => $loginHtml,
 1564+ 'code' => self::$code,
 1565+ 'hideFooter' => self::$hideFooter,
 1566+ 'isBetaGroupMember' => self::$isBetaGroupMember,
 1567+ );
 1568+ $footerTemplate->setByArray( $options );
 1569+ wfProfileOut( __METHOD__ );
 1570+ return $footerTemplate;
 1571+ }
 1572+
 1573+ public function getSearchTemplate() {
 1574+ global $wgExtensionAssetsPath, $wgMobileFrontendLogo;
 1575+ wfProfileIn( __METHOD__ );
 1576+ $searchTemplate = new SearchTemplate();
 1577+ $options = array(
 1578+ 'searchField' => self::$searchField,
 1579+ 'mainPageUrl' => self::$mainPageUrl,
 1580+ 'randomPageUrl' => self::$randomPageUrl,
 1581+ 'messages' => self::$messages,
 1582+ 'hideSearchBox' => self::$hideSearchBox,
 1583+ 'hideLogo' => self::$hideLogo,
 1584+ 'buildLanguageSelection' => self::buildLanguageSelection(),
 1585+ 'device' => self::$device,
 1586+ 'wgExtensionAssetsPath' => $wgExtensionAssetsPath,
 1587+ 'wgMobileFrontendLogo' => $wgMobileFrontendLogo,
 1588+ );
 1589+ $searchTemplate->setByArray( $options );
 1590+ wfProfileOut( __METHOD__ );
 1591+ return $searchTemplate;
 1592+ }
 1593+
 1594+ public function getApplicationTemplate() {
 1595+ global $wgAppleTouchIcon, $wgExtensionAssetsPath, $wgScriptPath;
 1596+ wfProfileIn( __METHOD__ );
 1597+ $applicationTemplate = new ApplicationTemplate();
 1598+ $options = array(
 1599+ 'dir' => self::$dir,
 1600+ 'code' => self::$code,
 1601+ 'placeholder' => self::$messages['mobile-frontend-placeholder'],
 1602+ 'dismissNotification' => self::$messages['mobile-frontend-dismiss-notification'],
 1603+ 'wgAppleTouchIcon' => $wgAppleTouchIcon,
 1604+ 'isBetaGroupMember' => self::$isBetaGroupMember,
 1605+ 'device' => self::$device,
 1606+ 'wgExtensionAssetsPath' => $wgExtensionAssetsPath,
 1607+ 'wgScriptPath' => $wgScriptPath,
 1608+ 'isFilePage' => self::$isFilePage,
 1609+ 'zeroRatedBanner' => self::$zeroRatedBanner,
 1610+ );
 1611+ $applicationTemplate->setByArray( $options );
 1612+ wfProfileOut( __METHOD__ );
 1613+ return $applicationTemplate;
 1614+ }
 1615+
 1616+ public static function buildLanguageSelection() {
 1617+ global $wgLanguageCode;
 1618+ wfProfileIn( __METHOD__ );
 1619+ $output = Html::openElement( 'select',
 1620+ array( 'id' => 'languageselection',
 1621+ 'onchange' => 'javascript:navigateToLanguageSelection();' ) );
 1622+ foreach ( self::$languageUrls as $languageUrl ) {
 1623+ if ( $languageUrl['lang'] == $wgLanguageCode ) {
 1624+ $output .= Html::element( 'option',
 1625+ array( 'value' => $languageUrl['href'], 'selected' => 'selected' ),
 1626+ $languageUrl['language'] );
 1627+ } else {
 1628+ $output .= Html::element( 'option',
 1629+ array( 'value' => $languageUrl['href'] ),
 1630+ $languageUrl['language'] );
 1631+ }
 1632+ }
 1633+ $output .= Html::closeElement( 'select', array() );
 1634+ wfProfileOut( __METHOD__ );
 1635+ return $output;
 1636+ }
 1637+
 1638+ /**
 1639+ * Sets up the default logo image used in mobile view if none is set
 1640+ */
 1641+ public function setDefaultLogo() {
 1642+ global $wgMobileFrontendLogo, $wgExtensionAssetsPath, $wgMFCustomLogos;
 1643+ wfProfileIn( __METHOD__ );
 1644+ if ( $wgMobileFrontendLogo === false ) {
 1645+ $wgMobileFrontendLogo = $wgExtensionAssetsPath . '/MobileFrontend/stylesheets/images/mw.png';
 1646+ }
 1647+
 1648+ if ( self::$isBetaGroupMember ) {
 1649+ $this->getSite( $site, $lang );
 1650+ if ( is_array( $wgMFCustomLogos ) && isset( $wgMFCustomLogos[0]['site'] ) ) {
 1651+ foreach ( $wgMFCustomLogos as $wgMFCustomLogo ) {
 1652+ if ( isset( $wgMFCustomLogo['site'] ) && $site == $wgMFCustomLogo['site'] ) {
 1653+ if ( isset( $wgMFCustomLogo['logo'] ) ) {
 1654+ $wgMobileFrontendLogo = $wgMFCustomLogo['logo'];
 1655+ }
 1656+ }
 1657+ }
 1658+ }
 1659+ }
 1660+ wfProfileOut( __METHOD__ );
 1661+ }
 1662+
 1663+ public function getVersion() {
 1664+ return __CLASS__ . ': $Id$';
 1665+ }
 1666+}
Property changes on: branches/wmf/1.18wmf1/extensions/MobileFrontend/MobileFrontend.body.php
___________________________________________________________________
Added: svn:eol-style
11667 + native
Added: svn:keywords
21668 + Id
Index: branches/wmf/1.18wmf1/extensions/MobileFrontend/MobileFrontend.php
@@ -30,7 +30,7 @@
3131 $wgExtensionCredits['other'][] = array(
3232 'path' => __FILE__,
3333 'name' => 'MobileFrontend',
34 - 'version' => ExtMobileFrontend::VERSION,
 34+ 'version' => '0.7.0',
3535 'author' => '[http://www.mediawiki.org/wiki/User:Preilly Preilly]',
3636 'descriptionmsg' => 'mobile-frontend-desc',
3737 'url' => 'https://www.mediawiki.org/wiki/Extension:MobileFrontend',
@@ -42,23 +42,24 @@
4343 // autoload extension classes
4444
4545 $autoloadClasses = array (
46 - 'DeviceDetection',
47 - 'CssDetection',
48 - 'MobileFrontendTemplate',
49 - 'ApplicationTemplate',
50 - 'SearchTemplate',
51 - 'FooterTemplate',
52 - 'LeaveFeedbackTemplate',
53 - 'DisableTemplate',
54 - 'OptInTemplate',
55 - 'OptOutTemplate',
56 - 'ApplicationWmlTemplate',
57 - 'ThanksNoticeTemplate',
58 - 'SopaNoticeTemplate',
 46+ 'ExtMobileFrontend' => 'MobileFrontend.body',
 47+ 'DeviceDetection' => 'DeviceDetection',
 48+ 'CssDetection' => 'CssDetection',
 49+ 'MobileFrontendTemplate' => 'MobileFrontendTemplate',
 50+ 'ApplicationTemplate' => 'ApplicationTemplate',
 51+ 'SearchTemplate' => 'SearchTemplate',
 52+ 'FooterTemplate' => 'FooterTemplate',
 53+ 'LeaveFeedbackTemplate' => 'LeaveFeedbackTemplate',
 54+ 'DisableTemplate' => 'DisableTemplate',
 55+ 'OptInTemplate' => 'OptInTemplate',
 56+ 'OptOutTemplate' => 'OptOutTemplate',
 57+ 'ApplicationWmlTemplate' => 'ApplicationWmlTemplate',
 58+ 'ThanksNoticeTemplate' => 'ThanksNoticeTemplate',
 59+ 'SopaNoticeTemplate' => 'SopaNoticeTemplate',
5960 );
6061
61 -foreach ( $autoloadClasses as $class ) {
62 - $wgAutoloadClasses[$class] = $cwd . $class . '.php';
 62+foreach ( $autoloadClasses as $className => $classFilename ) {
 63+ $wgAutoloadClasses[$className] = $cwd . $classFilename . '.php';
6364 }
6465
6566 /**
@@ -111,1667 +112,3 @@
112113 $files[] = dirname( __FILE__ ) . '/tests/DeviceDetectionTest.php';
113114 return true;
114115 }
115 -
116 -class ExtMobileFrontend {
117 - const VERSION = '0.6.1';
118 -
119 - /**
120 - * @var DOMDocument
121 - */
122 - private $doc, $mainPage;
123 - public $contentFormat = '';
124 - public $WMLSectionSeperator = '***************************************************************************';
125 -
126 - /**
127 - * @var Title
128 - */
129 - public static $title;
130 - public static $messages = array();
131 - public static $htmlTitle;
132 - public static $dir;
133 - public static $code;
134 - public static $device;
135 - public static $headings;
136 - public static $mainPageUrl;
137 - public static $randomPageUrl;
138 - public static $requestedSegment;
139 - public static $format;
140 - public static $search;
141 - public static $callback;
142 - public static $useFormat;
143 - public static $disableImages;
144 - public static $enableImages;
145 - public static $isMainPage = false;
146 - public static $searchField;
147 - public static $disableImagesURL;
148 - public static $enableImagesURL;
149 - public static $disableMobileSiteURL;
150 - public static $viewNormalSiteURL;
151 - public static $currentURL;
152 - public static $displayNoticeId;
153 - public static $leaveFeedbackURL;
154 - public static $mobileRedirectFormAction;
155 - public static $isBetaGroupMember = false;
156 - public static $hideSearchBox = false;
157 - public static $hideLogo = false;
158 - public static $hideFooter = false;
159 - public static $languageUrls;
160 - public static $wsLoginToken = '';
161 - public static $wsLoginFormAction = '';
162 - public static $isFilePage;
163 - public static $logoutHtml;
164 - public static $loginHtml;
165 - public static $zeroRatedBanner;
166 -
167 - public static $messageKeys = array(
168 - 'mobile-frontend-show-button',
169 - 'mobile-frontend-hide-button',
170 - 'mobile-frontend-back-to-top-of-section',
171 - 'mobile-frontend-regular-site',
172 - 'mobile-frontend-perm-stop-redirect',
173 - 'mobile-frontend-home-button',
174 - 'mobile-frontend-random-button',
175 - 'mobile-frontend-are-you-sure',
176 - 'mobile-frontend-explain-disable',
177 - 'mobile-frontend-disable-button',
178 - 'mobile-frontend-back-button',
179 - 'mobile-frontend-opt-in-message',
180 - 'mobile-frontend-opt-in-yes-button',
181 - 'mobile-frontend-opt-in-no-button',
182 - 'mobile-frontend-opt-in-title',
183 - 'mobile-frontend-opt-out-message',
184 - 'mobile-frontend-opt-out-yes-button',
185 - 'mobile-frontend-opt-out-no-button',
186 - 'mobile-frontend-opt-out-title',
187 - 'mobile-frontend-opt-in-explain',
188 - 'mobile-frontend-opt-out-explain',
189 - 'mobile-frontend-disable-images',
190 - 'mobile-frontend-wml-continue',
191 - 'mobile-frontend-wml-back',
192 - 'mobile-frontend-enable-images',
193 - 'mobile-frontend-featured-article',
194 - 'mobile-frontend-news-items',
195 - 'mobile-frontend-leave-feedback-title',
196 - 'mobile-frontend-leave-feedback-notice',
197 - 'mobile-frontend-leave-feedback-subject',
198 - 'mobile-frontend-leave-feedback-message',
199 - 'mobile-frontend-leave-feedback-cancel',
200 - 'mobile-frontend-leave-feedback-submit',
201 - 'mobile-frontend-leave-feedback-link-text',
202 - 'mobile-frontend-leave-feedback',
203 - 'mobile-frontend-feedback-page',
204 - 'mobile-frontend-leave-feedback-thanks',
205 - 'mobile-frontend-search-submit',
206 - 'mobile-frontend-language',
207 - 'mobile-frontend-username',
208 - 'mobile-frontend-password',
209 - 'mobile-frontend-login',
210 - 'mobile-frontend-placeholder',
211 - 'mobile-frontend-dismiss-notification',
212 - 'mobile-frontend-sopa-notice',
213 - );
214 -
215 - public $itemsToRemove = array(
216 - '#contentSub',
217 - 'div.messagebox',
218 - '#siteNotice',
219 - '#siteSub',
220 - '#jump-to-nav',
221 - 'div.editsection',
222 - 'div.infobox',
223 - 'table.toc',
224 - '#catlinks',
225 - 'div.stub',
226 - 'form',
227 - 'div.sister-project',
228 - 'script',
229 - 'div.magnify',
230 - '.editsection',
231 - 'span.t',
232 - 'sup[style*="help"]',
233 - '.portal',
234 - '#protected-icon',
235 - '.printfooter',
236 - '.boilerplate',
237 - '#id-articulo-destacado',
238 - '#coordinates',
239 - '#top',
240 - '.hiddenStructure',
241 - '.noprint',
242 - '.medialist',
243 - '.mw-search-createlink',
244 - '#ogg_player_1',
245 - '.nomobile',
246 - );
247 -
248 - /**
249 - * Work out the site and language name from a database name
250 - * @param $site string
251 - * @param $lang string
252 - * @return string
253 - */
254 - public function getSite( &$site, &$lang ) {
255 - global $wgConf;
256 - wfProfileIn( __METHOD__ );
257 - $DB = wfGetDB( DB_MASTER );
258 - $DBName = $DB->getDBname();
259 - list( $site, $lang ) = $wgConf->siteFromDB( $DBName );
260 - wfProfileOut( __METHOD__ );
261 - return true;
262 - }
263 -
264 - /**
265 - * @param $request WebRequest
266 - * @param $title Title
267 - * @param $output OutputPage
268 - * @return bool
269 - * @throws HttpError
270 - */
271 - public function testCanonicalRedirect( $request, $title, $output ) {
272 - global $wgUsePathInfo, $wgMobileDomain;
273 - $xDevice = isset( $_SERVER['HTTP_X_DEVICE'] ) ? $_SERVER['HTTP_X_DEVICE'] : '';
274 - if ( empty( $xDevice ) ) {
275 - return true; // Let the redirect happen
276 - } else {
277 - if ( $title->getNamespace() == NS_SPECIAL ) {
278 - list( $name, $subpage ) = SpecialPage::resolveAlias( $title->getDBkey() );
279 - if ( $name ) {
280 - $title = SpecialPage::getTitleFor( $name, $subpage );
281 - }
282 - }
283 - $targetUrl = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
284 - // Redirect to canonical url, make it a 301 to allow caching
285 - if ( $targetUrl == $request->getFullRequestURL() ) {
286 - $message = "Redirect loop detected!\n\n" .
287 - "This means the wiki got confused about what page was " .
288 - "requested; this sometimes happens when moving a wiki " .
289 - "to a new server or changing the server configuration.\n\n";
290 -
291 - if ( $wgUsePathInfo ) {
292 - $message .= "The wiki is trying to interpret the page " .
293 - "title from the URL path portion (PATH_INFO), which " .
294 - "sometimes fails depending on the web server. Try " .
295 - "setting \"\$wgUsePathInfo = false;\" in your " .
296 - "LocalSettings.php, or check that \$wgArticlePath " .
297 - "is correct.";
298 - } else {
299 - $message .= "Your web server was detected as possibly not " .
300 - "supporting URL path components (PATH_INFO) correctly; " .
301 - "check your LocalSettings.php for a customized " .
302 - "\$wgArticlePath setting and/or toggle \$wgUsePathInfo " .
303 - "to true.";
304 - }
305 - throw new HttpError( 500, $message );
306 - } else {
307 - $parsedUrl = wfParseUrl( $targetUrl );
308 - if ( stristr( $parsedUrl['host'], $wgMobileDomain ) === false ) {
309 - $hostParts = explode( '.', $parsedUrl['host'] );
310 - $parsedUrl['host'] = $hostParts[0] . $wgMobileDomain . $hostParts[1] . '.' . $hostParts[2];
311 - }
312 - $fragmentDelimiter = ( !empty( $parsedUrl['fragment'] ) ) ? '#' : '';
313 - $queryDelimiter = ( !empty( $parsedUrl['query'] ) ) ? '?' : '';
314 - $targetUrl = $parsedUrl['scheme'] . '://' . $parsedUrl['host'] . $parsedUrl['path']
315 - . $queryDelimiter . $parsedUrl['query'] . $fragmentDelimiter . $parsedUrl['fragment'];
316 - $output->setSquidMaxage( 1200 );
317 - $output->redirect( $targetUrl, '301' );
318 - }
319 - return false; // Prevent the redirect from occuring
320 - }
321 - }
322 -
323 - /**
324 - * @param $obj Article
325 - * @param $tpl
326 - * @return bool
327 - */
328 - public function addMobileFooter( &$obj, &$tpl ) {
329 - global $wgRequest;
330 - wfProfileIn( __METHOD__ );
331 -
332 - $title = $obj->getTitle();
333 - $isSpecial = $title->isSpecialPage();
334 -
335 - if ( ! $isSpecial ) {
336 - $footerlinks = $tpl->data['footerlinks'];
337 - $mobileViewUrl = $wgRequest->escapeAppendQuery( 'useformat=mobile' );
338 -
339 - $tpl->set( 'mobileview', "<a href='{$mobileViewUrl}' class='noprint'>" . wfMsg( 'mobile-frontend-view' ) . "</a>" );
340 - $footerlinks['places'][] = 'mobileview';
341 - $tpl->set( 'footerlinks', $footerlinks );
342 - }
343 - wfProfileOut( __METHOD__ );
344 - return true;
345 - }
346 -
347 - /**
348 - * @param $url string
349 - * @param $field string
350 - * @return string
351 - */
352 - private function removeQueryStringParameter( $url, $field ) {
353 - wfProfileIn( __METHOD__ );
354 - $url = preg_replace( '/(.*)(\?|&)' . $field . '=[^&]+?(&)(.*)/i', '$1$2$4', $url . '&' );
355 - $url = substr( $url, 0, -1 );
356 - wfProfileOut( __METHOD__ );
357 - return $url;
358 - }
359 -
360 - public function getMsg() {
361 - global $wgUser, $wgContLang, $wgRequest, $wgServer, $wgMobileRedirectFormAction, $wgMobileDomain, $wgOut, $wgLanguageCode;
362 - wfProfileIn( __METHOD__ );
363 -
364 - self::$disableImagesURL = $wgRequest->escapeAppendQuery( 'disableImages=1' );
365 - self::$enableImagesURL = $wgRequest->escapeAppendQuery( 'enableImages=1' );
366 - self::$disableMobileSiteURL = $wgRequest->escapeAppendQuery( 'mobileaction=disable_mobile_site' );
367 - self::$viewNormalSiteURL = $wgRequest->escapeAppendQuery( 'mobileaction=view_normal_site' );
368 - self::$currentURL = $wgRequest->getFullRequestURL();
369 - self::$leaveFeedbackURL = $wgRequest->escapeAppendQuery( 'mobileaction=leave_feedback' );
370 -
371 - $skin = $wgUser->getSkin();
372 - $copyright = $skin->getCopyright();
373 - if ( stristr( $copyright, '<li class="noprint">' ) !== false ) {
374 - $copyright = '<ul id="footer-info"><li>' . $copyright . '</li></ul>';
375 - }
376 -
377 - // Need to stash the results of the "wfMsg" call before the Output Buffering handler
378 - // because at this point the database connection is shut down, etc.
379 -
380 - self::$messages['mobile-frontend-copyright'] = $copyright;
381 -
382 - foreach ( self::$messageKeys as $messageKey ) {
383 -
384 - if ( $messageKey == 'mobile-frontend-leave-feedback-notice' ) {
385 - $linkText = wfMsg( 'mobile-frontend-leave-feedback-link-text' );
386 - $linkTarget = wfMsgNoTrans( 'mobile-frontend-feedback-page' );
387 - self::$messages[$messageKey] = wfMsgExt( $messageKey, array( 'replaceafter' ), Html::element( 'a', array( 'href' => Title::newFromText( $linkTarget )->getFullURL(), 'target' => '_blank' ), $linkText ) );
388 - } elseif ( $messageKey == 'mobile-frontend-feedback-page' ) {
389 - self::$messages[$messageKey] = wfMsgNoTrans( $messageKey );
390 - } else {
391 - self::$messages[$messageKey] = wfMsg( $messageKey );
392 - }
393 - }
394 -
395 - self::$dir = $wgContLang->getDir();
396 - self::$code = $wgContLang->getCode();
397 -
398 - $languageUrls = array();
399 -
400 - $languageUrls[] = array(
401 - 'href' => self::$currentURL,
402 - 'text' => self::$htmlTitle,
403 - 'language' => $wgContLang->getLanguageName( $wgLanguageCode ),
404 - 'class' => 'interwiki-' . $wgLanguageCode,
405 - 'lang' => $wgLanguageCode,
406 - );
407 -
408 - foreach ( $wgOut->getLanguageLinks() as $l ) {
409 - $tmp = explode( ':', $l, 2 );
410 - $class = 'interwiki-' . $tmp[0];
411 - $lang = $tmp[0];
412 - unset( $tmp );
413 - $nt = Title::newFromText( $l );
414 - if ( $nt ) {
415 - $parsedUrl = wfParseUrl( $nt->getFullURL() );
416 - if ( stristr( $parsedUrl['host'], $wgMobileDomain ) === false ) {
417 - $hostParts = explode( '.', $parsedUrl['host'] );
418 - $parsedUrl['host'] = $hostParts[0] . $wgMobileDomain . $hostParts[1] . '.' . $hostParts[2];
419 - }
420 - $fragmentDelimiter = ( isset( $parsedUrl['fragment'] ) && $parsedUrl['fragment'] !== null ) ? '#' : '';
421 - $queryDelimiter = ( isset( $parsedUrl['query'] ) && $parsedUrl['query'] !== null ) ? '?' : '';
422 -
423 - $languageUrl = $parsedUrl['scheme'] . $parsedUrl['delimiter'] . $parsedUrl['host'] . $parsedUrl['path'];
424 - if ( isset( $parsedUrl['query'] ) ) {
425 - $languageUrl .= $queryDelimiter . $parsedUrl['query'];
426 - }
427 - if ( isset( $parsedUrl['fragment'] ) ) {
428 - $languageUrl .= $fragmentDelimiter . $parsedUrl['fragment'];
429 - }
430 - $languageUrls[] = array(
431 - 'href' => $languageUrl,
432 - 'text' => ( $wgContLang->getLanguageName( $nt->getInterwiki() ) != ''
433 - ? $wgContLang->getLanguageName( $nt->getInterwiki() )
434 - : $l ),
435 - 'language' => $wgContLang->getLanguageName( $lang ),
436 - 'class' => $class,
437 - 'lang' => $lang,
438 - );
439 - }
440 - }
441 -
442 - self::$languageUrls = $languageUrls;
443 -
444 - $nonMobileServerBaseURL = str_replace( $wgMobileDomain, '.', $wgServer );
445 - self::$mobileRedirectFormAction = ( $wgMobileRedirectFormAction !== false )
446 - ? $wgMobileRedirectFormAction
447 - : "{$nonMobileServerBaseURL}/w/mobileRedirect.php";
448 -
449 - self::$mainPageUrl = Title::newMainPage()->getLocalUrl();
450 - self::$randomPageUrl = $this->getRelativeURL( SpecialPage::getTitleFor( 'Randompage' )->getLocalUrl() );
451 - wfProfileOut( __METHOD__ );
452 - return true;
453 - }
454 -
455 - /**
456 - * @param $parsedUrl wfParseUrl Array
457 - * @return string
458 - */
459 - public function parsePageRedirect( $parsedUrl ) {
460 - global $wgMobileDomain;
461 - wfProfileIn( __METHOD__ );
462 - $redirect = '';
463 - $hostParts = explode( '.', $parsedUrl['host'] );
464 - $parsedUrl['host'] = $hostParts[0] . $wgMobileDomain . $hostParts[1] . '.' . $hostParts[2];
465 - $fragmentDelimiter = ( !empty( $parsedUrl['fragment'] ) ) ? '#' : '';
466 - $queryDelimiter = ( !empty( $parsedUrl['query'] ) ) ? '?' : '';
467 - $redirect = $parsedUrl['scheme'] . '://' . $parsedUrl['host'] . $parsedUrl['path'];
468 - if ( isset( $parsedUrl['query'] ) ) {
469 - $redirect .= $queryDelimiter . $parsedUrl['query'];
470 - }
471 - if ( isset( $parsedUrl['fragment'] ) ) {
472 - $redirect .= $fragmentDelimiter . $parsedUrl['fragment'];
473 - }
474 - wfProfileOut( __METHOD__ );
475 - return $redirect;
476 - }
477 -
478 - /**
479 - * @param $out OutputPage
480 - * @param $redirect
481 - * @param $code
482 - * @return bool
483 - */
484 - public function beforePageRedirect( $out, &$redirect, &$code ) {
485 - global $wgMobileDomain;
486 - wfProfileIn( __METHOD__ );
487 - if ( $out->getTitle()->isSpecial( 'Userlogin' ) ) {
488 - $xDevice = isset( $_SERVER['HTTP_X_DEVICE'] ) ? $_SERVER['HTTP_X_DEVICE'] : '';
489 - if ( $xDevice ) {
490 - $parsedUrl = wfParseUrl( $redirect );
491 - if ( stristr( $parsedUrl['host'], $wgMobileDomain ) === false ) {
492 - $hostParts = explode( '.', $parsedUrl['host'] );
493 - $parsedUrl['host'] = $hostParts[0] . $wgMobileDomain . $hostParts[1] . '.' . $hostParts[2];
494 - }
495 - if ( $parsedUrl['scheme'] == 'http' ) {
496 - $parsedUrl['scheme'] = 'https';
497 - }
498 -
499 - $redirect = $this->parsePageRedirect( $parsedUrl );
500 - }
501 - } else if ( $out->getTitle()->isSpecial( 'Randompage' ) ) {
502 - $xDevice = isset( $_SERVER['HTTP_X_DEVICE'] ) ? $_SERVER['HTTP_X_DEVICE'] : '';
503 - if ( $xDevice ) {
504 - $parsedUrl = wfParseUrl( $redirect );
505 - if ( stristr( $parsedUrl['host'], $wgMobileDomain ) === false ) {
506 - $redirect = $this->parsePageRedirect( $parsedUrl );
507 - }
508 - }
509 - }
510 - wfProfileOut( __METHOD__ );
511 - return true;
512 - }
513 -
514 - /**
515 - * @param $out OutputPage
516 - * @param $text String
517 - * @return bool
518 - */
519 - public function beforePageDisplayHTML( &$out, &$text ) {
520 - global $wgContLang, $wgRequest, $wgMemc, $wgUser;
521 - wfProfileIn( __METHOD__ );
522 -
523 - // Note: The WebRequest Class calls are made in this block because
524 - // since PHP 5.1.x, all objects have their destructors called
525 - // before the output buffer callback function executes.
526 - // Thus, globalized objects will not be available as expected in the function.
527 - // This is stated to be intended behavior, as per the following: [http://bugs.php.net/bug.php?id=40104]
528 -
529 - $xDevice = isset( $_SERVER['HTTP_X_DEVICE'] ) ? $_SERVER['HTTP_X_DEVICE'] : '';
530 - self::$useFormat = $wgRequest->getText( 'useformat' );
531 - $mobileAction = $wgRequest->getText( 'mobileaction' );
532 - $action = $wgRequest->getText( 'action' );
533 -
534 - if ( self::$useFormat !== 'mobile' && self::$useFormat !== 'mobile-wap' &&
535 - !$xDevice ) {
536 - wfProfileOut( __METHOD__ );
537 - return true;
538 - }
539 - if ( $action === 'edit' ||
540 - $mobileAction === 'view_normal_site' ) {
541 - wfProfileOut( __METHOD__ );
542 - return true;
543 - }
544 -
545 - $userAgent = $_SERVER['HTTP_USER_AGENT'];
546 - $acceptHeader = isset( $_SERVER["HTTP_ACCEPT"] ) ? $_SERVER["HTTP_ACCEPT"] : '';
547 - $uAmd5 = md5( $userAgent );
548 -
549 - $key = wfMemcKey( 'mobile', 'ua', $uAmd5 );
550 -
551 - $props = null;
552 - try {
553 - $props = $wgMemc->get( $key );
554 - if ( !$props ) {
555 - $wurflConfigFile = RESOURCES_DIR . 'wurfl-config.xml';
556 - $wurflConfig = new WURFL_Configuration_XmlConfig( $wurflConfigFile );
557 - $wurflManagerFactory = new WURFL_WURFLManagerFactory( $wurflConfig );
558 - $wurflManager = $wurflManagerFactory->create();
559 - $device = $wurflManager->getDeviceForHttpRequest( $_SERVER );
560 -
561 - if ( $device->isSpecific() === true ) {
562 - $props = $device->getAllCapabilities();
563 - $wgMemc->set( $key, $props, 86400 );
564 - } else {
565 - $wgMemc->set( $key, 'generic', 86400 );
566 - $props = 'generic';
567 - }
568 - }
569 - } catch ( Exception $e ) {
570 - // echo $e->getMessage();
571 - }
572 -
573 - self::$title = $out->getTitle();
574 -
575 - if ( self::$title->isMainPage() ) {
576 - self::$isMainPage = true;
577 - }
578 - if ( self::$title->getNamespace() == NS_FILE ) {
579 - self::$isFilePage = true;
580 - }
581 -
582 - self::$htmlTitle = $out->getHTMLTitle();
583 - self::$disableImages = $wgRequest->getText( 'disableImages', 0 );
584 - self::$enableImages = $wgRequest->getText( 'enableImages', 0 );
585 - self::$displayNoticeId = $wgRequest->getText( 'noticeid', '' );
586 -
587 - if ( self::$disableImages == 1 ) {
588 - $wgRequest->response()->setcookie( 'disableImages', 1 );
589 - $location = str_replace( '?disableImages=1', '', str_replace( '&disableImages=1', '', $wgRequest->getFullRequestURL() ) );
590 - $location = str_replace( '&mfi=1', '', str_replace( '&mfi=0', '', $location ) );
591 - $location = $this->getRelativeURL( $location );
592 - $wgRequest->response()->header( 'Location: ' . $location . '&mfi=0' );
593 - } elseif ( self::$disableImages == 0 ) {
594 - $disableImages = $wgRequest->getCookie( 'disableImages' );
595 - if ( $disableImages ) {
596 - self::$disableImages = $disableImages;
597 - }
598 - }
599 -
600 - if ( self::$enableImages == 1 ) {
601 - $disableImages = $wgRequest->getCookie( 'disableImages' );
602 - if ( $disableImages ) {
603 - $wgRequest->response()->setcookie( 'disableImages', '' );
604 - }
605 - $location = str_replace( '?enableImages=1', '', str_replace( '&enableImages=1', '', $wgRequest->getFullRequestURL() ) );
606 - $location = str_replace( '&mfi=1', '', str_replace( '&mfi=0', '', $location ) );
607 - $location = $this->getRelativeURL( $location );
608 - $wgRequest->response()->header( 'Location: ' . $location . '&mfi=1' );
609 - }
610 -
611 - self::$format = $wgRequest->getText( 'format' );
612 - self::$callback = $wgRequest->getText( 'callback' );
613 - self::$requestedSegment = $wgRequest->getText( 'seg', 0 );
614 - self::$search = $wgRequest->getText( 'search' );
615 - self::$searchField = $wgRequest->getText( 'search', '' );
616 -
617 - $device = new DeviceDetection();
618 -
619 - if ( $xDevice ) {
620 - $formatName = $xDevice;
621 - } else {
622 - $formatName = $device->formatName( $userAgent, $acceptHeader );
623 - }
624 -
625 - self::$device = $device->format( $formatName );
626 -
627 - if ( self::$device['view_format'] === 'wml' ) {
628 - $this->contentFormat = 'WML';
629 - } elseif ( self::$device['view_format'] === 'html' ) {
630 - $this->contentFormat = 'XHTML';
631 - }
632 -
633 - if ( self::$useFormat === 'mobile-wap' ) {
634 - $this->contentFormat = 'WML';
635 - }
636 -
637 - if ( $mobileAction == 'leave_feedback' ) {
638 - echo $this->renderLeaveFeedbackXHTML();
639 - wfProfileOut( __METHOD__ );
640 - exit();
641 - }
642 -
643 - if ( $mobileAction == 'leave_feedback_post' ) {
644 -
645 - $this->getMsg();
646 -
647 - $subject = $wgRequest->getText( 'subject', '' );
648 - $message = $wgRequest->getText( 'message', '' );
649 - $token = $wgRequest->getText( 'edittoken', '' );
650 -
651 - $title = Title::newFromText( self::$messages['mobile-frontend-feedback-page'] );
652 -
653 - if ( $title->userCan( 'edit' ) &&
654 - !$wgUser->isBlockedFrom( $title ) &&
655 - $wgUser->matchEditToken( $token ) ) {
656 - $article = new Article( $title, 0 );
657 - $rawtext = $article->getRawText();
658 - $rawtext .= "\n== {$subject} == \n {$message} ~~~~ \n <small>User agent: {$userAgent}</small> ";
659 - $article->doEdit( $rawtext, '' );
660 - }
661 -
662 - $location = str_replace( '&mobileaction=leave_feedback_post', '', $wgRequest->getFullRequestURL() . '&noticeid=1&useformat=mobile' );
663 - $location = $this->getRelativeURL( $location );
664 - $wgRequest->response()->header( 'Location: ' . $location );
665 - wfProfileOut( __METHOD__ );
666 - exit();
667 - }
668 -
669 - if ( $mobileAction == 'disable_mobile_site' && $this->contentFormat == 'XHTML' ) {
670 - echo $this->renderDisableMobileSiteXHTML();
671 - wfProfileOut( __METHOD__ );
672 - exit();
673 - }
674 -
675 - if ( $mobileAction == 'opt_in_mobile_site' && $this->contentFormat == 'XHTML' ) {
676 - echo $this->renderOptInMobileSiteXHTML();
677 - wfProfileOut( __METHOD__ );
678 - exit();
679 - }
680 -
681 - if ( $mobileAction == 'opt_out_mobile_site' && $this->contentFormat == 'XHTML' ) {
682 - echo $this->renderOptOutMobileSiteXHTML();
683 - wfProfileOut( __METHOD__ );
684 - exit();
685 - }
686 -
687 - if ( $mobileAction == 'opt_in_cookie' ) {
688 - wfIncrStats( 'mobile.opt_in_cookie_set' );
689 - $this->setOptInOutCookie( '1' );
690 - $this->disableCaching();
691 - $location = wfExpandUrl( Title::newMainPage()->getFullURL(), PROTO_CURRENT );
692 - $wgRequest->response()->header( 'Location: ' . $location );
693 - }
694 -
695 - if ( $mobileAction == 'opt_out_cookie' ) {
696 - $this->setOptInOutCookie( '' );
697 - }
698 -
699 - $this->getMsg();
700 - $this->disableCaching();
701 - $this->sendXDeviceVaryHeader();
702 - $this->sendApplicationVersionVaryHeader();
703 - $this->checkUserStatus();
704 - $this->checkUserLoggedIn();
705 -
706 - if ( self::$title->isSpecial( 'Userlogin' ) && self::$isBetaGroupMember ) {
707 - self::$wsLoginToken = $wgRequest->getSessionData( 'wsLoginToken' );
708 - $q = array( 'action' => 'submitlogin', 'type' => 'login' );
709 - $returnToVal = $wgRequest->getVal( 'returnto' );
710 -
711 - if ( $returnToVal ) {
712 - $q['returnto'] = $returnToVal;
713 - }
714 -
715 - self::$wsLoginFormAction = self::$title->getLocalURL( $q );
716 - }
717 -
718 - $this->setDefaultLogo();
719 - ob_start( array( $this, 'DOMParse' ) );
720 -
721 - wfProfileOut( __METHOD__ );
722 - return true;
723 - }
724 -
725 - /**
726 - * @return bool
727 - */
728 - private function checkUserLoggedIn() {
729 - global $wgUser, $wgCookieDomain, $wgRequest, $wgCookiePrefix;
730 - wfProfileIn( __METHOD__ );
731 - $tempWgCookieDomain = $wgCookieDomain;
732 - $wgCookieDomain = $this->getBaseDomain();
733 - $tempWgCookiePrefix = $wgCookiePrefix;
734 - $wgCookiePrefix = '';
735 -
736 - if ( $wgUser->isLoggedIn() ) {
737 - $wgRequest->response()->setcookie( 'mfsecure', '1', 0, '' );
738 - } else {
739 - $mfSecure = $wgRequest->getCookie( 'mfsecure', '' );
740 - if ( $mfSecure && $mfSecure == '1' ) {
741 - $wgRequest->response()->setcookie( 'mfsecure', '', 0, '' );
742 - }
743 - }
744 -
745 - $wgCookieDomain = $tempWgCookieDomain;
746 - $wgCookiePrefix = $tempWgCookiePrefix;
747 - wfProfileOut( __METHOD__ );
748 - return true;
749 - }
750 -
751 - private function checkUserStatus() {
752 - global $wgRequest;
753 - wfProfileIn( __METHOD__ );
754 -
755 - $hideSearchBox = $wgRequest->getInt( 'hidesearchbox' );
756 -
757 - if ( $hideSearchBox === 1 ) {
758 - self::$hideSearchBox = true;
759 - }
760 -
761 - $hideLogo = $wgRequest->getInt( 'hidelogo' );
762 -
763 - if ( $hideLogo === 1 ) {
764 - self::$hideLogo = true;
765 - }
766 -
767 - if ( !empty( $_SERVER['HTTP_APPLICATION_VERSION'] ) &&
768 - strpos( $_SERVER['HTTP_APPLICATION_VERSION'], 'Wikipedia Mobile' ) !== false ) {
769 - self::$hideSearchBox = true;
770 - if ( strpos( $_SERVER['HTTP_APPLICATION_VERSION'], 'Android' ) !== false ) {
771 - self::$hideLogo = true;
772 - }
773 - }
774 -
775 - if ( self::$hideLogo == true ) {
776 - self::$hideFooter = true;
777 - }
778 -
779 - $optInCookie = $this->getOptInOutCookie();
780 - if ( !empty( $optInCookie ) &&
781 - $optInCookie == 1 ) {
782 - self::$isBetaGroupMember = true;
783 - }
784 - wfProfileOut( __METHOD__ );
785 - return true;
786 - }
787 -
788 - /**
789 - * @param $value string
790 - */
791 - private function setOptInOutCookie( $value ) {
792 - global $wgCookieDomain, $wgRequest, $wgCookiePrefix;
793 - wfProfileIn( __METHOD__ );
794 - $tempWgCookieDomain = $wgCookieDomain;
795 - $wgCookieDomain = $this->getBaseDomain();
796 - $tempWgCookiePrefix = $wgCookiePrefix;
797 - $wgCookiePrefix = '';
798 - $wgRequest->response()->setcookie( 'optin', $value, 0, '' );
799 - $wgCookieDomain = $tempWgCookieDomain;
800 - $wgCookiePrefix = $tempWgCookiePrefix;
801 - wfProfileOut( __METHOD__ );
802 - return true;
803 - }
804 -
805 - /**
806 - * @return Mixed
807 - */
808 - private function getOptInOutCookie() {
809 - global $wgRequest;
810 - wfProfileIn( __METHOD__ );
811 - $optInCookie = $wgRequest->getCookie( 'optin', '' );
812 - wfProfileOut( __METHOD__ );
813 - return $optInCookie;
814 - }
815 -
816 - /**
817 - * @return string
818 - */
819 - private function getBaseDomain() {
820 - wfProfileIn( __METHOD__ );
821 - // Validates value as IP address
822 - if ( !IP::isValid( $_SERVER['HTTP_HOST'] ) ) {
823 - $domainParts = explode( '.', $_SERVER['HTTP_HOST'] );
824 - $domainParts = array_reverse( $domainParts );
825 - // Although some browsers will accept cookies without the initial ., » RFC 2109 requires it to be included.
826 - wfProfileOut( __METHOD__ );
827 - return '.' . $domainParts[1] . '.' . $domainParts[0];
828 - }
829 - wfProfileOut( __METHOD__ );
830 - return $_SERVER['HTTP_HOST'];
831 - }
832 -
833 - /**
834 - * @param $url string
835 - * @return string
836 - */
837 - private function getRelativeURL( $url ) {
838 - wfProfileIn( __METHOD__ );
839 - $parsedUrl = parse_url( $url );
840 - // Validates value as IP address
841 - if ( !empty( $parsedUrl['host'] ) && !IP::isValid( $parsedUrl['host'] ) ) {
842 - $baseUrl = $parsedUrl['scheme'] . '://' . $parsedUrl['host'];
843 - $baseUrl = str_replace( $baseUrl, '', $url );
844 - wfProfileOut( __METHOD__ );
845 - return $baseUrl;
846 - }
847 - wfProfileOut( __METHOD__ );
848 - return $url;
849 - }
850 -
851 - private function disableCaching() {
852 - global $wgRequest;
853 - wfProfileIn( __METHOD__ );
854 - if ( isset( $_SERVER['HTTP_VIA'] ) &&
855 - stripos( $_SERVER['HTTP_VIA'], '.wikimedia.org:3128' ) !== false ) {
856 - $wgRequest->response()->header( 'Cache-Control: no-cache, must-revalidate' );
857 - $wgRequest->response()->header( 'Expires: Sat, 26 Jul 1997 05:00:00 GMT' );
858 - $wgRequest->response()->header( 'Pragma: no-cache' );
859 - }
860 - wfProfileOut( __METHOD__ );
861 - return true;
862 - }
863 -
864 - private function sendXDeviceVaryHeader() {
865 - global $wgOut, $wgRequest;
866 - wfProfileIn( __METHOD__ );
867 - if ( isset( $_SERVER['HTTP_X_DEVICE'] ) ) {
868 - $wgRequest->response()->header( 'X-Device: ' . $_SERVER['HTTP_X_DEVICE'] );
869 - $wgOut->addVaryHeader( 'X-Device' );
870 - }
871 - $wgOut->addVaryHeader( 'Cookie' );
872 - $wgOut->addVaryHeader( 'X-Carrier' );
873 - wfProfileOut( __METHOD__ );
874 - return true;
875 - }
876 -
877 - private function sendApplicationVersionVaryHeader() {
878 - global $wgOut, $wgRequest;
879 - wfProfileIn( __METHOD__ );
880 - $wgOut->addVaryHeader( 'Application_Version' );
881 - if ( isset( $_SERVER['HTTP_APPLICATION_VERSION'] ) ) {
882 - $wgRequest->response()->header( 'Application_Version: ' . $_SERVER['HTTP_APPLICATION_VERSION'] );
883 - } else {
884 - if ( isset( $_SERVER['HTTP_X_DEVICE'] ) ) {
885 - if ( stripos( $_SERVER['HTTP_X_DEVICE'], 'iphone' ) !== false ||
886 - stripos( $_SERVER['HTTP_X_DEVICE'], 'android' ) !== false ) {
887 - $wgRequest->response()->header( 'Application_Version: ' . $_SERVER['HTTP_X_DEVICE'] );
888 - }
889 - }
890 - }
891 - wfProfileOut( __METHOD__ );
892 - return true;
893 - }
894 -
895 - /**
896 - * @return string
897 - */
898 - private function renderLeaveFeedbackXHTML() {
899 - global $wgRequest, $wgUser;
900 - wfProfileIn( __METHOD__ );
901 - if ( $this->contentFormat == 'XHTML' ) {
902 - $this->getMsg();
903 - $searchTemplate = $this->getSearchTemplate();
904 - $searchWebkitHtml = $searchTemplate->getHTML();
905 - $footerTemplate = $this->getFooterTemplate();
906 - $footerHtml = $footerTemplate->getHTML();
907 - $leaveFeedbackTemplate = new LeaveFeedbackTemplate();
908 - $options = array(
909 - 'feedbackPostURL' => str_replace( '&mobileaction=leave_feedback', '', $wgRequest->getFullRequestURL() ) . '&mobileaction=leave_feedback_post',
910 - 'editToken' => $wgUser->editToken(),
911 - 'title' => self::$messages['mobile-frontend-leave-feedback-title'],
912 - 'notice' => self::$messages['mobile-frontend-leave-feedback-notice'],
913 - 'subject' => self::$messages['mobile-frontend-leave-feedback-subject'],
914 - 'message' => self::$messages['mobile-frontend-leave-feedback-message'],
915 - 'cancel' => self::$messages['mobile-frontend-leave-feedback-cancel'],
916 - 'submit' => self::$messages['mobile-frontend-leave-feedback-submit'],
917 - );
918 - $leaveFeedbackTemplate->setByArray( $options );
919 - $leaveFeedbackHtml = $leaveFeedbackTemplate->getHTML();
920 - $contentHtml = $leaveFeedbackHtml;
921 - $applicationTemplate = $this->getApplicationTemplate();
922 - $options = array(
923 - 'htmlTitle' => self::$messages['mobile-frontend-leave-feedback'],
924 - 'searchWebkitHtml' => $searchWebkitHtml,
925 - 'contentHtml' => $contentHtml,
926 - 'footerHtml' => $footerHtml,
927 - );
928 - $applicationTemplate->setByArray( $options );
929 - $applicationHtml = $applicationTemplate->getHTML();
930 - wfProfileOut( __METHOD__ );
931 - return $applicationHtml;
932 - }
933 - wfProfileOut( __METHOD__ );
934 - return '';
935 - }
936 -
937 - /**
938 - * @return string
939 - */
940 - private function renderOptInMobileSiteXHTML() {
941 - wfProfileIn( __METHOD__ );
942 - if ( $this->contentFormat == 'XHTML' ) {
943 - $this->getMsg();
944 - $searchTemplate = $this->getSearchTemplate();
945 - $searchWebkitHtml = $searchTemplate->getHTML();
946 - $footerTemplate = $this->getFooterTemplate();
947 - $footerHtml = $footerTemplate->getHTML();
948 - $optInTemplate = new OptInTemplate();
949 - $options = array(
950 - 'explainOptIn' => self::$messages['mobile-frontend-opt-in-explain'],
951 - 'optInMessage' => self::$messages['mobile-frontend-opt-in-message'],
952 - 'yesButton' => self::$messages['mobile-frontend-opt-in-yes-button'],
953 - 'noButton' => self::$messages['mobile-frontend-opt-in-no-button'],
954 - 'formAction' => wfExpandUrl( Title::newMainPage()->getFullURL(), PROTO_CURRENT ),
955 - );
956 - $optInTemplate->setByArray( $options );
957 - $optInHtml = $optInTemplate->getHTML();
958 - $contentHtml = $optInHtml;
959 - $applicationTemplate = $this->getApplicationTemplate();
960 - $options = array(
961 - 'htmlTitle' => self::$messages['mobile-frontend-opt-in-title'],
962 - 'searchWebkitHtml' => $searchWebkitHtml,
963 - 'contentHtml' => $contentHtml,
964 - 'footerHtml' => $footerHtml,
965 - );
966 - $applicationTemplate->setByArray( $options );
967 - $applicationHtml = $applicationTemplate->getHTML();
968 - wfProfileOut( __METHOD__ );
969 - return $applicationHtml;
970 - }
971 - wfProfileOut( __METHOD__ );
972 - return '';
973 - }
974 -
975 - /**
976 - * @return string
977 - */
978 - private function renderOptOutMobileSiteXHTML() {
979 - wfProfileIn( __METHOD__ );
980 - if ( $this->contentFormat == 'XHTML' ) {
981 - $this->getMsg();
982 - $searchTemplate = $this->getSearchTemplate();
983 - $searchWebkitHtml = $searchTemplate->getHTML();
984 - $footerTemplate = $this->getFooterTemplate();
985 - $footerHtml = $footerTemplate->getHTML();
986 - $optOutTemplate = new OptOutTemplate();
987 - $options = array(
988 - 'explainOptOut' => self::$messages['mobile-frontend-opt-out-explain'],
989 - 'optOutMessage' => self::$messages['mobile-frontend-opt-out-message'],
990 - 'yesButton' => self::$messages['mobile-frontend-opt-out-yes-button'],
991 - 'noButton' => self::$messages['mobile-frontend-opt-out-no-button'],
992 - 'formAction' => wfExpandUrl( Title::newMainPage()->getFullURL(), PROTO_CURRENT ),
993 - );
994 - $optOutTemplate->setByArray( $options );
995 - $optOutHtml = $optOutTemplate->getHTML();
996 - $contentHtml = $optOutHtml;
997 - $applicationTemplate = $this->getApplicationTemplate();
998 - $options = array(
999 - 'htmlTitle' => self::$messages['mobile-frontend-opt-out-title'],
1000 - 'searchWebkitHtml' => $searchWebkitHtml,
1001 - 'contentHtml' => $contentHtml,
1002 - 'footerHtml' => $footerHtml,
1003 - );
1004 - $applicationTemplate->setByArray( $options );
1005 - $applicationHtml = $applicationTemplate->getHTML();
1006 - wfProfileOut( __METHOD__ );
1007 - return $applicationHtml;
1008 - }
1009 - wfProfileOut( __METHOD__ );
1010 - return '';
1011 - }
1012 -
1013 - /**
1014 - * @return string
1015 - */
1016 - private function renderDisableMobileSiteXHTML() {
1017 - wfProfileIn( __METHOD__ );
1018 - if ( $this->contentFormat == 'XHTML' ) {
1019 - $this->getMsg();
1020 - $areYouSure = self::$messages['mobile-frontend-are-you-sure'];
1021 - $explainDisable = self::$messages['mobile-frontend-explain-disable'];
1022 - $disableButton = self::$messages['mobile-frontend-disable-button'];
1023 - $backButton = self::$messages['mobile-frontend-back-button'];
1024 - $htmlTitle = $areYouSure;
1025 - $title = $areYouSure;
1026 - $searchTemplate = $this->getSearchTemplate();
1027 - $searchWebkitHtml = $searchTemplate->getHTML();
1028 - $footerTemplate = $this->getFooterTemplate();
1029 - $footerHtml = $footerTemplate->getHTML();
1030 - $disableTemplate = new DisableTemplate();
1031 - $options = array(
1032 - 'currentURL' => self::$currentURL,
1033 - 'mobileRedirectFormAction' => self::$mobileRedirectFormAction,
1034 - 'areYouSure' => $areYouSure,
1035 - 'explainDisable' => $explainDisable,
1036 - 'disableButton' => $disableButton,
1037 - 'backButton' => $backButton,
1038 - 'htmlTitle' => $htmlTitle,
1039 - 'title' => $title,
1040 - );
1041 - $disableTemplate->setByArray( $options );
1042 - $disableHtml = $disableTemplate->getHTML();
1043 -
1044 - $contentHtml = $disableHtml;
1045 - $applicationTemplate = $this->getApplicationTemplate();
1046 - $options = array(
1047 - 'htmlTitle' => $htmlTitle,
1048 - 'searchWebkitHtml' => $searchWebkitHtml,
1049 - 'contentHtml' => $contentHtml,
1050 - 'footerHtml' => $footerHtml,
1051 - );
1052 - $applicationTemplate->setByArray( $options );
1053 - $applicationHtml = $applicationTemplate->getHTML();
1054 - wfProfileOut( __METHOD__ );
1055 - return $applicationHtml;
1056 - }
1057 - wfProfileOut( __METHOD__ );
1058 - return '';
1059 - }
1060 -
1061 - /**
1062 - * @param $matches array
1063 - * @return string
1064 - */
1065 - private function headingTransformCallbackWML( $matches ) {
1066 - wfProfileIn( __METHOD__ );
1067 - static $headings = 0;
1068 - ++$headings;
1069 -
1070 - $base = $this->WMLSectionSeperator .
1071 - "<h2 class='section_heading' id='section_{$headings}'>{$matches[2]}</h2>";
1072 -
1073 - self::$headings = $headings;
1074 - wfProfileOut( __METHOD__ );
1075 - return $base;
1076 - }
1077 -
1078 - /**
1079 - * @param $matches array
1080 - * @return string
1081 - */
1082 - private function headingTransformCallbackXHTML( $matches ) {
1083 - wfProfileIn( __METHOD__ );
1084 - if ( isset( $matches[0] ) ) {
1085 - preg_match( '/id="([^"]*)"/', $matches[0], $headlineMatches );
1086 - }
1087 -
1088 - $headlineId = ( isset( $headlineMatches[1] ) ) ? $headlineMatches[1] : '';
1089 -
1090 - static $headings = 0;
1091 - $show = self::$messages['mobile-frontend-show-button'];
1092 - $hide = self::$messages['mobile-frontend-hide-button'];
1093 - $backToTop = self::$messages['mobile-frontend-back-to-top-of-section'];
1094 - ++$headings;
1095 - // Back to top link
1096 - $base = Html::openElement( 'div',
1097 - array( 'id' => 'anchor_' . intval( $headings - 1 ),
1098 - 'class' => 'section_anchors', )
1099 - ) .
1100 - Html::rawElement( 'a',
1101 - array( 'href' => '#section_' . intval( $headings - 1 ),
1102 - 'class' => 'back_to_top' ),
1103 - '&#8593;' . $backToTop ) .
1104 - Html::closeElement( 'div' );
1105 - // generate the HTML we are going to inject
1106 - $buttons = Html::element( 'button',
1107 - array( 'class' => 'section_heading show',
1108 - 'section_id' => $headings ),
1109 - $show ) .
1110 - Html::element( 'button',
1111 - array( 'class' => 'section_heading hide',
1112 - 'section_id' => $headings ),
1113 - $hide );
1114 - if ( self::$device['supports_javascript'] ) {
1115 - $h2OnClick = 'javascript:wm_toggle_section(' . $headings . ');';
1116 - $base .= Html::openElement( 'h2',
1117 - array( 'class' => 'section_heading',
1118 - 'id' => 'section_' . $headings,
1119 - 'onclick' => $h2OnClick ) );
1120 - } else {
1121 - $base .= Html::openElement( 'h2',
1122 - array( 'class' => 'section_heading',
1123 - 'id' => 'section_' . $headings ) );
1124 - }
1125 - $base .= $buttons .
1126 - Html::rawElement( 'span',
1127 - array( 'id' => $headlineId ),
1128 - $matches[2] ) .
1129 - Html::closeElement( 'h2' ) .
1130 - Html::openElement( 'div',
1131 - array( 'class' => 'content_block',
1132 - 'id' => 'content_' . $headings ) );
1133 -
1134 - if ( $headings > 1 ) {
1135 - // Close it up here
1136 - $base = Html::closeElement( 'div' ) . $base;
1137 - }
1138 -
1139 - self::$headings = $headings;
1140 - wfProfileOut( __METHOD__ );
1141 - return $base;
1142 - }
1143 -
1144 - /**
1145 - * @param $s string
1146 - * @return string
1147 - */
1148 - public function headingTransform( $s ) {
1149 - wfProfileIn( __METHOD__ );
1150 - $callback = 'headingTransformCallback';
1151 - $callback .= $this->contentFormat;
1152 -
1153 - // Closures are a PHP 5.3 feature.
1154 - // MediaWiki currently requires PHP 5.2.3 or higher.
1155 - // So, using old style for now.
1156 - $s = preg_replace_callback(
1157 - '/<h2(.*)<span class="mw-headline" [^>]*>(.+)<\/span>\w*<\/h2>/',
1158 - array( $this, $callback ),
1159 - $s
1160 - );
1161 -
1162 - // if we had any, make sure to close the whole thing!
1163 - if ( isset( self::$headings ) && self::$headings > 0 ) {
1164 - $s = str_replace(
1165 - '<div class="visualClear">',
1166 - '</div><div class="visualClear">',
1167 - $s
1168 - );
1169 - }
1170 - wfProfileOut( __METHOD__ );
1171 - return $s;
1172 - }
1173 -
1174 - /**
1175 - * @param $s string
1176 - * @return string
1177 - */
1178 - private function createWMLCard( $s ) {
1179 - wfProfileIn( __METHOD__ );
1180 - $segments = explode( $this->WMLSectionSeperator, $s );
1181 - $card = '';
1182 - $idx = 0;
1183 - $requestedSegment = htmlspecialchars( self::$requestedSegment );
1184 - $title = htmlspecialchars( self::$title->getText() );
1185 -
1186 - $card .= "<card id='s{$idx}' title='{$title}'><p>{$segments[$requestedSegment]}</p>";
1187 - $idx = $requestedSegment + 1;
1188 - $segmentsCount = count( $segments );
1189 - $card .= "<p>" . $idx . "/" . $segmentsCount . "</p>";
1190 -
1191 - $useFormatParam = ( isset( self::$useFormat ) ) ? '&amp;' . 'useformat=' . self::$useFormat : '';
1192 -
1193 - // Title::getLocalUrl doesn't work at this point since PHP 5.1.x, all objects have their destructors called
1194 - // before the output buffer callback function executes.
1195 - // Thus, globalized objects will not be available as expected in the function.
1196 - // This is stated to be intended behavior, as per the following: [http://bugs.php.net/bug.php?id=40104]
1197 - $mDefaultQuery = $_GET;
1198 - unset( $mDefaultQuery['seg'] );
1199 - unset( $mDefaultQuery['useformat'] );
1200 -
1201 - $qs = wfArrayToCGI( $mDefaultQuery );
1202 - $delimiter = ( !empty( $qs ) ) ? '?' : '';
1203 - $basePageParts = wfParseUrl( self::$currentURL );
1204 - $basePage = $basePageParts['scheme'] . $basePageParts['delimiter'] . $basePageParts['host'] . $basePageParts['path'] . $delimiter . $qs;
1205 - $appendDelimiter = ( $delimiter === '?' ) ? '&amp;' : '?';
1206 -
1207 - if ( $idx < $segmentsCount ) {
1208 - $card .= "<p><a href=\"{$basePage}{$appendDelimiter}seg={$idx}{$useFormatParam}\">" . self::$messages['mobile-frontend-wml-continue'] . "</a></p>";
1209 - }
1210 -
1211 - if ( $idx > 1 ) {
1212 - $back_idx = $requestedSegment - 1;
1213 - $card .= "<p><a href=\"{$basePage}{$appendDelimiter}seg={$back_idx}{$useFormatParam}\">" . self::$messages['mobile-frontend-wml-back'] . "</a></p>";
1214 - }
1215 -
1216 - $card .= '</card>';
1217 - wfProfileOut( __METHOD__ );
1218 - return $card;
1219 - }
1220 -
1221 - /**
1222 - * @return array
1223 - */
1224 - private function parseItemsToRemove() {
1225 - global $wgMFRemovableClasses;
1226 - wfProfileIn( __METHOD__ );
1227 - $itemToRemoveRecords = array();
1228 -
1229 - foreach ( array_merge( $this->itemsToRemove, $wgMFRemovableClasses ) as $itemToRemove ) {
1230 - $type = '';
1231 - $rawName = '';
1232 - CssDetection::detectIdCssOrTag( $itemToRemove, $type, $rawName );
1233 - $itemToRemoveRecords[$type][] = $rawName;
1234 - }
1235 -
1236 - wfProfileOut( __METHOD__ );
1237 - return $itemToRemoveRecords;
1238 - }
1239 -
1240 - /**
1241 - * @param $html string
1242 - * @return string
1243 - */
1244 - public function DOMParseMainPage( $html ) {
1245 - wfProfileIn( __METHOD__ );
1246 - $html = mb_convert_encoding( $html, 'HTML-ENTITIES', "UTF-8" );
1247 - libxml_use_internal_errors( true );
1248 - $this->mainPage = new DOMDocument();
1249 - // It seems that loadhtml() does not "attach" the html dtd that defines id as an id-attribute to the DOM.
1250 - $this->mainPage->loadHTML( '<?xml encoding="UTF-8"><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
1251 - <html><head><title></title></head><body>' . $html . '</body></html>' );
1252 - libxml_use_internal_errors( false );
1253 - $this->mainPage->preserveWhiteSpace = false;
1254 - $this->mainPage->strictErrorChecking = false;
1255 - $this->mainPage->encoding = 'UTF-8';
1256 -
1257 - $zeroLandingPage = $this->mainPage->getElementById( 'zero-landing-page' );
1258 - $featuredArticle = $this->mainPage->getElementById( 'mp-tfa' );
1259 - $newsItems = $this->mainPage->getElementById( 'mp-itn' );
1260 -
1261 - $xpath = new DOMXpath( $this->mainPage );
1262 - $elements = $xpath->query( '//*[starts-with(@id, "mf-")]' );
1263 -
1264 - $commonAttributes = array( 'mp-tfa', 'mp-itn' );
1265 -
1266 - $content = $this->mainPage->createElement( 'div' );
1267 - $content->setAttribute( 'id', 'content' );
1268 -
1269 - if ( $zeroLandingPage ) {
1270 - $content->appendChild( $zeroLandingPage );
1271 - }
1272 -
1273 - if ( $featuredArticle ) {
1274 - $h2FeaturedArticle = $this->mainPage->createElement( 'h2', self::$messages['mobile-frontend-featured-article'] );
1275 - $content->appendChild( $h2FeaturedArticle );
1276 - $content->appendChild( $featuredArticle );
1277 - }
1278 -
1279 - if ( $newsItems ) {
1280 - $h2NewsItems = $this->mainPage->createElement( 'h2', self::$messages['mobile-frontend-news-items'] );
1281 - $content->appendChild( $h2NewsItems );
1282 - $content->appendChild( $newsItems );
1283 - }
1284 -
1285 - foreach ( $elements as $element ) {
1286 - if ( $element->hasAttribute( 'id' ) ) {
1287 - $id = $element->getAttribute( 'id' );
1288 - if ( !in_array( $id, $commonAttributes ) ) {
1289 - $elementTitle = $element->hasAttribute( 'title' ) ? $element->getAttribute( 'title' ) : '';
1290 - $h2UnknownMobileSection = $this->mainPage->createElement( 'h2', $elementTitle );
1291 - $br = $this->mainPage->createElement( 'br' );
1292 - $br->setAttribute( 'CLEAR', 'ALL' );
1293 - $content->appendChild( $h2UnknownMobileSection );
1294 - $content->appendChild( $element );
1295 - $content->appendChild( $br );
1296 - }
1297 - }
1298 - }
1299 -
1300 - $contentHtml = $this->mainPage->saveXML( $content, LIBXML_NOEMPTYTAG );
1301 - wfProfileOut( __METHOD__ );
1302 - return $contentHtml;
1303 - }
1304 -
1305 - /**
1306 - * @return DomElement
1307 - */
1308 - public function renderLogin() {
1309 - wfProfileIn( __METHOD__ );
1310 - $form = Html::openElement( 'form',
1311 - array( 'name' => 'userlogin',
1312 - 'method' => 'post',
1313 - 'action' => self::$wsLoginFormAction ) ) .
1314 - Html::openElement( 'table',
1315 - array( 'class' => 'user-login' ) ) .
1316 - Html::openElement( 'tbody' ) .
1317 - Html::openElement( 'tr' ) .
1318 - Html::openElement( 'td',
1319 - array( 'class' => 'mw-label' ) ) .
1320 - Html::element( 'label',
1321 - array( 'for' => 'wpName1' ), self::$messages['mobile-frontend-username'] ) .
1322 - Html::closeElement( 'td' ) .
1323 - Html::closeElement( 'tr' ) .
1324 - Html::openElement( 'tr' ) .
1325 - Html::openElement( 'td' ) .
1326 - Html::input( 'wpName', null, 'text',
1327 - array( 'class' => 'loginText',
1328 - 'id' => 'wpName1',
1329 - 'tabindex' => '1',
1330 - 'size' => '20',
1331 - 'required' ) ) .
1332 - Html::closeElement( 'td' ) .
1333 - Html::closeElement( 'tr' ) .
1334 - Html::openElement( 'tr' ) .
1335 - Html::openElement( 'td',
1336 - array( 'class' => 'mw-label' ) ) .
1337 - Html::element( 'label',
1338 - array( 'for' => 'wpPassword1' ), self::$messages['mobile-frontend-password'] ) .
1339 - Html::closeElement( 'td' ) .
1340 - Html::closeElement( 'tr' ) .
1341 - Html::openElement( 'tr' ) .
1342 - Html::openElement( 'td',
1343 - array( 'class' => 'mw-input' ) ) .
1344 - Html::input( 'wpPassword', null, 'password',
1345 - array( 'class' => 'loginPassword',
1346 - 'id' => 'wpPassword1',
1347 - 'tabindex' => '2',
1348 - 'size' => '20' ) ) .
1349 - Html::closeElement( 'td' ) .
1350 - Html::closeElement( 'tr' ) .
1351 - Html::openElement( 'tr' ) .
1352 - Html::element( 'td' ) .
1353 - Html::closeElement( 'tr' ) .
1354 - Html::openElement( 'tr' ) .
1355 - Html::openElement( 'td',
1356 - array( 'class' => 'mw-submit' ) ) .
1357 - Html::input( 'wpLoginAttempt', self::$messages['mobile-frontend-login'], 'submit',
1358 - array( 'id' => 'wpLoginAttempt',
1359 - 'tabindex' => '3' ) ) .
1360 - Html::closeElement( 'td' ) .
1361 - Html::closeElement( 'tr' ) .
1362 - Html::closeElement( 'tbody' ) .
1363 - Html::closeElement( 'table' ) .
1364 - Html::input( 'wpLoginToken', self::$wsLoginToken, 'hidden' ) .
1365 - Html::closeElement( 'form' );
1366 - wfProfileOut( __METHOD__ );
1367 - return $this->getDomDocumentNodeByTagName( $form, 'form' );
1368 - }
1369 -
1370 - /**
1371 - * @param $html string
1372 - * @param $tagName string
1373 - * @return DomElement
1374 - */
1375 - private function getDomDocumentNodeByTagName( $html, $tagName ) {
1376 - wfProfileIn( __METHOD__ );
1377 - libxml_use_internal_errors( true );
1378 - $dom = new DOMDocument();
1379 - $dom->loadHTML( $html );
1380 - libxml_use_internal_errors( false );
1381 - $dom->preserveWhiteSpace = false;
1382 - $dom->strictErrorChecking = false;
1383 - $dom->encoding = 'UTF-8';
1384 - $node = $dom->getElementsByTagName( $tagName )->item( 0 );
1385 - wfProfileOut( __METHOD__ );
1386 - return $node;
1387 - }
1388 -
1389 - /**
1390 - * @param $html string
1391 - * @return string
1392 - */
1393 - public function DOMParse( $html ) {
1394 - global $wgScript;
1395 - wfProfileIn( __METHOD__ );
1396 - $html = mb_convert_encoding( $html, 'HTML-ENTITIES', "UTF-8" );
1397 - libxml_use_internal_errors( true );
1398 - $this->doc = new DOMDocument();
1399 - $this->doc->loadHTML( '<?xml encoding="UTF-8">' . $html );
1400 - libxml_use_internal_errors( false );
1401 - $this->doc->preserveWhiteSpace = false;
1402 - $this->doc->strictErrorChecking = false;
1403 - $this->doc->encoding = 'UTF-8';
1404 -
1405 - $itemToRemoveRecords = $this->parseItemsToRemove();
1406 -
1407 - $zeroRatedBannerElement = $this->doc->getElementById( 'zero-rated-banner' );
1408 -
1409 - if ( !$zeroRatedBannerElement ) {
1410 - $zeroRatedBannerElement = $this->doc->getElementById( 'zero-rated-banner-red' );
1411 - }
1412 -
1413 - if ( $zeroRatedBannerElement ) {
1414 - self::$zeroRatedBanner = $this->doc->saveXML( $zeroRatedBannerElement, LIBXML_NOEMPTYTAG );
1415 - }
1416 -
1417 - if ( self::$isBetaGroupMember ) {
1418 - $ptLogout = $this->doc->getElementById( 'pt-logout' );
1419 -
1420 - if ( $ptLogout ) {
1421 - $ptLogoutLink = $ptLogout->firstChild;
1422 - self::$logoutHtml = $this->doc->saveXML( $ptLogoutLink, LIBXML_NOEMPTYTAG );
1423 - }
1424 - $ptAnonLogin = $this->doc->getElementById( 'pt-anonlogin' );
1425 -
1426 - if ( !$ptAnonLogin ) {
1427 - $ptAnonLogin = $this->doc->getElementById( 'pt-login' );
1428 - }
1429 -
1430 - if ( $ptAnonLogin ) {
1431 - $ptAnonLoginLink = $ptAnonLogin->firstChild;
1432 - if ( $ptAnonLoginLink && $ptAnonLoginLink->hasAttributes() ) {
1433 - $ptAnonLoginLinkHref = $ptAnonLoginLink->getAttributeNode( 'href' );
1434 - $ptAnonLoginLinkTitle = $ptAnonLoginLink->getAttributeNode( 'title' );
1435 - if ( $ptAnonLoginLinkTitle ) {
1436 - $ptAnonLoginLinkTitle->nodeValue = self::$messages['mobile-frontend-login'];
1437 - }
1438 - if ( $ptAnonLoginLinkHref ) {
1439 - $ptAnonLoginLinkHref->nodeValue = str_replace( "&", "&amp;", $ptAnonLoginLinkHref->nodeValue );
1440 - }
1441 - $ptAnonLoginLinkText = $ptAnonLoginLink->firstChild;
1442 - if ( $ptAnonLoginLinkText ) {
1443 - $ptAnonLoginLinkText->nodeValue = self::$messages['mobile-frontend-login'];
1444 - }
1445 - }
1446 - self::$loginHtml = $this->doc->saveXML( $ptAnonLoginLink, LIBXML_NOEMPTYTAG );
1447 - }
1448 - }
1449 -
1450 - if ( self::$title->isSpecial( 'Userlogin' ) && self::$isBetaGroupMember ) {
1451 - $userlogin = $this->doc->getElementById( 'userloginForm' );
1452 -
1453 - if ( $userlogin && get_class( $userlogin ) === 'DOMElement' ) {
1454 - $firstHeading = $this->doc->getElementById( 'firstHeading' );
1455 - if ( $firstHeading ) {
1456 - $firstHeading->nodeValue = '';
1457 - }
1458 - }
1459 - }
1460 -
1461 - // Tags
1462 -
1463 - // You can't remove DOMNodes from a DOMNodeList as you're iterating
1464 - // over them in a foreach loop. It will seemingly leave the internal
1465 - // iterator on the foreach out of wack and results will be quite
1466 - // strange. Though, making a queue of items to remove seems to work.
1467 - // For example:
1468 -
1469 - if ( self::$disableImages == 1 ) {
1470 - $itemToRemoveRecords['TAG'][] = "img";
1471 - $itemToRemoveRecords['TAG'][] = "audio";
1472 - $itemToRemoveRecords['TAG'][] = "video";
1473 - $itemToRemoveRecords['CLASS'][] = "thumb tright";
1474 - $itemToRemoveRecords['CLASS'][] = "thumb tleft";
1475 - $itemToRemoveRecords['CLASS'][] = "thumbcaption";
1476 - $itemToRemoveRecords['CLASS'][] = "gallery";
1477 - }
1478 -
1479 - $tagToRemoveNodeIdAttributeValues = array( 'zero-language-search' );
1480 -
1481 - $domElemsToRemove = array();
1482 - foreach ( $itemToRemoveRecords['TAG'] as $tagToRemove ) {
1483 - $tagToRemoveNodes = $this->doc->getElementsByTagName( $tagToRemove );
1484 - foreach ( $tagToRemoveNodes as $tagToRemoveNode ) {
1485 - $tagToRemoveNodeIdAttributeValue = '';
1486 - if ( $tagToRemoveNode ) {
1487 - $tagToRemoveNodeIdAttribute = $tagToRemoveNode->getAttributeNode( 'id' );
1488 - if ( $tagToRemoveNodeIdAttribute ) {
1489 - $tagToRemoveNodeIdAttributeValue = $tagToRemoveNodeIdAttribute->value;
1490 - }
1491 - if ( !in_array( $tagToRemoveNodeIdAttributeValue, $tagToRemoveNodeIdAttributeValues ) ) {
1492 - $domElemsToRemove[] = $tagToRemoveNode;
1493 - }
1494 - }
1495 - }
1496 - }
1497 -
1498 - foreach ( $domElemsToRemove as $domElement ) {
1499 - $domElement->parentNode->removeChild( $domElement );
1500 - }
1501 -
1502 - // Elements with named IDs
1503 - foreach ( $itemToRemoveRecords['ID'] as $itemToRemove ) {
1504 - $itemToRemoveNode = $this->doc->getElementById( $itemToRemove );
1505 - if ( $itemToRemoveNode ) {
1506 - $itemToRemoveNode->parentNode->removeChild( $itemToRemoveNode );
1507 - }
1508 - }
1509 -
1510 - // CSS Classes
1511 - $xpath = new DOMXpath( $this->doc );
1512 - foreach ( $itemToRemoveRecords['CLASS'] as $classToRemove ) {
1513 - $elements = $xpath->query( '//*[@class="' . $classToRemove . '"]' );
1514 -
1515 - foreach ( $elements as $element ) {
1516 - $element->parentNode->removeChild( $element );
1517 - }
1518 - }
1519 -
1520 - // Tags with CSS Classes
1521 - foreach ( $itemToRemoveRecords['TAG_CLASS'] as $classToRemove ) {
1522 - $parts = explode( '.', $classToRemove );
1523 -
1524 - $elements = $xpath->query(
1525 - '//' . $parts[0] . '[@class="' . $parts[1] . '"]'
1526 - );
1527 -
1528 - foreach ( $elements as $element ) {
1529 - $removedElement = $element->parentNode->removeChild( $element );
1530 - }
1531 - }
1532 -
1533 - // Handle red links with action equal to edit
1534 - $redLinks = $xpath->query( '//a[@class="new"]' );
1535 - foreach ( $redLinks as $redLink ) {
1536 - // PHP Bug #36795 — Inappropriate "unterminated entity reference"
1537 - $spanNode = $this->doc->createElement( "span", str_replace( "&", "&amp;", $redLink->nodeValue ) );
1538 -
1539 - if ( $redLink->hasAttributes() ) {
1540 - $attributes = $redLink->attributes;
1541 - foreach ( $attributes as $i => $attribute ) {
1542 - if ( $attribute->name != 'href' ) {
1543 - $spanNode->setAttribute( $attribute->name, $attribute->value );
1544 - }
1545 - }
1546 - }
1547 -
1548 - $redLink->parentNode->replaceChild( $spanNode, $redLink );
1549 - }
1550 -
1551 - if ( self::$title->isSpecial( 'Userlogin' ) && self::$isBetaGroupMember ) {
1552 - if ( $userlogin && get_class( $userlogin ) === 'DOMElement' ) {
1553 - $login = $this->renderLogin();
1554 - $loginNode = $this->doc->importNode( $login, true );
1555 - $userlogin->appendChild( $loginNode );
1556 - }
1557 - }
1558 -
1559 - $content = $this->doc->getElementById( 'content' );
1560 -
1561 - $contentHtml = $this->doc->saveXML( $content, LIBXML_NOEMPTYTAG );
1562 -
1563 - if ( self::$isMainPage ) {
1564 - $contentHtml = $this->DOMParseMainPage( $contentHtml );
1565 - }
1566 -
1567 - $title = htmlspecialchars( self::$title->getText() );
1568 - $htmlTitle = htmlspecialchars( self::$htmlTitle );
1569 -
1570 - if ( strlen( $contentHtml ) > 4000 && $this->contentFormat == 'XHTML'
1571 - && self::$device['supports_javascript'] === true
1572 - && empty( self::$search ) && !self::$isMainPage ) {
1573 - $contentHtml = $this->headingTransform( $contentHtml );
1574 - } elseif ( $this->contentFormat == 'WML' ) {
1575 - header( 'Content-Type: text/vnd.wap.wml' );
1576 - $contentHtml = $this->headingTransform( $contentHtml );
1577 -
1578 - // Content removal for WML rendering
1579 - $elements = array( 'span', 'div', 'sup', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'sup', 'sub' );
1580 - foreach ( $elements as $element ) {
1581 - $contentHtml = preg_replace( '#</?' . $element . '[^>]*>#is', '', $contentHtml );
1582 - }
1583 -
1584 - // Wml for searching
1585 - $searchWml = '<p><input emptyok="true" format="*M" type="text" name="search" value="" size="16" />' .
1586 - '<do type="accept" label="' . self::$messages['mobile-frontend-search-submit'] . '">' .
1587 - '<go href="' . $wgScript . '?title=Special%3ASearch&amp;search=$(search)"></go></do></p>';
1588 - $contentHtml = $searchWml . $contentHtml;
1589 - // Content wrapping
1590 - $contentHtml = $this->createWMLCard( $contentHtml );
1591 - $applicationWmlTemplate = new ApplicationWmlTemplate();
1592 - $options = array(
1593 - 'mainPageUrl' => self::$mainPageUrl,
1594 - 'randomPageUrl' => self::$randomPageUrl,
1595 - 'dir' => self::$dir,
1596 - 'code' => self::$code,
1597 - 'contentHtml' => $contentHtml,
1598 - 'homeButton' => self::$messages['mobile-frontend-home-button'],
1599 - 'randomButton' => self::$messages['mobile-frontend-random-button'],
1600 - );
1601 - $applicationWmlTemplate->setByArray( $options );
1602 - $applicationHtml = $applicationWmlTemplate->getHTML();
1603 - }
1604 -
1605 - if ( $this->contentFormat == 'XHTML' && self::$format != 'json' ) {
1606 - if ( !empty( self::$displayNoticeId ) ) {
1607 - if ( intval( self::$displayNoticeId ) === 1 ) {
1608 - $thanksNoticeTemplate = new ThanksNoticeTemplate();
1609 - $thanksNoticeTemplate->set( 'messages', self::$messages );
1610 - $noticeHtml = $thanksNoticeTemplate->getHTML();
1611 - }
1612 - }
1613 -
1614 - if ( !empty( self::$displayNoticeId ) ) {
1615 - if ( intval( self::$displayNoticeId ) === 2 ) {
1616 - $sopaNoticeTemplate = new SopaNoticeTemplate();
1617 - $sopaNoticeTemplate->set( 'messages', self::$messages );
1618 - $noticeHtml = $sopaNoticeTemplate->getHTML();
1619 - }
1620 - }
1621 -
1622 - // header( 'Content-Type: application/xhtml+xml; charset=utf-8' );
1623 - $searchTemplate = $this->getSearchTemplate();
1624 - $searchWebkitHtml = $searchTemplate->getHTML();
1625 - $footerTemplate = $this->getFooterTemplate();
1626 - $footerHtml = $footerTemplate->getHTML();
1627 - $noticeHtml = ( !empty( $noticeHtml ) ) ? $noticeHtml : '';
1628 - $applicationTemplate = $this->getApplicationTemplate();
1629 - $options = array(
1630 - 'noticeHtml' => $noticeHtml,
1631 - 'htmlTitle' => $htmlTitle,
1632 - 'searchWebkitHtml' => $searchWebkitHtml,
1633 - 'contentHtml' => $contentHtml,
1634 - 'footerHtml' => $footerHtml,
1635 - );
1636 - $applicationTemplate->setByArray( $options );
1637 - $applicationHtml = $applicationTemplate->getHTML();
1638 - }
1639 -
1640 - if ( self::$format === 'json' ) {
1641 - header( 'Content-Type: application/javascript' );
1642 - header( 'Content-Disposition: attachment; filename="data.js";' );
1643 - $json_data = array();
1644 - $json_data['title'] = htmlspecialchars ( self::$title->getText() );
1645 - $json_data['html'] = $contentHtml;
1646 -
1647 - $json = FormatJson::encode( $json_data );
1648 -
1649 - if ( !empty( self::$callback ) ) {
1650 - $json = urlencode( htmlspecialchars( self::$callback ) ) . '(' . $json . ')';
1651 - }
1652 -
1653 - wfProfileOut( __METHOD__ );
1654 - return $json;
1655 - }
1656 -
1657 - wfProfileOut( __METHOD__ );
1658 - return $applicationHtml;
1659 - }
1660 -
1661 - public function getFooterTemplate() {
1662 - wfProfileIn( __METHOD__ );
1663 - $footerTemplate = new FooterTemplate();
1664 - $logoutHtml = ( self::$logoutHtml ) ? self::$logoutHtml : '';
1665 - $loginHtml = ( self::$loginHtml ) ? self::$loginHtml : '';
1666 - $options = array(
1667 - 'messages' => self::$messages,
1668 - 'leaveFeedbackURL' => self::$leaveFeedbackURL,
1669 - 'disableMobileSiteURL' => self::$disableMobileSiteURL,
1670 - 'viewNormalSiteURL' => self::$viewNormalSiteURL,
1671 - 'disableImages' => self::$disableImages,
1672 - 'disableImagesURL' => self::$disableImagesURL,
1673 - 'enableImagesURL' => self::$enableImagesURL,
1674 - 'logoutHtml' => $logoutHtml,
1675 - 'loginHtml' => $loginHtml,
1676 - 'code' => self::$code,
1677 - 'hideFooter' => self::$hideFooter,
1678 - 'isBetaGroupMember' => self::$isBetaGroupMember,
1679 - );
1680 - $footerTemplate->setByArray( $options );
1681 - wfProfileOut( __METHOD__ );
1682 - return $footerTemplate;
1683 - }
1684 -
1685 - public function getSearchTemplate() {
1686 - global $wgExtensionAssetsPath, $wgMobileFrontendLogo;
1687 - wfProfileIn( __METHOD__ );
1688 - $searchTemplate = new SearchTemplate();
1689 - $options = array(
1690 - 'searchField' => self::$searchField,
1691 - 'mainPageUrl' => self::$mainPageUrl,
1692 - 'randomPageUrl' => self::$randomPageUrl,
1693 - 'messages' => self::$messages,
1694 - 'hideSearchBox' => self::$hideSearchBox,
1695 - 'hideLogo' => self::$hideLogo,
1696 - 'buildLanguageSelection' => self::buildLanguageSelection(),
1697 - 'device' => self::$device,
1698 - 'wgExtensionAssetsPath' => $wgExtensionAssetsPath,
1699 - 'wgMobileFrontendLogo' => $wgMobileFrontendLogo,
1700 - );
1701 - $searchTemplate->setByArray( $options );
1702 - wfProfileOut( __METHOD__ );
1703 - return $searchTemplate;
1704 - }
1705 -
1706 - public function getApplicationTemplate() {
1707 - global $wgAppleTouchIcon, $wgExtensionAssetsPath, $wgScriptPath;
1708 - wfProfileIn( __METHOD__ );
1709 - $applicationTemplate = new ApplicationTemplate();
1710 - $options = array(
1711 - 'dir' => self::$dir,
1712 - 'code' => self::$code,
1713 - 'placeholder' => self::$messages['mobile-frontend-placeholder'],
1714 - 'dismissNotification' => self::$messages['mobile-frontend-dismiss-notification'],
1715 - 'wgAppleTouchIcon' => $wgAppleTouchIcon,
1716 - 'isBetaGroupMember' => self::$isBetaGroupMember,
1717 - 'device' => self::$device,
1718 - 'wgExtensionAssetsPath' => $wgExtensionAssetsPath,
1719 - 'wgScriptPath' => $wgScriptPath,
1720 - 'isFilePage' => self::$isFilePage,
1721 - 'zeroRatedBanner' => self::$zeroRatedBanner,
1722 - );
1723 - $applicationTemplate->setByArray( $options );
1724 - wfProfileOut( __METHOD__ );
1725 - return $applicationTemplate;
1726 - }
1727 -
1728 - public static function buildLanguageSelection() {
1729 - global $wgLanguageCode;
1730 - wfProfileIn( __METHOD__ );
1731 - $output = Html::openElement( 'select',
1732 - array( 'id' => 'languageselection',
1733 - 'onchange' => 'javascript:navigateToLanguageSelection();' ) );
1734 - foreach ( self::$languageUrls as $languageUrl ) {
1735 - if ( $languageUrl['lang'] == $wgLanguageCode ) {
1736 - $output .= Html::element( 'option',
1737 - array( 'value' => $languageUrl['href'], 'selected' => 'selected' ),
1738 - $languageUrl['language'] );
1739 - } else {
1740 - $output .= Html::element( 'option',
1741 - array( 'value' => $languageUrl['href'] ),
1742 - $languageUrl['language'] );
1743 - }
1744 - }
1745 - $output .= Html::closeElement( 'select', array() );
1746 - wfProfileOut( __METHOD__ );
1747 - return $output;
1748 - }
1749 -
1750 - /**
1751 - * Sets up the default logo image used in mobile view if none is set
1752 - */
1753 - public function setDefaultLogo() {
1754 - global $wgMobileFrontendLogo, $wgExtensionAssetsPath, $wgMFCustomLogos;
1755 - wfProfileIn( __METHOD__ );
1756 - if ( $wgMobileFrontendLogo === false ) {
1757 - $wgMobileFrontendLogo = $wgExtensionAssetsPath . '/MobileFrontend/stylesheets/images/mw.png';
1758 - }
1759 -
1760 - if ( self::$isBetaGroupMember ) {
1761 - $this->getSite( $site, $lang );
1762 - if ( is_array( $wgMFCustomLogos ) && isset( $wgMFCustomLogos[0]['site'] ) ) {
1763 - foreach ( $wgMFCustomLogos as $wgMFCustomLogo ) {
1764 - if ( isset( $wgMFCustomLogo['site'] ) && $site == $wgMFCustomLogo['site'] ) {
1765 - if ( isset( $wgMFCustomLogo['logo'] ) ) {
1766 - $wgMobileFrontendLogo = $wgMFCustomLogo['logo'];
1767 - }
1768 - }
1769 - }
1770 - }
1771 - }
1772 - wfProfileOut( __METHOD__ );
1773 - }
1774 -
1775 - public function getVersion() {
1776 - return __CLASS__ . ': $Id$';
1777 - }
1778 -}
Property changes on: branches/wmf/1.18wmf1/extensions/MobileFrontend/MobileFrontend.php
___________________________________________________________________
Deleted: svn:keywords
1779116 - Id
Modified: svn:mergeinfo
1780117 Merged /trunk/extensions/MobileFrontend/MobileFrontend.php:r109927-109928
Property changes on: branches/wmf/1.18wmf1/extensions/MobileFrontend
___________________________________________________________________
Modified: svn:mergeinfo
1781118 Merged /trunk/extensions/MobileFrontend:r109927-109928

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r109927Redoing r109884 using svn copyreedy14:41, 24 January 2012
r109928Fix borked characters from r109927reedy14:44, 24 January 2012

Status & tagging log