Difference between revisions of "MediaWikiDoc:Article.php"

From HypertWiki
Jump to navigation Jump to search
(more functions. still editing.)
m (finished with function list; going to try complete code paste, with headers)
Line 33: Line 33:
*function getMinorEdit() {
*function getMinorEdit() {
===More Other Methods===
===More Other Methods===
*function getContributors($limit = 0, $offset = 0) {
*function getContributors($limit = 0, $offset = 0)
*function view() {
*function view()
**This is the default action of the script: just view the page of the given title.
**This is the default action of the script: just view the page of the given title.
*function insertNewArticle( $text, $summary, $isminor, $watchthis ) {
*function insertNewArticle( $text, $summary, $isminor, $watchthis )
**Theoretically we could defer these whole insert and update functions for after display, but that's taking a big leap of faith, and we want to be able to report database errors at some point.
**Theoretically we could defer these whole insert and update functions for after display, but that's taking a big leap of faith, and we want to be able to report database errors at some point.
*function getTextOfLastEditWithSectionReplacedOrAdded($section, $text, $summary = '', $edittime = NULL) {
*function getTextOfLastEditWithSectionReplacedOrAdded($section, $text, $summary = '', $edittime = NULL)
**Side effects: loads last edit if $edittime is NULL
**Side effects: loads last edit if $edittime is NULL
*function updateArticle( $text, $summary, $minor, $watchthis, $forceBot = false, $sectionanchor = '' ) {
*function updateArticle( $text, $summary, $minor, $watchthis, $forceBot = false, $sectionanchor = '' )
**Change an existing article. Puts the previous version back into the old table, updates RC and all necessary caches, mostly via the deferred update array.
**Change an existing article. Puts the previous version back into the old table, updates RC and all necessary caches, mostly via the deferred update array.
**It is possible to call this function from a command-line script, but note that you should first set $wgUser, and clean up $wgDeferredUpdates after each edit.
**It is possible to call this function from a command-line script, but note that you should first set $wgUser, and clean up $wgDeferredUpdates after each edit.
*function showArticle( $text, $subtitle , $sectionanchor = '' ) {
*function showArticle( $text, $subtitle , $sectionanchor = '' )
**After we've either updated or inserted the article, update the link tables and redirect to the new page.
**After we've either updated or inserted the article, update the link tables and redirect to the new page.
*function validate () {
*function validate ()
**Validate article
**Validate article
*function markpatrolled() {
*function markpatrolled()
**Mark this particular edit as patrolled
**Mark this particular edit as patrolled
*function watch() {
*function watch()
**Add this page to $wgUser's watchlist
**Add this page to $wgUser's watchlist
*function unwatch() {
*function unwatch()
**Stop watching a page
**Stop watching a page
*function protect( $limit = 'sysop' ) {
*function protect( $limit = 'sysop' )
**protect a page
**protect a page
*function confirmProtect( $par, $reason, $limit = 'sysop'  ) {
*function confirmProtect( $par, $reason, $limit = 'sysop'  )
**Output protection confirmation dialog
**Output protection confirmation dialog
*function unprotect() {
*function unprotect()
**Unprotect the pages
**Unprotect the pages
*function delete() {
*function delete()
**UI entry point for page deletion
**UI entry point for page deletion
/**
*function confirmDelete( $par, $reason )
* Output deletion confirmation dialog
**Output deletion confirmation dialog
*/
*function doDelete( $reason )
function confirmDelete( $par, $reason ) {
**Perform a deletion and output success or failure messages
global $wgOut, $wgUser;
*function doDeleteArticle( $reason )
 
**Back-end article deletion
wfDebug( "Article::confirmDelete\n" );
**Deletes the article with database consistency, writes logs, purges caches
 
**Returns success
$sub = htmlspecialchars( $this->mTitle->getPrefixedText() );
*function rollback()
$wgOut->setSubtitle( wfMsg( 'deletesub', $sub ) );
**Revert a modification
$wgOut->setRobotpolicy( 'noindex,nofollow' );
*function viewUpdates()
$wgOut->addWikiText( wfMsg( 'confirmdeletetext' ) );
**Do standard deferred updates after page view
 
*function editUpdates( $text )
$formaction = $this->mTitle->escapeLocalURL( 'action=delete' . $par );
**Do standard deferred updates after page edit. Every 1000th edit, prune the recent changes table.
 
*function setOldSubtitle( $oldid=0 )
$confirm = htmlspecialchars( wfMsg( 'confirm' ) );
*function preSaveTransform( $text )
$check = htmlspecialchars( wfMsg( 'confirmcheck' ) );
**This function is called right before saving the wikitext, so we can do things like signatures and links-in-context.
$delcom = htmlspecialchars( wfMsg( 'deletecomment' ) );
*function tryFileCache()
$token = htmlspecialchars( $wgUser->editToken() );
**checkLastModified returns true if it has taken care of all output to the client that is necessary for this request (that is, it has sent a cached version of the page)
 
*function isFileCacheable()
$wgOut->addHTML( "
**Check if the page can be cached
<form id='deleteconfirm' method='post' action=\"{$formaction}\">
*function checkTouched()
<table border='0'>
**Loads cur_touched and returns a value indicating if it should be used
<tr>
*function quickEdit( $text, $comment = '', $minor = 0 )
<td align='right'>
**Edit an article without doing all that other stuff
<label for='wpReason'>{$delcom}:</label>
*function incViewCount( $id )
</td>
**Used to increment the view counter
<td align='left'>
===Event Hooks===
<input type='text' size='60' name='wpReason' id='wpReason' value=\"" . htmlspecialchars( $reason ) . "\" />
The onArticle*() functions are supposed to be a kind of hooks which should be called whenever any of the specified actions are done. This is a good place to put code to clear caches, for instance.
</td>
*function onArticleCreate($title_obj)
</tr>
**This is called on page move and undelete, as well as edit
<tr>
*function onArticleDelete($title_obj)
<td>&nbsp;</td>
*function onArticleEdit($title_obj)
</tr>
===More Other Functions===
<tr>
*function info()
<td align='right'>
**Info about this page
<input type='checkbox' name='wpConfirm' value='1' id='wpConfirm' />
</td>
<td>
<label for='wpConfirm'>{$check}</label>
</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>
<input type='submit' name='wpConfirmB' value=\"{$confirm}\" />
</td>
</tr>
</table>
<input type='hidden' name='wpEditToken' value=\"{$token}\" />
</form>\n" );
 
$wgOut->returnToMain( false );
}
 
 
/**
* Perform a deletion and output success or failure messages
*/
function doDelete( $reason ) {
global $wgOut, $wgUser, $wgContLang;
$fname = 'Article::doDelete';
wfDebug( $fname."\n" );
 
if (wfRunHooks('ArticleDelete', array(&$this, &$wgUser, &$reason))) {
if ( $this->doDeleteArticle( $reason ) ) {
$deleted = $this->mTitle->getPrefixedText();
$wgOut->setPagetitle( wfMsg( 'actioncomplete' ) );
$wgOut->setRobotpolicy( 'noindex,nofollow' );
$sk = $wgUser->getSkin();
$loglink = $sk->makeKnownLink( $wgContLang->getNsText( NS_SPECIAL ) .
  ':Log/delete',
  wfMsg( 'deletionlog' ) );
$text = wfMsg( 'deletedtext', $deleted, $loglink );
$wgOut->addHTML( '<p>' . $text . "</p>\n" );
$wgOut->returnToMain( false );
wfRunHooks('ArticleDeleteComplete', array(&$this, &$wgUser, $reason));
} else {
$wgOut->fatalError( wfMsg( 'cannotdelete' ) );
}
}
}
 
/**
* Back-end article deletion
* Deletes the article with database consistency, writes logs, purges caches
* Returns success
*/
function doDeleteArticle( $reason ) {
global $wgUser;
global  $wgUseSquid, $wgDeferredUpdateList, $wgInternalServer, $wgPostCommitUpdateList;
 
$fname = 'Article::doDeleteArticle';
wfDebug( $fname."\n" );
 
$dbw =& wfGetDB( DB_MASTER );
$ns = $this->mTitle->getNamespace();
$t = $this->mTitle->getDBkey();
$id = $this->mTitle->getArticleID();
 
if ( $t == '' || $id == 0 ) {
return false;
}
 
$u = new SiteStatsUpdate( 0, 1, -$this->isCountable( $this->getContent( true ) ), -1 );
array_push( $wgDeferredUpdateList, $u );
 
$linksTo = $this->mTitle->getLinksTo();
 
# Squid purging
if ( $wgUseSquid ) {
$urls = array(
$this->mTitle->getInternalURL(),
$this->mTitle->getInternalURL( 'history' )
);
foreach ( $linksTo as $linkTo ) {
$urls[] = $linkTo->getInternalURL();
}
 
$u = new SquidUpdate( $urls );
array_push( $wgPostCommitUpdateList, $u );
 
}
 
# Client and file cache invalidation
Title::touchArray( $linksTo );
 
# Move article and history to the "archive" table
$archiveTable = $dbw->tableName( 'archive' );
$oldTable = $dbw->tableName( 'old' );
$curTable = $dbw->tableName( 'cur' );
$recentchangesTable = $dbw->tableName( 'recentchanges' );
$linksTable = $dbw->tableName( 'links' );
$brokenlinksTable = $dbw->tableName( 'brokenlinks' );
 
# Archive cur row
$dbw->insertSelect( 'archive', 'cur',
array(
'ar_namespace' => 'cur_namespace',
'ar_title' => 'cur_title',
'ar_text' => 'cur_text',
'ar_comment' => 'cur_comment',
'ar_user' => 'cur_user',
'ar_user_text' => 'cur_user_text',
'ar_timestamp' => 'cur_timestamp',
'ar_minor_edit' => 'cur_minor_edit',
'ar_flags' => 0,
), array(
'cur_namespace' => $ns,
'cur_title' => $t,
), $fname
);
 
# Archive old rows
$res = $dbw->select( 'old', array( 'old_namespace', 'old_title', 'old_text', 'old_comment',
'old_user', 'old_user_text', 'old_timestamp', 'old_minor_edit', 'old_flags' ),
array( 'old_namespace' => $ns, 'old_title' => $t ), $fname );
 
while ( $row = $dbw->fetchObject( $res ) ) {
# Recompress block-compressed revisions
if ( $row->old_flags == 'object' ) {
$text = Article::getRevisionText( $row );
$flags = Article::compressRevisionText( $text );
} else {
$text = $row->old_text;
$flags = $row->old_flags;
}
 
$dbw->insert( 'archive',
array(
'ar_namespace' => $ns,
'ar_title' => $t,
'ar_text' => $text,
'ar_comment' => $row->old_comment,
'ar_user' => $row->old_user,
'ar_user_text' => $row->old_user_text,
'ar_timestamp' => $row->old_timestamp,
'ar_minor_edit' => $row->old_minor_edit,
'ar_flags' => $flags
), $fname
);
}
# Now that it's safely backed up, delete it
 
$dbw->delete( 'cur', array( 'cur_namespace' => $ns, 'cur_title' => $t ), $fname );
$dbw->delete( 'old', array( 'old_namespace' => $ns, 'old_title' => $t ), $fname );
$dbw->delete( 'recentchanges', array( 'rc_namespace' => $ns, 'rc_title' => $t ), $fname );
 
# Finally, clean up the link tables
$t = $this->mTitle->getPrefixedDBkey();
 
Article::onArticleDelete( $this->mTitle );
 
# Insert broken links
$brokenLinks = array();
foreach ( $linksTo as $titleObj ) {
# Get article ID. Efficient because it was loaded into the cache by getLinksTo().
$linkID = $titleObj->getArticleID();
$brokenLinks[] = array( 'bl_from' => $linkID, 'bl_to' => $t );
}
$dbw->insert( 'brokenlinks', $brokenLinks, $fname, 'IGNORE' );
 
# Delete live links
$dbw->delete( 'links', array( 'l_to' => $id ) );
$dbw->delete( 'links', array( 'l_from' => $id ) );
$dbw->delete( 'imagelinks', array( 'il_from' => $id ) );
$dbw->delete( 'brokenlinks', array( 'bl_from' => $id ) );
$dbw->delete( 'categorylinks', array( 'cl_from' => $id ) );
 
# Log the deletion
$log = new LogPage( 'delete' );
$log->addEntry( 'delete', $this->mTitle, $reason );
 
# Clear the cached article id so the interface doesn't act like we exist
$this->mTitle->resetArticleID( 0 );
$this->mTitle->mArticleID = 0;
return true;
}
 
/**
* Revert a modification
*/
function rollback() {
global $wgUser, $wgOut, $wgRequest;
$fname = 'Article::rollback';
 
if ( ! $wgUser->isAllowed('rollback') ) {
$wgOut->sysopRequired();
return;
}
if ( wfReadOnly() ) {
$wgOut->readOnlyPage( $this->getContent( true ) );
return;
}
if( !$wgUser->matchEditToken( $wgRequest->getVal( 'token' ),
array( $this->mTitle->getPrefixedText(),
$wgRequest->getVal( 'from' ) )  ) ) {
$wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) );
$wgOut->addWikiText( wfMsg( 'sessionfailure' ) );
return;
}
$dbw =& wfGetDB( DB_MASTER );
 
# Enhanced rollback, marks edits rc_bot=1
$bot = $wgRequest->getBool( 'bot' );
 
# Replace all this user's current edits with the next one down
$tt = $this->mTitle->getDBKey();
$n = $this->mTitle->getNamespace();
 
# Get the last editor, lock table exclusively
$s = $dbw->selectRow( 'cur',
array( 'cur_id','cur_user','cur_user_text','cur_comment' ),
array( 'cur_title' => $tt, 'cur_namespace' => $n ),
$fname, 'FOR UPDATE'
);
if( $s === false ) {
# Something wrong
$wgOut->addHTML( wfMsg( 'notanarticle' ) );
return;
}
$ut = $dbw->strencode( $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'));
$wgOut->addWikiText( wfMsg( 'alreadyrolled',
htmlspecialchars( $this->mTitle->getPrefixedText()),
htmlspecialchars( $from ),
htmlspecialchars( $s->cur_user_text ) ) );
if($s->cur_comment != '') {
$wgOut->addHTML(
wfMsg('editcomment',
htmlspecialchars( $s->cur_comment ) ) );
}
return;
}
 
# Get the last edit not by this guy
$s = $dbw->selectRow( 'old',
array( 'old_text','old_user','old_user_text','old_timestamp','old_flags' ),
array(
'old_namespace' => $n,
'old_title' => $tt,
"old_user <> {$uid} OR old_user_text <> '{$ut}'"
), $fname, array( 'FOR UPDATE', 'USE INDEX' => 'name_title_timestamp' )
);
if( $s === false ) {
# Something wrong
$wgOut->setPageTitle(wfMsg('rollbackfailed'));
$wgOut->addHTML( wfMsg( 'cantrollback' ) );
return;
}
 
if ( $bot ) {
# Mark all reverted edits as bot
$dbw->update( 'recentchanges',
array( /* SET */
'rc_bot' => 1
), array( /* WHERE */
'rc_user' => $uid,
"rc_timestamp > '{$s->old_timestamp}'",
), $fname
);
}
 
# Save it!
$newcomment = wfMsg( 'revertpage', $s->old_user_text, $from );
$wgOut->setPagetitle( wfMsg( 'actioncomplete' ) );
$wgOut->setRobotpolicy( 'noindex,nofollow' );
$wgOut->addHTML( '<h2>' . htmlspecialchars( $newcomment ) . "</h2>\n<hr />\n" );
$this->updateArticle( Article::getRevisionText( $s ), $newcomment, 1, $this->mTitle->userIsWatching(), $bot );
Article::onArticleEdit( $this->mTitle );
$wgOut->returnToMain( false );
}
 
 
/**
* Do standard deferred updates after page view
* @private
*/
function viewUpdates() {
global $wgDeferredUpdateList;
if ( 0 != $this->getID() ) {
global $wgDisableCounters;
if( !$wgDisableCounters ) {
Article::incViewCount( $this->getID() );
$u = new SiteStatsUpdate( 1, 0, 0 );
array_push( $wgDeferredUpdateList, $u );
}
}
# Update newtalk status if user is reading their own
# talk page
 
global $wgUser;
if ($this->mTitle->getNamespace() == NS_USER_TALK &&
$this->mTitle->getText() == $wgUser->getName())
{
$wgUser->setNewtalk(0);
$wgUser->saveNewtalk();
}
}
 
/**
* Do standard deferred updates after page edit.
* Every 1000th edit, prune the recent changes table.
* @private
* @param string $text
*/
function editUpdates( $text ) {
global $wgDeferredUpdateList, $wgDBname, $wgMemc;
global $wgMessageCache, $wgUser;
 
wfSeedRandom();
if ( 0 == mt_rand( 0, 999 ) ) {
# Periodically flush old entries from the recentchanges table.
global $wgRCMaxAge;
$dbw =& wfGetDB( DB_MASTER );
$cutoff = $dbw->timestamp( time() - $wgRCMaxAge );
$recentchanges = $dbw->tableName( 'recentchanges' );
$sql = "DELETE FROM $recentchanges WHERE rc_timestamp < '{$cutoff}'";
$dbw->query( $sql );
}
$id = $this->getID();
$title = $this->mTitle->getPrefixedDBkey();
$shortTitle = $this->mTitle->getDBkey();
 
if ( 0 != $id ) {
$u = new LinksUpdate( $id, $title );
array_push( $wgDeferredUpdateList, $u );
$u = new SiteStatsUpdate( 0, 1, $this->mGoodAdjustment, $this->mTotalAdjustment );
array_push( $wgDeferredUpdateList, $u );
$u = new SearchUpdate( $id, $title, $text );
array_push( $wgDeferredUpdateList, $u );
 
# If this is another user's talk page, save a
# newtalk notification for them
if ($this->mTitle->getNamespace() == NS_USER_TALK &&
$shortTitle != $wgUser->getName())
{
$other = User::newFromName($shortTitle);
$other->setNewtalk(1);
$other->saveNewtalk();
}
 
if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
$wgMessageCache->replace( $shortTitle, $text );
}
}
}
 
/**
* @todo document this function
* @private
* @param string $oldid Revision ID of this article revision
*/
function setOldSubtitle( $oldid=0 ) {
global $wgLang, $wgOut, $wgUser;
 
$td = $wgLang->timeanddate( $this->mTimestamp, true );
$sk = $wgUser->getSkin();
$lnk = $sk->makeKnownLinkObj ( $this->mTitle, wfMsg( 'currentrevisionlink' ) );
$prevlink = $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'previousrevision' ), 'direction=prev&oldid='.$oldid );
$nextlink = $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'nextrevision' ), 'direction=next&oldid='.$oldid );
$r = wfMsg( 'revisionasofwithlink', $td, $lnk, $prevlink, $nextlink );
$wgOut->setSubtitle( $r );
}
 
/**
* This function is called right before saving the wikitext,
* so we can do things like signatures and links-in-context.
*
* @param string $text
*/
function preSaveTransform( $text ) {
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
* output to the client that is necessary for this request.
* (that is, it has sent a cached version of the page)
*/
function tryFileCache() {
static $called = false;
if( $called ) {
wfDebug( " tryFileCache() -- called twice!?\n" );
return;
}
$called = true;
if($this->isFileCacheable()) {
$touched = $this->mTouched;
$cache = new CacheManager( $this->mTitle );
if($cache->isFileCacheGood( $touched )) {
global $wgOut;
wfDebug( " tryFileCache() - about to load\n" );
$cache->loadFromFileCache();
return true;
} else {
wfDebug( " tryFileCache() - starting buffer\n" );
ob_start( array(&$cache, 'saveToFileCache' ) );
}
} else {
wfDebug( " tryFileCache() - not cacheable\n" );
}
}
/**
* Check if the page can be cached
* @return bool
*/
function isFileCacheable() {
global $wgUser, $wgUseFileCache, $wgShowIPinHeader, $wgRequest;
extract( $wgRequest->getValues( 'action', 'oldid', 'diff', 'redirect', 'printable' ) );
 
return $wgUseFileCache
and (!$wgShowIPinHeader)
and ($this->getID() != 0)
and ($wgUser->getId() == 0)
and (!$wgUser->getNewtalk())
and ($this->mTitle->getNamespace() != NS_SPECIAL )
and (empty( $action ) || $action == 'view')
and (!isset($oldid))
and (!isset($diff))
and (!isset($redirect))
and (!isset($printable))
and (!$this->mRedirectedFrom);
}
 
/**
* Loads cur_touched and returns a value indicating if it should be used
*
*/
function checkTouched() {
$fname = 'Article::checkTouched';
$id = $this->getID();
$dbr =& $this->getDB();
$s = $dbr->selectRow( 'cur', array( 'cur_touched', 'cur_is_redirect' ),
array( 'cur_id' => $id ), $fname, $this->getSelectOptions() );
if( $s !== false ) {
$this->mTouched = wfTimestamp(TS_MW,$s->cur_touched);
return !$s->cur_is_redirect;
} else {
return false;
}
}
 
/**
* Edit an article without doing all that other stuff
*
* @param string $text text submitted
* @param string $comment comment submitted
* @param integer $minor whereas it's a minor modification
*/
function quickEdit( $text, $comment = '', $minor = 0 ) {
global $wgUser;
$fname = 'Article::quickEdit';
wfProfileIn( $fname );
 
$dbw =& wfGetDB( DB_MASTER );
$ns = $this->mTitle->getNamespace();
$dbkey = $this->mTitle->getDBkey();
$encDbKey = $dbw->strencode( $dbkey );
$timestamp = $dbw->timestamp();
 
# Save to history
$dbw->insertSelect( 'old', 'cur',
array(
'old_namespace' => 'cur_namespace',
'old_title' => 'cur_title',
'old_text' => 'cur_text',
'old_comment' => 'cur_comment',
'old_user' => 'cur_user',
'old_user_text' => 'cur_user_text',
'old_timestamp' => 'cur_timestamp',
'inverse_timestamp' => '99999999999999-cur_timestamp',
), array(
'cur_namespace' => $ns,
'cur_title' => $dbkey,
), $fname
);
 
# Use the affected row count to determine if the article is new
$numRows = $dbw->affectedRows();
 
# Make an array of fields to be inserted
$fields = array(
'cur_text' => $text,
'cur_timestamp' => $dbw->timestamp($timestamp),
'cur_user' => $wgUser->getID(),
'cur_user_text' => $wgUser->getName(),
'inverse_timestamp' => wfInvertTimestamp( $timestamp ),
'cur_comment' => $comment,
'cur_is_redirect' => $this->isRedirect( $text ) ? 1 : 0,
'cur_minor_edit' => intval($minor),
'cur_touched' => $dbw->timestamp($timestamp),
);
 
if ( $numRows ) {
# Update article
$fields['cur_is_new'] = 0;
$dbw->update( 'cur', $fields, array( 'cur_namespace' => $ns, 'cur_title' => $dbkey ), $fname );
} else {
# Insert new article
$fields['cur_is_new'] = 1;
$fields['cur_namespace'] = $ns;
$fields['cur_title'] = $dbkey;
$fields['cur_random'] = $rand = wfRandom();
$dbw->insert( 'cur', $fields, $fname );
}
wfProfileOut( $fname );
}
 
/**
* Used to increment the view counter
*
* @static
* @param integer $id article id
*/
function incViewCount( $id ) {
$id = intval( $id );
global $wgHitcounterUpdateFreq;
 
$dbw =& wfGetDB( DB_MASTER );
$curTable = $dbw->tableName( 'cur' );
$hitcounterTable = $dbw->tableName( 'hitcounter' );
$acchitsTable = $dbw->tableName( 'acchits' );
 
if( $wgHitcounterUpdateFreq <= 1 ){ //
$dbw->query( "UPDATE $curTable SET cur_counter = cur_counter + 1 WHERE cur_id = $id" );
return;
}
 
# Not important enough to warrant an error page in case of failure
$oldignore = $dbw->ignoreErrors( true );
 
$dbw->query( "INSERT INTO $hitcounterTable (hc_id) VALUES ({$id})" );
 
$checkfreq = intval( $wgHitcounterUpdateFreq/25 + 1 );
if( (rand() % $checkfreq != 0) or ($dbw->lastErrno() != 0) ){
# Most of the time (or on SQL errors), skip row count check
$dbw->ignoreErrors( $oldignore );
return;
}
 
$res = $dbw->query("SELECT COUNT(*) as n FROM $hitcounterTable");
$row = $dbw->fetchObject( $res );
$rown = intval( $row->n );
if( $rown >= $wgHitcounterUpdateFreq ){
wfProfileIn( 'Article::incViewCount-collect' );
$old_user_abort = ignore_user_abort( true );
 
$dbw->query("LOCK TABLES $hitcounterTable WRITE");
$dbw->query("CREATE TEMPORARY TABLE $acchitsTable TYPE=HEAP ".
"SELECT hc_id,COUNT(*) AS hc_n FROM $hitcounterTable ".
'GROUP BY hc_id');
$dbw->query("DELETE FROM $hitcounterTable");
$dbw->query('UNLOCK TABLES');
$dbw->query("UPDATE $curTable,$acchitsTable SET cur_counter=cur_counter + hc_n ".
'WHERE cur_id = hc_id');
$dbw->query("DROP TABLE $acchitsTable");
 
ignore_user_abort( $old_user_abort );
wfProfileOut( 'Article::incViewCount-collect' );
}
$dbw->ignoreErrors( $oldignore );
}
 
/**#@+
* The onArticle*() functions are supposed to be a kind of hooks
* which should be called whenever any of the specified actions
* are done.
*
* This is a good place to put code to clear caches, for instance.
*  
* This is called on page move and undelete, as well as edit
* @static
* @param $title_obj a title object
*/
 
function onArticleCreate($title_obj) {
global $wgUseSquid, $wgPostCommitUpdateList;
 
$titles = $title_obj->getBrokenLinksTo();
 
# Purge squid
if ( $wgUseSquid ) {
$urls = $title_obj->getSquidURLs();
foreach ( $titles as $linkTitle ) {
$urls[] = $linkTitle->getInternalURL();
}
$u = new SquidUpdate( $urls );
array_push( $wgPostCommitUpdateList, $u );
}
 
# Clear persistent link cache
LinkCache::linksccClearBrokenLinksTo( $title_obj->getPrefixedDBkey() );
}
 
function onArticleDelete($title_obj) {
LinkCache::linksccClearLinksTo( $title_obj->getArticleID() );
}
function onArticleEdit($title_obj) {
LinkCache::linksccClearPage( $title_obj->getArticleID() );
}
/**#@-*/
 
/**
* Info about this page
*/
function info() {
global $wgUser, $wgTitle, $wgOut, $wgAllowPageInfo;
$fname = 'Article::info';
 
if ( !$wgAllowPageInfo ) {
$wgOut->errorpage( 'nosuchaction', 'nosuchactiontext' );
return;
}
 
$dbr =& $this->getDB();
 
$basenamespace = $wgTitle->getNamespace() & (~1);
$cur_clause = array( 'cur_title' => $wgTitle->getDBkey(), 'cur_namespace' => $basenamespace );
$old_clause = array( 'old_title' => $wgTitle->getDBkey(), 'old_namespace' => $basenamespace );
$wl_clause  = array( 'wl_title' => $wgTitle->getDBkey(), 'wl_namespace' => $basenamespace );
$fullTitle = $wgTitle->makeName($basenamespace, $wgTitle->getDBKey());
$wgOut->setPagetitle(  $fullTitle );
$wgOut->setSubtitle( wfMsg( 'infosubtitle' ));
 
# first, see if the page exists at all.
$exists = $dbr->selectField( 'cur', 'COUNT(*)', $cur_clause, $fname, $this->getSelectOptions() );
if ($exists < 1) {
$wgOut->addHTML( wfMsg('noarticletext') );
} else {
$numwatchers = $dbr->selectField( 'watchlist', 'COUNT(*)', $wl_clause, $fname,
$this->getSelectOptions() );
$wgOut->addHTML( "<ul><li>" . wfMsg("numwatchers", $numwatchers) . '</li>' );
$old = $dbr->selectField( 'old', 'COUNT(*)', $old_clause, $fname, $this->getSelectOptions() );
$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
$cur_author = $dbr->selectField( 'cur', 'cur_user_text', $cur_clause, $fname,
$this->getSelectOptions() );
 
# find number of 'old' authors excluding 'cur' author
$authors = $dbr->selectField( 'old', 'COUNT(DISTINCT old_user_text)',
$old_clause + array( 'old_user_text<>' . $dbr->addQuotes( $cur_author ) ), $fname,
$this->getSelectOptions() ) + 1;
 
# now for the Talk page ...
$cur_clause = array( 'cur_title' => $wgTitle->getDBkey(), 'cur_namespace' => $basenamespace+1 );
$old_clause = array( 'old_title' => $wgTitle->getDBkey(), 'old_namespace' => $basenamespace+1 );
 
# does it exist?
$exists = $dbr->selectField( 'cur', 'COUNT(*)', $cur_clause, $fname, $this->getSelectOptions() );
 
# number of edits
if ($exists > 0) {
$old = $dbr->selectField( 'old', 'COUNT(*)', $old_clause, $fname, $this->getSelectOptions() );
$wgOut->addHTML( '<li>' . wfMsg("numtalkedits", $old + 1) . '</li>');
}
$wgOut->addHTML( '<li>' . wfMsg("numauthors", $authors) . '</li>' );
 
# number of authors
if ($exists > 0) {
$cur_author = $dbr->selectField( 'cur', 'cur_user_text', $cur_clause, $fname,
$this->getSelectOptions() );
$authors = $dbr->selectField( 'old', 'COUNT(DISTINCT old_user_text)',
$old_clause + array( 'old_user_text<>' . $dbr->addQuotes( $cur_author ) ),
$fname, $this->getSelectOptions() );
 
$wgOut->addHTML( '<li>' . wfMsg('numtalkauthors', $authors) . '</li></ul>' );
}
}
}
}
 
?>
 
 
 
{{editing}}
{{editing}}

Revision as of 14:26, 16 June 2005

Techniques: MediaWiki: Article.php

Class Functions

function Article( &$title ) { function getTitle() { function clear() { function getRevisionText( $row, $prefix = 'old_' ) { function compressRevisionText( &$text ) { function getContent( $noredir ) { function getSection($text,$section) { function &getCurContentFields() { function &getOldContentFields() { function getOldID() { function loadContent( $noredir = false ) { function getContentWithoutUsingSoManyDamnGlobals( $oldid = 0, $noredir = false ) { function forUpdate( $x = NULL ) { function &getDB() { function getSelectOptions( $options = ) { function getID() { function getCount() {

    • Get the view count for this article

function isCountable( $text ) {

    • Would the given text make this article a "good" article (i.e., suitable for including in the article count)?
  • function isRedirect( $text = false ) {
    • Tests if the article text represents a redirect
  • function loadLastEdit() {
    • Loads everything from cur except cur_text
    • This isn't necessary for all uses, so it's only done if needed.

Data Access

  • function getTimestamp() {
  • function getUser() {
  • function getUserText() {
  • function getComment() {
  • function getMinorEdit() {

More Other Methods

  • function getContributors($limit = 0, $offset = 0)
  • function view()
    • This is the default action of the script: just view the page of the given title.
  • function insertNewArticle( $text, $summary, $isminor, $watchthis )
    • Theoretically we could defer these whole insert and update functions for after display, but that's taking a big leap of faith, and we want to be able to report database errors at some point.
  • function getTextOfLastEditWithSectionReplacedOrAdded($section, $text, $summary = , $edittime = NULL)
    • Side effects: loads last edit if $edittime is NULL
  • function updateArticle( $text, $summary, $minor, $watchthis, $forceBot = false, $sectionanchor = )
    • Change an existing article. Puts the previous version back into the old table, updates RC and all necessary caches, mostly via the deferred update array.
    • It is possible to call this function from a command-line script, but note that you should first set $wgUser, and clean up $wgDeferredUpdates after each edit.
  • function showArticle( $text, $subtitle , $sectionanchor = )
    • After we've either updated or inserted the article, update the link tables and redirect to the new page.
  • function validate ()
    • Validate article
  • function markpatrolled()
    • Mark this particular edit as patrolled
  • function watch()
    • Add this page to $wgUser's watchlist
  • function unwatch()
    • Stop watching a page
  • function protect( $limit = 'sysop' )
    • protect a page
  • function confirmProtect( $par, $reason, $limit = 'sysop' )
    • Output protection confirmation dialog
  • function unprotect()
    • Unprotect the pages
  • function delete()
    • UI entry point for page deletion
  • function confirmDelete( $par, $reason )
    • Output deletion confirmation dialog
  • function doDelete( $reason )
    • Perform a deletion and output success or failure messages
  • function doDeleteArticle( $reason )
    • Back-end article deletion
    • Deletes the article with database consistency, writes logs, purges caches
    • Returns success
  • function rollback()
    • Revert a modification
  • function viewUpdates()
    • Do standard deferred updates after page view
  • function editUpdates( $text )
    • Do standard deferred updates after page edit. Every 1000th edit, prune the recent changes table.
  • function setOldSubtitle( $oldid=0 )
  • function preSaveTransform( $text )
    • This function is called right before saving the wikitext, so we can do things like signatures and links-in-context.
  • function tryFileCache()
    • checkLastModified returns true if it has taken care of all output to the client that is necessary for this request (that is, it has sent a cached version of the page)
  • function isFileCacheable()
    • Check if the page can be cached
  • function checkTouched()
    • Loads cur_touched and returns a value indicating if it should be used
  • function quickEdit( $text, $comment = , $minor = 0 )
    • Edit an article without doing all that other stuff
  • function incViewCount( $id )
    • Used to increment the view counter

Event Hooks

The onArticle*() functions are supposed to be a kind of hooks which should be called whenever any of the specified actions are done. This is a good place to put code to clear caches, for instance.

  • function onArticleCreate($title_obj)
    • This is called on page move and undelete, as well as edit
  • function onArticleDelete($title_obj)
  • function onArticleEdit($title_obj)

More Other Functions

  • function info()
    • Info about this page
Editing is currently in progress on this article. Although editing is incomplete, the author or editor has saved their work to prevent loss. Please check back later by reloading the page, and do not edit while this message is still showing. Thank you.