X-Git-Url: http://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2FArticle.php;h=62f3129b180f9cba92937446c071b1130855e980;hb=4049a9d9e16dcd5b8c5af55b3e019a2551912c05;hp=1f6e0ccaf302d25673094de77ebc32ab579c02f2;hpb=cab1e5d8171a0e64a9b3fab2084b6baf2c52d54a;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/Article.php b/includes/Article.php index 1f6e0ccaf3..62f3129b18 100644 --- a/includes/Article.php +++ b/includes/Article.php @@ -1,14 +1,14 @@ clear(); } + /** + * get the title object of the article + * @public + */ + function getTitle() { + return $this->mTitle; + } + /** * Clear the object * @private @@ -59,188 +67,6 @@ class Article { $this->mForUpdate = false; } - /** - * Get revision text associated with an old or archive row - * $row is usually an object from wfFetchRow(), both the flags and the text - * field must be included - * @static - * @param integer $row Id of a row - * @param string $prefix table prefix (default 'old_') - * @return string $text|false the text requested - */ - function getRevisionText( $row, $prefix = 'old_' ) { - # Get data - $textField = $prefix . 'text'; - $flagsField = $prefix . 'flags'; - - if ( isset( $row->$flagsField ) ) { - $flags = explode( ',', $row->$flagsField ); - } else { - $flags = array(); - } - - if ( isset( $row->$textField ) ) { - $text = $row->$textField; - } else { - return false; - } - - if ( in_array( 'link', $flags ) ) { - # Handle link type - $text = Article::followLink( $text ); - } elseif ( in_array( 'gzip', $flags ) ) { - # 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. - return gzinflate( $text ); - } - return $text; - } - - /** - * If $wgCompressRevisions is enabled, we will compress datas - * @static - * @param mixed $text reference to a text - * @return string 'gzip' if it get compressed, '' overwise - */ - 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'; - } - - /** - * Returns the text associated with a "link" type old table row - * @static - * @param mixed $link - * @return string $text|false - */ - function followLink( $link ) { - # Split the link into fields and values - $lines = explode( '\n', $link ); - $hash = ''; - $locations = array(); - foreach ( $lines as $line ) { - # Comments - if ( $line{0} == '#' ) { - continue; - } - # Field/value pairs - if ( preg_match( '/^(.*?)\s*:\s*(.*)$/', $line, $matches ) ) { - $field = strtolower($matches[1]); - $value = $matches[2]; - if ( $field == 'hash' ) { - $hash = $value; - } elseif ( $field == 'location' ) { - $locations[] = $value; - } - } - } - - if ( $hash === '' ) { - return false; - } - - # Look in each specified location for the text - $text = false; - foreach ( $locations as $location ) { - $text = Article::fetchFromLocation( $location, $hash ); - if ( $text !== false ) { - break; - } - } - - return $text; - } - - /** - * @static - * @param $location - * @param $hash - */ - function fetchFromLocation( $location, $hash ) { - global $wgLoadBalancer; - $fname = 'fetchFromLocation'; - wfProfileIn( $fname ); - - $p = strpos( $location, ':' ); - if ( $p === false ) { - wfProfileOut( $fname ); - return false; - } - - $type = substr( $location, 0, $p ); - $text = false; - switch ( $type ) { - case 'mysql': - # MySQL locations are specified by mysql:///// - # Machine ID 0 is the current connection - if ( preg_match( '/^mysql:\/\/(\d+)\/([A-Za-z_]+)\/([A-Za-z_]+)\/([A-Za-z_]+)$/', - $location, $matches ) ) { - $machineID = $matches[1]; - $dbName = $matches[2]; - $tblName = $matches[3]; - $index = $matches[4]; - if ( $machineID == 0 ) { - # Current connection - $db =& $this->getDB(); - } else { - # Alternate connection - $db =& $wgLoadBalancer->getConnection( $machineID ); - - if ( array_key_exists( $machineId, $wgKnownMysqlServers ) ) { - # Try to open, return false on failure - $params = $wgKnownDBServers[$machineId]; - $db = Database::newFromParams( $params['server'], $params['user'], $params['password'], - $dbName, 1, DBO_IGNORE ); - } - } - if ( $db->isOpen() ) { - $index = $db->strencode( $index ); - $res = $db->query( "SELECT blob_data FROM $dbName.$tblName " . - "WHERE blob_index='$index' " . $this->getSelectOptions(), $fname ); - $row = $db->fetchObject( $res ); - $text = $row->text_data; - } - } - break; - case 'file': - # File locations are of the form file://, relative to the current directory - if ( preg_match( '/^file:\/\/(.*)$', $location, $matches ) ) - $filename = strstr( $location, 'file://' ); - $text = @file_get_contents( $matches[1] ); - } - if ( $text !== false ) { - # Got text, now we need to interpret it - # The first line contains information about how to do this - $p = strpos( $text, '\n' ); - $type = substr( $text, 0, $p ); - $text = substr( $text, $p + 1 ); - switch ( $type ) { - case 'plain': - break; - case 'gzip': - $text = gzinflate( $text ); - break; - case 'object': - $object = unserialize( $text ); - $text = $object->getItem( $hash ); - break; - default: - $text = false; - } - } - wfProfileOut( $fname ); - return $text; - } - /** * Note that getContent/loadContent may follow redirects if * not told otherwise, and so may cause a change to mTitle. @@ -367,126 +193,181 @@ class Article { /** * Return an array of the columns of the "cur"-table */ - function &getCurContentFields() { - global $wgArticleCurContentFields; - if ( !$wgArticleCurContentFields ) { - $wgArticleCurContentFields = array( 'cur_text','cur_timestamp','cur_user', 'cur_user_text', - 'cur_comment','cur_counter','cur_restrictions','cur_touched' ); - } - return $wgArticleCurContentFields; + function getContentFields() { + return $wgArticleContentFields = array( + 'old_text','old_flags', + 'rev_timestamp','rev_user', 'rev_user_text', 'rev_comment','page_counter', + 'page_namespace', 'page_title', 'page_restrictions','page_touched','page_is_redirect' ); } /** - * Return an array of the columns of the "old"-table + * Return the oldid of the article that is to be shown. + * For requests with a "direction", this is not the oldid of the + * query */ - function &getOldContentFields() { - global $wgArticleOldContentFields; - if ( !$wgArticleOldContentFields ) { - $wgArticleOldContentFields = array( 'old_namespace','old_title','old_text','old_timestamp', - 'old_user','old_user_text','old_comment','old_flags' ); + function getOldID() { + global $wgRequest, $wgOut; + static $lastid; + + if ( isset( $lastid ) ) { + return $lastid; } - return $wgArticleOldContentFields; + # Query variables :P + $oldid = $wgRequest->getVal( 'oldid' ); + if ( isset( $oldid ) ) { + $oldid = IntVal( $oldid ); + if ( $wgRequest->getVal( 'direction' ) == 'next' ) { + $nextid = $this->mTitle->getNextRevisionID( $oldid ); + if ( $nextid ) { + $oldid = $nextid; + } else { + $wgOut->redirect( $this->mTitle->getFullURL( 'redirect=no' ) ); + } + } elseif ( $wgRequest->getVal( 'direction' ) == 'prev' ) { + $previd = $this->mTitle->getPreviousRevisionID( $oldid ); + if ( $previd ) { + $oldid = $previd; + } else { + # TODO + } + } + $lastid = $oldid; + } + return @$oldid; # "@" to be able to return "unset" without PHP complaining } + /** * Load the revision (including cur_text) into this object - */ + */ function loadContent( $noredir = false ) { - global $wgOut, $wgMwRedir, $wgRequest; + global $wgOut, $wgRequest; - $dbr =& $this->getDB(); + if ( $this->mContentLoaded ) return; + # Query variables :P - $oldid = $wgRequest->getVal( 'oldid' ); + $oldid = $this->getOldID(); $redirect = $wgRequest->getVal( 'redirect' ); - 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 = $this->mTitle->getPrefixedText(); - if ( isset( $oldid ) ) { - $oldid = IntVal( $oldid ); + + $noredir = $noredir || ($wgRequest->getVal( 'redirect' ) == 'no'); + $this->mOldId = $oldid; + $this->fetchContent( $oldid, $noredir, true ); + } + + /** + * Get text of an article from database + * @param int $oldid 0 for whatever the latest revision is + * @param bool $noredir Set to true to avoid following redirects + * @param bool $globalTitle Set to true to change the global $wgTitle object when following redirects or other unexpected title changes + * @return string + */ + function fetchContent( $oldid = 0, $noredir = false, $globalTitle = false ) { + if ( $this->mContentLoaded ) { + return $this->mContent; + } + $dbr =& $this->getDB(); + $fname = 'Article::fetchContent'; + + # Pre-fill content with error message so that if something + # fails we'll have something telling us what we intended. + $t = $this->mTitle->getPrefixedText(); + if( $oldid ) { $t .= ',oldid='.$oldid; } - if ( isset( $redirect ) ) { + if( isset( $redirect ) ) { $redirect = ($redirect == 'no') ? 'no' : 'yes'; $t .= ',redirect='.$redirect; } $this->mContent = wfMsg( 'missingarticle', $t ); - if ( ! $oldid ) { # Retrieve current version + if( !$oldid ) { + # Retrieve current version $id = $this->getID(); - if ( 0 == $id ) return; - - $s = $dbr->selectRow( 'cur', $this->getCurContentFields(), array( 'cur_id' => $id ), $fname, - $this->getSelectOptions() ); - if ( $s === false ) { - return; + if ( 0 == $id ) { + return false; } - # 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 ) ) { - $rt = Title::newFromRedirect( $s->cur_text ); - # process if title object is valid and not special:userlogout - if ( $rt && ! ( $rt->getNamespace() == NS_SPECIAL && $rt->getText() == 'Userlogout' ) ) { - # 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() != '' ) { + $s = $dbr->selectRow( array( 'text', 'revision', 'page' ), + $this->getContentFields(), + "page_id='$id' AND rev_page=page_id AND rev_id=page_latest AND old_id=rev_id", + $fname, $this->getSelectOptions() ); + } else { + # Historical revision + $s = $dbr->selectRow( array( 'text', 'revision', 'page' ), + $this->getContentFields(), + "rev_page=page_id AND rev_id='$oldid' AND old_id=rev_id", + $fname, $this->getSelectOptions() ); + } + if ( $s === false ) { + return false; + } + + # If we got a redirect, follow it (unless we've been told + # not to by either the function parameter or the query + if ( !$oldid && !$noredir ) { + $rt = Title::newFromRedirect( Revision::getRevisionText( $s ) ); + # process if title object is valid and not special:userlogout + if ( $rt && ! ( $rt->getNamespace() == NS_SPECIAL && $rt->getText() == 'Userlogout' ) ) { + # Gotta hand redirects to special pages differently: + # Fill the HTTP response "Location" header and ignore + # the rest of the page we're on. + if( $globalTitle ) { + global $wgOut; + if ( $rt->getInterwiki() != '' && $rt->isLocal() ) { $wgOut->redirect( $rt->getFullURL() ) ; - return; + return false; } if ( $rt->getNamespace() == NS_SPECIAL ) { $wgOut->redirect( $rt->getFullURL() ); - return; + return false; } - $rid = $rt->getArticleID(); - if ( 0 != $rid ) { - $redirRow = $dbr->selectRow( 'cur', $this->getCurContentFields(), - array( 'cur_id' => $rid ), $fname, $this->getSelectOptions() ); - - if ( $redirRow !== false ) { - $this->mRedirectedFrom = $this->mTitle->getPrefixedText(); - $this->mTitle = $rt; - $s = $redirRow; - } + } + $rid = $rt->getArticleID(); + if ( 0 != $rid ) { + $redirRow = $dbr->selectRow( array( 'text', 'revision', 'page' ), + $this->getContentFields(), + "page_id='$rid' AND rev_page=page_id AND rev_id=page_latest AND old_id=rev_id", + $fname, $this->getSelectOptions() ); + + if ( $redirRow !== false ) { + $this->mRedirectedFrom = $this->mTitle->getPrefixedText(); + $this->mTitle = $rt; + $s = $redirRow; } } } - - $this->mContent = $s->cur_text; - $this->mUser = $s->cur_user; - $this->mUserText = $s->cur_user_text; - $this->mComment = $s->cur_comment; - $this->mCounter = $s->cur_counter; - $this->mTimestamp = wfTimestamp(TS_MW,$s->cur_timestamp); - $this->mTouched = wfTimestamp(TS_MW,$s->cur_touched); - $this->mTitle->mRestrictions = explode( ',', trim( $s->cur_restrictions ) ); - $this->mTitle->mRestrictionsLoaded = true; - } else { # oldid set, retrieve historical version - $s = $dbr->getArray( 'old', $this->getOldContentFields(), array( 'old_id' => $oldid ), - $fname, $this->getSelectOptions() ); - if ( $s === false ) { - return; - } - - if( $this->mTitle->getNamespace() != $s->old_namespace || - $this->mTitle->getDBkey() != $s->old_title ) { - $oldTitle = Title::makeTitle( $s->old_namesapce, $s->old_title ); - $this->mTitle = $oldTitle; - $wgTitle = $oldTitle; - } - $this->mContent = Article::getRevisionText( $s ); - $this->mUser = $s->old_user; - $this->mUserText = $s->old_user_text; - $this->mComment = $s->old_comment; - $this->mCounter = 0; - $this->mTimestamp = wfTimestamp(TS_MW,$s->old_timestamp); } + + # if the title's different from expected, update... + if( $globalTitle && + ( $this->mTitle->getNamespace() != $s->page_namespace || + $this->mTitle->getDBkey() != $s->page_title ) ) { + $oldTitle = Title::makeTitle( $s->page_namesapce, $s->page_title ); + $this->mTitle = $oldTitle; + global $wgTitle; + $wgTitle = $oldTitle; + } + + # Back to the business at hand... + $this->mCounter = $s->page_counter; + $this->mTitle->mRestrictions = explode( ',', trim( $s->page_restrictions ) ); + $this->mTitle->mRestrictionsLoaded = true; + $this->mTouched = wfTimestamp( TS_MW, $s->page_touched ); + + $this->mContent = Revision::getRevisionText( $s ); + + $this->mUser = $s->rev_user; + $this->mUserText = $s->rev_user_text; + $this->mComment = $s->rev_comment; + $this->mTimestamp = wfTimestamp( TS_MW, $s->rev_timestamp ); + $this->mContentLoaded = true; return $this->mContent; } @@ -496,74 +377,9 @@ class Article { * Returns false on error * * @param integer $oldid - * @uses $wgMwRedir */ function getContentWithoutUsingSoManyDamnGlobals( $oldid = 0, $noredir = false ) { - global $wgMwRedir; - - if ( $this->mContentLoaded ) { - return $this->mContent; - } - $this->mContent = false; - - $fname = 'Article::getContentWithout'; - $dbr =& $this->getDB(); - - if ( ! $oldid ) { # Retrieve current version - $id = $this->getID(); - if ( 0 == $id ) { - return false; - } - - $s = $dbr->selectRow( 'cur', $this->getCurContentFields(), array( 'cur_id' => $id ), - $fname, $this->getSelectOptions() ); - if ( $s === false ) { - return false; - } - - # If we got a redirect, follow it (unless we've been told - # not to by either the function parameter or the query - if ( !$noredir ) { - $rt = Title::newFromRedirect( $s->cur_text ); - if( $rt && $rt->getInterwiki() == '' && $rt->getNamespace() != NS_SPECIAL ) { - $rid = $rt->getArticleID(); - if ( 0 != $rid ) { - $redirRow = $dbr->selectRow( 'cur', $this->getCurContentFields(), - array( 'cur_id' => $rid ), $fname, $this->getSelectOptions() ); - - if ( $redirRow !== false ) { - $this->mRedirectedFrom = $this->mTitle->getPrefixedText(); - $this->mTitle = $rt; - $s = $redirRow; - } - } - } - } - - $this->mContent = $s->cur_text; - $this->mUser = $s->cur_user; - $this->mUserText = $s->cur_user_text; - $this->mComment = $s->cur_comment; - $this->mCounter = $s->cur_counter; - $this->mTimestamp = wfTimestamp(TS_MW,$s->cur_timestamp); - $this->mTouched = wfTimestamp(TS_MW,$s->cur_touched); - $this->mTitle->mRestrictions = explode( ',', trim( $s->cur_restrictions ) ); - $this->mTitle->mRestrictionsLoaded = true; - } else { # oldid set, retrieve historical version - $s = $dbr->selectRow( 'old', $this->getOldContentFields(), array( 'old_id' => $oldid ), - $fname, $this->getSelectOptions() ); - if ( $s === false ) { - return false; - } - $this->mContent = Article::getRevisionText( $s ); - $this->mUser = $s->old_user; - $this->mUserText = $s->old_user_text; - $this->mComment = $s->old_comment; - $this->mCounter = 0; - $this->mTimestamp = wfTimestamp(TS_MW,$s->old_timestamp); - } - $this->mContentLoaded = true; - return $this->mContent; + return $this->fetchContent( $oldid, $noredir, false ); } /** @@ -590,7 +406,7 @@ class Article { */ function getSelectOptions( $options = '' ) { if ( $this->mForUpdate ) { - if ( $options ) { + if ( is_array( $options ) ) { $options[] = 'FOR UPDATE'; } else { $options = 'FOR UPDATE'; @@ -610,14 +426,14 @@ class Article { } } - /* - * @todo document function + /** + * Get the view count for this article */ function getCount() { if ( -1 == $this->mCounter ) { $id = $this->getID(); $dbr =& $this->getDB(); - $this->mCounter = $dbr->selectField( 'cur', 'cur_counter', 'cur_id='.$id, + $this->mCounter = $dbr->selectField( 'page', 'page_counter', array( 'page_id' => $id ), 'Article::getCount', $this->getSelectOptions() ); } return $this->mCounter; @@ -626,39 +442,60 @@ class Article { /** * Would the given text make this article a "good" article (i.e., * suitable for including in the article count)? + * @param string $text Text to analyze + * @return integer 1 if it can be counted else 0 */ function isCountable( $text ) { - global $wgUseCommaCount, $wgMwRedir; + global $wgUseCommaCount; - if ( 0 != $this->mTitle->getNamespace() ) { return 0; } - if ( $wgMwRedir->matchStart( $text ) ) { return 0; } + if ( NS_MAIN != $this->mTitle->getNamespace() ) { return 0; } + if ( $this->isRedirect( $text ) ) { return 0; } $token = ($wgUseCommaCount ? ',' : '[[' ); if ( false === strstr( $text, $token ) ) { return 0; } return 1; } + /** + * Tests if the article text represents a redirect + */ + function isRedirect( $text = false ) { + if ( $text === false ) { + $this->loadContent(); + $titleObj = Title::newFromRedirect( $this->mText ); + } else { + $titleObj = Title::newFromRedirect( $text ); + } + return $titleObj !== NULL; + } + /** - * Loads everything from cur except cur_text + * Loads everything except the text * This isn't necessary for all uses, so it's only done if needed. * @private */ function loadLastEdit() { global $wgOut; - if ( -1 != $this->mUser ) return; + + if ( -1 != $this->mUser ) + return; + + # New or non-existent articles have no user information + $id = $this->getID(); + if ( 0 == $id ) return; $fname = 'Article::loadLastEdit'; $dbr =& $this->getDB(); - $s = $dbr->getArray( 'cur', - array( 'cur_user','cur_user_text','cur_timestamp', 'cur_comment','cur_minor_edit' ), - array( 'cur_id' => $this->getID() ), $fname, $this->getSelectOptions() ); + $s = $dbr->selectRow( array( 'revision', 'page') , + array( 'rev_user','rev_user_text','rev_timestamp', 'rev_comment','rev_minor_edit' ), + array( 'page_id' => $id, 'page_latest=rev_id' ), $fname, $this->getSelectOptions() ); if ( $s !== false ) { - $this->mUser = $s->cur_user; - $this->mUserText = $s->cur_user_text; - $this->mTimestamp = wfTimestamp(TS_MW,$s->cur_timestamp); - $this->mComment = $s->cur_comment; - $this->mMinorEdit = $s->cur_minor_edit; + $this->mUser = $s->rev_user; + $this->mUserText = $s->rev_user_text; + $this->mTimestamp = wfTimestamp(TS_MW,$s->rev_timestamp); + $this->mComment = $s->rev_comment; + $this->mMinorEdit = $s->rev_minor_edit; } } @@ -695,18 +532,18 @@ class Article { $title = $this->mTitle; $contribs = array(); $dbr =& $this->getDB(); - $oldTable = $dbr->tableName( 'old' ); + $revTable = $dbr->tableName( 'revision' ); $userTable = $dbr->tableName( 'user' ); - $encDBkey = $dbr->strencode( $title->getDBkey() ); + $encDBkey = $dbr->addQuotes( $title->getDBkey() ); $ns = $title->getNamespace(); $user = $this->getUser(); + $pageId = $this->getId(); - $sql = "SELECT old_user, old_user_text, user_real_name, MAX(old_timestamp) as timestamp - FROM $oldTable LEFT JOIN $userTable ON old_user = user_id - WHERE old_namespace = $user - AND old_title = $encDBkey - AND old_user != $user - GROUP BY old_user + $sql = "SELECT rev_user, rev_user_text, user_real_name, MAX(rev_timestamp) as timestamp + FROM $revTable LEFT JOIN $userTable ON rev_user = user_id + WHERE rev_page = $pageId + AND rev_user != $user + GROUP BY rev_user, rev_user_text, user_real_name ORDER BY timestamp DESC"; if ($limit > 0) { $sql .= ' LIMIT '.$limit; } @@ -715,7 +552,7 @@ class Article { $res = $dbr->query($sql, $fname); while ( $line = $dbr->fetchObject( $res ) ) { - $contribs[] = array($line->old_user, $line->old_user_text, $line->user_real_name); + $contribs[] = array($line->rev_user, $line->rev_user_text, $line->user_real_name); } $dbr->freeResult($res); @@ -727,14 +564,15 @@ class Article { * the given title. */ function view() { - global $wgUser, $wgOut, $wgLang, $wgRequest, $wgMwRedir, $wgOnlySysopsCanPatrol; + global $wgUser, $wgOut, $wgRequest, $wgOnlySysopsCanPatrol, $wgLang; global $wgLinkCache, $IP, $wgEnableParserCache, $wgStylePath, $wgUseRCPatrol; + global $wgEnotif; $sk = $wgUser->getSkin(); $fname = 'Article::view'; wfProfileIn( $fname ); # Get variables from query string - $oldid = $wgRequest->getVal( 'oldid' ); + $oldid = $this->getOldID(); $diff = $wgRequest->getVal( 'diff' ); $rcid = $wgRequest->getVal( 'rcid' ); @@ -748,20 +586,22 @@ class Article { $wgOut->setPageTitle( $this->mTitle->getPrefixedText() ); $de = new DifferenceEngine( $oldid, $diff, $rcid ); $de->showDiffPage(); - wfProfileOut( $fname ); if( $diff == 0 ) { # Run view updates for current revision only $this->viewUpdates(); } + wfProfileOut( $fname ); return; } if ( empty( $oldid ) && $this->checkTouched() ) { if( $wgOut->checkLastModified( $this->mTouched ) ){ + wfProfileOut( $fname ); return; } else if ( $this->tryFileCache() ) { # tell wgOut that output is taken care of $wgOut->disable(); $this->viewUpdates(); + wfProfileOut( $fname ); return; } } @@ -792,7 +632,7 @@ class Article { # We're looking at an old revision if ( !empty( $oldid ) ) { - $this->setOldSubtitle(); + $this->setOldSubtitle( isset($this->mOldId) ? $this->mOldId : $oldid ); $wgOut->setRobotpolicy( 'noindex,follow' ); } if ( '' != $this->mRedirectedFrom ) { @@ -816,12 +656,14 @@ class Article { $wgOut->addHTML( '
'.htmlspecialchars($this->mContent)."\n
" ); } else if ( $rt = Title::newFromRedirect( $text ) ) { # Display redirect - $imageUrl = $wgStylePath.'/images/redirect.png'; + $imageUrl = $wgStylePath.'/common/images/redirect.png'; $targetUrl = $rt->escapeLocalURL(); $titleText = htmlspecialchars( $rt->getPrefixedText() ); $link = $sk->makeLinkObj( $rt ); - $wgOut->addHTML( '' . + + $wgOut->addHTML( '#REDIRECT' . ''.$link.'' ); + } else if ( $pcache ) { # Display content and save to parser cache $wgOut->addPrimaryWikiText( $text, $this ); @@ -833,8 +675,8 @@ class Article { $wgOut->setPageTitle( $this->mTitle->getPrefixedText() ); # If we have been passed an &rcid= parameter, we want to give the user a # chance to mark this new article as patrolled. - if ( $wgUseRCPatrol && !is_null ( $rcid ) && $rcid != 0 && $wgUser->getID() != 0 && - ( $wgUser->isSysop() || !$wgOnlySysopsCanPatrol ) ) + if ( $wgUseRCPatrol && !is_null ( $rcid ) && $rcid != 0 && $wgUser->isLoggedIn() && + ( $wgUser->isAllowed('patrol') || !$wgOnlySysopsCanPatrol ) ) { $wgOut->addHTML( wfMsg ( 'markaspatrolledlink', $sk->makeKnownLinkObj ( $this->mTitle, wfMsg ( 'markaspatrolledtext' ), @@ -850,6 +692,8 @@ class Article { $this->viewUpdates(); wfProfileOut( $fname ); + + $wgUser->clearNotification( $this->mTitle ); } /** @@ -860,7 +704,7 @@ class Article { * @private */ function insertNewArticle( $text, $summary, $isminor, $watchthis ) { - global $wgOut, $wgUser, $wgMwRedir; + global $wgOut, $wgUser; global $wgUseSquid, $wgDeferredUpdateList, $wgInternalServer; $fname = 'Article::insertNewArticle'; @@ -870,39 +714,52 @@ class Article { $ns = $this->mTitle->getNamespace(); $ttl = $this->mTitle->getDBkey(); $text = $this->preSaveTransform( $text ); - if ( $wgMwRedir->matchStart( $text ) ) { $redir = 1; } + if ( $this->isRedirect( $text ) ) { $redir = 1; } else { $redir = 0; } $now = wfTimestampNow(); - $won = wfInvertTimestamp( $now ); wfSeedRandom(); - $rand = number_format( mt_rand() / mt_getrandmax(), 12, '.', '' ); + $rand = wfRandom(); + $isminor = ( $isminor && $wgUser->isLoggedIn() ) ? 1 : 0; + + $mungedText = $text; + $flags = Revision::compressRevisionText( $mungedText ); + $dbw =& wfGetDB( DB_MASTER ); - $cur_id = $dbw->nextSequenceValue( 'cur_cur_id_seq' ); - - $isminor = ( $isminor && $wgUser->getID() ) ? 1 : 0; - - $dbw->insertArray( 'cur', array( - 'cur_id' => $cur_id, - 'cur_namespace' => $ns, - 'cur_title' => $ttl, - 'cur_text' => $text, - 'cur_comment' => $summary, - 'cur_user' => $wgUser->getID(), - 'cur_timestamp' => $dbw->timestamp($now), - 'cur_minor_edit' => $isminor, - 'cur_counter' => 0, - 'cur_restrictions' => '', - 'cur_user_text' => $wgUser->getName(), - 'cur_is_redirect' => $redir, - 'cur_is_new' => 1, - 'cur_random' => $rand, - 'cur_touched' => $dbw->timestamp($now), - 'inverse_timestamp' => $won, + $old_id = $dbw->nextSequenceValue( 'text_old_id_seq' ); + $dbw->insert( 'text', array( + 'old_id' => $old_id, + 'old_text' => $mungedText, + 'old_flags' => $flags, + ), $fname ); + $revisionId = $dbw->insertId(); + + $page_id = $dbw->nextSequenceValue( 'page_page_id_seq' ); + $dbw->insert( 'page', array( + 'page_id' => $page_id, + 'page_namespace' => $ns, + 'page_title' => $ttl, + 'page_counter' => 0, + 'page_restrictions' => '', + 'page_is_redirect' => $redir, + 'page_is_new' => 1, + 'page_random' => $rand, + 'page_touched' => $dbw->timestamp($now), + 'page_latest' => $revisionId, ), $fname ); - $newid = $dbw->insertId(); + + $dbw->insert( 'revision', array( + 'rev_page' => $newid, + 'rev_id' => $revisionId, + 'rev_comment' => $summary, + 'rev_user' => $wgUser->getID(), + 'rev_timestamp' => $dbw->timestamp($now), + 'rev_minor_edit' => $isminor, + 'rev_user_text' => $wgUser->getName(), + ), $fname ); + $this->mTitle->resetArticleID( $newid ); Article::onArticleCreate( $this->mTitle ); @@ -918,22 +775,56 @@ class Article { # The talk page isn't in the regular link tables, so we need to update manually: $talkns = $ns ^ 1; # talk -> normal; normal -> talk - $dbw->updateArray( 'cur', array('cur_touched' => $dbw->timestamp($now) ), - array( 'cur_namespace' => $talkns, 'cur_title' => $ttl ), $fname ); + $dbw->update( 'page', + array( 'page_touched' => $dbw->timestamp($now) ), + array( 'page_namespace' => $talkns, + 'page_title' => $ttl ), + $fname ); # standard deferred updates - $this->editUpdates( $text ); + $this->editUpdates( $text, $summary, $isminor, $now ); - $this->showArticle( $text, wfMsg( 'newarticle' ) ); + $oldid = 0; # new article + $this->showArticle( $text, wfMsg( 'newarticle' ), false, $isminor, $now, $summary, $oldid ); } - /** - * Side effects: loads last edit + * Fetch and uncompress the text for a given revision. + * Can ask by rev_id number or timestamp (set $field) */ - function getTextOfLastEditWithSectionReplacedOrAdded($section, $text, $summary = '') { - $this->loadLastEdit(); - $oldtext = $this->getContent( true ); + function fetchRevisionText( $revId = null, $field = 'rev_id' ) { + $fname = 'Article::fetchRevisionText'; + $dbw =& wfGetDB( DB_MASTER ); + if( $revId ) { + $rev = $dbw->addQuotes( $revId ); + } else { + $rev = 'page_latest'; + } + $result = $dbw->query( + sprintf( "SELECT old_text, old_flags + FROM %s,%s,%s + WHERE old_id=rev_id AND rev_page=page_id AND page_id=%d + AND %s=%s", + $dbw->tableName( 'page' ), + $dbw->tableName( 'revision' ), + $dbw->tableName( 'text' ), + IntVal( $this->mTitle->getArticleId() ), + $field, + $rev ), + $fname ); + $obj = $dbw->fetchObject( $result ); + $dbw->freeResult( $result ); + $oldtext = Revision::getRevisionText( $obj ); + return $oldtext; + } + + function getTextOfLastEditWithSectionReplacedOrAdded($section, $text, $summary = '', $edittime = NULL) { + $fname = 'Article::getTextOfLastEditWithSectionReplacedOrAdded'; + if( is_null( $edittime ) ) { + $oldtext = $this->fetchRevisionText(); + } else { + $oldtext = $this->fetchRevisionText( $edittime, 'rev_timestamp' ); + } if ($section != '') { if($section=='new') { if($summary) $subject="== {$summary} ==\n\n"; @@ -1007,7 +898,11 @@ class Article { } /** - * @todo document this function + * Change an existing article. Puts the previous version back into the old table, updates RC + * and all necessary caches, mostly via the deferred update array. + * + * It is possible to call this function from a command-line script, but note that you should + * first set $wgUser, and clean up $wgDeferredUpdates after each edit. */ function updateArticle( $text, $summary, $minor, $watchthis, $forceBot = false, $sectionanchor = '' ) { global $wgOut, $wgUser; @@ -1018,10 +913,16 @@ class Article { $good = true; if ( $this->mMinorEdit ) { $me1 = 1; } else { $me1 = 0; } - 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 + if ( $minor && $wgUser->isLoggedIn() ) { $me2 = 1; } else { $me2 = 0; } + if ( $this->isRedirect( $text ) ) { + # Remove all content but redirect + # This could be done by reconstructing the redirect from a title given by + # Title::newFromRedirect(), but then we wouldn't know which synonym the user + # wants to see + if ( preg_match( "/^((" . $wgMwRedir->getBaseRegex() . ')[^\\n]+)/i', $text, $m ) ) { + $redir = 1; + $text = $m[1] . "\n"; + } } else { $redir = 0; } @@ -1045,24 +946,47 @@ class Article { $this->mCountAdjustment = $this->isCountable( $text ) - $this->isCountable( $oldtext ); $now = wfTimestampNow(); - $won = wfInvertTimestamp( $now ); - # First update the cur row - $dbw->updateArray( 'cur', + $mungedText = $text; + $flags = Revision::compressRevisionText( $mungedText ); + + $lastRevision = $dbw->selectField( + 'page', 'page_latest', array( 'page_id' => $this->getId() ) ); + + # Record the text to the text table + $old_id = $dbw->nextSequenceValue( 'text_old_id_val' ); + $dbw->insert( 'text', + array( + 'old_id' => $old_id, + 'old_text' => $mungedText, + 'old_flags' => $flags, + ), $fname + ); + $revisionId = $dbw->insertId(); + + # Record the edit in revisions + $dbw->insert( 'revision', + array( + 'rev_id' => $revisionId, + 'rev_page' => $this->getID(), + 'rev_comment' => $summary, + 'rev_minor_edit' => $me2, + 'rev_user' => $wgUser->getID(), + 'rev_user_text' => $wgUser->getName(), + 'rev_timestamp' => $dbw->timestamp( $now ), + ), $fname + ); + + # Update page + $dbw->update( 'page', array( /* SET */ - 'cur_text' => $text, - 'cur_comment' => $summary, - 'cur_minor_edit' => $me2, - 'cur_user' => $wgUser->getID(), - 'cur_timestamp' => $dbw->timestamp($now), - 'cur_user_text' => $wgUser->getName(), - 'cur_is_redirect' => $redir, - 'cur_is_new' => 0, - 'cur_touched' => $dbw->timestamp($now), - 'inverse_timestamp' => $won + 'page_latest' => $revisionId, + 'page_touched' => $dbw->timestamp( $now ), + 'page_is_new' => 0, + 'page_is_redirect' => $redir, ), array( /* WHERE */ - 'cur_id' => $this->getID(), - 'cur_timestamp' => $dbw->timestamp($this->getTimestamp()) + 'page_id' => $this->getID(), + 'page_latest' => $lastRevision, # Paranoia ), $fname ); @@ -1070,32 +994,10 @@ class Article { /* Belated edit conflict! Run away!! */ $good = false; } else { - # Now insert the previous revision into old - - # This overwrites $oldtext if revision compression is on - $flags = Article::compressRevisionText( $oldtext ); - - $dbw->insertArray( 'old', - array( - 'old_id' => $dbw->nextSequenceValue( 'old_old_id_seq' ), - 'old_namespace' => $this->mTitle->getNamespace(), - 'old_title' => $this->mTitle->getDBkey(), - 'old_text' => $oldtext, - 'old_comment' => $this->getComment(), - 'old_user' => $this->getUser(), - 'old_user_text' => $this->getUserText(), - 'old_timestamp' => $dbw->timestamp($this->getTimestamp()), - 'old_minor_edit' => $me1, - 'inverse_timestamp' => wfInvertTimestamp( $this->getTimestamp() ), - 'old_flags' => $flags, - ), $fname - ); - - $oldid = $dbw->insertId(); - + # Update recentchanges and purge cache and whatnot $bot = (int)($wgUser->isBot() || $forceBot); RecentChange::notifyEdit( $now, $this->mTitle, $me2, $wgUser, $summary, - $oldid, $this->getTimestamp(), $bot ); + $lastRevision, $this->getTimestamp(), $bot ); Article::onArticleEdit( $this->mTitle ); } } @@ -1115,7 +1017,7 @@ class Article { } } # standard deferred updates - $this->editUpdates( $text ); + $this->editUpdates( $text, $summary, $minor, $now ); $urls = array(); @@ -1138,7 +1040,7 @@ class Article { $u->doUpdate(); } - $this->showArticle( $text, wfMsg( 'updated' ), $sectionanchor ); + $this->showArticle( $text, wfMsg( 'updated' ), $sectionanchor, $me2, $now, $summary, $lastRevision ); } return $good; } @@ -1147,9 +1049,8 @@ class Article { * After we've either updated or inserted the article, update * the link tables and redirect to the new page. */ - function showArticle( $text, $subtitle , $sectionanchor = '' ) { - global $wgOut, $wgUser, $wgLinkCache; - global $wgMwRedir; + function showArticle( $text, $subtitle , $sectionanchor = '', $me2, $now, $summary, $oldid ) { + global $wgOut, $wgUser, $wgLinkCache, $wgEnotif; $wgLinkCache = new LinkCache(); # Select for update @@ -1166,32 +1067,18 @@ class Article { # Look up the links in the DB and add them to the link cache $wgOut->transformBuffer( RLH_FOR_UPDATE ); - if( $wgMwRedir->matchStart( $text ) ) + if( $this->isRedirect( $text ) ) $r = 'redirect=no'; else $r = ''; $wgOut->redirect( $this->mTitle->getFullURL( $r ).$sectionanchor ); - } - /** - * Validate article - * @todo document this function a bit more - */ - function validate () { - global $wgOut, $wgUseValidation; - if( $wgUseValidation ) { - require_once ( 'SpecialValidate.php' ) ; - $wgOut->setPagetitle( wfMsg( 'validate' ) . ': ' . $this->mTitle->getPrefixedText() ); - $wgOut->setRobotpolicy( 'noindex,follow' ); - if( $this->mTitle->getNamespace() != 0 ) { - $wgOut->addHTML( wfMsg( 'val_validate_article_namespace_only' ) ); - return; - } - $v = new Validation; - $v->validate_form( $this->mTitle->getDBkey() ); - } else { - $wgOut->errorpage( 'nosuchaction', 'nosuchactiontext' ); - } + # this call would better fit into RecentChange::notifyEdit and RecentChange::notifyNew . + # this will be improved later (to-do) + + include_once( "UserMailer.php" ); + $wgEnotif = new EmailNotification (); + $wgEnotif->NotifyOnPageChange( $wgUser->getID(), $this->mTitle->getDBkey(), $this->mTitle->getNamespace(),$now, $summary, $me2, $oldid ); } /** @@ -1206,12 +1093,12 @@ class Article { $wgOut->errorpage( 'rcpatroldisabled', 'rcpatroldisabledtext' ); return; } - if ( $wgUser->getID() == 0 ) + if ( $wgUser->isAnon() ) { $wgOut->loginToUse(); return; } - if ( $wgOnlySysopsCanPatrol && !$wgUser->isSysop() ) + if ( $wgOnlySysopsCanPatrol && !$wgUser->isAllowed('patrol') ) { $wgOut->sysopRequired(); return; @@ -1222,7 +1109,9 @@ class Article { RecentChange::markPatrolled( $rcid ); $wgOut->setPagetitle( wfMsg( 'markedaspatrolled' ) ); $wgOut->addWikiText( wfMsg( 'markedaspatrolledtext' ) ); - $wgOut->returnToMain( true, $this->mTitle->getPrefixedText() ); + + $rcTitle = Title::makeTitle( NS_SPECIAL, 'Recentchanges' ); + $wgOut->returnToMain( false, $rcTitle->getPrefixedText() ); } else { @@ -1232,13 +1121,14 @@ class Article { /** - * Add or remove this page to my watchlist based on value of $add + * Add this page to $wgUser's watchlist */ - function watch( $add = true ) { - global $wgUser, $wgOut, $wgLang; - global $wgDeferredUpdateList; + + function watch() { + + global $wgUser, $wgOut; - if ( 0 == $wgUser->getID() ) { + if ( $wgUser->isAnon() ) { $wgOut->errorpage( 'watchnologin', 'watchnologintext' ); return; } @@ -1246,34 +1136,58 @@ class Article { $wgOut->readOnlyPage(); return; } - if( $add ) - $wgUser->addWatch( $this->mTitle ); - else - $wgUser->removeWatch( $this->mTitle ); - $wgOut->setPagetitle( wfMsg( $add ? 'addedwatch' : 'removedwatch' ) ); - $wgOut->setRobotpolicy( 'noindex,follow' ); - - $sk = $wgUser->getSkin() ; - $link = $this->mTitle->getPrefixedText(); + if (wfRunHooks('WatchArticle', $wgUser, $this)) { + + $wgUser->addWatch( $this->mTitle ); + $wgUser->saveSettings(); - if($add) + wfRunHooks('WatchArticleComplete', $wgUser, $this); + + $wgOut->setPagetitle( wfMsg( 'addedwatch' ) ); + $wgOut->setRobotpolicy( 'noindex,follow' ); + + $link = $this->mTitle->getPrefixedText(); $text = wfMsg( 'addedwatchtext', $link ); - else - $text = wfMsg( 'removedwatchtext', $link ); - $wgOut->addWikiText( $text ); - - $up = new UserUpdate(); - array_push( $wgDeferredUpdateList, $up ); - + $wgOut->addWikiText( $text ); + } + $wgOut->returnToMain( true, $this->mTitle->getPrefixedText() ); } /** - * Stop watching a page, it act just like a call to watch(false) + * Stop watching a page */ + function unwatch() { - $this->watch( false ); + + global $wgUser, $wgOut; + + if ( $wgUser->isAnon() ) { + $wgOut->errorpage( 'watchnologin', 'watchnologintext' ); + return; + } + if ( wfReadOnly() ) { + $wgOut->readOnlyPage(); + return; + } + + if (wfRunHooks('UnwatchArticle', $wgUser, $this)) { + + $wgUser->removeWatch( $this->mTitle ); + $wgUser->saveSettings(); + + wfRunHooks('UnwatchArticleComplete', $wgUser, $this); + + $wgOut->setPagetitle( wfMsg( 'removedwatch' ) ); + $wgOut->setRobotpolicy( 'noindex,follow' ); + + $link = $this->mTitle->getPrefixedText(); + $text = wfMsg( 'removedwatchtext', $link ); + $wgOut->addWikiText( $text ); + } + + $wgOut->returnToMain( true, $this->mTitle->getPrefixedText() ); } /** @@ -1282,7 +1196,7 @@ class Article { function protect( $limit = 'sysop' ) { global $wgUser, $wgOut, $wgRequest; - if ( ! $wgUser->isSysop() ) { + if ( ! $wgUser->isAllowed('protect') ) { $wgOut->sysopRequired(); return; } @@ -1296,27 +1210,49 @@ class Article { return; } - $confirm = $wgRequest->getBool( 'wpConfirmProtect' ) && $wgRequest->wasPosted(); + $confirm = $wgRequest->getBool( 'wpConfirmProtect' ) && + $wgRequest->wasPosted() && + $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ); + $moveonly = $wgRequest->getBool( 'wpMoveOnly' ); $reason = $wgRequest->getText( 'wpReasonProtect' ); if ( $confirm ) { $dbw =& wfGetDB( DB_MASTER ); - $dbw->updateArray( 'cur', + $dbw->update( 'page', array( /* SET */ - 'cur_touched' => $dbw->timestamp(), - 'cur_restrictions' => (string)$limit + 'page_touched' => $dbw->timestamp(), + 'page_restrictions' => (string)$limit ), array( /* WHERE */ - 'cur_id' => $id + 'page_id' => $id ), 'Article::protect' ); - $log = new LogPage( 'protect' ); - if ( $limit === '' ) { + $restrictions = "move=" . $limit; + if( !$moveonly ) { + $restrictions .= ":edit=" . $limit; + } + if (wfRunHooks('ArticleProtect', $this, $wgUser, $limit == 'sysop', $reason, $moveonly)) { + + $dbw =& wfGetDB( DB_MASTER ); + $dbw->update( 'page', + array( /* SET */ + 'page_touched' => $dbw->timestamp(), + 'page_restrictions' => $restrictions + ), array( /* WHERE */ + 'page_id' => $id + ), 'Article::protect' + ); + + wfRunHooks('ArticleProtectComplete', $this, $wgUser, $limit == 'sysop', $reason, $moveonly); + + $log = new LogPage( 'protect' ); + if ( $limit === '' ) { $log->addEntry( 'unprotect', $this->mTitle, $reason ); - } else { + } else { $log->addEntry( 'protect', $this->mTitle, $reason ); + } + $wgOut->redirect( $this->mTitle->getFullURL() ); } - $wgOut->redirect( $this->mTitle->getFullURL() ); return; } else { $reason = htmlspecialchars( wfMsg( 'protectreason' ) ); @@ -1328,7 +1264,7 @@ class Article { * Output protection confirmation dialog */ function confirmProtect( $par, $reason, $limit = 'sysop' ) { - global $wgOut; + global $wgOut, $wgUser; wfDebug( "Article::confirmProtect\n" ); @@ -1337,6 +1273,7 @@ class Article { $check = ''; $protcom = ''; + $moveonly = ''; if ( $limit === '' ) { $wgOut->setPageTitle( wfMsg( 'confirmunprotect' ) ); @@ -1350,11 +1287,13 @@ class Article { $wgOut->setSubtitle( wfMsg( 'protectsub', $sub ) ); $wgOut->addWikiText( wfMsg( 'confirmprotecttext' ) ); $check = htmlspecialchars( wfMsg( 'confirmprotect' ) ); + $moveonly = htmlspecialchars( wfMsg( 'protectmoveonly' ) ); $protcom = htmlspecialchars( wfMsg( 'protectcomment' ) ); $formaction = $this->mTitle->escapeLocalURL( 'action=protect' . $par ); } $confirm = htmlspecialchars( wfMsg( 'confirm' ) ); + $token = htmlspecialchars( $wgUser->editToken() ); $wgOut->addHTML( "
@@ -1377,7 +1316,19 @@ class Article { - + " ); + if($moveonly != '') { + $wgOut->AddHTML( " + + + + + + + + " ); + } + $wgOut->addHTML( "   @@ -1385,6 +1336,7 @@ class Article { +
\n" ); $wgOut->returnToMain( false ); @@ -1403,13 +1355,15 @@ class Article { function delete() { global $wgUser, $wgOut, $wgMessageCache, $wgRequest; $fname = 'Article::delete'; - $confirm = $wgRequest->getBool( 'wpConfirm' ) && $wgRequest->wasPosted(); + $confirm = $wgRequest->getBool( 'wpConfirm' ) && + $wgRequest->wasPosted() && + $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ); $reason = $wgRequest->getText( 'wpReason' ); # This code desperately needs to be totally rewritten # Check permissions - if ( ( ! $wgUser->isSysop() ) ) { + if ( ( ! $wgUser->isAllowed('delete') ) ) { $wgOut->sysopRequired(); return; } @@ -1437,26 +1391,28 @@ class Article { $dbr =& $this->getDB(); $ns = $this->mTitle->getNamespace(); $title = $this->mTitle->getDBkey(); - $old = $dbr->getArray( 'old', - array( 'old_text', 'old_flags' ), + $revisions = $dbr->select( array( 'page', 'revision' ), + array( 'rev_id' ), array( - 'old_namespace' => $ns, - 'old_title' => $title, - ), $fname, $this->getSelectOptions( array( 'ORDER BY' => 'inverse_timestamp' ) ) + 'page_namespace' => $ns, + 'page_title' => $title, + 'rev_page = page_id' + ), $fname, $this->getSelectOptions( array( 'ORDER BY' => 'rev_timestamp DESC' ) ) ); - if( $old !== false && !$confirm ) { + if( $dbr->numRows( $revisions ) > 1 && !$confirm ) { $skin=$wgUser->getSkin(); $wgOut->addHTML(''.wfMsg('historywarning')); $wgOut->addHTML( $skin->historyLink() .''); } # Fetch cur_text - $s = $dbr->getArray( 'cur', - array( 'cur_text' ), + $s = $dbr->selectRow( array( 'page', 'text' ), + array( 'old_text' ), array( - 'cur_namespace' => $ns, - 'cur_title' => $title, + 'page_namespace' => $ns, + 'page_title' => $title, + 'page_latest = old_id' ), $fname, $this->getSelectOptions() ); @@ -1465,11 +1421,11 @@ class Article { #if this is empty, an earlier revision may contain "useful" text $blanked = false; - if($s->cur_text != '') { - $text=$s->cur_text; + if($s->old_text != '') { + $text=$s->old_text; } else { - if($old) { - $text = Article::getRevisionText( $old ); + if($old) { # TODO + $text = Revision::getRevisionText( $old ); $blanked = true; } @@ -1510,7 +1466,7 @@ class Article { * Output deletion confirmation dialog */ function confirmDelete( $par, $reason ) { - global $wgOut; + global $wgOut, $wgUser; wfDebug( "Article::confirmDelete\n" ); @@ -1524,6 +1480,7 @@ class Article { $confirm = htmlspecialchars( wfMsg( 'confirm' ) ); $check = htmlspecialchars( wfMsg( 'confirmcheck' ) ); $delcom = htmlspecialchars( wfMsg( 'deletecomment' ) ); + $token = htmlspecialchars( $wgUser->editToken() ); $wgOut->addHTML( "
@@ -1554,6 +1511,7 @@ class Article { +
\n" ); $wgOut->returnToMain( false ); @@ -1564,26 +1522,30 @@ class Article { * Perform a deletion and output success or failure messages */ function doDelete( $reason ) { - global $wgOut, $wgUser, $wgLang; + global $wgOut, $wgUser, $wgContLang; $fname = 'Article::doDelete'; wfDebug( $fname."\n" ); - if ( $this->doDeleteArticle( $reason ) ) { - $deleted = $this->mTitle->getPrefixedText(); - - $wgOut->setPagetitle( wfMsg( 'actioncomplete' ) ); - $wgOut->setRobotpolicy( 'noindex,nofollow' ); - - $sk = $wgUser->getSkin(); - $loglink = $sk->makeKnownLink( $wgLang->getNsText( NS_PROJECT ) . - ':' . wfMsg( 'dellogpage' ), wfMsg( 'deletionlog' ) ); - - $text = wfMsg( 'deletedtext', $deleted, $loglink ); - - $wgOut->addHTML( '

' . $text . "

\n" ); - $wgOut->returnToMain( false ); - } else { - $wgOut->fatalError( wfMsg( 'cannotdelete' ) ); + if (wfRunHooks('ArticleDelete', $this, $wgUser, $reason)) { + if ( $this->doDeleteArticle( $reason ) ) { + $deleted = $this->mTitle->getPrefixedText(); + + $wgOut->setPagetitle( wfMsg( 'actioncomplete' ) ); + $wgOut->setRobotpolicy( 'noindex,nofollow' ); + + $sk = $wgUser->getSkin(); + $loglink = $sk->makeKnownLink( $wgContLang->getNsText( NS_PROJECT ) . + ':' . wfMsgForContent( 'dellogpage' ), + wfMsg( 'deletionlog' ) ); + + $text = wfMsg( 'deletedtext', $deleted, $loglink ); + + $wgOut->addHTML( '

' . $text . "

\n" ); + $wgOut->returnToMain( false ); + wfRunHooks('ArticleDeleteComplete', $this, $wgUser, $reason); + } else { + $wgOut->fatalError( wfMsg( 'cannotdelete' ) ); + } } } @@ -1593,7 +1555,7 @@ class Article { * Returns success */ function doDeleteArticle( $reason ) { - global $wgUser, $wgLang; + global $wgUser; global $wgUseSquid, $wgDeferredUpdateList, $wgInternalServer; $fname = 'Article::doDeleteArticle'; @@ -1632,51 +1594,31 @@ class Article { Title::touchArray( $linksTo ); # Move article and history to the "archive" table - $archiveTable = $dbw->tableName( 'archive' ); - $oldTable = $dbw->tableName( 'old' ); - $curTable = $dbw->tableName( 'cur' ); - $recentchangesTable = $dbw->tableName( 'recentchanges' ); - $linksTable = $dbw->tableName( 'links' ); - $brokenlinksTable = $dbw->tableName( 'brokenlinks' ); - - $dbw->insertSelect( 'archive', 'cur', - array( - 'ar_namespace' => 'cur_namespace', - 'ar_title' => 'cur_title', - 'ar_text' => 'cur_text', - 'ar_comment' => 'cur_comment', - 'ar_user' => 'cur_user', - 'ar_user_text' => 'cur_user_text', - 'ar_timestamp' => 'cur_timestamp', - 'ar_minor_edit' => 'cur_minor_edit', - 'ar_flags' => 0, - ), array( - 'cur_namespace' => $ns, - 'cur_title' => $t, - ), $fname - ); - $dbw->insertSelect( 'archive', 'old', + $dbw->insertSelect( 'archive', array( 'page','revision', 'text' ), array( - 'ar_namespace' => 'old_namespace', - 'ar_title' => 'old_title', + 'ar_namespace' => 'page_namespace', + 'ar_title' => 'page_title', 'ar_text' => 'old_text', - 'ar_comment' => 'old_comment', - 'ar_user' => 'old_user', - 'ar_user_text' => 'old_user_text', - 'ar_timestamp' => 'old_timestamp', - 'ar_minor_edit' => 'old_minor_edit', - 'ar_flags' => 'old_flags' + 'ar_comment' => 'rev_comment', + 'ar_user' => 'rev_user', + 'ar_user_text' => 'rev_user_text', + 'ar_timestamp' => 'rev_timestamp', + 'ar_minor_edit' => 'rev_minor_edit', + 'ar_flags' => 0, ), array( - 'old_namespace' => $ns, - 'old_title' => $t, + 'page_namespace' => $ns, + 'page_title' => $t, + 'page_id = rev_page AND old_id = rev_id' ), $fname ); # Now that it's safely backed up, delete it - $dbw->delete( 'cur', array( 'cur_namespace' => $ns, 'cur_title' => $t ), $fname ); - $dbw->delete( 'old', array( 'old_namespace' => $ns, 'old_title' => $t ), $fname ); + $dbw->deleteJoin( 'text', 'revision', 'old_id', 'rev_id', array( "rev_page = {$id}" ), $fname ); + $dbw->delete( 'revision', array( 'rev_page' => $id ), $fname ); + $dbw->delete( 'page', array( 'page_id' => $id ), $fname); + $dbw->delete( 'recentchanges', array( 'rc_namespace' => $ns, 'rc_title' => $t ), $fname ); # Finally, clean up the link tables @@ -1691,7 +1633,7 @@ class Article { $linkID = $titleObj->getArticleID(); $brokenLinks[] = array( 'bl_from' => $linkID, 'bl_to' => $t ); } - $dbw->insertArray( 'brokenlinks', $brokenLinks, $fname, 'IGNORE' ); + $dbw->insert( 'brokenlinks', $brokenLinks, $fname, 'IGNORE' ); # Delete live links $dbw->delete( 'links', array( 'l_to' => $id ) ); @@ -1714,10 +1656,10 @@ class Article { * Revert a modification */ function rollback() { - global $wgUser, $wgLang, $wgOut, $wgRequest; + global $wgUser, $wgOut, $wgRequest; $fname = 'Article::rollback'; - if ( ! $wgUser->isSysop() ) { + if ( ! $wgUser->isAllowed('rollback') ) { $wgOut->sysopRequired(); return; } @@ -1725,6 +1667,13 @@ class Article { $wgOut->readOnlyPage( $this->getContent( true ) ); return; } + if( !$wgUser->matchEditToken( $wgRequest->getVal( 'token' ), + array( $this->mTitle->getPrefixedText(), + $wgRequest->getVal( 'from' ) ) ) ) { + $wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) ); + $wgOut->addWikiText( wfMsg( 'sessionfailure' ) ); + return; + } $dbw =& wfGetDB( DB_MASTER ); # Enhanced rollback, marks edits rc_bot=1 @@ -1735,9 +1684,9 @@ class Article { $n = $this->mTitle->getNamespace(); # Get the last editor, lock table exclusively - $s = $dbw->getArray( 'cur', - array( 'cur_id','cur_user','cur_user_text','cur_comment' ), - array( 'cur_title' => $tt, 'cur_namespace' => $n ), + $s = $dbw->selectRow( array( 'page', 'revision' ), + array( 'page_id','rev_user','rev_user_text','rev_comment' ), + array( 'page_title' => $tt, 'page_namespace' => $n ), $fname, 'FOR UPDATE' ); if( $s === false ) { @@ -1746,26 +1695,26 @@ class Article { return; } $ut = $dbw->strencode( $s->cur_user_text ); - $uid = $s->cur_user; - $pid = $s->cur_id; + $uid = $s->rev_user; + $pid = $s->page_id; $from = str_replace( '_', ' ', $wgRequest->getVal( 'from' ) ); - if( $from != $s->cur_user_text ) { + if( $from != $s->rev_user_text ) { $wgOut->setPageTitle(wfmsg('rollbackfailed')); $wgOut->addWikiText( wfMsg( 'alreadyrolled', htmlspecialchars( $this->mTitle->getPrefixedText()), htmlspecialchars( $from ), - htmlspecialchars( $s->cur_user_text ) ) ); - if($s->cur_comment != '') { + htmlspecialchars( $s->rev_user_text ) ) ); + if($s->rev_comment != '') { $wgOut->addHTML( wfMsg('editcomment', - htmlspecialchars( $s->cur_comment ) ) ); + htmlspecialchars( $s->rev_comment ) ) ); } return; } # Get the last edit not by this guy - $s = $dbw->getArray( 'old', + $s = $dbw->selectRow( 'old', array( 'old_text','old_user','old_user_text','old_timestamp','old_flags' ), array( 'old_namespace' => $n, @@ -1782,7 +1731,7 @@ class Article { if ( $bot ) { # Mark all reverted edits as bot - $dbw->updateArray( 'recentchanges', + $dbw->update( 'recentchanges', array( /* SET */ 'rc_bot' => 1 ), array( /* WHERE */ @@ -1796,8 +1745,8 @@ class Article { $newcomment = wfMsg( 'revertpage', $s->old_user_text, $from ); $wgOut->setPagetitle( wfMsg( 'actioncomplete' ) ); $wgOut->setRobotpolicy( 'noindex,nofollow' ); - $wgOut->addHTML( '

' . $newcomment . "

\n
\n" ); - $this->updateArticle( Article::getRevisionText( $s ), $newcomment, 1, $this->mTitle->userIsWatching(), $bot ); + $wgOut->addHTML( '

' . htmlspecialchars( $newcomment ) . "

\n
\n" ); + $this->updateArticle( Revision::getRevisionText( $s ), $newcomment, 1, $this->mTitle->userIsWatching(), $bot ); Article::onArticleEdit( $this->mTitle ); $wgOut->returnToMain( false ); } @@ -1809,6 +1758,7 @@ class Article { */ function viewUpdates() { global $wgDeferredUpdateList; + if ( 0 != $this->getID() ) { global $wgDisableCounters; if( !$wgDisableCounters ) { @@ -1817,9 +1767,16 @@ class Article { array_push( $wgDeferredUpdateList, $u ); } } - $u = new UserTalkUpdate( 0, $this->mTitle->getNamespace(), - $this->mTitle->getDBkey() ); - array_push( $wgDeferredUpdateList, $u ); + + # Update newtalk status if user is reading their own + # talk page + + global $wgUser; + if ($this->mTitle->getNamespace() == NS_USER_TALK && + $this->mTitle->getText() == $wgUser->getName()) { + require_once( 'UserTalkUpdate.php' ); + $u = new UserTalkUpdate( 0, $this->mTitle->getNamespace(), $this->mTitle->getDBkey(), false, false, false ); + } } /** @@ -1828,15 +1785,18 @@ class Article { * @private * @param string $text */ - function editUpdates( $text ) { + function editUpdates( $text, $summary, $minoredit, $timestamp_of_pagechange) { global $wgDeferredUpdateList, $wgDBname, $wgMemc; - global $wgMessageCache; + global $wgMessageCache, $wgUser; wfSeedRandom(); if ( 0 == mt_rand( 0, 999 ) ) { + # Periodically flush old entries from the recentchanges table. + global $wgRCMaxAge; $dbw =& wfGetDB( DB_MASTER ); - $cutoff = wfUnix2Timestamp( time() - ( 7 * 86400 ) ); - $sql = "DELETE FROM recentchanges WHERE rc_timestamp < '{$cutoff}'"; + $cutoff = $dbw->timestamp( time() - $wgRCMaxAge ); + $recentchanges = $dbw->tableName( 'recentchanges' ); + $sql = "DELETE FROM $recentchanges WHERE rc_timestamp < '{$cutoff}'"; $dbw->query( $sql ); } $id = $this->getID(); @@ -1853,8 +1813,14 @@ class Article { $u = new SearchUpdate( $id, $title, $text ); array_push( $wgDeferredUpdateList, $u ); - $u = new UserTalkUpdate( 1, $this->mTitle->getNamespace(), $shortTitle ); - array_push( $wgDeferredUpdateList, $u ); + # If this is another user's talk page, + # create a watchlist entry for this page + + if ($this->mTitle->getNamespace() == NS_USER_TALK && + $shortTitle != $wgUser->getName()) { + require_once( 'UserTalkUpdate.php' ); + $u = new UserTalkUpdate( 1, $this->mTitle->getNamespace(), $shortTitle, $summary, $minoredit, $timestamp_of_pagechange); + } if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) { $wgMessageCache->replace( $shortTitle, $text ); @@ -1865,14 +1831,17 @@ class Article { /** * @todo document this function * @private + * @param string $oldid Revision ID of this article revision */ - function setOldSubtitle() { + function setOldSubtitle( $oldid=0 ) { global $wgLang, $wgOut, $wgUser; $td = $wgLang->timeanddate( $this->mTimestamp, true ); $sk = $wgUser->getSkin(); $lnk = $sk->makeKnownLinkObj ( $this->mTitle, wfMsg( 'currentrevisionlink' ) ); - $r = wfMsg( 'revisionasofwithlink', $td, $lnk ); + $prevlink = $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'previousrevision' ), 'direction=prev&oldid='.$oldid ); + $nextlink = $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'nextrevision' ), 'direction=next&oldid='.$oldid ); + $r = wfMsg( 'revisionasofwithlink', $td, $lnk, $prevlink, $nextlink ); $wgOut->setSubtitle( $r ); } @@ -1903,11 +1872,6 @@ class Article { $called = true; if($this->isFileCacheable()) { $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; @@ -1934,7 +1898,7 @@ class Article { return $wgUseFileCache and (!$wgShowIPinHeader) and ($this->getID() != 0) - and ($wgUser->getId() == 0) + and ($wgUser->isAnon()) and (!$wgUser->getNewtalk()) and ($this->mTitle->getNamespace() != NS_SPECIAL ) and (empty( $action ) || $action == 'view') @@ -1953,11 +1917,11 @@ class Article { $fname = 'Article::checkTouched'; $id = $this->getID(); $dbr =& $this->getDB(); - $s = $dbr->getArray( 'cur', array( 'cur_touched', 'cur_is_redirect' ), - array( 'cur_id' => $id ), $fname, $this->getSelectOptions() ); + $s = $dbr->selectRow( 'page', array( 'page_touched', 'page_is_redirect' ), + array( 'page_id' => $id ), $fname, $this->getSelectOptions() ); if( $s !== false ) { - $this->mTouched = wfTimestamp(TS_MW,$s->cur_touched); - return !$s->cur_is_redirect; + $this->mTouched = wfTimestamp( TS_MW, $s->page_touched ); + return !$s->page_is_redirect; } else { return false; } @@ -1971,61 +1935,44 @@ class Article { * @param integer $minor whereas it's a minor modification */ function quickEdit( $text, $comment = '', $minor = 0 ) { - global $wgUser, $wgMwRedir; + global $wgUser; $fname = 'Article::quickEdit'; + + #wfDebugDieBacktrace( "$fname called." ); + wfProfileIn( $fname ); $dbw =& wfGetDB( DB_MASTER ); $ns = $this->mTitle->getNamespace(); $dbkey = $this->mTitle->getDBkey(); $encDbKey = $dbw->strencode( $dbkey ); - $timestamp = wfTimestampNow(); - - # Save to history - $dbw->insertSelect( 'old', 'cur', - array( - 'old_namespace' => 'cur_namespace', - 'old_title' => 'cur_title', - 'old_text' => 'cur_text', - 'old_comment' => 'cur_comment', - 'old_user' => 'cur_user', - 'old_user_text' => 'cur_user_text', - 'old_timestamp' => 'cur_timestamp', - 'inverse_timestamp' => '99999999999999-cur_timestamp', - ), array( - 'cur_namespace' => $ns, - 'cur_title' => $dbkey, - ), $fname - ); - - # Use the affected row count to determine if the article is new - $numRows = $dbw->affectedRows(); - - # Make an array of fields to be inserted - $fields = array( - 'cur_text' => $text, - 'cur_timestamp' => $timestamp, - 'cur_user' => $wgUser->getID(), - 'cur_user_text' => $wgUser->getName(), - 'inverse_timestamp' => wfInvertTimestamp( $timestamp ), - 'cur_comment' => $comment, - 'cur_is_redirect' => $wgMwRedir->matchStart( $text ) ? 1 : 0, - 'cur_minor_edit' => intval($minor), - 'cur_touched' => $dbw->timestamp($timestamp), - ); - - if ( $numRows ) { - # Update article - $fields['cur_is_new'] = 0; - $dbw->updateArray( 'cur', $fields, array( 'cur_namespace' => $ns, 'cur_title' => $dbkey ), $fname ); - } else { - # Insert new article - $fields['cur_is_new'] = 1; - $fields['cur_namespace'] = $ns; - $fields['cur_title'] = $dbkey; - $fields['cur_random'] = $rand = number_format( mt_rand() / mt_getrandmax(), 12, '.', '' ); - $dbw->insertArray( 'cur', $fields, $fname ); - } + $timestamp = $dbw->timestamp(); + # insert new text + $dbw->insert( 'text', array( + 'old_text' => $text, + 'old_flags' => "" ), $fname ); + $text_id = $dbw->insertID(); + + # update page + $dbw->update( 'page', array( + 'page_is_new' => 0, + 'page_touched' => $timestamp, + 'page_is_redirect' => $this->isRedirect( $text ) ? 1 : 0, + 'page_latest' => $text_id ), + array( 'page_namespace' => $ns, 'page_title' => $dbkey ), $fname ); + # Retrieve page ID + $page_id = $dbw->selectField( 'page', 'page_id', array( 'page_namespace' => $ns, 'page_title' => $dbkey ), $fname ); + + # update revision + $dbw->insert( 'revision', array( + 'rev_id' => $text_id, + 'rev_page' => $page_id, + 'rev_comment' => $comment, + 'rev_user' => $wgUser->getID(), + 'rev_user_text' => $wgUser->getName(), + 'rev_timestamp' => $timestamp, + 'rev_minor_edit' => intval($minor) ), + $fname ); wfProfileOut( $fname ); } @@ -2040,12 +1987,12 @@ class Article { global $wgHitcounterUpdateFreq; $dbw =& wfGetDB( DB_MASTER ); - $curTable = $dbw->tableName( 'cur' ); + $pageTable = $dbw->tableName( 'page' ); $hitcounterTable = $dbw->tableName( 'hitcounter' ); $acchitsTable = $dbw->tableName( 'acchits' ); if( $wgHitcounterUpdateFreq <= 1 ){ // - $dbw->query( "UPDATE $curTable SET cur_counter = cur_counter + 1 WHERE cur_id = $id" ); + $dbw->query( "UPDATE $pageTable SET page_counter = page_counter + 1 WHERE page_id = $id" ); return; } @@ -2125,9 +2072,12 @@ class Article { /** * Info about this page + * Called for ?action=info when $wgAllowPageInfo is on. + * + * @access public */ function info() { - global $wgUser, $wgTitle, $wgOut, $wgLang, $wgAllowPageInfo; + global $wgLang, $wgOut, $wgAllowPageInfo; $fname = 'Article::info'; if ( !$wgAllowPageInfo ) { @@ -2135,67 +2085,107 @@ class Article { return; } - $dbr =& $this->getDB(); - - $basenamespace = $wgTitle->getNamespace() & (~1); - $cur_clause = array( 'cur_title' => $wgTitle->getDBkey(), 'cur_namespace' => $basenamespace ); - $old_clause = array( 'old_title' => $wgTitle->getDBkey(), 'old_namespace' => $basenamespace ); - $wl_clause = array( 'wl_title' => $wgTitle->getDBkey(), 'wl_namespace' => $basenamespace ); - $fullTitle = $wgTitle->makeName($basenamespace, $wgTitle->getDBKey()); - $wgOut->setPagetitle( $fullTitle ); + $page = $this->mTitle->getSubjectPage(); + + $wgOut->setPagetitle( $page->getPrefixedText() ); $wgOut->setSubtitle( wfMsg( 'infosubtitle' )); # first, see if the page exists at all. - $exists = $dbr->selectField( 'cur', 'COUNT(*)', $cur_clause, $fname, $this->getSelectOptions() ); - if ($exists < 1) { + $exists = $page->getArticleId() != 0; + if( !$exists ) { $wgOut->addHTML( wfMsg('noarticletext') ); } else { - $numwatchers = $dbr->selectField( 'watchlist', 'COUNT(*)', $wl_clause, $fname, + $dbr =& $this->getDB( DB_SLAVE ); + $wl_clause = array( + 'wl_title' => $page->getDBkey(), + 'wl_namespace' => $page->getNamespace() ); + $numwatchers = $dbr->selectField( + 'watchlist', + 'COUNT(*)', + $wl_clause, + $fname, $this->getSelectOptions() ); - $wgOut->addHTML( "
  • " . wfMsg("numwatchers", $numwatchers) . '
  • ' ); - $old = $dbr->selectField( 'old', 'COUNT(*)', $old_clause, $fname, $this->getSelectOptions() ); - $wgOut->addHTML( "
  • " . wfMsg('numedits', $old + 1) . '
  • '); - - # to find number of distinct authors, we need to do some - # funny stuff because of the cur/old table split: - # - first, find the name of the 'cur' author - # - then, find the number of *other* authors in 'old' - - # find 'cur' author - $cur_author = $dbr->selectField( 'cur', 'cur_user_text', $cur_clause, $fname, - $this->getSelectOptions() ); - - # find number of 'old' authors excluding 'cur' author - $authors = $dbr->selectField( 'old', 'COUNT(DISTINCT old_user_text)', - $old_clause + array( 'old_user_text<>' . $dbr->addQuotes( $cur_author ) ), $fname, - $this->getSelectOptions() ) + 1; - # now for the Talk page ... - $cur_clause = array( 'cur_title' => $wgTitle->getDBkey(), 'cur_namespace' => $basenamespace+1 ); - $old_clause = array( 'old_title' => $wgTitle->getDBkey(), 'old_namespace' => $basenamespace+1 ); - - # does it exist? - $exists = $dbr->selectField( 'cur', 'COUNT(*)', $cur_clause, $fname, $this->getSelectOptions() ); - - # number of edits - if ($exists > 0) { - $old = $dbr->selectField( 'old', 'COUNT(*)', $old_clause, $fname, $this->getSelectOptions() ); - $wgOut->addHTML( '
  • ' . wfMsg("numtalkedits", $old + 1) . '
  • '); + $pageInfo = $this->pageCountInfo( $page ); + $talkInfo = $this->pageCountInfo( $page->getTalkPage() ); + + $wgOut->addHTML( "
    • " . wfMsg("numwatchers", $wgLang->formatNum( $numwatchers ) ) . '
    • ' ); + $wgOut->addHTML( "
    • " . wfMsg('numedits', $wgLang->formatNum( $pageInfo['edits'] ) ) . '
    • '); + if( $talkInfo ) { + $wgOut->addHTML( '
    • ' . wfMsg("numtalkedits", $wgLang->formatNum( $talkInfo['edits'] ) ) . '
    • '); + } + $wgOut->addHTML( '
    • ' . wfMsg("numauthors", $wgLang->formatNum( $pageInfo['authors'] ) ) . '
    • ' ); + if( $talkInfo ) { + $wgOut->addHTML( '
    • ' . wfMsg('numtalkauthors', $wgLang->formatNum( $talkInfo['authors'] ) ) . '
    • ' ); } - $wgOut->addHTML( '
    • ' . wfMsg("numauthors", $authors) . '
    • ' ); + $wgOut->addHTML( '
    ' ); - # number of authors - if ($exists > 0) { - $cur_author = $dbr->selectField( 'cur', 'cur_user_text', $cur_clause, $fname, - $this->getSelectOptions() ); - $authors = $dbr->selectField( 'cur', 'COUNT(DISTINCT old_user_text)', - $old_clause + array( 'old_user_text<>' . $dbr->addQuotes( $cur_author ) ), - $fname, $this->getSelectOptions() ); + } + } + + /** + * Return the total number of edits and number of unique editors + * on a given page. If page does not exist, returns false. + * + * @param Title $title + * @return array + * @access private + */ + function pageCountInfo( $title ) { + $id = $title->getArticleId(); + if( $id == 0 ) { + return false; + } + + $dbr =& $this->getDB( DB_SLAVE ); + + $rev_clause = array( 'rev_page' => $id ); + $fname = 'Article::pageCountInfo'; + + $edits = $dbr->selectField( + 'revision', + 'COUNT(rev_page)', + $rev_clause, + $fname, + $this->getSelectOptions() ); + + $authors = $dbr->selectField( + 'revision', + 'COUNT(DISTINCT rev_user_text)', + $rev_clause, + $fname, + $this->getSelectOptions() ); + + return array( 'edits' => $edits, 'authors' => $authors ); + } +} - $wgOut->addHTML( '
  • ' . wfMsg('numtalkauthors', $authors) . '
' ); - } +/** + * Check whether an article is a stub + * + * @public + * @param integer $articleID ID of the article that is to be checked + */ +function wfArticleIsStub( $articleID ) { + global $wgUser; + $fname = 'wfArticleIsStub'; + + wfDebugDieBacktrace( 'This function seems to be unused. Pending removal.' ); + + $threshold = $wgUser->getOption('stubthreshold') ; + if ( $threshold > 0 ) { + $dbr =& wfGetDB( DB_SLAVE ); + $s = $dbr->selectRow( array('page', 'text'), + array( 'LENGTH(old_text) AS len', 'page_namespace', 'page_is_redirect' ), + array( 'page_id' => $articleID, "page.page_latest=text.old_id" ), + $fname ) ; + if ( $s == false OR $s->page_is_redirect OR $s->page_namespace != NS_MAIN ) { + return false; } + $size = $s->len; + return ( $size < $threshold ); } + return false; } ?>