r51306 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r51305‎ | r51306 | r51307 >
Date:20:50, 1 June 2009
Author:skizzerz
Status:deferred
Tags:
Comment:
* fix fatal error & other glitches in AuthorProtect extension
Modified paths:
  • /trunk/extensions/AuthorProtect/AuthorProtect.php (modified) (history)

Diff [purge]

Index: trunk/extensions/AuthorProtect/AuthorProtect.php
@@ -1,12 +1,13 @@
22 <?php
3 -/*
4 -* AuthorProtect extension by Ryan Schmidt
5 -* See http://www.mediawiki.org/wiki/Extension:AuthorProtect for more details
6 -*/
73
8 -if(!defined('MEDIAWIKI')) {
9 - echo("This file is an extension to MediaWiki and cannot be run externally");
10 - die(1);
 4+/**
 5+ * AuthorProtect extension by Ryan Schmidt
 6+ * See http://www.mediawiki.org/wiki/Extension:AuthorProtect for more details
 7+ */
 8+
 9+if( !defined( 'MEDIAWIKI' ) ) {
 10+ echo "This file is an extension to MediaWiki and cannot be run externally\n";
 11+ die( 1 );
1112 }
1213
1314 $wgExtensionCredits['other'][] = array(
@@ -15,132 +16,178 @@
1617 'author' => 'Ryan Schmidt',
1718 'url' => 'http://www.mediawiki.org/wiki/Extension:AuthorProtect',
1819 'version' => '1.2',
19 - 'description' => 'Allows the author of a page to protect it from other users',
 20+ 'description' => 'Allows the author of a page to protect it from other users',
2021 'descriptionmsg' => 'authorprotect-desc',
2122 );
2223
23 -$wgAvailableRights[] = 'author'; //dynamically assigned to the author of a page, but can be set w/ GroupPermissions too
 24+$wgAvailableRights[] = 'author'; //dynamically assigned to the author of a page, but can be set w/ wgGroupPermissions too
2425 $wgAvailableRights[] = 'authorprotect'; //users without this right cannot protect pages they author
25 -$wgExtensionMessagesFiles['AuthorProtect'] = dirname(__FILE__) . '/AuthorProtect.i18n.php';
 26+$wgExtensionMessagesFiles['AuthorProtect'] = dirname( __FILE__ ) . '/AuthorProtect.i18n.php';
2627 $wgGroupPermissions['sysop']['author'] = true; //sysops can edit every page despite author protection
2728 $wgGroupPermissions['user']['authorprotect'] = true; //registered users can protect pages they author
2829 $wgHooks['SkinTemplateContentActions'][] = 'efMakeContentAction';
2930 $wgHooks['UnknownAction'][] = 'efAuthorProtectForm';
3031 $wgHooks['userCan'][] = 'efAuthorProtectDelay';
 32+$wgHooks['UserGetRights'][] = 'efAssignAuthor';
3133 $wgRestrictionLevels[] = 'author'; //so sysops, etc. using the normal protection interface can protect and unprotect it at the author level
3234
33 -// FIXME: split off into one or more class files to reduce initial loading time
34 -function efAuthorProtectDelay($title, &$user, $action, $result) {
35 - $user->mRights = null;
36 - $wgHooks['UserGetRights'][] = 'efAssignAuthor';
37 - $user->getRights(); //delay hook execution for compatibility w/ ConfirmAccount
38 - $act = ( $action == '' || $action == 'view' ) ? 'edit' : $action;
39 - if( userIsAuthor() && isAuthorProtected($title, $act) ) {
40 - $result = true;
41 - return false;
 35+//internal variables, do not modify
 36+$wgAuthorProtectDoProtect = false;
 37+$wgAuthorProtectDelayRun = true;
 38+
 39+/**
 40+ * Extensions like ConfirmAccount do some weird stuff to $wgTitle during the UserGetRights hook
 41+ * So this delays the hook's execution to a point where $wgTitle is set
 42+ */
 43+function efAuthorProtectDelay( $title, &$user, $action, $result ) {
 44+ global $wgAuthorProtectDelayRun;
 45+ if( $wgAuthorProtectDelayRun ) {
 46+ $user->mRights = null;
 47+ $user->getRights(); //delay hook execution for compatibility w/ ConfirmAccount
 48+ $act = ( $action == '' || $action == 'view' ) ? 'edit' : $action;
 49+ $wgAuthorProtectDelayRun = false;
 50+ if( userIsAuthor() && isAuthorProtected( $title, $act ) ) {
 51+ $result = true;
 52+ return false;
 53+ }
4254 }
4355 $result = null;
4456 return true;
4557 }
4658
47 -function efAssignAuthor(&$user, &$aRights) {
 59+function efAssignAuthor( &$user, &$aRights ) {
4860 //don't assign author to anons... messes up logging stuff.
4961 //plus it's all user_id based so it is impossible to differentiate one anon from another
5062 if( userIsAuthor() && $user->isLoggedIn() ) {
5163 $aRights[] = 'author';
52 - $aRights = array_unique($aRights);
 64+ $aRights = array_unique( $aRights );
5365 }
 66+ //assign protect too if we need to
 67+ global $wgAuthorProtectDoProtect;
 68+ if( $wgAuthorProtectDoProtect ) {
 69+ $aRights[] = 'protect';
 70+ $aRights = array_unique( $aRights );
 71+ }
5472 return true;
5573 }
5674
57 -function efMakeContentAction(&$cactions) {
 75+function efAuthorProtectAssignProtect() {
 76+ global $wgAuthorProtectDoProtect, $wgUser;
 77+ $wgAuthorProtectDoProtect = true;
 78+ $wgUser->mRights = null;
 79+ $wgUser->getRights(); //re-trigger the above function to assign the protect right
 80+ $wgAuthorProtectDoProtect = false;
 81+}
 82+
 83+function efAuthorProtectUnassignProtect() {
 84+ global $wgUser;
 85+ $wgUser->mRights = null;
 86+ $wgUser->getRights();
 87+}
 88+
 89+function efMakeContentAction( &$cactions ) {
5890 global $wgUser, $wgRequest, $wgTitle;
59 - if( userIsAuthor() && $wgUser->isAllowed('authorprotect') && !$wgUser->isAllowed('protect') ) {
60 - $action = $wgRequest->getText('action');
 91+ if( userIsAuthor() && $wgUser->isAllowed( 'authorprotect' ) && !$wgUser->isAllowed( 'protect' ) ) {
 92+ $action = $wgRequest->getText( 'action' );
6193 $cactions['authorprotect'] = array(
6294 'class' => $action == 'authorprotect' ? 'selected' : false,
63 - 'text' => wfMsg(protectMessage($wgTitle)),
64 - 'href' => $wgTitle->getLocalUrl('action=authorprotect'),
 95+ 'text' => wfMsg( efAuthorProtectMessage( $wgTitle ) ),
 96+ 'href' => $wgTitle->getLocalUrl( 'action=authorprotect' ),
6597 );
6698 }
6799 return true;
68100 }
69101
70 -function efAuthorProtectForm($action, &$article) {
71 - global $wgTitle;
72 - if($action == 'authorprotect') {
73 - wfLoadExtensionMessages('AuthorProtect');
 102+function efAuthorProtectForm( $action, &$article ) {
 103+ global $wgTitle, $wgAuthorProtectDoProtect;
 104+ if( $action == 'authorprotect' ) {
 105+ wfLoadExtensionMessages( 'AuthorProtect' );
74106 global $wgOut, $wgUser, $wgRequest, $wgRestrictionTypes;
75 - if($wgUser->isAllowed('authorprotect')) {
76 - if(userIsAuthor()) {
77 - $wgOut->setPageTitle(wfMsg('authorprotect'));
 107+ if( $wgUser->isAllowed( 'authorprotect' ) ) {
 108+ if( userIsAuthor() ) {
 109+ $wgOut->setPageTitle( wfMsg( 'authorprotect' ) );
78110 if( !$wgRequest->wasPosted() ) {
79 - $wgOut->addHTML(makeProtectForm());
 111+ $wgOut->addHTML( efAuthorProtectMakeProtectForm() );
80112 } else {
81113 if( !$wgUser->matchEditToken( $wgRequest->getText('wpToken') ) ) {
82 - $wgOut->setPageTitle(wfMsg('errorpagetitle'));
83 - $wgOut->addWikiText(wfMsg('sessionfailure'));
 114+ $wgOut->setPageTitle( wfMsg( 'errorpagetitle' ) );
 115+ $wgOut->addWikiText( wfMsg( 'sessionfailure' ) );
84116 return;
85117 }
86118 $restrictions = array();
 119+ $expiration = array();
 120+ $expiry = efAuthorProtectExpiry( $wgRequest->getText( 'wpExpiryTime' ) );
87121 foreach( $wgRestrictionTypes as $type ) {
88 - $rest = $wgTitle->getRestrictions($type);
 122+ $rest = $wgTitle->getRestrictions( $type );
89123 if( $rest !== array() ) {
90 - if(!$wgUser->isAllowed($rest[0]) && !in_array('author', $rest) ) {
 124+ if( !$wgUser->isAllowed( $rest[0] ) && !in_array( 'author', $rest ) ) {
91125 $restrictions[$type] = $rest[0]; //don't let them lower the protection level
92126 continue;
93127 }
94128 }
95 - if( $wgRequest->getCheck("check-{$type}") ) {
 129+ if( $wgRequest->getCheck( "check-{$type}" ) ) {
96130 $restrictions[$type] = 'author';
 131+ $expiration[$type] = $expiry;
97132 } else {
98133 if( in_array( 'author', $rest ) ) {
99134 $restrictions[$type] = '';
 135+ $expiration[$type] = $expiry;
100136 } else {
101 - $restrictions[$type] = $rest !== array() ? $rest[0] : ''; //we're not setting it
 137+ $restrictions[$type] = ( $rest !== array() ) ? $rest[0] : ''; //we're not setting it
 138+ $expiration[$type] = $expiry;
102139 }
103140 }
104141 }
105 - $success = doProtect( $restrictions, $wgRequest->getText('wpReason'), $wgRequest->getText('wpExpiryTime') );
106 - if($success) {
107 - $wgOut->addWikiText(wfMsg('authorprotect-success'));
 142+ $cascade = false;
 143+ efAuthorProtectAssignProtect();
 144+ $str = var_export(array('restrictions' => $restrictions, 'reason' => $wgRequest->getText('wpReason'), 'cascade' => $cascade, 'expiry' => $expiration), true);
 145+ wfDebugLog('authorprotect', $str);
 146+ $success = $article->updateRestrictions(
 147+ $restrictions, //array of restrictions
 148+ $wgRequest->getText( 'wpReason' ), //reason
 149+ $cascade, //cascading protection disabled, need to pass by reference
 150+ $expiration //expiration
 151+ );
 152+ efAuthorProtectUnassignProtect();
 153+ if( $success ) {
 154+ $wgOut->addWikiText( wfMsg( 'authorprotect-success' ) );
108155 } else {
109 - $wgOut->addWikiText(wfMsg('authorprotect-failure'));
 156+ $wgOut->addWikiText( wfMsg( 'authorprotect-failure' ) );
110157 }
111158 }
112159 } else {
113 - $wgOut->setPageTitle(wfMsg('errorpagetitle'));
114 - $wgOut->addWikiText(wfMsg('authorprotect-notauthor'));
 160+ $wgOut->setPageTitle( wfMsg('errorpagetitle') );
 161+ $wgOut->addWikiText( wfMsg('authorprotect-notauthor') );
115162 }
116163 } else {
117 - $wgOut->permissionRequired('authorprotect');
 164+ $wgOut->permissionRequired( 'authorprotect' );
118165 }
119166 return false; //still continues hook processing, but doesn't throw an error message
120167 }
121168 return true; //unknown action, so state that the action doesn't exist
122169 }
123170
124 -function makeProtectForm() {
 171+function efAuthorProtectMakeProtectForm() {
125172 global $wgRestrictionTypes, $wgTitle, $wgUser;
126173 $token = $wgUser->editToken();
127 - $form = '<p>' . wfMsg('authorprotect-intro') . '</p>';
128 - $form .= Xml::openElement( 'form', array( 'method' => 'post', 'action' => $wgTitle->getLocalUrl('action=authorprotect') ) );
 174+ $form = Xml::openElement( 'p' ) . wfMsg( 'authorprotect-intro' ) . Xml::closeElement( 'p' );
 175+ $form .= Xml::openElement( 'form', array( 'method' => 'post', 'action' => $wgTitle->getLocalUrl( 'action=authorprotect' ) ) );
129176 foreach( $wgRestrictionTypes as $type ) {
130 - $rest = $wgTitle->getRestrictions($type);
 177+ $rest = $wgTitle->getRestrictions( $type );
131178 if( $rest !== array() ) {
132 - if(!$wgUser->isAllowed($rest[0]) && !in_array('author', $rest) )
 179+ if( !$wgUser->isAllowed( $rest[0] ) && !in_array( 'author', $rest ) )
133180 continue; //it's protected at a level higher than them, so don't let them change it so they can now mess with stuff
134181 }
135182 $checked = in_array( 'author', $rest );
136183 $array = array( 'type' => 'checkbox', 'name' => 'check-' . $type, 'value' => $type );
137 - if($checked)
 184+ if( $checked )
138185 $array = array_merge( $array, array( 'checked' => 'checked' ) );
139186 $form .= Xml::element( 'input', $array );
140 - $form .= ' ' . wfMsg('authorprotect-' . $type) . Xml::element( 'br' );
 187+ $form .= ' ' . wfMsg( 'authorprotect-' . $type ) . Xml::element( 'br' );
141188 }
142 - $form .= Xml::element( 'br' ) . Xml::element( 'label', array( 'for' => 'wpExpiryTime' ), wfMsg('authorprotect-expiry') ) . ' ';
 189+ $form .= Xml::element( 'br' ) . Xml::element( 'label', array( 'for' => 'wpExpiryTime' ), wfMsg( 'authorprotect-expiry' ) ) . ' ';
143190 $form .= Xml::element( 'input', array( 'type' => 'text', 'name' => 'wpExpiryTime' ) ) . Xml::element( 'br' );
144 - $form .= Xml::element( 'br' ) . Xml::element( 'label', array( 'for' => 'wpReason' ), wfMsg('authorprotect-reason') ) . ' ';
 191+ $form .= Xml::element( 'br' ) . Xml::element( 'label', array( 'for' => 'wpReason' ), wfMsg( 'authorprotect-reason' ) ) . ' ';
145192 $form .= Xml::element( 'input', array( 'type' => 'text', 'name' => 'wpReason' ) );
146193 $form .= Xml::element( 'br' ) . Xml::element( 'input', array( 'type' => 'hidden', 'name' => 'wpToken', 'value' => $token ) );
147194 $form .= Xml::element( 'br' ) . Xml::element( 'input', array( 'type' => 'submit', 'name' => 'wpConfirm', 'value' => wfMsg( 'authorprotect-confirm' ) ) );
@@ -149,17 +196,16 @@
150197 }
151198
152199 function userIsAuthor() {
153 - global $wgTitle, $wgUser, $wgDBprefix;
154 - if(!$wgTitle instanceOf Title)
 200+ global $wgTitle, $wgUser;
 201+ if( !$wgTitle instanceOf Title )
155202 return false; //quick hack to prevent the API from messing up.
156203 $id = $wgTitle->getArticleId();
157 - $dbr = wfGetDB(DB_SLAVE); //grab the slave for reading
158 - $res = $dbr->query( "SELECT `rev_user` FROM `{$wgDBprefix}revision` WHERE rev_page={$id} LIMIT 1", __METHOD__ );
159 - $row = $dbr->fetchRow($res);
160 - return $wgUser->getID() == $row['rev_user'];
 204+ $dbr = wfGetDB( DB_SLAVE ); //grab the slave for reading
 205+ $aid = $dbr->selectField( 'revision', 'rev_user', array( 'rev_page' => $id ), __METHOD__ );
 206+ return $wgUser->getID() == $aid;
161207 }
162208
163 -function protectMessage($title) {
 209+function efAuthorProtectMessage( $title ) {
164210 global $wgRestrictionTypes;
165211 foreach( $wgRestrictionTypes as $type ) {
166212 if( in_array( 'author', $title->getRestrictions( $type ) ) )
@@ -168,126 +214,25 @@
169215 return 'protect';
170216 }
171217
172 -// do the protection, copied from Article.php's updateRestrictions then modified
173 -// so that it isn't so picky about having the 'protect' right.
174 -function doProtect( $limit = array(), $reason = '', &$expiry = '' ) {
175 - global $wgUser, $wgRestrictionTypes, $wgContLang, $wgTitle;
 218+function isAuthorProtected($title, $action) {
 219+ $rest = $title->getRestrictions($action);
 220+ return in_array('author', $rest);
 221+}
176222
177 - $id = $wgTitle->getArticleID();
178 - if( wfReadOnly() || $id == 0 ) {
179 - return false;
180 - }
181 -
182 - if ( strlen( $expiry ) == 0 ) {
183 - $expiry = 'infinite';
184 - }
185 -
186 - if ( $expiry == 'infinite' || $expiry == 'indefinite' ) {
187 - $expiry = Block::infinity();
 223+//forked from ProtectionForm::getExpiry and modified to rewrite '' to infinity
 224+function efAuthorProtectExpiry( $value ) {
 225+ if ( $value == 'infinite' || $value == 'indefinite' || $value == 'infinity' || $value == '' ) {
 226+ $time = Block::infinity();
188227 } else {
189 - # Convert GNU-style date, on error returns -1 for PHP <5.1 and false for PHP >=5.1
190 - $expiry = strtotime( $expiry );
 228+ $unix = strtotime( $value );
191229
192 - if ( $expiry < 0 || $expiry === false ) {
193 - //invalid expiry, rewrite to infinity
194 - $expiry = Block::infinity();
195 - } else {
196 - // Fixme: non-qualified absolute times are not in users specified timezone
197 - // and there isn't notice about it in the ui
198 - $expiry = wfTimestamp( TS_MW, $expiry );
 230+ if ( !$unix || $unix === -1 ) {
 231+ return false;
199232 }
200 - }
201 -
202 - // Take this opportunity to purge out expired restrictions
203 - Title::purgeExpiredRestrictions();
204233
205 - # FIXME: Same limitations as described in ProtectionForm.php (line 37);
206 - # we expect a single selection, but the schema allows otherwise.
207 - $current = array();
208 - foreach( $wgRestrictionTypes as $action )
209 - $current[$action] = implode( '', $wgTitle->getRestrictions( $action ) );
210 -
211 - $current = Article::flattenRestrictions( $current );
212 - $updated = Article::flattenRestrictions( $limit );
213 - $changed = ( $current != $updated );
214 - $changed = $changed || ($wgTitle->mRestrictionsExpiry != $expiry);
215 - $protect = ( $updated != '' );
216 -
217 - # If nothing's changed, do nothing
218 - if( $changed ) {
219 - global $wgGroupPermissions;
220 -
221 - $dbw = wfGetDB( DB_MASTER );
222 -
223 - $encodedExpiry = Block::encodeExpiry($expiry, $dbw );
224 -
225 - $expiry_description = '';
226 - if ( $encodedExpiry != 'infinity' ) {
227 - $expiry_description = ' (' . wfMsgForContent(
228 - 'protect-expiring',
229 - $wgContLang->timeanddate( $expiry, false, false ),
230 - $wgContLang->date( $expiry, false, false ),
231 - $wgContLang->time( $expiry, false, false )
232 - ) . ')';
233 - }
234 -
235 - # Prepare a null revision to be added to the history
236 - $modified = $current != '' && $protect;
237 - if ( $protect ) {
238 - $comment_type = $modified ? 'modifiedarticleprotection' : 'protectedarticle';
239 - } else {
240 - $comment_type = 'unprotectedarticle';
241 - }
242 - $comment = $wgContLang->ucfirst( wfMsgForContent( $comment_type, $wgTitle->getPrefixedText() ) );
243 -
244 - if( $reason )
245 - $comment .= ": $reason";
246 - if( $protect )
247 - $comment .= " [$updated]";
248 - if ( $expiry_description && $protect )
249 - $comment .= "$expiry_description";
250 -
251 - # Update restrictions table
252 - foreach( $limit as $action => $restrictions ) {
253 - if ($restrictions != '' ) {
254 - $dbw->replace( 'page_restrictions', array(array('pr_page', 'pr_type')),
255 - array( 'pr_page' => $id, 'pr_type' => $action
256 - , 'pr_level' => $restrictions, 'pr_cascade' => 0
257 - , 'pr_expiry' => $encodedExpiry ), __METHOD__ );
258 - } else {
259 - $dbw->delete( 'page_restrictions', array( 'pr_page' => $id,
260 - 'pr_type' => $action ), __METHOD__ );
261 - }
262 - }
263 -
264 - # Insert a null revision
265 - $nullRevision = Revision::newNullRevision( $dbw, $id, $comment, true );
266 - $nullRevId = $nullRevision->insertOn( $dbw );
267 -
268 - # Update page record
269 - $dbw->update( 'page',
270 - array( /* SET */
271 - 'page_touched' => $dbw->timestamp(),
272 - 'page_restrictions' => '',
273 - 'page_latest' => $nullRevId
274 - ), array( /* WHERE */
275 - 'page_id' => $id
276 - ), 'Article::protect'
277 - );
278 - # Update the protection log
279 - $log = new LogPage( 'protect' );
280 -
281 - if( $protect ) {
282 - $log->addEntry( $modified ? 'modify' : 'protect', $wgTitle, trim( $reason . " [$updated]$expiry_description" ) );
283 - } else {
284 - $log->addEntry( 'unprotect', $wgTitle, $reason );
285 - }
286 - } # End "changed" check
287 -
288 - return true;
 234+ // Fixme: non-qualified absolute times are not in users specified timezone
 235+ // and there isn't notice about it in the ui
 236+ $time = wfTimestamp( TS_MW, $unix );
 237+ }
 238+ return $time;
289239 }
290 -
291 -function isAuthorProtected($title, $action) {
292 - $rest = $title->getRestrictions($action);
293 - return in_array('author', $rest);
294 -}

Status & tagging log