Index: branches/snapshot-work/maintenance/tables.sql |
— | — | @@ -1018,6 +1018,9 @@ |
1019 | 1019 | -- Key to page_id of the page being tagged |
1020 | 1020 | snap_page INT, |
1021 | 1021 | |
| 1022 | + -- Key to rev_id of the primary page tagged |
| 1023 | + snap_rev INT, |
| 1024 | + |
1022 | 1025 | -- Tag identifier; machine-readable such as "reviewed" |
1023 | 1026 | -- There may be multiple versions with a given tag on a given page, |
1024 | 1027 | -- for updatable states where the latest with a given tag will be shown. |
Index: branches/snapshot-work/includes/Article.php |
— | — | @@ -717,6 +717,26 @@ |
718 | 718 | } |
719 | 719 | |
720 | 720 | $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 | + |
721 | 741 | if ( $pcache ) { |
722 | 742 | if ( $wgOut->tryParserCache( $this, $wgUser ) ) { |
723 | 743 | $outputDone = true; |
Index: branches/snapshot-work/includes/Parser.php |
— | — | @@ -271,10 +271,12 @@ |
272 | 272 | $this->mOptions = $options; |
273 | 273 | $this->mTitle =& $title; |
274 | 274 | |
275 | | - if( $revid != null ) { |
| 275 | + $oldRevisionId = $this->mRevisionId; |
| 276 | + if( $revid !== null ) { |
276 | 277 | $this->mRevisionId = $revid; |
277 | 278 | } |
278 | | - if( $snapshot != null ) { |
| 279 | + $oldSnapshot = $this->mSnapshot; |
| 280 | + if( $snapshot !== null ) { |
279 | 281 | $this->mSnapshot = $snapshot; |
280 | 282 | } |
281 | 283 | |
— | — | @@ -358,6 +360,8 @@ |
359 | 361 | wfRunHooks( 'ParserAfterTidy', array( &$this, &$text ) ); |
360 | 362 | |
361 | 363 | $this->mOutput->setText( $text ); |
| 364 | + $this->mSnapshot = $oldSnapshot; |
| 365 | + $this->mRevisionId = $oldRevisionId; |
362 | 366 | wfProfileOut( $fname ); |
363 | 367 | |
364 | 368 | 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 |
1 | 50 | + native |
Index: branches/snapshot-work/includes/OutputPage.php |
— | — | @@ -51,6 +51,7 @@ |
52 | 52 | $this->mScripts = ''; |
53 | 53 | $this->mETag = false; |
54 | 54 | $this->mRevisionId = null; |
| 55 | + $this->mSnapshot = null; |
55 | 56 | $this->mNewSectionLink = false; |
56 | 57 | } |
57 | 58 | |
— | — | @@ -274,6 +275,16 @@ |
275 | 276 | } |
276 | 277 | |
277 | 278 | /** |
| 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 | + /** |
278 | 289 | * Convert wikitext to HTML and add it to the buffer |
279 | 290 | * Default assumes that the current page title will |
280 | 291 | * be used. |
— | — | @@ -322,7 +333,8 @@ |
323 | 334 | |
324 | 335 | $this->mParserOptions->setTidy(true); |
325 | 336 | $parserOutput = $wgParser->parse( $text, $article->mTitle, |
326 | | - $this->mParserOptions, true, true, $this->mRevisionId ); |
| 337 | + $this->mParserOptions, true, true, $this->mRevisionId, |
| 338 | + $this->mSnapshot ); |
327 | 339 | $this->mParserOptions->setTidy(false); |
328 | 340 | if ( $cache && $article && $parserOutput->getCacheTime() != -1 ) { |
329 | 341 | $parserCache =& ParserCache::singleton(); |
Index: branches/snapshot-work/includes/DefaultSettings.php |
— | — | @@ -910,6 +910,7 @@ |
911 | 911 | $wgGroupPermissions['sysop']['reupload-shared'] = true; |
912 | 912 | $wgGroupPermissions['sysop']['unwatchedpages'] = true; |
913 | 913 | $wgGroupPermissions['sysop']['autoconfirmed'] = true; |
| 914 | +$wgGroupPermissions['sysop']['snapshot'] = true; |
914 | 915 | |
915 | 916 | // Permission to change users' group assignments |
916 | 917 | $wgGroupPermissions['bureaucrat']['userrights'] = true; |
Index: branches/snapshot-work/includes/Snapshot.php |
— | — | @@ -1,11 +1,22 @@ |
2 | 2 | <?php |
3 | 3 | |
4 | 4 | class Snapshot { |
| 5 | + /** |
| 6 | + * Create an empty snapshot object |
| 7 | + */ |
5 | 8 | function __construct() { |
6 | 9 | $this->mRevs = array(); |
| 10 | + $this->mRevisionId = 0; |
7 | 11 | } |
8 | 12 | |
9 | 13 | /** |
| 14 | + * Get the revision ID for the primary page resources, if stored. |
| 15 | + */ |
| 16 | + public function getSnapRevision() { |
| 17 | + return $this->mRevisionId; |
| 18 | + } |
| 19 | + |
| 20 | + /** |
10 | 21 | * Get the revision number for a given page resource as embedded |
11 | 22 | * in the snapshot. Note that this particular revision may no |
12 | 23 | * longer exist, for instance if the referred page has been |
— | — | @@ -26,6 +37,19 @@ |
27 | 38 | } |
28 | 39 | |
29 | 40 | /** |
| 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 | + /** |
30 | 54 | * Load a particular snapshot out of the database. |
31 | 55 | */ |
32 | 56 | public static function newFromId( $snapId ) { |
— | — | @@ -43,40 +67,99 @@ |
44 | 68 | 'snap_page' => $pageId, |
45 | 69 | 'snap_tag' => $tag |
46 | 70 | ), |
47 | | - array( 'ORDER BY' => 'snap_timestamp DESC' ) ); |
| 71 | + array( |
| 72 | + 'ORDER BY' => 'snap_timestamp DESC', |
| 73 | + 'LIMIT' => 1 |
| 74 | + ) |
| 75 | + ); |
48 | 76 | } |
49 | 77 | |
| 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 | + } |
50 | 118 | |
| 119 | + |
51 | 120 | /** |
52 | 121 | * Load a Snapshot object from a particluar snapshot ID |
| 122 | + * @fixme fallback to master |
53 | 123 | */ |
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 ); |
57 | 138 | |
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; |
61 | 144 | } |
62 | | - |
63 | | - $this->mRevs = $revs; |
64 | 145 | } |
65 | 146 | |
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 ) { |
67 | 153 | $result = $db->select( 'snapshot_revs', |
68 | 154 | array( 'sr_namespace', 'sr_title', 'sr_rev' ), |
69 | 155 | array( 'sr_snap' => $id ), |
70 | 156 | __METHOD__ ); |
71 | 157 | |
72 | | - $revs = array(); |
73 | 158 | while( $row = $db->fetchObject( $result ) ) { |
74 | 159 | $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 ); |
77 | 161 | } |
78 | 162 | |
79 | 163 | $db->freeResult( $result ); |
80 | | - return $revs; |
81 | 164 | } |
82 | 165 | } |
83 | 166 | |
Index: branches/snapshot-work/includes/SpecialPage.php |
— | — | @@ -129,6 +129,7 @@ |
130 | 130 | 'Revisiondelete' => array( 'SpecialPage', 'Revisiondelete', 'deleterevision' ), |
131 | 131 | 'Unusedtemplates' => array( 'SpecialPage', 'Unusedtemplates' ), |
132 | 132 | 'Randomredirect' => array( 'SpecialPage', 'Randomredirect' ), |
| 133 | + 'Snapshot' => array( 'SpecialPage', 'Snapshot', 'snapshot' ), |
133 | 134 | ); |
134 | 135 | |
135 | 136 | static public $mListInitialised = false; |