Index: trunk/extensions/MultilingualLiquidThreads/LiquidThreads/classes/Thread.php |
— | — | @@ -113,7 +113,7 @@ |
114 | 114 | NewMessages::writeMessageStateForUpdatedThread( $thread, $change_type, $wgUser ); |
115 | 115 | |
116 | 116 | if ( $wgUser->getOption( 'lqt-watch-threads', false ) ) { |
117 | | - $thread->topmostThread()->root()->doWatch(); |
| 117 | + Action::factory( 'watch', $thread->topmostThread()->root() )->execute(); |
118 | 118 | } |
119 | 119 | |
120 | 120 | return $thread; |
Index: trunk/extensions/LiquidThreads/classes/Thread.php |
— | — | @@ -112,7 +112,7 @@ |
113 | 113 | NewMessages::writeMessageStateForUpdatedThread( $thread, $change_type, $wgUser ); |
114 | 114 | |
115 | 115 | if ( $wgUser->getOption( 'lqt-watch-threads', false ) ) { |
116 | | - $thread->topmostThread()->root()->doWatch(); |
| 116 | + Action::factory( 'watch', $thread->topmostThread()->root() )->execute(); |
117 | 117 | } |
118 | 118 | |
119 | 119 | return $thread; |
Index: trunk/extensions/MetavidWiki/includes/articlepages/MV_DataPage.php |
— | — | @@ -63,9 +63,9 @@ |
64 | 64 | if ( $confirm ) { |
65 | 65 | $this->doDelete( $reason ); |
66 | 66 | if ( $wgRequest->getCheck( 'wpWatch' ) ) { |
67 | | - $this->doWatch(); |
| 67 | + Action::factory( 'watch', $this )->execute(); |
68 | 68 | } elseif ( $this->mTitle->userIsWatching() ) { |
69 | | - $this->doUnwatch(); |
| 69 | + Action::factory( 'watch', $this )->execute(); |
70 | 70 | } |
71 | 71 | return; |
72 | 72 | } |
Index: trunk/extensions/ReplaceText/ReplaceTextJob.php |
— | — | @@ -42,8 +42,7 @@ |
43 | 43 | $create_redirect = $this->params['create_redirect']; |
44 | 44 | $this->title->moveTo( $new_title, true, $reason, $create_redirect ); |
45 | 45 | if ( $this->params['watch_page'] ) { |
46 | | - $article = new Article( $new_title ); |
47 | | - $article->doWatch(); |
| 46 | + Action::factory( 'watch', new Article( $new_title ) )->execute(); |
48 | 47 | } |
49 | 48 | $wgUser = $actual_user; |
50 | 49 | } else { |
Index: trunk/phase3/includes/Credits.php |
— | — | @@ -1,238 +0,0 @@ |
2 | | -<?php |
3 | | -/** |
4 | | - * Formats credits for articles |
5 | | - * |
6 | | - * Copyright 2004, Evan Prodromou <evan@wikitravel.org>. |
7 | | - * |
8 | | - * This program is free software; you can redistribute it and/or modify |
9 | | - * it under the terms of the GNU General Public License as published by |
10 | | - * the Free Software Foundation; either version 2 of the License, or |
11 | | - * (at your option) any later version. |
12 | | - * |
13 | | - * This program is distributed in the hope that it will be useful, |
14 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | - * GNU General Public License for more details. |
17 | | - * |
18 | | - * You should have received a copy of the GNU General Public License |
19 | | - * along with this program; if not, write to the Free Software |
20 | | - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
21 | | - * |
22 | | - * @file |
23 | | - * @author <evan@wikitravel.org> |
24 | | - */ |
25 | | - |
26 | | -class Credits { |
27 | | - /** |
28 | | - * This is largely cadged from PageHistory::history |
29 | | - * @param $article Article object |
30 | | - */ |
31 | | - public static function showPage( Article $article ) { |
32 | | - global $wgOut; |
33 | | - |
34 | | - wfProfileIn( __METHOD__ ); |
35 | | - |
36 | | - $wgOut->setPageTitle( $article->mTitle->getPrefixedText() ); |
37 | | - $wgOut->setSubtitle( wfMsg( 'creditspage' ) ); |
38 | | - $wgOut->setArticleFlag( false ); |
39 | | - $wgOut->setArticleRelated( true ); |
40 | | - $wgOut->setRobotPolicy( 'noindex,nofollow' ); |
41 | | - |
42 | | - if ( $article->mTitle->getArticleID() == 0 ) { |
43 | | - $s = wfMsg( 'nocredits' ); |
44 | | - } else { |
45 | | - $s = self::getCredits( $article, -1 ); |
46 | | - } |
47 | | - |
48 | | - $wgOut->addHTML( $s ); |
49 | | - |
50 | | - wfProfileOut( __METHOD__ ); |
51 | | - } |
52 | | - |
53 | | - /** |
54 | | - * Get a list of contributors of $article |
55 | | - * @param $article Article object |
56 | | - * @param $cnt Int: maximum list of contributors to show |
57 | | - * @param $showIfMax Bool: whether to contributors if there more than $cnt |
58 | | - * @return String: html |
59 | | - */ |
60 | | - public static function getCredits( Article $article, $cnt, $showIfMax = true ) { |
61 | | - wfProfileIn( __METHOD__ ); |
62 | | - $s = ''; |
63 | | - |
64 | | - if ( isset( $cnt ) && $cnt != 0 ) { |
65 | | - $s = self::getAuthor( $article ); |
66 | | - if ( $cnt > 1 || $cnt < 0 ) { |
67 | | - $s .= ' ' . self::getContributors( $article, $cnt - 1, $showIfMax ); |
68 | | - } |
69 | | - } |
70 | | - |
71 | | - wfProfileOut( __METHOD__ ); |
72 | | - return $s; |
73 | | - } |
74 | | - |
75 | | - /** |
76 | | - * Get the last author with the last modification time |
77 | | - * @param $article Article object |
78 | | - */ |
79 | | - protected static function getAuthor( Article $article ) { |
80 | | - global $wgLang; |
81 | | - |
82 | | - $user = User::newFromId( $article->getUser() ); |
83 | | - |
84 | | - $timestamp = $article->getTimestamp(); |
85 | | - if ( $timestamp ) { |
86 | | - $d = $wgLang->date( $article->getTimestamp(), true ); |
87 | | - $t = $wgLang->time( $article->getTimestamp(), true ); |
88 | | - } else { |
89 | | - $d = ''; |
90 | | - $t = ''; |
91 | | - } |
92 | | - return wfMsgExt( 'lastmodifiedatby', 'parsemag', $d, $t, self::userLink( $user ), $user->getName() ); |
93 | | - } |
94 | | - |
95 | | - /** |
96 | | - * Get a list of contributors of $article |
97 | | - * @param $article Article object |
98 | | - * @param $cnt Int: maximum list of contributors to show |
99 | | - * @param $showIfMax Bool: whether to contributors if there more than $cnt |
100 | | - * @return String: html |
101 | | - */ |
102 | | - protected static function getContributors( Article $article, $cnt, $showIfMax ) { |
103 | | - global $wgLang, $wgHiddenPrefs; |
104 | | - |
105 | | - $contributors = $article->getContributors(); |
106 | | - |
107 | | - $others_link = false; |
108 | | - |
109 | | - # Hmm... too many to fit! |
110 | | - if ( $cnt > 0 && $contributors->count() > $cnt ) { |
111 | | - $others_link = self::othersLink( $article ); |
112 | | - if ( !$showIfMax ) |
113 | | - return wfMsgExt( 'othercontribs', 'parsemag', $others_link, $contributors->count() ); |
114 | | - } |
115 | | - |
116 | | - $real_names = array(); |
117 | | - $user_names = array(); |
118 | | - $anon_ips = array(); |
119 | | - |
120 | | - # Sift for real versus user names |
121 | | - foreach ( $contributors as $user ) { |
122 | | - $cnt--; |
123 | | - if ( $user->isLoggedIn() ) { |
124 | | - $link = self::link( $user ); |
125 | | - if ( !in_array( 'realname', $wgHiddenPrefs ) && $user->getRealName() ) { |
126 | | - $real_names[] = $link; |
127 | | - } else { |
128 | | - $user_names[] = $link; |
129 | | - } |
130 | | - } else { |
131 | | - $anon_ips[] = self::link( $user ); |
132 | | - } |
133 | | - |
134 | | - if ( $cnt == 0 ) { |
135 | | - break; |
136 | | - } |
137 | | - } |
138 | | - |
139 | | - if ( count( $real_names ) ) { |
140 | | - $real = $wgLang->listToText( $real_names ); |
141 | | - } else { |
142 | | - $real = false; |
143 | | - } |
144 | | - |
145 | | - # "ThisSite user(s) A, B and C" |
146 | | - if ( count( $user_names ) ) { |
147 | | - $user = wfMsgExt( |
148 | | - 'siteusers', |
149 | | - 'parsemag', |
150 | | - $wgLang->listToText( $user_names ), count( $user_names ) |
151 | | - ); |
152 | | - } else { |
153 | | - $user = false; |
154 | | - } |
155 | | - |
156 | | - if ( count( $anon_ips ) ) { |
157 | | - $anon = wfMsgExt( |
158 | | - 'anonusers', |
159 | | - 'parsemag', |
160 | | - $wgLang->listToText( $anon_ips ), count( $anon_ips ) |
161 | | - ); |
162 | | - } else { |
163 | | - $anon = false; |
164 | | - } |
165 | | - |
166 | | - # This is the big list, all mooshed together. We sift for blank strings |
167 | | - $fulllist = array(); |
168 | | - foreach ( array( $real, $user, $anon, $others_link ) as $s ) { |
169 | | - if ( $s ) { |
170 | | - array_push( $fulllist, $s ); |
171 | | - } |
172 | | - } |
173 | | - |
174 | | - # Make the list into text... |
175 | | - $creds = $wgLang->listToText( $fulllist ); |
176 | | - |
177 | | - # "Based on work by ..." |
178 | | - return strlen( $creds ) |
179 | | - ? wfMsgExt( 'othercontribs', 'parsemag', $creds, count( $fulllist ) ) |
180 | | - : ''; |
181 | | - } |
182 | | - |
183 | | - /** |
184 | | - * Get a link to $user's user page |
185 | | - * @param $user User object |
186 | | - * @return String: html |
187 | | - */ |
188 | | - protected static function link( User $user ) { |
189 | | - global $wgUser, $wgHiddenPrefs; |
190 | | - if ( !in_array( 'realname', $wgHiddenPrefs ) && !$user->isAnon() ) { |
191 | | - $real = $user->getRealName(); |
192 | | - } else { |
193 | | - $real = false; |
194 | | - } |
195 | | - |
196 | | - $skin = $wgUser->getSkin(); |
197 | | - $page = $user->isAnon() ? |
198 | | - SpecialPage::getTitleFor( 'Contributions', $user->getName() ) : |
199 | | - $user->getUserPage(); |
200 | | - |
201 | | - return $skin->link( $page, htmlspecialchars( $real ? $real : $user->getName() ) ); |
202 | | - } |
203 | | - |
204 | | - /** |
205 | | - * Get a link to $user's user page |
206 | | - * @param $user User object |
207 | | - * @return String: html |
208 | | - */ |
209 | | - protected static function userLink( User $user ) { |
210 | | - $link = self::link( $user ); |
211 | | - if ( $user->isAnon() ) { |
212 | | - return wfMsgExt( 'anonuser', array( 'parseinline', 'replaceafter' ), $link ); |
213 | | - } else { |
214 | | - global $wgHiddenPrefs; |
215 | | - if ( !in_array( 'realname', $wgHiddenPrefs ) && $user->getRealName() ) { |
216 | | - return $link; |
217 | | - } else { |
218 | | - return wfMsgExt( 'siteuser', 'parsemag', $link, $user->getName() ); |
219 | | - } |
220 | | - } |
221 | | - } |
222 | | - |
223 | | - /** |
224 | | - * Get a link to action=credits of $article page |
225 | | - * @param $article Article object |
226 | | - * @return String: html |
227 | | - */ |
228 | | - protected static function othersLink( Article $article ) { |
229 | | - global $wgUser; |
230 | | - $skin = $wgUser->getSkin(); |
231 | | - return $skin->link( |
232 | | - $article->getTitle(), |
233 | | - wfMsgHtml( 'others' ), |
234 | | - array(), |
235 | | - array( 'action' => 'credits' ), |
236 | | - array( 'known' ) |
237 | | - ); |
238 | | - } |
239 | | -} |
Index: trunk/phase3/includes/ProtectionForm.php |
— | — | @@ -317,9 +317,9 @@ |
318 | 318 | } |
319 | 319 | |
320 | 320 | if( $wgRequest->getCheck( 'mwProtectWatch' ) && $wgUser->isLoggedIn() ) { |
321 | | - $this->mArticle->doWatch(); |
| 321 | + Action::factory( 'watch', $this->mArticle )->execute(); |
322 | 322 | } elseif( $this->mTitle->userIsWatching() ) { |
323 | | - $this->mArticle->doUnwatch(); |
| 323 | + Action::factory( 'unwatch', $this->mArticle )->execute(); |
324 | 324 | } |
325 | 325 | return $ok; |
326 | 326 | } |
Index: trunk/phase3/includes/Article.php |
— | — | @@ -1672,32 +1672,7 @@ |
1673 | 1673 | * Handle action=purge |
1674 | 1674 | */ |
1675 | 1675 | public function purge() { |
1676 | | - global $wgRequest, $wgOut; |
1677 | | - |
1678 | | - if ( $wgOut->getUser()->isAllowed( 'purge' ) || $wgRequest->wasPosted() ) { |
1679 | | - //FIXME: shouldn't this be in doPurge()? |
1680 | | - if ( wfRunHooks( 'ArticlePurge', array( &$this ) ) ) { |
1681 | | - $this->doPurge(); |
1682 | | - $this->view(); |
1683 | | - } |
1684 | | - } else { |
1685 | | - $formParams = array( |
1686 | | - 'method' => 'post', |
1687 | | - 'action' => $wgRequest->getRequestURL(), |
1688 | | - ); |
1689 | | - |
1690 | | - $wgOut->addWikiMsg( 'confirm-purge-top' ); |
1691 | | - |
1692 | | - $form = Html::openElement( 'form', $formParams ); |
1693 | | - $form .= Xml::submitButton( wfMsg( 'confirm_purge_button' ) ); |
1694 | | - $form .= Html::closeElement( 'form' ); |
1695 | | - |
1696 | | - $wgOut->addHTML( $form ); |
1697 | | - $wgOut->addWikiMsg( 'confirm-purge-bottom' ); |
1698 | | - |
1699 | | - $wgOut->setPageTitle( $this->mTitle->getPrefixedText() ); |
1700 | | - $wgOut->setRobotPolicy( 'noindex,nofollow' ); |
1701 | | - } |
| 1676 | + return Action::factory( 'purge', $this )->show(); |
1702 | 1677 | } |
1703 | 1678 | |
1704 | 1679 | /** |
— | — | @@ -1706,6 +1681,10 @@ |
1707 | 1682 | public function doPurge() { |
1708 | 1683 | global $wgUseSquid; |
1709 | 1684 | |
| 1685 | + if( !wfRunHooks( 'ArticlePurge', array( &$this ) ) ){ |
| 1686 | + return false; |
| 1687 | + } |
| 1688 | + |
1710 | 1689 | // Invalidate the cache |
1711 | 1690 | $this->mTitle->invalidateCache(); |
1712 | 1691 | $this->clear(); |
— | — | @@ -2345,27 +2324,10 @@ |
2346 | 2325 | |
2347 | 2326 | /** |
2348 | 2327 | * User-interface handler for the "watch" action |
| 2328 | + * @deprecated since 1.18 |
2349 | 2329 | */ |
2350 | 2330 | public function watch() { |
2351 | | - global $wgOut; |
2352 | | - |
2353 | | - if ( $wgOut->getUser()->isAnon() ) { |
2354 | | - $wgOut->showErrorPage( 'watchnologin', 'watchnologintext' ); |
2355 | | - return; |
2356 | | - } |
2357 | | - |
2358 | | - if ( wfReadOnly() ) { |
2359 | | - $wgOut->readOnlyPage(); |
2360 | | - return; |
2361 | | - } |
2362 | | - |
2363 | | - if ( $this->doWatch() ) { |
2364 | | - $wgOut->setPagetitle( wfMsg( 'addedwatch' ) ); |
2365 | | - $wgOut->setRobotPolicy( 'noindex,nofollow' ); |
2366 | | - $wgOut->addWikiMsg( 'addedwatchtext', $this->mTitle->getPrefixedText() ); |
2367 | | - } |
2368 | | - |
2369 | | - $wgOut->returnToMain( true, $this->mTitle->getPrefixedText() ); |
| 2331 | + Action::factory( 'watch', $this )->show(); |
2370 | 2332 | } |
2371 | 2333 | |
2372 | 2334 | /** |
— | — | @@ -2374,64 +2336,27 @@ |
2375 | 2337 | * This is safe to be called multiple times |
2376 | 2338 | * |
2377 | 2339 | * @return bool true on successful watch operation |
| 2340 | + * @deprecated since 1.18 |
2378 | 2341 | */ |
2379 | 2342 | public function doWatch() { |
2380 | | - global $wgUser; |
2381 | | - |
2382 | | - if ( $wgUser->isAnon() ) { |
2383 | | - return false; |
2384 | | - } |
2385 | | - |
2386 | | - if ( wfRunHooks( 'WatchArticle', array( &$wgUser, &$this ) ) ) { |
2387 | | - $wgUser->addWatch( $this->mTitle ); |
2388 | | - return wfRunHooks( 'WatchArticleComplete', array( &$wgUser, &$this ) ); |
2389 | | - } |
2390 | | - |
2391 | | - return false; |
| 2343 | + return Action::factory( 'watch', $this )->execute(); |
2392 | 2344 | } |
2393 | 2345 | |
2394 | 2346 | /** |
2395 | 2347 | * User interface handler for the "unwatch" action. |
| 2348 | + * @deprecated since 1.18 |
2396 | 2349 | */ |
2397 | 2350 | public function unwatch() { |
2398 | | - global $wgOut; |
2399 | | - |
2400 | | - if ( $wgOut->getUser()->isAnon() ) { |
2401 | | - $wgOut->showErrorPage( 'watchnologin', 'watchnologintext' ); |
2402 | | - return; |
2403 | | - } |
2404 | | - |
2405 | | - if ( wfReadOnly() ) { |
2406 | | - $wgOut->readOnlyPage(); |
2407 | | - return; |
2408 | | - } |
2409 | | - |
2410 | | - if ( $this->doUnwatch() ) { |
2411 | | - $wgOut->setPagetitle( wfMsg( 'removedwatch' ) ); |
2412 | | - $wgOut->setRobotPolicy( 'noindex,nofollow' ); |
2413 | | - $wgOut->addWikiMsg( 'removedwatchtext', $this->mTitle->getPrefixedText() ); |
2414 | | - } |
2415 | | - |
2416 | | - $wgOut->returnToMain( true, $this->mTitle->getPrefixedText() ); |
| 2351 | + Action::factory( 'unwatch', $this )->show(); |
2417 | 2352 | } |
2418 | 2353 | |
2419 | 2354 | /** |
2420 | 2355 | * Stop watching a page |
2421 | 2356 | * @return bool true on successful unwatch |
| 2357 | + * @deprecated since 1.18 |
2422 | 2358 | */ |
2423 | 2359 | public function doUnwatch() { |
2424 | | - global $wgUser; |
2425 | | - |
2426 | | - if ( $wgUser->isAnon() ) { |
2427 | | - return false; |
2428 | | - } |
2429 | | - |
2430 | | - if ( wfRunHooks( 'UnwatchArticle', array( &$wgUser, &$this ) ) ) { |
2431 | | - $wgUser->removeWatch( $this->mTitle ); |
2432 | | - return wfRunHooks( 'UnwatchArticleComplete', array( &$wgUser, &$this ) ); |
2433 | | - } |
2434 | | - |
2435 | | - return false; |
| 2360 | + return Action::factory( 'unwatch', $this )->execute(); |
2436 | 2361 | } |
2437 | 2362 | |
2438 | 2363 | /** |
Index: trunk/phase3/includes/Setup.php |
— | — | @@ -270,6 +270,14 @@ |
271 | 271 | $wgHiddenPrefs[] = 'enotifminoredits'; |
272 | 272 | } |
273 | 273 | |
| 274 | +# $wgDisabledActions is deprecated as of 1.18 |
| 275 | +foreach( $wgDisabledActions as $action ){ |
| 276 | + $wgActions[$action] = false; |
| 277 | +} |
| 278 | +if( !$wgAllowPageInfo ){ |
| 279 | + $wgActions['info'] = false; |
| 280 | +} |
| 281 | + |
274 | 282 | if ( !$wgHtml5Version && $wgHtml5 && $wgAllowRdfaAttributes ) { |
275 | 283 | # see http://www.w3.org/TR/rdfa-in-html/#document-conformance |
276 | 284 | if ( $wgMimeType == 'application/xhtml+xml' ) { |
Index: trunk/phase3/includes/EditPage.php |
— | — | @@ -1153,9 +1153,9 @@ |
1154 | 1154 | $dbw = wfGetDB( DB_MASTER ); |
1155 | 1155 | $dbw->begin(); |
1156 | 1156 | if ( $this->watchthis ) { |
1157 | | - $this->mArticle->doWatch(); |
| 1157 | + Action::factory( 'watch', $this->mArticle )->execute(); |
1158 | 1158 | } else { |
1159 | | - $this->mArticle->doUnwatch(); |
| 1159 | + Action::factory( 'watch', $this->mArticle )->execute(); |
1160 | 1160 | } |
1161 | 1161 | $dbw->commit(); |
1162 | 1162 | } |
Index: trunk/phase3/includes/actions/DeleteAction.php |
— | — | @@ -0,0 +1,476 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Performs the watch and unwatch actions on a page |
| 5 | + * |
| 6 | + * This program is free software; you can redistribute it and/or modify |
| 7 | + * it under the terms of the GNU General Public License as published by |
| 8 | + * the Free Software Foundation; either version 2 of the License, or |
| 9 | + * (at your option) any later version. |
| 10 | + * |
| 11 | + * This program is distributed in the hope that it will be useful, |
| 12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | + * GNU General Public License for more details. |
| 15 | + * |
| 16 | + * You should have received a copy of the GNU General Public License |
| 17 | + * along with this program; if not, write to the Free Software |
| 18 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
| 19 | + * |
| 20 | + * @file |
| 21 | + * @ingroup Actions |
| 22 | + */ |
| 23 | + |
| 24 | +class DeleteAction extends Action { |
| 25 | + |
| 26 | + public function getName(){ |
| 27 | + return 'delete'; |
| 28 | + } |
| 29 | + |
| 30 | + public function getRestriction(){ |
| 31 | + return 'delete'; |
| 32 | + } |
| 33 | + |
| 34 | + protected function getDescription(){ |
| 35 | + return wfMsg( 'delete-confirm', $this->getTitle()->getPrefixedText() ); |
| 36 | + } |
| 37 | + |
| 38 | + /** |
| 39 | + * Check that the deletion can be executed. In addition to checking the user permissions, |
| 40 | + * check that the page is not too big and has not already been deleted. |
| 41 | + * @throws ErrorPageError |
| 42 | + * @see Action::checkCanExecute |
| 43 | + */ |
| 44 | + protected function checkCanExecute( User $user ){ |
| 45 | + |
| 46 | + // Check that the article hasn't already been deleted |
| 47 | + $dbw = wfGetDB( DB_MASTER ); |
| 48 | + $conds = $this->getTitle()->pageCond(); |
| 49 | + $latest = $dbw->selectField( 'page', 'page_latest', $conds, __METHOD__ ); |
| 50 | + if ( $latest === false ) { |
| 51 | + // Get the deletion log |
| 52 | + $log = ''; |
| 53 | + LogEventsList::showLogExtract( |
| 54 | + $log, |
| 55 | + 'delete', |
| 56 | + $this->getTitle()->getPrefixedText() |
| 57 | + ); |
| 58 | + |
| 59 | + $msg = new Message( 'cannotdelete' ); |
| 60 | + $msg->params( $this->getTitle()->getPrefixedText() ); // This parameter is parsed |
| 61 | + $msg->rawParams( $log ); // This is not |
| 62 | + |
| 63 | + throw new ErrorPageError( 'internalerror', $msg ); |
| 64 | + } |
| 65 | + |
| 66 | + // Limit deletions of big pages |
| 67 | + $bigHistory = $this->isBigDeletion(); |
| 68 | + if ( $bigHistory && !$user->isAllowed( 'bigdelete' ) ) { |
| 69 | + global $wgDeleteRevisionsLimit; |
| 70 | + throw new ErrorPageError( |
| 71 | + 'internalerror', |
| 72 | + 'delete-toobig', |
| 73 | + $this->getContext()->lang->formatNum( $wgDeleteRevisionsLimit ) |
| 74 | + ); |
| 75 | + } |
| 76 | + |
| 77 | + return parent::checkCanExecute( $user ); |
| 78 | + } |
| 79 | + |
| 80 | + protected function getFormFields(){ |
| 81 | + // TODO: add more useful things here? |
| 82 | + $infoText = Html::rawElement( |
| 83 | + 'strong', |
| 84 | + array(), |
| 85 | + Linker::link( $this->getTitle(), $this->getTitle()->getText() ) |
| 86 | + ); |
| 87 | + |
| 88 | + $arr = array( |
| 89 | + 'Page' => array( |
| 90 | + 'type' => 'info', |
| 91 | + 'raw' => true, |
| 92 | + 'default' => $infoText, |
| 93 | + ), |
| 94 | + 'Reason' => array( |
| 95 | + 'type' => 'selectandother', |
| 96 | + 'label-message' => 'deletecomment', |
| 97 | + 'options-message' => 'deletereason-dropdown', |
| 98 | + 'size' => '60', |
| 99 | + 'maxlength' => '255', |
| 100 | + 'default' => self::getAutoReason( $this->page), |
| 101 | + ), |
| 102 | + ); |
| 103 | + |
| 104 | + if( $this->getUser()->isLoggedIn() ){ |
| 105 | + $arr['Watch'] = array( |
| 106 | + 'type' => 'check', |
| 107 | + 'label-message' => 'watchthis', |
| 108 | + 'default' => $this->getUser()->getBoolOption( 'watchdeletion' ) || $this->getTitle()->userIsWatching() |
| 109 | + ); |
| 110 | + } |
| 111 | + |
| 112 | + if( $this->getUser()->isAllowed( 'suppressrevision' ) ){ |
| 113 | + $arr['Suppress'] = array( |
| 114 | + 'type' => 'check', |
| 115 | + 'label-message' => 'revdelete-suppress', |
| 116 | + 'default' => false, |
| 117 | + ); |
| 118 | + } |
| 119 | + |
| 120 | + return $arr; |
| 121 | + } |
| 122 | + |
| 123 | + /** |
| 124 | + * Text to go at the top of the form, before the opening fieldset |
| 125 | + * @see Action::preText() |
| 126 | + * @return String |
| 127 | + */ |
| 128 | + protected function preText() { |
| 129 | + |
| 130 | + // If the page has a history, insert a warning |
| 131 | + if ( $this->page->estimateRevisionCount() ) { |
| 132 | + global $wgLang; |
| 133 | + |
| 134 | + $link = Linker::link( |
| 135 | + $this->getTitle(), |
| 136 | + wfMsgHtml( 'history' ), |
| 137 | + array( 'rel' => 'archives' ), |
| 138 | + array( 'action' => 'history' ) |
| 139 | + ); |
| 140 | + |
| 141 | + return Html::rawElement( |
| 142 | + 'strong', |
| 143 | + array( 'class' => 'mw-delete-warning-revisions' ), |
| 144 | + wfMessage( |
| 145 | + 'historywarning', |
| 146 | + $wgLang->formatNum( $this->page->estimateRevisionCount() ) |
| 147 | + )->rawParams( $link )->parse() |
| 148 | + ); |
| 149 | + } |
| 150 | + } |
| 151 | + |
| 152 | + /** |
| 153 | + * Text to go at the bottom of the form, below the closing fieldset |
| 154 | + * @see Action::postText() |
| 155 | + * @return string |
| 156 | + */ |
| 157 | + protected function postText(){ |
| 158 | + $s = ''; |
| 159 | + LogEventsList::showLogExtract( |
| 160 | + $s, |
| 161 | + 'delete', |
| 162 | + $this->getTitle()->getPrefixedText() |
| 163 | + ); |
| 164 | + return Html::element( 'h2', array(), LogPage::logName( 'delete' ) ) . $s; |
| 165 | + } |
| 166 | + |
| 167 | + protected function alterForm( HTMLForm &$form ){ |
| 168 | + $form->setWrapperLegend( wfMsgExt( 'delete-legend', array( 'parsemag', 'escapenoentities' ) ) ); |
| 169 | + |
| 170 | + if ( $this->getUser()->isAllowed( 'editinterface' ) ) { |
| 171 | + $link = Linker::link( |
| 172 | + Title::makeTitle( NS_MEDIAWIKI, 'Deletereason-dropdown' ), |
| 173 | + wfMsgHtml( 'delete-edit-reasonlist' ), |
| 174 | + array(), |
| 175 | + array( 'action' => 'edit' ) |
| 176 | + ); |
| 177 | + $form->addHeaderText( '<p class="mw-delete-editreasons">' . $link . '</p>' ); |
| 178 | + } |
| 179 | + } |
| 180 | + |
| 181 | + /** |
| 182 | + * Function called on form submission. Privilege checks and validation have already been |
| 183 | + * completed by this point; we just need to jump out to the heavy-lifting function, |
| 184 | + * which is implemented as a static method so it can be called from other places |
| 185 | + * TODO: make those other places call $action->execute() properly |
| 186 | + * @see Action::onSubmit() |
| 187 | + * @param $data Array |
| 188 | + * @return Array|Bool |
| 189 | + */ |
| 190 | + public function onSubmit( $data ){ |
| 191 | + $status = self::doDeleteArticle( $this->page, $this->getContext(), $data, true ); |
| 192 | + return $status; |
| 193 | + } |
| 194 | + |
| 195 | + public function onSuccess(){ |
| 196 | + // Watch or unwatch, if requested |
| 197 | + if( $this->getRequest()->getCheck( 'wpWatch' ) && $this->getUser()->isLoggedIn() ) { |
| 198 | + Action::factory( 'watch', $this->page )->execute(); |
| 199 | + } elseif ( $this->getTitle()->userIsWatching() ) { |
| 200 | + Action::factory( 'unwatch', $this->page )->execute(); |
| 201 | + } |
| 202 | + |
| 203 | + $this->getOutput()->setPagetitle( wfMsg( 'actioncomplete' ) ); |
| 204 | + $this->getOutput()->addWikiMsg( |
| 205 | + 'deletedtext', |
| 206 | + $this->getTitle()->getPrefixedText(), |
| 207 | + '[[Special:Log/delete|' . wfMsgNoTrans( 'deletionlog' ) . ']]' |
| 208 | + ); |
| 209 | + $this->getOutput()->returnToMain( false ); |
| 210 | + } |
| 211 | + |
| 212 | + /** |
| 213 | + * @return bool whether or not the page surpasses $wgDeleteRevisionsLimit revisions |
| 214 | + */ |
| 215 | + protected function isBigDeletion() { |
| 216 | + global $wgDeleteRevisionsLimit; |
| 217 | + return $wgDeleteRevisionsLimit && $this->page->estimateRevisionCount() > $wgDeleteRevisionsLimit; |
| 218 | + } |
| 219 | + |
| 220 | + /** |
| 221 | + * Back-end article deletion |
| 222 | + * Deletes the article with database consistency, writes logs, purges caches |
| 223 | + * |
| 224 | + * @param $commit boolean defaults to true, triggers transaction end |
| 225 | + * @return Bool|Array true if successful, error array on failure |
| 226 | + */ |
| 227 | + public static function doDeleteArticle( Article $page, RequestContext $context, array $data, $commit = true ) { |
| 228 | + global $wgDeferredUpdateList, $wgUseTrackbacks; |
| 229 | + |
| 230 | + wfDebug( __METHOD__ . "\n" ); |
| 231 | + |
| 232 | + // The normal syntax from HTMLSelectAndOtherField is for the reason to be in the form |
| 233 | + // 'Reason' => array( <full reason>, <dropdown>, <custom> ), but it's reasonable for other |
| 234 | + // functions to just pass 'Reason' => <reason> |
| 235 | + $data['Reason'] = (array)$data['Reason']; |
| 236 | + |
| 237 | + $error = null; |
| 238 | + if ( !wfRunHooks( 'ArticleDelete', array( &$page, &$context->user, &$data['Reason'][0], &$error ) ) ) { |
| 239 | + return $error; |
| 240 | + } |
| 241 | + |
| 242 | + $title = $page->getTitle(); |
| 243 | + $id = $page->getID( Title::GAID_FOR_UPDATE ); |
| 244 | + |
| 245 | + if ( $title->getDBkey() === '' || $id == 0 ) { |
| 246 | + return false; |
| 247 | + } |
| 248 | + |
| 249 | + $updates = new SiteStatsUpdate( 0, 1, - (int)$page->isCountable( $page->getRawText() ), -1 ); |
| 250 | + array_push( $wgDeferredUpdateList, $updates ); |
| 251 | + |
| 252 | + // Bitfields to further suppress the content |
| 253 | + if ( isset( $data['Suppress'] ) && $data['Suppress'] ) { |
| 254 | + $bitfield = 0; |
| 255 | + // This should be 15... |
| 256 | + $bitfield |= Revision::DELETED_TEXT; |
| 257 | + $bitfield |= Revision::DELETED_COMMENT; |
| 258 | + $bitfield |= Revision::DELETED_USER; |
| 259 | + $bitfield |= Revision::DELETED_RESTRICTED; |
| 260 | + |
| 261 | + $logtype = 'suppress'; |
| 262 | + } else { |
| 263 | + // Otherwise, leave it unchanged |
| 264 | + $bitfield = 'rev_deleted'; |
| 265 | + $logtype = 'delete'; |
| 266 | + } |
| 267 | + |
| 268 | + $dbw = wfGetDB( DB_MASTER ); |
| 269 | + $dbw->begin(); |
| 270 | + // For now, shunt the revision data into the archive table. |
| 271 | + // Text is *not* removed from the text table; bulk storage |
| 272 | + // is left intact to avoid breaking block-compression or |
| 273 | + // immutable storage schemes. |
| 274 | + // |
| 275 | + // For backwards compatibility, note that some older archive |
| 276 | + // table entries will have ar_text and ar_flags fields still. |
| 277 | + // |
| 278 | + // In the future, we may keep revisions and mark them with |
| 279 | + // the rev_deleted field, which is reserved for this purpose. |
| 280 | + $dbw->insertSelect( |
| 281 | + 'archive', |
| 282 | + array( 'page', 'revision' ), |
| 283 | + array( |
| 284 | + 'ar_namespace' => 'page_namespace', |
| 285 | + 'ar_title' => 'page_title', |
| 286 | + 'ar_comment' => 'rev_comment', |
| 287 | + 'ar_user' => 'rev_user', |
| 288 | + 'ar_user_text' => 'rev_user_text', |
| 289 | + 'ar_timestamp' => 'rev_timestamp', |
| 290 | + 'ar_minor_edit' => 'rev_minor_edit', |
| 291 | + 'ar_rev_id' => 'rev_id', |
| 292 | + 'ar_text_id' => 'rev_text_id', |
| 293 | + 'ar_text' => "''", // Be explicit to appease |
| 294 | + 'ar_flags' => "''", // MySQL's "strict mode"... |
| 295 | + 'ar_len' => 'rev_len', |
| 296 | + 'ar_page_id' => 'page_id', |
| 297 | + 'ar_deleted' => $bitfield |
| 298 | + ), |
| 299 | + array( |
| 300 | + 'page_id' => $id, |
| 301 | + 'page_id = rev_page' |
| 302 | + ), |
| 303 | + __METHOD__ |
| 304 | + ); |
| 305 | + |
| 306 | + // Delete restrictions for it |
| 307 | + $dbw->delete( 'page_restrictions', array ( 'pr_page' => $id ), __METHOD__ ); |
| 308 | + |
| 309 | + // Now that it's safely backed up, delete it |
| 310 | + $dbw->delete( 'page', array( 'page_id' => $id ), __METHOD__ ); |
| 311 | + |
| 312 | + // getArticleId() uses slave, could be laggy |
| 313 | + if ( $dbw->affectedRows() == 0 ) { |
| 314 | + $dbw->rollback(); |
| 315 | + return false; |
| 316 | + } |
| 317 | + |
| 318 | + // Fix category table counts |
| 319 | + $res = $dbw->select( 'categorylinks', 'cl_to', array( 'cl_from' => $id ), __METHOD__ ); |
| 320 | + $cats = array(); |
| 321 | + foreach ( $res as $row ) { |
| 322 | + $cats[] = $row->cl_to; |
| 323 | + } |
| 324 | + $page->updateCategoryCounts( array(), $cats ); |
| 325 | + |
| 326 | + // If using cascading deletes, we can skip some explicit deletes |
| 327 | + if ( !$dbw->cascadingDeletes() ) { |
| 328 | + $dbw->delete( 'revision', array( 'rev_page' => $id ), __METHOD__ ); |
| 329 | + |
| 330 | + if ( $wgUseTrackbacks ){ |
| 331 | + $dbw->delete( 'trackbacks', array( 'tb_page' => $id ), __METHOD__ ); |
| 332 | + } |
| 333 | + |
| 334 | + // Delete outgoing links |
| 335 | + $dbw->delete( 'pagelinks', array( 'pl_from' => $id ) ); |
| 336 | + $dbw->delete( 'imagelinks', array( 'il_from' => $id ) ); |
| 337 | + $dbw->delete( 'categorylinks', array( 'cl_from' => $id ) ); |
| 338 | + $dbw->delete( 'templatelinks', array( 'tl_from' => $id ) ); |
| 339 | + $dbw->delete( 'externallinks', array( 'el_from' => $id ) ); |
| 340 | + $dbw->delete( 'langlinks', array( 'll_from' => $id ) ); |
| 341 | + $dbw->delete( 'redirect', array( 'rd_from' => $id ) ); |
| 342 | + } |
| 343 | + |
| 344 | + // If using cleanup triggers, we can skip some manual deletes |
| 345 | + if ( !$dbw->cleanupTriggers() ) { |
| 346 | + // Clean up recentchanges entries... |
| 347 | + $dbw->delete( 'recentchanges', |
| 348 | + array( |
| 349 | + 'rc_type != ' . RC_LOG, |
| 350 | + 'rc_namespace' => $title->getNamespace(), |
| 351 | + 'rc_title' => $title->getDBkey() ), |
| 352 | + __METHOD__ |
| 353 | + ); |
| 354 | + $dbw->delete( |
| 355 | + 'recentchanges', |
| 356 | + array( 'rc_type != ' . RC_LOG, 'rc_cur_id' => $id ), |
| 357 | + __METHOD__ |
| 358 | + ); |
| 359 | + } |
| 360 | + |
| 361 | + // Clear caches |
| 362 | + // TODO: should this be in here or left in Article? |
| 363 | + Article::onArticleDelete( $title ); |
| 364 | + |
| 365 | + // Clear the cached article id so the interface doesn't act like we exist |
| 366 | + $title->resetArticleID( 0 ); |
| 367 | + |
| 368 | + // Log the deletion, if the page was suppressed, log it at Oversight instead |
| 369 | + $log = new LogPage( $logtype ); |
| 370 | + |
| 371 | + // Make sure logging got through |
| 372 | + $log->addEntry( 'delete', $title, $data['Reason'][0], array() ); |
| 373 | + |
| 374 | + if ( $commit ) { |
| 375 | + $dbw->commit(); |
| 376 | + } |
| 377 | + |
| 378 | + wfRunHooks( 'ArticleDeleteComplete', array( &$page, &$context->user, $data['Reason'][0], $id ) ); |
| 379 | + return true; |
| 380 | + } |
| 381 | + |
| 382 | + /** |
| 383 | + * Auto-generates a deletion reason. Also sets $this->hasHistory if the page has old |
| 384 | + * revisions. |
| 385 | + * |
| 386 | + * @return mixed String containing default reason or empty string, or boolean false |
| 387 | + * if no revision was found |
| 388 | + */ |
| 389 | + public static function getAutoReason( Article $page ) { |
| 390 | + global $wgContLang; |
| 391 | + |
| 392 | + $dbw = wfGetDB( DB_MASTER ); |
| 393 | + // Get the last revision |
| 394 | + $rev = Revision::newFromTitle( $page->getTitle() ); |
| 395 | + |
| 396 | + if ( is_null( $rev ) ) { |
| 397 | + return false; |
| 398 | + } |
| 399 | + |
| 400 | + // Get the article's contents |
| 401 | + $contents = $rev->getText(); |
| 402 | + $blank = false; |
| 403 | + |
| 404 | + // If the page is blank, use the text from the previous revision, |
| 405 | + // which can only be blank if there's a move/import/protect dummy revision involved |
| 406 | + if ( $contents == '' ) { |
| 407 | + $prev = $rev->getPrevious(); |
| 408 | + |
| 409 | + if ( $prev ) { |
| 410 | + $contents = $prev->getText(); |
| 411 | + $blank = true; |
| 412 | + } |
| 413 | + } |
| 414 | + |
| 415 | + // Find out if there was only one contributor |
| 416 | + // Only scan the last 20 revisions |
| 417 | + $res = $dbw->select( 'revision', 'rev_user_text', |
| 418 | + array( |
| 419 | + 'rev_page' => $page->getID(), |
| 420 | + $dbw->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ' = 0' |
| 421 | + ), |
| 422 | + __METHOD__, |
| 423 | + array( 'LIMIT' => 20 ) |
| 424 | + ); |
| 425 | + |
| 426 | + if ( $res === false ) { |
| 427 | + // This page has no revisions, which is very weird |
| 428 | + return false; |
| 429 | + } |
| 430 | + |
| 431 | + $row = $dbw->fetchObject( $res ); |
| 432 | + |
| 433 | + if ( $row ) { // $row is false if the only contributor is hidden |
| 434 | + $onlyAuthor = $row->rev_user_text; |
| 435 | + // Try to find a second contributor |
| 436 | + foreach ( $res as $row ) { |
| 437 | + if ( $row->rev_user_text != $onlyAuthor ) { // Bug 22999 |
| 438 | + $onlyAuthor = false; |
| 439 | + break; |
| 440 | + } |
| 441 | + } |
| 442 | + } else { |
| 443 | + $onlyAuthor = false; |
| 444 | + } |
| 445 | + |
| 446 | + // Generate the summary with a '$1' placeholder |
| 447 | + if ( $blank ) { |
| 448 | + // The current revision is blank and the one before is also |
| 449 | + // blank. It's just not our lucky day |
| 450 | + $reason = wfMessage( 'exbeforeblank', '$1' )->inContentLanguage()->text(); |
| 451 | + } else { |
| 452 | + if ( $onlyAuthor ) { |
| 453 | + $reason = wfMessage( 'excontentauthor', '$1', $onlyAuthor )->inContentLanguage()->text(); |
| 454 | + } else { |
| 455 | + $reason = wfMessage( 'excontent', '$1' )->inContentLanguage()->text(); |
| 456 | + } |
| 457 | + } |
| 458 | + |
| 459 | + if ( $reason == '-' ) { |
| 460 | + // Allow these UI messages to be blanked out cleanly |
| 461 | + return ''; |
| 462 | + } |
| 463 | + |
| 464 | + // Replace newlines with spaces to prevent uglyness |
| 465 | + $contents = preg_replace( "/[\n\r]/", ' ', $contents ); |
| 466 | + // Calculate the maximum number of chars to get |
| 467 | + // Max content length = max comment length - length of the comment (excl. $1) |
| 468 | + $maxLength = 255 - ( strlen( $reason ) - 2 ); |
| 469 | + $contents = $wgContLang->truncate( $contents, $maxLength ); |
| 470 | + // Remove possible unfinished links |
| 471 | + $contents = preg_replace( '/\[\[([^\]]*)\]?$/', '$1', $contents ); |
| 472 | + // Now replace the '$1' placeholder |
| 473 | + $reason = str_replace( '$1', $contents, $reason ); |
| 474 | + |
| 475 | + return $reason; |
| 476 | + } |
| 477 | +} |
Property changes on: trunk/phase3/includes/actions/DeleteAction.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 478 | + native |
Index: trunk/phase3/includes/actions/PurgeAction.php |
— | — | @@ -0,0 +1,93 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Formats credits for articles |
| 5 | + * |
| 6 | + * Copyright 2004, Evan Prodromou <evan@wikitravel.org>. |
| 7 | + * |
| 8 | + * This program is free software; you can redistribute it and/or modify |
| 9 | + * it under the terms of the GNU General Public License as published by |
| 10 | + * the Free Software Foundation; either version 2 of the License, or |
| 11 | + * (at your option) any later version. |
| 12 | + * |
| 13 | + * This program is distributed in the hope that it will be useful, |
| 14 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | + * GNU General Public License for more details. |
| 17 | + * |
| 18 | + * You should have received a copy of the GNU General Public License |
| 19 | + * along with this program; if not, write to the Free Software |
| 20 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
| 21 | + * |
| 22 | + * @file |
| 23 | + * @ingroup Actions |
| 24 | + * @author <evan@wikitravel.org> |
| 25 | + */ |
| 26 | + |
| 27 | +class PurgeAction extends FormAction { |
| 28 | + |
| 29 | + public function getName(){ |
| 30 | + return 'purge'; |
| 31 | + } |
| 32 | + |
| 33 | + public function getRestriction(){ |
| 34 | + return null; |
| 35 | + } |
| 36 | + |
| 37 | + public function requiresUnblock(){ |
| 38 | + return false; |
| 39 | + } |
| 40 | + |
| 41 | + public function getDescription(){ |
| 42 | + return ''; |
| 43 | + } |
| 44 | + |
| 45 | + /** |
| 46 | + * Just get an empty form with a single submit button |
| 47 | + * @return array |
| 48 | + */ |
| 49 | + protected function getFormFields(){ |
| 50 | + return array(); |
| 51 | + } |
| 52 | + |
| 53 | + public function onSubmit( $data ){ |
| 54 | + $this->page->doPurge(); |
| 55 | + return true; |
| 56 | + } |
| 57 | + |
| 58 | + /** |
| 59 | + * purge is slightly wierd because it can be either formed or formless depending |
| 60 | + * on user permissions |
| 61 | + */ |
| 62 | + public function show(){ |
| 63 | + $this->setHeaders(); |
| 64 | + |
| 65 | + // This will throw exceptions if there's a problem |
| 66 | + $this->checkCanExecute( $this->getUser() ); |
| 67 | + |
| 68 | + if( $this->getUser()->isAllowed( 'purge' ) ){ |
| 69 | + $this->onSubmit( array() ); |
| 70 | + $this->onSuccess(); |
| 71 | + } else { |
| 72 | + $form = $this->getForm(); |
| 73 | + if( $form->show() ){ |
| 74 | + $this->onSuccess(); |
| 75 | + } |
| 76 | + } |
| 77 | + } |
| 78 | + |
| 79 | + protected function alterForm( HTMLForm $form ){ |
| 80 | + $form->setSubmitText( wfMsg( 'confirm_purge_button' ) ); |
| 81 | + } |
| 82 | + |
| 83 | + protected function preText(){ |
| 84 | + return wfMessage( 'confirm-purge-top' )->parse(); |
| 85 | + } |
| 86 | + |
| 87 | + protected function postText(){ |
| 88 | + return wfMessage( 'confirm-purge-bottom' )->parse(); |
| 89 | + } |
| 90 | + |
| 91 | + public function onSuccess(){ |
| 92 | + $this->getOutput()->redirect( $this->getTitle() ); |
| 93 | + } |
| 94 | +} |
Property changes on: trunk/phase3/includes/actions/PurgeAction.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 95 | + native |
Index: trunk/phase3/includes/actions/WatchAction.php |
— | — | @@ -0,0 +1,86 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Performs the watch and unwatch actions on a page |
| 5 | + * |
| 6 | + * This program is free software; you can redistribute it and/or modify |
| 7 | + * it under the terms of the GNU General Public License as published by |
| 8 | + * the Free Software Foundation; either version 2 of the License, or |
| 9 | + * (at your option) any later version. |
| 10 | + * |
| 11 | + * This program is distributed in the hope that it will be useful, |
| 12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | + * GNU General Public License for more details. |
| 15 | + * |
| 16 | + * You should have received a copy of the GNU General Public License |
| 17 | + * along with this program; if not, write to the Free Software |
| 18 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
| 19 | + * |
| 20 | + * @file |
| 21 | + * @ingroup Actions |
| 22 | + */ |
| 23 | + |
| 24 | +class WatchAction extends FormlessAction { |
| 25 | + |
| 26 | + public function getName(){ |
| 27 | + return 'watch'; |
| 28 | + } |
| 29 | + |
| 30 | + public function getRestriction(){ |
| 31 | + return 'read'; |
| 32 | + } |
| 33 | + |
| 34 | + public function requiresUnblock(){ |
| 35 | + return false; |
| 36 | + } |
| 37 | + |
| 38 | + protected function getDescription(){ |
| 39 | + return wfMsg( 'addedwatch' ); |
| 40 | + } |
| 41 | + |
| 42 | + protected function checkCanExecute( User $user ){ |
| 43 | + if ( $user->isAnon() ) { |
| 44 | + throw new ErrorPageError( 'watchnologin', 'watchnologintext' ); |
| 45 | + } |
| 46 | + return parent::checkCanExecute( $user ); |
| 47 | + } |
| 48 | + |
| 49 | + public function onView() { |
| 50 | + wfProfileIn( __METHOD__ ); |
| 51 | + |
| 52 | + $user = $this->getUser(); |
| 53 | + if ( wfRunHooks( 'WatchArticle', array( &$user, &$this->page ) ) ) { |
| 54 | + $this->getUser()->addWatch( $this->getTitle() ); |
| 55 | + wfRunHooks( 'WatchArticleComplete', array( &$user, &$this->page ) ); |
| 56 | + } |
| 57 | + |
| 58 | + wfProfileOut( __METHOD__ ); |
| 59 | + |
| 60 | + return wfMessage( 'addedwatchtext', $this->getTitle()->getPrefixedText() )->parse(); |
| 61 | + } |
| 62 | +} |
| 63 | + |
| 64 | +class UnwatchAction extends WatchAction { |
| 65 | + |
| 66 | + public function getName(){ |
| 67 | + return 'unwatch'; |
| 68 | + } |
| 69 | + |
| 70 | + protected function getDescription(){ |
| 71 | + return wfMsg( 'removedwatch' ); |
| 72 | + } |
| 73 | + |
| 74 | + public function onView() { |
| 75 | + wfProfileIn( __METHOD__ ); |
| 76 | + |
| 77 | + $user = $this->getUser(); |
| 78 | + if ( wfRunHooks( 'UnwatchArticle', array( &$user, &$this->page ) ) ) { |
| 79 | + $this->getUser()->removeWatch( $this->getTitle() ); |
| 80 | + wfRunHooks( 'UnwatchArticleComplete', array( &$user, &$this->page ) ); |
| 81 | + } |
| 82 | + |
| 83 | + wfProfileOut( __METHOD__ ); |
| 84 | + |
| 85 | + return wfMessage( 'removedwatchtext', $this->getTitle()->getPrefixedText() )->parse(); |
| 86 | + } |
| 87 | +} |
Property changes on: trunk/phase3/includes/actions/WatchAction.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 88 | + native |
Index: trunk/phase3/includes/actions/CreditsAction.php |
— | — | @@ -0,0 +1,237 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Formats credits for articles |
| 5 | + * |
| 6 | + * Copyright 2004, Evan Prodromou <evan@wikitravel.org>. |
| 7 | + * |
| 8 | + * This program is free software; you can redistribute it and/or modify |
| 9 | + * it under the terms of the GNU General Public License as published by |
| 10 | + * the Free Software Foundation; either version 2 of the License, or |
| 11 | + * (at your option) any later version. |
| 12 | + * |
| 13 | + * This program is distributed in the hope that it will be useful, |
| 14 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | + * GNU General Public License for more details. |
| 17 | + * |
| 18 | + * You should have received a copy of the GNU General Public License |
| 19 | + * along with this program; if not, write to the Free Software |
| 20 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
| 21 | + * |
| 22 | + * @file |
| 23 | + * @ingroup Actions |
| 24 | + * @author <evan@wikitravel.org> |
| 25 | + */ |
| 26 | + |
| 27 | +class CreditsAction extends FormlessAction { |
| 28 | + |
| 29 | + public function getName(){ |
| 30 | + return 'credits'; |
| 31 | + } |
| 32 | + |
| 33 | + public function getRestriction(){ |
| 34 | + return null; |
| 35 | + } |
| 36 | + |
| 37 | + /** |
| 38 | + * This is largely cadged from PageHistory::history |
| 39 | + */ |
| 40 | + public function onView() { |
| 41 | + wfProfileIn( __METHOD__ ); |
| 42 | + |
| 43 | + if ( $this->page->getID() == 0 ) { |
| 44 | + $s = wfMsg( 'nocredits' ); |
| 45 | + } else { |
| 46 | + $s = $this->getCredits( -1 ); |
| 47 | + } |
| 48 | + |
| 49 | + wfProfileOut( __METHOD__ ); |
| 50 | + |
| 51 | + return $s; |
| 52 | + } |
| 53 | + |
| 54 | + /** |
| 55 | + * Get a list of contributors of $article |
| 56 | + * @param $article Article object |
| 57 | + * @param $cnt Int: maximum list of contributors to show |
| 58 | + * @param $showIfMax Bool: whether to contributors if there more than $cnt |
| 59 | + * @return String: html |
| 60 | + */ |
| 61 | + protected function getCredits( $cnt, $showIfMax = true ) { |
| 62 | + wfProfileIn( __METHOD__ ); |
| 63 | + $s = ''; |
| 64 | + |
| 65 | + if ( isset( $cnt ) && $cnt != 0 ) { |
| 66 | + $s = self::getAuthor( $this->page ); |
| 67 | + if ( $cnt > 1 || $cnt < 0 ) { |
| 68 | + $s .= ' ' . $this->getContributors( $cnt - 1, $showIfMax ); |
| 69 | + } |
| 70 | + } |
| 71 | + |
| 72 | + wfProfileOut( __METHOD__ ); |
| 73 | + return $s; |
| 74 | + } |
| 75 | + |
| 76 | + /** |
| 77 | + * Get the last author with the last modification time |
| 78 | + * @param $article Article object |
| 79 | + */ |
| 80 | + protected static function getAuthor( Article $article ) { |
| 81 | + global $wgLang; |
| 82 | + |
| 83 | + $user = User::newFromId( $article->getUser() ); |
| 84 | + |
| 85 | + $timestamp = $article->getTimestamp(); |
| 86 | + if ( $timestamp ) { |
| 87 | + $d = $wgLang->date( $article->getTimestamp(), true ); |
| 88 | + $t = $wgLang->time( $article->getTimestamp(), true ); |
| 89 | + } else { |
| 90 | + $d = ''; |
| 91 | + $t = ''; |
| 92 | + } |
| 93 | + return wfMsgExt( 'lastmodifiedatby', 'parsemag', $d, $t, self::userLink( $user ), $user->getName() ); |
| 94 | + } |
| 95 | + |
| 96 | + /** |
| 97 | + * Get a list of contributors of $article |
| 98 | + * @param $article Article object |
| 99 | + * @param $cnt Int: maximum list of contributors to show |
| 100 | + * @param $showIfMax Bool: whether to contributors if there more than $cnt |
| 101 | + * @return String: html |
| 102 | + */ |
| 103 | + protected function getContributors( $cnt, $showIfMax ) { |
| 104 | + global $wgLang, $wgHiddenPrefs; |
| 105 | + |
| 106 | + $contributors = $this->page->getContributors(); |
| 107 | + |
| 108 | + $others_link = false; |
| 109 | + |
| 110 | + # Hmm... too many to fit! |
| 111 | + if ( $cnt > 0 && $contributors->count() > $cnt ) { |
| 112 | + $others_link = $this->othersLink(); |
| 113 | + if ( !$showIfMax ) |
| 114 | + return wfMsgExt( 'othercontribs', 'parsemag', $others_link, $contributors->count() ); |
| 115 | + } |
| 116 | + |
| 117 | + $real_names = array(); |
| 118 | + $user_names = array(); |
| 119 | + $anon_ips = array(); |
| 120 | + |
| 121 | + # Sift for real versus user names |
| 122 | + foreach ( $contributors as $user ) { |
| 123 | + $cnt--; |
| 124 | + if ( $user->isLoggedIn() ) { |
| 125 | + $link = self::link( $user ); |
| 126 | + if ( !in_array( 'realname', $wgHiddenPrefs ) && $user->getRealName() ) { |
| 127 | + $real_names[] = $link; |
| 128 | + } else { |
| 129 | + $user_names[] = $link; |
| 130 | + } |
| 131 | + } else { |
| 132 | + $anon_ips[] = self::link( $user ); |
| 133 | + } |
| 134 | + |
| 135 | + if ( $cnt == 0 ) { |
| 136 | + break; |
| 137 | + } |
| 138 | + } |
| 139 | + |
| 140 | + if ( count( $real_names ) ) { |
| 141 | + $real = $wgLang->listToText( $real_names ); |
| 142 | + } else { |
| 143 | + $real = false; |
| 144 | + } |
| 145 | + |
| 146 | + # "ThisSite user(s) A, B and C" |
| 147 | + if ( count( $user_names ) ) { |
| 148 | + $user = wfMsgExt( |
| 149 | + 'siteusers', |
| 150 | + 'parsemag', |
| 151 | + $wgLang->listToText( $user_names ), count( $user_names ) |
| 152 | + ); |
| 153 | + } else { |
| 154 | + $user = false; |
| 155 | + } |
| 156 | + |
| 157 | + if ( count( $anon_ips ) ) { |
| 158 | + $anon = wfMsgExt( |
| 159 | + 'anonusers', |
| 160 | + 'parsemag', |
| 161 | + $wgLang->listToText( $anon_ips ), count( $anon_ips ) |
| 162 | + ); |
| 163 | + } else { |
| 164 | + $anon = false; |
| 165 | + } |
| 166 | + |
| 167 | + # This is the big list, all mooshed together. We sift for blank strings |
| 168 | + $fulllist = array(); |
| 169 | + foreach ( array( $real, $user, $anon, $others_link ) as $s ) { |
| 170 | + if ( $s ) { |
| 171 | + array_push( $fulllist, $s ); |
| 172 | + } |
| 173 | + } |
| 174 | + |
| 175 | + # Make the list into text... |
| 176 | + $creds = $wgLang->listToText( $fulllist ); |
| 177 | + |
| 178 | + # "Based on work by ..." |
| 179 | + return strlen( $creds ) |
| 180 | + ? wfMsgExt( 'othercontribs', 'parsemag', $creds, count( $fulllist ) ) |
| 181 | + : ''; |
| 182 | + } |
| 183 | + |
| 184 | + /** |
| 185 | + * Get a link to $user's user page |
| 186 | + * @param $user User object |
| 187 | + * @return String: html |
| 188 | + */ |
| 189 | + protected static function link( User $user ) { |
| 190 | + global $wgUser, $wgHiddenPrefs; |
| 191 | + if ( !in_array( 'realname', $wgHiddenPrefs ) && !$user->isAnon() ) { |
| 192 | + $real = $user->getRealName(); |
| 193 | + } else { |
| 194 | + $real = false; |
| 195 | + } |
| 196 | + |
| 197 | + $page = $user->isAnon() |
| 198 | + ? SpecialPage::getTitleFor( 'Contributions', $user->getName() ) |
| 199 | + : $user->getUserPage(); |
| 200 | + |
| 201 | + return Linker::link( $page, htmlspecialchars( $real ? $real : $user->getName() ) ); |
| 202 | + } |
| 203 | + |
| 204 | + /** |
| 205 | + * Get a link to $user's user page |
| 206 | + * @param $user User object |
| 207 | + * @return String: html |
| 208 | + */ |
| 209 | + protected static function userLink( User $user ) { |
| 210 | + $link = self::link( $user ); |
| 211 | + if ( $user->isAnon() ) { |
| 212 | + return wfMsgExt( 'anonuser', array( 'parseinline', 'replaceafter' ), $link ); |
| 213 | + } else { |
| 214 | + global $wgHiddenPrefs; |
| 215 | + if ( !in_array( 'realname', $wgHiddenPrefs ) && $user->getRealName() ) { |
| 216 | + return $link; |
| 217 | + } else { |
| 218 | + return wfMsgExt( 'siteuser', 'parsemag', $link, $user->getName() ); |
| 219 | + } |
| 220 | + } |
| 221 | + } |
| 222 | + |
| 223 | + /** |
| 224 | + * Get a link to action=credits of $article page |
| 225 | + * @param $article Article object |
| 226 | + * @return String: html |
| 227 | + */ |
| 228 | + protected function othersLink() { |
| 229 | + global $wgUser; |
| 230 | + return Linker::link( |
| 231 | + $this->getTitle(), |
| 232 | + wfMsgHtml( 'others' ), |
| 233 | + array(), |
| 234 | + array( 'action' => 'credits' ), |
| 235 | + array( 'known' ) |
| 236 | + ); |
| 237 | + } |
| 238 | +} |
Property changes on: trunk/phase3/includes/actions/CreditsAction.php |
___________________________________________________________________ |
Added: svn:keywords |
1 | 239 | + Author Date Id Revision |
Added: svn:eol-style |
2 | 240 | + native |
Index: trunk/phase3/includes/api/ApiBase.php |
— | — | @@ -645,9 +645,9 @@ |
646 | 646 | |
647 | 647 | $articleObj = new Article( $titleObj ); |
648 | 648 | if ( $value ) { |
649 | | - $articleObj->doWatch(); |
| 649 | + Action::factory( 'watch', $articleObj )->execute(); |
650 | 650 | } else { |
651 | | - $articleObj->doUnwatch(); |
| 651 | + Action::factory( 'unwatch', $articleObj )->execute(); |
652 | 652 | } |
653 | 653 | } |
654 | 654 | |
Index: trunk/phase3/includes/api/ApiWatch.php |
— | — | @@ -59,11 +59,11 @@ |
60 | 60 | if ( $params['unwatch'] ) { |
61 | 61 | $res['unwatched'] = ''; |
62 | 62 | $res['message'] = wfMsgExt( 'removedwatchtext', array( 'parse' ), $title->getPrefixedText() ); |
63 | | - $success = $article->doUnwatch(); |
| 63 | + $success = Action::factory( 'unwatch', $article )->execute(); |
64 | 64 | } else { |
65 | 65 | $res['watched'] = ''; |
66 | 66 | $res['message'] = wfMsgExt( 'addedwatchtext', array( 'parse' ), $title->getPrefixedText() ); |
67 | | - $success = $article->doWatch(); |
| 67 | + $success = Action::factory( 'watch', $article )->execute(); |
68 | 68 | } |
69 | 69 | if ( !$success ) { |
70 | 70 | $this->dieUsageMsg( array( 'hookaborted' ) ); |
Index: trunk/phase3/includes/AutoLoader.php |
— | — | @@ -14,6 +14,7 @@ |
15 | 15 | |
16 | 16 | $wgAutoloadLocalClasses = array( |
17 | 17 | # Includes |
| 18 | + 'Action' => 'includes/Action.php', |
18 | 19 | 'AjaxDispatcher' => 'includes/AjaxDispatcher.php', |
19 | 20 | 'AjaxResponse' => 'includes/AjaxResponse.php', |
20 | 21 | 'AlphabeticPager' => 'includes/Pager.php', |
— | — | @@ -51,7 +52,6 @@ |
52 | 53 | 'ConfEditorToken' => 'includes/ConfEditor.php', |
53 | 54 | 'ConstantDependency' => 'includes/CacheDependency.php', |
54 | 55 | 'CreativeCommonsRdf' => 'includes/Metadata.php', |
55 | | - 'Credits' => 'includes/Credits.php', |
56 | 56 | 'CSSJanus' => 'includes/libs/CSSJanus.php', |
57 | 57 | 'CSSMin' => 'includes/libs/CSSMin.php', |
58 | 58 | 'DependencyWrapper' => 'includes/CacheDependency.php', |
— | — | @@ -274,6 +274,12 @@ |
275 | 275 | 'ZhClient' => 'includes/ZhClient.php', |
276 | 276 | 'ZipDirectoryReader' => 'includes/ZipDirectoryReader.php', |
277 | 277 | |
| 278 | + # includes/actions |
| 279 | + 'CreditsAction' => 'includes/actions/CreditsAction.php', |
| 280 | + 'PurgeAction' => 'includes/actions/PurgeAction.php', |
| 281 | + 'UnwatchAction' => 'includes/actions/WatchAction.php', |
| 282 | + 'WatchAction' => 'includes/actions/WatchAction.php', |
| 283 | + |
278 | 284 | # includes/api |
279 | 285 | 'ApiBase' => 'includes/api/ApiBase.php', |
280 | 286 | 'ApiBlock' => 'includes/api/ApiBlock.php', |
Index: trunk/phase3/includes/Wiki.php |
— | — | @@ -471,9 +471,16 @@ |
472 | 472 | return; |
473 | 473 | } |
474 | 474 | |
475 | | - $action = $this->getAction(); |
| 475 | + $act = $this->getAction(); |
476 | 476 | |
477 | | - switch( $action ) { |
| 477 | + $action = Action::factory( $this->getAction(), $article ); |
| 478 | + if( $action instanceof Action ){ |
| 479 | + $action->show(); |
| 480 | + wfProfileOut( __METHOD__ ); |
| 481 | + return; |
| 482 | + } |
| 483 | + |
| 484 | + switch( $act ) { |
478 | 485 | case 'view': |
479 | 486 | $this->context->output->setSquidMaxage( $this->getVal( 'SquidMaxage' ) ); |
480 | 487 | $article->view(); |
— | — | @@ -484,8 +491,6 @@ |
485 | 492 | $raw->view(); |
486 | 493 | wfProfileOut( __METHOD__ . '-raw' ); |
487 | 494 | break; |
488 | | - case 'watch': |
489 | | - case 'unwatch': |
490 | 495 | case 'delete': |
491 | 496 | case 'revert': |
492 | 497 | case 'rollback': |
— | — | @@ -495,8 +500,7 @@ |
496 | 501 | case 'markpatrolled': |
497 | 502 | case 'render': |
498 | 503 | case 'deletetrackback': |
499 | | - case 'purge': |
500 | | - $article->$action(); |
| 504 | + $article->$act(); |
501 | 505 | break; |
502 | 506 | case 'print': |
503 | 507 | $article->view(); |
— | — | @@ -517,9 +521,6 @@ |
518 | 522 | $rdf->show(); |
519 | 523 | } |
520 | 524 | break; |
521 | | - case 'credits': |
522 | | - Credits::showPage( $article ); |
523 | | - break; |
524 | 525 | case 'submit': |
525 | 526 | if ( session_id() == '' ) { |
526 | 527 | // Send a cookie so anons get talk message notifications |
— | — | @@ -532,7 +533,7 @@ |
533 | 534 | $external = $this->context->request->getVal( 'externaledit' ); |
534 | 535 | $section = $this->context->request->getVal( 'section' ); |
535 | 536 | $oldid = $this->context->request->getVal( 'oldid' ); |
536 | | - if ( !$this->getVal( 'UseExternalEditor' ) || $action == 'submit' || $internal || |
| 537 | + if ( !$this->getVal( 'UseExternalEditor' ) || $act == 'submit' || $internal || |
537 | 538 | $section || $oldid || ( !$this->context->user->getOption( 'externaleditor' ) && !$external ) ) { |
538 | 539 | $editor = new EditPage( $article ); |
539 | 540 | $editor->submit(); |
— | — | @@ -561,7 +562,7 @@ |
562 | 563 | $special->execute( '' ); |
563 | 564 | break; |
564 | 565 | default: |
565 | | - if ( wfRunHooks( 'UnknownAction', array( $action, $article ) ) ) { |
| 566 | + if ( wfRunHooks( 'UnknownAction', array( $act, $article ) ) ) { |
566 | 567 | $this->context->output->showErrorPage( 'nosuchaction', 'nosuchactiontext' ); |
567 | 568 | } |
568 | 569 | } |
Index: trunk/phase3/includes/FileDeleteForm.php |
— | — | @@ -125,9 +125,9 @@ |
126 | 126 | if( $article->doDeleteArticle( $reason, $suppress, $id, false ) ) { |
127 | 127 | global $wgRequest; |
128 | 128 | if( $wgRequest->getCheck( 'wpWatch' ) && $wgUser->isLoggedIn() ) { |
129 | | - $article->doWatch(); |
| 129 | + Action::factory( 'watch', $article )->execute(); |
130 | 130 | } elseif( $title->userIsWatching() ) { |
131 | | - $article->doUnwatch(); |
| 131 | + Action::factory( 'unwatch', $article )->execute(); |
132 | 132 | } |
133 | 133 | $status = $file->delete( $reason, $suppress ); |
134 | 134 | if( $status->ok ) { |
Index: trunk/phase3/includes/DefaultSettings.php |
— | — | @@ -27,12 +27,10 @@ |
28 | 28 | } |
29 | 29 | |
30 | 30 | # Create a site configuration object. Not used for much in a default install |
31 | | -if ( !defined( 'MW_PHP4' ) ) { |
32 | | - if ( !defined( 'MW_COMPILED' ) ) { |
33 | | - require_once( "$IP/includes/SiteConfiguration.php" ); |
34 | | - } |
35 | | - $wgConf = new SiteConfiguration; |
| 31 | +if ( !defined( 'MW_COMPILED' ) ) { |
| 32 | + require_once( "$IP/includes/SiteConfiguration.php" ); |
36 | 33 | } |
| 34 | +$wgConf = new SiteConfiguration; |
37 | 35 | /** @endcond */ |
38 | 36 | |
39 | 37 | /** MediaWiki version number */ |
— | — | @@ -5024,6 +5022,38 @@ |
5025 | 5023 | /** @} */ # end special pages } |
5026 | 5024 | |
5027 | 5025 | /*************************************************************************//** |
| 5026 | + * @name Actions |
| 5027 | + * @{ |
| 5028 | + */ |
| 5029 | + |
| 5030 | +/** |
| 5031 | + * Array of allowed values for the title=foo&action=<action> parameter. Syntax is: |
| 5032 | + * 'foo' => 'ClassName' Load the specified class which subclasses Action |
| 5033 | + * 'foo' => true Load the class FooAction which subclasses Action |
| 5034 | + * 'foo' => false The action is disabled; show an error message |
| 5035 | + * Unsetting core actions will probably cause things to complain loudly. |
| 5036 | + */ |
| 5037 | +$wgActions = array( |
| 5038 | + 'credits' => true, |
| 5039 | + 'purge' => true, |
| 5040 | + 'unwatch' => true, |
| 5041 | + 'watch' => true, |
| 5042 | +); |
| 5043 | + |
| 5044 | +/** |
| 5045 | + * Array of disabled article actions, e.g. view, edit, dublincore, delete, etc. |
| 5046 | + * @deprecated since 1.18; just set $wgActions['action'] = false instead |
| 5047 | + */ |
| 5048 | +$wgDisabledActions = array(); |
| 5049 | + |
| 5050 | +/** |
| 5051 | + * Allow the "info" action, very inefficient at the moment |
| 5052 | + */ |
| 5053 | +$wgAllowPageInfo = false; |
| 5054 | + |
| 5055 | +/** @} */ # end actions } |
| 5056 | + |
| 5057 | +/*************************************************************************//** |
5028 | 5058 | * @name Robot (search engine crawler) policy |
5029 | 5059 | * See also $wgNoFollowLinks. |
5030 | 5060 | * @{ |
— | — | @@ -5288,18 +5318,10 @@ |
5289 | 5319 | * @{ |
5290 | 5320 | */ |
5291 | 5321 | |
5292 | | -/** Allow the "info" action, very inefficient at the moment */ |
5293 | | -$wgAllowPageInfo = false; |
5294 | | - |
5295 | 5322 | /** Name of the external diff engine to use */ |
5296 | 5323 | $wgExternalDiffEngine = false; |
5297 | 5324 | |
5298 | 5325 | /** |
5299 | | - * Array of disabled article actions, e.g. view, edit, dublincore, delete, etc. |
5300 | | - */ |
5301 | | -$wgDisabledActions = array(); |
5302 | | - |
5303 | | -/** |
5304 | 5326 | * Disable redirects to special pages and interwiki redirects, which use a 302 |
5305 | 5327 | * and have no "redirected from" link. Note this is only for articles with #Redirect |
5306 | 5328 | * in them. URL's containing a local interwiki prefix (or a non-canonical special |
Index: trunk/phase3/includes/Action.php |
— | — | @@ -0,0 +1,441 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Actions are things which can be done to pages (edit, delete, rollback, etc). They |
| 5 | + * are distinct from Special Pages because an action must apply to exactly one page. |
| 6 | + * |
| 7 | + * To add an action in an extension, create a subclass of Action, and add the key to |
| 8 | + * $wgActions. There is also the deprecated UnknownAction hook |
| 9 | + * |
| 10 | + * |
| 11 | + * This program is free software; you can redistribute it and/or modify |
| 12 | + * it under the terms of the GNU General Public License as published by |
| 13 | + * the Free Software Foundation; either version 2 of the License, or |
| 14 | + * (at your option) any later version. |
| 15 | + * |
| 16 | + * This program is distributed in the hope that it will be useful, |
| 17 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 18 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 19 | + * GNU General Public License for more details. |
| 20 | + * |
| 21 | + * You should have received a copy of the GNU General Public License |
| 22 | + * along with this program; if not, write to the Free Software |
| 23 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
| 24 | + * |
| 25 | + * @file |
| 26 | + */ |
| 27 | +abstract class Action { |
| 28 | + |
| 29 | + // Page on which we're performing the action |
| 30 | + // @var Article |
| 31 | + protected $page; |
| 32 | + |
| 33 | + // RequestContext if specified; otherwise we'll use the Context from the Page |
| 34 | + // @var RequestContext |
| 35 | + protected $context; |
| 36 | + |
| 37 | + // The fields used to create the HTMLForm |
| 38 | + // @var Array |
| 39 | + protected $fields; |
| 40 | + |
| 41 | + /** |
| 42 | + * Get the Action subclass which should be used to handle this action, false if |
| 43 | + * the action is disabled, or null if it's not recognised |
| 44 | + * @param $action String |
| 45 | + * @return bool|null|string |
| 46 | + */ |
| 47 | + private final static function getClass( $action ){ |
| 48 | + global $wgActions; |
| 49 | + $action = strtolower( $action ); |
| 50 | + |
| 51 | + if( !isset( $wgActions[$action] ) ){ |
| 52 | + return null; |
| 53 | + } |
| 54 | + |
| 55 | + if( $wgActions[$action] === false ){ |
| 56 | + return false; |
| 57 | + } |
| 58 | + |
| 59 | + elseif( $wgActions[$action] === true ){ |
| 60 | + return ucfirst( $action ) . 'Action'; |
| 61 | + } |
| 62 | + |
| 63 | + else { |
| 64 | + return $wgActions[$action]; |
| 65 | + } |
| 66 | + } |
| 67 | + |
| 68 | + /** |
| 69 | + * Get an appropriate Action subclass for the given action |
| 70 | + * @param $action String |
| 71 | + * @param $page Article |
| 72 | + * @return Action|false|null false if the action is disabled, null |
| 73 | + * if it is not recognised |
| 74 | + */ |
| 75 | + public final static function factory( $action, Article $page ){ |
| 76 | + $class = self::getClass( $action ); |
| 77 | + if( $class ){ |
| 78 | + $obj = new $class( $page ); |
| 79 | + return $obj; |
| 80 | + } |
| 81 | + return $class; |
| 82 | + } |
| 83 | + |
| 84 | + /** |
| 85 | + * Check if a given action is recognised, even if it's disabled |
| 86 | + * |
| 87 | + * @param $name String: name of an action |
| 88 | + * @return Bool |
| 89 | + */ |
| 90 | + public final static function exists( $name ) { |
| 91 | + return self::getClass( $name ) !== null; |
| 92 | + } |
| 93 | + |
| 94 | + /** |
| 95 | + * Get the RequestContext in use here |
| 96 | + * @return RequestContext |
| 97 | + */ |
| 98 | + protected final function getContext(){ |
| 99 | + if( $this->context instanceof RequestContext ){ |
| 100 | + return $this->context; |
| 101 | + } |
| 102 | + return $this->page->getContext(); |
| 103 | + } |
| 104 | + |
| 105 | + /** |
| 106 | + * Get the WebRequest being used for this instance |
| 107 | + * |
| 108 | + * @return WebRequest |
| 109 | + */ |
| 110 | + protected final function getRequest() { |
| 111 | + return $this->getContext()->request; |
| 112 | + } |
| 113 | + |
| 114 | + /** |
| 115 | + * Get the OutputPage being used for this instance |
| 116 | + * |
| 117 | + * @return OutputPage |
| 118 | + */ |
| 119 | + protected final function getOutput() { |
| 120 | + return $this->getContext()->output; |
| 121 | + } |
| 122 | + |
| 123 | + /** |
| 124 | + * Shortcut to get the User being used for this instance |
| 125 | + * |
| 126 | + * @return User |
| 127 | + */ |
| 128 | + protected final function getUser() { |
| 129 | + return $this->getContext()->user; |
| 130 | + } |
| 131 | + |
| 132 | + /** |
| 133 | + * Shortcut to get the Skin being used for this instance |
| 134 | + * |
| 135 | + * @return Skin |
| 136 | + */ |
| 137 | + protected final function getSkin() { |
| 138 | + return $this->getContext()->skin; |
| 139 | + } |
| 140 | + |
| 141 | + /** |
| 142 | + * Shortcut to get the Title object from the page |
| 143 | + * @return Title |
| 144 | + */ |
| 145 | + protected final function getTitle(){ |
| 146 | + return $this->page->getTitle(); |
| 147 | + } |
| 148 | + |
| 149 | + /** |
| 150 | + * Protected constructor: use Action::factory( $action, $page ) to actually build |
| 151 | + * these things in the real world |
| 152 | + * @param Article $page |
| 153 | + */ |
| 154 | + protected function __construct( Article $page ){ |
| 155 | + $this->page = $page; |
| 156 | + } |
| 157 | + |
| 158 | + /** |
| 159 | + * Return the name of the action this object responds to |
| 160 | + * @return String lowercase |
| 161 | + */ |
| 162 | + public abstract function getName(); |
| 163 | + |
| 164 | + /** |
| 165 | + * Get the permission required to perform this action. Often, but not always, |
| 166 | + * the same as the action name |
| 167 | + */ |
| 168 | + public abstract function getRestriction(); |
| 169 | + |
| 170 | + /** |
| 171 | + * Checks if the given user (identified by an object) can perform this action. Can be |
| 172 | + * overridden by sub-classes with more complicated permissions schemes. Failures here |
| 173 | + * must throw subclasses of ErrorPageError |
| 174 | + * |
| 175 | + * @param $user User: the user to check, or null to use the context user |
| 176 | + * @throws ErrorPageError |
| 177 | + */ |
| 178 | + protected function checkCanExecute( User $user ) { |
| 179 | + if( $this->requiresWrite() && wfReadOnly() ){ |
| 180 | + throw new ReadOnlyError(); |
| 181 | + } |
| 182 | + |
| 183 | + if( $this->getRestriction() !== null && !$user->isAllowed( $this->getRestriction() ) ){ |
| 184 | + throw new PermissionsError( $this->getRestriction() ); |
| 185 | + } |
| 186 | + |
| 187 | + if( $this->requiresUnblock() && $user->isBlocked() ){ |
| 188 | + $block = $user->mBlock; |
| 189 | + throw new UserBlockedError( $block ); |
| 190 | + } |
| 191 | + } |
| 192 | + |
| 193 | + /** |
| 194 | + * Whether this action requires the wiki not to be locked |
| 195 | + * @return Bool |
| 196 | + */ |
| 197 | + public function requiresWrite(){ |
| 198 | + return true; |
| 199 | + } |
| 200 | + |
| 201 | + /** |
| 202 | + * Whether this action can still be executed by a blocked user |
| 203 | + * @return Bool |
| 204 | + */ |
| 205 | + public function requiresUnblock(){ |
| 206 | + return true; |
| 207 | + } |
| 208 | + |
| 209 | + /** |
| 210 | + * Set output headers for noindexing etc. This function will not be called through |
| 211 | + * the execute() entry point, so only put UI-related stuff in here. |
| 212 | + */ |
| 213 | + protected function setHeaders() { |
| 214 | + $out = $this->getOutput(); |
| 215 | + $out->setRobotPolicy( "noindex,nofollow" ); |
| 216 | + $out->setPageTitle( $this->getTitle()->getPrefixedText() ); |
| 217 | + $this->getOutput()->setSubtitle( $this->getDescription() ); |
| 218 | + $out->setArticleRelated( true ); |
| 219 | + } |
| 220 | + |
| 221 | + /** |
| 222 | + * Returns the name that goes in the \<h1\> page title |
| 223 | + * |
| 224 | + * Derived classes can override this, but usually it is easier to keep the |
| 225 | + * default behaviour. Messages can be added at run-time, see |
| 226 | + * MessageCache.php. |
| 227 | + * |
| 228 | + * @return String |
| 229 | + */ |
| 230 | + protected function getDescription() { |
| 231 | + return wfMsg( strtolower( $this->getName() ) ); |
| 232 | + } |
| 233 | + |
| 234 | + /** |
| 235 | + * The main action entry point. Do all output for display and send it to the context |
| 236 | + * output. Do not use globals $wgOut, $wgRequest, etc, in implementations; use |
| 237 | + * $this->getOutput(), etc. |
| 238 | + * @throws ErrorPageError |
| 239 | + */ |
| 240 | + public abstract function show(); |
| 241 | + |
| 242 | + /** |
| 243 | + * Execute the action in a silent fashion: do not display anything or release any errors. |
| 244 | + * @param $data Array values that would normally be in the POST request |
| 245 | + * @param $captureErrors Bool whether to catch exceptions and just return false |
| 246 | + * @return Bool whether execution was successful |
| 247 | + */ |
| 248 | + public abstract function execute(); |
| 249 | +} |
| 250 | + |
| 251 | +abstract class FormAction extends Action { |
| 252 | + |
| 253 | + /** |
| 254 | + * Get an HTMLForm descriptor array |
| 255 | + * @return Array |
| 256 | + */ |
| 257 | + protected abstract function getFormFields(); |
| 258 | + |
| 259 | + /** |
| 260 | + * Add pre- or post-text to the form |
| 261 | + * @return String HTML which will be sent to $form->addPreText() |
| 262 | + */ |
| 263 | + protected function preText(){ return ''; } |
| 264 | + protected function postText(){ return ''; } |
| 265 | + |
| 266 | + /** |
| 267 | + * Play with the HTMLForm if you need to more substantially |
| 268 | + * @param &$form HTMLForm |
| 269 | + */ |
| 270 | + protected function alterForm( HTMLForm &$form ){} |
| 271 | + |
| 272 | + /** |
| 273 | + * Get the HTMLForm to control behaviour |
| 274 | + * @return HTMLForm|null |
| 275 | + */ |
| 276 | + protected function getForm(){ |
| 277 | + $this->fields = $this->getFormFields(); |
| 278 | + |
| 279 | + // Give hooks a chance to alter the form, adding extra fields or text etc |
| 280 | + wfRunHooks( 'ActionModifyFormFields', array( $this->getName(), &$this->fields, $this->page ) ); |
| 281 | + |
| 282 | + $form = new HTMLForm( $this->fields, $this->getContext() ); |
| 283 | + $form->setSubmitCallback( array( $this, 'onSubmit' ) ); |
| 284 | + $form->addHiddenField( 'action', $this->getName() ); |
| 285 | + |
| 286 | + $form->addPreText( $this->preText() ); |
| 287 | + $form->addPostText( $this->postText() ); |
| 288 | + $this->alterForm( $form ); |
| 289 | + |
| 290 | + // Give hooks a chance to alter the form, adding extra fields or text etc |
| 291 | + wfRunHooks( 'ActionBeforeFormDisplay', array( $this->getName(), &$form, $this->page ) ); |
| 292 | + |
| 293 | + return $form; |
| 294 | + } |
| 295 | + |
| 296 | + /** |
| 297 | + * Process the form on POST submission. If you return false from getFormFields(), |
| 298 | + * this will obviously never be reached. If you don't want to do anything with the |
| 299 | + * form, just return false here |
| 300 | + * @param $data Array |
| 301 | + * @return Bool|Array true for success, false for didn't-try, array of errors on failure |
| 302 | + */ |
| 303 | + public abstract function onSubmit( $data ); |
| 304 | + |
| 305 | + /** |
| 306 | + * Do something exciting on successful processing of the form. This might be to show |
| 307 | + * a confirmation message (watch, rollback, etc) or to redirect somewhere else (edit, |
| 308 | + * protect, etc). |
| 309 | + */ |
| 310 | + public abstract function onSuccess(); |
| 311 | + |
| 312 | + /** |
| 313 | + * The basic pattern for actions is to display some sort of HTMLForm UI, maybe with |
| 314 | + * some stuff underneath (history etc); to do some processing on submission of that |
| 315 | + * form (delete, protect, etc) and to do something exciting on 'success', be that |
| 316 | + * display something new or redirect to somewhere. Some actions have more exotic |
| 317 | + * behaviour, but that's what subclassing is for :D |
| 318 | + */ |
| 319 | + public function show(){ |
| 320 | + $this->setHeaders(); |
| 321 | + |
| 322 | + // This will throw exceptions if there's a problem |
| 323 | + $this->checkCanExecute( $this->getUser() ); |
| 324 | + |
| 325 | + $form = $this->getForm(); |
| 326 | + if( $form->show() ){ |
| 327 | + $this->onSuccess(); |
| 328 | + } |
| 329 | + } |
| 330 | + |
| 331 | + /** |
| 332 | + * @see Action::execute() |
| 333 | + * @throws ErrorPageError |
| 334 | + * @param array|null $data |
| 335 | + * @param bool $captureErrors |
| 336 | + * @return bool |
| 337 | + */ |
| 338 | + public function execute( array $data = null, $captureErrors = true ){ |
| 339 | + try { |
| 340 | + // Set a new context so output doesn't leak. |
| 341 | + $this->context = clone $this->page->getContext(); |
| 342 | + |
| 343 | + // This will throw exceptions if there's a problem |
| 344 | + $this->checkCanExecute( $this->getUser() ); |
| 345 | + |
| 346 | + $fields = array(); |
| 347 | + foreach( $this->fields as $key => $params ){ |
| 348 | + if( isset( $data[$key] ) ){ |
| 349 | + $fields[$key] = $data[$key]; |
| 350 | + } elseif( isset( $params['default'] ) ) { |
| 351 | + $fields[$key] = $params['default']; |
| 352 | + } else { |
| 353 | + $fields[$key] = null; |
| 354 | + } |
| 355 | + } |
| 356 | + $status = $this->onSubmit( $fields ); |
| 357 | + if( $status === true ){ |
| 358 | + // This might do permanent stuff |
| 359 | + $this->onSuccess(); |
| 360 | + return true; |
| 361 | + } else { |
| 362 | + return false; |
| 363 | + } |
| 364 | + } |
| 365 | + catch ( ErrorPageError $e ){ |
| 366 | + if( $captureErrors ){ |
| 367 | + return false; |
| 368 | + } else { |
| 369 | + throw $e; |
| 370 | + } |
| 371 | + } |
| 372 | + } |
| 373 | +} |
| 374 | + |
| 375 | +/** |
| 376 | + * Actions generally fall into two groups: the show-a-form-then-do-something-with-the-input |
| 377 | + * format (protect, delete, move, etc), and the just-do-something format (watch, rollback, |
| 378 | + * patrol, etc). |
| 379 | + */ |
| 380 | +abstract class FormlessAction extends Action { |
| 381 | + |
| 382 | + /** |
| 383 | + * Show something on GET request. |
| 384 | + * @return String|null will be added to the HTMLForm if present, or just added to the |
| 385 | + * output if not. Return null to not add anything |
| 386 | + */ |
| 387 | + public abstract function onView(); |
| 388 | + |
| 389 | + /** |
| 390 | + * We don't want an HTMLForm |
| 391 | + */ |
| 392 | + protected function getFormFields(){ |
| 393 | + return false; |
| 394 | + } |
| 395 | + |
| 396 | + public function onSubmit( $data ){ |
| 397 | + return false; |
| 398 | + } |
| 399 | + |
| 400 | + public function onSuccess(){ |
| 401 | + return false; |
| 402 | + } |
| 403 | + |
| 404 | + public function show(){ |
| 405 | + $this->setHeaders(); |
| 406 | + |
| 407 | + // This will throw exceptions if there's a problem |
| 408 | + $this->checkCanExecute( $this->getUser() ); |
| 409 | + |
| 410 | + $this->getOutput()->addHTML( $this->onView() ); |
| 411 | + } |
| 412 | + |
| 413 | + /** |
| 414 | + * Execute the action silently, not giving any output. Since these actions don't have |
| 415 | + * forms, they probably won't have any data, but some (eg rollback) may do |
| 416 | + * @param $data Array values that would normally be in the GET request |
| 417 | + * @param $captureErrors Bool whether to catch exceptions and just return false |
| 418 | + * @return Bool whether execution was successful |
| 419 | + */ |
| 420 | + public function execute( array $data = null, $captureErrors = true){ |
| 421 | + try { |
| 422 | + // Set a new context so output doesn't leak. |
| 423 | + $this->context = clone $this->page->getContext(); |
| 424 | + if( is_array( $data ) ){ |
| 425 | + $this->context->setRequest( new FauxRequest( $data, false ) ); |
| 426 | + } |
| 427 | + |
| 428 | + // This will throw exceptions if there's a problem |
| 429 | + $this->checkCanExecute( $this->getUser() ); |
| 430 | + |
| 431 | + $this->onView(); |
| 432 | + return true; |
| 433 | + } |
| 434 | + catch ( ErrorPageError $e ){ |
| 435 | + if( $captureErrors ){ |
| 436 | + return false; |
| 437 | + } else { |
| 438 | + throw $e; |
| 439 | + } |
| 440 | + } |
| 441 | + } |
| 442 | +} |
\ No newline at end of file |
Property changes on: trunk/phase3/includes/Action.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 443 | + native |