Main Page | Namespace List | Class Hierarchy | Class List | File List | Class Members | File Members | Related Pages

Article.php

Go to the documentation of this file.
00001 <?php 00002 # $Id: Article.php,v 1.173 2004/06/18 01:06:29 timstarling Exp $ 00003 # 00004 # Class representing a Wikipedia article and history. 00005 # See design.doc for an overview. 00006 00007 # Note: edit user interface and cache support functions have been 00008 # moved to separate EditPage and CacheManager classes. 00009 00010 require_once( 'CacheManager.php' ); 00011 00012 class Article { 00013 /* private */ var $mContent, $mContentLoaded; 00014 /* private */ var $mUser, $mTimestamp, $mUserText; 00015 /* private */ var $mCounter, $mComment, $mCountAdjustment; 00016 /* private */ var $mMinorEdit, $mRedirectedFrom; 00017 /* private */ var $mTouched, $mFileCache, $mTitle; 00018 /* private */ var $mId, $mTable; 00019 00020 function Article( &$title ) { 00021 $this->mTitle =& $title; 00022 $this->clear(); 00023 } 00024 00025 /* private */ function clear() 00026 { 00027 $this->mContentLoaded = false; 00028 $this->mCurID = $this->mUser = $this->mCounter = -1; # Not loaded 00029 $this->mRedirectedFrom = $this->mUserText = 00030 $this->mTimestamp = $this->mComment = $this->mFileCache = ''; 00031 $this->mCountAdjustment = 0; 00032 $this->mTouched = '19700101000000'; 00033 } 00034 00035 # Get revision text associated with an old or archive row 00036 # $row is usually an object from wfFetchRow(), both the flags and the text field must be included 00037 /* static */ function getRevisionText( $row, $prefix = 'old_' ) { 00038 # Get data 00039 $textField = $prefix . 'text'; 00040 $flagsField = $prefix . 'flags'; 00041 00042 if ( isset( $row->$flagsField ) ) { 00043 $flags = explode( ",", $row->$flagsField ); 00044 } else { 00045 $flags = array(); 00046 } 00047 00048 if ( isset( $row->$textField ) ) { 00049 $text = $row->$textField; 00050 } else { 00051 return false; 00052 } 00053 00054 if ( in_array( 'link', $flags ) ) { 00055 # Handle link type 00056 $text = Article::followLink( $text ); 00057 } elseif ( in_array( 'gzip', $flags ) ) { 00058 # Deal with optional compression of archived pages. 00059 # This can be done periodically via maintenance/compressOld.php, and 00060 # as pages are saved if $wgCompressRevisions is set. 00061 return gzinflate( $text ); 00062 } 00063 return $text; 00064 } 00065 00066 /* static */ function compressRevisionText( &$text ) { 00067 global $wgCompressRevisions; 00068 if( !$wgCompressRevisions ) { 00069 return ''; 00070 } 00071 if( !function_exists( 'gzdeflate' ) ) { 00072 wfDebug( "Article::compressRevisionText() -- no zlib support, not compressing\n" ); 00073 return ''; 00074 } 00075 $text = gzdeflate( $text ); 00076 return 'gzip'; 00077 } 00078 00079 # Returns the text associated with a "link" type old table row 00080 /* static */ function followLink( $link ) { 00081 # Split the link into fields and values 00082 $lines = explode( '\n', $link ); 00083 $hash = ''; 00084 $locations = array(); 00085 foreach ( $lines as $line ) { 00086 # Comments 00087 if ( $line{0} == '#' ) { 00088 continue; 00089 } 00090 # Field/value pairs 00091 if ( preg_match( '/^(.*?)\s*:\s*(.*)$/', $line, $matches ) ) { 00092 $field = strtolower($matches[1]); 00093 $value = $matches[2]; 00094 if ( $field == 'hash' ) { 00095 $hash = $value; 00096 } elseif ( $field == 'location' ) { 00097 $locations[] = $value; 00098 } 00099 } 00100 } 00101 00102 if ( $hash === '' ) { 00103 return false; 00104 } 00105 00106 # Look in each specified location for the text 00107 $text = false; 00108 foreach ( $locations as $location ) { 00109 $text = Article::fetchFromLocation( $location, $hash ); 00110 if ( $text !== false ) { 00111 break; 00112 } 00113 } 00114 00115 return $text; 00116 } 00117 00118 /* static */ function fetchFromLocation( $location, $hash ) { 00119 global $wgDatabase, $wgKnownDBServers; 00120 $fname = 'fetchFromLocation'; 00121 wfProfileIn( $fname ); 00122 00123 $p = strpos( $location, ':' ); 00124 if ( $p === false ) { 00125 wfProfileOut( $fname ); 00126 return false; 00127 } 00128 00129 $type = substr( $location, 0, $p ); 00130 $text = false; 00131 switch ( $type ) { 00132 case 'mysql': 00133 # MySQL locations are specified by mysql://<machineID>/<dbname>/<tblname>/<index> 00134 # Machine ID 0 is the current connection 00135 if ( preg_match( '/^mysql:\/\/(\d+)\/([A-Za-z_]+)\/([A-Za-z_]+)\/([A-Za-z_]+)$/', 00136 $location, $matches ) ) { 00137 $machineID = $matches[1]; 00138 $dbName = $matches[2]; 00139 $tblName = $matches[3]; 00140 $index = $matches[4]; 00141 if ( $machineID == 0 ) { 00142 # Current connection 00143 $db =& wfGetDB(); 00144 } else { 00145 # Alternate connection 00146 $db =& $wgLoadBalancer->getConnection( $machineID ); 00147 00148 if ( array_key_exists( $machineId, $wgKnownMysqlServers ) ) { 00149 # Try to open, return false on failure 00150 $params = $wgKnownDBServers[$machineId]; 00151 $db = Database::newFromParams( $params['server'], $params['user'], $params['password'], 00152 $dbName, 1, false, true, true ); 00153 } 00154 } 00155 if ( $db->isOpen() ) { 00156 $index = wfStrencode( $index ); 00157 $res = $db->query( "SELECT blob_data FROM $dbName.$tblName WHERE blob_index='$index'", $fname ); 00158 $row = $db->fetchObject( $res ); 00159 $text = $row->text_data; 00160 } 00161 } 00162 break; 00163 case 'file': 00164 # File locations are of the form file://<filename>, relative to the current directory 00165 if ( preg_match( '/^file:\/\/(.*)$', $location, $matches ) ) 00166 $filename = strstr( $location, 'file://' ); 00167 $text = @file_get_contents( $matches[1] ); 00168 } 00169 if ( $text !== false ) { 00170 # Got text, now we need to interpret it 00171 # The first line contains information about how to do this 00172 $p = strpos( $text, '\n' ); 00173 $type = substr( $text, 0, $p ); 00174 $text = substr( $text, $p + 1 ); 00175 switch ( $type ) { 00176 case 'plain': 00177 break; 00178 case 'gzip': 00179 $text = gzinflate( $text ); 00180 break; 00181 case 'object': 00182 $object = unserialize( $text ); 00183 $text = $object->getItem( $hash ); 00184 break; 00185 default: 00186 $text = false; 00187 } 00188 } 00189 wfProfileOut( $fname ); 00190 return $text; 00191 } 00192 00193 # Note that getContent/loadContent may follow redirects if 00194 # not told otherwise, and so may cause a change to mTitle. 00195 00196 # Return the text of this revision 00197 function getContent( $noredir ) 00198 { 00199 global $wgRequest; 00200 00201 # Get variables from query string :P 00202 $action = $wgRequest->getText( 'action', 'view' ); 00203 $section = $wgRequest->getText( 'section' ); 00204 00205 $fname = 'Article::getContent'; 00206 wfProfileIn( $fname ); 00207 00208 if ( 0 == $this->getID() ) { 00209 if ( 'edit' == $action ) { 00210 wfProfileOut( $fname ); 00211 return ''; # was "newarticletext", now moved above the box) 00212 } 00213 wfProfileOut( $fname ); 00214 return wfMsg( 'noarticletext' ); 00215 } else { 00216 $this->loadContent( $noredir ); 00217 00218 if( 00219 # check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page 00220 ( $this->mTitle->getNamespace() == Namespace::getTalk( Namespace::getUser()) ) && 00221 preg_match('/^\d{1,3}\.\d{1,3}.\d{1,3}\.\d{1,3}$/',$this->mTitle->getText()) && 00222 $action=='view' 00223 ) 00224 { 00225 wfProfileOut( $fname ); 00226 return $this->mContent . "\n" .wfMsg('anontalkpagetext'); } 00227 else { 00228 if($action=='edit') { 00229 if($section!='') { 00230 if($section=='new') { 00231 wfProfileOut( $fname ); 00232 return ''; 00233 } 00234 00235 # strip NOWIKI etc. to avoid confusion (true-parameter causes HTML 00236 # comments to be stripped as well) 00237 $rv=$this->getSection($this->mContent,$section); 00238 wfProfileOut( $fname ); 00239 return $rv; 00240 } 00241 } 00242 wfProfileOut( $fname ); 00243 return $this->mContent; 00244 } 00245 } 00246 } 00247 00248 # This function returns the text of a section, specified by a number ($section). 00249 # A section is text under a heading like == Heading == or <h1>Heading</h1>, or 00250 # the first section before any such heading (section 0). 00251 # 00252 # If a section contains subsections, these are also returned. 00253 # 00254 function getSection($text,$section) { 00255 00256 # strip NOWIKI etc. to avoid confusion (true-parameter causes HTML 00257 # comments to be stripped as well) 00258 $striparray=array(); 00259 $parser=new Parser(); 00260 $parser->mOutputType=OT_WIKI; 00261 $striptext=$parser->strip($text, $striparray, true); 00262 00263 # now that we can be sure that no pseudo-sections are in the source, 00264 # split it up by section 00265 $secs = 00266 preg_split( 00267 '/(^=+.*?=+|^<h[1-6].*?' . '>.*?<\/h[1-6].*?' . '>)/mi', 00268 $striptext, -1, 00269 PREG_SPLIT_DELIM_CAPTURE); 00270 if($section==0) { 00271 $rv=$secs[0]; 00272 } else { 00273 $headline=$secs[$section*2-1]; 00274 preg_match( '/^(=+).*?=+|^<h([1-6]).*?' . '>.*?<\/h[1-6].*?' . '>/mi',$headline,$matches); 00275 $hlevel=$matches[1]; 00276 00277 # translate wiki heading into level 00278 if(strpos($hlevel,'=')!==false) { 00279 $hlevel=strlen($hlevel); 00280 } 00281 00282 $rv=$headline. $secs[$section*2]; 00283 $count=$section+1; 00284 00285 $break=false; 00286 while(!empty($secs[$count*2-1]) && !$break) { 00287 00288 $subheadline=$secs[$count*2-1]; 00289 preg_match( '/^(=+).*?=+|^<h([1-6]).*?' . '>.*?<\/h[1-6].*?' . '>/mi',$subheadline,$matches); 00290 $subhlevel=$matches[1]; 00291 if(strpos($subhlevel,'=')!==false) { 00292 $subhlevel=strlen($subhlevel); 00293 } 00294 if($subhlevel > $hlevel) { 00295 $rv.=$subheadline.$secs[$count*2]; 00296 } 00297 if($subhlevel <= $hlevel) { 00298 $break=true; 00299 } 00300 $count++; 00301 00302 } 00303 } 00304 # reinsert stripped tags 00305 $rv=$parser->unstrip($rv,$striparray); 00306 $rv=$parser->unstripNoWiki($rv,$striparray); 00307 $rv=trim($rv); 00308 return $rv; 00309 00310 } 00311 00312 00313 # Load the revision (including cur_text) into this object 00314 function loadContent( $noredir = false ) 00315 { 00316 global $wgOut, $wgMwRedir, $wgRequest, $wgIsPg; 00317 00318 # Query variables :P 00319 $oldid = $wgRequest->getVal( 'oldid' ); 00320 $redirect = $wgRequest->getVal( 'redirect' ); 00321 00322 if ( $this->mContentLoaded ) return; 00323 $fname = 'Article::loadContent'; 00324 00325 # Pre-fill content with error message so that if something 00326 # fails we'll have something telling us what we intended. 00327 00328 $t = $this->mTitle->getPrefixedText(); 00329 if ( isset( $oldid ) ) { 00330 $oldid = IntVal( $oldid ); 00331 $t .= ",oldid={$oldid}"; 00332 } 00333 if ( isset( $redirect ) ) { 00334 $redirect = ($redirect == 'no') ? 'no' : 'yes'; 00335 $t .= ",redirect={$redirect}"; 00336 } 00337 $this->mContent = wfMsg( 'missingarticle', $t ); 00338 00339 if ( ! $oldid ) { # Retrieve current version 00340 $id = $this->getID(); 00341 if ( 0 == $id ) return; 00342 00343 $sql = 'SELECT ' . 00344 'cur_text,cur_timestamp,cur_user,cur_user_text,cur_comment,cur_counter,cur_restrictions,cur_touched ' . 00345 "FROM cur WHERE cur_id={$id}"; 00346 wfDebug( "$sql\n" ); 00347 $res = wfQuery( $sql, DB_READ, $fname ); 00348 if ( 0 == wfNumRows( $res ) ) { 00349 return; 00350 } 00351 00352 $s = wfFetchObject( $res ); 00353 # If we got a redirect, follow it (unless we've been told 00354 # not to by either the function parameter or the query 00355 if ( ( 'no' != $redirect ) && ( false == $noredir ) && 00356 ( $wgMwRedir->matchStart( $s->cur_text ) ) ) { 00357 if ( preg_match( '/\\[\\[([^\\]\\|]+)[\\]\\|]/', 00358 $s->cur_text, $m ) ) { 00359 $rt = Title::newFromText( $m[1] ); 00360 if( $rt ) { 00361 # Gotta hand redirects to special pages differently: 00362 # Fill the HTTP response "Location" header and ignore 00363 # the rest of the page we're on. 00364 00365 if ( $rt->getInterwiki() != '' ) { 00366 $wgOut->redirect( $rt->getFullURL() ) ; 00367 return; 00368 } 00369 if ( $rt->getNamespace() == Namespace::getSpecial() ) { 00370 $wgOut->redirect( $rt->getFullURL() ); 00371 return; 00372 } 00373 $rid = $rt->getArticleID(); 00374 if ( 0 != $rid ) { 00375 $sql = 'SELECT cur_text,cur_timestamp,cur_user,cur_user_text,cur_comment,' . 00376 "cur_counter,cur_restrictions,cur_touched FROM cur WHERE cur_id={$rid}"; 00377 $res = wfQuery( $sql, DB_READ, $fname ); 00378 00379 if ( 0 != wfNumRows( $res ) ) { 00380 $this->mRedirectedFrom = $this->mTitle->getPrefixedText(); 00381 $this->mTitle = $rt; 00382 $s = wfFetchObject( $res ); 00383 } 00384 } 00385 } 00386 } 00387 } 00388 00389 $this->mContent = $s->cur_text; 00390 $this->mUser = $s->cur_user; 00391 $this->mUserText = $s->cur_user_text; 00392 $this->mComment = $s->cur_comment; 00393 $this->mCounter = $s->cur_counter; 00394 $this->mTimestamp = $s->cur_timestamp; 00395 $this->mTouched = $s->cur_touched; 00396 $this->mTitle->mRestrictions = explode( ',', trim( $s->cur_restrictions ) ); 00397 $this->mTitle->mRestrictionsLoaded = true; 00398 wfFreeResult( $res ); 00399 } else { # oldid set, retrieve historical version 00400 $oldtable=$wgIsPg?'"old"':'old'; 00401 $sql = "SELECT old_namespace,old_title,old_text,old_timestamp,". 00402 "old_user,old_user_text,old_comment,old_flags FROM old ". 00403 "WHERE old_id={$oldid}"; 00404 $res = wfQuery( $sql, DB_READ, $fname ); 00405 if ( 0 == wfNumRows( $res ) ) { 00406 return; 00407 } 00408 00409 $s = wfFetchObject( $res ); 00410 if( $this->mTitle->getNamespace() != $s->old_namespace || 00411 $this->mTitle->getDBkey() != $s->old_title ) { 00412 $oldTitle = Title::makeTitle( $s->old_namesapce, $s->old_title ); 00413 $this->mTitle = $oldTitle; 00414 $wgTitle = $oldTitle; 00415 } 00416 $this->mContent = Article::getRevisionText( $s ); 00417 $this->mUser = $s->old_user; 00418 $this->mUserText = $s->old_user_text; 00419 $this->mComment = $s->old_comment; 00420 $this->mCounter = 0; 00421 $this->mTimestamp = $s->old_timestamp; 00422 wfFreeResult( $res ); 00423 } 00424 $this->mContentLoaded = true; 00425 return $this->mContent; 00426 } 00427 00428 # Gets the article text without using so many damn globals 00429 # Returns false on error 00430 function getContentWithoutUsingSoManyDamnGlobals( $oldid = 0, $noredir = false ) { 00431 global $wgMwRedir, $wgIsPg; 00432 00433 if ( $this->mContentLoaded ) { 00434 return $this->mContent; 00435 } 00436 $this->mContent = false; 00437 00438 $fname = 'Article::loadContent'; 00439 00440 if ( ! $oldid ) { # Retrieve current version 00441 $id = $this->getID(); 00442 if ( 0 == $id ) { 00443 return false; 00444 } 00445 00446 $sql = 'SELECT ' . 00447 'cur_text,cur_timestamp,cur_user,cur_counter,cur_restrictions,cur_touched ' . 00448 "FROM cur WHERE cur_id={$id}"; 00449 $res = wfQuery( $sql, DB_READ, $fname ); 00450 if ( 0 == wfNumRows( $res ) ) { 00451 return false; 00452 } 00453 00454 $s = wfFetchObject( $res ); 00455 # If we got a redirect, follow it (unless we've been told 00456 # not to by either the function parameter or the query 00457 if ( !$noredir && $wgMwRedir->matchStart( $s->cur_text ) ) { 00458 if ( preg_match( '/\\[\\[([^\\]\\|]+)[\\]\\|]/', 00459 $s->cur_text, $m ) ) { 00460 $rt = Title::newFromText( $m[1] ); 00461 if( $rt && $rt->getInterwiki() == '' && $rt->getNamespace() != Namespace::getSpecial() ) { 00462 $rid = $rt->getArticleID(); 00463 if ( 0 != $rid ) { 00464 $sql = 'SELECT cur_text,cur_timestamp,cur_user,' . 00465 "cur_counter,cur_restrictions,cur_touched FROM cur WHERE cur_id={$rid}"; 00466 $res = wfQuery( $sql, DB_READ, $fname ); 00467 00468 if ( 0 != wfNumRows( $res ) ) { 00469 $this->mRedirectedFrom = $this->mTitle->getPrefixedText(); 00470 $this->mTitle = $rt; 00471 $s = wfFetchObject( $res ); 00472 } 00473 } 00474 } 00475 } 00476 } 00477 00478 $this->mContent = $s->cur_text; 00479 $this->mUser = $s->cur_user; 00480 $this->mCounter = $s->cur_counter; 00481 $this->mTimestamp = $s->cur_timestamp; 00482 $this->mTouched = $s->cur_touched; 00483 $this->mTitle->mRestrictions = explode( ",", trim( $s->cur_restrictions ) ); 00484 $this->mTitle->mRestrictionsLoaded = true; 00485 wfFreeResult( $res ); 00486 } else { # oldid set, retrieve historical version 00487 $oldtable=$wgIsPg?'"old"':'old'; 00488 $sql = "SELECT old_text,old_timestamp,old_user,old_flags FROM $oldtable " . 00489 "WHERE old_id={$oldid}"; 00490 $res = wfQuery( $sql, DB_READ, $fname ); 00491 if ( 0 == wfNumRows( $res ) ) { 00492 return false; 00493 } 00494 00495 $s = wfFetchObject( $res ); 00496 $this->mContent = Article::getRevisionText( $s ); 00497 $this->mUser = $s->old_user; 00498 $this->mCounter = 0; 00499 $this->mTimestamp = $s->old_timestamp; 00500 wfFreeResult( $res ); 00501 } 00502 $this->mContentLoaded = true; 00503 return $this->mContent; 00504 } 00505 00506 function getID() { 00507 if( $this->mTitle ) { 00508 return $this->mTitle->getArticleID(); 00509 } else { 00510 return 0; 00511 } 00512 } 00513 00514 function getCount() 00515 { 00516 if ( -1 == $this->mCounter ) { 00517 $id = $this->getID(); 00518 $this->mCounter = wfGetSQL( 'cur', 'cur_counter', "cur_id={$id}" ); 00519 } 00520 return $this->mCounter; 00521 } 00522 00523 # Would the given text make this article a "good" article (i.e., 00524 # suitable for including in the article count)? 00525 00526 function isCountable( $text ) 00527 { 00528 global $wgUseCommaCount, $wgMwRedir; 00529 00530 if ( 0 != $this->mTitle->getNamespace() ) { return 0; } 00531 if ( $wgMwRedir->matchStart( $text ) ) { return 0; } 00532 $token = ($wgUseCommaCount ? ',' : '[[' ); 00533 if ( false === strstr( $text, $token ) ) { return 0; } 00534 return 1; 00535 } 00536 00537 # Loads everything from cur except cur_text 00538 # This isn't necessary for all uses, so it's only done if needed. 00539 00540 /* private */ function loadLastEdit() 00541 { 00542 global $wgOut; 00543 if ( -1 != $this->mUser ) return; 00544 00545 $sql = 'SELECT cur_user,cur_user_text,cur_timestamp,' . 00546 'cur_comment,cur_minor_edit FROM cur WHERE ' . 00547 'cur_id=' . $this->getID(); 00548 $res = wfQuery( $sql, DB_READ, 'Article::loadLastEdit' ); 00549 00550 if ( wfNumRows( $res ) > 0 ) { 00551 $s = wfFetchObject( $res ); 00552 $this->mUser = $s->cur_user; 00553 $this->mUserText = $s->cur_user_text; 00554 $this->mTimestamp = $s->cur_timestamp; 00555 $this->mComment = $s->cur_comment; 00556 $this->mMinorEdit = $s->cur_minor_edit; 00557 } 00558 } 00559 00560 function getTimestamp() 00561 { 00562 $this->loadLastEdit(); 00563 return $this->mTimestamp; 00564 } 00565 00566 function getUser() 00567 { 00568 $this->loadLastEdit(); 00569 return $this->mUser; 00570 } 00571 00572 function getUserText() 00573 { 00574 $this->loadLastEdit(); 00575 return $this->mUserText; 00576 } 00577 00578 function getComment() 00579 { 00580 $this->loadLastEdit(); 00581 return $this->mComment; 00582 } 00583 00584 function getMinorEdit() 00585 { 00586 $this->loadLastEdit(); 00587 return $this->mMinorEdit; 00588 } 00589 00590 function getContributors($limit = 0, $offset = 0) 00591 { 00592 $fname = 'Article::getContributors'; 00593 00594 # XXX: this is expensive; cache this info somewhere. 00595 00596 $title = $this->mTitle; 00597 00598 $contribs = array(); 00599 00600 $sql = 'SELECT old.old_user, old.old_user_text, ' . 00601 ' user.user_real_name, MAX(old.old_timestamp) as timestamp' . 00602 ' FROM old, user ' . 00603 ' WHERE old.old_user = user.user_id ' . 00604 ' AND old.old_namespace = ' . $title->getNamespace() . 00605 ' AND old.old_title = "' . $title->getDBkey() . '"' . 00606 ' AND old.old_user != 0 ' . 00607 ' AND old.old_user != ' . $this->getUser() . 00608 ' GROUP BY old.old_user ' . 00609 ' ORDER BY timestamp DESC '; 00610 00611 if ($limit > 0) { 00612 $sql .= ' LIMIT '.$limit; 00613 } 00614 00615 $res = wfQuery($sql, DB_READ, $fname); 00616 00617 while ( $line = wfFetchObject( $res ) ) { 00618 $contribs[$line->old_user] = 00619 array($line->old_user_text, $line->user_real_name); 00620 } 00621 00622 # Count anonymous users 00623 00624 $res = wfQuery('SELECT COUNT(*) AS cnt ' . 00625 ' FROM old ' . 00626 ' WHERE old_namespace = ' . $title->getNamespace() . 00627 " AND old_title = '" . $title->getDBkey() . "'" . 00628 ' AND old_user = 0 ', DB_READ, $fname); 00629 00630 while ( $line = wfFetchObject( $res ) ) { 00631 $contribs[0] = array($line->cnt, 'Anonymous'); 00632 } 00633 00634 return $contribs; 00635 } 00636 00637 # This is the default action of the script: just view the page of 00638 # the given title. 00639 00640 function view() 00641 { 00642 global $wgUser, $wgOut, $wgLang, $wgRequest; 00643 global $wgLinkCache, $IP, $wgEnableParserCache; 00644 00645 $fname = 'Article::view'; 00646 wfProfileIn( $fname ); 00647 00648 # Get variables from query string :P 00649 $oldid = $wgRequest->getVal( 'oldid' ); 00650 $diff = $wgRequest->getVal( 'diff' ); 00651 00652 $wgOut->setArticleFlag( true ); 00653 $wgOut->setRobotpolicy( 'index,follow' ); 00654 00655 # If we got diff and oldid in the query, we want to see a 00656 # diff page instead of the article. 00657 00658 if ( !is_null( $diff ) ) { 00659 $wgOut->setPageTitle( $this->mTitle->getPrefixedText() ); 00660 $de = new DifferenceEngine( intval($oldid), intval($diff) ); 00661 $de->showDiffPage(); 00662 wfProfileOut( $fname ); 00663 return; 00664 } 00665 00666 if ( !is_null( $oldid ) and $this->checkTouched() ) { 00667 if( $wgOut->checkLastModified( $this->mTouched ) ){ 00668 return; 00669 } else if ( $this->tryFileCache() ) { 00670 # tell wgOut that output is taken care of 00671 $wgOut->disable(); 00672 $this->viewUpdates(); 00673 return; 00674 } 00675 } 00676 00677 # Should the parser cache be used? 00678 if ( $wgEnableParserCache && intval($wgUser->getOption( 'stubthreshold' )) == 0 && empty( $oldid ) ) { 00679 $pcache = true; 00680 } else { 00681 $pcache = false; 00682 } 00683 00684 $outputDone = false; 00685 if ( $pcache ) { 00686 if ( $wgOut->tryParserCache( $this, $wgUser ) ) { 00687 $outputDone = true; 00688 } 00689 } 00690 00691 if ( !$outputDone ) { 00692 $text = $this->getContent( false ); # May change mTitle by following a redirect 00693 00694 # Another whitelist check in case oldid or redirects are altering the title 00695 if ( !$this->mTitle->userCanRead() ) { 00696 $wgOut->loginToUse(); 00697 $wgOut->output(); 00698 exit; 00699 } 00700 00701 00702 # We're looking at an old revision 00703 00704 if ( !empty( $oldid ) ) { 00705 $this->setOldSubtitle(); 00706 $wgOut->setRobotpolicy( 'noindex,follow' ); 00707 } 00708 if ( '' != $this->mRedirectedFrom ) { 00709 $sk = $wgUser->getSkin(); 00710 $redir = $sk->makeKnownLink( $this->mRedirectedFrom, '', 00711 'redirect=no' ); 00712 $s = wfMsg( 'redirectedfrom', $redir ); 00713 $wgOut->setSubtitle( $s ); 00714 00715 # Can't cache redirects 00716 $pcache = false; 00717 } 00718 00719 $wgLinkCache->preFill( $this->mTitle ); 00720 00721 # wrap user css and user js in pre and don't parse 00722 # XXX: use $this->mTitle->usCssJsSubpage() when php is fixed/ a workaround is found 00723 if ( 00724 $this->mTitle->getNamespace() == Namespace::getUser() && 00725 preg_match('/\\/[\\w]+\\.(css|js)$/', $this->mTitle->getDBkey()) 00726 ) { 00727 $wgOut->addWikiText( wfMsg('clearyourcache')); 00728 $wgOut->addHTML( '<pre>'.htmlspecialchars($this->mContent)."\n</pre>" ); 00729 } else if ( $pcache ) { 00730 $wgOut->addWikiText( $text, true, $this ); 00731 } else { 00732 $wgOut->addWikiText( $text ); 00733 } 00734 } 00735 $wgOut->setPageTitle( $this->mTitle->getPrefixedText() ); 00736 00737 # Add link titles as META keywords 00738 $wgOut->addMetaTags() ; 00739 00740 $this->viewUpdates(); 00741 wfProfileOut( $fname ); 00742 } 00743 00744 # Theoretically we could defer these whole insert and update 00745 # functions for after display, but that's taking a big leap 00746 # of faith, and we want to be able to report database 00747 # errors at some point. 00748 00749 /* private */ function insertNewArticle( $text, $summary, $isminor, $watchthis ) 00750 { 00751 global $wgOut, $wgUser, $wgLinkCache, $wgMwRedir; 00752 global $wgUseSquid, $wgDeferredUpdateList, $wgInternalServer, $wgIsPg, $wgIsMySQL; 00753 00754 $fname = 'Article::insertNewArticle'; 00755 00756 $this->mCountAdjustment = $this->isCountable( $text ); 00757 00758 $ns = $this->mTitle->getNamespace(); 00759 $ttl = $this->mTitle->getDBkey(); 00760 $text = $this->preSaveTransform( $text ); 00761 if ( $wgMwRedir->matchStart( $text ) ) { $redir = 1; } 00762 else { $redir = 0; } 00763 00764 $now = wfTimestampNow(); 00765 $won = wfInvertTimestamp( $now ); 00766 wfSeedRandom(); 00767 $rand = number_format( mt_rand() / mt_getrandmax(), 12, '.', '' ); 00768 00769 if ($wgIsPg) { 00770 $cur_id_column="cur_id,"; 00771 $cur_id=wfGetSQL(""," nextval('cur_cur_id_seq')"); 00772 $cur_id_value="{$cur_id},"; 00773 } else { 00774 $cur_id_column=""; 00775 $cur_id=""; 00776 $cur_id_value=""; 00777 } 00778 00779 $isminor = ( $isminor && $wgUser->getID() ) ? 1 : 0; 00780 $sql = "INSERT INTO cur ({$cur_id_column}cur_namespace,cur_title,cur_text," . 00781 'cur_comment,cur_user,cur_timestamp,cur_minor_edit,cur_counter,' . 00782 'cur_restrictions,cur_user_text,cur_is_redirect,' . 00783 "cur_is_new,cur_random,cur_touched,inverse_timestamp) VALUES ({$cur_id_value}{$ns},'" . wfStrencode( $ttl ) . "', '" . 00784 wfStrencode( $text ) . "', '" . 00785 wfStrencode( $summary ) . "', '" . 00786 $wgUser->getID() . "', '{$now}', " . 00787 $isminor . ", 0, '', '" . 00788 wfStrencode( $wgUser->getName() ) . "', $redir, 1, $rand, '{$now}', '{$won}')"; 00789 $res = wfQuery( $sql, DB_WRITE, $fname ); 00790 00791 $newid = $wgIsPg?$cur_id:wfInsertId(); 00792 $this->mTitle->resetArticleID( $newid ); 00793 00794 Article::onArticleCreate( $this->mTitle ); 00795 RecentChange::notifyNew( $now, $this->mTitle, $isminor, $wgUser, $summary ); 00796 00797 if ($watchthis) { 00798 if(!$this->mTitle->userIsWatching()) $this->watch(); 00799 } else { 00800 if ( $this->mTitle->userIsWatching() ) { 00801 $this->unwatch(); 00802 } 00803 } 00804 00805 # The talk page isn't in the regular link tables, so we need to update manually: 00806 $talkns = $ns ^ 1; # talk -> normal; normal -> talk 00807 $sql = "UPDATE cur set cur_touched='$now' WHERE cur_namespace=$talkns AND cur_title='" . wfStrencode( $ttl ) . "'"; 00808 wfQuery( $sql, DB_WRITE ); 00809 00810 # standard deferred updates 00811 $this->editUpdates( $text ); 00812 00813 $this->showArticle( $text, wfMsg( 'newarticle' ) ); 00814 } 00815 00816 00817 /* Side effects: loads last edit */ 00818 function getTextOfLastEditWithSectionReplacedOrAdded($section, $text, $summary = ''){ 00819 $this->loadLastEdit(); 00820 $oldtext = $this->getContent( true ); 00821 if ($section != '') { 00822 if($section=='new') { 00823 if($summary) $subject="== {$summary} ==\n\n"; 00824 $text=$oldtext."\n\n".$subject.$text; 00825 } else { 00826 00827 # strip NOWIKI etc. to avoid confusion (true-parameter causes HTML 00828 # comments to be stripped as well) 00829 $striparray=array(); 00830 $parser=new Parser(); 00831 $parser->mOutputType=OT_WIKI; 00832 $oldtext=$parser->strip($oldtext, $striparray, true); 00833 00834 # now that we can be sure that no pseudo-sections are in the source, 00835 # split it up 00836 # Unfortunately we can't simply do a preg_replace because that might 00837 # replace the wrong section, so we have to use the section counter instead 00838 $secs=preg_split('/(^=+.*?=+|^<h[1-6].*?' . '>.*?<\/h[1-6].*?' . '>)/mi', 00839 $oldtext,-1,PREG_SPLIT_DELIM_CAPTURE); 00840 $secs[$section*2]=$text."\n\n"; // replace with edited 00841 00842 # section 0 is top (intro) section 00843 if($section!=0) { 00844 00845 # headline of old section - we need to go through this section 00846 # to determine if there are any subsections that now need to 00847 # be erased, as the mother section has been replaced with 00848 # the text of all subsections. 00849 $headline=$secs[$section*2-1]; 00850 preg_match( '/^(=+).*?=+|^<h([1-6]).*?' . '>.*?<\/h[1-6].*?' . '>/mi',$headline,$matches); 00851 $hlevel=$matches[1]; 00852 00853 # determine headline level for wikimarkup headings 00854 if(strpos($hlevel,'=')!==false) { 00855 $hlevel=strlen($hlevel); 00856 } 00857 00858 $secs[$section*2-1]=''; // erase old headline 00859 $count=$section+1; 00860 $break=false; 00861 while(!empty($secs[$count*2-1]) && !$break) { 00862 00863 $subheadline=$secs[$count*2-1]; 00864 preg_match( 00865 '/^(=+).*?=+|^<h([1-6]).*?' . '>.*?<\/h[1-6].*?' . '>/mi',$subheadline,$matches); 00866 $subhlevel=$matches[1]; 00867 if(strpos($subhlevel,'=')!==false) { 00868 $subhlevel=strlen($subhlevel); 00869 } 00870 if($subhlevel > $hlevel) { 00871 // erase old subsections 00872 $secs[$count*2-1]=''; 00873 $secs[$count*2]=''; 00874 } 00875 if($subhlevel <= $hlevel) { 00876 $break=true; 00877 } 00878 $count++; 00879 00880 } 00881 00882 } 00883 $text=join('',$secs); 00884 # reinsert the stuff that we stripped out earlier 00885 $text=$parser->unstrip($text,$striparray); 00886 $text=$parser->unstripNoWiki($text,$striparray); 00887 } 00888 00889 } 00890 return $text; 00891 } 00892 00893 function updateArticle( $text, $summary, $minor, $watchthis, $forceBot = false, $sectionanchor = '' ) 00894 { 00895 global $wgOut, $wgUser, $wgLinkCache; 00896 global $wgDBtransactions, $wgMwRedir; 00897 global $wgUseSquid, $wgInternalServer; 00898 global $wgIsPg; 00899 $fname = 'Article::updateArticle'; 00900 00901 if ( $this->mMinorEdit ) { $me1 = 1; } else { $me1 = 0; } 00902 if ( $minor && $wgUser->getID() ) { $me2 = 1; } else { $me2 = 0; } 00903 if ( preg_match( "/^((" . $wgMwRedir->getBaseRegex() . ')[^\\n]+)/i', $text, $m ) ) { 00904 $redir = 1; 00905 $text = $m[1] . "\n"; # Remove all content but redirect 00906 } 00907 else { $redir = 0; } 00908 00909 $text = $this->preSaveTransform( $text ); 00910 00911 # Update article, but only if changed. 00912 00913 if( $wgDBtransactions ) { 00914 $sql = 'BEGIN'; 00915 wfQuery( $sql, DB_WRITE ); 00916 } 00917 $oldtext = $this->getContent( true ); 00918 00919 if ( 0 != strcmp( $text, $oldtext ) ) { 00920 $this->mCountAdjustment = $this->isCountable( $text ) 00921 - $this->isCountable( $oldtext ); 00922 00923 $now = wfTimestampNow(); 00924 $won = wfInvertTimestamp( $now ); 00925 $sql = "UPDATE cur SET cur_text='" . wfStrencode( $text ) . 00926 "',cur_comment='" . wfStrencode( $summary ) . 00927 "',cur_minor_edit={$me2}, cur_user=" . $wgUser->getID() . 00928 ",cur_timestamp='{$now}',cur_user_text='" . 00929 wfStrencode( $wgUser->getName() ) . 00930 "',cur_is_redirect={$redir}, cur_is_new=0, cur_touched='{$now}', inverse_timestamp='{$won}' " . 00931 "WHERE cur_id=" . $this->getID() . 00932 " AND cur_timestamp='" . $this->getTimestamp() . "'"; 00933 $res = wfQuery( $sql, DB_WRITE, $fname ); 00934 00935 if( wfAffectedRows() == 0 ) { 00936 /* Belated edit conflict! Run away!! */ 00937 return false; 00938 } 00939 00940 # This overwrites $oldtext if revision compression is on 00941 $flags = Article::compressRevisionText( $oldtext ); 00942 00943 $oldtable=$wgIsPg?'"old"':'old'; 00944 if ($wgIsPg) { 00945 $oldtable='"old"'; 00946 $old_id_column='old_id,'; 00947 $old_id=wfGetSQL(""," nextval('old_old_id_seq')"); 00948 $old_id_value=$old_id.','; 00949 } else { 00950 $oldtable='old'; 00951 $old_id_column=''; 00952 $old_id_value=''; 00953 } 00954 00955 $sql = "INSERT INTO $oldtable ({$old_id_column}old_namespace,old_title,old_text," . 00956 'old_comment,old_user,old_user_text,old_timestamp,' . 00957 'old_minor_edit,inverse_timestamp,old_flags) VALUES (' . 00958 $old_id_value. 00959 $this->mTitle->getNamespace() . ", '" . 00960 wfStrencode( $this->mTitle->getDBkey() ) . "', '" . 00961 wfStrencode( $oldtext ) . "', '" . 00962 wfStrencode( $this->getComment() ) . "', " . 00963 $this->getUser() . ", '" . 00964 wfStrencode( $this->getUserText() ) . "', '" . 00965 $this->getTimestamp() . "', " . $me1 . ", '" . 00966 wfInvertTimestamp( $this->getTimestamp() ) . "','$flags')"; 00967 $res = wfQuery( $sql, DB_WRITE, $fname ); 00968 00969 $oldid = $wgIsPg?$old_id:wfInsertId( $res ); 00970 00971 $bot = (int)($wgUser->isBot() || $forceBot); 00972 RecentChange::notifyEdit( $now, $this->mTitle, $me2, $wgUser, $summary, 00973 $oldid, $this->getTimestamp(), $bot ); 00974 Article::onArticleEdit( $this->mTitle ); 00975 } 00976 00977 if( $wgDBtransactions ) { 00978 $sql = 'COMMIT'; 00979 wfQuery( $sql, DB_WRITE ); 00980 } 00981 00982 if ($watchthis) { 00983 if (!$this->mTitle->userIsWatching()) $this->watch(); 00984 } else { 00985 if ( $this->mTitle->userIsWatching() ) { 00986 $this->unwatch(); 00987 } 00988 } 00989 # standard deferred updates 00990 $this->editUpdates( $text ); 00991 00992 00993 $urls = array(); 00994 # Template namespace 00995 # Purge all articles linking here 00996 if ( $this->mTitle->getNamespace() == NS_TEMPLATE) { 00997 $titles = $this->mTitle->getLinksTo(); 00998 Title::touchArray( $titles ); 00999 if ( $wgUseSquid ) { 01000 foreach ( $titles as $title ) { 01001 $urls[] = $title->getInternalURL(); 01002 } 01003 } 01004 } 01005 01006 # Squid updates 01007 if ( $wgUseSquid ) { 01008 $urls = array_merge( $urls, $this->mTitle->getSquidURLs() ); 01009 $u = new SquidUpdate( $urls ); 01010 $u->doUpdate(); 01011 } 01012 01013 $this->showArticle( $text, wfMsg( 'updated' ), $sectionanchor ); 01014 return true; 01015 } 01016 01017 # After we've either updated or inserted the article, update 01018 # the link tables and redirect to the new page. 01019 01020 function showArticle( $text, $subtitle , $sectionanchor = '' ) 01021 { 01022 global $wgOut, $wgUser, $wgLinkCache; 01023 global $wgMwRedir; 01024 01025 $wgLinkCache = new LinkCache(); 01026 01027 # Get old version of link table to allow incremental link updates 01028 $wgLinkCache->preFill( $this->mTitle ); 01029 $wgLinkCache->clear(); 01030 01031 # Now update the link cache by parsing the text 01032 $wgOut = new OutputPage(); 01033 $wgOut->addWikiText( $text ); 01034 01035 if( $wgMwRedir->matchStart( $text ) ) 01036 $r = 'redirect=no'; 01037 else 01038 $r = ''; 01039 $wgOut->redirect( $this->mTitle->getFullURL( $r ).$sectionanchor ); 01040 } 01041 01042 # Add this page to my watchlist 01043 01044 function watch( $add = true ) 01045 { 01046 global $wgUser, $wgOut, $wgLang; 01047 global $wgDeferredUpdateList; 01048 01049 if ( 0 == $wgUser->getID() ) { 01050 $wgOut->errorpage( 'watchnologin', 'watchnologintext' ); 01051 return; 01052 } 01053 if ( wfReadOnly() ) { 01054 $wgOut->readOnlyPage(); 01055 return; 01056 } 01057 if( $add ) 01058 $wgUser->addWatch( $this->mTitle ); 01059 else 01060 $wgUser->removeWatch( $this->mTitle ); 01061 01062 $wgOut->setPagetitle( wfMsg( $add ? 'addedwatch' : 'removedwatch' ) ); 01063 $wgOut->setRobotpolicy( 'noindex,follow' ); 01064 01065 $sk = $wgUser->getSkin() ; 01066 $link = $this->mTitle->getPrefixedText(); 01067 01068 if($add) 01069 $text = wfMsg( 'addedwatchtext', $link ); 01070 else 01071 $text = wfMsg( 'removedwatchtext', $link ); 01072 $wgOut->addWikiText( $text ); 01073 01074 $up = new UserUpdate(); 01075 array_push( $wgDeferredUpdateList, $up ); 01076 01077 $wgOut->returnToMain( false ); 01078 } 01079 01080 function unwatch() 01081 { 01082 $this->watch( false ); 01083 } 01084 01085 function protect( $limit = 'sysop' ) 01086 { 01087 global $wgUser, $wgOut, $wgRequest; 01088 01089 if ( ! $wgUser->isSysop() ) { 01090 $wgOut->sysopRequired(); 01091 return; 01092 } 01093 if ( wfReadOnly() ) { 01094 $wgOut->readOnlyPage(); 01095 return; 01096 } 01097 $id = $this->mTitle->getArticleID(); 01098 if ( 0 == $id ) { 01099 $wgOut->fatalError( wfMsg( 'badarticleerror' ) ); 01100 return; 01101 } 01102 01103 $confirm = $wgRequest->getBool( 'wpConfirmProtect' ) && $wgRequest->wasPosted(); 01104 $reason = $wgRequest->getText( 'wpReasonProtect' ); 01105 01106 if ( $confirm ) { 01107 01108 $sql = "UPDATE cur SET cur_touched='" . wfTimestampNow() . "'," . 01109 "cur_restrictions='{$limit}' WHERE cur_id={$id}"; 01110 wfQuery( $sql, DB_WRITE, 'Article::protect' ); 01111 01112 $log = new LogPage( wfMsg( 'protectlogpage' ), wfMsg( 'protectlogtext' ) ); 01113 if ( $limit === "" ) { 01114 $log->addEntry( wfMsg( 'unprotectedarticle', $this->mTitle->getPrefixedText() ), $reason ); 01115 } else { 01116 $log->addEntry( wfMsg( 'protectedarticle', $this->mTitle->getPrefixedText() ), $reason ); 01117 } 01118 $wgOut->redirect( $this->mTitle->getFullURL() ); 01119 return; 01120 } else { 01121 $reason = htmlspecialchars( wfMsg( 'protectreason' ) ); 01122 return $this->confirmProtect( '', $reason, $limit ); 01123 } 01124 } 01125 01126 # Output protection confirmation dialog 01127 function confirmProtect( $par, $reason, $limit = 'sysop' ) 01128 { 01129 global $wgOut; 01130 01131 wfDebug( "Article::confirmProtect\n" ); 01132 01133 $sub = htmlspecialchars( $this->mTitle->getPrefixedText() ); 01134 $wgOut->setRobotpolicy( 'noindex,nofollow' ); 01135 01136 $check = ''; 01137 $protcom = ''; 01138 01139 if ( $limit === '' ) { 01140 $wgOut->setSubtitle( wfMsg( 'unprotectsub', $sub ) ); 01141 $wgOut->addWikiText( wfMsg( 'confirmunprotecttext' ) ); 01142 $check = htmlspecialchars( wfMsg( 'confirmunprotect' ) ); 01143 $protcom = htmlspecialchars( wfMsg( 'unprotectcomment' ) ); 01144 $formaction = $this->mTitle->escapeLocalURL( 'action=unprotect' . $par ); 01145 } else { 01146 $wgOut->setSubtitle( wfMsg( 'protectsub', $sub ) ); 01147 $wgOut->addWikiText( wfMsg( 'confirmprotecttext' ) ); 01148 $check = htmlspecialchars( wfMsg( 'confirmprotect' ) ); 01149 $protcom = htmlspecialchars( wfMsg( 'protectcomment' ) ); 01150 $formaction = $this->mTitle->escapeLocalURL( 'action=protect' . $par ); 01151 } 01152 01153 $confirm = htmlspecialchars( wfMsg( 'confirm' ) ); 01154 01155 $wgOut->addHTML( " 01156 <form id='protectconfirm' method='post' action=\"{$formaction}\"> 01157 <table border='0'> 01158 <tr> 01159 <td align='right'> 01160 <label for='wpReasonProtect'>{$protcom}:</label> 01161 </td> 01162 <td align='left'> 01163 <input type='text' size='60' name='wpReasonProtect' id='wpReasonProtect' value=\"" . htmlspecialchars( $reason ) . "\" /> 01164 </td> 01165 </tr> 01166 <tr> 01167 <td>&nbsp;</td> 01168 </tr> 01169 <tr> 01170 <td align='right'> 01171 <input type='checkbox' name='wpConfirmProtect' value='1' id='wpConfirmProtect' /> 01172 </td> 01173 <td> 01174 <label for='wpConfirmProtect'>{$check}</label> 01175 </td> 01176 </tr> 01177 <tr> 01178 <td>&nbsp;</td> 01179 <td> 01180 <input type='submit' name='wpConfirmProtectB' value=\"{$confirm}\" /> 01181 </td> 01182 </tr> 01183 </table> 01184 </form>\n" ); 01185 01186 $wgOut->returnToMain( false ); 01187 } 01188 01189 function unprotect() 01190 { 01191 return $this->protect( '' ); 01192 } 01193 01194 # UI entry point for page deletion 01195 function delete() 01196 { 01197 global $wgUser, $wgOut, $wgMessageCache, $wgRequest, $wgIsPg; 01198 $fname = 'Article::delete'; 01199 $confirm = $wgRequest->getBool( 'wpConfirm' ) && $wgRequest->wasPosted(); 01200 $reason = $wgRequest->getText( 'wpReason' ); 01201 01202 # This code desperately needs to be totally rewritten 01203 01204 # Check permissions 01205 if ( ( ! $wgUser->isSysop() ) ) { 01206 $wgOut->sysopRequired(); 01207 return; 01208 } 01209 if ( wfReadOnly() ) { 01210 $wgOut->readOnlyPage(); 01211 return; 01212 } 01213 01214 # Better double-check that it hasn't been deleted yet! 01215 $wgOut->setPagetitle( wfMsg( 'confirmdelete' ) ); 01216 if ( ( '' == trim( $this->mTitle->getText() ) ) 01217 or ( $this->mTitle->getArticleId() == 0 ) ) { 01218 $wgOut->fatalError( wfMsg( 'cannotdelete' ) ); 01219 return; 01220 } 01221 01222 if ( $confirm ) { 01223 $this->doDelete( $reason ); 01224 return; 01225 } 01226 01227 # determine whether this page has earlier revisions 01228 # and insert a warning if it does 01229 # we select the text because it might be useful below 01230 $ns = $this->mTitle->getNamespace(); 01231 $title = $this->mTitle->getDBkey(); 01232 $etitle = wfStrencode( $title ); 01233 $oldtable=$wgIsPg?'"old"':'old'; 01234 $sql = "SELECT old_text,old_flags FROM $oldtable WHERE old_namespace=$ns and old_title='$etitle' ORDER BY inverse_timestamp LIMIT 1"; 01235 $res = wfQuery( $sql, DB_READ, $fname ); 01236 if( ($old=wfFetchObject($res)) && !$confirm ) { 01237 $skin=$wgUser->getSkin(); 01238 $wgOut->addHTML('<b>'.wfMsg('historywarning')); 01239 $wgOut->addHTML( $skin->historyLink() .'</b>'); 01240 } 01241 01242 $sql="SELECT cur_text FROM cur WHERE cur_namespace=$ns and cur_title='$etitle'"; 01243 $res=wfQuery($sql, DB_READ, $fname); 01244 if( ($s=wfFetchObject($res))) { 01245 01246 # if this is a mini-text, we can paste part of it into the deletion reason 01247 01248 #if this is empty, an earlier revision may contain "useful" text 01249 $blanked = false; 01250 if($s->cur_text!="") { 01251 $text=$s->cur_text; 01252 } else { 01253 if($old) { 01254 $text = Article::getRevisionText( $old ); 01255 $blanked = true; 01256 } 01257 01258 } 01259 01260 $length=strlen($text); 01261 01262 # this should not happen, since it is not possible to store an empty, new 01263 # page. Let's insert a standard text in case it does, though 01264 if($length == 0 && $reason === '') { 01265 $reason = wfMsg('exblank'); 01266 } 01267 01268 if($length < 500 && $reason === '') { 01269 01270 # comment field=255, let's grep the first 150 to have some user 01271 # space left 01272 $text=substr($text,0,150); 01273 # let's strip out newlines and HTML tags 01274 $text=preg_replace('/\"/',"'",$text); 01275 $text=preg_replace('/</','&lt;',$text); 01276 $text=preg_replace('/>/','&gt;',$text); 01277 $text=preg_replace("/[\n\r]/",'',$text); 01278 if(!$blanked) { 01279 $reason=wfMsg('excontent'). " '".$text; 01280 } else { 01281 $reason=wfMsg('exbeforeblank') . " '".$text; 01282 } 01283 if($length>150) { $reason .= '...'; } # we've only pasted part of the text 01284 $reason.="'"; 01285 } 01286 } 01287 01288 return $this->confirmDelete( '', $reason ); 01289 } 01290 01291 # Output deletion confirmation dialog 01292 function confirmDelete( $par, $reason ) 01293 { 01294 global $wgOut; 01295 01296 wfDebug( "Article::confirmDelete\n" ); 01297 01298 $sub = htmlspecialchars( $this->mTitle->getPrefixedText() ); 01299 $wgOut->setSubtitle( wfMsg( 'deletesub', $sub ) ); 01300 $wgOut->setRobotpolicy( 'noindex,nofollow' ); 01301 $wgOut->addWikiText( wfMsg( 'confirmdeletetext' ) ); 01302 01303 $formaction = $this->mTitle->escapeLocalURL( 'action=delete' . $par ); 01304 01305 $confirm = htmlspecialchars( wfMsg( 'confirm' ) ); 01306 $check = htmlspecialchars( wfMsg( 'confirmcheck' ) ); 01307 $delcom = htmlspecialchars( wfMsg( 'deletecomment' ) ); 01308 01309 $wgOut->addHTML( " 01310 <form id='deleteconfirm' method='post' action=\"{$formaction}\"> 01311 <table border='0'> 01312 <tr> 01313 <td align='right'> 01314 <label for='wpReason'>{$delcom}:</label> 01315 </td> 01316 <td align='left'> 01317 <input type='text' size='60' name='wpReason' id='wpReason' value=\"" . htmlspecialchars( $reason ) . "\" /> 01318 </td> 01319 </tr> 01320 <tr> 01321 <td>&nbsp;</td> 01322 </tr> 01323 <tr> 01324 <td align='right'> 01325 <input type='checkbox' name='wpConfirm' value='1' id='wpConfirm' /> 01326 </td> 01327 <td> 01328 <label for='wpConfirm'>{$check}</label> 01329 </td> 01330 </tr> 01331 <tr> 01332 <td>&nbsp;</td> 01333 <td> 01334 <input type='submit' name='wpConfirmB' value=\"{$confirm}\" /> 01335 </td> 01336 </tr> 01337 </table> 01338 </form>\n" ); 01339 01340 $wgOut->returnToMain( false ); 01341 } 01342 01343 # Perform a deletion and output success or failure messages 01344 function doDelete( $reason ) 01345 { 01346 global $wgOut, $wgUser, $wgLang; 01347 $fname = 'Article::doDelete'; 01348 wfDebug( "$fname\n" ); 01349 01350 if ( $this->doDeleteArticle( $reason ) ) { 01351 $deleted = $this->mTitle->getPrefixedText(); 01352 01353 $wgOut->setPagetitle( wfMsg( 'actioncomplete' ) ); 01354 $wgOut->setRobotpolicy( 'noindex,nofollow' ); 01355 01356 $sk = $wgUser->getSkin(); 01357 $loglink = $sk->makeKnownLink( $wgLang->getNsText( 01358 Namespace::getWikipedia() ) . 01359 ':' . wfMsg( 'dellogpage' ), wfMsg( 'deletionlog' ) ); 01360 01361 $text = wfMsg( "deletedtext", $deleted, $loglink ); 01362 01363 $wgOut->addHTML( '<p>' . $text . "</p>\n" ); 01364 $wgOut->returnToMain( false ); 01365 } else { 01366 $wgOut->fatalError( wfMsg( 'cannotdelete' ) ); 01367 } 01368 } 01369 01370 # Back-end article deletion 01371 # Deletes the article with database consistency, writes logs, purges caches 01372 # Returns success 01373 function doDeleteArticle( $reason ) 01374 { 01375 global $wgUser, $wgLang; 01376 global $wgUseSquid, $wgDeferredUpdateList, $wgInternalServer; 01377 01378 $fname = 'Article::doDeleteArticle'; 01379 wfDebug( $fname."\n" ); 01380 01381 $ns = $this->mTitle->getNamespace(); 01382 $t = wfStrencode( $this->mTitle->getDBkey() ); 01383 $id = $this->mTitle->getArticleID(); 01384 01385 if ( '' == $t || $id == 0 ) { 01386 return false; 01387 } 01388 01389 $u = new SiteStatsUpdate( 0, 1, -$this->isCountable( $this->getContent( true ) ) ); 01390 array_push( $wgDeferredUpdateList, $u ); 01391 01392 $linksTo = $this->mTitle->getLinksTo(); 01393 01394 # Squid purging 01395 if ( $wgUseSquid ) { 01396 $urls = array( 01397 $this->mTitle->getInternalURL(), 01398 $this->mTitle->getInternalURL( 'history' ) 01399 ); 01400 foreach ( $linksTo as $linkTo ) { 01401 $urls[] = $linkTo->getInternalURL(); 01402 } 01403 01404 $u = new SquidUpdate( $urls ); 01405 array_push( $wgDeferredUpdateList, $u ); 01406 01407 } 01408 01409 # Client and file cache invalidation 01410 Title::touchArray( $linksTo ); 01411 01412 # Move article and history to the "archive" table 01413 $sql = 'INSERT INTO archive (ar_namespace,ar_title,ar_text,' . 01414 'ar_comment,ar_user,ar_user_text,ar_timestamp,ar_minor_edit,' . 01415 'ar_flags) SELECT cur_namespace,cur_title,cur_text,cur_comment,' . 01416 'cur_user,cur_user_text,cur_timestamp,cur_minor_edit,0 FROM cur ' . 01417 "WHERE cur_namespace={$ns} AND cur_title='{$t}'"; 01418 wfQuery( $sql, DB_WRITE, $fname ); 01419 01420 $sql = 'INSERT INTO archive (ar_namespace,ar_title,ar_text,' . 01421 'ar_comment,ar_user,ar_user_text,ar_timestamp,ar_minor_edit,' . 01422 'ar_flags) SELECT old_namespace,old_title,old_text,old_comment,' . 01423 'old_user,old_user_text,old_timestamp,old_minor_edit,old_flags ' . 01424 "FROM old WHERE old_namespace={$ns} AND old_title='{$t}'"; 01425 wfQuery( $sql, DB_WRITE, $fname ); 01426 01427 # Now that it's safely backed up, delete it 01428 01429 $sql = "DELETE FROM cur WHERE cur_namespace={$ns} AND " . 01430 "cur_title='{$t}'"; 01431 wfQuery( $sql, DB_WRITE, $fname ); 01432 01433 $sql = "DELETE FROM old WHERE old_namespace={$ns} AND " . 01434 "old_title='{$t}'"; 01435 wfQuery( $sql, DB_WRITE, $fname ); 01436 01437 $sql = "DELETE FROM recentchanges WHERE rc_namespace={$ns} AND " . 01438 "rc_title='{$t}'"; 01439 wfQuery( $sql, DB_WRITE, $fname ); 01440 01441 # Finally, clean up the link tables 01442 $t = wfStrencode( $this->mTitle->getPrefixedDBkey() ); 01443 01444 Article::onArticleDelete( $this->mTitle ); 01445 01446 $sql = 'INSERT INTO brokenlinks (bl_from,bl_to) VALUES '; 01447 $first = true; 01448 01449 foreach ( $linksTo as $titleObj ) { 01450 if ( ! $first ) { $sql .= ','; } 01451 $first = false; 01452 # Get article ID. Efficient because it was loaded into the cache by getLinksTo(). 01453 $linkID = $titleObj->getArticleID(); 01454 $sql .= "({$linkID},'{$t}')"; 01455 } 01456 if ( ! $first ) { 01457 wfQuery( $sql, DB_WRITE, $fname ); 01458 } 01459 01460 $sql = "DELETE FROM links WHERE l_to={$id}"; 01461 wfQuery( $sql, DB_WRITE, $fname ); 01462 01463 $sql = "DELETE FROM links WHERE l_from={$id}"; 01464 wfQuery( $sql, DB_WRITE, $fname ); 01465 01466 $sql = "DELETE FROM imagelinks WHERE il_from={$id}"; 01467 wfQuery( $sql, DB_WRITE, $fname ); 01468 01469 $sql = "DELETE FROM brokenlinks WHERE bl_from={$id}"; 01470 wfQuery( $sql, DB_WRITE, $fname ); 01471 01472 $sql = "DELETE FROM categorylinks WHERE cl_from={$id}"; 01473 wfQuery( $sql, DB_WRITE, $fname ); 01474 01475 $log = new LogPage( wfMsg( 'dellogpage' ), wfMsg( 'dellogpagetext' ) ); 01476 $art = $this->mTitle->getPrefixedText(); 01477 $log->addEntry( wfMsg( 'deletedarticle', $art ), $reason ); 01478 01479 # Clear the cached article id so the interface doesn't act like we exist 01480 $this->mTitle->resetArticleID( 0 ); 01481 $this->mTitle->mArticleID = 0; 01482 return true; 01483 } 01484 01485 function rollback() 01486 { 01487 global $wgUser, $wgLang, $wgOut, $wgRequest, $wgIsMySQL, $wgIsPg; 01488 01489 if ( ! $wgUser->isSysop() ) { 01490 $wgOut->sysopRequired(); 01491 return; 01492 } 01493 if ( wfReadOnly() ) { 01494 $wgOut->readOnlyPage( $this->getContent( true ) ); 01495 return; 01496 } 01497 01498 # Enhanced rollback, marks edits rc_bot=1 01499 $bot = $wgRequest->getBool( 'bot' ); 01500 01501 # Replace all this user's current edits with the next one down 01502 $tt = wfStrencode( $this->mTitle->getDBKey() ); 01503 $n = $this->mTitle->getNamespace(); 01504 01505 # Get the last editor 01506 $sql = 'SELECT cur_id,cur_user,cur_user_text,cur_comment ' . 01507 "FROM cur WHERE cur_title='{$tt}' AND cur_namespace={$n}"; 01508 $res = wfQuery( $sql, DB_READ ); 01509 if( ($x = wfNumRows( $res )) != 1 ) { 01510 # Something wrong 01511 $wgOut->addHTML( wfMsg( 'notanarticle' ) ); 01512 return; 01513 } 01514 $s = wfFetchObject( $res ); 01515 $ut = wfStrencode( $s->cur_user_text ); 01516 $uid = $s->cur_user; 01517 $pid = $s->cur_id; 01518 01519 $from = str_replace( '_', ' ', $wgRequest->getVal( 'from' ) ); 01520 if( $from != $s->cur_user_text ) { 01521 $wgOut->setPageTitle(wfmsg('rollbackfailed')); 01522 $wgOut->addWikiText( wfMsg( 'alreadyrolled', 01523 htmlspecialchars( $this->mTitle->getPrefixedText()), 01524 htmlspecialchars( $from ), 01525 htmlspecialchars( $s->cur_user_text ) ) ); 01526 if($s->cur_comment != '') { 01527 $wgOut->addHTML( 01528 wfMsg('editcomment', 01529 htmlspecialchars( $s->cur_comment ) ) ); 01530 } 01531 return; 01532 } 01533 01534 # Get the last edit not by this guy 01535 01536 $use_index=$wgIsMySQL?"USE INDEX (name_title_timestamp)":""; 01537 $oldtable=$wgIsPg?'"old"':'old'; 01538 $sql = 'SELECT old_text,old_user,old_user_text,old_timestamp,old_flags ' . 01539 "FROM $oldtable {$use_index} " . 01540 "WHERE old_namespace={$n} AND old_title='{$tt}' " . 01541 "AND (old_user <> {$uid} OR old_user_text <> '{$ut}') " . 01542 'ORDER BY inverse_timestamp LIMIT 1'; 01543 $res = wfQuery( $sql, DB_READ ); 01544 if( wfNumRows( $res ) != 1 ) { 01545 # Something wrong 01546 $wgOut->setPageTitle(wfMsg('rollbackfailed')); 01547 $wgOut->addHTML( wfMsg( 'cantrollback' ) ); 01548 return; 01549 } 01550 $s = wfFetchObject( $res ); 01551 01552 if ( $bot ) { 01553 # Mark all reverted edits as bot 01554 $sql = 'UPDATE recentchanges SET rc_bot=1 WHERE ' . 01555 "rc_cur_id=$pid AND rc_user=$uid AND rc_timestamp > '{$s->old_timestamp}'"; 01556 wfQuery( $sql, DB_WRITE, $fname ); 01557 } 01558 01559 # Save it! 01560 $newcomment = wfMsg( 'revertpage', $s->old_user_text, $from ); 01561 $wgOut->setPagetitle( wfMsg( 'actioncomplete' ) ); 01562 $wgOut->setRobotpolicy( 'noindex,nofollow' ); 01563 $wgOut->addHTML( '<h2>' . $newcomment . "</h2>\n<hr />\n" ); 01564 $this->updateArticle( Article::getRevisionText( $s ), $newcomment, 1, $this->mTitle->userIsWatching(), $bot ); 01565 Article::onArticleEdit( $this->mTitle ); 01566 $wgOut->returnToMain( false ); 01567 } 01568 01569 01570 # Do standard deferred updates after page view 01571 01572 /* private */ function viewUpdates() 01573 { 01574 global $wgDeferredUpdateList; 01575 if ( 0 != $this->getID() ) { 01576 global $wgDisableCounters; 01577 if( !$wgDisableCounters ) { 01578 Article::incViewCount( $this->getID() ); 01579 $u = new SiteStatsUpdate( 1, 0, 0 ); 01580 array_push( $wgDeferredUpdateList, $u ); 01581 } 01582 $u = new UserTalkUpdate( 0, $this->mTitle->getNamespace(), 01583 $this->mTitle->getDBkey() ); 01584 array_push( $wgDeferredUpdateList, $u ); 01585 } 01586 } 01587 01588 # Do standard deferred updates after page edit. 01589 # Every 1000th edit, prune the recent changes table. 01590 01591 /* private */ function editUpdates( $text ) 01592 { 01593 global $wgDeferredUpdateList, $wgDBname, $wgMemc; 01594 global $wgMessageCache; 01595 01596 wfSeedRandom(); 01597 if ( 0 == mt_rand( 0, 999 ) ) { 01598 $cutoff = wfUnix2Timestamp( time() - ( 7 * 86400 ) ); 01599 $sql = "DELETE FROM recentchanges WHERE rc_timestamp < '{$cutoff}'"; 01600 wfQuery( $sql, DB_WRITE ); 01601 } 01602 $id = $this->getID(); 01603 $title = $this->mTitle->getPrefixedDBkey(); 01604 $shortTitle = $this->mTitle->getDBkey(); 01605 01606 $adj = $this->mCountAdjustment; 01607 01608 if ( 0 != $id ) { 01609 $u = new LinksUpdate( $id, $title ); 01610 array_push( $wgDeferredUpdateList, $u ); 01611 $u = new SiteStatsUpdate( 0, 1, $adj ); 01612 array_push( $wgDeferredUpdateList, $u ); 01613 $u = new SearchUpdate( $id, $title, $text ); 01614 array_push( $wgDeferredUpdateList, $u ); 01615 01616 $u = new UserTalkUpdate( 1, $this->mTitle->getNamespace(), $shortTitle ); 01617 array_push( $wgDeferredUpdateList, $u ); 01618 01619 if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) { 01620 $wgMessageCache->replace( $shortTitle, $text ); 01621 } 01622 } 01623 } 01624 01625 /* private */ function setOldSubtitle() 01626 { 01627 global $wgLang, $wgOut; 01628 01629 $td = $wgLang->timeanddate( $this->mTimestamp, true ); 01630 $r = wfMsg( 'revisionasof', $td ); 01631 $wgOut->setSubtitle( "({$r})" ); 01632 } 01633 01634 # This function is called right before saving the wikitext, 01635 # so we can do things like signatures and links-in-context. 01636 01637 function preSaveTransform( $text ) 01638 { 01639 global $wgParser, $wgUser; 01640 return $wgParser->preSaveTransform( $text, $this->mTitle, $wgUser, ParserOptions::newFromUser( $wgUser ) ); 01641 } 01642 01643 /* Caching functions */ 01644 01645 # checkLastModified returns true if it has taken care of all 01646 # output to the client that is necessary for this request. 01647 # (that is, it has sent a cached version of the page) 01648 function tryFileCache() { 01649 static $called = false; 01650 if( $called ) { 01651 wfDebug( " tryFileCache() -- called twice!?\n" ); 01652 return; 01653 } 01654 $called = true; 01655 if($this->isFileCacheable()) { 01656 $touched = $this->mTouched; 01657 if( $this->mTitle->getPrefixedDBkey() == wfMsg( 'mainpage' ) ) { 01658 # Expire the main page quicker 01659 $expire = wfUnix2Timestamp( time() - 3600 ); 01660 $touched = max( $expire, $touched ); 01661 } 01662 $cache = new CacheManager( $this->mTitle ); 01663 if($cache->isFileCacheGood( $touched )) { 01664 global $wgOut; 01665 wfDebug( " tryFileCache() - about to load\n" ); 01666 $cache->loadFromFileCache(); 01667 return true; 01668 } else { 01669 wfDebug( " tryFileCache() - starting buffer\n" ); 01670 ob_start( array(&$cache, 'saveToFileCache' ) ); 01671 } 01672 } else { 01673 wfDebug( " tryFileCache() - not cacheable\n" ); 01674 } 01675 } 01676 01677 function isFileCacheable() { 01678 global $wgUser, $wgUseFileCache, $wgShowIPinHeader, $wgRequest; 01679 extract( $wgRequest->getValues( 'action', 'oldid', 'diff', 'redirect', 'printable' ) ); 01680 01681 return $wgUseFileCache 01682 and (!$wgShowIPinHeader) 01683 and ($this->getID() != 0) 01684 and ($wgUser->getId() == 0) 01685 and (!$wgUser->getNewtalk()) 01686 and ($this->mTitle->getNamespace() != Namespace::getSpecial()) 01687 and ($action == 'view') 01688 and (!isset($oldid)) 01689 and (!isset($diff)) 01690 and (!isset($redirect)) 01691 and (!isset($printable)) 01692 and (!$this->mRedirectedFrom); 01693 } 01694 01695 # Loads cur_touched and returns a value indicating if it should be used 01696 function checkTouched() { 01697 $id = $this->getID(); 01698 $sql = 'SELECT cur_touched,cur_is_redirect FROM cur WHERE cur_id='.$id; 01699 $res = wfQuery( $sql, DB_READ, 'Article::checkTouched' ); 01700 if( $s = wfFetchObject( $res ) ) { 01701 $this->mTouched = $s->cur_touched; 01702 return !$s->cur_is_redirect; 01703 } else { 01704 return false; 01705 } 01706 } 01707 01708 # Edit an article without doing all that other stuff 01709 function quickEdit( $text, $comment = '', $minor = 0 ) { 01710 global $wgUser, $wgMwRedir, $wgIsPg; 01711 $fname = 'Article::quickEdit'; 01712 wfProfileIn( $fname ); 01713 01714 $ns = $this->mTitle->getNamespace(); 01715 $dbkey = $this->mTitle->getDBkey(); 01716 $encDbKey = wfStrencode( $dbkey ); 01717 $timestamp = wfTimestampNow(); 01718 01719 # Save to history 01720 $oldtable=$wgIsPg?'"old"':'old'; 01721 $sql = "INSERT INTO $oldtable (old_namespace,old_title,old_text,old_comment,old_user,old_user_text,old_timestamp,inverse_timestamp) 01722 SELECT cur_namespace,cur_title,cur_text,cur_comment,cur_user,cur_user_text,cur_timestamp,99999999999999-cur_timestamp 01723 FROM cur WHERE cur_namespace=$ns AND cur_title='$encDbKey'"; 01724 wfQuery( $sql, DB_WRITE ); 01725 01726 # Use the affected row count to determine if the article is new 01727 $numRows = wfAffectedRows(); 01728 01729 # Make an array of fields to be inserted 01730 $fields = array( 01731 'cur_text' => $text, 01732 'cur_timestamp' => $timestamp, 01733 'cur_user' => $wgUser->getID(), 01734 'cur_user_text' => $wgUser->getName(), 01735 'inverse_timestamp' => wfInvertTimestamp( $timestamp ), 01736 'cur_comment' => $comment, 01737 'cur_is_redirect' => $wgMwRedir->matchStart( $text ) ? 1 : 0, 01738 'cur_minor_edit' => intval($minor), 01739 'cur_touched' => $timestamp, 01740 ); 01741 01742 if ( $numRows ) { 01743 # Update article 01744 $fields['cur_is_new'] = 0; 01745 wfUpdateArray( 'cur', $fields, array( 'cur_namespace' => $ns, 'cur_title' => $dbkey ), $fname ); 01746 } else { 01747 # Insert new article 01748 $fields['cur_is_new'] = 1; 01749 $fields['cur_namespace'] = $ns; 01750 $fields['cur_title'] = $dbkey; 01751 $fields['cur_random'] = $rand = number_format( mt_rand() / mt_getrandmax(), 12, '.', '' ); 01752 wfInsertArray( 'cur', $fields, $fname ); 01753 } 01754 wfProfileOut( $fname ); 01755 } 01756 01757 /* static */ function incViewCount( $id ) 01758 { 01759 $id = intval( $id ); 01760 global $wgHitcounterUpdateFreq; 01761 01762 if( $wgHitcounterUpdateFreq <= 1 ){ // 01763 wfQuery('UPDATE cur SET cur_counter = cur_counter + 1 ' . 01764 'WHERE cur_id = '.$id, DB_WRITE); 01765 return; 01766 } 01767 01768 # Not important enough to warrant an error page in case of failure 01769 $oldignore = wfIgnoreSQLErrors( true ); 01770 01771 wfQuery("INSERT INTO hitcounter (hc_id) VALUES ({$id})", DB_WRITE); 01772 01773 $checkfreq = intval( $wgHitcounterUpdateFreq/25 + 1 ); 01774 if( (rand() % $checkfreq != 0) or (wfLastErrno() != 0) ){ 01775 # Most of the time (or on SQL errors), skip row count check 01776 wfIgnoreSQLErrors( $oldignore ); 01777 return; 01778 } 01779 01780 $res = wfQuery('SELECT COUNT(*) as n FROM hitcounter', DB_WRITE); 01781 $row = wfFetchObject( $res ); 01782 $rown = intval( $row->n ); 01783 if( $rown >= $wgHitcounterUpdateFreq ){ 01784 wfProfileIn( 'Article::incViewCount-collect' ); 01785 $old_user_abort = ignore_user_abort( true ); 01786 01787 wfQuery('LOCK TABLES hitcounter WRITE', DB_WRITE); 01788 wfQuery('CREATE TEMPORARY TABLE acchits TYPE=HEAP '. 01789 'SELECT hc_id,COUNT(*) AS hc_n FROM hitcounter '. 01790 'GROUP BY hc_id', DB_WRITE); 01791 wfQuery('DELETE FROM hitcounter', DB_WRITE); 01792 wfQuery('UNLOCK TABLES', DB_WRITE); 01793 wfQuery('UPDATE cur,acchits SET cur_counter=cur_counter + hc_n '. 01794 'WHERE cur_id = hc_id', DB_WRITE); 01795 wfQuery('DROP TABLE acchits', DB_WRITE); 01796 01797 ignore_user_abort( $old_user_abort ); 01798 wfProfileOut( 'Article::incViewCount-collect' ); 01799 } 01800 wfIgnoreSQLErrors( $oldignore ); 01801 } 01802 01803 # The onArticle*() functions are supposed to be a kind of hooks 01804 # which should be called whenever any of the specified actions 01805 # are done. 01806 # 01807 # This is a good place to put code to clear caches, for instance. 01808 01809 # This is called on page move and undelete, as well as edit 01810 /* static */ function onArticleCreate($title_obj){ 01811 global $wgUseSquid, $wgDeferredUpdateList; 01812 01813 $titles = $title_obj->getBrokenLinksTo(); 01814 01815 # Purge squid 01816 if ( $wgUseSquid ) { 01817 $urls = $title_obj->getSquidURLs(); 01818 foreach ( $titles as $linkTitle ) { 01819 $urls[] = $linkTitle->getInternalURL(); 01820 } 01821 $u = new SquidUpdate( $urls ); 01822 array_push( $wgDeferredUpdateList, $u ); 01823 } 01824 01825 # Clear persistent link cache 01826 LinkCache::linksccClearBrokenLinksTo( $title_obj->getPrefixedDBkey() ); 01827 } 01828 01829 /* static */ function onArticleDelete($title_obj){ 01830 LinkCache::linksccClearLinksTo( $title_obj->getArticleID() ); 01831 } 01832 01833 /* static */ function onArticleEdit($title_obj){ 01834 LinkCache::linksccClearPage( $title_obj->getArticleID() ); 01835 } 01836 } 01837 01838 ?>

Generated on Tue Jun 29 23:40:02 2004 for Mediawiki by doxygen 1.3.7