Index: branches/jhb/phase3/extensions/Makevalidate.class.php |
— | — | @@ -1,172 +0,0 @@ |
2 | | -<?php
|
3 | | -
|
4 | | -global $IP;
|
5 | | -require_once( "$IP/includes/LogPage.php" );
|
6 | | -require_once( "$IP/includes/SpecialLog.php" );
|
7 | | -
|
8 | | -class MakeValidate extends SpecialPage {
|
9 | | -
|
10 | | - var $target = '';
|
11 | | -
|
12 | | - /**
|
13 | | - * Constructor
|
14 | | - */
|
15 | | - function MakeValidate() {
|
16 | | - SpecialPage::SpecialPage( 'Makevalidate', 'makevalidate' );
|
17 | | - }
|
18 | | -
|
19 | | - /**
|
20 | | - * Main execution function
|
21 | | - * @param $par Parameters passed to the page
|
22 | | - */
|
23 | | - function execute( $par ) {
|
24 | | - global $wgRequest, $wgOut, $wgmakevalidatePrivileged, $wgUser;
|
25 | | -
|
26 | | - if( !$wgUser->isAllowed( 'makevalidate' ) ) {
|
27 | | - $wgOut->permissionRequired( 'makevalidate' );
|
28 | | - return;
|
29 | | - }
|
30 | | -
|
31 | | - $this->setHeaders();
|
32 | | -
|
33 | | - $this->target = $par
|
34 | | - ? $par
|
35 | | - : $wgRequest->getText( 'username', '' );
|
36 | | -
|
37 | | - $wgOut->addWikiText( wfMsgNoTrans( 'makevalidate-header' ) );
|
38 | | - $wgOut->addHtml( $this->makeSearchForm() );
|
39 | | -
|
40 | | - if( $this->target != '' ) {
|
41 | | - $wgOut->addHtml( wfElement( 'p', NULL, NULL ) );
|
42 | | - $user = User::newFromName( $this->target );
|
43 | | - if( is_object( $user ) && !is_null( $user ) ) {
|
44 | | - global $wgVersion;
|
45 | | - if( version_compare( $wgVersion, '1.9alpha' ) < 0 ) {
|
46 | | - $user->loadFromDatabase();
|
47 | | - } else {
|
48 | | - $user->load();
|
49 | | - }
|
50 | | - # Valid username, check existence
|
51 | | - if( $user->getID() ) {
|
52 | | - if( $wgRequest->getCheck( 'dosearch' ) || !$wgRequest->wasPosted() || !$wgUser->matchEditToken( $wgRequest->getVal( 'token' ), 'makevalidate' ) ) {
|
53 | | - # Exists, check reviewerness
|
54 | | - if( in_array( 'reviewer', $user->mGroups ) ) {
|
55 | | - # Has a reviewer flag
|
56 | | - $wgOut->addWikiText( wfMsg( 'makevalidate-isvalidator', $user->getName() ) );
|
57 | | - $wgOut->addHtml( $this->makeGrantForm( MW_MAKEVALIDATE_REVOKE ) );
|
58 | | - } else {
|
59 | | - # Not a reviewer; show the grant form
|
60 | | - $wgOut->addHtml( $this->makeGrantForm( MW_MAKEVALIDATE_GRANT ) );
|
61 | | - }
|
62 | | - } elseif( $wgRequest->getCheck( 'grant' ) ) {
|
63 | | - # Grant the flag
|
64 | | - $user->addGroup( 'reviewer' );
|
65 | | - $this->addLogItem( 'grant', $user, trim( $wgRequest->getText( 'comment' ) ) );
|
66 | | - $wgOut->addWikiText( wfMsg( 'makevalidate-granted', $user->getName() ) );
|
67 | | - } elseif( $wgRequest->getCheck( 'revoke' ) ) {
|
68 | | - # Revoke the flag
|
69 | | - $user->removeGroup( 'reviewer' );
|
70 | | - $this->addLogItem( 'revoke', $user, trim( $wgRequest->getText( 'comment' ) ) );
|
71 | | - $wgOut->addWikiText( wfMsg( 'makevalidate-revoked', $user->getName() ) );
|
72 | | - }
|
73 | | - # Show log entries
|
74 | | - $this->showLogEntries( $user );
|
75 | | - } else {
|
76 | | - # Doesn't exist
|
77 | | - $wgOut->addWikiText( wfMsg( 'nosuchusershort', htmlspecialchars( $this->target ) ) );
|
78 | | - }
|
79 | | - } else {
|
80 | | - # Invalid username
|
81 | | - $wgOut->addWikiText( wfMsg( 'noname' ) );
|
82 | | - }
|
83 | | - }
|
84 | | -
|
85 | | - }
|
86 | | -
|
87 | | - /**
|
88 | | - * Produce a form to allow for entering a username
|
89 | | - * @return string
|
90 | | - */
|
91 | | - function makeSearchForm() {
|
92 | | - $thisTitle = Title::makeTitle( NS_SPECIAL, $this->getName() );
|
93 | | - $form = wfOpenElement( 'form', array( 'method' => 'post', 'action' => $thisTitle->getLocalUrl() ) );
|
94 | | - $form .= wfElement( 'label', array( 'for' => 'username' ), wfMsg( 'makevalidate-username' ) ) . ' ';
|
95 | | - $form .= wfElement( 'input', array( 'type' => 'text', 'name' => 'username', 'id' => 'username', 'value' => $this->target ) ) . ' ';
|
96 | | - $form .= wfElement( 'input', array( 'type' => 'submit', 'name' => 'dosearch', 'value' => wfMsg( 'makevalidate-search' ) ) );
|
97 | | - $form .= wfCloseElement( 'form' );
|
98 | | - return $form;
|
99 | | - }
|
100 | | -
|
101 | | - /**
|
102 | | - * Produce a form to allow granting or revocation of the flag
|
103 | | - * @param $type Either MW_makevalidate_GRANT or MW_makevalidate_REVOKE
|
104 | | - * where the trailing name refers to what's enabled
|
105 | | - * @return string
|
106 | | - */
|
107 | | - function makeGrantForm( $type ) {
|
108 | | - global $wgUser;
|
109 | | - $thisTitle = Title::makeTitle( NS_SPECIAL, $this->getName() );
|
110 | | - if( $type == MW_MAKEVALIDATE_GRANT ) {
|
111 | | - $grant = true;
|
112 | | - $revoke = false;
|
113 | | - } else {
|
114 | | - $grant = false;
|
115 | | - $revoke = true;
|
116 | | - }
|
117 | | -
|
118 | | - # Start the table
|
119 | | - $form = wfOpenElement( 'form', array( 'method' => 'post', 'action' => $thisTitle->getLocalUrl() ) );
|
120 | | - $form .= wfOpenElement( 'table' ) . wfOpenElement( 'tr' );
|
121 | | - # Grant/revoke buttons
|
122 | | - $form .= wfElement( 'td', array( 'align' => 'right' ), wfMsg( 'makevalidate-change' ) );
|
123 | | - $form .= wfOpenElement( 'td' );
|
124 | | - foreach( array( 'grant', 'revoke' ) as $button ) {
|
125 | | - $attribs = array( 'type' => 'submit', 'name' => $button, 'value' => wfMsg( 'makevalidate-' . $button ) );
|
126 | | - if( !$$button )
|
127 | | - $attribs['disabled'] = 'disabled';
|
128 | | - $form .= wfElement( 'input', $attribs );
|
129 | | - }
|
130 | | - $form .= wfCloseElement( 'td' ) . wfCloseElement( 'tr' );
|
131 | | - # Comment field
|
132 | | - $form .= wfOpenElement( 'td', array( 'align' => 'right' ) );
|
133 | | - $form .= wfElement( 'label', array( 'for' => 'comment' ), wfMsg( 'makevalidate-comment' ) );
|
134 | | - $form .= wfOpenElement( 'td' );
|
135 | | - $form .= wfElement( 'input', array( 'type' => 'text', 'name' => 'comment', 'id' => 'comment', 'size' => 45 ) );
|
136 | | - $form .= wfCloseElement( 'td' ) . wfCloseElement( 'tr' );
|
137 | | - # End table
|
138 | | - $form .= wfCloseElement( 'table' );
|
139 | | - # Username
|
140 | | - $form .= wfElement( 'input', array( 'type' => 'hidden', 'name' => 'username', 'value' => $this->target ) );
|
141 | | - # Edit token
|
142 | | - $form .= wfElement( 'input', array( 'type' => 'hidden', 'name' => 'token', 'value' => $wgUser->editToken( 'makevalidate' ) ) );
|
143 | | - $form .= wfCloseElement( 'form' );
|
144 | | - return $form;
|
145 | | - }
|
146 | | -
|
147 | | - /**
|
148 | | - * Add logging entries for the specified action
|
149 | | - * @param $type Either grant or revoke
|
150 | | - * @param $target User receiving the action
|
151 | | - * @param $comment Comment for the log item
|
152 | | - */
|
153 | | - function addLogItem( $type, &$target, $comment = '' ) {
|
154 | | - $log = new LogPage( 'validate' );
|
155 | | - $targetPage = $target->getUserPage();
|
156 | | - $log->addEntry( $type, $targetPage, $comment );
|
157 | | - }
|
158 | | -
|
159 | | - /**
|
160 | | - * Show the bot status log entries for the specified user
|
161 | | - * @param $user User to show the log for
|
162 | | - */
|
163 | | - function showLogEntries( &$user ) {
|
164 | | - global $wgOut;
|
165 | | - $title = $user->getUserPage();
|
166 | | - $wgOut->addHtml( wfElement( 'h2', NULL, htmlspecialchars( LogPage::logName( 'validate' ) ) ) );
|
167 | | - $logViewer = new LogViewer( new LogReader( new FauxRequest( array( 'page' => $title->getPrefixedText(), 'type' => 'validate' ) ) ) );
|
168 | | - $logViewer->showList( $wgOut );
|
169 | | - }
|
170 | | -
|
171 | | -}
|
172 | | -
|
173 | | -?>
|
Index: branches/jhb/phase3/extensions/FlaggedRevs.sql |
— | — | @@ -1,54 +0,0 @@ |
2 | | -
|
3 | | -
|
4 | | -CREATE TABLE /*$wgDBprefix*/flaggedrevs (
|
5 | | - fr_id int(10) NOT NULL auto_increment,
|
6 | | - fr_page_id int(10) NOT NULL,
|
7 | | - fr_rev_id int(10) NOT NULL,
|
8 | | - fr_acc int(2) NOT NULL,
|
9 | | - fr_dep int(2) NOT NULL,
|
10 | | - fr_sty int(2) NOT NULL,
|
11 | | - fr_user int(5) NOT NULL,
|
12 | | - fr_timestamp char(14) NOT NULL,
|
13 | | - fr_comment mediumblob default NULL,
|
14 | | -
|
15 | | - PRIMARY KEY fr_rev_id (fr_rev_id),
|
16 | | - UNIQUE KEY (fr_id),
|
17 | | - INDEX fr_page_rev (fr_page_id,fr_rev_id),
|
18 | | - INDEX fr_acc_dep_sty (fr_acc,fr_dep,fr_sty)
|
19 | | -) TYPE=InnoDB;
|
20 | | -
|
21 | | -CREATE TABLE /*$wgDBprefix*/flaggedtext (
|
22 | | - ft_id int(10) NOT NULL auto_increment,
|
23 | | - ft_rev_id int(10) NOT NULL,
|
24 | | - ft_text mediumblob NOT NULL default '',
|
25 | | -
|
26 | | - PRIMARY KEY ft_id (ft_id),
|
27 | | - UNIQUE KEY ft_rev_id (ft_rev_id)
|
28 | | -) TYPE=InnoDB;
|
29 | | -
|
30 | | -CREATE TABLE /*$wgDBprefix*/flaggedimages (
|
31 | | - fi_id int(10) NOT NULL auto_increment,
|
32 | | - fi_name varchar(255) NOT NULL,
|
33 | | - fi_rev_id int(10) NOT NULL,
|
34 | | -
|
35 | | - PRIMARY KEY (fi_name,fi_rev_id),
|
36 | | - UNIQUE KEY (fi_id),
|
37 | | - INDEX fi_name (fi_name)
|
38 | | -) TYPE=InnoDB;
|
39 | | -
|
40 | | -CREATE TABLE /*$wgDBprefix*/flaggedcache (
|
41 | | - fc_key char(255) binary NOT NULL default '',
|
42 | | - fc_cache mediumblob NOT NULL default '',
|
43 | | - fc_date char(14) NOT NULL,
|
44 | | -
|
45 | | - PRIMARY KEY fc_key (fc_key)
|
46 | | -) TYPE=InnoDB; |
\ No newline at end of file |
Index: branches/jhb/phase3/extensions/FlaggedRevsPage.body.php |
— | — | @@ -1,302 +0,0 @@ |
2 | | -<?php |
3 | | - |
4 | | -#(c) Joerg Baach, Aaron Schulz, 2007 GPL |
5 | | - |
6 | | -global $IP; |
7 | | -require_once( "$IP/includes/LogPage.php" ); |
8 | | -require_once( "$IP/includes/SpecialLog.php" ); |
9 | | - |
10 | | -class Revisionreview extends SpecialPage |
11 | | -{ |
12 | | - |
13 | | - function Revisionreview() { |
14 | | - SpecialPage::SpecialPage('Revisionreview', 'review'); |
15 | | - } |
16 | | - |
17 | | - function execute( $par ) { |
18 | | - global $wgRequest, $wgUser, $wgOut, $wgFlaggedRevComments; |
19 | | - |
20 | | - if( !$wgUser->isAllowed( 'review' ) ) { |
21 | | - $wgOut->permissionRequired( 'review' ); |
22 | | - return; |
23 | | - } |
24 | | - |
25 | | - $this->setHeaders(); |
26 | | - // Our target page |
27 | | - $this->target = $wgRequest->getText( 'target' ); |
28 | | - // Revision ID |
29 | | - $this->oldid = $wgRequest->getIntOrNull( 'oldid' ); |
30 | | - // Log comment |
31 | | - $this->comment = $wgRequest->getText( 'wpReason' ); |
32 | | - // Additional notes |
33 | | - $this->notes = ($wgFlaggedRevComments) ? $wgRequest->getText('wpNotes') : ''; |
34 | | - // Get our accuracy/quality array |
35 | | - $this->dimensions = array(); |
36 | | - $this->dimensions['acc'] = $wgRequest->getIntOrNull('accuracy'); |
37 | | - $this->dimensions['depth'] = $wgRequest->getIntOrNull('depth'); |
38 | | - $this->dimensions['style'] = $wgRequest->getIntOrNull('style'); |
39 | | - // Must be a valid page |
40 | | - $this->page = Title::newFromUrl( $this->target ); |
41 | | - if( is_null($this->page) || is_null($this->oldid) || !$this->page->isContentPage() ) { |
42 | | - $wgOut->showErrorPage( $this->page, 'notargettitle', 'notargettext' ); |
43 | | - return; |
44 | | - } |
45 | | - if( $wgRequest->wasPosted() ) { |
46 | | - $this->submit( $wgRequest ); |
47 | | - } else { |
48 | | - $this->showRevision( $wgRequest ); |
49 | | - } |
50 | | - } |
51 | | - |
52 | | - /** |
53 | | - * @param webrequest $request |
54 | | - */ |
55 | | - function showRevision( $request ) { |
56 | | - global $wgOut, $wgUser, $wgTitle, $wgFlaggedRevComments; |
57 | | - |
58 | | - $wgOut->addWikiText( wfMsgExt( 'revreview-selected', array('parsemag'), $this->page->getPrefixedText() ) ); |
59 | | - |
60 | | - $this->skin = $wgUser->getSkin(); |
61 | | - $rev = Revision::newFromTitle( $this->page, $this->oldid ); |
62 | | - // Check if rev exists |
63 | | - if( !isset( $rev ) ) { |
64 | | - $wgOut->showErrorPage( 'internalerror', 'notargettitle', 'notargettext' ); |
65 | | - return; |
66 | | - } |
67 | | - // Do not mess with deleted revisions |
68 | | - if ( $rev->mDeleted ) { |
69 | | - $wgOut->showErrorPage( 'internalerror', 'badarticleerror' ); |
70 | | - return; |
71 | | - } |
72 | | - $wgOut->addHtml( "<ul>" ); |
73 | | - $wgOut->addHtml( $this->historyLine( $rev ) ); |
74 | | - $wgOut->addHtml( "</ul>" ); |
75 | | - |
76 | | - $wgOut->addWikiText( wfMsgHtml( 'revreview-text' ) ); |
77 | | - |
78 | | - $this->accRadios = array( |
79 | | - array( 'revreview-acc-0', 'wpAcc1', 0 ), |
80 | | - array( 'revreview-acc-1', 'wpAcc2', 1 ), |
81 | | - array( 'revreview-acc-2', 'wpAcc3', 2 ), |
82 | | - array( 'revreview-acc-3', 'wpAcc4', 3 ) ); |
83 | | - $this->depthRadios = array( |
84 | | - array( 'revreview-depth-0', 'wpDepth1', 0 ), |
85 | | - array( 'revreview-depth-1', 'wpDepth2', 1 ), |
86 | | - array( 'revreview-depth-2', 'wpDepth3', 2 ), |
87 | | - array( 'revreview-depth-3', 'wpDepth4', 3 ) ); |
88 | | - $this->styleRadios = array( |
89 | | - array( 'revreview-style-0', 'wpStyle1', 0 ), |
90 | | - array( 'revreview-style-1', 'wpStyle2', 1 ), |
91 | | - array( 'revreview-style-2', 'wpStyle3', 2 ), |
92 | | - array( 'revreview-style-3', 'wpStyle4', 3 ) ); |
93 | | - $items = array( |
94 | | - wfInputLabel( wfMsgHtml( 'revreview-log' ), 'wpReason', 'wpReason', 60 ), |
95 | | - wfSubmitButton( wfMsgHtml( 'revreview-submit' ) ) ); |
96 | | - $hidden = array( |
97 | | - wfHidden( 'wpEditToken', $wgUser->editToken() ), |
98 | | - wfHidden( 'target', $this->page->getPrefixedText() ), |
99 | | - wfHidden( 'oldid', $this->oldid ) ); |
100 | | - |
101 | | - $action = $wgTitle->escapeLocalUrl( 'action=submit' ); |
102 | | - $form = "<form name='revisionreview' action='$action' method='post'>"; |
103 | | - $form .= '<fieldset><legend>' . wfMsgHtml( 'revreview-legend' ) . '</legend><table><tr>'; |
104 | | - $form .= '<td><strong>' . wfMsgHtml( 'revreview-acc' ) . '</strong></td>'; |
105 | | - $form .= '<td width=\'25\'></td><td><strong>' . wfMsgHtml( 'revreview-depth' ) . '</strong></td>'; |
106 | | - $form .= '<td width=\'25\'></td><td><strong>' . wfMsgHtml( 'revreview-style' ) . '</strong></td>'; |
107 | | - $form .= '</tr><tr><td>'; |
108 | | - foreach( $this->accRadios as $item ) { |
109 | | - list( $message, $name, $field ) = $item; |
110 | | - $form .= "<div>" . |
111 | | - Xml::radio( 'accuracy', $field, ($field==$this->dimensions['acc']) ) . ' ' . wfMsgHtml($message) . |
112 | | - "</div>\n"; |
113 | | - } |
114 | | - $form .= '<td width=\'25\'></td></td><td>'; |
115 | | - foreach( $this->depthRadios as $item ) { |
116 | | - list( $message, $name, $field ) = $item; |
117 | | - $form .= "<div>" . |
118 | | - Xml::radio( 'depth', $field, ($field==$this->dimensions['depth']) ) . ' ' . wfMsgHtml($message) . |
119 | | - "</div>\n"; |
120 | | - } |
121 | | - $form .= '<td width=\'25\'></td></td><td>'; |
122 | | - foreach( $this->styleRadios as $item ) { |
123 | | - list( $message, $name, $field ) = $item; |
124 | | - $form .= "<div>" . |
125 | | - Xml::radio( 'style', $field, ($field==$this->dimensions['style']) ) . ' ' . wfMsgHtml($message) . |
126 | | - "</div>\n"; |
127 | | - } |
128 | | - $form .= '</td></tr></table></fieldset>'; |
129 | | - |
130 | | - list($images,$thumbs) = FlaggedRevs::findLocalImages( FlaggedRevs::expandText( $rev->getText() ) ); |
131 | | - if ( $images ) { |
132 | | - $form .= wfMsg('revreview-images') . "\n"; |
133 | | - $form .= "<ul>"; |
134 | | - $imglist = ''; |
135 | | - foreach ( $images as $image ) { |
136 | | - $imglist .= "<li>" . $this->skin->makeKnownLink( $image ) . "</li>\n"; |
137 | | - } |
138 | | - $form .= $imglist; |
139 | | - $form .= "</ul>\n"; |
140 | | - } |
141 | | - if ( $wgFlaggedRevComments ) { |
142 | | - $form .= "<fieldset><legend>" . wfMsgHtml( 'revreview-notes' ) . "</legend>" . |
143 | | - "<textarea tabindex='1' name='wpNotes' id='wpNotes' rows='3' cols='80' style='width:100%'></textarea>" . |
144 | | - "</fieldset>"; |
145 | | - } |
146 | | - |
147 | | - foreach( $items as $item ) { |
148 | | - $form .= '<p>' . $item . '</p>'; |
149 | | - } |
150 | | - foreach( $hidden as $item ) { |
151 | | - $form .= $item; |
152 | | - } |
153 | | - |
154 | | - $form .= '</form>'; |
155 | | - $wgOut->addHtml( $form ); |
156 | | - } |
157 | | - |
158 | | - /** |
159 | | - * @param Revision $rev |
160 | | - * @returns string |
161 | | - */ |
162 | | - function historyLine( $rev ) { |
163 | | - global $wgContLang; |
164 | | - $date = $wgContLang->timeanddate( $rev->getTimestamp() ); |
165 | | - |
166 | | - $difflink = '(' . $this->skin->makeKnownLinkObj( $this->page, wfMsgHtml('diff'), |
167 | | - '&diff=' . $rev->getId() . '&oldid=prev' ) . ')'; |
168 | | - |
169 | | - $revlink = $this->skin->makeLinkObj( $this->page, $date, 'oldid=' . $rev->getId() ); |
170 | | - |
171 | | - return |
172 | | - "<li> $difflink $revlink " . $this->skin->revUserLink( $rev ) . " " . $this->skin->revComment( $rev ) . "</li>"; |
173 | | - } |
174 | | - |
175 | | - function submit( $request ) { |
176 | | - global $wgOut; |
177 | | - |
178 | | - $rev = Revision::newFromTitle( $this->page, $this->oldid ); |
179 | | - // Do not mess with deleted revisions |
180 | | - if ( is_null($rev) || $rev->mDeleted ) { |
181 | | - $wgOut->showErrorPage( 'internalerror', 'badarticleerror' ); |
182 | | - return; |
183 | | - } |
184 | | - $approved = false; |
185 | | - # If all values are set to zero, this has been unnapproved |
186 | | - foreach( $this->dimensions as $quality => $value ) { |
187 | | - if( $value ) $approved = true; |
188 | | - } |
189 | | - $success = ( $approved ) ? $this->approveRevision( $rev ) : $this->unapproveRevision( $rev ); |
190 | | - // Return to our page |
191 | | - if ( $success ) { |
192 | | - $wgOut->redirect( $this->page->escapeLocalUrl() ); |
193 | | - } else { |
194 | | - $wgOut->showErrorPage( 'internalerror', 'badarticleerror' ); |
195 | | - } |
196 | | - } |
197 | | - |
198 | | - /** |
199 | | - * @param Revision $rev |
200 | | - * Adds or updates the flagged revision table for this page/id set |
201 | | - */ |
202 | | - function approveRevision( $rev=NULL ) { |
203 | | - global $wgUser; |
204 | | - if( is_null($rev) ) return false; |
205 | | - |
206 | | - wfProfileIn( __METHOD__ ); |
207 | | - |
208 | | - $db = wfGetDB( DB_MASTER ); |
209 | | - $user = $wgUser->getId(); |
210 | | - $timestamp = wfTimestampNow(); |
211 | | - |
212 | | - $cache_text = FlaggedRevs::expandText( $rev->getText() ); |
213 | | - // Add or update entry for this revision |
214 | | - $set = array( |
215 | | - 'fr_page_id' => $rev->getPage(), |
216 | | - 'fr_rev_id' => $rev->getId(), |
217 | | - 'fr_acc' => $this->dimensions['acc'], |
218 | | - 'fr_dep' => $this->dimensions['depth'], |
219 | | - 'fr_sty' => $this->dimensions['style'], |
220 | | - 'fr_user' => $user, |
221 | | - 'fr_timestamp' => $timestamp, |
222 | | - 'fr_comment'=> $this->notes |
223 | | - ); |
224 | | - $set2 = array('ft_rev_id' => $rev->getId(), 'ft_text' => $cache_text); |
225 | | - // Update flagrevisions table |
226 | | - $db->replace( 'flaggedrevs', array( array('fr_page_id','fr_rev_id') ), $set, __METHOD__ ); |
227 | | - // Store/update the text |
228 | | - $db->replace( 'flaggedtext', array('ft_rev_id'), $set2, __METHOD__ ); |
229 | | - // Update the article review log |
230 | | - $this->updateLog( $this->page, $this->dimensions, $this->comment, $this->oldid, true ); |
231 | | - // Clone images to stable dir |
232 | | - list($images,$thumbs) = FlaggedRevs::findLocalImages( $cache_text ); |
233 | | - $copies = FlaggedRevs::makeStableImages( $images ); |
234 | | - FlaggedRevs::purgeStableThumbnails( $thumbs ); |
235 | | - // Update stable image table |
236 | | - FlaggedRevs::insertStableImages( $rev->getId(), $copies ); |
237 | | - // Clear cache... |
238 | | - $this->page->invalidateCache(); |
239 | | - return true; |
240 | | - } |
241 | | - |
242 | | - /** |
243 | | - * @param Revision $rev |
244 | | - * Removes flagged revision data for this page/id set |
245 | | - */ |
246 | | - function unapproveRevision( $rev=NULL ) { |
247 | | - global $wgUser; |
248 | | - |
249 | | - if( is_null($rev) ) return false; |
250 | | - $db = wfGetDB( DB_MASTER ); |
251 | | - $user = $wgUser->getId(); |
252 | | - $timestamp = wfTimestampNow(); |
253 | | - // get the flagged revision to access its cache text |
254 | | - $frev = FlaggedRevs::getFlaggedRevision( $rev->getId() ); |
255 | | - if( !$frev ) { |
256 | | - // Quitly ignore this... |
257 | | - return true; |
258 | | - } |
259 | | - $db->delete( 'flaggedrevs', array( 'fr_rev_id' => $rev->getId ) ); |
260 | | - // Update the article review log |
261 | | - $this->updateLog( $this->page, $this->dimensions, $this->comment, $this->oldid, false ); |
262 | | - |
263 | | - $cache_text = FlaggedRevs::getFlaggedRevText( $rev->getId ) ; |
264 | | - // Delete stable images if needed |
265 | | - list($images,$thumbs) = FlaggedRevs::findLocalImages( $cache_text ); |
266 | | - $copies = FlaggedRevs::deleteStableImages( $images ); |
267 | | - // Stable versions must remake this thumbnail |
268 | | - FlaggedRevs::purgeStableThumbnails( $thumbs ); |
269 | | - // Update stable image table |
270 | | - FlaggedRevs::removeStableImages( $rev->getId(), $copies ); |
271 | | - // Clear cache... |
272 | | - $this->page->invalidateCache(); |
273 | | - return true; |
274 | | - } |
275 | | - |
276 | | - /** |
277 | | - * Record a log entry on the action |
278 | | - * @param Title $title |
279 | | - * @param array $dimensions |
280 | | - * @param string $comment |
281 | | - * @param int $revid |
282 | | - * @param bool $approve |
283 | | - */ |
284 | | - function updateLog( $title, $dimensions, $comment, $oldid, $approve ) { |
285 | | - $log = new LogPage( 'review' ); |
286 | | - // ID, accuracy, depth, style |
287 | | - $ratings = array(); |
288 | | - foreach( $dimensions as $quality => $level ) { |
289 | | - $ratings[] = wfMsg( "revreview-$quality" ) . ": " . wfMsg("revreview-$quality-$level"); |
290 | | - } |
291 | | - $rating = ($approve) ? ' [' . implode(', ',$ratings). ']' : ''; |
292 | | - // Append comment with action |
293 | | - $action = wfMsgExt('review-logaction', array('parsemag'), $oldid ); |
294 | | - $comment = ($comment) ? "$action: $comment$rating" : "$action $rating"; |
295 | | - |
296 | | - if ( $approve ) { |
297 | | - $log->addEntry( 'approve', $title, $comment ); |
298 | | - } else { |
299 | | - $log->addEntry( 'unapprove', $title, $comment ); |
300 | | - } |
301 | | - } |
302 | | -} |
303 | | -?> |
Index: branches/jhb/phase3/extensions/FlaggedRevsPage.i18n.php |
— | — | @@ -1,66 +0,0 @@ |
2 | | -<?php |
3 | | -$RevisionreviewMessages = array(); |
4 | | - |
5 | | -// English (Aaron Schulz) |
6 | | -$RevisionreviewMessages['en'] = array( |
7 | | - 'reviewer' => 'Reviewer', |
8 | | - 'group-reviewer' => 'Reviewers', |
9 | | - 'group-reviewer' => 'Reviewer', |
10 | | - 'grouppage-reviewer' => '{{ns:project}}:Reviewer', |
11 | | - |
12 | | - 'revreview-noflagged' => 'There are no reviewed revisions of this page, so it has \'\'\'not\'\'\' been |
13 | | - [[Help:Article validation|checked]] for quality.', |
14 | | - 'revreview-isnewest' => 'This is the latest [[Help:Article validation|reviewed]] revision of this page (with |
15 | | - updated images and templates) [{{fullurl:Special:Log/review|page={{FULLPAGENAMEE}}}} approved] on <i>$1</i>.', |
16 | | - 'revreview-newest' => 'The [{{fullurl:{{FULLPAGENAMEE}}|oldid=$1}} latest reviewed revision] |
17 | | - ([{{fullurl:{{FULLPAGENAMEE}}|oldid=$2&diff=$3}} compare]) was [{{fullurl:Special:Log/review|page={{FULLPAGENAMEE}}}} approved] |
18 | | - on <i>$4</i>, rated as:', |
19 | | - 'revreview-replaced' => 'This is the latest [[Help:Article validation|reviewed]] revision of this page, |
20 | | - [{{fullurl:Special:Log/review|page={{FULLPAGENAMEE}}}} approved] on <i>$4</i>. The [{{fullurl:{{FULLPAGENAMEE}}|oldid=$2}} current revision] |
21 | | - is editable and may be more up to date. There {{plural:$3|is $3 revision|are $3 revisions}} |
22 | | - ([{{fullurl:{{FULLPAGENAMEE}}|oldid=$1&diff=$2}} changes]) awaiting review.', |
23 | | - |
24 | | - 'revisionreview' => 'Review revisions', |
25 | | - |
26 | | - 'flaggedrevs' => 'Flagged Revisions', |
27 | | - 'review-logpage' => 'Article review log', |
28 | | - 'review-logpagetext' => 'This is a log of changes to revisions\' [[Help:Article validation|approval]] status |
29 | | - for content pages.', |
30 | | - 'review-logentrygrant' => 'approved [[$1]]', |
31 | | - 'review-logentryrevoke' => 'unapproved [[$1]]', |
32 | | - 'review-logaction' => 'reviewed revision $1', |
33 | | - |
34 | | - 'revreview-selected' => "Selected revision of '''$1:'''", |
35 | | - 'revreview-text' => "Approved revisions are set as the default revision shown upon page view rather |
36 | | - than the top revision. The content of this approved revision will remain constant regardless of any transcluded |
37 | | - pages or internal images. Users on this wiki will still be able to access |
38 | | - unreviewed content through the page history.", |
39 | | - 'revreview-images' => 'Internal images on this page will be copied to the stable image directory, updating |
40 | | - existing versions, and stored there until no reviewed revisions use them. The following images are transcluded onto this page:', |
41 | | - |
42 | | - 'revreview-hist' => '[reviewed]', |
43 | | - |
44 | | - 'revreview-note' => '[[User:$1]] made the following notes [[Help:Article validation|reviewing]] this revision:', |
45 | | - |
46 | | - 'revreview-flag' => 'Review this revision (#$1):', |
47 | | - 'revreview-legend' => 'Rate revision content:', |
48 | | - 'revreview-notes' => 'Observations or notes to display:', |
49 | | - 'revreview-acc' => 'Accuracy', |
50 | | - 'revreview-acc-0' => 'Unapproved', |
51 | | - 'revreview-acc-1' => 'Not vandalized', |
52 | | - 'revreview-acc-2' => 'Accurate', |
53 | | - 'revreview-acc-3' => 'Well sourced', |
54 | | - 'revreview-depth' => 'Depth', |
55 | | - 'revreview-depth-0' => 'Unapproved', |
56 | | - 'revreview-depth-1' => 'Stub', |
57 | | - 'revreview-depth-2' => 'Moderate', |
58 | | - 'revreview-depth-3' => 'Complete', |
59 | | - 'revreview-style' => 'Readability', |
60 | | - 'revreview-style-0' => 'Unapproved', |
61 | | - 'revreview-style-1' => 'Acceptable', |
62 | | - 'revreview-style-2' => 'Good', |
63 | | - 'revreview-style-3' => 'Concise', |
64 | | - 'revreview-log' => 'Log comment:', |
65 | | - 'revreview-submit' => 'Apply to selected revision', |
66 | | -); |
67 | | -?> |
\ No newline at end of file |
Index: branches/jhb/phase3/extensions/Makevalidate.i18n.php |
— | — | @@ -1,34 +0,0 @@ |
2 | | -<?php
|
3 | | -/**
|
4 | | - * Internationalisation file for makevalidate extension.
|
5 | | - *
|
6 | | - * @package MediaWiki
|
7 | | - * @subpackage Extensions
|
8 | | -*/
|
9 | | -
|
10 | | -function efMakeValidateMessages() {
|
11 | | - $messages = array();
|
12 | | -
|
13 | | -/* English (Aaron Schulz) */
|
14 | | -$messages['en'] = array(
|
15 | | - 'makevalidate' => 'Promote/demote reviewers',
|
16 | | - 'makevalidate-header' => '<strong>This form is used by bureaucrats to turn ordinary users into stable version validators.</strong><br> Type the name of the user in the box and press the button to make the user a validator.',
|
17 | | - 'makevalidate-username' => 'Name of the user:',
|
18 | | - 'makevalidate-search' => 'Go',
|
19 | | - 'makevalidate-isvalidator' => '[[User:$1|$1]] has reviewer status.',
|
20 | | - 'makevalidate-notvalidator' => '[[User:$1|$1]] does not have reviewer status.',
|
21 | | - 'makevalidate-change' => 'Change status:',
|
22 | | - 'makevalidate-grant' => 'Grant',
|
23 | | - 'makevalidate-revoke' => 'Revoke',
|
24 | | - 'makevalidate-comment' => 'Comment:',
|
25 | | - 'makevalidate-granted' => '[[User:$1|$1]] now has validator status.',
|
26 | | - 'makevalidate-revoked' => '[[User:$1|$1]] no longer has validator status.',
|
27 | | - 'makevalidate-logpage' => 'Reviewer status log',
|
28 | | - 'makevalidate-logpagetext' => 'This is a log of changes to users\' [[Help:Article validation|article validation]] status.',
|
29 | | - 'makevalidate-logentrygrant' => 'granted validator status to [[$1]]',
|
30 | | - 'makevalidate-logentryrevoke' => 'removed validator status from [[$1]]',
|
31 | | -);
|
32 | | -
|
33 | | -return $messages;
|
34 | | -}
|
35 | | -?>
|
Index: branches/jhb/phase3/extensions/specs.txt |
— | — | @@ -1,94 +0,0 @@ |
2 | | -== The implementation == |
3 | | - |
4 | | -The specs to implement are a variation of the proposal by Philipp and |
5 | | -other German Wikipedians, found at: |
6 | | -http://de.wikipedia.org/wiki/Wikipedia:Gesichtete_Versionen |
7 | | -http://de.wikipedia.org/wiki/Wikipedia:Gepr%C3%BCfte_Versionen |
8 | | - |
9 | | -The changes are aimed mostly at making the feature more flexibly |
10 | | -configurable, and streamlining some of the UI tasks. |
11 | | - |
12 | | -The feature is to be implemented as an extension, if at all possible. |
13 | | -It must be GPL-licensed and will be committed to the Wikimedia |
14 | | -Subversion server. |
15 | | - |
16 | | -The wiki will allow the configuration of "revision tags", which can |
17 | | -then be associated with any revision of an article. Tags are organized |
18 | | -in tag array, where each array represents a set of tags that describe |
19 | | -a similar attribute, e.g., accuracy-related tags would be organized in |
20 | | -one array, while those used for flagging materials for export might be |
21 | | -organized in another. This is to ensure that "levels" of quality |
22 | | -(unvandalized -> reviewed for accuracy -> featured article etc.) can |
23 | | -be represented correctly. |
24 | | - |
25 | | -Each tag has certain attributes: |
26 | | -- tag comments: Should the tag support a comment field which explains |
27 | | -why the revision was tagged in a certain way? |
28 | | -- permissions: Which user groups have permission to set the tag? |
29 | | -- stylesheet and UI: not requiring explicit configuration, each tag |
30 | | -should have a stylesheet class and MediaWiki: namespace message(s) |
31 | | -associated with it, to allow for differences in visual and textual |
32 | | -representation |
33 | | -- implicit tagging: should this tag be implicitly set by any user |
34 | | -within the associated group when editing? (this will be used for the |
35 | | -"non-vandalized" tag) |
36 | | - |
37 | | -A generic change is required to allow for automatic membership in a |
38 | | -user group when a user has more than X edits and is older than Y days. |
39 | | - |
40 | | -There should be a configuration option which associates a namespace with |
41 | | -- a tag-array |
42 | | -- a minimum level in that array. |
43 | | - |
44 | | -This option determines that, by default, the revision shown from this |
45 | | -namespace will be the one from that array which is also >= the minimum |
46 | | -level. So, for instance, one could determine that pages in the article |
47 | | -namespace need to be at least checked for vandalism, or at least |
48 | | -reviewed for accuracy. |
49 | | - |
50 | | -As a high priority wishlist item, these view options should also be |
51 | | -applicable on a per-page basis. The existing protection UI should be |
52 | | -expanded for this purpose. Implementation (and schedule) of this item |
53 | | -will depend on overall implementation progress. |
54 | | - |
55 | | -Whichever view one ends up with, we expect that the top of the page |
56 | | -indicates this, and allows you to switch & get diffs to other views. |
57 | | - |
58 | | -Because MediaWiki currently does not show templates and revisions in |
59 | | -time synchronization, this behavior has to be fixed for old revisions. |
60 | | -When one has expressed a preference for a revision with a specific |
61 | | -tag/level (e.g. "unvandalized") AND this is the most recent revision, |
62 | | -it will be shown together with the most recent equally tagged |
63 | | -templates if they exist, otherwise with the most recent ones. |
64 | | - |
65 | | -Example: On a page like the Main Page, which includes many templates, |
66 | | -one would typically want to have an unvandalized view of the entire |
67 | | -construction. The Main Page itself rarely changes, but because the |
68 | | -most recent revision is flagged as "unvandalized", it would be |
69 | | -synchronized with templates for which this is also true. When viewing |
70 | | -an older version, however, the templates would be shown as they were |
71 | | -at that date&time. |
72 | | - |
73 | | -It is crucial that queries for revision lookup are highly performant; |
74 | | -we should aim for a performance impact of less than 10% on uncached |
75 | | -pageviews with a revision tag preference. Needless to say, the feature |
76 | | -needs to interact correctly with Squid proxy caching. |
77 | | - |
78 | | -Tagging revisions should be possible from three places: |
79 | | -- when editing (with the help of a collapsible diff) |
80 | | -- when looking at diffs |
81 | | -- when looking at revisions without any prior or later tags. |
82 | | - |
83 | | -A tag can only be set with reference to a diff to the last version |
84 | | -that has the same tag. The Special:Recentchanges tool should be |
85 | | -customizable to have such a diff link to the last version with a given |
86 | | -tag. It is desirable that this view also includes an icon that |
87 | | -indicates the state of the logged revision (derived from the tag |
88 | | -stylesheets). |
89 | | - |
90 | | -Wishlist items for the future include things like mass vandalism |
91 | | -review and advanced queries. I also have some ideas for phase II of |
92 | | -the project, which I would love to see implemented before the tool is |
93 | | -switched on on the English Wikipedia, but this will wait until phase I |
94 | | -and any adjustments needed for its operations are successfully |
95 | | -completed. |
Index: branches/jhb/phase3/extensions/SpecialMakevalidate.php |
— | — | @@ -1,65 +0,0 @@ |
2 | | -<?php
|
3 | | -
|
4 | | -/**
|
5 | | - * Special page to allow local bureaucrats to grant/revoke the reviewer flag
|
6 | | - * for a particular user
|
7 | | - *
|
8 | | - * @addtogroup Extensions
|
9 | | - * Tiny modifications by Aaron Schulz to MakeBot
|
10 | | - * MakeBot extension:
|
11 | | - ** @author Rob Church <robchur@gmail.com>
|
12 | | - ** @copyright © 2006 Rob Church
|
13 | | - ** @licence GNU General Public Licence 2.0 or later
|
14 | | - */
|
15 | | -
|
16 | | -if( defined( 'MEDIAWIKI' ) ) {
|
17 | | -
|
18 | | - define( 'MW_MAKEVALIDATE_GRANT', 1 );
|
19 | | - define( 'MW_MAKEVALIDATE_REVOKE', 2 );
|
20 | | -
|
21 | | - $wgExtensionFunctions[] = 'efMakevalidate';
|
22 | | - $wgAvailableRights[] = 'makevalidate';
|
23 | | - $wgExtensionCredits['specialpage'][] = array(
|
24 | | - 'name' => 'MakeBot',
|
25 | | - 'author' => 'Rob Church',
|
26 | | - 'url' => 'http://www.mediawiki.org/wiki/Extension:MakeBot',
|
27 | | - 'description' => 'Special page allows local bureaucrats to grant and revoke bot permissions',
|
28 | | - );
|
29 | | -
|
30 | | - /**
|
31 | | - * Determines who can use the extension; as a default, bureaucrats are permitted
|
32 | | - */
|
33 | | - $wgGroupPermissions['bureaucrat']['makevalidate'] = true;
|
34 | | -
|
35 | | - /**
|
36 | | - * Toggles whether or not a bot flag can be given to a user who is also a sysop or bureaucrat
|
37 | | - */
|
38 | | - $wgMakeBotPrivileged = false;
|
39 | | -
|
40 | | - /**
|
41 | | - * Register the special page
|
42 | | - */
|
43 | | - $wgAutoloadClasses['Makevalidate'] = dirname( __FILE__ ) . '/Makevalidate.class.php';
|
44 | | - $wgSpecialPages['Makevalidate'] = 'Makevalidate';
|
45 | | -
|
46 | | - /**
|
47 | | - * Populate the message cache and set up the auditing
|
48 | | - */
|
49 | | - function efMakeValidate() {
|
50 | | - global $wgMessageCache, $wgLogTypes, $wgLogNames, $wgLogHeaders, $wgLogActions;
|
51 | | - require_once( dirname( __FILE__ ) . '/Makevalidate.i18n.php' );
|
52 | | - foreach( efMakeValidateMessages() as $lang => $messages )
|
53 | | - $wgMessageCache->addMessages( $messages, $lang );
|
54 | | - $wgLogTypes[] = 'validate';
|
55 | | - $wgLogNames['validate'] = 'makevalidate-logpage';
|
56 | | - $wgLogHeaders['validate'] = 'makevalidate-logpagetext';
|
57 | | - $wgLogActions['validate/grant'] = 'makevalidate-logentrygrant';
|
58 | | - $wgLogActions['validate/revoke'] = 'makevalidate-logentryrevoke';
|
59 | | - }
|
60 | | -
|
61 | | -} else {
|
62 | | - echo( "This file is an extension to the MediaWiki software and cannot be used standalone.\n" );
|
63 | | - exit( 1 );
|
64 | | -}
|
65 | | -
|
66 | | -?>
|
Index: branches/jhb/phase3/extensions/FlaggedRevs.php |
— | — | @@ -1,766 +0,0 @@ |
2 | | -<? |
3 | | -#(c) Joerg Baach, Aaron Schulz 2007 GPL |
4 | | -/* |
5 | | -Possible Hooks |
6 | | - |
7 | | -'BeforePageDisplay': Called just before outputting a page (all kinds of, |
8 | | - articles, special, history, preview, diff, edit, ...) |
9 | | - Can be used to set custom CSS/JS |
10 | | -$out: OutputPage object |
11 | | - |
12 | | -'OutputPageBeforeHTML': a page has been processed by the parser and |
13 | | -the resulting HTML is about to be displayed. |
14 | | -$parserOutput: the parserOutput (object) that corresponds to the page |
15 | | -$text: the text that will be displayed, in HTML (string) |
16 | | -*/ |
17 | | - |
18 | | -if ( !defined( 'MEDIAWIKI' ) ) { |
19 | | - echo "FlaggedRevs extension\n"; |
20 | | - exit( 1 ); |
21 | | -} |
22 | | - |
23 | | -$wgExtensionFunctions[] = 'efLoadReviewMessages'; |
24 | | - |
25 | | -# Internationilization |
26 | | -function efLoadReviewMessages() { |
27 | | - global $wgMessageCache, $RevisionreviewMessages; |
28 | | - require( dirname( __FILE__ ) . '/FlaggedRevsPage.i18n.php' ); |
29 | | - foreach ( $RevisionreviewMessages as $lang => $langMessages ) { |
30 | | - $wgMessageCache->addMessages( $langMessages, $lang ); |
31 | | - } |
32 | | -} |
33 | | - |
34 | | -# Revision tagging can slow development... |
35 | | -# For example, the main user base may become complacent, |
36 | | -# treating flagged pages as "done", |
37 | | -# or just be too damn lazy to always click "current". |
38 | | -# We may just want non-user visitors to see reviewd pages by default. |
39 | | -$wgFlaggedRevsAnonOnly = true; |
40 | | -# Can users make comments that will show up below flagged revisions? |
41 | | -$wgFlaggedRevComments = true; |
42 | | -# How long to cache stable versions |
43 | | -$wgFlaggedRevsExpire = 7 * 24 * 3600; |
44 | | - |
45 | | -$wgAvailableRights[] = 'review'; |
46 | | -# Define our reviewer class |
47 | | -$wgGroupPermissions['reviewer']['rollback'] = true; |
48 | | -$wgGroupPermissions['reviewer']['patrol'] = true; |
49 | | -$wgGroupPermissions['reviewer']['review'] = true; |
50 | | - |
51 | | -# Add review log |
52 | | -$wgLogTypes[] = 'review'; |
53 | | -$wgLogNames['review'] = 'review-logpage'; |
54 | | -$wgLogHeaders['review'] = 'review-logpagetext'; |
55 | | -$wgLogActions['review/approve'] = 'review-logentrygrant'; |
56 | | -$wgLogActions['review/unapprove'] = 'review-logentryrevoke'; |
57 | | - |
58 | | -class FlaggedRevs { |
59 | | - /* 50MB allows fixing those huge pages */ |
60 | | - const MAX_INCLUDE_SIZE = 50000000; |
61 | | - |
62 | | - function __construct() { |
63 | | - $this->dimensions = array( 'acc' => array( 0=>'acc-0', |
64 | | - 1=>'acc-1', |
65 | | - 2=>'acc-2', |
66 | | - 3=>'acc-3'), |
67 | | - 'depth' => array( 0=>'depth-0', |
68 | | - 1=>'depth-1', |
69 | | - 2=>'depth-2', |
70 | | - 3=>'depth-3'), |
71 | | - 'style' => array( 0=>'style-0', |
72 | | - 1=>'style-1', |
73 | | - 2=>'style-2', |
74 | | - 3=>'style-3') ); |
75 | | - } |
76 | | - |
77 | | - function pageOverride() { |
78 | | - global $wgFlaggedRevsAnonOnly, $wgUser; |
79 | | - return !( $wgFlaggedRevsAnonOnly && !$wgUser->isAnon() ); |
80 | | - } |
81 | | - |
82 | | - function getFlaggedRevision( $rev_id ) { |
83 | | - $db = wfGetDB( DB_SLAVE ); |
84 | | - // select a row, this should be unique |
85 | | - $result = $db->select( 'flaggedrevs', array('*'), array('fr_rev_id' => $rev_id) ); |
86 | | - if( $row = $db->fetchObject($result) ) { |
87 | | - return $row; |
88 | | - } |
89 | | - return NULL; |
90 | | - } |
91 | | - |
92 | | - function getFlaggedRevText( $rev_id ) { |
93 | | - $db = wfGetDB( DB_SLAVE ); |
94 | | - // select a row, this should be unique |
95 | | - $result = $db->select( 'flaggedtext', array('ft_text'), array('ft_rev_id' => $rev_id) ); |
96 | | - if( $row = $db->fetchObject($result) ) { |
97 | | - return $row->ft_text; |
98 | | - } |
99 | | - return NULL; |
100 | | - } |
101 | | - |
102 | | - /** |
103 | | - * @param string $text |
104 | | - * @returns string |
105 | | - * All included pages are expanded out to keep this text frozen |
106 | | - */ |
107 | | - function expandText( $text ) { |
108 | | - global $wgParser, $wgTitle; |
109 | | - // Do not treat this as paring an article on normal view |
110 | | - // enter the title object as wgTitle |
111 | | - $options = new ParserOptions; |
112 | | - $options->setRemoveComments( true ); |
113 | | - $options->setMaxIncludeSize( self::MAX_INCLUDE_SIZE ); |
114 | | - $output = $wgParser->preprocess( $text, $wgTitle, $options ); |
115 | | - return $output; |
116 | | - } |
117 | | - |
118 | | - function getFlagsForRevision( $rev_id ) { |
119 | | - // Set default blank flags |
120 | | - $flags = array( 'acc' => 0, 'depth' => 0, 'style' => 0 ); |
121 | | - |
122 | | - $db = wfGetDB( DB_SLAVE ); |
123 | | - // select a row, this should be unique |
124 | | - $result = $db->select( 'flaggedrevs', array('*'), array('fr_rev_id' => $rev_id) ); |
125 | | - if( $row = $db->fetchObject($result) ) { |
126 | | - $flags = array( 'acc' => $row->fr_acc, 'depth' => $row->fr_dep, 'style' => $row->fr_sty ); |
127 | | - } |
128 | | - return $flags; |
129 | | - } |
130 | | - |
131 | | - function getLatestFlaggedRev( $page_id ) { |
132 | | - wfProfileIn( __METHOD__ ); |
133 | | - |
134 | | - $db = wfGetDB( DB_SLAVE ); |
135 | | - // Skip deleted revisions |
136 | | - $result = $db->select( |
137 | | - array('flaggedrevs', 'revision'), |
138 | | - array('*'), |
139 | | - array( 'fr_page_id' => $page_id, 'rev_id = fr_rev_id', 'rev_deleted = 0'), |
140 | | - __METHOD__ , |
141 | | - array('ORDER BY' => 'fr_rev_id DESC') ); |
142 | | - // Sorted from highest to lowest, so just take the first one if any |
143 | | - if ( $row = $db->fetchObject( $result ) ) { |
144 | | - return $row; |
145 | | - } |
146 | | - return NULL; |
147 | | - } |
148 | | - |
149 | | - function getReviewedRevs( $page_id ) { |
150 | | - wfProfileIn( __METHOD__ ); |
151 | | - |
152 | | - $db = wfGetDB( DB_SLAVE ); |
153 | | - $rows = array(); |
154 | | - // Skip deleted revisions |
155 | | - $result = $db->select( |
156 | | - array('flaggedrevs', 'revision'), |
157 | | - array('*'), |
158 | | - array( 'fr_page_id' => $page_id, 'rev_id = fr_rev_id', 'rev_deleted = 0'), |
159 | | - __METHOD__ , |
160 | | - array('ORDER BY' => 'fr_rev_id DESC') ); |
161 | | - while ( $row = $db->fetchObject( $result ) ) { |
162 | | - $rows[] = $row; |
163 | | - } |
164 | | - return $rows; |
165 | | - } |
166 | | - |
167 | | - function getUnreviewedRevCount( $page_id, $from_rev ) { |
168 | | - wfProfileIn( __METHOD__ ); |
169 | | - |
170 | | - $db = wfGetDB( DB_SLAVE ); |
171 | | - $result = $db->select( |
172 | | - array('revision'), |
173 | | - array('rev_page'), |
174 | | - array( 'rev_page' => $page_id, "rev_id > $from_rev" ), |
175 | | - __METHOD__ , |
176 | | - array('ORDER BY' => 'rev_id DESC') ); |
177 | | - // Return count of revisions |
178 | | - return $db->numRows($result); |
179 | | - } |
180 | | - |
181 | | - function parseStableText( $title, $text, $id=NULL, $options, $returnHTML=true ) { |
182 | | - global $wgUser, $wgParser, $wgUploadDirectory, $wgUseSharedUploads, $wgUploadPath; |
183 | | - # hack...temporarily change image directories |
184 | | - # There is no nice option to set this for each parse. |
185 | | - # This lets the parser know where to look... |
186 | | - $uploadPath = $wgUploadPath; |
187 | | - $uploadDir = $wgUploadDirectory; |
188 | | - $useSharedUploads = $wgUseSharedUploads; |
189 | | - # Stable thumbnails need to have the right path |
190 | | - $wgUploadPath = ($wgUploadPath) ? "{$uploadPath}/stable" : false; |
191 | | - # Create <img> tags with the right url |
192 | | - $wgUploadDirectory = "{$uploadDir}/stable"; |
193 | | - # Stable images are never stored at commons |
194 | | - $wgUseSharedUploads = false; |
195 | | - |
196 | | - $options->setTidy(true); |
197 | | - # Don't show section-edit links |
198 | | - # They can be old and misleading |
199 | | - $options->setEditSection(false); |
200 | | - # Parse the new body, wikitext -> html |
201 | | - $parserOut = $wgParser->parse( $text, $title, $options, true, true, $id ); |
202 | | - if ( !$returnHTML ) |
203 | | - return $parserOut; |
204 | | - |
205 | | - $HTMLout = $parserOut->getText(); |
206 | | - # hack... |
207 | | - $HTMLout = $this->proportionalImgScaling($HTMLout); |
208 | | - |
209 | | - # Reset our image directories |
210 | | - $wgUploadPath = $uploadPath; |
211 | | - $wgUploadDirectory = $uploadDir; |
212 | | - $wgUseSharedUploads = $useSharedUploads; |
213 | | - |
214 | | - return $HTMLout; |
215 | | - } |
216 | | - |
217 | | - function proportionalImgScaling( $text ) { |
218 | | - # goddamn hack... |
219 | | - # Thumbnails are stored based on width, don't do any unscaled resizing |
220 | | - # MW will add height/width based on the metadata in the db for the current image |
221 | | - $text = preg_replace( '/(<img[^<>]+ )height="\d+" ([^<>]+>)/i','$1$2', $text); |
222 | | - return $text; |
223 | | - } |
224 | | - |
225 | | - function setPageContent( &$out ) { |
226 | | - global $wgArticle, $wgRequest, $wgTitle, $wgOut, $action; |
227 | | - // Only trigger on article view, not for protect/delete/hist |
228 | | - // Talk pages cannot be validated |
229 | | - if( !$wgArticle || !$wgTitle->isContentPage() || $action !='view' ) |
230 | | - return; |
231 | | - // Find out revision id |
232 | | - $revid = ( $wgArticle->mRevision ) ? $wgArticle->mRevision->mId : $wgArticle->getLatest(); |
233 | | - // Grab the ratings for this revision if any |
234 | | - if( !$revid ) return; |
235 | | - $visible_id = $revid; |
236 | | - |
237 | | - // Set new body html text as that of now |
238 | | - $flaghtml = ''; $newbodytext = $out->mBodytext; |
239 | | - // Check the newest stable version |
240 | | - $top_frev = $this->getLatestFlaggedRev( $wgArticle->getId() ); |
241 | | - if( $wgRequest->getVal('diff') ) { |
242 | | - // Do not clutter up diffs any further... |
243 | | - } else if( $top_frev ) { |
244 | | - global $wgParser, $wgLang; |
245 | | - // Parse the timestamp |
246 | | - $time = $wgLang->timeanddate( wfTimestamp(TS_MW, $top_frev->fr_timestamp), true ); |
247 | | - // Grab the flags |
248 | | - $flags = $this->getFlagsForRevision( $top_frev->fr_rev_id ); |
249 | | - # Looking at some specific old rev or if flagged revs override only for anons |
250 | | - if( $wgRequest->getVal('oldid') || !$this->pageOverride() ) { |
251 | | - if( $revid==$top_frev->rev_id ) { |
252 | | - $flaghtml = wfMsgExt('revreview-isnewest', array('parse'),$time); |
253 | | - } else { |
254 | | - # Our compare link should have a reasonable time-ordered old->new combination |
255 | | - $oldid = ($revid > $top_frev->fr_rev_id) ? $top_frev->fr_rev_id : $revid; |
256 | | - $diff = ($revid > $top_frev->fr_rev_id) ? $revid : $top_frev->fr_rev_id; |
257 | | - $flaghtml = wfMsgExt('revreview-newest', array('parse'), $top_frev->fr_rev_id, $oldid, $diff, $time ); |
258 | | - } |
259 | | - } # Viewing the page normally: override the page |
260 | | - else { |
261 | | - global $wgUser; |
262 | | - # We will be looking at the reviewed revision... |
263 | | - $visible_id = $top_frev->fr_rev_id; |
264 | | - $revs_since = $this->getUnreviewedRevCount( $wgArticle->getId(), $visible_id ); |
265 | | - $flaghtml = wfMsgExt('revreview-replaced', array('parse'), $visible_id, $wgArticle->getLatest(), $revs_since, $time ); |
266 | | - $newbodytext = NULL; |
267 | | - # Try the stable cache |
268 | | - $newbodytext = $this->getPageCache( $wgArticle ); |
269 | | - # If no cache is available, get the text and parse it |
270 | | - if ( is_null($newbodytext) ) { |
271 | | - $text = $this->getFlaggedRevText( $visible_id ); |
272 | | - # For anons, use standard prefs, for users, get theirs |
273 | | - $options = ParserOptions::newFromUser($wgUser); |
274 | | - # Parsing this text is kind of funky... |
275 | | - $newbodytext = $this->parseStableText( $wgTitle, $text, $visible_id, $options ); |
276 | | - # Update the general cache for non-users |
277 | | - $this->updatePageCache( $wgArticle, $newbodytext ); |
278 | | - } |
279 | | - } |
280 | | - // Construct some tagging |
281 | | - $flaghtml .= "<table align='center' cellspadding=\'0\'><tr>"; |
282 | | - foreach ( $this->dimensions as $quality => $value ) { |
283 | | - $value = wfMsgHtml('revreview-' . $this->dimensions[$quality][$flags[$quality]]); |
284 | | - $flaghtml .= "<td> <strong>" . wfMsgHtml("revreview-$quality") . "</strong>: $value </td>\n"; |
285 | | - } |
286 | | - $flaghtml .= '</tr></table>'; |
287 | | - // Should use CSS? |
288 | | - $flaghtml = "<small>$flaghtml</small>"; |
289 | | - |
290 | | - // Set the new body HTML, place a tag on top |
291 | | - $out->mBodytext = '<div class="mw-warning plainlinks">' . $flaghtml . '</div>' . $newbodytext; |
292 | | - // Add any notes at the bottom |
293 | | - $this->addReviewNotes( $top_frev ); |
294 | | - } else { |
295 | | - $flaghtml = wfMsgExt('revreview-noflagged', array('parse')); |
296 | | - $out->mBodytext = '<div class="mw-warning plainlinks">' . $flaghtml . '</div>' . $out->mBodytext; |
297 | | - } |
298 | | - // Override our reference ID for permalink/citation hooks |
299 | | - $wgArticle->mRevision = Revision::newFromId( $visible_id ); |
300 | | - // Show review links for the VISIBLE revision |
301 | | - // We cannot review deleted revisions |
302 | | - if( is_object($wgArticle->mRevision) && $wgArticle->mRevision->mDeleted ) return; |
303 | | - // Add quick review links IF we did not override, otherwise, they might |
304 | | - // review a revision that parses out newer templates/images than what they say |
305 | | - // Note: overrides are never done when viewing with "oldid=" |
306 | | - if( $visible_id==$revid || !$this->pageOverride() ) { |
307 | | - $this->addQuickReview( $visible_id, false, $out ); |
308 | | - } |
309 | | - } |
310 | | - |
311 | | - function addToEditView( &$editform ) { |
312 | | - global $wgRequest, $wgTitle, $wgOut; |
313 | | - // Talk pages cannot be validated |
314 | | - if( !$editform->mArticle || !$wgTitle->isContentPage() ) |
315 | | - return; |
316 | | - // Find out revision id |
317 | | - if( $editform->mArticle->mRevision ) |
318 | | - $revid = $editform->mArticle->mRevision->mId; |
319 | | - else |
320 | | - $revid = $editform->mArticle->getLatest(); |
321 | | - // Grab the ratings for this revision if any |
322 | | - if( !$revid ) return; |
323 | | - |
324 | | - // Set new body html text as that of now |
325 | | - $flaghtml = ''; |
326 | | - // Check the newest stable version |
327 | | - $top_frev = $this->getLatestFlaggedRev( $editform->mArticle->getId() ); |
328 | | - if( is_object($top_frev) ) { |
329 | | - global $wgParser, $wgLang; |
330 | | - $time = $wgLang->timeanddate( wfTimestamp(TS_MW, $top_frev->fr_timestamp), true ); |
331 | | - $flags = $this->getFlagsForRevision( $top_frev->fr_rev_id ); |
332 | | - # Looking at some specific old rev |
333 | | - if( $wgRequest->getVal('oldid') ) { |
334 | | - if( $revid==$top_frev->rev_id ) { |
335 | | - $flaghtml = wfMsgExt('revreview-isnewest', array('parse'),$time); |
336 | | - } else { |
337 | | - # Our compare link should have a reasonable time-ordered old->new combination |
338 | | - $oldid = ($revid > $top_frev->fr_rev_id) ? $top_frev->fr_rev_id : $revid; |
339 | | - $diff = ($revid > $top_frev->fr_rev_id) ? $revid : $top_frev->fr_rev_id; |
340 | | - $flaghtml = wfMsgExt('revreview-newest', array('parse'), $top_frev->fr_rev_id, $oldid, $diff, $time ); |
341 | | - } |
342 | | - } # Editing the page normally |
343 | | - else { |
344 | | - if( $revid==$top_frev->rev_id ) |
345 | | - $flaghtml = wfMsgExt('revreview-isnewest', array('parse'), $time); |
346 | | - else |
347 | | - $flaghtml = wfMsgExt('revreview-newest', array('parse'), $top_frev->fr_rev_id, $top_frev->fr_rev_id, $revid, $time ); |
348 | | - } |
349 | | - // Construct some tagging |
350 | | - $flaghtml .= "<table align='center' cellpadding=\'0\'><tr>"; |
351 | | - foreach ( $this->dimensions as $quality => $value ) { |
352 | | - $value = wfMsgHtml('revreview-' . $this->dimensions[$quality][$flags[$quality]]); |
353 | | - $flaghtml .= "<td> <strong>" . wfMsgHtml("revreview-$quality") . "</strong>: $value </td>\n"; |
354 | | - } |
355 | | - $flaghtml .= '</tr></table>'; |
356 | | - // Should use CSS? |
357 | | - $flaghtml = "<small>$flaghtml</small>"; |
358 | | - $wgOut->addHTML( '<div class="mw-warning plainlinks">' . $flaghtml . '</div><br/>' ); |
359 | | - } |
360 | | - } |
361 | | - |
362 | | - function addToDiff( &$diff, &$oldrev, &$newrev ) { |
363 | | - $id = $newrev->getId(); |
364 | | - // We cannot review deleted edits |
365 | | - if( $newrev->mDeleted ) return; |
366 | | - $this->addQuickReview( $id, true ); |
367 | | - } |
368 | | - |
369 | | - function setCurrentTab( &$sktmp, &$content_actions ) { |
370 | | - global $wgRequest, $wgArticle, $action; |
371 | | - // Only trigger on article view, not for protect/delete/hist |
372 | | - // Non-content pages cannot be validated |
373 | | - if( !$wgArticle || !$sktmp->mTitle->exists() || !$sktmp->mTitle->isContentPage() || $action !='view' ) |
374 | | - return; |
375 | | - // If we are viewing a page normally, and it was overrode |
376 | | - // change the edit tab to a "current revision" tab |
377 | | - if( !$wgRequest->getVal('oldid') ) { |
378 | | - $top_frev = $this->getLatestFlaggedRev( $wgArticle->getId() ); |
379 | | - // Note that revisions may not be set to override for users |
380 | | - if( is_object($top_frev) && $this->pageOverride() ) { |
381 | | - # Remove edit option altogether |
382 | | - unset( $content_actions['edit']); |
383 | | - unset( $content_actions['viewsource']); |
384 | | - # Straighten out order |
385 | | - $new_actions = array(); $counter = 0; |
386 | | - foreach ( $content_actions as $action => $data ) { |
387 | | - if( $counter==1 ) { |
388 | | - # Set current rev tab AFTER the main tab is set |
389 | | - $new_actions['current'] = array( |
390 | | - 'class' => '', |
391 | | - 'text' => wfMsg('currentrev'), |
392 | | - 'href' => $sktmp->mTitle->getLocalUrl( 'oldid=' . $wgArticle->getLatest() ) |
393 | | - ); |
394 | | - } |
395 | | - $new_actions[$action] = $data; |
396 | | - $counter++; |
397 | | - } |
398 | | - # Reset static array |
399 | | - $content_actions = $new_actions; |
400 | | - } |
401 | | - } |
402 | | - } |
403 | | - |
404 | | - function addToPageHist( &$article ) { |
405 | | - $this->pageFlaggedRevs = array(); |
406 | | - $rows = $this->getReviewedRevs( $article->getID() ); |
407 | | - if( !$rows ) return; |
408 | | - foreach( $rows as $row => $data ) { |
409 | | - $this->pageFlaggedRevs[] = $data->rev_id; |
410 | | - } |
411 | | - } |
412 | | - |
413 | | - function addToHistLine( &$row, &$s ) { |
414 | | - if( isset($this->pageFlaggedRevs) ) { |
415 | | - if( in_array( $row->rev_id, $this->pageFlaggedRevs ) ) |
416 | | - $s .= ' <small><strong>' . wfMsgHtml('revreview-hist') . '</strong></small>'; |
417 | | - } |
418 | | - } |
419 | | - |
420 | | - function addQuickReview( $id, $ontop=false, &$out=false ) { |
421 | | - global $wgOut, $wgTitle, $wgUser, $wgScript; |
422 | | - // We don't want two forms! |
423 | | - if( isset($this->formCount) && $this->formCount > 0 ) return; |
424 | | - $this->formCount = 1; |
425 | | - |
426 | | - if( !$wgUser->isAllowed( 'review' ) ) return; |
427 | | - |
428 | | - $flags = $this->getFlagsForRevision( $id ); |
429 | | - |
430 | | - $reviewtitle = SpecialPage::getTitleFor( 'Revisionreview' ); |
431 | | - $form = Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) ); |
432 | | - $form .= "<fieldset><legend>" . wfMsgHtml( 'revreview-flag', $id ) . "</legend>\n"; |
433 | | - $form .= wfHidden( 'title', $reviewtitle->getPrefixedText() ); |
434 | | - $form .= wfHidden( 'target', $wgTitle->getPrefixedText() ); |
435 | | - $form .= wfHidden( 'oldid', $id ); |
436 | | - foreach ( $this->dimensions as $quality => $levels ) { |
437 | | - $form .= wfMsgHtml("revreview-$quality") . ": <select name='$quality'>\n"; |
438 | | - foreach ( $levels as $idx => $label ) { |
439 | | - if( $flags[$quality]==$idx ) |
440 | | - $selected = 'selected'; |
441 | | - else |
442 | | - $selected = ''; |
443 | | - $form .= "<option value='$idx' $selected>" . wfMsgHtml("revreview-$label") . "</option>\n"; |
444 | | - } |
445 | | - $form .= "</select>\n"; |
446 | | - } |
447 | | - $form .= Xml::submitButton( wfMsgHtml( 'go' ) ) . "</fieldset>"; |
448 | | - $form .= Xml::closeElement( 'form' ); |
449 | | - // Hacks, to fiddle around with location a bit |
450 | | - if( $ontop && $out ) { |
451 | | - $out->mBodytext = $form . '<hr/>' . $out->mBodytext; |
452 | | - } else if( $ontop ) { |
453 | | - $wgOut->addHTML( $form ); |
454 | | - } else { |
455 | | - $wgOut->addHTML( $form ); |
456 | | - } |
457 | | - } |
458 | | - |
459 | | - function addReviewNotes( $row, $breakline=true ) { |
460 | | - global $wgOut, $wgUser, $wgFlaggedRevComments; |
461 | | - |
462 | | - if( !$row || !$wgFlaggedRevComments) return; |
463 | | - |
464 | | - $this->skin = $wgUser->getSkin(); |
465 | | - if( $row->fr_comment ) { |
466 | | - $notes = ($breakline) ? '<hr/><br/>' : ''; |
467 | | - $notes .= '<div class="mw-warning plainlinks">'; |
468 | | - $notes .= wfMsgExt('revreview-note', array('parse'), User::whoIs( $row->fr_user ) ); |
469 | | - $notes .= '<i>' . $this->skin->formatComment( $row->fr_comment ) . '</i></div>'; |
470 | | - $wgOut->addHTML( $notes ); |
471 | | - } |
472 | | - } |
473 | | - |
474 | | - /** |
475 | | - * Get all local image files and generate an array of them |
476 | | - * @param string $s, wikitext |
477 | | - * $output array, (string title array, string thumbnail array) |
478 | | - */ |
479 | | - function findLocalImages( $s ) { |
480 | | - global $wgUploadPath; |
481 | | - |
482 | | - $fname = 'findLocalImages'; |
483 | | - $imagelist = array(); $thumblist = array(); |
484 | | - |
485 | | - if( !$s || !strval($s) ) return $imagelist; |
486 | | - |
487 | | - static $tc = FALSE; |
488 | | - # the % is needed to support urlencoded titles as well |
489 | | - if( !$tc ) { $tc = Title::legalChars() . '#%'; } |
490 | | - |
491 | | - # split the entire text string on occurences of [[ |
492 | | - $a = explode( '[[', $s ); |
493 | | - |
494 | | - # Ignore things that start with colons, they are image links, not images |
495 | | - $e1_img = "/^([:{$tc}]+)(.+)$/sD"; |
496 | | - # Loop for each link |
497 | | - for ($k = 0; isset( $a[$k] ); $k++) { |
498 | | - if( preg_match( $e1_img, $a[$k], $m ) ) { |
499 | | - # page with normal text or alt of form x or ns:x |
500 | | - $nt = Title::newFromText( $m[1] ); |
501 | | - $ns = $nt->getNamespace(); |
502 | | - # add if this is an image |
503 | | - if( $ns == NS_IMAGE ) { |
504 | | - $imagelist[] = $nt->getPrefixedText(); |
505 | | - } |
506 | | - $image = $nt->getDBKey(); |
507 | | - # check for data for thumbnails |
508 | | - $part = array_map( 'trim', explode( '|', $m[2]) ); |
509 | | - foreach( $part as $val ) { |
510 | | - if( preg_match( '/^([0-9]+)px$/', $val, $n ) ) { |
511 | | - $width = intval( $n[1] ); |
512 | | - $thumblist[$image] = $width; |
513 | | - } else if( preg_match( '/^([0-9]+)x([0-9]+)$/', $val, $n ) ) { |
514 | | - $width = intval( $n[1] ); |
515 | | - $thumblist[$image] = $width; |
516 | | - } |
517 | | - } |
518 | | - } |
519 | | - } |
520 | | - return array( $imagelist, $thumblist ); |
521 | | - } |
522 | | - |
523 | | - /** |
524 | | - * Showtime! Copy all used images to a stable directory |
525 | | - * This updates (overwrites) any existing stable images |
526 | | - * Won't work for sites with unhashed dirs that have subfolders protected |
527 | | - * The future FileStore migration might effect this, not sure... |
528 | | - * @param array $imagelist, list of string names |
529 | | - * $output array, list of string names of images sucessfully cloned |
530 | | - */ |
531 | | - function makeStableImages( $imagelist ) { |
532 | | - global $wgUploadDirectory, $wgSharedUploadDirectory; |
533 | | - // All stable images are local, not shared |
534 | | - // Otherwise, we could have some nasty cross language/wiki conflicts |
535 | | - $stableDir = "$wgUploadDirectory/stable"; |
536 | | - // Copy images to stable dir |
537 | | - $usedimages = array(); |
538 | | - // We need valid input |
539 | | - if( !is_array($imagelist) ) return $usedimages; |
540 | | - foreach ( $imagelist as $name ) { |
541 | | - // We want a clean and consistant title entry |
542 | | - $nt = Title::newFromText( $name ); |
543 | | - if( is_null($nt) ) { |
544 | | - // If this title somehow doesn't work, ignore it |
545 | | - // this shouldn't happen... |
546 | | - continue; |
547 | | - } |
548 | | - $name = $nt->getDBkey(); |
549 | | - $hash = wfGetHashPath($name); |
550 | | - $path = $wgUploadDirectory . $hash; |
551 | | - $sharedpath = $wgSharedUploadDirectory . $hash; |
552 | | - // Try local repository |
553 | | - if( is_dir($path) ) { |
554 | | - if( file_exists("{$path}{$name}") ) { |
555 | | - // Check if our stable dir exists |
556 | | - // Make it if it doesn't |
557 | | - if( !is_dir($stableDir . $hash) ) { |
558 | | - wfMkdirParents($stableDir . $hash); |
559 | | - } |
560 | | - copy("{$path}{$name}","{$stableDir}{$hash}{$name}"); |
561 | | - $usedimages[] = $name; |
562 | | - } |
563 | | - } // Try shared repository |
564 | | - else if( is_dir($sharedpath) ) { |
565 | | - if( file_exists("{$sharedpath}{$name}") ) { |
566 | | - // Check if our stable dir exists |
567 | | - // Make it if it doesn't |
568 | | - if( !is_dir($stableDir . $hash) ) { |
569 | | - wfMkdirParents($stableDir . $hash); |
570 | | - } |
571 | | - copy("{$sharedpath}{$name}","{$stableDir}{$hash}{$name}"); |
572 | | - $usedimages[] = $name; |
573 | | - } |
574 | | - } |
575 | | - } |
576 | | - return $usedimages; |
577 | | - } |
578 | | - |
579 | | - /** |
580 | | - * Delete an a list of stable image files |
581 | | - * @param array $imagelist, list of string names |
582 | | - * $output array, list of string names of images to be deleted |
583 | | - */ |
584 | | - function deleteStableImages( $imagelist ) { |
585 | | - global $wgSharedUploadDirectory; |
586 | | - // All stable images are local, not shared |
587 | | - // Otherwise, we could have some nasty cross language/wiki conflicts |
588 | | - $stableDir = "$wgUploadDirectory/stable"; |
589 | | - // Copy images to stable dir |
590 | | - $deletedimages = array(); |
591 | | - // We need valid input |
592 | | - if( !is_array($imagelist) ) return $usedimages; |
593 | | - foreach ( $imagelist as $name ) { |
594 | | - // We want a clean and consistant title entry |
595 | | - $nt = Title::newFromText( $name ); |
596 | | - if( is_null($nt) ) { |
597 | | - // If this title somehow doesn't work, ignore it |
598 | | - // this shouldn't happen... |
599 | | - continue; |
600 | | - } |
601 | | - $name = $nt->getDBkey(); |
602 | | - $hash = wfGetHashPath($name); |
603 | | - $path = $stableDir . $hash; |
604 | | - // Try the stable repository |
605 | | - if( is_dir($path) ) { |
606 | | - if( file_exists("{$path}{$name}") ) { |
607 | | - // Delete! |
608 | | - delete("{$path}{$name}"); |
609 | | - $deletedimages[] = $name; |
610 | | - } |
611 | | - } |
612 | | - } |
613 | | - return $deletedimages; |
614 | | - } |
615 | | - |
616 | | - /** |
617 | | - * Delete an a list of stable image thumbnails |
618 | | - * New thumbnails don't normally override old ones, causing outdated images |
619 | | - * This allows for tagged revisions to be re-reviewed with newer images |
620 | | - * @param array $imagelist, list of string names |
621 | | - * $output array, list of string names of images to be deleted |
622 | | - */ |
623 | | - function purgeStableThumbnails( $thumblist ) { |
624 | | - global $wgUploadDirectory, $wgUseImageResize; |
625 | | - // Are thumbs even enabled? |
626 | | - if ( !$wgUseImageResize ) return true; |
627 | | - // We need valid input |
628 | | - if( !is_array($thumblist) ) return false; |
629 | | - foreach ( $thumblist as $name => $width ) { |
630 | | - $thumburl = "{$wgUploadDirectory}/stable/thumb" . wfGetHashPath( $name, false ) . "$name/". $width."px-".$name; |
631 | | - if( file_exists($thumburl) ) { |
632 | | - unlink($thumburl); |
633 | | - } |
634 | | - } |
635 | | - return true; |
636 | | - } |
637 | | - |
638 | | - /** |
639 | | - * Update the stable image usage table |
640 | | - * Add some images if not redundant |
641 | | - * @param array $imagelist, list of string names |
642 | | - * $output bool, on succeed |
643 | | - */ |
644 | | - function insertStableImages( $revid, $imagelist ) { |
645 | | - wfProfileIn( __METHOD__ ); |
646 | | - |
647 | | - if( !is_array($imagelist) ) return false; |
648 | | - |
649 | | - $db = wfGetDB( DB_MASTER ); |
650 | | - foreach( $imagelist as $name ) { |
651 | | - // We want a clean and consistant title entry |
652 | | - $nt = Title::newFromText( $name ); |
653 | | - if( is_null($nt) ) { |
654 | | - // If this title somehow doesn't work, ignore it |
655 | | - // this shouldn't happen... |
656 | | - continue; |
657 | | - } |
658 | | - $imagename = $nt->getDBkey(); |
659 | | - // Add image and the revision that uses it |
660 | | - $set = array('fi_rev_id' => $revid, 'fi_name' => $imagename); |
661 | | - // Add entries or replace any that have the same rev_id |
662 | | - $db->replace( 'flaggedimages', array( array('fi_rev_id', 'fi_name') ), $set, __METHOD__ ); |
663 | | - } |
664 | | - return true; |
665 | | - } |
666 | | - |
667 | | - /** |
668 | | - * Update the stable image usage table |
669 | | - * Clean out unused images if needed |
670 | | - * @param array $imagelist, list of string names |
671 | | - * $output bool, on succeed |
672 | | - */ |
673 | | - function removeStableImages( $revid, $imagelist ) { |
674 | | - wfProfileIn( __METHOD__ ); |
675 | | - |
676 | | - if( !is_array($imagelist) ) return false; |
677 | | - $unusedimages = array(); |
678 | | - $db = wfGetDB( DB_MASTER ); |
679 | | - foreach( $imagelist as $name ) { |
680 | | - // We want a clean and consistant title entry |
681 | | - $nt = Title::newFromText( $name ); |
682 | | - if( is_null($nt) ) { |
683 | | - // If this title somehow doesn't work, ignore it |
684 | | - // this shouldn't happen... |
685 | | - continue; |
686 | | - } |
687 | | - $imagename = $nt->getDBkey(); |
688 | | - $where = array( 'fi_rev_id' => $revid, 'fi_name' => $imagename ); |
689 | | - // See how many revisions use this image total... |
690 | | - $result = $db->select( 'flaggedimages', array('fi_id'), array( 'fi_name' => $imagename ) ); |
691 | | - // If only one, then delete the image |
692 | | - // since it's about to be remove from that one |
693 | | - if( $db->numRows($result)==1 ) { |
694 | | - $unusedimages[] = $imagename; |
695 | | - } |
696 | | - // Clear out this revision's entry |
697 | | - $db->delete( 'flaggedimages', $where ); |
698 | | - } |
699 | | - $this->deleteStableImages( $unusedimages ); |
700 | | - return true; |
701 | | - } |
702 | | - |
703 | | - function getPageCache( $article ) { |
704 | | - global $wgUser, $wgFlaggedRevsExpire; |
705 | | - |
706 | | - wfProfileIn( __METHOD__ ); |
707 | | - |
708 | | - // Make sure it is valid |
709 | | - if ( !$article || !$article->getId() ) return NULL; |
710 | | - $cachekey = ParserCache::getKey( $article, $wgUser ); |
711 | | - |
712 | | - $db = wfGetDB( DB_SLAVE ); |
713 | | - $cutoff = $db->timestamp( time() - $wgFlaggedRevsExpire ); |
714 | | - // Replace the page cache if it is out of date |
715 | | - $result = $db->select( |
716 | | - array('flaggedcache'), |
717 | | - array('fc_cache'), |
718 | | - array('fc_key' => $cachekey, 'fc_date >= ' . $article->getTouched(), 'fc_date >= ' . $cutoff ), |
719 | | - __METHOD__); |
720 | | - if ( $row = $db->fetchObject($result) ) { |
721 | | - return $row->fc_cache; |
722 | | - } |
723 | | - return NULL; |
724 | | - } |
725 | | - |
726 | | - function updatePageCache( $article, $value=NULL ) { |
727 | | - global $wgUser; |
728 | | - wfProfileIn( __METHOD__ ); |
729 | | - |
730 | | - // Make sure it is valid |
731 | | - if ( is_null($value) || !$article || !$article->getId() ) return false; |
732 | | - $cachekey = ParserCache::getKey( $article, $wgUser ); |
733 | | - // Add cache mark |
734 | | - $timestamp = wfTimestampNow(); |
735 | | - $value .= "\n<!-- Saved in stable version parser cache with key $cachekey and timestamp $timestamp -->"; |
736 | | - |
737 | | - $dbw = wfGetDB( DB_MASTER ); |
738 | | - // Replace the page cache if it is out of date |
739 | | - $dbw->replace('flaggedcache', |
740 | | - array('fc_key'), |
741 | | - array('fc_key' => $cachekey, 'fc_cache' => $value, 'fc_date' => $timestamp), |
742 | | - __METHOD__); |
743 | | - |
744 | | - return true; |
745 | | - } |
746 | | -} |
747 | | - |
748 | | -# Load expert promotion UI |
749 | | -include_once('SpecialMakevalidate.php'); |
750 | | - |
751 | | -if( !function_exists( 'extAddSpecialPage' ) ) { |
752 | | - require( dirname(__FILE__) . '/../ExtensionFunctions.php' ); |
753 | | -} |
754 | | -extAddSpecialPage( dirname(__FILE__) . '/FlaggedRevsPage.body.php', 'Revisionreview', 'Revisionreview' ); |
755 | | - |
756 | | -# Load approve/unapprove UI |
757 | | -$wgHooks['LoadAllMessages'][] = 'efLoadReviewMessages'; |
758 | | - |
759 | | -$flaggedrevs = new FlaggedRevs(); |
760 | | -$wgHooks['BeforePageDisplay'][] = array($flaggedrevs, 'setPageContent'); |
761 | | -$wgHooks['DiffViewHeader'][] = array($flaggedrevs, 'addToDiff'); |
762 | | -$wgHooks['EditPage::showEditForm:initial'][] = array($flaggedrevs, 'addToEditView'); |
763 | | -$wgHooks['SkinTemplateTabs'][] = array($flaggedrevs, 'setCurrentTab'); |
764 | | -$wgHooks['PageHistoryBeforeList'][] = array($flaggedrevs, 'addToPageHist'); |
765 | | -$wgHooks['PageHistoryLineEnding'][] = array($flaggedrevs, 'addToHistLine'); |
766 | | -?> |
Index: branches/jhb/phase3/extensions/FlaggedRevs/FlaggedRevsPage.body.php |
— | — | @@ -0,0 +1,302 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +#(c) Joerg Baach, Aaron Schulz, 2007 GPL |
| 5 | + |
| 6 | +global $IP; |
| 7 | +require_once( "$IP/includes/LogPage.php" ); |
| 8 | +require_once( "$IP/includes/SpecialLog.php" ); |
| 9 | + |
| 10 | +class Revisionreview extends SpecialPage |
| 11 | +{ |
| 12 | + |
| 13 | + function Revisionreview() { |
| 14 | + SpecialPage::SpecialPage('Revisionreview', 'review'); |
| 15 | + } |
| 16 | + |
| 17 | + function execute( $par ) { |
| 18 | + global $wgRequest, $wgUser, $wgOut, $wgFlaggedRevComments; |
| 19 | + |
| 20 | + if( !$wgUser->isAllowed( 'review' ) ) { |
| 21 | + $wgOut->permissionRequired( 'review' ); |
| 22 | + return; |
| 23 | + } |
| 24 | + |
| 25 | + $this->setHeaders(); |
| 26 | + // Our target page |
| 27 | + $this->target = $wgRequest->getText( 'target' ); |
| 28 | + // Revision ID |
| 29 | + $this->oldid = $wgRequest->getIntOrNull( 'oldid' ); |
| 30 | + // Log comment |
| 31 | + $this->comment = $wgRequest->getText( 'wpReason' ); |
| 32 | + // Additional notes |
| 33 | + $this->notes = ($wgFlaggedRevComments) ? $wgRequest->getText('wpNotes') : ''; |
| 34 | + // Get our accuracy/quality array |
| 35 | + $this->dimensions = array(); |
| 36 | + $this->dimensions['acc'] = $wgRequest->getIntOrNull('accuracy'); |
| 37 | + $this->dimensions['depth'] = $wgRequest->getIntOrNull('depth'); |
| 38 | + $this->dimensions['style'] = $wgRequest->getIntOrNull('style'); |
| 39 | + // Must be a valid page |
| 40 | + $this->page = Title::newFromUrl( $this->target ); |
| 41 | + if( is_null($this->page) || is_null($this->oldid) || !$this->page->isContentPage() ) { |
| 42 | + $wgOut->showErrorPage( $this->page, 'notargettitle', 'notargettext' ); |
| 43 | + return; |
| 44 | + } |
| 45 | + if( $wgRequest->wasPosted() ) { |
| 46 | + $this->submit( $wgRequest ); |
| 47 | + } else { |
| 48 | + $this->showRevision( $wgRequest ); |
| 49 | + } |
| 50 | + } |
| 51 | + |
| 52 | + /** |
| 53 | + * @param webrequest $request |
| 54 | + */ |
| 55 | + function showRevision( $request ) { |
| 56 | + global $wgOut, $wgUser, $wgTitle, $wgFlaggedRevComments; |
| 57 | + |
| 58 | + $wgOut->addWikiText( wfMsgExt( 'revreview-selected', array('parsemag'), $this->page->getPrefixedText() ) ); |
| 59 | + |
| 60 | + $this->skin = $wgUser->getSkin(); |
| 61 | + $rev = Revision::newFromTitle( $this->page, $this->oldid ); |
| 62 | + // Check if rev exists |
| 63 | + if( !isset( $rev ) ) { |
| 64 | + $wgOut->showErrorPage( 'internalerror', 'notargettitle', 'notargettext' ); |
| 65 | + return; |
| 66 | + } |
| 67 | + // Do not mess with deleted revisions |
| 68 | + if ( $rev->mDeleted ) { |
| 69 | + $wgOut->showErrorPage( 'internalerror', 'badarticleerror' ); |
| 70 | + return; |
| 71 | + } |
| 72 | + $wgOut->addHtml( "<ul>" ); |
| 73 | + $wgOut->addHtml( $this->historyLine( $rev ) ); |
| 74 | + $wgOut->addHtml( "</ul>" ); |
| 75 | + |
| 76 | + $wgOut->addWikiText( wfMsgHtml( 'revreview-text' ) ); |
| 77 | + |
| 78 | + $this->accRadios = array( |
| 79 | + array( 'revreview-acc-0', 'wpAcc1', 0 ), |
| 80 | + array( 'revreview-acc-1', 'wpAcc2', 1 ), |
| 81 | + array( 'revreview-acc-2', 'wpAcc3', 2 ), |
| 82 | + array( 'revreview-acc-3', 'wpAcc4', 3 ) ); |
| 83 | + $this->depthRadios = array( |
| 84 | + array( 'revreview-depth-0', 'wpDepth1', 0 ), |
| 85 | + array( 'revreview-depth-1', 'wpDepth2', 1 ), |
| 86 | + array( 'revreview-depth-2', 'wpDepth3', 2 ), |
| 87 | + array( 'revreview-depth-3', 'wpDepth4', 3 ) ); |
| 88 | + $this->styleRadios = array( |
| 89 | + array( 'revreview-style-0', 'wpStyle1', 0 ), |
| 90 | + array( 'revreview-style-1', 'wpStyle2', 1 ), |
| 91 | + array( 'revreview-style-2', 'wpStyle3', 2 ), |
| 92 | + array( 'revreview-style-3', 'wpStyle4', 3 ) ); |
| 93 | + $items = array( |
| 94 | + wfInputLabel( wfMsgHtml( 'revreview-log' ), 'wpReason', 'wpReason', 60 ), |
| 95 | + wfSubmitButton( wfMsgHtml( 'revreview-submit' ) ) ); |
| 96 | + $hidden = array( |
| 97 | + wfHidden( 'wpEditToken', $wgUser->editToken() ), |
| 98 | + wfHidden( 'target', $this->page->getPrefixedText() ), |
| 99 | + wfHidden( 'oldid', $this->oldid ) ); |
| 100 | + |
| 101 | + $action = $wgTitle->escapeLocalUrl( 'action=submit' ); |
| 102 | + $form = "<form name='revisionreview' action='$action' method='post'>"; |
| 103 | + $form .= '<fieldset><legend>' . wfMsgHtml( 'revreview-legend' ) . '</legend><table><tr>'; |
| 104 | + $form .= '<td><strong>' . wfMsgHtml( 'revreview-acc' ) . '</strong></td>'; |
| 105 | + $form .= '<td width=\'25\'></td><td><strong>' . wfMsgHtml( 'revreview-depth' ) . '</strong></td>'; |
| 106 | + $form .= '<td width=\'25\'></td><td><strong>' . wfMsgHtml( 'revreview-style' ) . '</strong></td>'; |
| 107 | + $form .= '</tr><tr><td>'; |
| 108 | + foreach( $this->accRadios as $item ) { |
| 109 | + list( $message, $name, $field ) = $item; |
| 110 | + $form .= "<div>" . |
| 111 | + Xml::radio( 'accuracy', $field, ($field==$this->dimensions['acc']) ) . ' ' . wfMsgHtml($message) . |
| 112 | + "</div>\n"; |
| 113 | + } |
| 114 | + $form .= '<td width=\'25\'></td></td><td>'; |
| 115 | + foreach( $this->depthRadios as $item ) { |
| 116 | + list( $message, $name, $field ) = $item; |
| 117 | + $form .= "<div>" . |
| 118 | + Xml::radio( 'depth', $field, ($field==$this->dimensions['depth']) ) . ' ' . wfMsgHtml($message) . |
| 119 | + "</div>\n"; |
| 120 | + } |
| 121 | + $form .= '<td width=\'25\'></td></td><td>'; |
| 122 | + foreach( $this->styleRadios as $item ) { |
| 123 | + list( $message, $name, $field ) = $item; |
| 124 | + $form .= "<div>" . |
| 125 | + Xml::radio( 'style', $field, ($field==$this->dimensions['style']) ) . ' ' . wfMsgHtml($message) . |
| 126 | + "</div>\n"; |
| 127 | + } |
| 128 | + $form .= '</td></tr></table></fieldset>'; |
| 129 | + |
| 130 | + list($images,$thumbs) = FlaggedRevs::findLocalImages( FlaggedRevs::expandText( $rev->getText() ) ); |
| 131 | + if ( $images ) { |
| 132 | + $form .= wfMsg('revreview-images') . "\n"; |
| 133 | + $form .= "<ul>"; |
| 134 | + $imglist = ''; |
| 135 | + foreach ( $images as $image ) { |
| 136 | + $imglist .= "<li>" . $this->skin->makeKnownLink( $image ) . "</li>\n"; |
| 137 | + } |
| 138 | + $form .= $imglist; |
| 139 | + $form .= "</ul>\n"; |
| 140 | + } |
| 141 | + if ( $wgFlaggedRevComments ) { |
| 142 | + $form .= "<fieldset><legend>" . wfMsgHtml( 'revreview-notes' ) . "</legend>" . |
| 143 | + "<textarea tabindex='1' name='wpNotes' id='wpNotes' rows='3' cols='80' style='width:100%'></textarea>" . |
| 144 | + "</fieldset>"; |
| 145 | + } |
| 146 | + |
| 147 | + foreach( $items as $item ) { |
| 148 | + $form .= '<p>' . $item . '</p>'; |
| 149 | + } |
| 150 | + foreach( $hidden as $item ) { |
| 151 | + $form .= $item; |
| 152 | + } |
| 153 | + |
| 154 | + $form .= '</form>'; |
| 155 | + $wgOut->addHtml( $form ); |
| 156 | + } |
| 157 | + |
| 158 | + /** |
| 159 | + * @param Revision $rev |
| 160 | + * @returns string |
| 161 | + */ |
| 162 | + function historyLine( $rev ) { |
| 163 | + global $wgContLang; |
| 164 | + $date = $wgContLang->timeanddate( $rev->getTimestamp() ); |
| 165 | + |
| 166 | + $difflink = '(' . $this->skin->makeKnownLinkObj( $this->page, wfMsgHtml('diff'), |
| 167 | + '&diff=' . $rev->getId() . '&oldid=prev' ) . ')'; |
| 168 | + |
| 169 | + $revlink = $this->skin->makeLinkObj( $this->page, $date, 'oldid=' . $rev->getId() ); |
| 170 | + |
| 171 | + return |
| 172 | + "<li> $difflink $revlink " . $this->skin->revUserLink( $rev ) . " " . $this->skin->revComment( $rev ) . "</li>"; |
| 173 | + } |
| 174 | + |
| 175 | + function submit( $request ) { |
| 176 | + global $wgOut; |
| 177 | + |
| 178 | + $rev = Revision::newFromTitle( $this->page, $this->oldid ); |
| 179 | + // Do not mess with deleted revisions |
| 180 | + if ( is_null($rev) || $rev->mDeleted ) { |
| 181 | + $wgOut->showErrorPage( 'internalerror', 'badarticleerror' ); |
| 182 | + return; |
| 183 | + } |
| 184 | + $approved = false; |
| 185 | + # If all values are set to zero, this has been unnapproved |
| 186 | + foreach( $this->dimensions as $quality => $value ) { |
| 187 | + if( $value ) $approved = true; |
| 188 | + } |
| 189 | + $success = ( $approved ) ? $this->approveRevision( $rev ) : $this->unapproveRevision( $rev ); |
| 190 | + // Return to our page |
| 191 | + if ( $success ) { |
| 192 | + $wgOut->redirect( $this->page->escapeLocalUrl() ); |
| 193 | + } else { |
| 194 | + $wgOut->showErrorPage( 'internalerror', 'badarticleerror' ); |
| 195 | + } |
| 196 | + } |
| 197 | + |
| 198 | + /** |
| 199 | + * @param Revision $rev |
| 200 | + * Adds or updates the flagged revision table for this page/id set |
| 201 | + */ |
| 202 | + function approveRevision( $rev=NULL ) { |
| 203 | + global $wgUser; |
| 204 | + if( is_null($rev) ) return false; |
| 205 | + |
| 206 | + wfProfileIn( __METHOD__ ); |
| 207 | + |
| 208 | + $db = wfGetDB( DB_MASTER ); |
| 209 | + $user = $wgUser->getId(); |
| 210 | + $timestamp = wfTimestampNow(); |
| 211 | + |
| 212 | + $cache_text = FlaggedRevs::expandText( $rev->getText() ); |
| 213 | + // Add or update entry for this revision |
| 214 | + $set = array( |
| 215 | + 'fr_page_id' => $rev->getPage(), |
| 216 | + 'fr_rev_id' => $rev->getId(), |
| 217 | + 'fr_acc' => $this->dimensions['acc'], |
| 218 | + 'fr_dep' => $this->dimensions['depth'], |
| 219 | + 'fr_sty' => $this->dimensions['style'], |
| 220 | + 'fr_user' => $user, |
| 221 | + 'fr_timestamp' => $timestamp, |
| 222 | + 'fr_comment'=> $this->notes |
| 223 | + ); |
| 224 | + $set2 = array('ft_rev_id' => $rev->getId(), 'ft_text' => $cache_text); |
| 225 | + // Update flagrevisions table |
| 226 | + $db->replace( 'flaggedrevs', array( array('fr_page_id','fr_rev_id') ), $set, __METHOD__ ); |
| 227 | + // Store/update the text |
| 228 | + $db->replace( 'flaggedtext', array('ft_rev_id'), $set2, __METHOD__ ); |
| 229 | + // Update the article review log |
| 230 | + $this->updateLog( $this->page, $this->dimensions, $this->comment, $this->oldid, true ); |
| 231 | + // Clone images to stable dir |
| 232 | + list($images,$thumbs) = FlaggedRevs::findLocalImages( $cache_text ); |
| 233 | + $copies = FlaggedRevs::makeStableImages( $images ); |
| 234 | + FlaggedRevs::purgeStableThumbnails( $thumbs ); |
| 235 | + // Update stable image table |
| 236 | + FlaggedRevs::insertStableImages( $rev->getId(), $copies ); |
| 237 | + // Clear cache... |
| 238 | + $this->page->invalidateCache(); |
| 239 | + return true; |
| 240 | + } |
| 241 | + |
| 242 | + /** |
| 243 | + * @param Revision $rev |
| 244 | + * Removes flagged revision data for this page/id set |
| 245 | + */ |
| 246 | + function unapproveRevision( $rev=NULL ) { |
| 247 | + global $wgUser; |
| 248 | + |
| 249 | + if( is_null($rev) ) return false; |
| 250 | + $db = wfGetDB( DB_MASTER ); |
| 251 | + $user = $wgUser->getId(); |
| 252 | + $timestamp = wfTimestampNow(); |
| 253 | + // get the flagged revision to access its cache text |
| 254 | + $frev = FlaggedRevs::getFlaggedRevision( $rev->getId() ); |
| 255 | + if( !$frev ) { |
| 256 | + // Quitly ignore this... |
| 257 | + return true; |
| 258 | + } |
| 259 | + $db->delete( 'flaggedrevs', array( 'fr_rev_id' => $rev->getId ) ); |
| 260 | + // Update the article review log |
| 261 | + $this->updateLog( $this->page, $this->dimensions, $this->comment, $this->oldid, false ); |
| 262 | + |
| 263 | + $cache_text = FlaggedRevs::getFlaggedRevText( $rev->getId ) ; |
| 264 | + // Delete stable images if needed |
| 265 | + list($images,$thumbs) = FlaggedRevs::findLocalImages( $cache_text ); |
| 266 | + $copies = FlaggedRevs::deleteStableImages( $images ); |
| 267 | + // Stable versions must remake this thumbnail |
| 268 | + FlaggedRevs::purgeStableThumbnails( $thumbs ); |
| 269 | + // Update stable image table |
| 270 | + FlaggedRevs::removeStableImages( $rev->getId(), $copies ); |
| 271 | + // Clear cache... |
| 272 | + $this->page->invalidateCache(); |
| 273 | + return true; |
| 274 | + } |
| 275 | + |
| 276 | + /** |
| 277 | + * Record a log entry on the action |
| 278 | + * @param Title $title |
| 279 | + * @param array $dimensions |
| 280 | + * @param string $comment |
| 281 | + * @param int $revid |
| 282 | + * @param bool $approve |
| 283 | + */ |
| 284 | + function updateLog( $title, $dimensions, $comment, $oldid, $approve ) { |
| 285 | + $log = new LogPage( 'review' ); |
| 286 | + // ID, accuracy, depth, style |
| 287 | + $ratings = array(); |
| 288 | + foreach( $dimensions as $quality => $level ) { |
| 289 | + $ratings[] = wfMsg( "revreview-$quality" ) . ": " . wfMsg("revreview-$quality-$level"); |
| 290 | + } |
| 291 | + $rating = ($approve) ? ' [' . implode(', ',$ratings). ']' : ''; |
| 292 | + // Append comment with action |
| 293 | + $action = wfMsgExt('review-logaction', array('parsemag'), $oldid ); |
| 294 | + $comment = ($comment) ? "$action: $comment$rating" : "$action $rating"; |
| 295 | + |
| 296 | + if ( $approve ) { |
| 297 | + $log->addEntry( 'approve', $title, $comment ); |
| 298 | + } else { |
| 299 | + $log->addEntry( 'unapprove', $title, $comment ); |
| 300 | + } |
| 301 | + } |
| 302 | +} |
| 303 | +?> |
Property changes on: branches/jhb/phase3/extensions/FlaggedRevs/FlaggedRevsPage.body.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 304 | + native |
Index: branches/jhb/phase3/extensions/FlaggedRevs/FlaggedRevsPage.i18n.php |
— | — | @@ -0,0 +1,66 @@ |
| 2 | +<?php |
| 3 | +$RevisionreviewMessages = array(); |
| 4 | + |
| 5 | +// English (Aaron Schulz) |
| 6 | +$RevisionreviewMessages['en'] = array( |
| 7 | + 'reviewer' => 'Reviewer', |
| 8 | + 'group-reviewer' => 'Reviewers', |
| 9 | + 'group-reviewer' => 'Reviewer', |
| 10 | + 'grouppage-reviewer' => '{{ns:project}}:Reviewer', |
| 11 | + |
| 12 | + 'revreview-noflagged' => 'There are no reviewed revisions of this page, so it has \'\'\'not\'\'\' been |
| 13 | + [[Help:Article validation|checked]] for quality.', |
| 14 | + 'revreview-isnewest' => 'This is the latest [[Help:Article validation|reviewed]] revision of this page (with |
| 15 | + updated images and templates) [{{fullurl:Special:Log/review|page={{FULLPAGENAMEE}}}} approved] on <i>$1</i>.', |
| 16 | + 'revreview-newest' => 'The [{{fullurl:{{FULLPAGENAMEE}}|oldid=$1}} latest reviewed revision] |
| 17 | + ([{{fullurl:{{FULLPAGENAMEE}}|oldid=$2&diff=$3}} compare]) was [{{fullurl:Special:Log/review|page={{FULLPAGENAMEE}}}} approved] |
| 18 | + on <i>$4</i>, rated as:', |
| 19 | + 'revreview-replaced' => 'This is the latest [[Help:Article validation|reviewed]] revision of this page, |
| 20 | + [{{fullurl:Special:Log/review|page={{FULLPAGENAMEE}}}} approved] on <i>$4</i>. The [{{fullurl:{{FULLPAGENAMEE}}|oldid=$2}} current revision] |
| 21 | + is editable and may be more up to date. There {{plural:$3|is $3 revision|are $3 revisions}} |
| 22 | + ([{{fullurl:{{FULLPAGENAMEE}}|oldid=$1&diff=$2}} changes]) awaiting review.', |
| 23 | + |
| 24 | + 'revisionreview' => 'Review revisions', |
| 25 | + |
| 26 | + 'flaggedrevs' => 'Flagged Revisions', |
| 27 | + 'review-logpage' => 'Article review log', |
| 28 | + 'review-logpagetext' => 'This is a log of changes to revisions\' [[Help:Article validation|approval]] status |
| 29 | + for content pages.', |
| 30 | + 'review-logentrygrant' => 'approved [[$1]]', |
| 31 | + 'review-logentryrevoke' => 'unapproved [[$1]]', |
| 32 | + 'review-logaction' => 'reviewed revision $1', |
| 33 | + |
| 34 | + 'revreview-selected' => "Selected revision of '''$1:'''", |
| 35 | + 'revreview-text' => "Approved revisions are set as the default revision shown upon page view rather |
| 36 | + than the top revision. The content of this approved revision will remain constant regardless of any transcluded |
| 37 | + pages or internal images. Users on this wiki will still be able to access |
| 38 | + unreviewed content through the page history.", |
| 39 | + 'revreview-images' => 'Internal images on this page will be copied to the stable image directory, updating |
| 40 | + existing versions, and stored there until no reviewed revisions use them. The following images are transcluded onto this page:', |
| 41 | + |
| 42 | + 'revreview-hist' => '[reviewed]', |
| 43 | + |
| 44 | + 'revreview-note' => '[[User:$1]] made the following notes [[Help:Article validation|reviewing]] this revision:', |
| 45 | + |
| 46 | + 'revreview-flag' => 'Review this revision (#$1):', |
| 47 | + 'revreview-legend' => 'Rate revision content:', |
| 48 | + 'revreview-notes' => 'Observations or notes to display:', |
| 49 | + 'revreview-acc' => 'Accuracy', |
| 50 | + 'revreview-acc-0' => 'Unapproved', |
| 51 | + 'revreview-acc-1' => 'Not vandalized', |
| 52 | + 'revreview-acc-2' => 'Accurate', |
| 53 | + 'revreview-acc-3' => 'Well sourced', |
| 54 | + 'revreview-depth' => 'Depth', |
| 55 | + 'revreview-depth-0' => 'Unapproved', |
| 56 | + 'revreview-depth-1' => 'Stub', |
| 57 | + 'revreview-depth-2' => 'Moderate', |
| 58 | + 'revreview-depth-3' => 'Complete', |
| 59 | + 'revreview-style' => 'Readability', |
| 60 | + 'revreview-style-0' => 'Unapproved', |
| 61 | + 'revreview-style-1' => 'Acceptable', |
| 62 | + 'revreview-style-2' => 'Good', |
| 63 | + 'revreview-style-3' => 'Concise', |
| 64 | + 'revreview-log' => 'Log comment:', |
| 65 | + 'revreview-submit' => 'Apply to selected revision', |
| 66 | +); |
| 67 | +?> |
\ No newline at end of file |
Property changes on: branches/jhb/phase3/extensions/FlaggedRevs/FlaggedRevsPage.i18n.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 68 | + native |
Index: branches/jhb/phase3/extensions/FlaggedRevs/Makevalidate.i18n.php |
— | — | @@ -0,0 +1,34 @@ |
| 2 | +<?php
|
| 3 | +/**
|
| 4 | + * Internationalisation file for makevalidate extension.
|
| 5 | + *
|
| 6 | + * @package MediaWiki
|
| 7 | + * @subpackage Extensions
|
| 8 | +*/
|
| 9 | +
|
| 10 | +function efMakeValidateMessages() {
|
| 11 | + $messages = array();
|
| 12 | +
|
| 13 | +/* English (Aaron Schulz) */
|
| 14 | +$messages['en'] = array(
|
| 15 | + 'makevalidate' => 'Promote/demote reviewers',
|
| 16 | + 'makevalidate-header' => '<strong>This form is used by bureaucrats to turn ordinary users into stable version validators.</strong><br> Type the name of the user in the box and press the button to make the user a validator.',
|
| 17 | + 'makevalidate-username' => 'Name of the user:',
|
| 18 | + 'makevalidate-search' => 'Go',
|
| 19 | + 'makevalidate-isvalidator' => '[[User:$1|$1]] has reviewer status.',
|
| 20 | + 'makevalidate-notvalidator' => '[[User:$1|$1]] does not have reviewer status.',
|
| 21 | + 'makevalidate-change' => 'Change status:',
|
| 22 | + 'makevalidate-grant' => 'Grant',
|
| 23 | + 'makevalidate-revoke' => 'Revoke',
|
| 24 | + 'makevalidate-comment' => 'Comment:',
|
| 25 | + 'makevalidate-granted' => '[[User:$1|$1]] now has validator status.',
|
| 26 | + 'makevalidate-revoked' => '[[User:$1|$1]] no longer has validator status.',
|
| 27 | + 'makevalidate-logpage' => 'Reviewer status log',
|
| 28 | + 'makevalidate-logpagetext' => 'This is a log of changes to users\' [[Help:Article validation|article validation]] status.',
|
| 29 | + 'makevalidate-logentrygrant' => 'granted validator status to [[$1]]',
|
| 30 | + 'makevalidate-logentryrevoke' => 'removed validator status from [[$1]]',
|
| 31 | +);
|
| 32 | +
|
| 33 | +return $messages;
|
| 34 | +}
|
| 35 | +?>
|
Index: branches/jhb/phase3/extensions/FlaggedRevs/specs.txt |
— | — | @@ -0,0 +1,94 @@ |
| 2 | +== The implementation == |
| 3 | + |
| 4 | +The specs to implement are a variation of the proposal by Philipp and |
| 5 | +other German Wikipedians, found at: |
| 6 | +http://de.wikipedia.org/wiki/Wikipedia:Gesichtete_Versionen |
| 7 | +http://de.wikipedia.org/wiki/Wikipedia:Gepr%C3%BCfte_Versionen |
| 8 | + |
| 9 | +The changes are aimed mostly at making the feature more flexibly |
| 10 | +configurable, and streamlining some of the UI tasks. |
| 11 | + |
| 12 | +The feature is to be implemented as an extension, if at all possible. |
| 13 | +It must be GPL-licensed and will be committed to the Wikimedia |
| 14 | +Subversion server. |
| 15 | + |
| 16 | +The wiki will allow the configuration of "revision tags", which can |
| 17 | +then be associated with any revision of an article. Tags are organized |
| 18 | +in tag array, where each array represents a set of tags that describe |
| 19 | +a similar attribute, e.g., accuracy-related tags would be organized in |
| 20 | +one array, while those used for flagging materials for export might be |
| 21 | +organized in another. This is to ensure that "levels" of quality |
| 22 | +(unvandalized -> reviewed for accuracy -> featured article etc.) can |
| 23 | +be represented correctly. |
| 24 | + |
| 25 | +Each tag has certain attributes: |
| 26 | +- tag comments: Should the tag support a comment field which explains |
| 27 | +why the revision was tagged in a certain way? |
| 28 | +- permissions: Which user groups have permission to set the tag? |
| 29 | +- stylesheet and UI: not requiring explicit configuration, each tag |
| 30 | +should have a stylesheet class and MediaWiki: namespace message(s) |
| 31 | +associated with it, to allow for differences in visual and textual |
| 32 | +representation |
| 33 | +- implicit tagging: should this tag be implicitly set by any user |
| 34 | +within the associated group when editing? (this will be used for the |
| 35 | +"non-vandalized" tag) |
| 36 | + |
| 37 | +A generic change is required to allow for automatic membership in a |
| 38 | +user group when a user has more than X edits and is older than Y days. |
| 39 | + |
| 40 | +There should be a configuration option which associates a namespace with |
| 41 | +- a tag-array |
| 42 | +- a minimum level in that array. |
| 43 | + |
| 44 | +This option determines that, by default, the revision shown from this |
| 45 | +namespace will be the one from that array which is also >= the minimum |
| 46 | +level. So, for instance, one could determine that pages in the article |
| 47 | +namespace need to be at least checked for vandalism, or at least |
| 48 | +reviewed for accuracy. |
| 49 | + |
| 50 | +As a high priority wishlist item, these view options should also be |
| 51 | +applicable on a per-page basis. The existing protection UI should be |
| 52 | +expanded for this purpose. Implementation (and schedule) of this item |
| 53 | +will depend on overall implementation progress. |
| 54 | + |
| 55 | +Whichever view one ends up with, we expect that the top of the page |
| 56 | +indicates this, and allows you to switch & get diffs to other views. |
| 57 | + |
| 58 | +Because MediaWiki currently does not show templates and revisions in |
| 59 | +time synchronization, this behavior has to be fixed for old revisions. |
| 60 | +When one has expressed a preference for a revision with a specific |
| 61 | +tag/level (e.g. "unvandalized") AND this is the most recent revision, |
| 62 | +it will be shown together with the most recent equally tagged |
| 63 | +templates if they exist, otherwise with the most recent ones. |
| 64 | + |
| 65 | +Example: On a page like the Main Page, which includes many templates, |
| 66 | +one would typically want to have an unvandalized view of the entire |
| 67 | +construction. The Main Page itself rarely changes, but because the |
| 68 | +most recent revision is flagged as "unvandalized", it would be |
| 69 | +synchronized with templates for which this is also true. When viewing |
| 70 | +an older version, however, the templates would be shown as they were |
| 71 | +at that date&time. |
| 72 | + |
| 73 | +It is crucial that queries for revision lookup are highly performant; |
| 74 | +we should aim for a performance impact of less than 10% on uncached |
| 75 | +pageviews with a revision tag preference. Needless to say, the feature |
| 76 | +needs to interact correctly with Squid proxy caching. |
| 77 | + |
| 78 | +Tagging revisions should be possible from three places: |
| 79 | +- when editing (with the help of a collapsible diff) |
| 80 | +- when looking at diffs |
| 81 | +- when looking at revisions without any prior or later tags. |
| 82 | + |
| 83 | +A tag can only be set with reference to a diff to the last version |
| 84 | +that has the same tag. The Special:Recentchanges tool should be |
| 85 | +customizable to have such a diff link to the last version with a given |
| 86 | +tag. It is desirable that this view also includes an icon that |
| 87 | +indicates the state of the logged revision (derived from the tag |
| 88 | +stylesheets). |
| 89 | + |
| 90 | +Wishlist items for the future include things like mass vandalism |
| 91 | +review and advanced queries. I also have some ideas for phase II of |
| 92 | +the project, which I would love to see implemented before the tool is |
| 93 | +switched on on the English Wikipedia, but this will wait until phase I |
| 94 | +and any adjustments needed for its operations are successfully |
| 95 | +completed. |
Property changes on: branches/jhb/phase3/extensions/FlaggedRevs/specs.txt |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 96 | + native |
Index: branches/jhb/phase3/extensions/FlaggedRevs/SpecialMakevalidate.php |
— | — | @@ -0,0 +1,65 @@ |
| 2 | +<?php
|
| 3 | +
|
| 4 | +/**
|
| 5 | + * Special page to allow local bureaucrats to grant/revoke the reviewer flag
|
| 6 | + * for a particular user
|
| 7 | + *
|
| 8 | + * @addtogroup Extensions
|
| 9 | + * Tiny modifications by Aaron Schulz to MakeBot
|
| 10 | + * MakeBot extension:
|
| 11 | + ** @author Rob Church <robchur@gmail.com>
|
| 12 | + ** @copyright © 2006 Rob Church
|
| 13 | + ** @licence GNU General Public Licence 2.0 or later
|
| 14 | + */
|
| 15 | +
|
| 16 | +if( defined( 'MEDIAWIKI' ) ) {
|
| 17 | +
|
| 18 | + define( 'MW_MAKEVALIDATE_GRANT', 1 );
|
| 19 | + define( 'MW_MAKEVALIDATE_REVOKE', 2 );
|
| 20 | +
|
| 21 | + $wgExtensionFunctions[] = 'efMakevalidate';
|
| 22 | + $wgAvailableRights[] = 'makevalidate';
|
| 23 | + $wgExtensionCredits['specialpage'][] = array(
|
| 24 | + 'name' => 'MakeBot',
|
| 25 | + 'author' => 'Rob Church',
|
| 26 | + 'url' => 'http://www.mediawiki.org/wiki/Extension:MakeBot',
|
| 27 | + 'description' => 'Special page allows local bureaucrats to grant and revoke bot permissions',
|
| 28 | + );
|
| 29 | +
|
| 30 | + /**
|
| 31 | + * Determines who can use the extension; as a default, bureaucrats are permitted
|
| 32 | + */
|
| 33 | + $wgGroupPermissions['bureaucrat']['makevalidate'] = true;
|
| 34 | +
|
| 35 | + /**
|
| 36 | + * Toggles whether or not a bot flag can be given to a user who is also a sysop or bureaucrat
|
| 37 | + */
|
| 38 | + $wgMakeBotPrivileged = false;
|
| 39 | +
|
| 40 | + /**
|
| 41 | + * Register the special page
|
| 42 | + */
|
| 43 | + $wgAutoloadClasses['Makevalidate'] = dirname( __FILE__ ) . '/Makevalidate.class.php';
|
| 44 | + $wgSpecialPages['Makevalidate'] = 'Makevalidate';
|
| 45 | +
|
| 46 | + /**
|
| 47 | + * Populate the message cache and set up the auditing
|
| 48 | + */
|
| 49 | + function efMakeValidate() {
|
| 50 | + global $wgMessageCache, $wgLogTypes, $wgLogNames, $wgLogHeaders, $wgLogActions;
|
| 51 | + require_once( dirname( __FILE__ ) . '/Makevalidate.i18n.php' );
|
| 52 | + foreach( efMakeValidateMessages() as $lang => $messages )
|
| 53 | + $wgMessageCache->addMessages( $messages, $lang );
|
| 54 | + $wgLogTypes[] = 'validate';
|
| 55 | + $wgLogNames['validate'] = 'makevalidate-logpage';
|
| 56 | + $wgLogHeaders['validate'] = 'makevalidate-logpagetext';
|
| 57 | + $wgLogActions['validate/grant'] = 'makevalidate-logentrygrant';
|
| 58 | + $wgLogActions['validate/revoke'] = 'makevalidate-logentryrevoke';
|
| 59 | + }
|
| 60 | +
|
| 61 | +} else {
|
| 62 | + echo( "This file is an extension to the MediaWiki software and cannot be used standalone.\n" );
|
| 63 | + exit( 1 );
|
| 64 | +}
|
| 65 | +
|
| 66 | +?>
|
Index: branches/jhb/phase3/extensions/FlaggedRevs/FlaggedRevs.php |
— | — | @@ -0,0 +1,766 @@ |
| 2 | +<? |
| 3 | +#(c) Joerg Baach, Aaron Schulz 2007 GPL |
| 4 | +/* |
| 5 | +Possible Hooks |
| 6 | +-------------- |
| 7 | + |
| 8 | +'BeforePageDisplay': Called just before outputting a page (all kinds of, |
| 9 | + articles, special, history, preview, diff, edit, ...) |
| 10 | + Can be used to set custom CSS/JS |
| 11 | +$out: OutputPage object |
| 12 | + |
| 13 | +'OutputPageBeforeHTML': a page has been processed by the parser and |
| 14 | +the resulting HTML is about to be displayed. |
| 15 | +$parserOutput: the parserOutput (object) that corresponds to the page |
| 16 | +$text: the text that will be displayed, in HTML (string) |
| 17 | +*/ |
| 18 | + |
| 19 | +if ( !defined( 'MEDIAWIKI' ) ) { |
| 20 | + echo "FlaggedRevs extension\n"; |
| 21 | + exit( 1 ); |
| 22 | +} |
| 23 | + |
| 24 | +$wgExtensionFunctions[] = 'efLoadReviewMessages'; |
| 25 | + |
| 26 | +# Internationilization |
| 27 | +function efLoadReviewMessages() { |
| 28 | + global $wgMessageCache, $RevisionreviewMessages; |
| 29 | + require( dirname( __FILE__ ) . '/FlaggedRevsPage.i18n.php' ); |
| 30 | + foreach ( $RevisionreviewMessages as $lang => $langMessages ) { |
| 31 | + $wgMessageCache->addMessages( $langMessages, $lang ); |
| 32 | + } |
| 33 | +} |
| 34 | + |
| 35 | +# Revision tagging can slow development... |
| 36 | +# For example, the main user base may become complacent, |
| 37 | +# treating flagged pages as "done", |
| 38 | +# or just be too damn lazy to always click "current". |
| 39 | +# We may just want non-user visitors to see reviewd pages by default. |
| 40 | +$wgFlaggedRevsAnonOnly = true; |
| 41 | +# Can users make comments that will show up below flagged revisions? |
| 42 | +$wgFlaggedRevComments = true; |
| 43 | +# How long to cache stable versions |
| 44 | +$wgFlaggedRevsExpire = 7 * 24 * 3600; |
| 45 | + |
| 46 | +$wgAvailableRights[] = 'review'; |
| 47 | +# Define our reviewer class |
| 48 | +$wgGroupPermissions['reviewer']['rollback'] = true; |
| 49 | +$wgGroupPermissions['reviewer']['patrol'] = true; |
| 50 | +$wgGroupPermissions['reviewer']['review'] = true; |
| 51 | + |
| 52 | +# Add review log |
| 53 | +$wgLogTypes[] = 'review'; |
| 54 | +$wgLogNames['review'] = 'review-logpage'; |
| 55 | +$wgLogHeaders['review'] = 'review-logpagetext'; |
| 56 | +$wgLogActions['review/approve'] = 'review-logentrygrant'; |
| 57 | +$wgLogActions['review/unapprove'] = 'review-logentryrevoke'; |
| 58 | + |
| 59 | +class FlaggedRevs { |
| 60 | + /* 50MB allows fixing those huge pages */ |
| 61 | + const MAX_INCLUDE_SIZE = 50000000; |
| 62 | + |
| 63 | + function __construct() { |
| 64 | + $this->dimensions = array( 'acc' => array( 0=>'acc-0', |
| 65 | + 1=>'acc-1', |
| 66 | + 2=>'acc-2', |
| 67 | + 3=>'acc-3'), |
| 68 | + 'depth' => array( 0=>'depth-0', |
| 69 | + 1=>'depth-1', |
| 70 | + 2=>'depth-2', |
| 71 | + 3=>'depth-3'), |
| 72 | + 'style' => array( 0=>'style-0', |
| 73 | + 1=>'style-1', |
| 74 | + 2=>'style-2', |
| 75 | + 3=>'style-3') ); |
| 76 | + } |
| 77 | + |
| 78 | + function pageOverride() { |
| 79 | + global $wgFlaggedRevsAnonOnly, $wgUser; |
| 80 | + return !( $wgFlaggedRevsAnonOnly && !$wgUser->isAnon() ); |
| 81 | + } |
| 82 | + |
| 83 | + function getFlaggedRevision( $rev_id ) { |
| 84 | + $db = wfGetDB( DB_SLAVE ); |
| 85 | + // select a row, this should be unique |
| 86 | + $result = $db->select( 'flaggedrevs', array('*'), array('fr_rev_id' => $rev_id) ); |
| 87 | + if( $row = $db->fetchObject($result) ) { |
| 88 | + return $row; |
| 89 | + } |
| 90 | + return NULL; |
| 91 | + } |
| 92 | + |
| 93 | + function getFlaggedRevText( $rev_id ) { |
| 94 | + $db = wfGetDB( DB_SLAVE ); |
| 95 | + // select a row, this should be unique |
| 96 | + $result = $db->select( 'flaggedtext', array('ft_text'), array('ft_rev_id' => $rev_id) ); |
| 97 | + if( $row = $db->fetchObject($result) ) { |
| 98 | + return $row->ft_text; |
| 99 | + } |
| 100 | + return NULL; |
| 101 | + } |
| 102 | + |
| 103 | + /** |
| 104 | + * @param string $text |
| 105 | + * @returns string |
| 106 | + * All included pages are expanded out to keep this text frozen |
| 107 | + */ |
| 108 | + function expandText( $text ) { |
| 109 | + global $wgParser, $wgTitle; |
| 110 | + // Do not treat this as paring an article on normal view |
| 111 | + // enter the title object as wgTitle |
| 112 | + $options = new ParserOptions; |
| 113 | + $options->setRemoveComments( true ); |
| 114 | + $options->setMaxIncludeSize( self::MAX_INCLUDE_SIZE ); |
| 115 | + $output = $wgParser->preprocess( $text, $wgTitle, $options ); |
| 116 | + return $output; |
| 117 | + } |
| 118 | + |
| 119 | + function getFlagsForRevision( $rev_id ) { |
| 120 | + // Set default blank flags |
| 121 | + $flags = array( 'acc' => 0, 'depth' => 0, 'style' => 0 ); |
| 122 | + |
| 123 | + $db = wfGetDB( DB_SLAVE ); |
| 124 | + // select a row, this should be unique |
| 125 | + $result = $db->select( 'flaggedrevs', array('*'), array('fr_rev_id' => $rev_id) ); |
| 126 | + if( $row = $db->fetchObject($result) ) { |
| 127 | + $flags = array( 'acc' => $row->fr_acc, 'depth' => $row->fr_dep, 'style' => $row->fr_sty ); |
| 128 | + } |
| 129 | + return $flags; |
| 130 | + } |
| 131 | + |
| 132 | + function getLatestFlaggedRev( $page_id ) { |
| 133 | + wfProfileIn( __METHOD__ ); |
| 134 | + |
| 135 | + $db = wfGetDB( DB_SLAVE ); |
| 136 | + // Skip deleted revisions |
| 137 | + $result = $db->select( |
| 138 | + array('flaggedrevs', 'revision'), |
| 139 | + array('*'), |
| 140 | + array( 'fr_page_id' => $page_id, 'rev_id = fr_rev_id', 'rev_deleted = 0'), |
| 141 | + __METHOD__ , |
| 142 | + array('ORDER BY' => 'fr_rev_id DESC') ); |
| 143 | + // Sorted from highest to lowest, so just take the first one if any |
| 144 | + if ( $row = $db->fetchObject( $result ) ) { |
| 145 | + return $row; |
| 146 | + } |
| 147 | + return NULL; |
| 148 | + } |
| 149 | + |
| 150 | + function getReviewedRevs( $page_id ) { |
| 151 | + wfProfileIn( __METHOD__ ); |
| 152 | + |
| 153 | + $db = wfGetDB( DB_SLAVE ); |
| 154 | + $rows = array(); |
| 155 | + // Skip deleted revisions |
| 156 | + $result = $db->select( |
| 157 | + array('flaggedrevs', 'revision'), |
| 158 | + array('*'), |
| 159 | + array( 'fr_page_id' => $page_id, 'rev_id = fr_rev_id', 'rev_deleted = 0'), |
| 160 | + __METHOD__ , |
| 161 | + array('ORDER BY' => 'fr_rev_id DESC') ); |
| 162 | + while ( $row = $db->fetchObject( $result ) ) { |
| 163 | + $rows[] = $row; |
| 164 | + } |
| 165 | + return $rows; |
| 166 | + } |
| 167 | + |
| 168 | + function getUnreviewedRevCount( $page_id, $from_rev ) { |
| 169 | + wfProfileIn( __METHOD__ ); |
| 170 | + |
| 171 | + $db = wfGetDB( DB_SLAVE ); |
| 172 | + $result = $db->select( |
| 173 | + array('revision'), |
| 174 | + array('rev_page'), |
| 175 | + array( 'rev_page' => $page_id, "rev_id > $from_rev" ), |
| 176 | + __METHOD__ , |
| 177 | + array('ORDER BY' => 'rev_id DESC') ); |
| 178 | + // Return count of revisions |
| 179 | + return $db->numRows($result); |
| 180 | + } |
| 181 | + |
| 182 | + function parseStableText( $title, $text, $id=NULL, $options, $returnHTML=true ) { |
| 183 | + global $wgUser, $wgParser, $wgUploadDirectory, $wgUseSharedUploads, $wgUploadPath; |
| 184 | + # hack...temporarily change image directories |
| 185 | + # There is no nice option to set this for each parse. |
| 186 | + # This lets the parser know where to look... |
| 187 | + $uploadPath = $wgUploadPath; |
| 188 | + $uploadDir = $wgUploadDirectory; |
| 189 | + $useSharedUploads = $wgUseSharedUploads; |
| 190 | + # Stable thumbnails need to have the right path |
| 191 | + $wgUploadPath = ($wgUploadPath) ? "{$uploadPath}/stable" : false; |
| 192 | + # Create <img> tags with the right url |
| 193 | + $wgUploadDirectory = "{$uploadDir}/stable"; |
| 194 | + # Stable images are never stored at commons |
| 195 | + $wgUseSharedUploads = false; |
| 196 | + |
| 197 | + $options->setTidy(true); |
| 198 | + # Don't show section-edit links |
| 199 | + # They can be old and misleading |
| 200 | + $options->setEditSection(false); |
| 201 | + # Parse the new body, wikitext -> html |
| 202 | + $parserOut = $wgParser->parse( $text, $title, $options, true, true, $id ); |
| 203 | + if ( !$returnHTML ) |
| 204 | + return $parserOut; |
| 205 | + |
| 206 | + $HTMLout = $parserOut->getText(); |
| 207 | + # hack... |
| 208 | + $HTMLout = $this->proportionalImgScaling($HTMLout); |
| 209 | + |
| 210 | + # Reset our image directories |
| 211 | + $wgUploadPath = $uploadPath; |
| 212 | + $wgUploadDirectory = $uploadDir; |
| 213 | + $wgUseSharedUploads = $useSharedUploads; |
| 214 | + |
| 215 | + return $HTMLout; |
| 216 | + } |
| 217 | + |
| 218 | + function proportionalImgScaling( $text ) { |
| 219 | + # goddamn hack... |
| 220 | + # Thumbnails are stored based on width, don't do any unscaled resizing |
| 221 | + # MW will add height/width based on the metadata in the db for the current image |
| 222 | + $text = preg_replace( '/(<img[^<>]+ )height="\d+" ([^<>]+>)/i','$1$2', $text); |
| 223 | + return $text; |
| 224 | + } |
| 225 | + |
| 226 | + function setPageContent( &$out ) { |
| 227 | + global $wgArticle, $wgRequest, $wgTitle, $wgOut, $action; |
| 228 | + // Only trigger on article view, not for protect/delete/hist |
| 229 | + // Talk pages cannot be validated |
| 230 | + if( !$wgArticle || !$wgTitle->isContentPage() || $action !='view' ) |
| 231 | + return; |
| 232 | + // Find out revision id |
| 233 | + $revid = ( $wgArticle->mRevision ) ? $wgArticle->mRevision->mId : $wgArticle->getLatest(); |
| 234 | + // Grab the ratings for this revision if any |
| 235 | + if( !$revid ) return; |
| 236 | + $visible_id = $revid; |
| 237 | + |
| 238 | + // Set new body html text as that of now |
| 239 | + $flaghtml = ''; $newbodytext = $out->mBodytext; |
| 240 | + // Check the newest stable version |
| 241 | + $top_frev = $this->getLatestFlaggedRev( $wgArticle->getId() ); |
| 242 | + if( $wgRequest->getVal('diff') ) { |
| 243 | + // Do not clutter up diffs any further... |
| 244 | + } else if( $top_frev ) { |
| 245 | + global $wgParser, $wgLang; |
| 246 | + // Parse the timestamp |
| 247 | + $time = $wgLang->timeanddate( wfTimestamp(TS_MW, $top_frev->fr_timestamp), true ); |
| 248 | + // Grab the flags |
| 249 | + $flags = $this->getFlagsForRevision( $top_frev->fr_rev_id ); |
| 250 | + # Looking at some specific old rev or if flagged revs override only for anons |
| 251 | + if( $wgRequest->getVal('oldid') || !$this->pageOverride() ) { |
| 252 | + if( $revid==$top_frev->rev_id ) { |
| 253 | + $flaghtml = wfMsgExt('revreview-isnewest', array('parse'),$time); |
| 254 | + } else { |
| 255 | + # Our compare link should have a reasonable time-ordered old->new combination |
| 256 | + $oldid = ($revid > $top_frev->fr_rev_id) ? $top_frev->fr_rev_id : $revid; |
| 257 | + $diff = ($revid > $top_frev->fr_rev_id) ? $revid : $top_frev->fr_rev_id; |
| 258 | + $flaghtml = wfMsgExt('revreview-newest', array('parse'), $top_frev->fr_rev_id, $oldid, $diff, $time ); |
| 259 | + } |
| 260 | + } # Viewing the page normally: override the page |
| 261 | + else { |
| 262 | + global $wgUser; |
| 263 | + # We will be looking at the reviewed revision... |
| 264 | + $visible_id = $top_frev->fr_rev_id; |
| 265 | + $revs_since = $this->getUnreviewedRevCount( $wgArticle->getId(), $visible_id ); |
| 266 | + $flaghtml = wfMsgExt('revreview-replaced', array('parse'), $visible_id, $wgArticle->getLatest(), $revs_since, $time ); |
| 267 | + $newbodytext = NULL; |
| 268 | + # Try the stable cache |
| 269 | + $newbodytext = $this->getPageCache( $wgArticle ); |
| 270 | + # If no cache is available, get the text and parse it |
| 271 | + if ( is_null($newbodytext) ) { |
| 272 | + $text = $this->getFlaggedRevText( $visible_id ); |
| 273 | + # For anons, use standard prefs, for users, get theirs |
| 274 | + $options = ParserOptions::newFromUser($wgUser); |
| 275 | + # Parsing this text is kind of funky... |
| 276 | + $newbodytext = $this->parseStableText( $wgTitle, $text, $visible_id, $options ); |
| 277 | + # Update the general cache for non-users |
| 278 | + $this->updatePageCache( $wgArticle, $newbodytext ); |
| 279 | + } |
| 280 | + } |
| 281 | + // Construct some tagging |
| 282 | + $flaghtml .= "<table align='center' cellspadding=\'0\'><tr>"; |
| 283 | + foreach ( $this->dimensions as $quality => $value ) { |
| 284 | + $value = wfMsgHtml('revreview-' . $this->dimensions[$quality][$flags[$quality]]); |
| 285 | + $flaghtml .= "<td> <strong>" . wfMsgHtml("revreview-$quality") . "</strong>: $value </td>\n"; |
| 286 | + } |
| 287 | + $flaghtml .= '</tr></table>'; |
| 288 | + // Should use CSS? |
| 289 | + $flaghtml = "<small>$flaghtml</small>"; |
| 290 | + |
| 291 | + // Set the new body HTML, place a tag on top |
| 292 | + $out->mBodytext = '<div class="mw-warning plainlinks">' . $flaghtml . '</div>' . $newbodytext; |
| 293 | + // Add any notes at the bottom |
| 294 | + $this->addReviewNotes( $top_frev ); |
| 295 | + } else { |
| 296 | + $flaghtml = wfMsgExt('revreview-noflagged', array('parse')); |
| 297 | + $out->mBodytext = '<div class="mw-warning plainlinks">' . $flaghtml . '</div>' . $out->mBodytext; |
| 298 | + } |
| 299 | + // Override our reference ID for permalink/citation hooks |
| 300 | + $wgArticle->mRevision = Revision::newFromId( $visible_id ); |
| 301 | + // Show review links for the VISIBLE revision |
| 302 | + // We cannot review deleted revisions |
| 303 | + if( is_object($wgArticle->mRevision) && $wgArticle->mRevision->mDeleted ) return; |
| 304 | + // Add quick review links IF we did not override, otherwise, they might |
| 305 | + // review a revision that parses out newer templates/images than what they say |
| 306 | + // Note: overrides are never done when viewing with "oldid=" |
| 307 | + if( $visible_id==$revid || !$this->pageOverride() ) { |
| 308 | + $this->addQuickReview( $visible_id, false, $out ); |
| 309 | + } |
| 310 | + } |
| 311 | + |
| 312 | + function addToEditView( &$editform ) { |
| 313 | + global $wgRequest, $wgTitle, $wgOut; |
| 314 | + // Talk pages cannot be validated |
| 315 | + if( !$editform->mArticle || !$wgTitle->isContentPage() ) |
| 316 | + return; |
| 317 | + // Find out revision id |
| 318 | + if( $editform->mArticle->mRevision ) |
| 319 | + $revid = $editform->mArticle->mRevision->mId; |
| 320 | + else |
| 321 | + $revid = $editform->mArticle->getLatest(); |
| 322 | + // Grab the ratings for this revision if any |
| 323 | + if( !$revid ) return; |
| 324 | + |
| 325 | + // Set new body html text as that of now |
| 326 | + $flaghtml = ''; |
| 327 | + // Check the newest stable version |
| 328 | + $top_frev = $this->getLatestFlaggedRev( $editform->mArticle->getId() ); |
| 329 | + if( is_object($top_frev) ) { |
| 330 | + global $wgParser, $wgLang; |
| 331 | + $time = $wgLang->timeanddate( wfTimestamp(TS_MW, $top_frev->fr_timestamp), true ); |
| 332 | + $flags = $this->getFlagsForRevision( $top_frev->fr_rev_id ); |
| 333 | + # Looking at some specific old rev |
| 334 | + if( $wgRequest->getVal('oldid') ) { |
| 335 | + if( $revid==$top_frev->rev_id ) { |
| 336 | + $flaghtml = wfMsgExt('revreview-isnewest', array('parse'),$time); |
| 337 | + } else { |
| 338 | + # Our compare link should have a reasonable time-ordered old->new combination |
| 339 | + $oldid = ($revid > $top_frev->fr_rev_id) ? $top_frev->fr_rev_id : $revid; |
| 340 | + $diff = ($revid > $top_frev->fr_rev_id) ? $revid : $top_frev->fr_rev_id; |
| 341 | + $flaghtml = wfMsgExt('revreview-newest', array('parse'), $top_frev->fr_rev_id, $oldid, $diff, $time ); |
| 342 | + } |
| 343 | + } # Editing the page normally |
| 344 | + else { |
| 345 | + if( $revid==$top_frev->rev_id ) |
| 346 | + $flaghtml = wfMsgExt('revreview-isnewest', array('parse'), $time); |
| 347 | + else |
| 348 | + $flaghtml = wfMsgExt('revreview-newest', array('parse'), $top_frev->fr_rev_id, $top_frev->fr_rev_id, $revid, $time ); |
| 349 | + } |
| 350 | + // Construct some tagging |
| 351 | + $flaghtml .= "<table align='center' cellpadding=\'0\'><tr>"; |
| 352 | + foreach ( $this->dimensions as $quality => $value ) { |
| 353 | + $value = wfMsgHtml('revreview-' . $this->dimensions[$quality][$flags[$quality]]); |
| 354 | + $flaghtml .= "<td> <strong>" . wfMsgHtml("revreview-$quality") . "</strong>: $value </td>\n"; |
| 355 | + } |
| 356 | + $flaghtml .= '</tr></table>'; |
| 357 | + // Should use CSS? |
| 358 | + $flaghtml = "<small>$flaghtml</small>"; |
| 359 | + $wgOut->addHTML( '<div class="mw-warning plainlinks">' . $flaghtml . '</div><br/>' ); |
| 360 | + } |
| 361 | + } |
| 362 | + |
| 363 | + function addToDiff( &$diff, &$oldrev, &$newrev ) { |
| 364 | + $id = $newrev->getId(); |
| 365 | + // We cannot review deleted edits |
| 366 | + if( $newrev->mDeleted ) return; |
| 367 | + $this->addQuickReview( $id, true ); |
| 368 | + } |
| 369 | + |
| 370 | + function setCurrentTab( &$sktmp, &$content_actions ) { |
| 371 | + global $wgRequest, $wgArticle, $action; |
| 372 | + // Only trigger on article view, not for protect/delete/hist |
| 373 | + // Non-content pages cannot be validated |
| 374 | + if( !$wgArticle || !$sktmp->mTitle->exists() || !$sktmp->mTitle->isContentPage() || $action !='view' ) |
| 375 | + return; |
| 376 | + // If we are viewing a page normally, and it was overrode |
| 377 | + // change the edit tab to a "current revision" tab |
| 378 | + if( !$wgRequest->getVal('oldid') ) { |
| 379 | + $top_frev = $this->getLatestFlaggedRev( $wgArticle->getId() ); |
| 380 | + // Note that revisions may not be set to override for users |
| 381 | + if( is_object($top_frev) && $this->pageOverride() ) { |
| 382 | + # Remove edit option altogether |
| 383 | + unset( $content_actions['edit']); |
| 384 | + unset( $content_actions['viewsource']); |
| 385 | + # Straighten out order |
| 386 | + $new_actions = array(); $counter = 0; |
| 387 | + foreach ( $content_actions as $action => $data ) { |
| 388 | + if( $counter==1 ) { |
| 389 | + # Set current rev tab AFTER the main tab is set |
| 390 | + $new_actions['current'] = array( |
| 391 | + 'class' => '', |
| 392 | + 'text' => wfMsg('currentrev'), |
| 393 | + 'href' => $sktmp->mTitle->getLocalUrl( 'oldid=' . $wgArticle->getLatest() ) |
| 394 | + ); |
| 395 | + } |
| 396 | + $new_actions[$action] = $data; |
| 397 | + $counter++; |
| 398 | + } |
| 399 | + # Reset static array |
| 400 | + $content_actions = $new_actions; |
| 401 | + } |
| 402 | + } |
| 403 | + } |
| 404 | + |
| 405 | + function addToPageHist( &$article ) { |
| 406 | + $this->pageFlaggedRevs = array(); |
| 407 | + $rows = $this->getReviewedRevs( $article->getID() ); |
| 408 | + if( !$rows ) return; |
| 409 | + foreach( $rows as $row => $data ) { |
| 410 | + $this->pageFlaggedRevs[] = $data->rev_id; |
| 411 | + } |
| 412 | + } |
| 413 | + |
| 414 | + function addToHistLine( &$row, &$s ) { |
| 415 | + if( isset($this->pageFlaggedRevs) ) { |
| 416 | + if( in_array( $row->rev_id, $this->pageFlaggedRevs ) ) |
| 417 | + $s .= ' <small><strong>' . wfMsgHtml('revreview-hist') . '</strong></small>'; |
| 418 | + } |
| 419 | + } |
| 420 | + |
| 421 | + function addQuickReview( $id, $ontop=false, &$out=false ) { |
| 422 | + global $wgOut, $wgTitle, $wgUser, $wgScript; |
| 423 | + // We don't want two forms! |
| 424 | + if( isset($this->formCount) && $this->formCount > 0 ) return; |
| 425 | + $this->formCount = 1; |
| 426 | + |
| 427 | + if( !$wgUser->isAllowed( 'review' ) ) return; |
| 428 | + |
| 429 | + $flags = $this->getFlagsForRevision( $id ); |
| 430 | + |
| 431 | + $reviewtitle = SpecialPage::getTitleFor( 'Revisionreview' ); |
| 432 | + $form = Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) ); |
| 433 | + $form .= "<fieldset><legend>" . wfMsgHtml( 'revreview-flag', $id ) . "</legend>\n"; |
| 434 | + $form .= wfHidden( 'title', $reviewtitle->getPrefixedText() ); |
| 435 | + $form .= wfHidden( 'target', $wgTitle->getPrefixedText() ); |
| 436 | + $form .= wfHidden( 'oldid', $id ); |
| 437 | + foreach ( $this->dimensions as $quality => $levels ) { |
| 438 | + $form .= wfMsgHtml("revreview-$quality") . ": <select name='$quality'>\n"; |
| 439 | + foreach ( $levels as $idx => $label ) { |
| 440 | + if( $flags[$quality]==$idx ) |
| 441 | + $selected = 'selected'; |
| 442 | + else |
| 443 | + $selected = ''; |
| 444 | + $form .= "<option value='$idx' $selected>" . wfMsgHtml("revreview-$label") . "</option>\n"; |
| 445 | + } |
| 446 | + $form .= "</select>\n"; |
| 447 | + } |
| 448 | + $form .= Xml::submitButton( wfMsgHtml( 'go' ) ) . "</fieldset>"; |
| 449 | + $form .= Xml::closeElement( 'form' ); |
| 450 | + // Hacks, to fiddle around with location a bit |
| 451 | + if( $ontop && $out ) { |
| 452 | + $out->mBodytext = $form . '<hr/>' . $out->mBodytext; |
| 453 | + } else if( $ontop ) { |
| 454 | + $wgOut->addHTML( $form ); |
| 455 | + } else { |
| 456 | + $wgOut->addHTML( $form ); |
| 457 | + } |
| 458 | + } |
| 459 | + |
| 460 | + function addReviewNotes( $row, $breakline=true ) { |
| 461 | + global $wgOut, $wgUser, $wgFlaggedRevComments; |
| 462 | + |
| 463 | + if( !$row || !$wgFlaggedRevComments) return; |
| 464 | + |
| 465 | + $this->skin = $wgUser->getSkin(); |
| 466 | + if( $row->fr_comment ) { |
| 467 | + $notes = ($breakline) ? '<hr/><br/>' : ''; |
| 468 | + $notes .= '<div class="mw-warning plainlinks">'; |
| 469 | + $notes .= wfMsgExt('revreview-note', array('parse'), User::whoIs( $row->fr_user ) ); |
| 470 | + $notes .= '<i>' . $this->skin->formatComment( $row->fr_comment ) . '</i></div>'; |
| 471 | + $wgOut->addHTML( $notes ); |
| 472 | + } |
| 473 | + } |
| 474 | + |
| 475 | + /** |
| 476 | + * Get all local image files and generate an array of them |
| 477 | + * @param string $s, wikitext |
| 478 | + * $output array, (string title array, string thumbnail array) |
| 479 | + */ |
| 480 | + function findLocalImages( $s ) { |
| 481 | + global $wgUploadPath; |
| 482 | + |
| 483 | + $fname = 'findLocalImages'; |
| 484 | + $imagelist = array(); $thumblist = array(); |
| 485 | + |
| 486 | + if( !$s || !strval($s) ) return $imagelist; |
| 487 | + |
| 488 | + static $tc = FALSE; |
| 489 | + # the % is needed to support urlencoded titles as well |
| 490 | + if( !$tc ) { $tc = Title::legalChars() . '#%'; } |
| 491 | + |
| 492 | + # split the entire text string on occurences of [[ |
| 493 | + $a = explode( '[[', $s ); |
| 494 | + |
| 495 | + # Ignore things that start with colons, they are image links, not images |
| 496 | + $e1_img = "/^([:{$tc}]+)(.+)$/sD"; |
| 497 | + # Loop for each link |
| 498 | + for ($k = 0; isset( $a[$k] ); $k++) { |
| 499 | + if( preg_match( $e1_img, $a[$k], $m ) ) { |
| 500 | + # page with normal text or alt of form x or ns:x |
| 501 | + $nt = Title::newFromText( $m[1] ); |
| 502 | + $ns = $nt->getNamespace(); |
| 503 | + # add if this is an image |
| 504 | + if( $ns == NS_IMAGE ) { |
| 505 | + $imagelist[] = $nt->getPrefixedText(); |
| 506 | + } |
| 507 | + $image = $nt->getDBKey(); |
| 508 | + # check for data for thumbnails |
| 509 | + $part = array_map( 'trim', explode( '|', $m[2]) ); |
| 510 | + foreach( $part as $val ) { |
| 511 | + if( preg_match( '/^([0-9]+)px$/', $val, $n ) ) { |
| 512 | + $width = intval( $n[1] ); |
| 513 | + $thumblist[$image] = $width; |
| 514 | + } else if( preg_match( '/^([0-9]+)x([0-9]+)$/', $val, $n ) ) { |
| 515 | + $width = intval( $n[1] ); |
| 516 | + $thumblist[$image] = $width; |
| 517 | + } |
| 518 | + } |
| 519 | + } |
| 520 | + } |
| 521 | + return array( $imagelist, $thumblist ); |
| 522 | + } |
| 523 | + |
| 524 | + /** |
| 525 | + * Showtime! Copy all used images to a stable directory |
| 526 | + * This updates (overwrites) any existing stable images |
| 527 | + * Won't work for sites with unhashed dirs that have subfolders protected |
| 528 | + * The future FileStore migration might effect this, not sure... |
| 529 | + * @param array $imagelist, list of string names |
| 530 | + * $output array, list of string names of images sucessfully cloned |
| 531 | + */ |
| 532 | + function makeStableImages( $imagelist ) { |
| 533 | + global $wgUploadDirectory, $wgSharedUploadDirectory; |
| 534 | + // All stable images are local, not shared |
| 535 | + // Otherwise, we could have some nasty cross language/wiki conflicts |
| 536 | + $stableDir = "$wgUploadDirectory/stable"; |
| 537 | + // Copy images to stable dir |
| 538 | + $usedimages = array(); |
| 539 | + // We need valid input |
| 540 | + if( !is_array($imagelist) ) return $usedimages; |
| 541 | + foreach ( $imagelist as $name ) { |
| 542 | + // We want a clean and consistant title entry |
| 543 | + $nt = Title::newFromText( $name ); |
| 544 | + if( is_null($nt) ) { |
| 545 | + // If this title somehow doesn't work, ignore it |
| 546 | + // this shouldn't happen... |
| 547 | + continue; |
| 548 | + } |
| 549 | + $name = $nt->getDBkey(); |
| 550 | + $hash = wfGetHashPath($name); |
| 551 | + $path = $wgUploadDirectory . $hash; |
| 552 | + $sharedpath = $wgSharedUploadDirectory . $hash; |
| 553 | + // Try local repository |
| 554 | + if( is_dir($path) ) { |
| 555 | + if( file_exists("{$path}{$name}") ) { |
| 556 | + // Check if our stable dir exists |
| 557 | + // Make it if it doesn't |
| 558 | + if( !is_dir($stableDir . $hash) ) { |
| 559 | + wfMkdirParents($stableDir . $hash); |
| 560 | + } |
| 561 | + copy("{$path}{$name}","{$stableDir}{$hash}{$name}"); |
| 562 | + $usedimages[] = $name; |
| 563 | + } |
| 564 | + } // Try shared repository |
| 565 | + else if( is_dir($sharedpath) ) { |
| 566 | + if( file_exists("{$sharedpath}{$name}") ) { |
| 567 | + // Check if our stable dir exists |
| 568 | + // Make it if it doesn't |
| 569 | + if( !is_dir($stableDir . $hash) ) { |
| 570 | + wfMkdirParents($stableDir . $hash); |
| 571 | + } |
| 572 | + copy("{$sharedpath}{$name}","{$stableDir}{$hash}{$name}"); |
| 573 | + $usedimages[] = $name; |
| 574 | + } |
| 575 | + } |
| 576 | + } |
| 577 | + return $usedimages; |
| 578 | + } |
| 579 | + |
| 580 | + /** |
| 581 | + * Delete an a list of stable image files |
| 582 | + * @param array $imagelist, list of string names |
| 583 | + * $output array, list of string names of images to be deleted |
| 584 | + */ |
| 585 | + function deleteStableImages( $imagelist ) { |
| 586 | + global $wgSharedUploadDirectory; |
| 587 | + // All stable images are local, not shared |
| 588 | + // Otherwise, we could have some nasty cross language/wiki conflicts |
| 589 | + $stableDir = "$wgUploadDirectory/stable"; |
| 590 | + // Copy images to stable dir |
| 591 | + $deletedimages = array(); |
| 592 | + // We need valid input |
| 593 | + if( !is_array($imagelist) ) return $usedimages; |
| 594 | + foreach ( $imagelist as $name ) { |
| 595 | + // We want a clean and consistant title entry |
| 596 | + $nt = Title::newFromText( $name ); |
| 597 | + if( is_null($nt) ) { |
| 598 | + // If this title somehow doesn't work, ignore it |
| 599 | + // this shouldn't happen... |
| 600 | + continue; |
| 601 | + } |
| 602 | + $name = $nt->getDBkey(); |
| 603 | + $hash = wfGetHashPath($name); |
| 604 | + $path = $stableDir . $hash; |
| 605 | + // Try the stable repository |
| 606 | + if( is_dir($path) ) { |
| 607 | + if( file_exists("{$path}{$name}") ) { |
| 608 | + // Delete! |
| 609 | + delete("{$path}{$name}"); |
| 610 | + $deletedimages[] = $name; |
| 611 | + } |
| 612 | + } |
| 613 | + } |
| 614 | + return $deletedimages; |
| 615 | + } |
| 616 | + |
| 617 | + /** |
| 618 | + * Delete an a list of stable image thumbnails |
| 619 | + * New thumbnails don't normally override old ones, causing outdated images |
| 620 | + * This allows for tagged revisions to be re-reviewed with newer images |
| 621 | + * @param array $imagelist, list of string names |
| 622 | + * $output array, list of string names of images to be deleted |
| 623 | + */ |
| 624 | + function purgeStableThumbnails( $thumblist ) { |
| 625 | + global $wgUploadDirectory, $wgUseImageResize; |
| 626 | + // Are thumbs even enabled? |
| 627 | + if ( !$wgUseImageResize ) return true; |
| 628 | + // We need valid input |
| 629 | + if( !is_array($thumblist) ) return false; |
| 630 | + foreach ( $thumblist as $name => $width ) { |
| 631 | + $thumburl = "{$wgUploadDirectory}/stable/thumb" . wfGetHashPath( $name, false ) . "$name/". $width."px-".$name; |
| 632 | + if( file_exists($thumburl) ) { |
| 633 | + unlink($thumburl); |
| 634 | + } |
| 635 | + } |
| 636 | + return true; |
| 637 | + } |
| 638 | + |
| 639 | + /** |
| 640 | + * Update the stable image usage table |
| 641 | + * Add some images if not redundant |
| 642 | + * @param array $imagelist, list of string names |
| 643 | + * $output bool, on succeed |
| 644 | + */ |
| 645 | + function insertStableImages( $revid, $imagelist ) { |
| 646 | + wfProfileIn( __METHOD__ ); |
| 647 | + |
| 648 | + if( !is_array($imagelist) ) return false; |
| 649 | + |
| 650 | + $db = wfGetDB( DB_MASTER ); |
| 651 | + foreach( $imagelist as $name ) { |
| 652 | + // We want a clean and consistant title entry |
| 653 | + $nt = Title::newFromText( $name ); |
| 654 | + if( is_null($nt) ) { |
| 655 | + // If this title somehow doesn't work, ignore it |
| 656 | + // this shouldn't happen... |
| 657 | + continue; |
| 658 | + } |
| 659 | + $imagename = $nt->getDBkey(); |
| 660 | + // Add image and the revision that uses it |
| 661 | + $set = array('fi_rev_id' => $revid, 'fi_name' => $imagename); |
| 662 | + // Add entries or replace any that have the same rev_id |
| 663 | + $db->replace( 'flaggedimages', array( array('fi_rev_id', 'fi_name') ), $set, __METHOD__ ); |
| 664 | + } |
| 665 | + return true; |
| 666 | + } |
| 667 | + |
| 668 | + /** |
| 669 | + * Update the stable image usage table |
| 670 | + * Clean out unused images if needed |
| 671 | + * @param array $imagelist, list of string names |
| 672 | + * $output bool, on succeed |
| 673 | + */ |
| 674 | + function removeStableImages( $revid, $imagelist ) { |
| 675 | + wfProfileIn( __METHOD__ ); |
| 676 | + |
| 677 | + if( !is_array($imagelist) ) return false; |
| 678 | + $unusedimages = array(); |
| 679 | + $db = wfGetDB( DB_MASTER ); |
| 680 | + foreach( $imagelist as $name ) { |
| 681 | + // We want a clean and consistant title entry |
| 682 | + $nt = Title::newFromText( $name ); |
| 683 | + if( is_null($nt) ) { |
| 684 | + // If this title somehow doesn't work, ignore it |
| 685 | + // this shouldn't happen... |
| 686 | + continue; |
| 687 | + } |
| 688 | + $imagename = $nt->getDBkey(); |
| 689 | + $where = array( 'fi_rev_id' => $revid, 'fi_name' => $imagename ); |
| 690 | + // See how many revisions use this image total... |
| 691 | + $result = $db->select( 'flaggedimages', array('fi_id'), array( 'fi_name' => $imagename ) ); |
| 692 | + // If only one, then delete the image |
| 693 | + // since it's about to be remove from that one |
| 694 | + if( $db->numRows($result)==1 ) { |
| 695 | + $unusedimages[] = $imagename; |
| 696 | + } |
| 697 | + // Clear out this revision's entry |
| 698 | + $db->delete( 'flaggedimages', $where ); |
| 699 | + } |
| 700 | + $this->deleteStableImages( $unusedimages ); |
| 701 | + return true; |
| 702 | + } |
| 703 | + |
| 704 | + function getPageCache( $article ) { |
| 705 | + global $wgUser, $wgFlaggedRevsExpire; |
| 706 | + |
| 707 | + wfProfileIn( __METHOD__ ); |
| 708 | + |
| 709 | + // Make sure it is valid |
| 710 | + if ( !$article || !$article->getId() ) return NULL; |
| 711 | + $cachekey = ParserCache::getKey( $article, $wgUser ); |
| 712 | + |
| 713 | + $db = wfGetDB( DB_SLAVE ); |
| 714 | + $cutoff = $db->timestamp( time() - $wgFlaggedRevsExpire ); |
| 715 | + // Replace the page cache if it is out of date |
| 716 | + $result = $db->select( |
| 717 | + array('flaggedcache'), |
| 718 | + array('fc_cache'), |
| 719 | + array('fc_key' => $cachekey, 'fc_date >= ' . $article->getTouched(), 'fc_date >= ' . $cutoff ), |
| 720 | + __METHOD__); |
| 721 | + if ( $row = $db->fetchObject($result) ) { |
| 722 | + return $row->fc_cache; |
| 723 | + } |
| 724 | + return NULL; |
| 725 | + } |
| 726 | + |
| 727 | + function updatePageCache( $article, $value=NULL ) { |
| 728 | + global $wgUser; |
| 729 | + wfProfileIn( __METHOD__ ); |
| 730 | + |
| 731 | + // Make sure it is valid |
| 732 | + if ( is_null($value) || !$article || !$article->getId() ) return false; |
| 733 | + $cachekey = ParserCache::getKey( $article, $wgUser ); |
| 734 | + // Add cache mark |
| 735 | + $timestamp = wfTimestampNow(); |
| 736 | + $value .= "\n<!-- Saved in stable version parser cache with key $cachekey and timestamp $timestamp -->"; |
| 737 | + |
| 738 | + $dbw = wfGetDB( DB_MASTER ); |
| 739 | + // Replace the page cache if it is out of date |
| 740 | + $dbw->replace('flaggedcache', |
| 741 | + array('fc_key'), |
| 742 | + array('fc_key' => $cachekey, 'fc_cache' => $value, 'fc_date' => $timestamp), |
| 743 | + __METHOD__); |
| 744 | + |
| 745 | + return true; |
| 746 | + } |
| 747 | +} |
| 748 | + |
| 749 | +# Load expert promotion UI |
| 750 | +include_once('SpecialMakevalidate.php'); |
| 751 | + |
| 752 | +if( !function_exists( 'extAddSpecialPage' ) ) { |
| 753 | + require( dirname(__FILE__) . '/../ExtensionFunctions.php' ); |
| 754 | +} |
| 755 | +extAddSpecialPage( dirname(__FILE__) . '/FlaggedRevsPage.body.php', 'Revisionreview', 'Revisionreview' ); |
| 756 | + |
| 757 | +# Load approve/unapprove UI |
| 758 | +$wgHooks['LoadAllMessages'][] = 'efLoadReviewMessages'; |
| 759 | + |
| 760 | +$flaggedrevs = new FlaggedRevs(); |
| 761 | +$wgHooks['BeforePageDisplay'][] = array($flaggedrevs, 'setPageContent'); |
| 762 | +$wgHooks['DiffViewHeader'][] = array($flaggedrevs, 'addToDiff'); |
| 763 | +$wgHooks['EditPage::showEditForm:initial'][] = array($flaggedrevs, 'addToEditView'); |
| 764 | +$wgHooks['SkinTemplateTabs'][] = array($flaggedrevs, 'setCurrentTab'); |
| 765 | +$wgHooks['PageHistoryBeforeList'][] = array($flaggedrevs, 'addToPageHist'); |
| 766 | +$wgHooks['PageHistoryLineEnding'][] = array($flaggedrevs, 'addToHistLine'); |
| 767 | +?> |
Property changes on: branches/jhb/phase3/extensions/FlaggedRevs/FlaggedRevs.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 768 | + native |
Index: branches/jhb/phase3/extensions/FlaggedRevs/Makevalidate.class.php |
— | — | @@ -0,0 +1,172 @@ |
| 2 | +<?php
|
| 3 | +
|
| 4 | +global $IP;
|
| 5 | +require_once( "$IP/includes/LogPage.php" );
|
| 6 | +require_once( "$IP/includes/SpecialLog.php" );
|
| 7 | +
|
| 8 | +class MakeValidate extends SpecialPage {
|
| 9 | +
|
| 10 | + var $target = '';
|
| 11 | +
|
| 12 | + /**
|
| 13 | + * Constructor
|
| 14 | + */
|
| 15 | + function MakeValidate() {
|
| 16 | + SpecialPage::SpecialPage( 'Makevalidate', 'makevalidate' );
|
| 17 | + }
|
| 18 | +
|
| 19 | + /**
|
| 20 | + * Main execution function
|
| 21 | + * @param $par Parameters passed to the page
|
| 22 | + */
|
| 23 | + function execute( $par ) {
|
| 24 | + global $wgRequest, $wgOut, $wgmakevalidatePrivileged, $wgUser;
|
| 25 | +
|
| 26 | + if( !$wgUser->isAllowed( 'makevalidate' ) ) {
|
| 27 | + $wgOut->permissionRequired( 'makevalidate' );
|
| 28 | + return;
|
| 29 | + }
|
| 30 | +
|
| 31 | + $this->setHeaders();
|
| 32 | +
|
| 33 | + $this->target = $par
|
| 34 | + ? $par
|
| 35 | + : $wgRequest->getText( 'username', '' );
|
| 36 | +
|
| 37 | + $wgOut->addWikiText( wfMsgNoTrans( 'makevalidate-header' ) );
|
| 38 | + $wgOut->addHtml( $this->makeSearchForm() );
|
| 39 | +
|
| 40 | + if( $this->target != '' ) {
|
| 41 | + $wgOut->addHtml( wfElement( 'p', NULL, NULL ) );
|
| 42 | + $user = User::newFromName( $this->target );
|
| 43 | + if( is_object( $user ) && !is_null( $user ) ) {
|
| 44 | + global $wgVersion;
|
| 45 | + if( version_compare( $wgVersion, '1.9alpha' ) < 0 ) {
|
| 46 | + $user->loadFromDatabase();
|
| 47 | + } else {
|
| 48 | + $user->load();
|
| 49 | + }
|
| 50 | + # Valid username, check existence
|
| 51 | + if( $user->getID() ) {
|
| 52 | + if( $wgRequest->getCheck( 'dosearch' ) || !$wgRequest->wasPosted() || !$wgUser->matchEditToken( $wgRequest->getVal( 'token' ), 'makevalidate' ) ) {
|
| 53 | + # Exists, check reviewerness
|
| 54 | + if( in_array( 'reviewer', $user->mGroups ) ) {
|
| 55 | + # Has a reviewer flag
|
| 56 | + $wgOut->addWikiText( wfMsg( 'makevalidate-isvalidator', $user->getName() ) );
|
| 57 | + $wgOut->addHtml( $this->makeGrantForm( MW_MAKEVALIDATE_REVOKE ) );
|
| 58 | + } else {
|
| 59 | + # Not a reviewer; show the grant form
|
| 60 | + $wgOut->addHtml( $this->makeGrantForm( MW_MAKEVALIDATE_GRANT ) );
|
| 61 | + }
|
| 62 | + } elseif( $wgRequest->getCheck( 'grant' ) ) {
|
| 63 | + # Grant the flag
|
| 64 | + $user->addGroup( 'reviewer' );
|
| 65 | + $this->addLogItem( 'grant', $user, trim( $wgRequest->getText( 'comment' ) ) );
|
| 66 | + $wgOut->addWikiText( wfMsg( 'makevalidate-granted', $user->getName() ) );
|
| 67 | + } elseif( $wgRequest->getCheck( 'revoke' ) ) {
|
| 68 | + # Revoke the flag
|
| 69 | + $user->removeGroup( 'reviewer' );
|
| 70 | + $this->addLogItem( 'revoke', $user, trim( $wgRequest->getText( 'comment' ) ) );
|
| 71 | + $wgOut->addWikiText( wfMsg( 'makevalidate-revoked', $user->getName() ) );
|
| 72 | + }
|
| 73 | + # Show log entries
|
| 74 | + $this->showLogEntries( $user );
|
| 75 | + } else {
|
| 76 | + # Doesn't exist
|
| 77 | + $wgOut->addWikiText( wfMsg( 'nosuchusershort', htmlspecialchars( $this->target ) ) );
|
| 78 | + }
|
| 79 | + } else {
|
| 80 | + # Invalid username
|
| 81 | + $wgOut->addWikiText( wfMsg( 'noname' ) );
|
| 82 | + }
|
| 83 | + }
|
| 84 | +
|
| 85 | + }
|
| 86 | +
|
| 87 | + /**
|
| 88 | + * Produce a form to allow for entering a username
|
| 89 | + * @return string
|
| 90 | + */
|
| 91 | + function makeSearchForm() {
|
| 92 | + $thisTitle = Title::makeTitle( NS_SPECIAL, $this->getName() );
|
| 93 | + $form = wfOpenElement( 'form', array( 'method' => 'post', 'action' => $thisTitle->getLocalUrl() ) );
|
| 94 | + $form .= wfElement( 'label', array( 'for' => 'username' ), wfMsg( 'makevalidate-username' ) ) . ' ';
|
| 95 | + $form .= wfElement( 'input', array( 'type' => 'text', 'name' => 'username', 'id' => 'username', 'value' => $this->target ) ) . ' ';
|
| 96 | + $form .= wfElement( 'input', array( 'type' => 'submit', 'name' => 'dosearch', 'value' => wfMsg( 'makevalidate-search' ) ) );
|
| 97 | + $form .= wfCloseElement( 'form' );
|
| 98 | + return $form;
|
| 99 | + }
|
| 100 | +
|
| 101 | + /**
|
| 102 | + * Produce a form to allow granting or revocation of the flag
|
| 103 | + * @param $type Either MW_makevalidate_GRANT or MW_makevalidate_REVOKE
|
| 104 | + * where the trailing name refers to what's enabled
|
| 105 | + * @return string
|
| 106 | + */
|
| 107 | + function makeGrantForm( $type ) {
|
| 108 | + global $wgUser;
|
| 109 | + $thisTitle = Title::makeTitle( NS_SPECIAL, $this->getName() );
|
| 110 | + if( $type == MW_MAKEVALIDATE_GRANT ) {
|
| 111 | + $grant = true;
|
| 112 | + $revoke = false;
|
| 113 | + } else {
|
| 114 | + $grant = false;
|
| 115 | + $revoke = true;
|
| 116 | + }
|
| 117 | +
|
| 118 | + # Start the table
|
| 119 | + $form = wfOpenElement( 'form', array( 'method' => 'post', 'action' => $thisTitle->getLocalUrl() ) );
|
| 120 | + $form .= wfOpenElement( 'table' ) . wfOpenElement( 'tr' );
|
| 121 | + # Grant/revoke buttons
|
| 122 | + $form .= wfElement( 'td', array( 'align' => 'right' ), wfMsg( 'makevalidate-change' ) );
|
| 123 | + $form .= wfOpenElement( 'td' );
|
| 124 | + foreach( array( 'grant', 'revoke' ) as $button ) {
|
| 125 | + $attribs = array( 'type' => 'submit', 'name' => $button, 'value' => wfMsg( 'makevalidate-' . $button ) );
|
| 126 | + if( !$$button )
|
| 127 | + $attribs['disabled'] = 'disabled';
|
| 128 | + $form .= wfElement( 'input', $attribs );
|
| 129 | + }
|
| 130 | + $form .= wfCloseElement( 'td' ) . wfCloseElement( 'tr' );
|
| 131 | + # Comment field
|
| 132 | + $form .= wfOpenElement( 'td', array( 'align' => 'right' ) );
|
| 133 | + $form .= wfElement( 'label', array( 'for' => 'comment' ), wfMsg( 'makevalidate-comment' ) );
|
| 134 | + $form .= wfOpenElement( 'td' );
|
| 135 | + $form .= wfElement( 'input', array( 'type' => 'text', 'name' => 'comment', 'id' => 'comment', 'size' => 45 ) );
|
| 136 | + $form .= wfCloseElement( 'td' ) . wfCloseElement( 'tr' );
|
| 137 | + # End table
|
| 138 | + $form .= wfCloseElement( 'table' );
|
| 139 | + # Username
|
| 140 | + $form .= wfElement( 'input', array( 'type' => 'hidden', 'name' => 'username', 'value' => $this->target ) );
|
| 141 | + # Edit token
|
| 142 | + $form .= wfElement( 'input', array( 'type' => 'hidden', 'name' => 'token', 'value' => $wgUser->editToken( 'makevalidate' ) ) );
|
| 143 | + $form .= wfCloseElement( 'form' );
|
| 144 | + return $form;
|
| 145 | + }
|
| 146 | +
|
| 147 | + /**
|
| 148 | + * Add logging entries for the specified action
|
| 149 | + * @param $type Either grant or revoke
|
| 150 | + * @param $target User receiving the action
|
| 151 | + * @param $comment Comment for the log item
|
| 152 | + */
|
| 153 | + function addLogItem( $type, &$target, $comment = '' ) {
|
| 154 | + $log = new LogPage( 'validate' );
|
| 155 | + $targetPage = $target->getUserPage();
|
| 156 | + $log->addEntry( $type, $targetPage, $comment );
|
| 157 | + }
|
| 158 | +
|
| 159 | + /**
|
| 160 | + * Show the bot status log entries for the specified user
|
| 161 | + * @param $user User to show the log for
|
| 162 | + */
|
| 163 | + function showLogEntries( &$user ) {
|
| 164 | + global $wgOut;
|
| 165 | + $title = $user->getUserPage();
|
| 166 | + $wgOut->addHtml( wfElement( 'h2', NULL, htmlspecialchars( LogPage::logName( 'validate' ) ) ) );
|
| 167 | + $logViewer = new LogViewer( new LogReader( new FauxRequest( array( 'page' => $title->getPrefixedText(), 'type' => 'validate' ) ) ) );
|
| 168 | + $logViewer->showList( $wgOut );
|
| 169 | + }
|
| 170 | +
|
| 171 | +}
|
| 172 | +
|
| 173 | +?>
|
Index: branches/jhb/phase3/extensions/FlaggedRevs/FlaggedRevs.sql |
— | — | @@ -0,0 +1,54 @@ |
| 2 | +-- (c) Joerg Baach, Aaron Schulz, 2007
|
| 3 | +
|
| 4 | +-- Table structure for table `revisiontags`
|
| 5 | +-- Replace /*$wgDBprefix*/ with the proper prefix
|
| 6 | +
|
| 7 | +-- This stores expanded revision wikitext caches
|
| 8 | +-- along with rating/user/notes data
|
| 9 | +CREATE TABLE /*$wgDBprefix*/flaggedrevs (
|
| 10 | + fr_id int(10) NOT NULL auto_increment,
|
| 11 | + fr_page_id int(10) NOT NULL,
|
| 12 | + fr_rev_id int(10) NOT NULL,
|
| 13 | + fr_acc int(2) NOT NULL,
|
| 14 | + fr_dep int(2) NOT NULL,
|
| 15 | + fr_sty int(2) NOT NULL,
|
| 16 | + fr_user int(5) NOT NULL,
|
| 17 | + fr_timestamp char(14) NOT NULL,
|
| 18 | + fr_comment mediumblob default NULL,
|
| 19 | +
|
| 20 | + PRIMARY KEY fr_rev_id (fr_rev_id),
|
| 21 | + UNIQUE KEY (fr_id),
|
| 22 | + INDEX fr_page_rev (fr_page_id,fr_rev_id),
|
| 23 | + INDEX fr_acc_dep_sty (fr_acc,fr_dep,fr_sty)
|
| 24 | +) TYPE=InnoDB;
|
| 25 | +
|
| 26 | +-- This stores expanded (transclusions resolved) revision text
|
| 27 | +CREATE TABLE /*$wgDBprefix*/flaggedtext (
|
| 28 | + ft_id int(10) NOT NULL auto_increment,
|
| 29 | + ft_rev_id int(10) NOT NULL,
|
| 30 | + ft_text mediumblob NOT NULL default '',
|
| 31 | +
|
| 32 | + PRIMARY KEY ft_id (ft_id),
|
| 33 | + UNIQUE KEY ft_rev_id (ft_rev_id)
|
| 34 | +) TYPE=InnoDB;
|
| 35 | +
|
| 36 | +-- This stores image usage for the stable image directory
|
| 37 | +-- Used for scripts that clear out unused images
|
| 38 | +CREATE TABLE /*$wgDBprefix*/flaggedimages (
|
| 39 | + fi_id int(10) NOT NULL auto_increment,
|
| 40 | + fi_name varchar(255) NOT NULL,
|
| 41 | + fi_rev_id int(10) NOT NULL,
|
| 42 | +
|
| 43 | + PRIMARY KEY (fi_name,fi_rev_id),
|
| 44 | + UNIQUE KEY (fi_id),
|
| 45 | + INDEX fi_name (fi_name)
|
| 46 | +) TYPE=InnoDB;
|
| 47 | +
|
| 48 | +-- This stores cached text for page view
|
| 49 | +CREATE TABLE /*$wgDBprefix*/flaggedcache (
|
| 50 | + fc_key char(255) binary NOT NULL default '',
|
| 51 | + fc_cache mediumblob NOT NULL default '',
|
| 52 | + fc_date char(14) NOT NULL,
|
| 53 | +
|
| 54 | + PRIMARY KEY fc_key (fc_key)
|
| 55 | +) TYPE=InnoDB; |
\ No newline at end of file |