r15516 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r15515‎ | r15516 | r15517 >
Date:22:20, 10 July 2006
Author:brion
Status:old
Tags:
Comment:
Initial proof of concept functionality for version tagging/snapshotting
Snapshots store versions of linked resources at snapshot time in a table
and force reading of those versions for embedded templates when the snapshot
is displayed.

For the moment snapshots can be manually defined at Special:Snapshot,
and viewed using ?tag=review or whatever while viewing. Work yet to go
on interfacing with parser cache, logging, review, clean systems, etc.

Images aren't snapshotted currently because it's difficult to refer to
image versions usefully at the moment. When the new system goes in this
should be handlable by storing the hash key of the file much as the
page revision numbers are stored now.
Modified paths:
  • /branches/snapshot-work/includes/Article.php (modified) (history)
  • /branches/snapshot-work/includes/DefaultSettings.php (modified) (history)
  • /branches/snapshot-work/includes/OutputPage.php (modified) (history)
  • /branches/snapshot-work/includes/Parser.php (modified) (history)
  • /branches/snapshot-work/includes/Snapshot.php (modified) (history)
  • /branches/snapshot-work/includes/SpecialPage.php (modified) (history)
  • /branches/snapshot-work/includes/SpecialSnapshot.php (added) (history)
  • /branches/snapshot-work/maintenance/tables.sql (modified) (history)

Diff [purge]

Index: branches/snapshot-work/maintenance/tables.sql
@@ -1018,6 +1018,9 @@
10191019 -- Key to page_id of the page being tagged
10201020 snap_page INT,
10211021
 1022+ -- Key to rev_id of the primary page tagged
 1023+ snap_rev INT,
 1024+
10221025 -- Tag identifier; machine-readable such as "reviewed"
10231026 -- There may be multiple versions with a given tag on a given page,
10241027 -- for updatable states where the latest with a given tag will be shown.
Index: branches/snapshot-work/includes/Article.php
@@ -717,6 +717,26 @@
718718 }
719719
720720 $outputDone = false;
 721+
 722+ // BEGIN FILTHY HACK
 723+ $tag = $wgRequest->getVal( 'tag' );
 724+ if( $tag ) {
 725+ $snap = Snapshot::newFromTag( $this->getId(), $tag );
 726+ $revId = $snap->getSnapRevision();
 727+ $rev = Revision::newFromTitle( $this->mTitle, $revId );
 728+ $text = $rev->getText();
 729+
 730+ $wgOut->setRevisionId( $revId );
 731+ $wgOut->setSnapshot( $snap );
 732+ $wgOut->addWikiText( "<div class=\"mw-tag-header\">" .
 733+ "This is tagged revision $revId</div>" );
 734+ $wgOut->addPrimaryWikiText( $text, $this, false );
 735+
 736+ $pcache = false;
 737+ $outputDone = true;
 738+ }
 739+ // END FILTHY HACK
 740+
721741 if ( $pcache ) {
722742 if ( $wgOut->tryParserCache( $this, $wgUser ) ) {
723743 $outputDone = true;
Index: branches/snapshot-work/includes/Parser.php
@@ -271,10 +271,12 @@
272272 $this->mOptions = $options;
273273 $this->mTitle =& $title;
274274
275 - if( $revid != null ) {
 275+ $oldRevisionId = $this->mRevisionId;
 276+ if( $revid !== null ) {
276277 $this->mRevisionId = $revid;
277278 }
278 - if( $snapshot != null ) {
 279+ $oldSnapshot = $this->mSnapshot;
 280+ if( $snapshot !== null ) {
279281 $this->mSnapshot = $snapshot;
280282 }
281283
@@ -358,6 +360,8 @@
359361 wfRunHooks( 'ParserAfterTidy', array( &$this, &$text ) );
360362
361363 $this->mOutput->setText( $text );
 364+ $this->mSnapshot = $oldSnapshot;
 365+ $this->mRevisionId = $oldRevisionId;
362366 wfProfileOut( $fname );
363367
364368 return $this->mOutput;
Index: branches/snapshot-work/includes/SpecialSnapshot.php
@@ -0,0 +1,48 @@
 2+<?php
 3+/**
 4+ * @package MediaWiki
 5+ * @subpackage SpecialPage
 6+ */
 7+
 8+function wfSpecialSnapshot( $par ) {
 9+ global $wgRequest, $wgOut, $wgUser;
 10+
 11+ if( $wgRequest->wasPosted() ) {
 12+ $page = $wgRequest->getText( 'wpTarget' );
 13+ $revId = $wgRequest->getInt( 'wpRevision' );
 14+ $tag = $wgRequest->getVal( 'wpTag' );
 15+
 16+ $title = Title::newFromText( $page );
 17+ $rev = Revision::newFromTitle( $title, $revId );
 18+ $text = $rev->getText();
 19+ $parserOptions = ParserOptions::newFromUser( $temp = NULL );
 20+
 21+ global $wgParser;
 22+ $parserOutput = $wgParser->parse( $text, $title,
 23+ $parserOptions, true, true,
 24+ $revId );
 25+
 26+ $snap = $parserOutput->getSnapshot();
 27+ $snapId = $snap->insertTag( $rev->getPage(), $revId, $tag );
 28+
 29+ $wgOut->addWikiText( "Saved tag snapshot id $snapId" );
 30+ }
 31+
 32+ // Quick hackie test form
 33+ $special = Title::makeTitle( NS_SPECIAL, 'Snapshot' );
 34+ $wgOut->addHtml(
 35+ Xml::openElement( 'form', array(
 36+ 'method' => 'post',
 37+ 'action' => $special->getLocalUrl() ) ) .
 38+ Xml::inputLabel( 'Snapshot page:', 'wpTarget', 'wpTarget', 40, $par ) .
 39+ '<br />' .
 40+ Xml::inputLabel( 'Revision id:', 'wpRevision', 'wpRevision', 20 ) .
 41+ '<br />' .
 42+ Xml::inputLabel( 'Tag name:', 'wpTag', 'wpTag', 16, 'reviewed' ) .
 43+ '<br />' .
 44+ Xml::submitButton( 'Save snapshot' ) .
 45+ Xml::hidden( 'wpEditToken', $wgUser->editToken() ) .
 46+ Xml::closeElement( 'form' ) );
 47+}
 48+
 49+?>
Property changes on: branches/snapshot-work/includes/SpecialSnapshot.php
___________________________________________________________________
Added: svn:eol-style
150 + native
Index: branches/snapshot-work/includes/OutputPage.php
@@ -51,6 +51,7 @@
5252 $this->mScripts = '';
5353 $this->mETag = false;
5454 $this->mRevisionId = null;
 55+ $this->mSnapshot = null;
5556 $this->mNewSectionLink = false;
5657 }
5758
@@ -274,6 +275,16 @@
275276 }
276277
277278 /**
 279+ * Set the version snapshot which will be seen by the wiki text parser
 280+ * for embedded templates and image resources.
 281+ * @param mixed $snapshot Snapshot object or null
 282+ * @return mixed previous value
 283+ */
 284+ function setSnapshot( $snapshot ) {
 285+ return wfSetVar( $this->mSnapshot, $snapshot );
 286+ }
 287+
 288+ /**
278289 * Convert wikitext to HTML and add it to the buffer
279290 * Default assumes that the current page title will
280291 * be used.
@@ -322,7 +333,8 @@
323334
324335 $this->mParserOptions->setTidy(true);
325336 $parserOutput = $wgParser->parse( $text, $article->mTitle,
326 - $this->mParserOptions, true, true, $this->mRevisionId );
 337+ $this->mParserOptions, true, true, $this->mRevisionId,
 338+ $this->mSnapshot );
327339 $this->mParserOptions->setTidy(false);
328340 if ( $cache && $article && $parserOutput->getCacheTime() != -1 ) {
329341 $parserCache =& ParserCache::singleton();
Index: branches/snapshot-work/includes/DefaultSettings.php
@@ -910,6 +910,7 @@
911911 $wgGroupPermissions['sysop']['reupload-shared'] = true;
912912 $wgGroupPermissions['sysop']['unwatchedpages'] = true;
913913 $wgGroupPermissions['sysop']['autoconfirmed'] = true;
 914+$wgGroupPermissions['sysop']['snapshot'] = true;
914915
915916 // Permission to change users' group assignments
916917 $wgGroupPermissions['bureaucrat']['userrights'] = true;
Index: branches/snapshot-work/includes/Snapshot.php
@@ -1,11 +1,22 @@
22 <?php
33
44 class Snapshot {
 5+ /**
 6+ * Create an empty snapshot object
 7+ */
58 function __construct() {
69 $this->mRevs = array();
 10+ $this->mRevisionId = 0;
711 }
812
913 /**
 14+ * Get the revision ID for the primary page resources, if stored.
 15+ */
 16+ public function getSnapRevision() {
 17+ return $this->mRevisionId;
 18+ }
 19+
 20+ /**
1021 * Get the revision number for a given page resource as embedded
1122 * in the snapshot. Note that this particular revision may no
1223 * longer exist, for instance if the referred page has been
@@ -26,6 +37,19 @@
2738 }
2839
2940 /**
 41+ * Store a page title <-> revision relationship in the snapshot
 42+ * object. If saved to the database, this can be used in future
 43+ * to pull the same versions of resources originally used.
 44+ *
 45+ * @param Title $title
 46+ * @param int $revision
 47+ */
 48+ public function addPage( $title, $revision ) {
 49+ $key = $title->getPrefixedDbKey();
 50+ $this->mRevs[$key] = $revision;
 51+ }
 52+
 53+ /**
3054 * Load a particular snapshot out of the database.
3155 */
3256 public static function newFromId( $snapId ) {
@@ -43,40 +67,99 @@
4468 'snap_page' => $pageId,
4569 'snap_tag' => $tag
4670 ),
47 - array( 'ORDER BY' => 'snap_timestamp DESC' ) );
 71+ array(
 72+ 'ORDER BY' => 'snap_timestamp DESC',
 73+ 'LIMIT' => 1
 74+ )
 75+ );
4876 }
4977
 78+ /**
 79+ * Tag and save a page snapshot state to the database.
 80+ * Returns the snapshot ID number.
 81+ * @param int $pageId
 82+ * @param string $tag
 83+ * @return int
 84+ */
 85+ public function insertTag( $pageId, $revId, $tag ) {
 86+ global $wgUser;
 87+ $dbw = wfGetDB( DB_MASTER );
 88+ $dbw->begin();
 89+ $dbw->insert( 'snapshot',
 90+ array(
 91+ 'snap_page' => $pageId,
 92+ 'snap_rev' => $revId,
 93+ 'snap_tag' => $tag,
 94+ 'snap_timestamp' => $dbw->timestamp(),
 95+ 'snap_user' => $wgUser->getId(),
 96+ ),
 97+ __METHOD__ );
 98+ $snapId = $dbw->insertId();
 99+
 100+ if( $this->mRevs ) {
 101+ $data = array();
 102+ foreach( $this->mRevs as $page => $rev ) {
 103+ // @fixme: hack
 104+ $title = Title::newFromText( $page );
 105+ $data[] = array(
 106+ 'sr_snap' => $snapId,
 107+ 'sr_namespace' => $title->getNamespace(),
 108+ 'sr_title' => $title->getDbKey(),
 109+ 'sr_rev' => $rev
 110+ );
 111+ }
 112+ $dbw->insert( 'snapshot_revs', $data, __METHOD__ );
 113+ }
 114+ $dbw->commit();
 115+
 116+ return $snapId;
 117+ }
50118
 119+
51120 /**
52121 * Load a Snapshot object from a particluar snapshot ID
 122+ * @fixme fallback to master
53123 */
54 - private static function fetchBy( $where, $options ) {
55 - $dbr = wfGetDB( DB_SLAVE );
56 - $revs = Snapshot::loadFromId( $dbr, $id );
 124+ private static function fetchBy( $where, $options=array() ) {
 125+ $db = wfGetDB( DB_SLAVE );
 126+ $row = $db->selectRow( 'snapshot',
 127+ array(
 128+ 'snap_id',
 129+ 'snap_page',
 130+ 'snap_rev',
 131+ 'snap_tag',
 132+ 'snap_timestamp',
 133+ 'snap_user'
 134+ ),
 135+ $where,
 136+ __METHOD__,
 137+ $options );
57138
58 - if( !$revs ) {
59 - $dbw = wfGetDB( DB_MASTER );
60 - $revs = Snapshot::loadFromId( $dbw, $id );
 139+ if( $row ) {
 140+ $snap = new Snapshot();
 141+ $snap->mRevisionId = $row->snap_rev;
 142+ $snap->linksFromId( $db, $row->snap_id );
 143+ return $snap;
61144 }
62 -
63 - $this->mRevs = $revs;
64145 }
65146
66 - private static function loadFromId( $db, $id ) {
 147+ /**
 148+ * Load frozen snapshot version data from database into this object
 149+ * @param Database $db
 150+ * @param int $id snapshot id
 151+ */
 152+ private function linksFromId( $db, $id ) {
67153 $result = $db->select( 'snapshot_revs',
68154 array( 'sr_namespace', 'sr_title', 'sr_rev' ),
69155 array( 'sr_snap' => $id ),
70156 __METHOD__ );
71157
72 - $revs = array();
73158 while( $row = $db->fetchObject( $result ) ) {
74159 $title = Title::makeTitle( $row->sr_namespace, $row->sr_title );
75 - $key = $title->getPrefixedDbKey();
76 - $revs[$key] = $row->sr_rev;
 160+ $this->addPage( $title, $row->sr_rev );
77161 }
78162
79163 $db->freeResult( $result );
80 - return $revs;
81164 }
82165 }
83166
Index: branches/snapshot-work/includes/SpecialPage.php
@@ -129,6 +129,7 @@
130130 'Revisiondelete' => array( 'SpecialPage', 'Revisiondelete', 'deleterevision' ),
131131 'Unusedtemplates' => array( 'SpecialPage', 'Unusedtemplates' ),
132132 'Randomredirect' => array( 'SpecialPage', 'Randomredirect' ),
 133+ 'Snapshot' => array( 'SpecialPage', 'Snapshot', 'snapshot' ),
133134 );
134135
135136 static public $mListInitialised = false;