Attached is a patch for a feature that creates an info page with information about a page. Currently, it is the number of people watching the page, the number of edits, and the number of talk edits, the number of authors of the article and the number of authors on the talk page.
It still some further work to: * Add link to info page from Views * Make it work with mysql 3 * Change it to a special page? * Break down the watchers into active and passive watchers (somehow).
Josh Cogliati wrote:
Attached is a patch for a feature that creates an info page with information about a page. Currently, it is the number of people watching the page, the number of edits, and the number of talk edits, the number of authors of the article and the number of authors on the talk page.
Thanks for your work! However, I have a few comments about it :)
- Your feature displays bogus information when the page and/or its talk page don't exist. - You forgot to display the total number of edits of the talk page, even though you added a string for it in Language.php. :) - Some of your queries were pretty inefficient: At one point, you retrieved a potentially large number of rows, just to count them. - Your patch uses "UNION". MySQL 3 doesn't support that. - The text you added to Language.php was a bit weird (why did you capitalise everything?).
I fixed all of this in one fell swoop, see attachment :)
Sorry about the whitespace changes in my patch, but there aren't many, they don't make the patch unreadable, and committing them once will make them never occur again :)
Timwi
Index: index.php =================================================================== RCS file: /cvsroot/wikipedia/phase3/index.php,v retrieving revision 1.35 diff -u -r1.35 index.php --- index.php 28 Jun 2004 17:46:49 -0000 1.35 +++ index.php 8 Jul 2004 11:04:20 -0000 @@ -108,6 +108,7 @@ case "rollback": case "protect": case "unprotect": + case "info": $wgArticle->$action(); break; case "print": Index: includes/Article.php =================================================================== RCS file: /cvsroot/wikipedia/phase3/includes/Article.php,v retrieving revision 1.175 diff -u -r1.175 Article.php --- includes/Article.php 28 Jun 2004 20:24:23 -0000 1.175 +++ includes/Article.php 8 Jul 2004 11:04:20 -0000 @@ -16,12 +16,12 @@ /* private */ var $mMinorEdit, $mRedirectedFrom; /* private */ var $mTouched, $mFileCache, $mTitle; /* private */ var $mId, $mTable; - + function Article( &$title ) { $this->mTitle =& $title; $this->clear(); } - + /* private */ function clear() { $this->mContentLoaded = false; @@ -62,7 +62,7 @@ } return $text; } - + /* static */ function compressRevisionText( &$text ) { global $wgCompressRevisions; if( !$wgCompressRevisions ) { @@ -75,7 +75,7 @@ $text = gzdeflate( $text ); return 'gzip'; } - + # Returns the text associated with a "link" type old table row /* static */ function followLink( $link ) { # Split the link into fields and values @@ -119,7 +119,7 @@ global $wgLoadBalancer; $fname = 'fetchFromLocation'; wfProfileIn( $fname ); - + $p = strpos( $location, ':' ); if ( $p === false ) { wfProfileOut( $fname ); @@ -132,7 +132,7 @@ case 'mysql': # MySQL locations are specified by mysql://<machineID>/<dbname>/<tblname>/<index> # Machine ID 0 is the current connection - if ( preg_match( '/^mysql://(\d+)/([A-Za-z_]+)/([A-Za-z_]+)/([A-Za-z_]+)$/', + if ( preg_match( '/^mysql://(\d+)/([A-Za-z_]+)/([A-Za-z_]+)/([A-Za-z_]+)$/', $location, $matches ) ) { $machineID = $matches[1]; $dbName = $matches[2]; @@ -144,11 +144,11 @@ } 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'], + $db = Database::newFromParams( $params['server'], $params['user'], $params['password'], $dbName, 1, false, true, true ); } } @@ -244,15 +244,15 @@ } } } - + # This function returns the text of a section, specified by a number ($section). - # A section is text under a heading like == Heading == or <h1>Heading</h1>, or + # A section is text under a heading like == Heading == or <h1>Heading</h1>, or # the first section before any such heading (section 0). # # If a section contains subsections, these are also returned. # function getSection($text,$section) { - + # strip NOWIKI etc. to avoid confusion (true-parameter causes HTML # comments to be stripped as well) $striparray=array(); @@ -273,23 +273,23 @@ $headline=$secs[$section*2-1]; preg_match( '/^(=+).*?=+|^<h([1-6]).*?' . '>.*?</h[1-6].*?' . '>/mi',$headline,$matches); $hlevel=$matches[1]; - + # translate wiki heading into level if(strpos($hlevel,'=')!==false) { - $hlevel=strlen($hlevel); + $hlevel=strlen($hlevel); } - + $rv=$headline. $secs[$section*2]; $count=$section+1; - + $break=false; while(!empty($secs[$count*2-1]) && !$break) { - + $subheadline=$secs[$count*2-1]; preg_match( '/^(=+).*?=+|^<h([1-6]).*?' . '>.*?</h[1-6].*?' . '>/mi',$subheadline,$matches); $subhlevel=$matches[1]; if(strpos($subhlevel,'=')!==false) { - $subhlevel=strlen($subhlevel); + $subhlevel=strlen($subhlevel); } if($subhlevel > $hlevel) { $rv.=$subheadline.$secs[$count*2]; @@ -298,7 +298,7 @@ $break=true; } $count++; - + } } # reinsert stripped tags @@ -308,7 +308,7 @@ return $rv;
} - +
# Load the revision (including cur_text) into this object function loadContent( $noredir = false ) @@ -321,21 +321,21 @@
if ( $this->mContentLoaded ) return; $fname = 'Article::loadContent'; - - # Pre-fill content with error message so that if something + + # 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 ); + $t = $this->mTitle->getPrefixedText(); + if ( isset( $oldid ) ) { + $oldid = IntVal( $oldid ); $t .= ",oldid={$oldid}"; - } - if ( isset( $redirect ) ) { - $redirect = ($redirect == 'no') ? 'no' : 'yes'; - $t .= ",redirect={$redirect}"; - } + } + if ( isset( $redirect ) ) { + $redirect = ($redirect == 'no') ? 'no' : 'yes'; + $t .= ",redirect={$redirect}"; + } $this->mContent = wfMsg( 'missingarticle', $t ); - + if ( ! $oldid ) { # Retrieve current version $id = $this->getID(); if ( 0 == $id ) return; @@ -345,8 +345,8 @@ "FROM cur WHERE cur_id={$id}"; wfDebug( "$sql\n" ); $res = wfQuery( $sql, DB_READ, $fname ); - if ( 0 == wfNumRows( $res ) ) { - return; + if ( 0 == wfNumRows( $res ) ) { + return; }
$s = wfFetchObject( $res ); @@ -361,7 +361,7 @@ # 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; @@ -375,7 +375,7 @@ $sql = 'SELECT cur_text,cur_timestamp,cur_user,cur_user_text,cur_comment,' . "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; @@ -434,9 +434,9 @@ return $this->mContent; } $this->mContent = false; - + $fname = 'Article::loadContent'; - + if ( ! $oldid ) { # Retrieve current version $id = $this->getID(); if ( 0 == $id ) { @@ -447,8 +447,8 @@ 'cur_text,cur_timestamp,cur_user,cur_counter,cur_restrictions,cur_touched ' . "FROM cur WHERE cur_id={$id}"; $res = wfQuery( $sql, DB_READ, $fname ); - if ( 0 == wfNumRows( $res ) ) { - return false; + if ( 0 == wfNumRows( $res ) ) { + return false; }
$s = wfFetchObject( $res ); @@ -464,7 +464,7 @@ $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; @@ -488,8 +488,8 @@ $sql = "SELECT old_text,old_timestamp,old_user,old_flags FROM $oldtable " . "WHERE old_id={$oldid}"; $res = wfQuery( $sql, DB_READ, $fname ); - if ( 0 == wfNumRows( $res ) ) { - return false; + if ( 0 == wfNumRows( $res ) ) { + return false; }
$s = wfFetchObject( $res ); @@ -526,7 +526,7 @@ function isCountable( $text ) { global $wgUseCommaCount, $wgMwRedir; - + if ( 0 != $this->mTitle->getNamespace() ) { return 0; } if ( $wgMwRedir->matchStart( $text ) ) { return 0; } $token = ($wgUseCommaCount ? ',' : '[[' ); @@ -592,7 +592,7 @@ $fname = 'Article::getContributors';
# XXX: this is expensive; cache this info somewhere. - + $title = $this->mTitle;
$contribs = array(); @@ -602,8 +602,8 @@ ' FROM old LEFT JOIN user ON old.old_user = user.user_id ' . ' WHERE old.old_namespace = ' . $title->getNamespace() . ' AND old.old_title = "' . $title->getDBkey() . '"' . - ' AND old.old_user != ' . $this->getUser() . - ' GROUP BY old.old_user ' . + ' AND old.old_user != ' . $this->getUser() . + ' GROUP BY old.old_user ' . ' ORDER BY timestamp DESC ';
if ($limit > 0) { @@ -611,16 +611,16 @@ }
$res = wfQuery($sql, DB_READ, $fname); - + while ( $line = wfFetchObject( $res ) ) { $contribs[] = array($line->old_user, $line->old_user_text, $line->user_real_name); } - + wfFreeResult($res); - + return $contribs; } - + # This is the default action of the script: just view the page of # the given title.
@@ -628,7 +628,7 @@ { global $wgUser, $wgOut, $wgLang, $wgRequest; global $wgLinkCache, $IP, $wgEnableParserCache; - + $fname = 'Article::view'; wfProfileIn( $fname );
@@ -660,14 +660,14 @@ return; } } - + # Should the parser cache be used? if ( $wgEnableParserCache && intval($wgUser->getOption( 'stubthreshold' )) == 0 && empty( $oldid ) ) { $pcache = true; } else { $pcache = false; } - + $outputDone = false; if ( $pcache ) { if ( $wgOut->tryParserCache( $this, $wgUser ) ) { @@ -677,14 +677,14 @@
if ( !$outputDone ) { $text = $this->getContent( false ); # May change mTitle by following a redirect - + # Another whitelist check in case oldid or redirects are altering the title if ( !$this->mTitle->userCanRead() ) { $wgOut->loginToUse(); $wgOut->output(); exit; } - +
# We're looking at an old revision
@@ -698,8 +698,8 @@ 'redirect=no' ); $s = wfMsg( 'redirectedfrom', $redir ); $wgOut->setSubtitle( $s ); - - # Can't cache redirects + + # Can't cache redirects $pcache = false; }
@@ -707,8 +707,8 @@
# wrap user css and user js in pre and don't parse # XXX: use $this->mTitle->usCssJsSubpage() when php is fixed/ a workaround is found - if ( - $this->mTitle->getNamespace() == Namespace::getUser() && + if ( + $this->mTitle->getNamespace() == Namespace::getUser() && preg_match('/\/[\w]+\.(css|js)$/', $this->mTitle->getDBkey()) ) { $wgOut->addWikiText( wfMsg('clearyourcache')); @@ -720,7 +720,7 @@ } } $wgOut->setPageTitle( $this->mTitle->getPrefixedText() ); - + # Add link titles as META keywords $wgOut->addMetaTags() ;
@@ -737,7 +737,7 @@ { global $wgOut, $wgUser, $wgLinkCache, $wgMwRedir; global $wgUseSquid, $wgDeferredUpdateList, $wgInternalServer, $wgIsPg, $wgIsMySQL; - + $fname = 'Article::insertNewArticle';
$this->mCountAdjustment = $this->isCountable( $text ); @@ -752,7 +752,7 @@ $won = wfInvertTimestamp( $now ); wfSeedRandom(); $rand = number_format( mt_rand() / mt_getrandmax(), 12, '.', '' ); - + if ($wgIsPg) { $cur_id_column="cur_id,"; $cur_id=wfGetSQL(""," nextval('cur_cur_id_seq')"); @@ -780,20 +780,20 @@
Article::onArticleCreate( $this->mTitle ); RecentChange::notifyNew( $now, $this->mTitle, $isminor, $wgUser, $summary ); - - if ($watchthis) { - if(!$this->mTitle->userIsWatching()) $this->watch(); + + if ($watchthis) { + if(!$this->mTitle->userIsWatching()) $this->watch(); } else { 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 ); - + # standard deferred updates $this->editUpdates( $text );
@@ -804,7 +804,7 @@ /* Side effects: loads last edit */ function getTextOfLastEditWithSectionReplacedOrAdded($section, $text, $summary = ''){ $this->loadLastEdit(); - $oldtext = $this->getContent( true ); + $oldtext = $this->getContent( true ); if ($section != '') { if($section=='new') { if($summary) $subject="== {$summary} ==\n\n"; @@ -825,10 +825,10 @@ $secs=preg_split('/(^=+.*?=+|^<h[1-6].*?' . '>.*?</h[1-6].*?' . '>)/mi', $oldtext,-1,PREG_SPLIT_DELIM_CAPTURE); $secs[$section*2]=$text."\n\n"; // replace with edited - + # section 0 is top (intro) section - if($section!=0) { - + if($section!=0) { + # headline of old section - we need to go through this section # to determine if there are any subsections that now need to # be erased, as the mother section has been replaced with @@ -836,23 +836,23 @@ $headline=$secs[$section*2-1]; preg_match( '/^(=+).*?=+|^<h([1-6]).*?' . '>.*?</h[1-6].*?' . '>/mi',$headline,$matches); $hlevel=$matches[1]; - + # determine headline level for wikimarkup headings if(strpos($hlevel,'=')!==false) { - $hlevel=strlen($hlevel); + $hlevel=strlen($hlevel); } - + $secs[$section*2-1]=''; // erase old headline $count=$section+1; $break=false; while(!empty($secs[$count*2-1]) && !$break) { - + $subheadline=$secs[$count*2-1]; preg_match( '/^(=+).*?=+|^<h([1-6]).*?' . '>.*?</h[1-6].*?' . '>/mi',$subheadline,$matches); $subhlevel=$matches[1]; if(strpos($subhlevel,'=')!==false) { - $subhlevel=strlen($subhlevel); + $subhlevel=strlen($subhlevel); } if($subhlevel > $hlevel) { // erase old subsections @@ -863,16 +863,16 @@ $break=true; } $count++; - + } - + } $text=join('',$secs); # reinsert the stuff that we stripped out earlier - $text=$parser->unstrip($text,$striparray); - $text=$parser->unstripNoWiki($text,$striparray); + $text=$parser->unstrip($text,$striparray); + $text=$parser->unstripNoWiki($text,$striparray); } - + } return $text; } @@ -886,7 +886,7 @@ $fname = 'Article::updateArticle';
if ( $this->mMinorEdit ) { $me1 = 1; } else { $me1 = 0; } - if ( $minor && $wgUser->getID() ) { $me2 = 1; } else { $me2 = 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 @@ -894,7 +894,7 @@ else { $redir = 0; }
$text = $this->preSaveTransform( $text ); - + # Update article, but only if changed.
if( $wgDBtransactions ) { @@ -918,7 +918,7 @@ "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; @@ -926,7 +926,7 @@
# This overwrites $oldtext if revision compression is on $flags = Article::compressRevisionText( $oldtext ); - + $oldtable=$wgIsPg?'"old"':'old'; if ($wgIsPg) { $oldtable='"old"'; @@ -956,7 +956,7 @@ $oldid = $wgIsPg?$old_id:wfInsertId( $res );
$bot = (int)($wgUser->isBot() || $forceBot); - RecentChange::notifyEdit( $now, $this->mTitle, $me2, $wgUser, $summary, + RecentChange::notifyEdit( $now, $this->mTitle, $me2, $wgUser, $summary, $oldid, $this->getTimestamp(), $bot ); Article::onArticleEdit( $this->mTitle ); } @@ -965,9 +965,9 @@ $sql = 'COMMIT'; wfQuery( $sql, DB_WRITE ); } - - if ($watchthis) { - if (!$this->mTitle->userIsWatching()) $this->watch(); + + if ($watchthis) { + if (!$this->mTitle->userIsWatching()) $this->watch(); } else { if ( $this->mTitle->userIsWatching() ) { $this->unwatch(); @@ -975,8 +975,8 @@ } # standard deferred updates $this->editUpdates( $text ); - - + + $urls = array(); # Template namespace # Purge all articles linking here @@ -989,7 +989,7 @@ } } } - + # Squid updates if ( $wgUseSquid ) { $urls = array_merge( $urls, $this->mTitle->getSquidURLs() ); @@ -1015,7 +1015,7 @@ $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 );
@@ -1178,16 +1178,16 @@ return $this->protect( '' ); }
- # UI entry point for page deletion + # UI entry point for page deletion function delete() { global $wgUser, $wgOut, $wgMessageCache, $wgRequest, $wgIsPg; $fname = 'Article::delete'; $confirm = $wgRequest->getBool( 'wpConfirm' ) && $wgRequest->wasPosted(); $reason = $wgRequest->getText( 'wpReason' ); - + # This code desperately needs to be totally rewritten - + # Check permissions if ( ( ! $wgUser->isSysop() ) ) { $wgOut->sysopRequired(); @@ -1241,19 +1241,19 @@ $text = Article::getRevisionText( $old ); $blanked = true; } - + } - - $length=strlen($text); - + + $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 && $reason === '') { + if($length == 0 && $reason === '') { $reason = wfMsg('exblank'); } - + if($length < 500 && $reason === '') { - + # comment field=255, let's grep the first 150 to have some user # space left $text=substr($text,0,150); @@ -1268,27 +1268,27 @@ $reason=wfMsg('exbeforeblank') . " '".$text; } if($length>150) { $reason .= '...'; } # we've only pasted part of the text - $reason.="'"; + $reason.="'"; } }
return $this->confirmDelete( '', $reason ); } - + # Output deletion confirmation dialog function confirmDelete( $par, $reason ) { global $wgOut;
wfDebug( "Article::confirmDelete\n" ); - + $sub = htmlspecialchars( $this->mTitle->getPrefixedText() ); $wgOut->setSubtitle( wfMsg( 'deletesub', $sub ) ); $wgOut->setRobotpolicy( 'noindex,nofollow' ); $wgOut->addWikiText( wfMsg( 'confirmdeletetext' ) );
$formaction = $this->mTitle->escapeLocalURL( 'action=delete' . $par ); - + $confirm = htmlspecialchars( wfMsg( 'confirm' ) ); $check = htmlspecialchars( wfMsg( 'confirmcheck' ) ); $delcom = htmlspecialchars( wfMsg( 'deletecomment' ) ); @@ -1334,7 +1334,7 @@ $fname = 'Article::doDelete'; wfDebug( "$fname\n" );
- if ( $this->doDeleteArticle( $reason ) ) { + if ( $this->doDeleteArticle( $reason ) ) { $deleted = $this->mTitle->getPrefixedText();
$wgOut->setPagetitle( wfMsg( 'actioncomplete' ) ); @@ -1375,14 +1375,14 @@
$u = new SiteStatsUpdate( 0, 1, -$this->isCountable( $this->getContent( true ) ) ); array_push( $wgDeferredUpdateList, $u ); - + $linksTo = $this->mTitle->getLinksTo();
# Squid purging if ( $wgUseSquid ) { - $urls = array( + $urls = array( $this->mTitle->getInternalURL(), - $this->mTitle->getInternalURL( 'history' ) + $this->mTitle->getInternalURL( 'history' ) ); foreach ( $linksTo as $linkTo ) { $urls[] = $linkTo->getInternalURL(); @@ -1420,7 +1420,7 @@ $sql = "DELETE FROM old WHERE old_namespace={$ns} AND " . "old_title='{$t}'"; wfQuery( $sql, DB_WRITE, $fname ); - + $sql = "DELETE FROM recentchanges WHERE rc_namespace={$ns} AND " . "rc_title='{$t}'"; wfQuery( $sql, DB_WRITE, $fname ); @@ -1429,7 +1429,7 @@ $t = wfStrencode( $this->mTitle->getPrefixedDBkey() );
Article::onArticleDelete( $this->mTitle ); - + $sql = 'INSERT INTO brokenlinks (bl_from,bl_to) VALUES '; $first = true;
@@ -1437,7 +1437,7 @@ if ( ! $first ) { $sql .= ','; } $first = false; # Get article ID. Efficient because it was loaded into the cache by getLinksTo(). - $linkID = $titleObj->getArticleID(); + $linkID = $titleObj->getArticleID(); $sql .= "({$linkID},'{$t}')"; } if ( ! $first ) { @@ -1455,10 +1455,10 @@
$sql = "DELETE FROM brokenlinks WHERE bl_from={$id}"; wfQuery( $sql, DB_WRITE, $fname ); - + $sql = "DELETE FROM categorylinks WHERE cl_from={$id}"; wfQuery( $sql, DB_WRITE, $fname ); - + $log = new LogPage( wfMsg( 'dellogpage' ), wfMsg( 'dellogpagetext' ) ); $art = $this->mTitle->getPrefixedText(); $log->addEntry( wfMsg( 'deletedarticle', $art ), $reason ); @@ -1481,14 +1481,14 @@ $wgOut->readOnlyPage( $this->getContent( true ) ); return; } - + # Enhanced rollback, marks edits rc_bot=1 $bot = $wgRequest->getBool( 'bot' ); - + # Replace all this user's current edits with the next one down $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}"; @@ -1502,7 +1502,7 @@ $ut = wfStrencode( $s->cur_user_text ); $uid = $s->cur_user; $pid = $s->cur_id; - + $from = str_replace( '_', ' ', $wgRequest->getVal( 'from' ) ); if( $from != $s->cur_user_text ) { $wgOut->setPageTitle(wfmsg('rollbackfailed')); @@ -1517,7 +1517,7 @@ } return; } - + # Get the last edit not by this guy
$use_index=$wgIsMySQL?"USE INDEX (name_title_timestamp)":""; @@ -1535,7 +1535,7 @@ return; } $s = wfFetchObject( $res ); - + if ( $bot ) { # Mark all reverted edits as bot $sql = 'UPDATE recentchanges SET rc_bot=1 WHERE ' . @@ -1552,7 +1552,7 @@ Article::onArticleEdit( $this->mTitle ); $wgOut->returnToMain( false ); } - +
# Do standard deferred updates after page view
@@ -1589,7 +1589,7 @@ $id = $this->getID(); $title = $this->mTitle->getPrefixedDBkey(); $shortTitle = $this->mTitle->getDBkey(); - + $adj = $this->mCountAdjustment;
if ( 0 != $id ) { @@ -1626,7 +1626,7 @@ global $wgParser, $wgUser; return $wgParser->preSaveTransform( $text, $this->mTitle, $wgUser, ParserOptions::newFromUser( $wgUser ) ); } - + /* Caching functions */
# checkLastModified returns true if it has taken care of all @@ -1653,7 +1653,7 @@ $cache->loadFromFileCache(); return true; } else { - wfDebug( " tryFileCache() - starting buffer\n" ); + wfDebug( " tryFileCache() - starting buffer\n" ); ob_start( array(&$cache, 'saveToFileCache' ) ); } } else { @@ -1664,7 +1664,7 @@ function isFileCacheable() { global $wgUser, $wgUseFileCache, $wgShowIPinHeader, $wgRequest; extract( $wgRequest->getValues( 'action', 'oldid', 'diff', 'redirect', 'printable' ) ); - + return $wgUseFileCache and (!$wgShowIPinHeader) and ($this->getID() != 0) @@ -1678,7 +1678,7 @@ and (!isset($printable)) and (!$this->mRedirectedFrom); } - + # Loads cur_touched and returns a value indicating if it should be used function checkTouched() { $id = $this->getID(); @@ -1702,14 +1702,14 @@ $dbkey = $this->mTitle->getDBkey(); $encDbKey = wfStrencode( $dbkey ); $timestamp = wfTimestampNow(); - + # Save to history $oldtable=$wgIsPg?'"old"':'old'; $sql = "INSERT INTO $oldtable (old_namespace,old_title,old_text,old_comment,old_user,old_user_text,old_timestamp,inverse_timestamp) SELECT cur_namespace,cur_title,cur_text,cur_comment,cur_user,cur_user_text,cur_timestamp,99999999999999-cur_timestamp FROM cur WHERE cur_namespace=$ns AND cur_title='$encDbKey'"; wfQuery( $sql, DB_WRITE ); - + # Use the affected row count to determine if the article is new $numRows = wfAffectedRows();
@@ -1746,14 +1746,14 @@ $id = intval( $id ); global $wgHitcounterUpdateFreq;
- if( $wgHitcounterUpdateFreq <= 1 ){ // + 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 ); + $oldignore = wfIgnoreSQLErrors( true );
wfQuery("INSERT INTO hitcounter (hc_id) VALUES ({$id})", DB_WRITE);
@@ -1767,7 +1767,7 @@ $res = wfQuery('SELECT COUNT(*) as n FROM hitcounter', DB_WRITE); $row = wfFetchObject( $res ); $rown = intval( $row->n ); - if( $rown >= $wgHitcounterUpdateFreq ){ + if( $rown >= $wgHitcounterUpdateFreq ){ wfProfileIn( 'Article::incViewCount-collect' ); $old_user_abort = ignore_user_abort( true );
@@ -1788,18 +1788,18 @@ }
# The onArticle*() functions are supposed to be a kind of hooks - # which should be called whenever any of the specified actions - # are done. + # 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. + # This is a good place to put code to clear caches, for instance.
- # This is called on page move and undelete, as well as edit + # This is called on page move and undelete, as well as edit /* static */ function onArticleCreate($title_obj){ global $wgUseSquid, $wgDeferredUpdateList;
$titles = $title_obj->getBrokenLinksTo(); - - # Purge squid + + # Purge squid if ( $wgUseSquid ) { $urls = $title_obj->getSquidURLs(); foreach ( $titles as $linkTitle ) { @@ -1820,6 +1820,90 @@ /* static */ function onArticleEdit($title_obj){ LinkCache::linksccClearPage( $title_obj->getArticleID() ); } + + # Info about this page + + function info() + { + global $wgUser, $wgTitle, $wgOut, $wgLang; + + $basenamespace = $wgTitle->getNamespace() & (~1); + $cur_clause = "cur_title='".$wgTitle->getDBkey()."' AND cur_namespace=".$basenamespace; + $old_clause = "old_title='".$wgTitle->getDBkey()."' AND old_namespace=".$basenamespace; + $wl_clause = "wl_title='".$wgTitle->getDBkey()."' AND wl_namespace=".$basenamespace; + $fullTitle = $wgTitle->makeName($basenamespace, $wgTitle->getDBKey()); + $wgOut->setPagetitle( $fullTitle ); + $wgOut->setSubtitle( wfMsg( "infosubtitle" )); + + # first, see if the page exists at all. + $sql = "SELECT COUNT(*) FROM cur WHERE ".$cur_clause; + $exists = wfSingleQuery( $sql , DB_READ ); + if ($exists < 1) { + $wgOut->addHTML( wfMsg("noarticletext") ); + } else { + $sql = "SELECT COUNT(*) FROM watchlist WHERE ".$wl_clause; + $wgOut->addHTML( "<ul><li>" . wfMsg("numwatchers") . wfSingleQuery( $sql, DB_READ ) . "</li>" ); + $sql = "SELECT COUNT(*) FROM old WHERE ".$old_clause; + $old = wfSingleQuery( $sql, DB_READ ); + $wgOut->addHTML( "<li>" . wfMsg("numedits") . ($old + 1) . "</li>"); + + # 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 authors in 'old' + # - then, find out if the 'cur' author is anywhere in 'old'; if not, add 1 + + # find 'cur' author + $sql = "SELECT cur_user_text FROM cur WHERE ".$cur_clause; + $cur_author = wfSingleQuery( $sql, DB_READ ); + + # find number of 'old' authors + $sql = "SELECT COUNT(DISTINCT old_user_text) FROM old WHERE ".$old_clause; + $authors = wfSingleQuery( $sql, DB_READ ); + + # find out if 'cur' author occurs in 'old' + $sql = "SELECT old_user_text FROM old WHERE ".$old_clause + ." AND old_user_text='".$cur_author."' LIMIT 1"; + $res = wfQuery( $sql, DB_READ); + $has_cur_user = wfNumRows( $res ); + wfFreeResult($res); + if ($has_cur_user < 1) { $authors++; } + + # now for the Talk page ... + $cur_clause = "cur_title='".$wgTitle->getDBkey()."' AND cur_namespace=".($basenamespace+1); + $old_clause = "old_title='".$wgTitle->getDBkey()."' AND old_namespace=".($basenamespace+1); + + # does it exist? + $sql = "SELECT COUNT(*) FROM cur WHERE ".$cur_clause; + $exists = wfSingleQuery( $sql , DB_READ ); + + # number of edits + if ($exists > 0) { + $sql = "SELECT COUNT(*) FROM old WHERE ".$old_clause; + $old = wfSingleQuery( $sql, DB_READ ); + $wgOut->addHTML( "<li>" . wfMsg("numtalkedits") . ($old + 1) . "</li>"); + } + $wgOut->addHTML( "<li>" . wfMsg("numauthors") . $authors . "</li>" ); + + # number of authors + if ($exists > 0) { + $sql = "SELECT cur_user_text FROM cur WHERE ".$cur_clause; + $cur_author = wfSingleQuery( $sql, DB_READ ); + + $sql = "SELECT COUNT(DISTINCT old_user_text) FROM old WHERE ".$old_clause; + $authors = wfSingleQuery( $sql, DB_READ ); + + $sql = "SELECT old_user_text FROM old WHERE ".$old_clause + ." AND old_user_text='".$cur_author."' LIMIT 1"; + $res = wfQuery( $sql, DB_READ); + $has_cur_user = wfNumRows( $res ); + wfFreeResult($res); + if ($has_cur_user < 1) { $authors++; } + + $wgOut->addHTML( "<li>" . wfMsg("numtalkauthors") . $authors . "</li></ul>" ); + } + } + } }
?> Index: includes/DatabaseFunctions.php =================================================================== RCS file: /cvsroot/wikipedia/phase3/includes/DatabaseFunctions.php,v retrieving revision 1.36 diff -u -r1.36 DatabaseFunctions.php --- includes/DatabaseFunctions.php 22 Jun 2004 08:54:26 -0000 1.36 +++ includes/DatabaseFunctions.php 8 Jul 2004 11:04:20 -0000 @@ -39,6 +39,15 @@ } }
+function wfSingleQuery( $sql, $db, $fname = "" ) +{ + $res = wfQuery($sql, $db, $fname ); + $row = wfFetchRow( $res ); + $ret = $row[0]; + wfFreeResult( $res ); + return $ret; +} + function &wfGetDB( $db = DB_LAST ) { global $wgLoadBalancer; Index: languages/Language.php =================================================================== RCS file: /cvsroot/wikipedia/phase3/languages/Language.php,v retrieving revision 1.257 diff -u -r1.257 Language.php --- languages/Language.php 5 Jul 2004 21:26:35 -0000 1.257 +++ languages/Language.php 8 Jul 2004 11:04:20 -0000 @@ -1307,6 +1307,14 @@ 'categoryarticlecount' => "There are $1 articles in this category.", 'usenewcategorypage' => "1\n\nSet first character to "0" to disable the new category page layout.",
+# Info page +"infosubtitle" => "Information for page", +"numedits" => "Number of edits (article): ", +"numtalkedits" => "Number of edits (discussion page): ", +"numwatchers" => "Number of watchers: ", +"numauthors" => "Number of distinct authors (article): ", +"numtalkauthors" => "Number of distinct authors (discussion page): ", + # Monobook.js: tooltips and access keys for monobook 'Monobook.js' => '/* tooltips and access keys */ ta = new Object();
Oh, I forgot to mention:
- Add link to info page from Views
Yes, someone with knowledge of the skin system should do this :)
- Make it work with mysql 3
My patch already does this
- Change it to a special page?
No, please not!... I don't like special pages because they make the tabs disappear. :) I'd really rather prefer if Special:Movepage were converted to a simple &action=move thingie.
- Break down the watchers into active and passive watchers (somehow).
How do you define this distinction?
Timwi
Timwi, Thanks for the version of the patch. I have some questions though. On Thu, Jul 08, 2004 at 12:07:08PM +0100, Timwi wrote:
- Change it to a special page?
No, please not!... I don't like special pages because they make the tabs disappear. :) I'd really rather prefer if Special:Movepage were converted to a simple &action=move thingie.
Tim Starling made the argument that special pages were easier to link to and did not involve adding more UI stuff to Article.php.
- Break down the watchers into active and passive watchers (somehow).
How do you define this distinction?
I am still thinking about this. Probably active watchers have looked at their watchlist in the past week. Doing this efficiently is the problem.
+ + # 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 authors in 'old' + # - then, find out if the 'cur' author is anywhere in 'old'; if not, add 1 + + # find 'cur' author + $sql = "SELECT cur_user_text FROM cur WHERE ".$cur_clause; + $cur_author = wfSingleQuery( $sql, DB_READ ); + + # find number of 'old' authors + $sql = "SELECT COUNT(DISTINCT old_user_text) FROM old WHERE ".$old_clause; + $authors = wfSingleQuery( $sql, DB_READ ); + + # find out if 'cur' author occurs in 'old' + $sql = "SELECT old_user_text FROM old WHERE ".$old_clause + ." AND old_user_text='".$cur_author."' LIMIT 1"; + $res = wfQuery( $sql, DB_READ); + $has_cur_user = wfNumRows( $res ); + wfFreeResult($res); + if ($has_cur_user < 1) { $authors++; }
Would it be quicker to turn this into two query? Where the second query is something like: + $sql = "SELECT COUNT(DISTINCT old_user_text) FROM old WH ERE ".$old_clause . " AND old_user_text != " . $cur_author; Then you can always $authors++ since the current author will not appear in the second query. So the second query is becomes more complicated, but the third query is unneeded.
Josh Cogliati wrote:
Timwi, Thanks for the version of the patch. I have some questions though. On Thu, Jul 08, 2004 at 12:07:08PM +0100, Timwi wrote:
- Change it to a special page?
No, please not!... I don't like special pages because they make the tabs disappear. :) I'd really rather prefer if Special:Movepage were converted to a simple &action=move thingie.
Tim Starling made the argument that special pages were easier to link to and did not involve adding more UI stuff to Article.php.
In my mind, it makes sense to be able to link to pages that pertain to the wiki as a whole (e.g. Special:Recentchanges) or to a user (e.g. Special:Contributions), but I don't see the benefit in being able to link to Special:Movepage. (Admittedly, there is an obvious benefit in being able to link to the info page for an article... but quite frankly, I think if we really need that so badly, we should come up with a new syntax for it so that we can also link to edit, history, etc.
Suggestion: [[::history:Article title]]
Would it be quicker to turn this into two query? Where the second query is something like:
$sql = "SELECT COUNT(DISTINCT old_user_text) FROM old WHERE ".$old_clause . " AND old_user_text != " . $cur_author;
Ingenious! Brilliant! Thanks!
I've also made it so that the <title> now shows "Article title - Information - Site name" as opposed to just "Article title - Site name", so it is consistent with History.
While I was at it, I noticed missing or wrong <title>s when protect or unprotect a page. I've fixed those too.
I'm attaching the entire patch again. I think it's committable like this, although someone should still add an "info" tab to the skin or else the info page is unreachable except by manually fiddling with the URL.
Timwi
? config/LocalSettings.php ? images/9 ? images/archive ? math/html.cmi ? math/html.cmx ? math/lexer.cmi ? math/lexer.cmx ? math/mathml.cmi ? math/mathml.cmx ? math/parser.cmi ? math/parser.cmx ? math/parser.mli ? math/render.cmi ? math/render.cmx ? math/render_info.cmi ? math/tex.cmi ? math/texutil.cmi ? math/texutil.cmx ? math/texvc ? math/texvc.cmi ? math/texvc.cmx ? math/texvc_test ? math/texvc_test.cmi ? math/texvc_test.cmx ? math/texvc_tex ? math/texvc_tex.cmi ? math/texvc_tex.cmx ? math/util.cmi ? math/util.cmx Index: index.php =================================================================== RCS file: /cvsroot/wikipedia/phase3/index.php,v retrieving revision 1.35 diff -u -r1.35 index.php --- index.php 28 Jun 2004 17:46:49 -0000 1.35 +++ index.php 8 Jul 2004 14:33:17 -0000 @@ -108,6 +108,7 @@ case "rollback": case "protect": case "unprotect": + case "info": $wgArticle->$action(); break; case "print": Index: includes/Article.php =================================================================== RCS file: /cvsroot/wikipedia/phase3/includes/Article.php,v retrieving revision 1.175 diff -u -r1.175 Article.php --- includes/Article.php 28 Jun 2004 20:24:23 -0000 1.175 +++ includes/Article.php 8 Jul 2004 14:33:17 -0000 @@ -16,12 +16,12 @@ /* private */ var $mMinorEdit, $mRedirectedFrom; /* private */ var $mTouched, $mFileCache, $mTitle; /* private */ var $mId, $mTable; - + function Article( &$title ) { $this->mTitle =& $title; $this->clear(); } - + /* private */ function clear() { $this->mContentLoaded = false; @@ -62,7 +62,7 @@ } return $text; } - + /* static */ function compressRevisionText( &$text ) { global $wgCompressRevisions; if( !$wgCompressRevisions ) { @@ -75,7 +75,7 @@ $text = gzdeflate( $text ); return 'gzip'; } - + # Returns the text associated with a "link" type old table row /* static */ function followLink( $link ) { # Split the link into fields and values @@ -119,7 +119,7 @@ global $wgLoadBalancer; $fname = 'fetchFromLocation'; wfProfileIn( $fname ); - + $p = strpos( $location, ':' ); if ( $p === false ) { wfProfileOut( $fname ); @@ -132,7 +132,7 @@ case 'mysql': # MySQL locations are specified by mysql://<machineID>/<dbname>/<tblname>/<index> # Machine ID 0 is the current connection - if ( preg_match( '/^mysql://(\d+)/([A-Za-z_]+)/([A-Za-z_]+)/([A-Za-z_]+)$/', + if ( preg_match( '/^mysql://(\d+)/([A-Za-z_]+)/([A-Za-z_]+)/([A-Za-z_]+)$/', $location, $matches ) ) { $machineID = $matches[1]; $dbName = $matches[2]; @@ -144,11 +144,11 @@ } 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'], + $db = Database::newFromParams( $params['server'], $params['user'], $params['password'], $dbName, 1, false, true, true ); } } @@ -244,15 +244,15 @@ } } } - + # This function returns the text of a section, specified by a number ($section). - # A section is text under a heading like == Heading == or <h1>Heading</h1>, or + # A section is text under a heading like == Heading == or <h1>Heading</h1>, or # the first section before any such heading (section 0). # # If a section contains subsections, these are also returned. # function getSection($text,$section) { - + # strip NOWIKI etc. to avoid confusion (true-parameter causes HTML # comments to be stripped as well) $striparray=array(); @@ -273,23 +273,23 @@ $headline=$secs[$section*2-1]; preg_match( '/^(=+).*?=+|^<h([1-6]).*?' . '>.*?</h[1-6].*?' . '>/mi',$headline,$matches); $hlevel=$matches[1]; - + # translate wiki heading into level if(strpos($hlevel,'=')!==false) { - $hlevel=strlen($hlevel); + $hlevel=strlen($hlevel); } - + $rv=$headline. $secs[$section*2]; $count=$section+1; - + $break=false; while(!empty($secs[$count*2-1]) && !$break) { - + $subheadline=$secs[$count*2-1]; preg_match( '/^(=+).*?=+|^<h([1-6]).*?' . '>.*?</h[1-6].*?' . '>/mi',$subheadline,$matches); $subhlevel=$matches[1]; if(strpos($subhlevel,'=')!==false) { - $subhlevel=strlen($subhlevel); + $subhlevel=strlen($subhlevel); } if($subhlevel > $hlevel) { $rv.=$subheadline.$secs[$count*2]; @@ -298,7 +298,7 @@ $break=true; } $count++; - + } } # reinsert stripped tags @@ -308,7 +308,7 @@ return $rv;
} - +
# Load the revision (including cur_text) into this object function loadContent( $noredir = false ) @@ -321,21 +321,21 @@
if ( $this->mContentLoaded ) return; $fname = 'Article::loadContent'; - - # Pre-fill content with error message so that if something + + # 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 ); + $t = $this->mTitle->getPrefixedText(); + if ( isset( $oldid ) ) { + $oldid = IntVal( $oldid ); $t .= ",oldid={$oldid}"; - } - if ( isset( $redirect ) ) { - $redirect = ($redirect == 'no') ? 'no' : 'yes'; - $t .= ",redirect={$redirect}"; - } + } + if ( isset( $redirect ) ) { + $redirect = ($redirect == 'no') ? 'no' : 'yes'; + $t .= ",redirect={$redirect}"; + } $this->mContent = wfMsg( 'missingarticle', $t ); - + if ( ! $oldid ) { # Retrieve current version $id = $this->getID(); if ( 0 == $id ) return; @@ -345,8 +345,8 @@ "FROM cur WHERE cur_id={$id}"; wfDebug( "$sql\n" ); $res = wfQuery( $sql, DB_READ, $fname ); - if ( 0 == wfNumRows( $res ) ) { - return; + if ( 0 == wfNumRows( $res ) ) { + return; }
$s = wfFetchObject( $res ); @@ -361,7 +361,7 @@ # 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; @@ -375,7 +375,7 @@ $sql = 'SELECT cur_text,cur_timestamp,cur_user,cur_user_text,cur_comment,' . "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; @@ -434,9 +434,9 @@ return $this->mContent; } $this->mContent = false; - + $fname = 'Article::loadContent'; - + if ( ! $oldid ) { # Retrieve current version $id = $this->getID(); if ( 0 == $id ) { @@ -447,8 +447,8 @@ 'cur_text,cur_timestamp,cur_user,cur_counter,cur_restrictions,cur_touched ' . "FROM cur WHERE cur_id={$id}"; $res = wfQuery( $sql, DB_READ, $fname ); - if ( 0 == wfNumRows( $res ) ) { - return false; + if ( 0 == wfNumRows( $res ) ) { + return false; }
$s = wfFetchObject( $res ); @@ -464,7 +464,7 @@ $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; @@ -488,8 +488,8 @@ $sql = "SELECT old_text,old_timestamp,old_user,old_flags FROM $oldtable " . "WHERE old_id={$oldid}"; $res = wfQuery( $sql, DB_READ, $fname ); - if ( 0 == wfNumRows( $res ) ) { - return false; + if ( 0 == wfNumRows( $res ) ) { + return false; }
$s = wfFetchObject( $res ); @@ -526,7 +526,7 @@ function isCountable( $text ) { global $wgUseCommaCount, $wgMwRedir; - + if ( 0 != $this->mTitle->getNamespace() ) { return 0; } if ( $wgMwRedir->matchStart( $text ) ) { return 0; } $token = ($wgUseCommaCount ? ',' : '[[' ); @@ -592,7 +592,7 @@ $fname = 'Article::getContributors';
# XXX: this is expensive; cache this info somewhere. - + $title = $this->mTitle;
$contribs = array(); @@ -602,8 +602,8 @@ ' FROM old LEFT JOIN user ON old.old_user = user.user_id ' . ' WHERE old.old_namespace = ' . $title->getNamespace() . ' AND old.old_title = "' . $title->getDBkey() . '"' . - ' AND old.old_user != ' . $this->getUser() . - ' GROUP BY old.old_user ' . + ' AND old.old_user != ' . $this->getUser() . + ' GROUP BY old.old_user ' . ' ORDER BY timestamp DESC ';
if ($limit > 0) { @@ -611,16 +611,16 @@ }
$res = wfQuery($sql, DB_READ, $fname); - + while ( $line = wfFetchObject( $res ) ) { $contribs[] = array($line->old_user, $line->old_user_text, $line->user_real_name); } - + wfFreeResult($res); - + return $contribs; } - + # This is the default action of the script: just view the page of # the given title.
@@ -628,7 +628,7 @@ { global $wgUser, $wgOut, $wgLang, $wgRequest; global $wgLinkCache, $IP, $wgEnableParserCache; - + $fname = 'Article::view'; wfProfileIn( $fname );
@@ -660,14 +660,14 @@ return; } } - + # Should the parser cache be used? if ( $wgEnableParserCache && intval($wgUser->getOption( 'stubthreshold' )) == 0 && empty( $oldid ) ) { $pcache = true; } else { $pcache = false; } - + $outputDone = false; if ( $pcache ) { if ( $wgOut->tryParserCache( $this, $wgUser ) ) { @@ -677,14 +677,14 @@
if ( !$outputDone ) { $text = $this->getContent( false ); # May change mTitle by following a redirect - + # Another whitelist check in case oldid or redirects are altering the title if ( !$this->mTitle->userCanRead() ) { $wgOut->loginToUse(); $wgOut->output(); exit; } - +
# We're looking at an old revision
@@ -698,8 +698,8 @@ 'redirect=no' ); $s = wfMsg( 'redirectedfrom', $redir ); $wgOut->setSubtitle( $s ); - - # Can't cache redirects + + # Can't cache redirects $pcache = false; }
@@ -707,8 +707,8 @@
# wrap user css and user js in pre and don't parse # XXX: use $this->mTitle->usCssJsSubpage() when php is fixed/ a workaround is found - if ( - $this->mTitle->getNamespace() == Namespace::getUser() && + if ( + $this->mTitle->getNamespace() == Namespace::getUser() && preg_match('/\/[\w]+\.(css|js)$/', $this->mTitle->getDBkey()) ) { $wgOut->addWikiText( wfMsg('clearyourcache')); @@ -720,7 +720,7 @@ } } $wgOut->setPageTitle( $this->mTitle->getPrefixedText() ); - + # Add link titles as META keywords $wgOut->addMetaTags() ;
@@ -737,7 +737,7 @@ { global $wgOut, $wgUser, $wgLinkCache, $wgMwRedir; global $wgUseSquid, $wgDeferredUpdateList, $wgInternalServer, $wgIsPg, $wgIsMySQL; - + $fname = 'Article::insertNewArticle';
$this->mCountAdjustment = $this->isCountable( $text ); @@ -752,7 +752,7 @@ $won = wfInvertTimestamp( $now ); wfSeedRandom(); $rand = number_format( mt_rand() / mt_getrandmax(), 12, '.', '' ); - + if ($wgIsPg) { $cur_id_column="cur_id,"; $cur_id=wfGetSQL(""," nextval('cur_cur_id_seq')"); @@ -780,20 +780,20 @@
Article::onArticleCreate( $this->mTitle ); RecentChange::notifyNew( $now, $this->mTitle, $isminor, $wgUser, $summary ); - - if ($watchthis) { - if(!$this->mTitle->userIsWatching()) $this->watch(); + + if ($watchthis) { + if(!$this->mTitle->userIsWatching()) $this->watch(); } else { 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 ); - + # standard deferred updates $this->editUpdates( $text );
@@ -804,7 +804,7 @@ /* Side effects: loads last edit */ function getTextOfLastEditWithSectionReplacedOrAdded($section, $text, $summary = ''){ $this->loadLastEdit(); - $oldtext = $this->getContent( true ); + $oldtext = $this->getContent( true ); if ($section != '') { if($section=='new') { if($summary) $subject="== {$summary} ==\n\n"; @@ -825,10 +825,10 @@ $secs=preg_split('/(^=+.*?=+|^<h[1-6].*?' . '>.*?</h[1-6].*?' . '>)/mi', $oldtext,-1,PREG_SPLIT_DELIM_CAPTURE); $secs[$section*2]=$text."\n\n"; // replace with edited - + # section 0 is top (intro) section - if($section!=0) { - + if($section!=0) { + # headline of old section - we need to go through this section # to determine if there are any subsections that now need to # be erased, as the mother section has been replaced with @@ -836,23 +836,23 @@ $headline=$secs[$section*2-1]; preg_match( '/^(=+).*?=+|^<h([1-6]).*?' . '>.*?</h[1-6].*?' . '>/mi',$headline,$matches); $hlevel=$matches[1]; - + # determine headline level for wikimarkup headings if(strpos($hlevel,'=')!==false) { - $hlevel=strlen($hlevel); + $hlevel=strlen($hlevel); } - + $secs[$section*2-1]=''; // erase old headline $count=$section+1; $break=false; while(!empty($secs[$count*2-1]) && !$break) { - + $subheadline=$secs[$count*2-1]; preg_match( '/^(=+).*?=+|^<h([1-6]).*?' . '>.*?</h[1-6].*?' . '>/mi',$subheadline,$matches); $subhlevel=$matches[1]; if(strpos($subhlevel,'=')!==false) { - $subhlevel=strlen($subhlevel); + $subhlevel=strlen($subhlevel); } if($subhlevel > $hlevel) { // erase old subsections @@ -863,16 +863,16 @@ $break=true; } $count++; - + } - + } $text=join('',$secs); # reinsert the stuff that we stripped out earlier - $text=$parser->unstrip($text,$striparray); - $text=$parser->unstripNoWiki($text,$striparray); + $text=$parser->unstrip($text,$striparray); + $text=$parser->unstripNoWiki($text,$striparray); } - + } return $text; } @@ -886,7 +886,7 @@ $fname = 'Article::updateArticle';
if ( $this->mMinorEdit ) { $me1 = 1; } else { $me1 = 0; } - if ( $minor && $wgUser->getID() ) { $me2 = 1; } else { $me2 = 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 @@ -894,7 +894,7 @@ else { $redir = 0; }
$text = $this->preSaveTransform( $text ); - + # Update article, but only if changed.
if( $wgDBtransactions ) { @@ -918,7 +918,7 @@ "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; @@ -926,7 +926,7 @@
# This overwrites $oldtext if revision compression is on $flags = Article::compressRevisionText( $oldtext ); - + $oldtable=$wgIsPg?'"old"':'old'; if ($wgIsPg) { $oldtable='"old"'; @@ -956,7 +956,7 @@ $oldid = $wgIsPg?$old_id:wfInsertId( $res );
$bot = (int)($wgUser->isBot() || $forceBot); - RecentChange::notifyEdit( $now, $this->mTitle, $me2, $wgUser, $summary, + RecentChange::notifyEdit( $now, $this->mTitle, $me2, $wgUser, $summary, $oldid, $this->getTimestamp(), $bot ); Article::onArticleEdit( $this->mTitle ); } @@ -965,9 +965,9 @@ $sql = 'COMMIT'; wfQuery( $sql, DB_WRITE ); } - - if ($watchthis) { - if (!$this->mTitle->userIsWatching()) $this->watch(); + + if ($watchthis) { + if (!$this->mTitle->userIsWatching()) $this->watch(); } else { if ( $this->mTitle->userIsWatching() ) { $this->unwatch(); @@ -975,8 +975,8 @@ } # standard deferred updates $this->editUpdates( $text ); - - + + $urls = array(); # Template namespace # Purge all articles linking here @@ -989,7 +989,7 @@ } } } - + # Squid updates if ( $wgUseSquid ) { $urls = array_merge( $urls, $this->mTitle->getSquidURLs() ); @@ -1015,7 +1015,7 @@ $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 );
@@ -1092,17 +1092,17 @@
if ( $confirm ) {
- $sql = "UPDATE cur SET cur_touched='" . wfTimestampNow() . "'," . - "cur_restrictions='{$limit}' WHERE cur_id={$id}"; - wfQuery( $sql, DB_WRITE, 'Article::protect' ); - - $log = new LogPage( wfMsg( 'protectlogpage' ), wfMsg( 'protectlogtext' ) ); - if ( $limit === "" ) { - $log->addEntry( wfMsg( 'unprotectedarticle', $this->mTitle->getPrefixedText() ), $reason ); - } else { - $log->addEntry( wfMsg( 'protectedarticle', $this->mTitle->getPrefixedText() ), $reason ); - } - $wgOut->redirect( $this->mTitle->getFullURL() ); + $sql = "UPDATE cur SET cur_touched='" . wfTimestampNow() . "'," . + "cur_restrictions='{$limit}' WHERE cur_id={$id}"; + wfQuery( $sql, DB_WRITE, 'Article::protect' ); + + $log = new LogPage( wfMsg( 'protectlogpage' ), wfMsg( 'protectlogtext' ) ); + if ( $limit === "" ) { + $log->addEntry( wfMsg( 'unprotectedarticle', $this->mTitle->getPrefixedText() ), $reason ); + } else { + $log->addEntry( wfMsg( 'protectedarticle', $this->mTitle->getPrefixedText() ), $reason ); + } + $wgOut->redirect( $this->mTitle->getFullURL() ); return; } else { $reason = htmlspecialchars( wfMsg( 'protectreason' ) ); @@ -1124,12 +1124,14 @@ $protcom = '';
if ( $limit === '' ) { + $wgOut->setPageTitle( wfMsg( 'confirmunprotect' ) ); $wgOut->setSubtitle( wfMsg( 'unprotectsub', $sub ) ); $wgOut->addWikiText( wfMsg( 'confirmunprotecttext' ) ); $check = htmlspecialchars( wfMsg( 'confirmunprotect' ) ); $protcom = htmlspecialchars( wfMsg( 'unprotectcomment' ) ); $formaction = $this->mTitle->escapeLocalURL( 'action=unprotect' . $par ); } else { + $wgOut->setPageTitle( wfMsg( 'confirmprotect' ) ); $wgOut->setSubtitle( wfMsg( 'protectsub', $sub ) ); $wgOut->addWikiText( wfMsg( 'confirmprotecttext' ) ); $check = htmlspecialchars( wfMsg( 'confirmprotect' ) ); @@ -1178,16 +1180,16 @@ return $this->protect( '' ); }
- # UI entry point for page deletion + # UI entry point for page deletion function delete() { global $wgUser, $wgOut, $wgMessageCache, $wgRequest, $wgIsPg; $fname = 'Article::delete'; $confirm = $wgRequest->getBool( 'wpConfirm' ) && $wgRequest->wasPosted(); $reason = $wgRequest->getText( 'wpReason' ); - + # This code desperately needs to be totally rewritten - + # Check permissions if ( ( ! $wgUser->isSysop() ) ) { $wgOut->sysopRequired(); @@ -1241,19 +1243,19 @@ $text = Article::getRevisionText( $old ); $blanked = true; } - + } - - $length=strlen($text); - + + $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 && $reason === '') { + if($length == 0 && $reason === '') { $reason = wfMsg('exblank'); } - + if($length < 500 && $reason === '') { - + # comment field=255, let's grep the first 150 to have some user # space left $text=substr($text,0,150); @@ -1268,27 +1270,27 @@ $reason=wfMsg('exbeforeblank') . " '".$text; } if($length>150) { $reason .= '...'; } # we've only pasted part of the text - $reason.="'"; + $reason.="'"; } }
return $this->confirmDelete( '', $reason ); } - + # Output deletion confirmation dialog function confirmDelete( $par, $reason ) { global $wgOut;
wfDebug( "Article::confirmDelete\n" ); - + $sub = htmlspecialchars( $this->mTitle->getPrefixedText() ); $wgOut->setSubtitle( wfMsg( 'deletesub', $sub ) ); $wgOut->setRobotpolicy( 'noindex,nofollow' ); $wgOut->addWikiText( wfMsg( 'confirmdeletetext' ) );
$formaction = $this->mTitle->escapeLocalURL( 'action=delete' . $par ); - + $confirm = htmlspecialchars( wfMsg( 'confirm' ) ); $check = htmlspecialchars( wfMsg( 'confirmcheck' ) ); $delcom = htmlspecialchars( wfMsg( 'deletecomment' ) ); @@ -1334,7 +1336,7 @@ $fname = 'Article::doDelete'; wfDebug( "$fname\n" );
- if ( $this->doDeleteArticle( $reason ) ) { + if ( $this->doDeleteArticle( $reason ) ) { $deleted = $this->mTitle->getPrefixedText();
$wgOut->setPagetitle( wfMsg( 'actioncomplete' ) ); @@ -1375,14 +1377,14 @@
$u = new SiteStatsUpdate( 0, 1, -$this->isCountable( $this->getContent( true ) ) ); array_push( $wgDeferredUpdateList, $u ); - + $linksTo = $this->mTitle->getLinksTo();
# Squid purging if ( $wgUseSquid ) { - $urls = array( + $urls = array( $this->mTitle->getInternalURL(), - $this->mTitle->getInternalURL( 'history' ) + $this->mTitle->getInternalURL( 'history' ) ); foreach ( $linksTo as $linkTo ) { $urls[] = $linkTo->getInternalURL(); @@ -1420,7 +1422,7 @@ $sql = "DELETE FROM old WHERE old_namespace={$ns} AND " . "old_title='{$t}'"; wfQuery( $sql, DB_WRITE, $fname ); - + $sql = "DELETE FROM recentchanges WHERE rc_namespace={$ns} AND " . "rc_title='{$t}'"; wfQuery( $sql, DB_WRITE, $fname ); @@ -1429,7 +1431,7 @@ $t = wfStrencode( $this->mTitle->getPrefixedDBkey() );
Article::onArticleDelete( $this->mTitle ); - + $sql = 'INSERT INTO brokenlinks (bl_from,bl_to) VALUES '; $first = true;
@@ -1437,7 +1439,7 @@ if ( ! $first ) { $sql .= ','; } $first = false; # Get article ID. Efficient because it was loaded into the cache by getLinksTo(). - $linkID = $titleObj->getArticleID(); + $linkID = $titleObj->getArticleID(); $sql .= "({$linkID},'{$t}')"; } if ( ! $first ) { @@ -1455,10 +1457,10 @@
$sql = "DELETE FROM brokenlinks WHERE bl_from={$id}"; wfQuery( $sql, DB_WRITE, $fname ); - + $sql = "DELETE FROM categorylinks WHERE cl_from={$id}"; wfQuery( $sql, DB_WRITE, $fname ); - + $log = new LogPage( wfMsg( 'dellogpage' ), wfMsg( 'dellogpagetext' ) ); $art = $this->mTitle->getPrefixedText(); $log->addEntry( wfMsg( 'deletedarticle', $art ), $reason ); @@ -1481,14 +1483,14 @@ $wgOut->readOnlyPage( $this->getContent( true ) ); return; } - + # Enhanced rollback, marks edits rc_bot=1 $bot = $wgRequest->getBool( 'bot' ); - + # Replace all this user's current edits with the next one down $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}"; @@ -1502,7 +1504,7 @@ $ut = wfStrencode( $s->cur_user_text ); $uid = $s->cur_user; $pid = $s->cur_id; - + $from = str_replace( '_', ' ', $wgRequest->getVal( 'from' ) ); if( $from != $s->cur_user_text ) { $wgOut->setPageTitle(wfmsg('rollbackfailed')); @@ -1517,7 +1519,7 @@ } return; } - + # Get the last edit not by this guy
$use_index=$wgIsMySQL?"USE INDEX (name_title_timestamp)":""; @@ -1535,7 +1537,7 @@ return; } $s = wfFetchObject( $res ); - + if ( $bot ) { # Mark all reverted edits as bot $sql = 'UPDATE recentchanges SET rc_bot=1 WHERE ' . @@ -1552,7 +1554,7 @@ Article::onArticleEdit( $this->mTitle ); $wgOut->returnToMain( false ); } - +
# Do standard deferred updates after page view
@@ -1589,7 +1591,7 @@ $id = $this->getID(); $title = $this->mTitle->getPrefixedDBkey(); $shortTitle = $this->mTitle->getDBkey(); - + $adj = $this->mCountAdjustment;
if ( 0 != $id ) { @@ -1626,7 +1628,7 @@ global $wgParser, $wgUser; return $wgParser->preSaveTransform( $text, $this->mTitle, $wgUser, ParserOptions::newFromUser( $wgUser ) ); } - + /* Caching functions */
# checkLastModified returns true if it has taken care of all @@ -1653,7 +1655,7 @@ $cache->loadFromFileCache(); return true; } else { - wfDebug( " tryFileCache() - starting buffer\n" ); + wfDebug( " tryFileCache() - starting buffer\n" ); ob_start( array(&$cache, 'saveToFileCache' ) ); } } else { @@ -1664,7 +1666,7 @@ function isFileCacheable() { global $wgUser, $wgUseFileCache, $wgShowIPinHeader, $wgRequest; extract( $wgRequest->getValues( 'action', 'oldid', 'diff', 'redirect', 'printable' ) ); - + return $wgUseFileCache and (!$wgShowIPinHeader) and ($this->getID() != 0) @@ -1678,7 +1680,7 @@ and (!isset($printable)) and (!$this->mRedirectedFrom); } - + # Loads cur_touched and returns a value indicating if it should be used function checkTouched() { $id = $this->getID(); @@ -1702,14 +1704,14 @@ $dbkey = $this->mTitle->getDBkey(); $encDbKey = wfStrencode( $dbkey ); $timestamp = wfTimestampNow(); - + # Save to history $oldtable=$wgIsPg?'"old"':'old'; $sql = "INSERT INTO $oldtable (old_namespace,old_title,old_text,old_comment,old_user,old_user_text,old_timestamp,inverse_timestamp) SELECT cur_namespace,cur_title,cur_text,cur_comment,cur_user,cur_user_text,cur_timestamp,99999999999999-cur_timestamp FROM cur WHERE cur_namespace=$ns AND cur_title='$encDbKey'"; wfQuery( $sql, DB_WRITE ); - + # Use the affected row count to determine if the article is new $numRows = wfAffectedRows();
@@ -1746,14 +1748,14 @@ $id = intval( $id ); global $wgHitcounterUpdateFreq;
- if( $wgHitcounterUpdateFreq <= 1 ){ // + 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 ); + $oldignore = wfIgnoreSQLErrors( true );
wfQuery("INSERT INTO hitcounter (hc_id) VALUES ({$id})", DB_WRITE);
@@ -1767,7 +1769,7 @@ $res = wfQuery('SELECT COUNT(*) as n FROM hitcounter', DB_WRITE); $row = wfFetchObject( $res ); $rown = intval( $row->n ); - if( $rown >= $wgHitcounterUpdateFreq ){ + if( $rown >= $wgHitcounterUpdateFreq ){ wfProfileIn( 'Article::incViewCount-collect' ); $old_user_abort = ignore_user_abort( true );
@@ -1788,18 +1790,18 @@ }
# The onArticle*() functions are supposed to be a kind of hooks - # which should be called whenever any of the specified actions - # are done. + # 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. + # This is a good place to put code to clear caches, for instance.
- # This is called on page move and undelete, as well as edit + # This is called on page move and undelete, as well as edit /* static */ function onArticleCreate($title_obj){ global $wgUseSquid, $wgDeferredUpdateList;
$titles = $title_obj->getBrokenLinksTo(); - - # Purge squid + + # Purge squid if ( $wgUseSquid ) { $urls = $title_obj->getSquidURLs(); foreach ( $titles as $linkTitle ) { @@ -1820,6 +1822,76 @@ /* static */ function onArticleEdit($title_obj){ LinkCache::linksccClearPage( $title_obj->getArticleID() ); } + + # Info about this page + + function info() + { + global $wgUser, $wgTitle, $wgOut, $wgLang; + + $basenamespace = $wgTitle->getNamespace() & (~1); + $cur_clause = "cur_title='".$wgTitle->getDBkey()."' AND cur_namespace=".$basenamespace; + $old_clause = "old_title='".$wgTitle->getDBkey()."' AND old_namespace=".$basenamespace; + $wl_clause = "wl_title='".$wgTitle->getDBkey()."' AND wl_namespace=".$basenamespace; + $fullTitle = $wgTitle->makeName($basenamespace, $wgTitle->getDBKey()); + $wgOut->setPagetitle( $fullTitle ); + $wgOut->setSubtitle( wfMsg( "infosubtitle" )); + + # first, see if the page exists at all. + $sql = "SELECT COUNT(*) FROM cur WHERE ".$cur_clause; + $exists = wfSingleQuery( $sql , DB_READ ); + if ($exists < 1) { + $wgOut->addHTML( wfMsg("noarticletext") ); + } else { + $sql = "SELECT COUNT(*) FROM watchlist WHERE ".$wl_clause; + $wgOut->addHTML( "<ul><li>" . wfMsg("numwatchers") . wfSingleQuery( $sql, DB_READ ) . "</li>" ); + $sql = "SELECT COUNT(*) FROM old WHERE ".$old_clause; + $old = wfSingleQuery( $sql, DB_READ ); + $wgOut->addHTML( "<li>" . wfMsg("numedits") . ($old + 1) . "</li>"); + + # 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 + $sql = "SELECT cur_user_text FROM cur WHERE ".$cur_clause; + $cur_author = wfSingleQuery( $sql, DB_READ ); + + # find number of 'old' authors excluding 'cur' author + $sql = "SELECT COUNT(DISTINCT old_user_text) FROM old WHERE ".$old_clause + ." AND old_user_text<>'" . $cur_author . "'"; + $authors = wfSingleQuery( $sql, DB_READ ) + 1; + + # now for the Talk page ... + $cur_clause = "cur_title='".$wgTitle->getDBkey()."' AND cur_namespace=".($basenamespace+1); + $old_clause = "old_title='".$wgTitle->getDBkey()."' AND old_namespace=".($basenamespace+1); + + # does it exist? + $sql = "SELECT COUNT(*) FROM cur WHERE ".$cur_clause; + $exists = wfSingleQuery( $sql , DB_READ ); + + # number of edits + if ($exists > 0) { + $sql = "SELECT COUNT(*) FROM old WHERE ".$old_clause; + $old = wfSingleQuery( $sql, DB_READ ); + $wgOut->addHTML( "<li>" . wfMsg("numtalkedits") . ($old + 1) . "</li>"); + } + $wgOut->addHTML( "<li>" . wfMsg("numauthors") . $authors . "</li>" ); + + # number of authors + if ($exists > 0) { + $sql = "SELECT cur_user_text FROM cur WHERE ".$cur_clause; + $cur_author = wfSingleQuery( $sql, DB_READ ); + + $sql = "SELECT COUNT(DISTINCT old_user_text) FROM old WHERE " + .$old_clause." AND old_user_text<>'" . $cur_author . "'"; + $authors = wfSingleQuery( $sql, DB_READ ) + 1; + + $wgOut->addHTML( "<li>" . wfMsg("numtalkauthors") . $authors . "</li></ul>" ); + } + } + } }
?> Index: includes/DatabaseFunctions.php =================================================================== RCS file: /cvsroot/wikipedia/phase3/includes/DatabaseFunctions.php,v retrieving revision 1.36 diff -u -r1.36 DatabaseFunctions.php --- includes/DatabaseFunctions.php 22 Jun 2004 08:54:26 -0000 1.36 +++ includes/DatabaseFunctions.php 8 Jul 2004 14:33:17 -0000 @@ -39,6 +39,15 @@ } }
+function wfSingleQuery( $sql, $db, $fname = "" ) +{ + $res = wfQuery($sql, $db, $fname ); + $row = wfFetchRow( $res ); + $ret = $row[0]; + wfFreeResult( $res ); + return $ret; +} + function &wfGetDB( $db = DB_LAST ) { global $wgLoadBalancer; Index: includes/OutputPage.php =================================================================== RCS file: /cvsroot/wikipedia/phase3/includes/OutputPage.php,v retrieving revision 1.152 diff -u -r1.152 OutputPage.php --- includes/OutputPage.php 19 Jun 2004 06:46:54 -0000 1.152 +++ includes/OutputPage.php 8 Jul 2004 14:33:17 -0000 @@ -14,19 +14,19 @@ var $mSuppressQuickbar; var $mOnloadHandler; var $mDoNothing; - var $mContainsOldMagic, $mContainsNewMagic; + var $mContainsOldMagic, $mContainsNewMagic; var $mIsArticleRelated; var $mParserOptions; var $mShowFeedLinks = false; var $mEnableClientCache = true; - + function OutputPage() { $this->mHeaders = $this->mCookies = $this->mMetatags = $this->mKeywords = $this->mLinktags = array(); $this->mHTMLtitle = $this->mPagetitle = $this->mBodytext = $this->mRedirect = $this->mLastModified = - $this->mSubtitle = $this->mDebugtext = $this->mRobotpolicy = + $this->mSubtitle = $this->mDebugtext = $this->mRobotpolicy = $this->mOnloadHandler = ""; $this->mIsArticleRelated = $this->mIsarticle = $this->mPrintable = true; $this->mSuppressQuickbar = $this->mPrintable = false; @@ -48,7 +48,7 @@ function addKeyword( $text ) { array_push( $this->mKeywords, $text ); } function addScript( $script ) { $this->mScripts .= $script; } function getScript() { return $this->mScripts; } - + function addLink( $linkarr ) { # $linkarr should be an associative array of attributes. We'll escape on output. array_push( $this->mLinktags, $linkarr ); @@ -65,7 +65,7 @@ # checkLastModified tells the client to use the client-cached page if # possible. If sucessful, the OutputPage is disabled so that # any future call to OutputPage->output() have no effect. The method - # returns true iff cache-ok headers was sent. + # returns true iff cache-ok headers was sent. function checkLastModified ( $timestamp ) { global $wgLang, $wgCachePages, $wgUser; @@ -92,7 +92,7 @@ $modsince = preg_replace( '/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"] ); $ismodsince = wfUnix2Timestamp( strtotime( $modsince ) ); wfDebug( "-- client send If-Modified-Since: " . $modsince . "\n", false ); - wfDebug( "-- we might send Last-Modified : $lastmod\n", false ); + wfDebug( "-- we might send Last-Modified : $lastmod\n", false );
if( ($ismodsince >= $timestamp ) and $wgUser->validateCache( $ismodsince ) ) { # Make sure you're in a place you can leave when you call us! @@ -116,11 +116,11 @@ global $action; switch($action) { case 'edit': - return wfMsg('edit'); + return wfMsg('edit'); case 'history': return wfMsg('history_short'); case 'protect': - return wfMsg('unprotect'); + return wfMsg('protect'); case 'unprotect': return wfMsg('unprotect'); case 'delete': @@ -131,6 +131,8 @@ return wfMsg('unwatch'); case 'submit': return wfMsg('preview'); + case 'info': + return wfMsg('info_short'); default: return ''; } @@ -168,8 +170,8 @@ $this->mIsarticle = false; } } - function setArticleFlag( $v ) { - $this->mIsarticle = $v; + function setArticleFlag( $v ) { + $this->mIsarticle = $v; if ( $v ) { $this->mIsArticleRelated = $v; } @@ -179,7 +181,7 @@ { return $this->mIsArticleRelated; } - + function getLanguageLinks() { return $this->mLanguageLinks; } @@ -191,7 +193,7 @@ } function getCategoryLinks() { return $this->mCategoryLinks; - } + } function addCategoryLinks($newLinkArray) { $this->mCategoryLinks += $newLinkArray; } @@ -218,12 +220,12 @@ function addWikiText( $text, $linestart = true, $cacheArticle = NULL ) { global $wgParser, $wgParserCache, $wgUser, $wgTitle; - + $parserOutput = $wgParser->parse( $text, $wgTitle, $this->mParserOptions, $linestart ); if ( $cacheArticle ) { $wgParserCache->save( $parserOutput, $cacheArticle, $wgUser ); } - + $this->mLanguageLinks += $parserOutput->getLanguageLinks(); $this->mCategoryLinks += $parserOutput->getCategoryLinks(); $this->addHTML( $parserOutput->getText() ); @@ -241,24 +243,24 @@ return false; } } - + # Set the maximum cache time on the Squid in seconds function setSquidMaxage( $maxage ) { $this->mSquidMaxage = $maxage; } - + # Use enableClientCache(false) to force it to send nocache headers function enableClientCache( $state ) { return wfSetVar( $this->mEnableClientCache, $state ); } - + function sendCacheControl() { global $wgUseSquid, $wgUseESI; # FIXME: This header may cause trouble with some versions of Internet Explorer header( "Vary: Accept-Encoding, Cookie" ); if( $this->mEnableClientCache ) { - if( $wgUseSquid && ! isset( $_COOKIE[ini_get( "session.name") ] ) && - ! $this->isPrintable() && $this->mSquidMaxage != 0 ) + if( $wgUseSquid && ! isset( $_COOKIE[ini_get( "session.name") ] ) && + ! $this->isPrintable() && $this->mSquidMaxage != 0 ) { if ( $wgUseESI ) { # We'll purge the proxy cache explicitly, but require end user agents @@ -272,7 +274,7 @@ } else { # We'll purge the proxy cache for anons explicitly, but require end user agents # to revalidate against the proxy on each visit. - # IMPORTANT! The Squid needs to replace the Cache-Control header with + # IMPORTANT! The Squid needs to replace the Cache-Control header with # Cache-Control: s-maxage=0, must-revalidate, max-age=0 wfDebug( "** local proxy caching; {$this->mLastModified} **\n", false ); # start with a shorter timeout for initial testing @@ -297,7 +299,7 @@ header( "Pragma: no-cache" ); } } - + # Finally, all the text has been munged and accumulated into # the object, let's actually output it: # @@ -311,7 +313,7 @@ } $fname = "OutputPage::output"; wfProfileIn( $fname ); - + $sk = $wgUser->getSkin();
if ( "" != $this->mRedirect ) { @@ -326,9 +328,9 @@ } $this->mLastModified = gmdate( "D, j M Y H:i:s" ) . " GMT"; } - + $this->sendCacheControl(); - + if( $wgDebugRedirects ) { $url = htmlspecialchars( $this->mRedirect ); print "<html>\n<head>\n<title>Redirect</title>\n</head>\n<body>\n"; @@ -339,10 +341,10 @@ } return; } - - + + $this->sendCacheControl(); - + header( "Content-type: $wgMimeType; charset={$wgOutputEncoding}" ); header( "Content-language: {$wgLanguageCode}" );
@@ -350,7 +352,7 @@ foreach( $this->mCookies as $name => $val ) { setcookie( $name, $val, $exp, "/" ); } - + $sk->outputPage( $this ); # flush(); } @@ -373,7 +375,7 @@ global $wgUser, $wgLang;
$wgInputEncoding = strtolower( $wgInputEncoding ); - + if( $wgUser->getOption( 'altencoding' ) ) { $wgLang->setAltEncoding(); return; @@ -383,11 +385,11 @@ $wgOutputEncoding = strtolower( $wgOutputEncoding ); return; } - + /* # This code is unused anyway! # Commenting out. --bv 2003-11-15 - + $a = explode( ",", $_SERVER['HTTP_ACCEPT_CHARSET'] ); $best = 0.0; $bestset = "*"; @@ -415,7 +417,7 @@ $wgOutputEncoding = $wgInputEncoding; }
- # Returns a HTML comment with the elapsed time since request. + # Returns a HTML comment with the elapsed time since request. # This method has no side effects. function reportTime() { @@ -425,7 +427,7 @@ list( $usec, $sec ) = explode( " ", $wgRequestTime ); $start = (float)$sec + (float)$usec; $elapsed = $now - $start; - + # Use real server name if available, so we know which machine # in a server farm generated the current page. if ( function_exists( "posix_uname" ) ) { @@ -477,7 +479,7 @@ $this->mBodytext = "";
$sk = $wgUser->getSkin(); - $ap = $sk->makeKnownLink( wfMsg( "administrators" ), "" ); + $ap = $sk->makeKnownLink( wfMsg( "administrators" ), "" ); $this->addHTML( wfMsg( "sysoptext", $ap ) ); $this->returnToMain(); } @@ -493,7 +495,7 @@ $this->mBodytext = "";
$sk = $wgUser->getSkin(); - $ap = $sk->makeKnownLink( wfMsg( "administrators" ), "" ); + $ap = $sk->makeKnownLink( wfMsg( "administrators" ), "" ); $this->addHTML( wfMsg( "developertext", $ap ) ); $this->returnToMain(); } @@ -511,9 +513,9 @@
# We put a comment in the .html file so a Sysop can diagnose the page the # user can't see. - $this->addHTML( "\n<!--" . - $wgLang->getNsText( $wgTitle->getNamespace() ) . - ":" . + $this->addHTML( "\n<!--" . + $wgLang->getNsText( $wgTitle->getNamespace() ) . + ":" . $wgTitle->getDBkey() . "-->" ); $this->returnToMain(); # Flip back to the main page after 10 seconds. } @@ -537,7 +539,7 @@ $msg = str_replace( "$2", htmlspecialchars( $fname ), $msg ); $msg = str_replace( "$3", $errno, $msg ); $msg = str_replace( "$4", htmlspecialchars( $error ), $msg ); - + if ( $wgCommandLineMode || !is_object( $wgUser )) { print "$msg\n"; wfAbruptExit(); @@ -566,7 +568,7 @@ $reason = file_get_contents( $wgReadOnlyFile ); $this->addWikiText( wfMsg( "readonlytext", $reason ) ); } - + if( is_string( $source ) ) { if( strcmp( $source, "" ) == 0 ) { $source = wfMsg( "noarticletext" ); @@ -577,7 +579,7 @@ htmlspecialchars( $source ) . "\n</textarea>"; $this->addHTML( $text ); } - + $this->returnToMain( false ); }
@@ -624,7 +626,7 @@ function returnToMain( $auto = true, $returnto = NULL ) { global $wgUser, $wgOut, $wgRequest; - + if ( $returnto == NULL ) { $returnto = $wgRequest->getText( 'returnto' ); } @@ -658,7 +660,7 @@ "/[_]/" => ' ' ); $a = htmlspecialchars(preg_replace(array_keys($strip), array_values($strip),$a )); - + $wgOut->addMeta ( "KEYWORDS" , $a ) ; }
@@ -673,7 +675,7 @@ } else { $ret = ""; } - + $ret .= "<!DOCTYPE html PUBLIC \"$wgDocType\"\n \"$wgDTD\">\n";
if ( "" == $this->mHTMLtitle ) { @@ -688,7 +690,7 @@ $ret .= "<html $xmlbits lang="$wgLanguageCode" $rtl>\n"; $ret .= "<head>\n<title>" . htmlspecialchars( $this->mHTMLtitle ) . "</title>\n"; array_push( $this->mMetatags, array( "http:Content-type", "$wgMimeType; charset={$wgOutputEncoding}" ) ); - + $ret .= $this->getHeadLinks(); global $wgStylePath; if( $this->isPrintable() ) { @@ -707,7 +709,7 @@ $ret .= "</head>\n"; return $ret; } - + function getHeadLinks() { global $wgRequest, $wgStylePath; $ret = ""; Index: languages/Language.php =================================================================== RCS file: /cvsroot/wikipedia/phase3/languages/Language.php,v retrieving revision 1.257 diff -u -r1.257 Language.php --- languages/Language.php 5 Jul 2004 21:26:35 -0000 1.257 +++ languages/Language.php 8 Jul 2004 14:33:17 -0000 @@ -66,7 +66,7 @@ 'davinci' => 'DaVinci', 'mono' => 'Mono', 'monobook' => 'MonoBook', - 'myskin' => 'MySkin' + 'myskin' => 'MySkin' );
define( "MW_MATH_PNG", 0 ); @@ -145,10 +145,10 @@ 'Sep', 'Oct', 'Nov', 'Dec' );
-# Note to translators: -# Please include the English words as synonyms. This allows people +# Note to translators: +# Please include the English words as synonyms. This allows people # from other wikis to contribute more easily. -# +# /* private */ $wgMagicWordsEn = array( # ID CASE SYNONYMS MAG_REDIRECT => array( 0, '#redirect' ), @@ -166,7 +166,7 @@ MAG_NUMBEROFARTICLES => array( 1, 'NUMBEROFARTICLES' ), MAG_CURRENTMONTHNAMEGEN => array( 1, 'CURRENTMONTHNAMEGEN' ), MAG_PAGENAME => array( 1, 'PAGENAME' ), - MAG_NAMESPACE => array( 1, 'NAMESPACE' ), + MAG_NAMESPACE => array( 1, 'NAMESPACE' ), MAG_MSG => array( 0, 'MSG:' ), MAG_SUBST => array( 0, 'SUBST:' ), MAG_MSGNW => array( 0, 'MSGNW:' ), @@ -260,6 +260,7 @@ 'go' => 'Go', "history" => 'Page history', 'history_short' => 'History', +'info_short' => 'Information', 'printableversion' => 'Printable version', 'edit' => 'Edit', 'editthispage' => 'Edit this page', @@ -441,10 +442,10 @@ 'youremail' => 'Your email*', 'yourrealname' => 'Your real name*', 'yournick' => 'Your nickname (for signatures)', -'emailforlost' => "Fields marked with a star (*) are optional. Storing an email address enables people to contact you through the website without you having to reveal your +'emailforlost' => "Fields marked with a star (*) are optional. Storing an email address enables people to contact you through the website without you having to reveal your email address to them, and it can be used to send you a new password if you forget it.<br /><br />Your real name, if you choose to provide it, will be used for giving you attribution for your work.", 'prefs-help-userdata' => '* <strong>Real name</strong> (optional): if you choose to provide it this will be used for giving you attribution for your work.<br/> -* <strong>Email</strong> (optional): Enables people to contact you through the website without you having to reveal your +* <strong>Email</strong> (optional): Enables people to contact you through the website without you having to reveal your email address to them, and it can be used to send you a new password if you forget it.', 'loginerror' => 'Login error', 'nocookiesnew' => "The user account was created, but you are not logged in. {{SITENAME}} uses cookies to log in users. You have cookies disabled. Please enable them, then log in with your new username and password.", @@ -525,7 +526,7 @@ 'newarticle' => '(New)', 'newarticletext' => "You've followed a link to a page that doesn't exist yet. -To create the page, start typing in the box below +To create the page, start typing in the box below (see the [[{{ns:4}}:Help|help page]] for more info). If you are here by mistake, just click your browser's '''back''' button.", 'talkpagetext' => '<!-- MediaWiki:talkpagetext -->', @@ -674,8 +675,8 @@
See [[{{ns:4}}:User preferences help]] for help deciphering the options.", 'prefsreset' => 'Preferences have been reset from storage.', -'qbsettings' => 'Quickbar settings', -'qbsettingsnote' => 'This preference only works in the 'Standard' and the 'CologneBlue' skin.', +'qbsettings' => 'Quickbar settings', +'qbsettingsnote' => 'This preference only works in the 'Standard' and the 'CologneBlue' skin.', 'changepassword' => 'Change password', 'skin' => 'Skin', 'math' => 'Rendering math', @@ -816,7 +817,7 @@ 'savefile' => 'Save file', 'uploadedimage' => "uploaded "$1"", 'uploaddisabled' => 'Sorry, uploading is disabled.', - + # Image list # 'imagelist' => 'Image list', @@ -894,7 +895,7 @@ 'randompage' => 'Random page', 'shortpages' => 'Short pages', 'longpages' => 'Long pages', -'deadendpages' => 'Dead-end pages', +'deadendpages' => 'Dead-end pages', 'listusers' => 'User list', 'listadmins' => 'Admins list', 'specialpages' => 'Special pages', @@ -939,7 +940,7 @@ as the "From" address of the mail, so the recipient will be able to reply.', 'usermailererror' => 'Mail object returned error: ', -'defemailsubject' => "{{SITENAME}} e-mail", +'defemailsubject' => "{{SITENAME}} e-mail", 'noemailtitle' => 'No e-mail address', 'noemailtext' => 'This user has not specified a valid e-mail address, or has chosen not to receive e-mail from other users.', @@ -1030,11 +1031,11 @@ 'rollbackfailed' => 'Rollback failed', 'cantrollback' => 'Cannot revert edit; last contributor is only author of this page.', 'alreadyrolled' => "Cannot rollback last edit of [[$1]] -by [[User:$2|$2]] ([[User talk:$2|Talk]]); someone else has edited or rolled back the page already. +by [[User:$2|$2]] ([[User talk:$2|Talk]]); someone else has edited or rolled back the page already.
Last edit was by [[User:$3|$3]] ([[User talk:$3|Talk]]). ", # only shown if there is an edit comment -'editcomment' => "The edit comment was: "<i>$1</i>".", +'editcomment' => "The edit comment was: "<i>$1</i>".", 'revertpage' => "Reverted edit of $2, changed back to last version by $1", 'protectlogpage' => 'Protection_log', 'protectlogtext' => "Below is a list of page locks/unlocks. @@ -1120,7 +1121,7 @@ 'autoblocker' => "Autoblocked because you share an IP address with "$1". Reason "$2".", 'blocklogpage' => 'Block_log', 'blocklogentry' => 'blocked "$1" with an expiry time of $2', -'blocklogtext' => 'This is a log of user blocking and unblocking actions. Automatically +'blocklogtext' => 'This is a log of user blocking and unblocking actions. Automatically blocked IP addresses are not be listed. See the [[Special:Ipblocklist|IP block list]] for the list of currently operational bans and blocks.', 'unblocklogentry' => 'unblocked "$1"', @@ -1171,7 +1172,7 @@
# Make sysop 'makesysoptitle' => 'Make a user into a sysop', -'makesysoptext' => 'This form is used by bureaucrats to turn ordinary users into administrators. +'makesysoptext' => 'This form is used by bureaucrats to turn ordinary users into administrators. Type the name of the user in the box and press the button to make the user an administrator', 'makesysopname' => 'Name of the user:', 'makesysopsubmit' => 'Make this user into a sysop', @@ -1292,7 +1293,7 @@ 'lastmodifiedby' => "This page was last modified $1 by $2.", 'and' => 'and', 'othercontribs' => "Based on work by $1.", -'others' => 'others', +'others' => 'others', 'siteusers' => "$wgSitename user(s) $1", 'creditspage' => 'Page credits', 'nocredits' => 'There is no credits info available for this page.', @@ -1300,65 +1301,73 @@ # Spam protection
'spamprotectiontitle' => 'Spam protection filter', -'spamprotectiontext' => 'The page you wanted to save was blocked by the spam filter. This is probably caused by a link to an external site. +'spamprotectiontext' => 'The page you wanted to save was blocked by the spam filter. This is probably caused by a link to an external site.
You might want to check the following regular expression for patterns that are currently blocked:', 'subcategorycount' => "There are $1 subcategories to this category.", 'categoryarticlecount' => "There are $1 articles in this category.", 'usenewcategorypage' => "1\n\nSet first character to "0" to disable the new category page layout.",
+# Info page +"infosubtitle" => "Information for page", +"numedits" => "Number of edits (article): ", +"numtalkedits" => "Number of edits (discussion page): ", +"numwatchers" => "Number of watchers: ", +"numauthors" => "Number of distinct authors (article): ", +"numtalkauthors" => "Number of distinct authors (discussion page): ", + # Monobook.js: tooltips and access keys for monobook 'Monobook.js' => '/* tooltips and access keys */ ta = new Object(); -ta['pt-userpage'] = new Array('.','My user page'); -ta['pt-anonuserpage'] = new Array('.','The user page for the ip you\'re editing as'); -ta['pt-mytalk'] = new Array('n','My talk page'); -ta['pt-anontalk'] = new Array('n','Discussion about edits from this ip address'); -ta['pt-preferences'] = new Array('','My preferences'); -ta['pt-watchlist'] = new Array('l','The list of pages you\'re monitoring for changes.'); -ta['pt-mycontris'] = new Array('y','List of my contributions'); -ta['pt-login'] = new Array('o','You are encouraged to log in, it is not mandatory however.'); -ta['pt-anonlogin'] = new Array('o','You are encouraged to log in, it is not mandatory however.'); -ta['pt-logout'] = new Array('o','Log out'); -ta['ca-talk'] = new Array('t','Discussion about the content page'); -ta['ca-edit'] = new Array('e','You can edit this page. Please use the preview button before saving.'); -ta['ca-addsection'] = new Array('+','Add a comment to this discussion.'); -ta['ca-viewsource'] = new Array('e','This page is protected. You can view its source.'); -ta['ca-history'] = new Array('h','Past versions of this page.'); -ta['ca-protect'] = new Array('=','Protect this page'); -ta['ca-delete'] = new Array('d','Delete this page'); -ta['ca-undelete'] = new Array('d','Restore the edits done to this page before it was deleted'); -ta['ca-move'] = new Array('m','Move this page'); -ta['ca-nomove'] = new Array('','You don\'t have the permissions to move this page'); -ta['ca-watch'] = new Array('w','Add this page to your watchlist'); -ta['ca-unwatch'] = new Array('w','Remove this page from your watchlist'); -ta['search'] = new Array('f','Search this wiki'); -ta['p-logo'] = new Array('','Main Page'); -ta['n-mainpage'] = new Array('z','Visit the Main Page'); -ta['n-portal'] = new Array('','About the project, what you can do, where to find things'); -ta['n-currentevents'] = new Array('','Find background information on current events'); -ta['n-recentchanges'] = new Array('r','The list of recent changes in the wiki.'); -ta['n-randompage'] = new Array('x','Load a random page'); -ta['n-help'] = new Array('','The place to find out.'); -ta['n-sitesupport'] = new Array('','Support us'); -ta['t-whatlinkshere'] = new Array('j','List of all wiki pages that link here'); -ta['t-recentchangeslinked'] = new Array('k','Recent changes in pages linking to this page'); -ta['feed-rss'] = new Array('','RSS feed for this page'); -ta['feed-atom'] = new Array('','Atom feed for this page'); -ta['t-contributions'] = new Array('','View the list of contributions of this user'); -ta['t-emailuser'] = new Array('','Send a mail to this user'); -ta['t-upload'] = new Array('u','Upload images or media files'); -ta['t-specialpages'] = new Array('q','List of all special pages'); -ta['ca-nstab-main'] = new Array('c','View the content page'); -ta['ca-nstab-user'] = new Array('c','View the user page'); -ta['ca-nstab-media'] = new Array('c','View the media page'); -ta['ca-nstab-special'] = new Array('','This is a special page, you can\'t edit the page itself.'); -ta['ca-nstab-wp'] = new Array('a','View the project page'); -ta['ca-nstab-image'] = new Array('c','View the image page'); -ta['ca-nstab-mediawiki'] = new Array('c','View the system message'); -ta['ca-nstab-template'] = new Array('c','View the template'); -ta['ca-nstab-help'] = new Array('c','View the help page'); -ta['ca-nstab-category'] = new Array('c','View the category page'); +ta['pt-userpage'] = new Array('.','My user page'); +ta['pt-anonuserpage'] = new Array('.','The user page for the ip you\'re editing as'); +ta['pt-mytalk'] = new Array('n','My talk page'); +ta['pt-anontalk'] = new Array('n','Discussion about edits from this ip address'); +ta['pt-preferences'] = new Array('','My preferences'); +ta['pt-watchlist'] = new Array('l','The list of pages you\'re monitoring for changes.'); +ta['pt-mycontris'] = new Array('y','List of my contributions'); +ta['pt-login'] = new Array('o','You are encouraged to log in, it is not mandatory however.'); +ta['pt-anonlogin'] = new Array('o','You are encouraged to log in, it is not mandatory however.'); +ta['pt-logout'] = new Array('o','Log out'); +ta['ca-talk'] = new Array('t','Discussion about the content page'); +ta['ca-edit'] = new Array('e','You can edit this page. Please use the preview button before saving.'); +ta['ca-addsection'] = new Array('+','Add a comment to this discussion.'); +ta['ca-viewsource'] = new Array('e','This page is protected. You can view its source.'); +ta['ca-history'] = new Array('h','Past versions of this page.'); +ta['ca-protect'] = new Array('=','Protect this page'); +ta['ca-delete'] = new Array('d','Delete this page'); +ta['ca-undelete'] = new Array('d','Restore the edits done to this page before it was deleted'); +ta['ca-move'] = new Array('m','Move this page'); +ta['ca-nomove'] = new Array('','You don\'t have the permissions to move this page'); +ta['ca-watch'] = new Array('w','Add this page to your watchlist'); +ta['ca-unwatch'] = new Array('w','Remove this page from your watchlist'); +ta['search'] = new Array('f','Search this wiki'); +ta['p-logo'] = new Array('','Main Page'); +ta['n-mainpage'] = new Array('z','Visit the Main Page'); +ta['n-portal'] = new Array('','About the project, what you can do, where to find things'); +ta['n-currentevents'] = new Array('','Find background information on current events'); +ta['n-recentchanges'] = new Array('r','The list of recent changes in the wiki.'); +ta['n-randompage'] = new Array('x','Load a random page'); +ta['n-help'] = new Array('','The place to find out.'); +ta['n-sitesupport'] = new Array('','Support us'); +ta['t-whatlinkshere'] = new Array('j','List of all wiki pages that link here'); +ta['t-recentchangeslinked'] = new Array('k','Recent changes in pages linking to this page'); +ta['feed-rss'] = new Array('','RSS feed for this page'); +ta['feed-atom'] = new Array('','Atom feed for this page'); +ta['t-contributions'] = new Array('','View the list of contributions of this user'); +ta['t-emailuser'] = new Array('','Send a mail to this user'); +ta['t-upload'] = new Array('u','Upload images or media files'); +ta['t-specialpages'] = new Array('q','List of all special pages'); +ta['ca-nstab-main'] = new Array('c','View the content page'); +ta['ca-nstab-user'] = new Array('c','View the user page'); +ta['ca-nstab-media'] = new Array('c','View the media page'); +ta['ca-nstab-special'] = new Array('','This is a special page, you can\'t edit the page itself.'); +ta['ca-nstab-wp'] = new Array('a','View the project page'); +ta['ca-nstab-image'] = new Array('c','View the image page'); +ta['ca-nstab-mediawiki'] = new Array('c','View the system message'); +ta['ca-nstab-template'] = new Array('c','View the template'); +ta['ca-nstab-help'] = new Array('c','View the help page'); +ta['ca-nstab-category'] = new Array('c','View the category page'); '
@@ -1392,7 +1401,7 @@ global $wgDefaultUserOptionsEn ; return $wgDefaultUserOptionsEn ; } - + function getBookstoreList () { global $wgBookstoreListEn ; return $wgBookstoreListEn ; @@ -1435,7 +1444,7 @@ global $wgMathNamesEn; return $wgMathNamesEn; } - + function getDateFormats() { global $wgDateFormatsEn; return $wgDateFormatsEn; @@ -1445,7 +1454,7 @@ global $wgUserTogglesEn; return $wgUserTogglesEn; } - + function getUserToggle( $tog ) { $togs =& $this->getUserToggles(); return $togs[$tog]; @@ -1469,7 +1478,7 @@ global $wgMonthNamesEn; return $wgMonthNamesEn[$key-1]; } - + /* by default we just return base form */ function getMonthNameGen( $key ) { @@ -1491,11 +1500,11 @@ function userAdjust( $ts ) { global $wgUser, $wgLocalTZoffset; - + $tz = $wgUser->getOption( 'timecorrection' ); if ( $tz === '' ) { $hrDiff = isset( $wgLocalTZoffset ) ? $wgLocalTZoffset : 0; - $minDiff = 0; + $minDiff = 0; } elseif ( strpos( $tz, ":" ) !== false ) { $tzArray = explode( ":", $tz ); $hrDiff = intval($tzArray[0]); @@ -1505,7 +1514,7 @@ } if ( 0 == $hrDiff && 0 == $minDiff ) { return $ts; }
- $t = mktime( ( + $t = mktime( ( (int)substr( $ts, 8, 2) ) + $hrDiff, # Hours (int)substr( $ts, 10, 2 ) + $minDiff, # Minutes (int)substr( $ts, 12, 2 ), # Seconds @@ -1514,26 +1523,26 @@ (int)substr( $ts, 0, 4 ) ); #Year return date( 'YmdHis', $t ); } - + function date( $ts, $adj = false ) { global $wgAmericanDates, $wgUser, $wgUseDynamicDates;
if ( $adj ) { $ts = $this->userAdjust( $ts ); } - + if ( $wgUseDynamicDates ) { - $datePreference = $wgUser->getOption( 'date' ); + $datePreference = $wgUser->getOption( 'date' ); if ( $datePreference == 0 ) { $datePreference = $wgAmericanDates ? 1 : 2; } } else { $datePreference = $wgAmericanDates ? 1 : 2; } - + $month = $this->getMonthAbbreviation( substr( $ts, 4, 2 ) ); $day = $this->formatNum( 0 + substr( $ts, 6, 2 ) ); $year = $this->formatNum( substr( $ts, 0, 4 ) ); - + switch( $datePreference ) { case 1: return "$month $day, $year"; case 2: return "$day $month $year"; @@ -1546,7 +1555,7 @@ if ( $adj ) { $ts = $this->userAdjust( $ts ); }
$t = substr( $ts, 8, 2 ) . ':' . substr( $ts, 10, 2 ); - if ( $seconds ) { + if ( $seconds ) { $t .= ':' . substr( $ts, 12, 2 ); } return $this->formatNum( $t ); @@ -1585,7 +1594,7 @@ global $wgAllMessagesEn; return @$wgAllMessagesEn[$key]; } - + function getAllMessages() { global $wgAllMessagesEn; @@ -1596,19 +1605,19 @@ # For most languages, this is a wrapper for iconv return iconv( $in, $out, $string ); } - + function ucfirst( $string ) { # For most languages, this is a wrapper for ucfirst() return ucfirst( $string ); } - + function lcfirst( $s ) { return strtolower( $s{0} ). substr( $s, 1 ); }
function checkTitleEncoding( $s ) { global $wgInputEncoding; - + # Check for UTF-8 URLs; Internet Explorer produces these if you # type non-ASCII chars in the URL bar or follow unescaped links. $ishigh = preg_match( '/[\x80-\xff]/', $s); @@ -1617,15 +1626,15 @@
if( ($wgInputEncoding != 'utf-8') and $ishigh and $isutf ) return @iconv( 'UTF-8', $wgInputEncoding, $s ); - + if( ($wgInputEncoding == 'utf-8') and $ishigh and !$isutf ) return utf8_encode( $s ); - + # Other languages can safely leave this function, or replace # it with one to detect and convert another legacy encoding. return $s; } - + function stripForSearch( $in ) { # Some languages have special punctuation to strip out # or characters which need to be converted for MySQL's @@ -1635,7 +1644,7 @@
function firstChar( $s ) { # Get the first character of a string. In ASCII, return - # first byte of the string. UTF8 and others have to + # first byte of the string. UTF8 and others have to # overload this. return $s[0]; } @@ -1686,7 +1695,7 @@ function linkPrefixExtension() { return false; }
- function &getMagicWords() + function &getMagicWords() { global $wgMagicWordsEn; return $wgMagicWordsEn; @@ -1695,7 +1704,7 @@ # Fill a MagicWord object with data from here function getMagic( &$mw ) { - $raw =& $this->getMagicWords(); + $raw =& $this->getMagicWords(); if( !isset( $raw[$mw->mId] ) ) { # Fall back to English if local list is incomplete $raw =& Language::getMagicWords(); @@ -1711,7 +1720,7 @@ return '<em>'.$text.'</em>'; }
- + # Normally we use the plain ASCII digits. Some languages such as Arabic will # want to output numbers using script-appropriate characters: override this # function with a translator. See LanguageAr.php for an example.
Oops. I'm sorry about this huge patch that is mostly whitespace changes. Here is the patch with whitespace changes ignored.
Index: index.php =================================================================== RCS file: /cvsroot/wikipedia/phase3/index.php,v retrieving revision 1.35 diff -u -b -r1.35 index.php --- index.php 28 Jun 2004 17:46:49 -0000 1.35 +++ index.php 8 Jul 2004 14:41:04 -0000 @@ -108,6 +108,7 @@ case "rollback": case "protect": case "unprotect": + case "info": $wgArticle->$action(); break; case "print": Index: includes/Article.php =================================================================== RCS file: /cvsroot/wikipedia/phase3/includes/Article.php,v retrieving revision 1.175 diff -u -b -r1.175 Article.php --- includes/Article.php 28 Jun 2004 20:24:23 -0000 1.175 +++ includes/Article.php 8 Jul 2004 14:41:04 -0000 @@ -1124,12 +1124,14 @@ $protcom = '';
if ( $limit === '' ) { + $wgOut->setPageTitle( wfMsg( 'confirmunprotect' ) ); $wgOut->setSubtitle( wfMsg( 'unprotectsub', $sub ) ); $wgOut->addWikiText( wfMsg( 'confirmunprotecttext' ) ); $check = htmlspecialchars( wfMsg( 'confirmunprotect' ) ); $protcom = htmlspecialchars( wfMsg( 'unprotectcomment' ) ); $formaction = $this->mTitle->escapeLocalURL( 'action=unprotect' . $par ); } else { + $wgOut->setPageTitle( wfMsg( 'confirmprotect' ) ); $wgOut->setSubtitle( wfMsg( 'protectsub', $sub ) ); $wgOut->addWikiText( wfMsg( 'confirmprotecttext' ) ); $check = htmlspecialchars( wfMsg( 'confirmprotect' ) ); @@ -1820,6 +1822,76 @@ /* static */ function onArticleEdit($title_obj){ LinkCache::linksccClearPage( $title_obj->getArticleID() ); } + + # Info about this page + + function info() + { + global $wgUser, $wgTitle, $wgOut, $wgLang; + + $basenamespace = $wgTitle->getNamespace() & (~1); + $cur_clause = "cur_title='".$wgTitle->getDBkey()."' AND cur_namespace=".$basenamespace; + $old_clause = "old_title='".$wgTitle->getDBkey()."' AND old_namespace=".$basenamespace; + $wl_clause = "wl_title='".$wgTitle->getDBkey()."' AND wl_namespace=".$basenamespace; + $fullTitle = $wgTitle->makeName($basenamespace, $wgTitle->getDBKey()); + $wgOut->setPagetitle( $fullTitle ); + $wgOut->setSubtitle( wfMsg( "infosubtitle" )); + + # first, see if the page exists at all. + $sql = "SELECT COUNT(*) FROM cur WHERE ".$cur_clause; + $exists = wfSingleQuery( $sql , DB_READ ); + if ($exists < 1) { + $wgOut->addHTML( wfMsg("noarticletext") ); + } else { + $sql = "SELECT COUNT(*) FROM watchlist WHERE ".$wl_clause; + $wgOut->addHTML( "<ul><li>" . wfMsg("numwatchers") . wfSingleQuery( $sql, DB_READ ) . "</li>" ); + $sql = "SELECT COUNT(*) FROM old WHERE ".$old_clause; + $old = wfSingleQuery( $sql, DB_READ ); + $wgOut->addHTML( "<li>" . wfMsg("numedits") . ($old + 1) . "</li>"); + + # 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 + $sql = "SELECT cur_user_text FROM cur WHERE ".$cur_clause; + $cur_author = wfSingleQuery( $sql, DB_READ ); + + # find number of 'old' authors excluding 'cur' author + $sql = "SELECT COUNT(DISTINCT old_user_text) FROM old WHERE ".$old_clause + ." AND old_user_text<>'" . $cur_author . "'"; + $authors = wfSingleQuery( $sql, DB_READ ) + 1; + + # now for the Talk page ... + $cur_clause = "cur_title='".$wgTitle->getDBkey()."' AND cur_namespace=".($basenamespace+1); + $old_clause = "old_title='".$wgTitle->getDBkey()."' AND old_namespace=".($basenamespace+1); + + # does it exist? + $sql = "SELECT COUNT(*) FROM cur WHERE ".$cur_clause; + $exists = wfSingleQuery( $sql , DB_READ ); + + # number of edits + if ($exists > 0) { + $sql = "SELECT COUNT(*) FROM old WHERE ".$old_clause; + $old = wfSingleQuery( $sql, DB_READ ); + $wgOut->addHTML( "<li>" . wfMsg("numtalkedits") . ($old + 1) . "</li>"); + } + $wgOut->addHTML( "<li>" . wfMsg("numauthors") . $authors . "</li>" ); + + # number of authors + if ($exists > 0) { + $sql = "SELECT cur_user_text FROM cur WHERE ".$cur_clause; + $cur_author = wfSingleQuery( $sql, DB_READ ); + + $sql = "SELECT COUNT(DISTINCT old_user_text) FROM old WHERE " + .$old_clause." AND old_user_text<>'" . $cur_author . "'"; + $authors = wfSingleQuery( $sql, DB_READ ) + 1; + + $wgOut->addHTML( "<li>" . wfMsg("numtalkauthors") . $authors . "</li></ul>" ); + } + } + } }
?> Index: includes/DatabaseFunctions.php =================================================================== RCS file: /cvsroot/wikipedia/phase3/includes/DatabaseFunctions.php,v retrieving revision 1.36 diff -u -b -r1.36 DatabaseFunctions.php --- includes/DatabaseFunctions.php 22 Jun 2004 08:54:26 -0000 1.36 +++ includes/DatabaseFunctions.php 8 Jul 2004 14:41:04 -0000 @@ -39,6 +39,15 @@ } }
+function wfSingleQuery( $sql, $db, $fname = "" ) +{ + $res = wfQuery($sql, $db, $fname ); + $row = wfFetchRow( $res ); + $ret = $row[0]; + wfFreeResult( $res ); + return $ret; +} + function &wfGetDB( $db = DB_LAST ) { global $wgLoadBalancer; Index: includes/OutputPage.php =================================================================== RCS file: /cvsroot/wikipedia/phase3/includes/OutputPage.php,v retrieving revision 1.152 diff -u -b -r1.152 OutputPage.php --- includes/OutputPage.php 19 Jun 2004 06:46:54 -0000 1.152 +++ includes/OutputPage.php 8 Jul 2004 14:41:04 -0000 @@ -120,7 +120,7 @@ case 'history': return wfMsg('history_short'); case 'protect': - return wfMsg('unprotect'); + return wfMsg('protect'); case 'unprotect': return wfMsg('unprotect'); case 'delete': @@ -131,6 +131,8 @@ return wfMsg('unwatch'); case 'submit': return wfMsg('preview'); + case 'info': + return wfMsg('info_short'); default: return ''; } Index: languages/Language.php =================================================================== RCS file: /cvsroot/wikipedia/phase3/languages/Language.php,v retrieving revision 1.257 diff -u -b -r1.257 Language.php --- languages/Language.php 5 Jul 2004 21:26:35 -0000 1.257 +++ languages/Language.php 8 Jul 2004 14:41:04 -0000 @@ -260,6 +260,7 @@ 'go' => 'Go', "history" => 'Page history', 'history_short' => 'History', +'info_short' => 'Information', 'printableversion' => 'Printable version', 'edit' => 'Edit', 'editthispage' => 'Edit this page', @@ -1307,6 +1308,14 @@ 'categoryarticlecount' => "There are $1 articles in this category.", 'usenewcategorypage' => "1\n\nSet first character to "0" to disable the new category page layout.",
+# Info page +"infosubtitle" => "Information for page", +"numedits" => "Number of edits (article): ", +"numtalkedits" => "Number of edits (discussion page): ", +"numwatchers" => "Number of watchers: ", +"numauthors" => "Number of distinct authors (article): ", +"numtalkauthors" => "Number of distinct authors (discussion page): ", + # Monobook.js: tooltips and access keys for monobook 'Monobook.js' => '/* tooltips and access keys */ ta = new Object();
On Thu, 08 Jul 2004 15:35:15 +0100, Timwi timwi@gmx.net wrote: snip
In my mind, it makes sense to be able to link to pages that pertain to the wiki as a whole (e.g. Special:Recentchanges) or to a user (e.g. Special:Contributions), but I don't see the benefit in being able to link to Special:Movepage. (Admittedly, there is an obvious benefit in being able to link to the info page for an article... but quite frankly, I think if we really need that so badly, we should come up with a new syntax for it so that we can also link to edit, history, etc.
Suggestion: [[::history:Article title]]
We can already link to edit and history, see for example: [{{SERVER}}{{localurl:Main Page|action=edit}}] [{{SERVER}}{{localurl:Main Page|action=history}}]
It's ugly, but that's cause it's generic (and it works)
On Thu, Jul 08, 2004 at 03:35:15PM +0100, Timwi wrote:
I've also made it so that the <title> now shows "Article title - Information - Site name" as opposed to just "Article title - Site name", so it is consistent with History.
While I was at it, I noticed missing or wrong <title>s when protect or unprotect a page. I've fixed those too.
I'm attaching the entire patch again. I think it's committable like this, although someone should still add an "info" tab to the skin or else the info page is unreachable except by manually fiddling with the URL.
Okay, here are my latest comments on the patch: 1. If there is a talk page, but no article, it says no article text and does not report statistics for the talk page. This does happen occasionly in wikipedia, for example: http://en.wikipedia.org/wiki/Talk:M.R.M._Parrott (Note that the UI gets this wrong in that the article like is blue, not red in monobook style) This can be fixed with a little rearranging of the order of the stats and a } move.
2. If the article or the most recent user have a ' in their name, the sql query errors out. I think this could be fixed by changing the $wgTitle->getDBkey() to $dbr->addQuotes($wgTitle->getDBkey()) and the $cur_author to $dbr->addQuotes($cur_author)
3. Do the wfQuery's need fname? What exactly is fname?
4. As you noted, the info tab needs to be added.
5. Has any developer tried the querys on votes for deletion, or some other article with lots of edits/authors to see how long the query takes?
Other than the ' problem, I think the patch is acceptable.
Josh Cogliati wrote:
Timwi, Thanks for the version of the patch. I have some questions though. On Thu, Jul 08, 2004 at 12:07:08PM +0100, Timwi wrote:
- Change it to a special page?
No, please not!... I don't like special pages because they make the tabs disappear. :) I'd really rather prefer if Special:Movepage were converted to a simple &action=move thingie.
Tim Starling made the argument that special pages were easier to link to and did not involve adding more UI stuff to Article.php.
I said something else too, but you left IRC a few seconds before I was able to say it.
Would it be quicker to turn this into two query? Where the second query is something like:
$sql = "SELECT COUNT(DISTINCT old_user_text) FROM old WHERE ".$old_clause . " AND old_user_text != " . $cur_author; Then you can always $authors++ since the current author will not appear in the second query. So the second query is becomes more complicated, but the third query is unneeded.
It doesn't matter which way you do it, it's going to be very slow on the live site. Any query which involves loading every row of the old table for a particular article will take at least a couple of minutes to complete for pages with large histories. This query:
SELECT old_user FROM old WHERE old_title ='Main_Page' and old_namespace=0 UNION SELECT cur_user FROM cur WHERE cur_title ='Main_Page' and cur_namespace=0;
...took almost 2 minutes on suda in a relatively quiet part of the day. I wasn't game to try it on the village pump. Before we can include this on the live site, we will need to make it efficient. Have you considered keeping a list of authors in a field in the cur table, and updating it when the article is saved?
-- Tim Starling
wikitech-l@lists.wikimedia.org