3 * MediaWiki is the to-be base class for this whole project
5 * @internal documentation reviewed 15 Mar 2010
10 * TODO: fold $output, etc, into this
15 public function request( WebRequest
$x = null ){
16 return wfSetVar( $this->context
->request
, $x );
19 public function output( OutputPage
$x = null ){
20 return wfSetVar( $this->context
->output
, $x );
23 public function __construct( RequestContext
$context ){
24 $this->context
= $context;
25 $this->context
->setTitle( $this->parseTitle() );
29 * Parse $request to get the Title object
31 * @return Title object to be $wgTitle
33 private function parseTitle() {
36 $curid = $this->context
->request
->getInt( 'curid' );
37 $title = $this->context
->request
->getVal( 'title' );
39 if ( $this->context
->request
->getCheck( 'search' ) ) {
40 // Compatibility with old search URLs which didn't use Special:Search
41 // Just check for presence here, so blank requests still
42 // show the search page when using ugly URLs (bug 8054).
43 $ret = SpecialPage
::getTitleFor( 'Search' );
45 // URLs like this are generated by RC, because rc_title isn't always accurate
46 $ret = Title
::newFromID( $curid );
47 } elseif ( $title == '' && $this->getAction() != 'delete' ) {
48 $ret = Title
::newMainPage();
50 $ret = Title
::newFromURL( $title );
51 // check variant links so that interwiki links don't have to worry
52 // about the possible different language variants
53 if ( count( $wgContLang->getVariants() ) > 1 && !is_null( $ret ) && $ret->getArticleID() == 0 ){
54 $wgContLang->findVariantLink( $title, $ret );
57 // For non-special titles, check for implicit titles
58 if ( is_null( $ret ) ||
$ret->getNamespace() != NS_SPECIAL
) {
59 // We can have urls with just ?diff=,?oldid= or even just ?diff=
60 $oldid = $this->context
->request
->getInt( 'oldid' );
61 $oldid = $oldid ?
$oldid : $this->context
->request
->getInt( 'diff' );
62 // Allow oldid to override a changed or missing title
64 $rev = Revision
::newFromId( $oldid );
65 $ret = $rev ?
$rev->getTitle() : $ret;
69 if( $ret === null ||
( $ret->getDBkey() == '' && $ret->getInterwiki() == '' ) ){
76 * Get the Title object that we'll be acting on, as specified in the WebRequest
79 public function getTitle(){
80 if( $this->context
->title
=== null ){
81 $this->context
->title
= $this->parseTitle();
83 return $this->context
->title
;
87 * Performs the request.
90 * - local interwiki redirects
95 * @return Article object
97 public function performRequest() {
98 global $wgServer, $wgUsePathInfo;
100 wfProfileIn( __METHOD__
);
102 if ( $this->context
->request
->getVal( 'printable' ) === 'yes' ) {
103 $this->context
->output
->setPrintable();
106 wfRunHooks( 'BeforeInitialize', array(
107 &$this->context
->title
,
109 &$this->context
->output
,
110 &$this->context
->user
,
111 $this->context
->request
,
115 // Invalid titles. Bug 21776: The interwikis must redirect even if the page name is empty.
116 if ( $this->context
->title
instanceof BadTitle
) {
117 throw new ErrorPageError( 'badtitle', 'badtitletext' );
118 // If the user is not logged in, the Namespace:title of the article must be in
119 // the Read array in order for the user to see it. (We have to check here to
120 // catch special pages etc. We check again in Article::view())
121 } else if ( !$this->context
->title
->userCanRead() ) {
122 $this->context
->output
->loginToUse();
123 // Interwiki redirects
124 } else if ( $this->context
->title
->getInterwiki() != '' ) {
125 $rdfrom = $this->context
->request
->getVal( 'rdfrom' );
127 $url = $this->context
->title
->getFullURL( 'rdfrom=' . urlencode( $rdfrom ) );
129 $query = $this->context
->request
->getValues();
130 unset( $query['title'] );
131 $url = $this->context
->title
->getFullURL( $query );
133 // Check for a redirect loop
134 if ( !preg_match( '/^' . preg_quote( $wgServer, '/' ) . '/', $url ) && $this->context
->title
->isLocal() ) {
135 // 301 so google et al report the target as the actual url.
136 $this->context
->output
->redirect( $url, 301 );
138 $this->context
->title
= new BadTitle
;
139 wfProfileOut( __METHOD__
);
140 throw new ErrorPageError( 'badtitle', 'badtitletext' );
142 // Redirect loops, no title in URL, $wgUsePathInfo URLs, and URLs with a variant
143 } else if ( $this->context
->request
->getVal( 'action', 'view' ) == 'view' && !$this->context
->request
->wasPosted()
144 && ( $this->context
->request
->getVal( 'title' ) === null ||
$this->context
->title
->getPrefixedDBKey() != $this->context
->request
->getVal( 'title' ) )
145 && !count( array_diff( array_keys( $this->context
->request
->getValues() ), array( 'action', 'title' ) ) ) )
147 if ( $this->context
->title
->getNamespace() == NS_SPECIAL
) {
148 list( $name, $subpage ) = SpecialPageFactory
::resolveAlias( $this->context
->title
->getDBkey() );
150 $this->context
->title
= SpecialPage
::getTitleFor( $name, $subpage );
153 $targetUrl = $this->context
->title
->getFullURL();
154 // Redirect to canonical url, make it a 301 to allow caching
155 if ( $targetUrl == $this->context
->request
->getFullRequestURL() ) {
156 $message = "Redirect loop detected!\n\n" .
157 "This means the wiki got confused about what page was " .
158 "requested; this sometimes happens when moving a wiki " .
159 "to a new server or changing the server configuration.\n\n";
161 if ( $wgUsePathInfo ) {
162 $message .= "The wiki is trying to interpret the page " .
163 "title from the URL path portion (PATH_INFO), which " .
164 "sometimes fails depending on the web server. Try " .
165 "setting \"\$wgUsePathInfo = false;\" in your " .
166 "LocalSettings.php, or check that \$wgArticlePath " .
169 $message .= "Your web server was detected as possibly not " .
170 "supporting URL path components (PATH_INFO) correctly; " .
171 "check your LocalSettings.php for a customized " .
172 "\$wgArticlePath setting and/or toggle \$wgUsePathInfo " .
175 wfHttpError( 500, "Internal error", $message );
177 $this->context
->output
->setSquidMaxage( 1200 );
178 $this->context
->output
->redirect( $targetUrl, '301' );
181 } else if ( NS_SPECIAL
== $this->context
->title
->getNamespace() ) {
182 // actions that need to be made when we have a special pages
183 SpecialPageFactory
::executePath( $this->context
->title
, $this->context
);
185 // ...otherwise treat it as an article view. The article
186 // may be a redirect to another article or URL.
187 $article = $this->initializeArticle();
188 if ( is_object( $article ) ) {
189 $this->performAction( $article );
190 wfProfileOut( __METHOD__
);
192 } elseif ( is_string( $article ) ) {
193 $this->context
->output
->redirect( $article );
195 wfProfileOut( __METHOD__
);
196 throw new MWException( "Shouldn't happen: MediaWiki::initializeArticle() returned neither an object nor a URL" );
199 wfProfileOut( __METHOD__
);
203 * Create an Article object of the appropriate class for the given page.
205 * @deprecated in 1.19; use Article::newFromTitle() instead
206 * @param $title Title
207 * @param $context RequestContext
208 * @return Article object
210 public static function articleFromTitle( $title, RequestContext
$context ) {
211 return Article
::newFromTitle( $title, $context );
215 * Returns the action that will be executed, not necesserly the one passed
216 * passed through the "action" parameter. Actions disabled in
217 * $wgDisabledActions will be replaced by "nosuchaction"
219 * @return String: action
221 public function getAction() {
222 global $wgDisabledActions;
224 $action = $this->context
->request
->getVal( 'action', 'view' );
226 // Check for disabled actions
227 if ( in_array( $action, $wgDisabledActions ) ) {
228 return 'nosuchaction';
231 // Workaround for bug #20966: inability of IE to provide an action dependent
232 // on which submit button is clicked.
233 if ( $action === 'historysubmit' ) {
234 if ( $this->context
->request
->getBool( 'revisiondelete' ) ) {
235 return 'revisiondelete';
239 } elseif ( $action == 'editredlink' ) {
247 * Initialize the main Article object for "standard" actions (view, etc)
248 * Create an Article object for the page, following redirects if needed.
250 * @return mixed an Article, or a string to redirect to another URL
252 private function initializeArticle() {
253 global $wgDisableHardRedirects;
255 wfProfileIn( __METHOD__
);
257 $action = $this->context
->request
->getVal( 'action', 'view' );
258 $article = Article
::newFromTitle( $this->context
->title
, $this->context
);
259 // NS_MEDIAWIKI has no redirects.
260 // It is also used for CSS/JS, so performance matters here...
261 if ( $this->context
->title
->getNamespace() == NS_MEDIAWIKI
) {
262 wfProfileOut( __METHOD__
);
265 // Namespace might change when using redirects
266 // Check for redirects ...
267 $file = ( $this->context
->title
->getNamespace() == NS_FILE
) ?
$article->getFile() : null;
268 if ( ( $action == 'view' ||
$action == 'render' ) // ... for actions that show content
269 && !$this->context
->request
->getVal( 'oldid' ) && // ... and are not old revisions
270 !$this->context
->request
->getVal( 'diff' ) && // ... and not when showing diff
271 $this->context
->request
->getVal( 'redirect' ) != 'no' && // ... unless explicitly told not to
272 // ... and the article is not a non-redirect image page with associated file
273 !( is_object( $file ) && $file->exists() && !$file->getRedirected() ) )
275 // Give extensions a change to ignore/handle redirects as needed
276 $ignoreRedirect = $target = false;
278 wfRunHooks( 'InitializeArticleMaybeRedirect',
279 array( &$this->context
->title
, &$this->context
->request
, &$ignoreRedirect, &$target, &$article ) );
281 // Follow redirects only for... redirects.
282 // If $target is set, then a hook wanted to redirect.
283 if ( !$ignoreRedirect && ( $target ||
$article->isRedirect() ) ) {
284 // Is the target already set by an extension?
285 $target = $target ?
$target : $article->followRedirect();
286 if ( is_string( $target ) ) {
287 if ( !$wgDisableHardRedirects ) {
288 // we'll need to redirect
289 wfProfileOut( __METHOD__
);
293 if ( is_object( $target ) ) {
294 // Rewrite environment to redirected article
295 $rarticle = Article
::newFromTitle( $target, $this->context
);
296 $rarticle->loadPageData();
297 if ( $rarticle->exists() ||
( is_object( $file ) && !$file->isLocal() ) ) {
298 $rarticle->setRedirectedFrom( $this->context
->title
);
299 $article = $rarticle;
300 $this->context
->title
= $target;
304 $this->context
->title
= $article->getTitle();
307 wfProfileOut( __METHOD__
);
312 * Cleaning up request by doing deferred updates, DB transaction, and the output
314 public function finalCleanup() {
315 wfProfileIn( __METHOD__
);
316 // Now commit any transactions, so that unreported errors after
317 // output() don't roll back the whole DB transaction
318 $factory = wfGetLBFactory();
319 $factory->commitMasterChanges();
320 // Output everything!
321 $this->context
->output
->output();
322 // Do any deferred jobs
323 wfDoUpdates( 'commit' );
326 wfProfileOut( __METHOD__
);
330 * Do a job from the job queue
332 private function doJobs() {
333 global $wgJobRunRate;
335 if ( $wgJobRunRate <= 0 ||
wfReadOnly() ) {
338 if ( $wgJobRunRate < 1 ) {
339 $max = mt_getrandmax();
340 if ( mt_rand( 0, $max ) > $max * $wgJobRunRate ) {
345 $n = intval( $wgJobRunRate );
348 // Close the session so that jobs don't access the current session
349 $this->shutdownLBFactory();
350 session_write_close();
352 while ( $n-- && false != ( $job = Job
::pop() ) ) {
353 $output = $job->toString() . "\n";
355 $success = $job->run();
357 $t = round( $t * 1000 );
359 $output .= "Error: " . $job->getLastError() . ", Time: $t ms\n";
361 $output .= "Success, Time: $t ms\n";
363 wfDebugLog( 'jobqueue', $output );
368 * Ends this task peacefully
370 public function restInPeace() {
371 MessageCache
::logMessages();
372 wfLogProfilingData();
373 $this->shutdownLBFactory();
374 wfDebug( "Request ended normally\n" );
378 * Commit pending master changes, shutdown the current loadbalancer
379 * factory and destroys the factory instance.
381 private function shutdownLBFactory() {
382 // Commit and close up!
383 $factory = LBFactory
::singleton();
384 $factory->commitMasterChanges();
385 $factory->shutdown();
386 LBFactory
::destroyInstance();
390 * Perform one of the "standard" actions
392 * @param $article Article
394 private function performAction( $article ) {
395 global $wgSquidMaxage, $wgUseExternalEditor;
397 wfProfileIn( __METHOD__
);
399 if ( !wfRunHooks( 'MediaWikiPerformAction', array(
400 $this->context
->output
, $article, $this->context
->title
,
401 $this->context
->user
, $this->context
->request
, $this ) ) )
403 wfProfileOut( __METHOD__
);
407 $act = $this->getAction();
409 $action = Action
::factory( $act, $article );
410 if( $action instanceof Action
){
412 wfProfileOut( __METHOD__
);
418 $this->context
->output
->setSquidMaxage( $wgSquidMaxage );
421 case 'raw': // includes JS/CSS
422 wfProfileIn( __METHOD__
. '-raw' );
423 $raw = new RawPage( $article );
425 wfProfileOut( __METHOD__
. '-raw' );
433 case 'markpatrolled':
435 case 'deletetrackback':
439 if ( session_id() == '' ) {
440 // Send a cookie so anons get talk message notifications
445 if ( wfRunHooks( 'CustomEditor', array( $article, $this->context
->user
) ) ) {
446 $internal = $this->context
->request
->getVal( 'internaledit' );
447 $external = $this->context
->request
->getVal( 'externaledit' );
448 $section = $this->context
->request
->getVal( 'section' );
449 $oldid = $this->context
->request
->getVal( 'oldid' );
450 if ( !$wgUseExternalEditor ||
$act == 'submit' ||
$internal ||
451 $section ||
$oldid ||
( !$this->context
->user
->getOption( 'externaleditor' ) && !$external ) ) {
452 $editor = new EditPage( $article );
454 } elseif ( $wgUseExternalEditor && ( $external ||
$this->context
->user
->getOption( 'externaleditor' ) ) ) {
455 $mode = $this->context
->request
->getVal( 'mode' );
456 $extedit = new ExternalEdit( $article, $mode );
462 if ( $this->context
->request
->getFullRequestURL() == $this->context
->title
->getInternalURL( 'action=history' ) ) {
463 $this->context
->output
->setSquidMaxage( $wgSquidMaxage );
465 $history = new HistoryPage( $article );
468 case 'revisiondelete':
469 // For show/hide submission from history page
470 $special = SpecialPageFactory
::getPage( 'Revisiondelete' );
471 $special->execute( '' );
474 if ( wfRunHooks( 'UnknownAction', array( $act, $article ) ) ) {
475 $this->context
->output
->showErrorPage( 'nosuchaction', 'nosuchactiontext' );
478 wfProfileOut( __METHOD__
);