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 var $mContent, $mContentLoaded;
00014 var $mUser, $mTimestamp, $mUserText;
00015 var $mCounter, $mComment, $mCountAdjustment;
00016 var $mMinorEdit, $mRedirectedFrom;
00017 var $mTouched, $mFileCache, $mTitle;
00018 var $mId, $mTable;
00019
00020 function Article( &$title ) {
00021 $this->mTitle =&
$title;
00022 $this->clear();
00023 }
00024
00025 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
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 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
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 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 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 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
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";
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]='';
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
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
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> </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> </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('/</','<',$text);
01276
$text=preg_replace('/>/','>',$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> </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> </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 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
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 function onArticleDelete($title_obj){
01830
LinkCache::linksccClearLinksTo( $title_obj->getArticleID() );
01831 }
01832
01833 function onArticleEdit($title_obj){
01834
LinkCache::linksccClearPage( $title_obj->getArticleID() );
01835 }
01836 }
01837
01838 ?>