r109926 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r109925‎ | r109926 | r109927 >
Date:14:38, 24 January 2012
Author:reedy
Status:ok
Tags:mobile 
Comment:
Revert r109884 to redo respecting history
Modified paths:
  • /trunk/extensions/MobileFrontend/MobileFrontend.body.php (deleted) (history)
  • /trunk/extensions/MobileFrontend/MobileFrontend.php (modified) (history)

Diff [purge]

Index: trunk/extensions/MobileFrontend/MobileFrontend.body.php
@@ -1,1665 +0,0 @@
2 -<?php
3 -
4 -class ExtMobileFrontend {
5 - const VERSION = '0.7.0';
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 -}
Index: trunk/extensions/MobileFrontend/MobileFrontend.php
@@ -30,7 +30,7 @@
3131 $wgExtensionCredits['other'][] = array(
3232 'path' => __FILE__,
3333 'name' => 'MobileFrontend',
34 - 'version' => '0.7.0',
 34+ 'version' => ExtMobileFrontend::VERSION,
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,24 +42,23 @@
4343 // autoload extension classes
4444
4545 $autoloadClasses = array (
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',
 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',
6059 );
6160
62 -foreach ( $autoloadClasses as $className => $classFilename ) {
63 - $wgAutoloadClasses[$className] = $cwd . $classFilename . '.php';
 61+foreach ( $autoloadClasses as $class ) {
 62+ $wgAutoloadClasses[$class] = $cwd . $class . '.php';
6463 }
6564
6665 /**
@@ -113,3 +112,1666 @@
114113 return true;
115114 }
116115
 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+}

Follow-up revisions

RevisionCommit summaryAuthorDate
r109929Merge r109926reedy14:45, 24 January 2012

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r109884move Mobile Frontend class to separate body filepreilly00:41, 24 January 2012

Status & tagging log