X-Git-Url: http://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2FArticle.php;h=b588066135a5dca8d0f6902a6fc6c42bda08160c;hb=e8b8cce1794e4602c82d7c7e266db91cdd7c0eb0;hp=aedebdb0beeb3881dc0581537d7c3b6526333e48;hpb=80441d522a24a0884216041934bda85635ee0f5f;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/Article.php b/includes/Article.php index aedebdb0be..b588066135 100644 --- a/includes/Article.php +++ b/includes/Article.php @@ -2,114 +2,134 @@ # Class representing a Wikipedia article and history. # See design.doc for an overview. +# Note: edit user interface and cache support functions have been +# moved to separate EditPage and CacheManager classes. + +/* CHECK MERGE @@@ + TEST THIS @@@ + + * s/\$wgTitle/\$this->mTitle/ performed, many replacements + * mTitle variable added to class +*/ + +include_once( "CacheManager.php" ); + class Article { /* private */ var $mContent, $mContentLoaded; /* private */ var $mUser, $mTimestamp, $mUserText; /* private */ var $mCounter, $mComment, $mCountAdjustment; /* private */ var $mMinorEdit, $mRedirectedFrom; - /* private */ var $mTouched, $mFileCache; - - function Article() { $this->clear(); } - + /* private */ var $mTouched, $mFileCache, $mTitle; + /* private */ var $mId, $mTable; + + function Article( &$title ) { + $this->mTitle =& $title; + $this->clear(); + } + /* private */ function clear() { $this->mContentLoaded = false; - $this->mUser = $this->mCounter = -1; # Not loaded + $this->mCurID = $this->mUser = $this->mCounter = -1; # Not loaded $this->mRedirectedFrom = $this->mUserText = $this->mTimestamp = $this->mComment = $this->mFileCache = ""; $this->mCountAdjustment = 0; $this->mTouched = "19700101000000"; } - /* static */ function newFromID( $newid ) - { - global $wgOut, $wgTitle, $wgArticle; - $a = new Article(); - $n = Article::nameOf( $newid ); - - $wgTitle = Title::newFromDBkey( $n ); - $wgTitle->resetArticleID( $newid ); - - return $a; + /* static */ function getRevisionText( $row, $prefix = "old_" ) { + # Deal with optional compression of archived pages. + # This can be done periodically via maintenance/compressOld.php, and + # as pages are saved if $wgCompressRevisions is set. + $text = $prefix . "text"; + $flags = $prefix . "flags"; + if( isset( $row->$flags ) && (false !== strpos( $row->$flags, "gzip" ) ) ) { + return gzinflate( $row->$text ); + } + if( isset( $row->$text ) ) { + return $row->$text; + } + return false; } - - /* static */ function nameOf( $id ) - { - $sql = "SELECT cur_namespace,cur_title FROM cur WHERE " . - "cur_id={$id}"; - $res = wfQuery( $sql, "Article::nameOf" ); - if ( 0 == wfNumRows( $res ) ) { return NULL; } - - $s = wfFetchObject( $res ); - $n = Title::makeName( $s->cur_namespace, $s->cur_title ); - return $n; + + /* static */ function compressRevisionText( &$text ) { + global $wgCompressRevisions; + if( !$wgCompressRevisions ) { + return ""; + } + if( !function_exists( "gzdeflate" ) ) { + wfDebug( "Article::compressRevisionText() -- no zlib support, not compressing\n" ); + return ""; + } + $text = gzdeflate( $text ); + return "gzip"; } - + # Note that getContent/loadContent may follow redirects if - # not told otherwise, and so may cause a change to wgTitle. + # not told otherwise, and so may cause a change to mTitle. + # Return the text of this revision function getContent( $noredir = false ) { - global $action,$section,$count,$wgTitle; # From query string - wfProfileIn( "Article::getContent" ); + global $action,$section,$count; # From query string + $fname = "Article::getContent"; + wfProfileIn( $fname ); if ( 0 == $this->getID() ) { if ( "edit" == $action ) { - - global $wgTitle; + wfProfileOut( $fname ); return ""; # was "newarticletext", now moved above the box) - - } - wfProfileOut(); + wfProfileOut( $fname ); return wfMsg( "noarticletext" ); } else { $this->loadContent( $noredir ); - wfProfileOut(); if( # check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page - ( $wgTitle->getNamespace() == Namespace::getTalk( Namespace::getUser()) ) && - preg_match("/^\d{1,3}\.\d{1,3}.\d{1,3}\.\d{1,3}$/",$wgTitle->getText()) && + ( $this->mTitle->getNamespace() == Namespace::getTalk( Namespace::getUser()) ) && + preg_match("/^\d{1,3}\.\d{1,3}.\d{1,3}\.\d{1,3}$/",$this->mTitle->getText()) && $action=="view" ) { + wfProfileOut( $fname ); return $this->mContent . "\n" .wfMsg("anontalkpagetext"); } else { if($action=="edit") { if($section!="") { + if($section=="new") { + wfProfileOut( $fname ); + return ""; + } - $secs=preg_split("/(^=+.*?=+)/m", + $secs=preg_split("/(^=+.*?=+|^.*?<\/h[1-6].*?>)/mi", $this->mContent, -1, PREG_SPLIT_DELIM_CAPTURE); if($section==0) { + wfProfileOut( $fname ); return trim($secs[0]); } else { + wfProfileOut( $fname ); return trim($secs[$section*2-1] . $secs[$section*2]); } } } + wfProfileOut( $fname ); return $this->mContent; } } } - + + # Load the revision (including cur_text) into this object function loadContent( $noredir = false ) { - global $wgOut, $wgTitle; + global $wgOut, $wgMwRedir; global $oldid, $redirect; # From query if ( $this->mContentLoaded ) return; $fname = "Article::loadContent"; - - # Pre-fill content with error message so that if something - # fails we'll have something telling us what we intended. - - $t = $wgTitle->getPrefixedText(); - if ( $oldid ) { $t .= ",oldid={$oldid}"; } - if ( $redirect ) { $t .= ",redirect={$redirect}"; } - $this->mContent = str_replace( "$1", $t, wfMsg( "missingarticle" ) ); - + $success = true; + if ( ! $oldid ) { # Retrieve current version $id = $this->getID(); if ( 0 == $id ) return; @@ -117,72 +137,97 @@ class Article { $sql = "SELECT " . "cur_text,cur_timestamp,cur_user,cur_counter,cur_restrictions,cur_touched " . "FROM cur WHERE cur_id={$id}"; - $res = wfQuery( $sql, $fname ); - if ( 0 == wfNumRows( $res ) ) { return; } + wfDebug( "$sql\n" ); + $res = wfQuery( $sql, DB_READ, $fname ); + if ( 0 == wfNumRows( $res ) ) { + return; + } $s = wfFetchObject( $res ); - # If we got a redirect, follow it (unless we've been told # not to by either the function parameter or the query - if ( ( "no" != $redirect ) && ( false == $noredir ) && - ( preg_match( "/^#redirect/i", $s->cur_text ) ) ) { + ( $wgMwRedir->matchStart( $s->cur_text ) ) ) { if ( preg_match( "/\\[\\[([^\\]\\|]+)[\\]\\|]/", $s->cur_text, $m ) ) { $rt = Title::newFromText( $m[1] ); - - # Gotta hand redirects to special pages differently: - # Fill the HTTP response "Location" header and ignore - # the rest of the page we're on. - - if ( $rt->getInterwiki() != "" ) { - $wgOut->redirect( $rt->getFullURL() ) ; - return; - } - if ( $rt->getNamespace() == Namespace::getSpecial() ) { - $wgOut->redirect( wfLocalUrl( - $rt->getPrefixedURL() ) ); - return; - } - $rid = $rt->getArticleID(); - if ( 0 != $rid ) { - $sql = "SELECT cur_text,cur_timestamp,cur_user," . - "cur_counter,cur_touched FROM cur WHERE cur_id={$rid}"; - $res = wfQuery( $sql, $fname ); - - if ( 0 != wfNumRows( $res ) ) { - $this->mRedirectedFrom = $wgTitle->getPrefixedText(); - $wgTitle = $rt; - $s = wfFetchObject( $res ); + if( $rt ) { + # Gotta hand redirects to special pages differently: + # Fill the HTTP response "Location" header and ignore + # the rest of the page we're on. + + if ( $rt->getInterwiki() != "" ) { + $wgOut->redirect( $rt->getFullURL() ) ; + return; + } + if ( $rt->getNamespace() == Namespace::getSpecial() ) { + $wgOut->redirect( wfLocalUrl( + $rt->getPrefixedURL() ) ); + return; + } + $rid = $rt->getArticleID(); + if ( 0 != $rid ) { + $sql = "SELECT cur_text,cur_timestamp,cur_user," . + "cur_counter,cur_restrictions,cur_touched FROM cur WHERE cur_id={$rid}"; + $res = wfQuery( $sql, DB_READ, $fname ); + + if ( 0 != wfNumRows( $res ) ) { + $this->mRedirectedFrom = $this->mTitle->getPrefixedText(); + $this->mTitle = $rt; + $s = wfFetchObject( $res ); + } } } } } + $this->mContent = $s->cur_text; $this->mUser = $s->cur_user; $this->mCounter = $s->cur_counter; $this->mTimestamp = $s->cur_timestamp; $this->mTouched = $s->cur_touched; - $wgTitle->mRestrictions = explode( ",", trim( $s->cur_restrictions ) ); - $wgTitle->mRestrictionsLoaded = true; + $this->mTitle->mRestrictions = explode( ",", trim( $s->cur_restrictions ) ); + $this->mTitle->mRestrictionsLoaded = true; wfFreeResult( $res ); } else { # oldid set, retrieve historical version - $sql = "SELECT old_text,old_timestamp,old_user FROM old " . + $sql = "SELECT old_text,old_timestamp,old_user,old_flags FROM old " . "WHERE old_id={$oldid}"; - $res = wfQuery( $sql, $fname ); + $res = wfQuery( $sql, DB_READ, $fname ); if ( 0 == wfNumRows( $res ) ) { return; } $s = wfFetchObject( $res ); - $this->mContent = $s->old_text; + $this->mContent = Article::getRevisionText( $s ); $this->mUser = $s->old_user; $this->mCounter = 0; $this->mTimestamp = $s->old_timestamp; wfFreeResult( $res ); } + + # Return error message :P + # Horrible, confusing UI and data. I think this should return false on error -- TS + if ( !$success ) { + $t = $this->mTitle->getPrefixedText(); + if ( isset( $oldid ) ) { + $oldid = IntVal( $oldid ); + $t .= ",oldid={$oldid}"; + } + if ( isset( $redirect ) ) { + $redirect = ($redirect == "no") ? "no" : "yes"; + $t .= ",redirect={$redirect}"; + } + $this->mContent = wfMsg( "missingarticle", $t ); + } + $this->mContentLoaded = true; } - function getID() { global $wgTitle; return $wgTitle->getArticleID(); } + function getID() { + if( $this->mTitle ) { + return $this->mTitle->getArticleID(); + } else { + return 0; + } + } function getCount() { @@ -198,16 +243,16 @@ class Article { function isCountable( $text ) { - global $wgTitle, $wgUseCommaCount; - - if ( 0 != $wgTitle->getNamespace() ) { return 0; } - if ( preg_match( "/^#redirect/i", $text ) ) { return 0; } + global $wgUseCommaCount, $wgMwRedir; + + if ( 0 != $this->mTitle->getNamespace() ) { return 0; } + if ( $wgMwRedir->matchStart( $text ) ) { return 0; } $token = ($wgUseCommaCount ? "," : "[[" ); if ( false === strstr( $text, $token ) ) { return 0; } return 1; } - # Load the field related to the last edit time of the article. + # Loads everything from cur except cur_text # This isn't necessary for all uses, so it's only done if needed. /* private */ function loadLastEdit() @@ -218,7 +263,7 @@ class Article { $sql = "SELECT cur_user,cur_user_text,cur_timestamp," . "cur_comment,cur_minor_edit FROM cur WHERE " . "cur_id=" . $this->getID(); - $res = wfQuery( $sql, "Article::loadLastEdit" ); + $res = wfQuery( $sql, DB_READ, "Article::loadLastEdit" ); if ( wfNumRows( $res ) > 0 ) { $s = wfFetchObject( $res ); @@ -265,10 +310,11 @@ class Article { function view() { - global $wgUser, $wgOut, $wgTitle, $wgLang; + global $wgUser, $wgOut, $wgLang; global $oldid, $diff; # From query - global $wgLinkCache; - wfProfileIn( "Article::view" ); + global $wgLinkCache, $IP; + $fname = "Article::view"; + wfProfileIn( $fname ); $wgOut->setArticleFlag( true ); $wgOut->setRobotpolicy( "index,follow" ); @@ -277,15 +323,27 @@ class Article { # diff page instead of the article. if ( isset( $diff ) ) { - $wgOut->setPageTitle( $wgTitle->getPrefixedText() ); + $wgOut->setPageTitle( $this->mTitle->getPrefixedText() ); $de = new DifferenceEngine( $oldid, $diff ); $de->showDiffPage(); - wfProfileOut(); + wfProfileOut( $fname ); return; } - $text = $this->getContent(); # May change wgTitle! - $wgOut->setPageTitle( $wgTitle->getPrefixedText() ); - $wgOut->setHTMLTitle( $wgTitle->getPrefixedText() . + + if ( !isset( $oldid ) and $this->checkTouched() ) { + if( $wgOut->checkLastModified( $this->mTouched ) ){ + return; + } else if ( $this->tryFileCache() ) { + # tell wgOut that output is taken care of + $wgOut->disable(); + $this->viewUpdates(); + return; + } + } + + $text = $this->getContent(); # May change mTitle + $wgOut->setPageTitle( $this->mTitle->getPrefixedText() ); + $wgOut->setHTMLTitle( $this->mTitle->getPrefixedText() . " - " . wfMsg( "wikititlesuffix" ) ); # We're looking at an old revision @@ -298,298 +356,15 @@ class Article { $sk = $wgUser->getSkin(); $redir = $sk->makeKnownLink( $this->mRedirectedFrom, "", "redirect=no" ); - $s = str_replace( "$1", $redir, wfMsg( "redirectedfrom" ) ); + $s = wfMsg( "redirectedfrom", $redir ); $wgOut->setSubtitle( $s ); } - $wgOut->checkLastModified( $this->mTouched ); - $this->tryFileCache(); - $wgLinkCache->preFill( $wgTitle ); - $wgOut->addWikiText( $text ); - # If the article we've just shown is in the "Image" namespace, - # follow it with the history list and link list for the image - # it describes. + $wgLinkCache->preFill( $this->mTitle ); + $wgOut->addWikiText( $text ); - if ( Namespace::getImage() == $wgTitle->getNamespace() ) { - $this->imageHistory(); - $this->imageLinks(); - } $this->viewUpdates(); - wfProfileOut(); - } - - # This is the function that gets called for "action=edit". - - function edit() - { - global $wgOut, $wgUser, $wgTitle; - global $wpTextbox1, $wpSummary, $wpSave, $wpPreview; - global $wpMinoredit, $wpEdittime, $wpTextbox2; - - $fields = array( "wpTextbox1", "wpSummary", "wpTextbox2" ); - wfCleanFormFields( $fields ); - - if ( ! $wgTitle->userCanEdit() ) { - $this->view(); - return; - } - if ( $wgUser->isBlocked() ) { - $this->blockedIPpage(); - return; - } - if ( wfReadOnly() ) { - if( isset( $wpSave ) or isset( $wpPreview ) ) { - $this->editForm( "preview" ); - } else { - $wgOut->readOnlyPage(); - } - return; - } - if ( $_SERVER['REQUEST_METHOD'] != "POST" ) unset( $wpSave ); - if ( isset( $wpSave ) ) { - $this->editForm( "save" ); - } else if ( isset( $wpPreview ) ) { - $this->editForm( "preview" ); - } else { # First time through - $this->editForm( "initial" ); - } - } - - # Since there is only one text field on the edit form, - # pressing will cause the form to be submitted, but - # the submit button value won't appear in the query, so we - # Fake it here before going back to edit(). This is kind of - # ugly, but it helps some old URLs to still work. - - function submit() - { - global $wpSave, $wpPreview; - if ( ! isset( $wpPreview ) ) { $wpSave = 1; } - - $this->edit(); - } - - # The edit form is self-submitting, so that when things like - # preview and edit conflicts occur, we get the same form back - # with the extra stuff added. Only when the final submission - # is made and all is well do we actually save and redirect to - # the newly-edited page. - - function editForm( $formtype ) - { - global $wgOut, $wgUser, $wgTitle; - global $wpTextbox1, $wpSummary, $wpWatchthis; - global $wpSave, $wpPreview; - global $wpMinoredit, $wpEdittime, $wpTextbox2, $wpSection; - global $oldid, $redirect, $section; - global $wgLang; - - if(isset($wpSection)) { $section=$wpSection; } - - $sk = $wgUser->getSkin(); - $isConflict = false; - $wpTextbox1 = rtrim ( $wpTextbox1 ) ; # To avoid text getting longer on each preview - - if(!$wgTitle->getArticleID()) { # new article - - $wgOut->addWikiText(wfmsg("newarticletext")); - - } - - # Attempt submission here. This will check for edit conflicts, - # and redundantly check for locked database, blocked IPs, etc. - # that edit() already checked just in case someone tries to sneak - # in the back door with a hand-edited submission URL. - - if ( "save" == $formtype ) { - if ( $wgUser->isBlocked() ) { - $this->blockedIPpage(); - return; - } - if ( wfReadOnly() ) { - $wgOut->readOnlyPage(); - return; - } - # If article is new, insert it. - - $aid = $wgTitle->getArticleID(); - if ( 0 == $aid ) { - # we need to strip Windoze linebreaks because some browsers - # append them and the string comparison fails - if ( ( "" == $wpTextbox1 ) || - ( wfMsg( "newarticletext" ) == rtrim( preg_replace("/\r/","",$wpTextbox1) ) ) ) { - $wgOut->redirect( wfLocalUrl( - $wgTitle->getPrefixedURL() ) ); - return; - } - $this->mCountAdjustment = $this->isCountable( $wpTextbox1 ); - $this->insertNewArticle( $wpTextbox1, $wpSummary, $wpMinoredit, $wpWatchthis ); - return; - } - # Article exists. Check for edit conflict. - - $this->clear(); # Force reload of dates, etc. - if ( $this->getTimestamp() != $wpEdittime ) { $isConflict = true; } - $u = $wgUser->getID(); - - # Supress edit conflict with self - - if ( ( 0 != $u ) && ( $this->getUser() == $u ) ) { - $isConflict = false; - } - if ( ! $isConflict ) { - # All's well: update the article here - $this->updateArticle( $wpTextbox1, $wpSummary, $wpMinoredit, $wpWatchthis, $wpSection ); - return; - } - } - # First time through: get contents, set time for conflict - # checking, etc. - - if ( "initial" == $formtype ) { - $wpEdittime = $this->getTimestamp(); - $wpTextbox1 = $this->getContent(true); - $wpSummary = ""; - } - $wgOut->setRobotpolicy( "noindex,nofollow" ); - $wgOut->setArticleFlag( false ); - - if ( $isConflict ) { - $s = str_replace( "$1", $wgTitle->getPrefixedText(), - wfMsg( "editconflict" ) ); - $wgOut->setPageTitle( $s ); - $wgOut->addHTML( wfMsg( "explainconflict" ) ); - - $wpTextbox2 = $wpTextbox1; - $wpTextbox1 = $this->getContent(true); - $wpEdittime = $this->getTimestamp(); - } else { - $s = str_replace( "$1", $wgTitle->getPrefixedText(), - wfMsg( "editing" ) ); - - if($section!="") { $s.=wfMsg("sectionedit");} - $wgOut->setPageTitle( $s ); - if ( $oldid ) { - $this->setOldSubtitle(); - $wgOut->addHTML( wfMsg( "editingold" ) ); - } - } - - if( wfReadOnly() ) { - $wgOut->addHTML( "" . - wfMsg( "readonlywarning" ) . - "" ); - } - if( $wgTitle->isProtected() ) { - $wgOut->addHTML( "" . wfMsg( "protectedpagewarning" ) . - "
\n" ); - } - - $kblength = (int)(strlen( $wpTextbox1 ) / 1024); - if( $kblength > 29 ) { - $wgOut->addHTML( "" . - str_replace( '$1', $kblength , wfMsg( "longpagewarning" ) ) - . "" ); - } - - $rows = $wgUser->getOption( "rows" ); - $cols = $wgUser->getOption( "cols" ); - - $ew = $wgUser->getOption( "editwidth" ); - if ( $ew ) $ew = " style=\"width:100%\""; - else $ew = "" ; - - $q = "action=submit"; - if ( "no" == $redirect ) { $q .= "&redirect=no"; } - $action = wfEscapeHTML( wfLocalUrl( $wgTitle->getPrefixedURL(), $q ) ); - - $summary = wfMsg( "summary" ); - $minor = wfMsg( "minoredit" ); - $watchthis = wfMsg ("watchthis"); - $save = wfMsg( "savearticle" ); - $prev = wfMsg( "showpreview" ); - - $cancel = $sk->makeKnownLink( $wgTitle->getPrefixedURL(), - wfMsg( "cancel" ) ); - $edithelp = $sk->makeKnownLink( wfMsg( "edithelppage" ), - wfMsg( "edithelp" ) ); - $copywarn = str_replace( "$1", $sk->makeKnownLink( - wfMsg( "copyrightpage" ) ), wfMsg( "copyrightwarning" ) ); - - $wpTextbox1 = wfEscapeHTML( $wpTextbox1 ); - $wpTextbox2 = wfEscapeHTML( $wpTextbox2 ); - $wpSummary = wfEscapeHTML( $wpSummary ); - - // activate checkboxes if user wants them to be always active - if (!$wpPreview && $wgUser->getOption("watchdefault")) $wpWatchthis=1; - if (!$wpPreview && $wgUser->getOption("minordefault")) $wpMinoredit=1; - - // activate checkbox also if user is already watching the page, - // require wpWatchthis to be unset so that second condition is not - // checked unnecessarily - if (!$wpWatchthis && !$wpPreview && $wgTitle->userIsWatching()) $wpWatchthis=1; - - if ( 0 != $wgUser->getID() ) { - $checkboxhtml= - "{$minor}". - "{$watchthis}
"; - - } else { - $checkboxhtml=""; - } - - - if ( "preview" == $formtype) { - - $previewhead="

" . wfMsg( "preview" ) . "

\n

" . - wfMsg( "note" ) . wfMsg( "previewnote" ) . "

\n"; - if ( $isConflict ) { - $previewhead.="

" . wfMsg( "previewconflict" ) . - "

\n"; - } - $previewtext = wfUnescapeHTML( $wpTextbox1 ); - - if($wgUser->getOption("previewontop")) { - $wgOut->addHTML($previewhead); - $wgOut->addWikiText( $this->preSaveTransform( $previewtext ) ."\n\n"); - } - $wgOut->addHTML( "
\n" ); - } - $wgOut->addHTML( " -
-
-{$summary}:
-{$checkboxhtml} - - -{$cancel} | {$edithelp} -

{$copywarn} - -\n" ); - - if ( $isConflict ) { - $wgOut->addHTML( "

" . wfMsg( "yourdiff" ) . "

\n" ); - DifferenceEngine::showDiff( $wpTextbox2, $wpTextbox1, - wfMsg( "yourtext" ), wfMsg( "storedversion" ) ); - - $wgOut->addHTML( "

" . wfMsg( "yourtext" ) . "

-" ); - } - $wgOut->addHTML( "
\n" ); - if($formtype =="preview" && !$wgUser->getOption("previewontop")) { - $wgOut->addHTML($previewhead); - $wgOut->addWikiText( $this->preSaveTransform( $previewtext ) ); - } - + wfProfileOut( $fname ); } # Theoretically we could defer these whole insert and update @@ -599,19 +374,23 @@ name=\"wpSummary\" maxlength=200 size=60>
/* private */ function insertNewArticle( $text, $summary, $isminor, $watchthis ) { - global $wgOut, $wgUser, $wgTitle, $wgLinkCache; + global $wgOut, $wgUser, $wgLinkCache, $wgMwRedir; + $fname = "Article::insertNewArticle"; - $ns = $wgTitle->getNamespace(); - $ttl = $wgTitle->getDBkey(); + $this->mCountAdjustment = $this->isCountable( $text ); + + $ns = $this->mTitle->getNamespace(); + $ttl = $this->mTitle->getDBkey(); $text = $this->preSaveTransform( $text ); - if ( preg_match( "/^#redirect/i", $text ) ) { $redir = 1; } + if ( $wgMwRedir->matchStart( $text ) ) { $redir = 1; } else { $redir = 0; } $now = wfTimestampNow(); $won = wfInvertTimestamp( $now ); wfSeedRandom(); $rand = number_format( mt_rand() / mt_getrandmax(), 12, ".", "" ); + $isminor = ( $isminor && $wgUser->getID() ) ? 1 : 0; $sql = "INSERT INTO cur (cur_namespace,cur_title,cur_text," . "cur_comment,cur_user,cur_timestamp,cur_minor_edit,cur_counter," . "cur_restrictions,cur_user_text,cur_is_redirect," . @@ -619,56 +398,62 @@ name=\"wpSummary\" maxlength=200 size=60>
wfStrencode( $text ) . "', '" . wfStrencode( $summary ) . "', '" . $wgUser->getID() . "', '{$now}', " . - ( $isminor ? 1 : 0 ) . ", 0, '', '" . + $isminor . ", 0, '', '" . wfStrencode( $wgUser->getName() ) . "', $redir, 1, $rand, '{$now}', '{$won}')"; - $res = wfQuery( $sql, $fname ); + $res = wfQuery( $sql, DB_WRITE, $fname ); $newid = wfInsertId(); - $wgTitle->resetArticleID( $newid ); - - $sql = "INSERT INTO recentchanges (rc_timestamp,rc_cur_time," . - "rc_namespace,rc_title,rc_new,rc_minor,rc_cur_id,rc_user," . - "rc_user_text,rc_comment,rc_this_oldid,rc_last_oldid,rc_bot) VALUES (" . - "'{$now}','{$now}',{$ns},'" . wfStrencode( $ttl ) . "',1," . - ( $isminor ? 1 : 0 ) . ",{$newid}," . $wgUser->getID() . ",'" . - wfStrencode( $wgUser->getName() ) . "','" . - wfStrencode( $summary ) . "',0,0," . - ( $wgUser->isBot() ? 1 : 0 ) . ")"; - wfQuery( $sql, $fname ); + $this->mTitle->resetArticleID( $newid ); + + Article::onArticleCreate( $this->mTitle, $text ); + RecentChange::notifyNew( $now, $this->mTitle, $isminor, $wgUser, $summary ); + if ($watchthis) { - if(!$wgTitle->userIsWatching()) $this->watch(); + if(!$this->mTitle->userIsWatching()) $this->watch(); } else { - if ( $wgTitle->userIsWatching() ) { + if ( $this->mTitle->userIsWatching() ) { $this->unwatch(); } } + # The talk page isn't in the regular link tables, so we need to update manually: + $talkns = $ns ^ 1; # talk -> normal; normal -> talk + $sql = "UPDATE cur set cur_touched='$now' WHERE cur_namespace=$talkns AND cur_title='" . wfStrencode( $ttl ) . "'"; + wfQuery( $sql, DB_WRITE ); + $this->showArticle( $text, wfMsg( "newarticle" ) ); } - function updateArticle( $text, $summary, $minor, $watchthis, $section ) + function updateArticle( $text, $summary, $minor, $watchthis, $section = "", $forceBot = false ) { - global $wgOut, $wgUser, $wgTitle, $wgLinkCache; - global $wgDBtransactions; + global $wgOut, $wgUser, $wgLinkCache; + global $wgDBtransactions, $wgMwRedir; $fname = "Article::updateArticle"; + $this->loadLastEdit(); + // insert updated section into old text if we have only edited part - // of the article - if ($section != "") { + // of the article + if ($section != "") { $oldtext=$this->getContent(); - $secs=preg_split("/(^=+.*?=+)/m",$oldtext,-1,PREG_SPLIT_DELIM_CAPTURE); - $secs[$section*2]=$text."\n\n"; // replace with edited - if($section) { $secs[$section*2-1]=""; } // erase old headline - $text=join("",$secs); + if($section=="new") { + if($summary) $subject="== {$summary} ==\n\n"; + $text=$oldtext."\n\n".$subject.$text; + } else { + $secs=preg_split("/(^=+.*?=+|^.*?<\/h[1-6].*?>)/mi", + $oldtext,-1,PREG_SPLIT_DELIM_CAPTURE); + $secs[$section*2]=$text."\n\n"; // replace with edited + if($section) { $secs[$section*2-1]=""; } // erase old headline + $text=join("",$secs); + } } if ( $this->mMinorEdit ) { $me1 = 1; } else { $me1 = 0; } - if ( $minor ) { $me2 = 1; } else { $me2 = 0; } - if ( preg_match( "/^(#redirect[^\\n]+)/i", $text, $m ) ) { + if ( $minor && $wgUser->getID() ) { $me2 = 1; } else { $me2 = 0; } + if ( preg_match( "/^((" . $wgMwRedir->getBaseRegex() . ")[^\\n]+)/i", $text, $m ) ) { $redir = 1; $text = $m[1] . "\n"; # Remove all content but redirect } else { $redir = 0; } - $this->loadLastEdit(); $text = $this->preSaveTransform( $text ); @@ -676,7 +461,7 @@ name=\"wpSummary\" maxlength=200 size=60>
if( $wgDBtransactions ) { $sql = "BEGIN"; - wfQuery( $sql ); + wfQuery( $sql, DB_WRITE ); } $oldtext = $this->getContent( true ); @@ -684,66 +469,61 @@ name=\"wpSummary\" maxlength=200 size=60>
$this->mCountAdjustment = $this->isCountable( $text ) - $this->isCountable( $oldtext ); + $now = wfTimestampNow(); + $won = wfInvertTimestamp( $now ); + $sql = "UPDATE cur SET cur_text='" . wfStrencode( $text ) . + "',cur_comment='" . wfStrencode( $summary ) . + "',cur_minor_edit={$me2}, cur_user=" . $wgUser->getID() . + ",cur_timestamp='{$now}',cur_user_text='" . + wfStrencode( $wgUser->getName() ) . + "',cur_is_redirect={$redir}, cur_is_new=0, cur_touched='{$now}', inverse_timestamp='{$won}' " . + "WHERE cur_id=" . $this->getID() . + " AND cur_timestamp='" . $this->getTimestamp() . "'"; + $res = wfQuery( $sql, DB_WRITE, $fname ); + + if( wfAffectedRows() == 0 ) { + /* Belated edit conflict! Run away!! */ + return false; + } + + # This overwrites $oldtext if revision compression is on + $flags = Article::compressRevisionText( $oldtext ); + $sql = "INSERT INTO old (old_namespace,old_title,old_text," . "old_comment,old_user,old_user_text,old_timestamp," . - "old_minor_edit,inverse_timestamp) VALUES (" . - $wgTitle->getNamespace() . ", '" . - wfStrencode( $wgTitle->getDBkey() ) . "', '" . + "old_minor_edit,inverse_timestamp,old_flags) VALUES (" . + $this->mTitle->getNamespace() . ", '" . + wfStrencode( $this->mTitle->getDBkey() ) . "', '" . wfStrencode( $oldtext ) . "', '" . wfStrencode( $this->getComment() ) . "', " . $this->getUser() . ", '" . wfStrencode( $this->getUserText() ) . "', '" . $this->getTimestamp() . "', " . $me1 . ", '" . - wfInvertTimestamp( $this->getTimestamp() ) . "')"; - $res = wfQuery( $sql, $fname ); + wfInvertTimestamp( $this->getTimestamp() ) . "','$flags')"; + $res = wfQuery( $sql, DB_WRITE, $fname ); $oldid = wfInsertID( $res ); - $now = wfTimestampNow(); - $won = wfInvertTimestamp( $now ); - $sql = "UPDATE cur SET cur_text='" . wfStrencode( $text ) . - "',cur_comment='" . wfStrencode( $summary ) . - "',cur_minor_edit={$me2}, cur_user=" . $wgUser->getID() . - ",cur_timestamp='{$now}',cur_user_text='" . - wfStrencode( $wgUser->getName() ) . - "',cur_is_redirect={$redir}, cur_is_new=0, cur_touched='{$now}', inverse_timestamp='{$won}' " . - "WHERE cur_id=" . $this->getID(); - wfQuery( $sql, $fname ); - - $sql = "INSERT INTO recentchanges (rc_timestamp,rc_cur_time," . - "rc_namespace,rc_title,rc_new,rc_minor,rc_bot,rc_cur_id,rc_user," . - "rc_user_text,rc_comment,rc_this_oldid,rc_last_oldid) VALUES (" . - "'{$now}','{$now}'," . $wgTitle->getNamespace() . ",'" . - wfStrencode( $wgTitle->getDBkey() ) . "',0,{$me2}," . - ( $wgUser->isBot() ? 1 : 0 ) . "," . - $this->getID() . "," . $wgUser->getID() . ",'" . - wfStrencode( $wgUser->getName() ) . "','" . - wfStrencode( $summary ) . "',0,{$oldid})"; - wfQuery( $sql, $fname ); - - $sql = "UPDATE recentchanges SET rc_this_oldid={$oldid} " . - "WHERE rc_namespace=" . $wgTitle->getNamespace() . " AND " . - "rc_title='" . wfStrencode( $wgTitle->getDBkey() ) . "' AND " . - "rc_timestamp='" . $this->getTimestamp() . "'"; - wfQuery( $sql, $fname ); - - $sql = "UPDATE recentchanges SET rc_cur_time='{$now}' " . - "WHERE rc_cur_id=" . $this->getID(); - wfQuery( $sql, $fname ); + $bot = (int)($wgUser->isBot() || $forceBot); + RecentChange::notifyEdit( $now, $this->mTitle, $me2, $wgUser, $summary, + $oldid, $this->getTimestamp(), $bot ); + Article::onArticleEdit( $this->mTitle ); } + if( $wgDBtransactions ) { $sql = "COMMIT"; - wfQuery( $sql ); + wfQuery( $sql, DB_WRITE ); } if ($watchthis) { - if (!$wgTitle->userIsWatching()) $this->watch(); + if (!$this->mTitle->userIsWatching()) $this->watch(); } else { - if ( $wgTitle->userIsWatching() ) { + if ( $this->mTitle->userIsWatching() ) { $this->unwatch(); } } - $this->showArticle( $text, wfMsg( "updated" ) ); + $this->showArticle( $text, wfMsg( "updated" ) ); + return true; } # After we've either updated or inserted the article, update @@ -751,95 +531,39 @@ name=\"wpSummary\" maxlength=200 size=60>
function showArticle( $text, $subtitle ) { - global $wgOut, $wgTitle, $wgUser, $wgLinkCache, $wgUseBetterLinksUpdate; + global $wgOut, $wgUser, $wgLinkCache; + global $wgMwRedir; $wgLinkCache = new LinkCache(); # Get old version of link table to allow incremental link updates - if ( $wgUseBetterLinksUpdate ) { - $wgLinkCache->preFill( $wgTitle ); - $wgLinkCache->clear(); - } + $wgLinkCache->preFill( $this->mTitle ); + $wgLinkCache->clear(); - # Now update the link cache by parsing the text + # Now update the link cache by parsing the text + $wgOut = new OutputPage(); $wgOut->addWikiText( $text ); - $this->editUpdates( $text ); - if( preg_match( "/^#redirect/i", $text ) ) + # Every 1000th edit, prune the recent changes table. + wfSeedRandom(); + if ( 0 == mt_rand( 0, 999 ) ) { + $cutoff = wfUnix2Timestamp( time() - ( 7 * 86400 ) ); + $sql = "DELETE FROM recentchanges WHERE rc_timestamp < '{$cutoff}'"; + wfQuery( $sql, DB_WRITE ); + } + + if( $wgMwRedir->matchStart( $text ) ) $r = "redirect=no"; else $r = ""; - $wgOut->redirect( wfLocalUrl( $wgTitle->getPrefixedURL(), $r ) ); - } - - # If the page we've just displayed is in the "Image" namespace, - # we follow it with an upload history of the image and its usage. - - function imageHistory() - { - global $wgUser, $wgOut, $wgLang, $wgTitle; - $fname = "Article::imageHistory"; - - $sql = "SELECT img_size,img_description,img_user," . - "img_user_text,img_timestamp FROM image WHERE " . - "img_name='" . wfStrencode( $wgTitle->getDBkey() ) . "'"; - $res = wfQuery( $sql, $fname ); - - if ( 0 == wfNumRows( $res ) ) { return; } - - $sk = $wgUser->getSkin(); - $s = $sk->beginImageHistoryList(); - - $line = wfFetchObject( $res ); - $s .= $sk->imageHistoryLine( true, $line->img_timestamp, - $wgTitle->getText(), $line->img_user, - $line->img_user_text, $line->img_size, $line->img_description ); - - $sql = "SELECT oi_size,oi_description,oi_user," . - "oi_user_text,oi_timestamp,oi_archive_name FROM oldimage WHERE " . - "oi_name='" . wfStrencode( $wgTitle->getDBkey() ) . "' " . - "ORDER BY oi_timestamp DESC"; - $res = wfQuery( $sql, $fname ); - - while ( $line = wfFetchObject( $res ) ) { - $s .= $sk->imageHistoryLine( false, $line->oi_timestamp, - $line->oi_archive_name, $line->oi_user, - $line->oi_user_text, $line->oi_size, $line->oi_description ); - } - $s .= $sk->endImageHistoryList(); - $wgOut->addHTML( $s ); - } - - function imageLinks() - { - global $wgUser, $wgOut, $wgTitle; - - $wgOut->addHTML( "

" . wfMsg( "imagelinks" ) . "

\n" ); - - $sql = "SELECT il_from FROM imagelinks WHERE il_to='" . - wfStrencode( $wgTitle->getDBkey() ) . "'"; - $res = wfQuery( $sql, "Article::imageLinks" ); - - if ( 0 == wfNumRows( $res ) ) { - $wgOut->addHtml( "

" . wfMsg( "nolinkstoimage" ) . "\n" ); - return; - } - $wgOut->addHTML( "

" . wfMsg( "linkstoimage" ) . "\n

    " ); - - $sk = $wgUser->getSkin(); - while ( $s = wfFetchObject( $res ) ) { - $name = $s->il_from; - $link = $sk->makeKnownLink( $name, "" ); - $wgOut->addHTML( "
  • {$link}
  • \n" ); - } - $wgOut->addHTML( "
\n" ); + $wgOut->redirect( wfLocalUrl( $this->mTitle->getPrefixedURL(), $r ) ); } # Add this page to my watchlist - function watch() + function watch( $add = true ) { - global $wgUser, $wgTitle, $wgOut, $wgLang; + global $wgUser, $wgOut, $wgLang; global $wgDeferredUpdateList; if ( 0 == $wgUser->getID() ) { @@ -850,16 +574,21 @@ name=\"wpSummary\" maxlength=200 size=60>
$wgOut->readOnlyPage(); return; } - $wgUser->addWatch( $wgTitle ); + if( $add ) + $wgUser->addWatch( $this->mTitle ); + else + $wgUser->removeWatch( $this->mTitle ); - $wgOut->setPagetitle( wfMsg( "addedwatch" ) ); + $wgOut->setPagetitle( wfMsg( $add ? "addedwatch" : "removedwatch" ) ); $wgOut->setRobotpolicy( "noindex,follow" ); $sk = $wgUser->getSkin() ; - $link = $sk->makeKnownLink ( $wgTitle->getPrefixedText() ) ; + $link = $sk->makeKnownLink ( $this->mTitle->getPrefixedText() ) ; - $text = str_replace( "$1", $link , - wfMsg( "addedwatchtext" ) ); + if($add) + $text = wfMsg( "addedwatchtext", $link ); + else + $text = wfMsg( "removedwatchtext", $link ); $wgOut->addHTML( $text ); $up = new UserUpdate(); @@ -870,107 +599,12 @@ name=\"wpSummary\" maxlength=200 size=60>
function unwatch() { - global $wgUser, $wgTitle, $wgOut, $wgLang; - global $wgDeferredUpdateList; - - if ( 0 == $wgUser->getID() ) { - $wgOut->errorpage( "watchnologin", "watchnologintext" ); - return; - } - if ( wfReadOnly() ) { - $wgOut->readOnlyPage(); - return; - } - $wgUser->removeWatch( $wgTitle ); - - $wgOut->setPagetitle( wfMsg( "removedwatch" ) ); - $wgOut->setRobotpolicy( "noindex,follow" ); - - $sk = $wgUser->getSkin() ; - $link = $sk->makeKnownLink ( $wgTitle->getPrefixedText() ) ; - - $text = str_replace( "$1", $link , - wfMsg( "removedwatchtext" ) ); - $wgOut->addHTML( $text ); - - $up = new UserUpdate(); - array_push( $wgDeferredUpdateList, $up ); - - $wgOut->returnToMain( false ); + $this->watch( false ); } - # This shares a lot of issues (and code) with Recent Changes - - function history() + function protect( $limit = "sysop" ) { - global $wgUser, $wgOut, $wgLang, $wgTitle, $offset, $limit; - - # If page hasn't changed, client can cache this - - $wgOut->checkLastModified( $this->getTimestamp() ); - wfProfileIn( "Article::history" ); - - $wgOut->setPageTitle( $wgTitle->getPRefixedText() ); - $wgOut->setSubtitle( wfMsg( "revhistory" ) ); - $wgOut->setArticleFlag( false ); - $wgOut->setRobotpolicy( "noindex,nofollow" ); - - if( $wgTitle->getArticleID() == 0 ) { - $wgOut->addHTML( wfMsg( "nohistory" ) ); - wfProfileOut(); - return; - } - - $offset = (int)$offset; - $limit = (int)$limit; - if( $limit == 0 ) $limit = 50; - $namespace = $wgTitle->getNamespace(); - $title = $wgTitle->getText(); - $sql = "SELECT old_id,old_user," . - "old_comment,old_user_text,old_timestamp,old_minor_edit ". - "FROM old USE INDEX (name_title_timestamp) " . - "WHERE old_namespace={$namespace} AND " . - "old_title='" . wfStrencode( $wgTitle->getDBkey() ) . "' " . - "ORDER BY inverse_timestamp LIMIT $offset, $limit"; - $res = wfQuery( $sql, "Article::history" ); - - $revs = wfNumRows( $res ); - if( $wgTitle->getArticleID() == 0 ) { - $wgOut->addHTML( wfMsg( "nohistory" ) ); - wfProfileOut(); - return; - } - - $sk = $wgUser->getSkin(); - $numbar = wfViewPrevNext( - $offset, $limit, - $wgTitle->getPrefixedText(), - "action=history" ); - $s = $numbar; - $s .= $sk->beginHistoryList(); - - if($offset == 0 ) - $s .= $sk->historyLine( $this->getTimestamp(), $this->getUser(), - $this->getUserText(), $namespace, - $title, 0, $this->getComment(), - ( $this->getMinorEdit() > 0 ) ); - - $revs = wfNumRows( $res ); - while ( $line = wfFetchObject( $res ) ) { - $s .= $sk->historyLine( $line->old_timestamp, $line->old_user, - $line->old_user_text, $namespace, - $title, $line->old_id, - $line->old_comment, ( $line->old_minor_edit > 0 ) ); - } - $s .= $sk->endHistoryList(); - $s .= $numbar; - $wgOut->addHTML( $s ); - wfProfileOut(); - } - - function protect() - { - global $wgUser, $wgOut, $wgTitle; + global $wgUser, $wgOut; if ( ! $wgUser->isSysop() ) { $wgOut->sysopRequired(); @@ -980,51 +614,37 @@ name=\"wpSummary\" maxlength=200 size=60>
$wgOut->readOnlyPage(); return; } - $id = $wgTitle->getArticleID(); + $id = $this->mTitle->getArticleID(); if ( 0 == $id ) { $wgOut->fatalEror( wfMsg( "badarticleerror" ) ); return; } $sql = "UPDATE cur SET cur_touched='" . wfTimestampNow() . "'," . - "cur_restrictions='sysop' WHERE cur_id={$id}"; - wfQuery( $sql, "Article::protect" ); + "cur_restrictions='{$limit}' WHERE cur_id={$id}"; + wfQuery( $sql, DB_WRITE, "Article::protect" ); - $wgOut->redirect( wfLocalUrl( $wgTitle->getPrefixedURL() ) ); + $log = new LogPage( wfMsg( "protectlogpage" ), wfMsg( "protectlogtext" ) ); + if ( $limit === "" ) { + $log->addEntry( wfMsg( "unprotectedarticle", $this->mTitle->getPrefixedText() ), "" ); + } else { + $log->addEntry( wfMsg( "protectedarticle", $this->mTitle->getPrefixedText() ), "" ); + } + $wgOut->redirect( wfLocalUrl( $this->mTitle->getPrefixedURL() ) ); } function unprotect() { - global $wgUser, $wgOut, $wgTitle; - - if ( ! $wgUser->isSysop() ) { - $wgOut->sysopRequired(); - return; - } - if ( wfReadOnly() ) { - $wgOut->readOnlyPage(); - return; - } - $id = $wgTitle->getArticleID(); - if ( 0 == $id ) { - $wgOut->fatalEror( wfMsg( "badarticleerror" ) ); - return; - } - $sql = "UPDATE cur SET cur_touched='" . wfTimestampNow() . "'," . - "cur_restrictions='' WHERE cur_id={$id}"; - wfQuery( $sql, "Article::unprotect" ); - - $wgOut->redirect( wfLocalUrl( $wgTitle->getPrefixedURL() ) ); + return $this->protect( "" ); } function delete() { - global $wgUser, $wgOut, $wgTitle; + global $wgUser, $wgOut, $wgMessageCache; global $wpConfirm, $wpReason, $image, $oldimage; - # Anybody can delete old revisions of images; only sysops - # can delete articles and current images - - if ( ( ! $oldimage ) && ( ! $wgUser->isSysop() ) ) { + # This code desperately needs to be totally rewritten + + if ( ( ! $wgUser->isSysop() ) ) { $wgOut->sysopRequired(); return; } @@ -1033,102 +653,101 @@ name=\"wpSummary\" maxlength=200 size=60>
return; } + # Can't delete cached MediaWiki namespace (i.e. vital messages) + if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI && $wgMessageCache->isCacheable( $this->mTitle->getDBkey() ) ) { + $wgOut->fatalError( wfMsg( "cannotdelete" ) ); + return; + } + # Better double-check that it hasn't been deleted yet! $wgOut->setPagetitle( wfMsg( "confirmdelete" ) ); - if ( $image ) { - if ( "" == trim( $image ) ) { - $wgOut->fatalError( wfMsg( "cannotdelete" ) ); - return; - } - $sub = str_replace( "$1", $image, wfMsg( "deletesub" ) ); - } else { + if ( ( "" == trim( $this->mTitle->getText() ) ) + or ( $this->mTitle->getArticleId() == 0 ) ) { + $wgOut->fatalError( wfMsg( "cannotdelete" ) ); + return; + } - if ( ( "" == trim( $wgTitle->getText() ) ) - or ( $wgTitle->getArticleId() == 0 ) ) { - $wgOut->fatalError( wfMsg( "cannotdelete" ) ); - return; - } - $sub = str_replace( "$1", $wgTitle->getPrefixedText(), - wfMsg( "deletesub" ) ); - - # determine whether this page has earlier revisions - # and insert a warning if it does - # we select the text because it might be useful below - $sql="SELECT old_text FROM old WHERE old_namespace=0 and old_title='" . wfStrencode($wgTitle->getPrefixedDBkey())."' ORDER BY inverse_timestamp LIMIT 1"; - $res=wfQuery($sql,$fname); - if( ($old=wfFetchObject($res)) && !$wpConfirm ) { - $skin=$wgUser->getSkin(); - $wgOut->addHTML("".wfMsg("historywarning")); - $wgOut->addHTML( $skin->historyLink() ."

"); - } + if ( $_POST["wpConfirm"] ) { + $this->doDelete(); + return; + } - $sql="SELECT cur_text FROM cur WHERE cur_namespace=0 and cur_title='" . wfStrencode($wgTitle->getPrefixedDBkey())."'"; - $res=wfQuery($sql,$fname); - if( ($s=wfFetchObject($res))) { + # determine whether this page has earlier revisions + # and insert a warning if it does + # we select the text because it might be useful below + $ns = $this->mTitle->getNamespace(); + $title = $this->mTitle->getDBkey(); + $etitle = wfStrencode( $title ); + $sql = "SELECT old_text,old_flags FROM old WHERE old_namespace=$ns and old_title='$etitle' ORDER BY inverse_timestamp LIMIT 1"; + $res = wfQuery( $sql, DB_READ, $fname ); + if( ($old=wfFetchObject($res)) && !$wpConfirm ) { + $skin=$wgUser->getSkin(); + $wgOut->addHTML("".wfMsg("historywarning")); + $wgOut->addHTML( $skin->historyLink() ."

"); + } - # if this is a mini-text, we can paste part of it into the deletion reason + $sql="SELECT cur_text FROM cur WHERE cur_namespace=$ns and cur_title='$etitle'"; + $res=wfQuery($sql, DB_READ, $fname); + if( ($s=wfFetchObject($res))) { - #if this is empty, an earlier revision may contain "useful" text - if($s->cur_text!="") { - $text=$s->cur_text; - } else { - if($old) { - $text=$old->old_text; - $blanked=1; - } - + # if this is a mini-text, we can paste part of it into the deletion reason + + #if this is empty, an earlier revision may contain "useful" text + if($s->cur_text!="") { + $text=$s->cur_text; + } else { + if($old) { + $text = Article::getRevisionText( $old ); + $blanked=1; } - $length=strlen($text); - - # this should not happen, since it is not possible to store an empty, new - # page. Let's insert a standard text in case it does, though - if($length==0 && !$wpReason) { $wpReason=wfmsg("exblank");} - - - if($length < 500 && !$wpReason) { - - # comment field=255, let's grep the first 150 to have some user - # space left - $text=substr($text,0,150); - # let's strip out newlines and HTML tags - $text=preg_replace("/\"/","'",$text); - $text=preg_replace("/\/",">",$text); - $text=preg_replace("/[\n\r]/","",$text); - if(!$blanked) { - $wpReason=wfMsg("excontent"). " '".$text; - } else { - $wpReason=wfMsg("exbeforeblank") . " '".$text; - } - if($length>150) { $wpReason .= "..."; } # we've only pasted part of the text - $wpReason.="'"; + } + + $length=strlen($text); + + # this should not happen, since it is not possible to store an empty, new + # page. Let's insert a standard text in case it does, though + if($length==0 && !$wpReason) { $wpReason=wfmsg("exblank");} + + + if($length < 500 && !$wpReason) { + + # comment field=255, let's grep the first 150 to have some user + # space left + $text=substr($text,0,150); + # let's strip out newlines and HTML tags + $text=preg_replace("/\"/","'",$text); + $text=preg_replace("/\/",">",$text); + $text=preg_replace("/[\n\r]/","",$text); + if(!$blanked) { + $wpReason=wfMsg("excontent"). " '".$text; + } else { + $wpReason=wfMsg("exbeforeblank") . " '".$text; } + if($length>150) { $wpReason .= "..."; } # we've only pasted part of the text + $wpReason.="'"; } - } - # Likewise, deleting old images doesn't require confirmation - if ( $oldimage || 1 == $wpConfirm ) { - $this->doDelete(); - return; - } + return $this->confirmDelete(); + } + + function confirmDelete( $par = "" ) + { + global $wgOut; + global $wpReason; - $wgOut->setSubtitle( $sub ); + wfDebug( "Article::confirmDelete\n" ); + + $sub = htmlspecialchars( $this->mTitle->getPrefixedText() ); + $wgOut->setSubtitle( wfMsg( "deletesub", $sub ) ); $wgOut->setRobotpolicy( "noindex,nofollow" ); $wgOut->addWikiText( wfMsg( "confirmdeletetext" ) ); - $t = $wgTitle->getPrefixedURL(); - $q = "action=delete"; + $t = $this->mTitle->getPrefixedURL(); - if ( $image ) { - $q .= "&image={$image}"; - } else if ( $oldimage ) { - $q .= "&oldimage={$oldimage}"; - } else { - $q .= "&title={$t}"; - } - $formaction = wfEscapeHTML( wfLocalUrl( "", $q ) ); + $formaction = wfEscapeHTML( wfLocalUrl( $t, "action=delete" . $par ) ); $confirm = wfMsg( "confirm" ); $check = wfMsg( "confirmcheck" ); $delcom = wfMsg( "deletecomment" ); @@ -1137,11 +756,11 @@ name=\"wpSummary\" maxlength=200 size=60>

+ +
{$delcom}: - +
 
- -{$check}
 
\n" ); @@ -1151,50 +770,14 @@ name=\"wpSummary\" maxlength=200 size=60>
function doDelete() { - global $wgOut, $wgTitle, $wgUser, $wgLang; - global $image, $oldimage, $wpReason; + global $wgOut, $wgUser, $wgLang; + global $wpReason; $fname = "Article::doDelete"; + wfDebug( "$fname\n" ); - if ( $image ) { - $dest = wfImageDir( $image ); - $archive = wfImageDir( $image ); - if ( ! unlink( "{$dest}/{$image}" ) ) { - $wgOut->fileDeleteError( "{$dest}/{$image}" ); - return; - } - $sql = "DELETE FROM image WHERE img_name='" . - wfStrencode( $image ) . "'"; - wfQuery( $sql, $fname ); - - $sql = "SELECT oi_archive_name FROM oldimage WHERE oi_name='" . - wfStrencode( $image ) . "'"; - $res = wfQuery( $sql, $fname ); + $this->doDeleteArticle( $this->mTitle ); + $deleted = $this->mTitle->getPrefixedText(); - while ( $s = wfFetchObject( $res ) ) { - $this->doDeleteOldImage( $s->oi_archive_name ); - } - $sql = "DELETE FROM oldimage WHERE oi_name='" . - wfStrencode( $image ) . "'"; - wfQuery( $sql, $fname ); - - # Image itself is now gone, and database is cleaned. - # Now we remove the image description page. - - $nt = Title::newFromText( $wgLang->getNsText( Namespace::getImage() ) . ":" . $image ); - $this->doDeleteArticle( $nt ); - - $deleted = $image; - } else if ( $oldimage ) { - $this->doDeleteOldImage( $oldimage ); - $sql = "DELETE FROM oldimage WHERE oi_archive_name='" . - wfStrencode( $oldimage ) . "'"; - wfQuery( $sql, $fname ); - - $deleted = $oldimage; - } else { - $this->doDeleteArticle( $wgTitle ); - $deleted = $wgTitle->getPrefixedText(); - } $wgOut->setPagetitle( wfMsg( "actioncomplete" ) ); $wgOut->setRobotpolicy( "noindex,nofollow" ); @@ -1203,29 +786,19 @@ name=\"wpSummary\" maxlength=200 size=60>
Namespace::getWikipedia() ) . ":" . wfMsg( "dellogpage" ), wfMsg( "deletionlog" ) ); - $text = str_replace( "$1" , $deleted, wfMsg( "deletedtext" ) ); - $text = str_replace( "$2", $loglink, $text ); + $text = wfMsg( "deletedtext", $deleted, $loglink ); $wgOut->addHTML( "

" . $text ); $wgOut->returnToMain( false ); } - function doDeleteOldImage( $oldimage ) - { - global $wgOut; - - $name = substr( $oldimage, 15 ); - $archive = wfImageArchiveDir( $name ); - if ( ! unlink( "{$archive}/{$oldimage}" ) ) { - $wgOut->fileDeleteError( "{$archive}/{$oldimage}" ); - } - } - function doDeleteArticle( $title ) { - global $wgUser, $wgOut, $wgLang, $wpReason, $wgTitle, $wgDeferredUpdateList; + global $wgUser, $wgOut, $wgLang, $wpReason, $wgDeferredUpdateList; $fname = "Article::doDeleteArticle"; + wfDebug( "$fname\n" ); + $ns = $title->getNamespace(); $t = wfStrencode( $title->getDBkey() ); $id = $title->getArticleID(); @@ -1244,38 +817,42 @@ name=\"wpSummary\" maxlength=200 size=60>
"ar_flags) SELECT cur_namespace,cur_title,cur_text,cur_comment," . "cur_user,cur_user_text,cur_timestamp,cur_minor_edit,0 FROM cur " . "WHERE cur_namespace={$ns} AND cur_title='{$t}'"; - wfQuery( $sql, $fname ); + wfQuery( $sql, DB_WRITE, $fname ); $sql = "INSERT INTO archive (ar_namespace,ar_title,ar_text," . "ar_comment,ar_user,ar_user_text,ar_timestamp,ar_minor_edit," . "ar_flags) SELECT old_namespace,old_title,old_text,old_comment," . "old_user,old_user_text,old_timestamp,old_minor_edit,old_flags " . "FROM old WHERE old_namespace={$ns} AND old_title='{$t}'"; - wfQuery( $sql, $fname ); + wfQuery( $sql, DB_WRITE, $fname ); # Now that it's safely backed up, delete it $sql = "DELETE FROM cur WHERE cur_namespace={$ns} AND " . "cur_title='{$t}'"; - wfQuery( $sql, $fname ); + wfQuery( $sql, DB_WRITE, $fname ); $sql = "DELETE FROM old WHERE old_namespace={$ns} AND " . "old_title='{$t}'"; - wfQuery( $sql, $fname ); + wfQuery( $sql, DB_WRITE, $fname ); $sql = "DELETE FROM recentchanges WHERE rc_namespace={$ns} AND " . "rc_title='{$t}'"; - wfQuery( $sql, $fname ); + wfQuery( $sql, DB_WRITE, $fname ); # Finally, clean up the link tables if ( 0 != $id ) { + $t = wfStrencode( $title->getPrefixedDBkey() ); + + Article::onArticleDelete( $title ); + $sql = "SELECT l_from FROM links WHERE l_to={$id}"; - $res = wfQuery( $sql, $fname ); + $res = wfQuery( $sql, DB_READ, $fname ); $sql = "INSERT INTO brokenlinks (bl_from,bl_to) VALUES "; - $now = wfTimestampNow(); + $now = wfTimestampNow(); $sql2 = "UPDATE cur SET cur_touched='{$now}' WHERE cur_id IN ("; $first = true; @@ -1290,92 +867,57 @@ name=\"wpSummary\" maxlength=200 size=60>
} $sql2 .= ")"; if ( ! $first ) { - wfQuery( $sql, $fname ); - wfQuery( $sql2, $fname ); + wfQuery( $sql, DB_WRITE, $fname ); + wfQuery( $sql2, DB_WRITE, $fname ); } wfFreeResult( $res ); $sql = "DELETE FROM links WHERE l_to={$id}"; - wfQuery( $sql, $fname ); + wfQuery( $sql, DB_WRITE, $fname ); $sql = "DELETE FROM links WHERE l_from='{$t}'"; - wfQuery( $sql, $fname ); + wfQuery( $sql, DB_WRITE, $fname ); $sql = "DELETE FROM imagelinks WHERE il_from='{$t}'"; - wfQuery( $sql, $fname ); + wfQuery( $sql, DB_WRITE, $fname ); $sql = "DELETE FROM brokenlinks WHERE bl_from={$id}"; - wfQuery( $sql, $fname ); + wfQuery( $sql, DB_WRITE, $fname ); } $log = new LogPage( wfMsg( "dellogpage" ), wfMsg( "dellogpagetext" ) ); $art = $title->getPrefixedText(); $wpReason = wfCleanQueryVar( $wpReason ); - $log->addEntry( str_replace( "$1", $art, wfMsg( "deletedarticle" ) ), $wpReason ); + $log->addEntry( wfMsg( "deletedarticle", $art ), $wpReason ); # Clear the cached article id so the interface doesn't act like we exist - $wgTitle->resetArticleID( 0 ); - $wgTitle->mArticleID = 0; - } - - function revert() - { - global $wgOut; - global $oldimage; - - if ( strlen( $oldimage ) < 16 ) { - $wgOut->unexpectedValueError( "oldimage", $oldimage ); - return; - } - if ( wfReadOnly() ) { - $wgOut->readOnlyPage(); - return; - } - $name = substr( $oldimage, 15 ); - - $dest = wfImageDir( $name ); - $archive = wfImageArchiveDir( $name ); - $curfile = "{$dest}/{$name}"; - - if ( ! is_file( $curfile ) ) { - $wgOut->fileNotFoundError( $curfile ); - return; - } - $oldver = wfTimestampNow() . "!{$name}"; - $size = wfGetSQL( "oldimage", "oi_size", "oi_archive_name='" . - wfStrencode( $oldimage ) . "'" ); - - if ( ! rename( $curfile, "${archive}/{$oldver}" ) ) { - $wgOut->fileRenameError( $curfile, "${archive}/{$oldver}" ); - return; - } - if ( ! copy( "{$archive}/{$oldimage}", $curfile ) ) { - $wgOut->fileCopyError( "${archive}/{$oldimage}", $curfile ); - } - wfRecordUpload( $name, $oldver, $size, wfMsg( "reverted" ) ); - - $wgOut->setPagetitle( wfMsg( "actioncomplete" ) ); - $wgOut->setRobotpolicy( "noindex,nofollow" ); - $wgOut->addHTML( wfMsg( "imagereverted" ) ); - $wgOut->returnToMain( false ); + $this->mTitle->resetArticleID( 0 ); + $this->mTitle->mArticleID = 0; } function rollback() { - global $wgUser, $wgTitle, $wgLang, $wgOut, $from; + global $wgUser, $wgLang, $wgOut, $from; if ( ! $wgUser->isSysop() ) { $wgOut->sysopRequired(); return; } - + if ( wfReadOnly() ) { + $wgOut->readOnlyPage( $this->getContent() ); + return; + } + + # Enhanced rollback, marks edits rc_bot=1 + $bot = !!$_REQUEST['bot']; + # Replace all this user's current edits with the next one down - $tt = wfStrencode( $wgTitle->getDBKey() ); - $n = $wgTitle->getNamespace(); + $tt = wfStrencode( $this->mTitle->getDBKey() ); + $n = $this->mTitle->getNamespace(); # Get the last editor $sql = "SELECT cur_id,cur_user,cur_user_text,cur_comment FROM cur WHERE cur_title='{$tt}' AND cur_namespace={$n}"; - $res = wfQuery( $sql ); + $res = wfQuery( $sql, DB_READ ); if( ($x = wfNumRows( $res )) != 1 ) { # Something wrong $wgOut->addHTML( wfMsg( "notanarticle" ) ); @@ -1390,24 +932,24 @@ name=\"wpSummary\" maxlength=200 size=60>
if( $from != $s->cur_user_text ) { $wgOut->setPageTitle(wfmsg("rollbackfailed")); $wgOut->addWikiText( wfMsg( "alreadyrolled", - htmlspecialchars( $wgTitle->getPrefixedText()), + htmlspecialchars( $this->mTitle->getPrefixedText()), htmlspecialchars( $from ), htmlspecialchars( $s->cur_user_text ) ) ); if($s->cur_comment != "") { $wgOut->addHTML( wfMsg("editcomment", htmlspecialchars( $s->cur_comment ) ) ); - } + } return; } # Get the last edit not by this guy - $sql = "SELECT old_text,old_user,old_user_text + $sql = "SELECT old_text,old_user,old_user_text,old_timestamp,old_flags FROM old USE INDEX (name_title_timestamp) WHERE old_namespace={$n} AND old_title='{$tt}' AND (old_user <> {$uid} OR old_user_text <> '{$ut}') ORDER BY inverse_timestamp LIMIT 1"; - $res = wfQuery( $sql ); + $res = wfQuery( $sql, DB_READ ); if( wfNumRows( $res ) != 1 ) { # Something wrong $wgOut->setPageTitle(wfMsg("rollbackfailed")); @@ -1415,14 +957,22 @@ name=\"wpSummary\" maxlength=200 size=60>
return; } $s = wfFetchObject( $res ); - + + if ( $bot ) { + # Mark all reverted edits as bot + $sql = "UPDATE recentchanges SET rc_bot=1 WHERE + rc_cur_id=$pid AND rc_user=$uid AND rc_timestamp > '{$s->old_timestamp}'"; + wfQuery( $sql, DB_WRITE, $fname ); + } + # Save it! - $newcomment = str_replace( "$1", $s->old_user_text, wfMsg( "revertpage" ) ); + $newcomment = wfMsg( "revertpage", $s->old_user_text, $from ); $wgOut->setPagetitle( wfMsg( "actioncomplete" ) ); $wgOut->setRobotpolicy( "noindex,nofollow" ); $wgOut->addHTML( "

" . $newcomment . "

\n
\n" ); - $this->updateArticle( $s->old_text, $newcomment, 1, $wgTitle->userIsWatching() ); - + $this->updateArticle( Article::getRevisionText( $s ), $newcomment, 1, $this->mTitle->userIsWatching(), "", $bot ); + + Article::onArticleEdit( $this->mTitle ); $wgOut->returnToMain( false ); } @@ -1431,81 +981,31 @@ name=\"wpSummary\" maxlength=200 size=60>
/* private */ function viewUpdates() { - global $wgDeferredUpdateList, $wgTitle; - + global $wgDeferredUpdateList; if ( 0 != $this->getID() ) { - $u = new ViewCountUpdate( $this->getID() ); - array_push( $wgDeferredUpdateList, $u ); - $u = new SiteStatsUpdate( 1, 0, 0 ); - array_push( $wgDeferredUpdateList, $u ); - - $u = new UserTalkUpdate( 0, $wgTitle->getNamespace(), - $wgTitle->getDBkey() ); + global $wgDisableCounters; + if( !$wgDisableCounters ) { + Article::incViewCount( $this->getID() ); + $u = new SiteStatsUpdate( 1, 0, 0 ); + array_push( $wgDeferredUpdateList, $u ); + } + $u = new UserTalkUpdate( 0, $this->mTitle->getNamespace(), + $this->mTitle->getDBkey() ); array_push( $wgDeferredUpdateList, $u ); } } - # Do standard deferred updates after page edit. - # Every 1000th edit, prune the recent changes table. - - /* private */ function editUpdates( $text ) - { - global $wgDeferredUpdateList, $wgTitle; - - wfSeedRandom(); - if ( 0 == mt_rand( 0, 999 ) ) { - $cutoff = wfUnix2Timestamp( time() - ( 7 * 86400 ) ); - $sql = "DELETE FROM recentchanges WHERE rc_timestamp < '{$cutoff}'"; - wfQuery( $sql ); - } - $id = $this->getID(); - $title = $wgTitle->getPrefixedDBkey(); - $adj = $this->mCountAdjustment; - - if ( 0 != $id ) { - $u = new LinksUpdate( $id, $title ); - array_push( $wgDeferredUpdateList, $u ); - $u = new SiteStatsUpdate( 0, 1, $adj ); - array_push( $wgDeferredUpdateList, $u ); - $u = new SearchUpdate( $id, $title, $text ); - array_push( $wgDeferredUpdateList, $u ); - $u = new UserTalkUpdate( 1, $wgTitle->getNamespace(), - $wgTitle->getDBkey() ); - array_push( $wgDeferredUpdateList, $u ); - } - } /* private */ function setOldSubtitle() { global $wgLang, $wgOut; $td = $wgLang->timeanddate( $this->mTimestamp, true ); - $r = str_replace( "$1", "{$td}", wfMsg( "revisionasof" ) ); + $r = wfMsg( "revisionasof", $td ); $wgOut->setSubtitle( "({$r})" ); } - function blockedIPpage() - { - global $wgOut, $wgUser, $wgLang; - - $wgOut->setPageTitle( wfMsg( "blockedtitle" ) ); - $wgOut->setRobotpolicy( "noindex,nofollow" ); - $wgOut->setArticleFlag( false ); - - $id = $wgUser->blockedBy(); - $reason = $wgUser->blockedFor(); - - $name = User::whoIs( $id ); - $link = "[[" . $wgLang->getNsText( Namespace::getUser() ) . - ":{$name}|{$name}]]"; - - $text = str_replace( "$1", $link, wfMsg( "blockedtext" ) ); - $text = str_replace( "$2", $reason, $text ); - $wgOut->addWikiText( $text ); - $wgOut->returnToMain( false ); - } - # This function is called right before saving the wikitext, # so we can do things like signatures and links-in-context. @@ -1528,7 +1028,7 @@ name=\"wpSummary\" maxlength=200 size=60>
/* private */ function pstPass2( $text ) { - global $wgUser, $wgLang, $wgTitle, $wgLocaltimezone; + global $wgUser, $wgLang, $wgLocaltimezone; # Signatures # @@ -1552,15 +1052,16 @@ name=\"wpSummary\" maxlength=200 size=60>
# $tc = "[&;%\\-,.\\(\\)' _0-9A-Za-z\\/:\\x80-\\xff]"; $np = "[&;%\\-,.' _0-9A-Za-z\\/:\\x80-\\xff]"; # No parens + $namespacechar = '[ _0-9A-Za-z\x80-\xff]'; # Namespaces can use non-ascii! $conpat = "/^({$np}+) \\(({$tc}+)\\)$/"; $p1 = "/\[\[({$np}+) \\(({$np}+)\\)\\|]]/"; # [[page (context)|]] $p2 = "/\[\[\\|({$tc}+)]]/"; # [[|page]] - $p3 = "/\[\[([A-Za-z _]+):({$np}+)\\|]]/"; # [[namespace:page|]] - $p4 = "/\[\[([A-Aa-z _]+):({$np}+) \\(({$np}+)\\)\\|]]/"; + $p3 = "/\[\[($namespacechar+):({$np}+)\\|]]/"; # [[namespace:page|]] + $p4 = "/\[\[($namespacechar+):({$np}+) \\(({$np}+)\\)\\|]]/"; # [[ns:page (cont)|]] $context = ""; - $t = $wgTitle->getText(); + $t = $this->mTitle->getText(); if ( preg_match( $conpat, $t, $m ) ) { $context = $m[2]; } @@ -1573,32 +1074,51 @@ name=\"wpSummary\" maxlength=200 size=60>
} else { $text = preg_replace( $p2, "[[\\1 ({$context})|\\1]]", $text ); } - # Replace local image links with new [[image:]] style - - $text = preg_replace( - "/(^|[^[])http:\/\/(www.|)wikipedia.com\/upload\/" . - "([a-zA-Z0-9_:.~\%\-]+)\.(png|PNG|jpg|JPG|jpeg|JPEG|gif|GIF)/", - "\\1[[image:\\3.\\4]]", $text ); - $text = preg_replace( - "/(^|[^[])http:\/\/(www.|)wikipedia.com\/images\/uploads\/" . - "([a-zA-Z0-9_:.~\%\-]+)\.(png|PNG|jpg|JPG|jpeg|JPEG|gif|GIF)/", - "\\1[[image:\\3.\\4]]", $text ); - + + # {{SUBST:xxx}} variables + # + $mw =& MagicWord::get( MAG_SUBST ); + $text = $mw->substituteCallback( $text, "wfReplaceSubstVar" ); + +/* Experimental: + # Trim trailing whitespace + # MAG_END (__END__) tag allows for trailing + # whitespace to be deliberately included + $text = rtrim( $text ); + $mw =& MagicWord::get( MAG_END ); + $mw->matchAndRemove( $text ); +*/ return $text; } - /* Caching functions */ - - function tryFileCache() { + + # checkLastModified returns true iff it has taken care of all + # output to the client that is necessary for this request. + # (that is, it has sent a cached version of the page) + function tryFileCache() { + static $called = false; + if( $called ) { + wfDebug( " tryFileCache() -- called twice!?\n" ); + return; + } + $called = true; if($this->isFileCacheable()) { - if($this->isFileCacheGood()) { - wfDebug( " tryFileCache() - about to load\n" ); - $this->loadFromFileCache(); - exit; + $touched = $this->mTouched; + if( $this->mTitle->getPrefixedDBkey() == wfMsg( "mainpage" ) ) { + # Expire the main page quicker + $expire = wfUnix2Timestamp( time() - 3600 ); + $touched = max( $expire, $touched ); + } + $cache = new CacheManager( $this->mTitle ); + if($cache->isFileCacheGood( $touched )) { + global $wgOut; + wfDebug( " tryFileCache() - about to load\n" ); + $cache->loadFromFileCache(); + return true; } else { - wfDebug( " tryFileCache() - starting buffer\n" ); - ob_start( array(&$this, 'saveToFileCache' ) ); + wfDebug( " tryFileCache() - starting buffer\n" ); + ob_start( array(&$cache, 'saveToFileCache' ) ); } } else { wfDebug( " tryFileCache() - not cacheable\n" ); @@ -1606,117 +1126,208 @@ name=\"wpSummary\" maxlength=200 size=60>
} function isFileCacheable() { - global $wgUser, $wgTitle, $wgUseFileCache, $wgShowIPinHeader; + global $wgUser, $wgUseFileCache, $wgShowIPinHeader; global $action, $oldid, $diff, $redirect, $printable; return $wgUseFileCache and (!$wgShowIPinHeader) + and ($this->getID() != 0) and ($wgUser->getId() == 0) and (!$wgUser->getNewtalk()) - and ($wgTitle->getNamespace != Namespace::getSpecial()) + and ($this->mTitle->getNamespace() != Namespace::getSpecial()) and ($action == "view") and (!isset($oldid)) and (!isset($diff)) and (!isset($redirect)) and (!isset($printable)) and (!$this->mRedirectedFrom); - } - function fileCacheName() { - global $wgTitle, $wgFileCacheDirectory, $wgLang; - if( !$this->mFileCache ) { - $hash = md5( $key = $wgTitle->getDbkey() ); - if( $wgTitle->getNamespace() ) - $key = $wgLang->getNsText( $wgTitle->getNamespace() ) . ":" . $key; - $key = str_replace( ".", "%2E", urlencode( $key ) ); - $hash1 = substr( $hash, 0, 1 ); - $hash2 = substr( $hash, 0, 2 ); - $this->mFileCache = "{$wgFileCacheDirectory}/{$hash1}/{$hash2}/{$key}.html"; - wfDebug( " fileCacheName() - {$this->mFileCache}\n" ); - } - return $this->mFileCache; + function checkTouched() { + $id = $this->getID(); + $sql = "SELECT cur_touched,cur_is_redirect FROM cur WHERE cur_id=$id"; + $res = wfQuery( $sql, DB_READ, "Article::checkTouched" ); + if( $s = wfFetchObject( $res ) ) { + $this->mTouched = $s->cur_touched; + return !$s->cur_is_redirect; + } else { + return false; + } + } + + /* static */ function incViewCount( $id ) + { + $id = intval( $id ); + global $wgHitcounterUpdateFreq; + + if( $wgHitcounterUpdateFreq <= 1 ){ // + wfQuery("UPDATE cur SET cur_counter = cur_counter + 1 " . + "WHERE cur_id = $id", DB_WRITE); + return; + } + + # Not important enough to warrant an error page in case of failure + $oldignore = wfIgnoreSQLErrors( true ); + + wfQuery("INSERT INTO hitcounter (hc_id) VALUES ({$id})", DB_WRITE); + + $checkfreq = intval( $wgHitcounterUpdateFreq/25 + 1 ); + if( (rand() % $checkfreq != 0) or (wfLastErrno() != 0) ){ + # Most of the time (or on SQL errors), skip row count check + wfIgnoreSQLErrors( $oldignore ); + return; + } + + $res = wfQuery("SELECT COUNT(*) as n FROM hitcounter", DB_WRITE); + $row = wfFetchObject( $res ); + $rown = intval( $row->n ); + if( $rown >= $wgHitcounterUpdateFreq ){ + wfProfileIn( "Article::incViewCount-collect" ); + $old_user_abort = ignore_user_abort( true ); + + wfQuery("LOCK TABLES hitcounter WRITE", DB_WRITE); + wfQuery("CREATE TEMPORARY TABLE acchits TYPE=HEAP ". + "SELECT hc_id,COUNT(*) AS hc_n FROM hitcounter ". + "GROUP BY hc_id", DB_WRITE); + wfQuery("DELETE FROM hitcounter", DB_WRITE); + wfQuery("UNLOCK TABLES", DB_WRITE); + wfQuery("UPDATE cur,acchits SET cur_counter=cur_counter + hc_n ". + "WHERE cur_id = hc_id", DB_WRITE); + wfQuery("DROP TABLE acchits", DB_WRITE); + + ignore_user_abort( $old_user_abort ); + wfProfileOut( "Article::incViewCount-collect" ); + } + wfIgnoreSQLErrors( $oldignore ); } - function isFileCacheGood() { - global $wgUser, $wgCacheEpoch; - if(!file_exists( $fn = $this->fileCacheName() ) ) return false; - $cachetime = wfUnix2Timestamp( filemtime( $fn ) ); - $good = (( $this->mTouched <= $cachetime ) && - ($wgCacheEpoch <= $cachetime )); - wfDebug(" isFileCacheGood() - cachetime $cachetime, touched {$this->mTouched} epoch {$wgCacheEpoch}, good $good\n"); - return $good; + # The onArticle*() functions are supposed to be a kind of hooks + # which should be called whenever any of the specified actions + # are done. + # + # This is a good place to put code to clear caches, for instance. + + /* static */ function onArticleCreate($title_obj,$text=''){ + global $wgEnablePersistentLC, $wgEnableParserCache, $wgUseSquid; + global $wgDeferredUpdateList, $wgDBname, $wgMemc; + global $wgMessageCache, $wgInternalServer; + # Do standard deferred updates after page edit. + $id = $title_obj->getArticleID(); + $title = $title_obj->getPrefixedDBkey(); + $shortTitle = $title_obj->getDBkey(); + + $adj = $this->mCountAdjustment; + + if ( 0 != $id ) { + $u = new LinksUpdate( $id, $title ); + array_push( $wgDeferredUpdateList, $u ); + $u = new SiteStatsUpdate( 0, 1, $adj ); + array_push( $wgDeferredUpdateList, $u ); + $u = new SearchUpdate( $id, $title, $text ); + array_push( $wgDeferredUpdateList, $u ); + + $u = new UserTalkUpdate( 1, $title_obj->getNamespace(), $shortTitle ); + array_push( $wgDeferredUpdateList, $u ); + + if ( $title_obj->getNamespace() == NS_MEDIAWIKI ) { + $wgMessageCache->replace( $shortTitle, $text ); + } + } + if ( $wgEnablePersistentLC ) { + LinkCache::linksccClearBrokenLinksTo( $title ); + } + if ( $wgEnableParserCache ) { + OutputPage::parsercacheClearBrokenLinksTo( $title ); + } + if ( $wgUseSquid ) { + $urlArr = Array( + $wgInternalServer.wfLocalUrl( $title_obj->getPrefixedURL()) + ); + wfPurgeSquidServers($urlArr); + /* this needs to be done after LinksUpdate */ + $u = new SquidUpdate($title_obj); + array_push( $wgDeferredUpdateList, $u ); + } } - function loadFromFileCache() { - global $wgUseGzip, $wgOut; - wfDebug(" loadFromFileCache()\n"); - $filename=$this->fileCacheName(); - $filenamegz = "{$filename}.gz"; - $wgOut->sendCacheControl(); - if( $wgUseGzip - && wfClientAcceptsGzip() - && file_exists( $filenamegz) - && ( filemtime( $filenamegz ) >= filemtime( $filename ) ) ) { - wfDebug(" sending gzip\n"); - header( "Content-Encoding: gzip" ); - header( "Vary: Accept-Encoding" ); - $filename = $filenamegz; - } - readfile( $filename ); + /* static */ function onArticleDelete($title_obj,$text=''){ + global $wgEnablePersistentLC, $wgEnableParserCache, $wgUseSquid, $wgDeferredUpdateList; + global $wgDeferredUpdateList, $wgDBname, $wgMemc; + global $wgMessageCache, $wgInternalServer; + + $id = $title_obj->getArticleID(); + $title = $title_obj->getPrefixedDBkey(); + $shortTitle = $title_obj->getDBkey(); + + if ( $wgEnablePersistentLC ) { + LinkCache::linksccClearLinksTo( $id ); + } + if ( $wgEnableParserCache ) { + OutputPage::parsercacheClearLinksTo( $id ); + } + if ( $wgUseSquid ) { + $urlArr = Array( + $wgInternalServer.wfLocalUrl( $title_obj->getPrefixedURL()) + ); + wfPurgeSquidServers($urlArr); + + /* prepare the list of urls to purge */ + $sql = "SELECT l_from FROM links WHERE l_to={$id}" ; + $res = wfQuery ( $sql, DB_READ ) ; + while ( $BL = wfFetchObject ( $res ) ) + { + $t = Title::newFromDBkey( $BL->l_from) ; + $blurlArr[] = $wgInternalServer.wfLocalUrl( $t->getPrefixedURL() ); + } + wfFreeResult ( $res ) ; + $u = new SquidUpdate( $title_obj, $blurlArr ); + array_push( $wgDeferredUpdateList, $u ); + + } } - - function saveToFileCache( $text ) { - global $wgUseGzip, $wgCompressByDefault; - if(strcmp($text,"") == 0) return ""; - - wfDebug(" saveToFileCache()\n", false); - $filename=$this->fileCacheName(); - $mydir2=substr($filename,0,strrpos($filename,"/")); # subdirectory level 2 - $mydir1=substr($mydir2,0,strrpos($mydir2,"/")); # subdirectory level 1 - if(!file_exists($mydir1)) { mkdir($mydir1,0775); } # create if necessary - if(!file_exists($mydir2)) { mkdir($mydir2,0775); } - - $f = fopen( $filename, "w" ); - if($f) { - $now = wfTimestampNow(); - fwrite( $f, str_replace( "", - "\n", - $text ) ); - fclose( $f ); - if( $wgUseGzip and $wgCompressByDefault ) { - $start = microtime(); - wfDebug(" saving gzip\n"); - $gzout = gzencode( str_replace( "", - "\n", - $text ) ); - if( $gzout === false ) { - wfDebug(" failed to gzip compress, sending plaintext\n"); - return $text; - } - if( $f = fopen( "{$filename}.gz", "w" ) ) { - fwrite( $f, $gzout ); - fclose( $f ); - $end = microtime(); - - list($usec1, $sec1) = explode(" ",$start); - list($usec2, $sec2) = explode(" ",$end); - $interval = ((float)$usec2 + (float)$sec2) - - ((float)$usec1 + (float)$sec1); - wfDebug(" saved gzip in $interval\n"); - } else { - wfDebug(" failed to write gzip, still sending\n" ); - } - if(wfClientAcceptsGzip()) { - header( "Content-Encoding: gzip" ); - header( "Vary: Accept-Encoding" ); - wfDebug(" sending NEW gzip now...\n" ); - return $gzout; - } + + /* static */ function onArticleEdit($title_obj,$text=''){ + global $wgEnablePersistentLC, $wgEnableParserCache, $wgUseSquid; + global $wgDeferredUpdateList, $wgDBname, $wgMemc; + global $wgMessageCache, $wgInternalServer; + + $id = $title_obj->getArticleID(); + $title = $title_obj->getPrefixedDBkey(); + $shortTitle = $title_obj->getDBkey(); + + $adj = $this->mCountAdjustment; + + if ( 0 != $id ) { + $u = new LinksUpdate( $id, $title ); + array_push( $wgDeferredUpdateList, $u ); + $u = new SiteStatsUpdate( 0, 1, $adj ); + array_push( $wgDeferredUpdateList, $u ); + $u = new SearchUpdate( $id, $title, $text ); + array_push( $wgDeferredUpdateList, $u ); + + $u = new UserTalkUpdate( 1, $title_obj->getNamespace(), $shortTitle ); + array_push( $wgDeferredUpdateList, $u ); + + if ( $title_obj->getNamespace() == NS_MEDIAWIKI ) { + $wgMessageCache->replace( $shortTitle, $text ); } } - return $text; + if ( $wgEnablePersistentLC ) { + LinkCache::linksccClearPage( $id ); + } + if ( $wgEnableParserCache ) { + OutputPage::parsercacheClearPage( $id ); + } + if ( $wgUseSquid ) { + $urlArr = Array( + $wgInternalServer.wfLocalUrl( $title_obj->getPrefixedURL()), + ); + wfPurgeSquidServers($urlArr); + } } +} +function wfReplaceSubstVar( $matches ) { + return wfMsg( $matches[1] ); } ?>