$wgStyleVersion!
[lhc/web/wiklou.git] / includes / SpecialRevisiondelete.php
1 <?php
2
3 /**
4 * Not quite ready for production use yet; need to fix up the restricted mode,
5 * and provide for preservation across delete/undelete of the page.
6 *
7 * To try this out, set up extra permissions something like:
8 * $wgGroupPermissions['sysop']['deleterevision'] = true;
9 * $wgGroupPermissions['bureaucrat']['hiderevision'] = true;
10 */
11
12 function wfSpecialRevisiondelete( $par = null ) {
13 global $wgOut, $wgRequest;
14
15 $target = $wgRequest->getVal( 'target' );
16 $oldid = $wgRequest->getIntArray( 'oldid' );
17
18 $page = Title::newFromUrl( $target );
19
20 if( is_null( $page ) ) {
21 $wgOut->showErrorPage( 'notargettitle', 'notargettext' );
22 return;
23 }
24
25 if( is_null( $oldid ) ) {
26 $wgOut->showErrorPage( 'revdelete-nooldid-title', 'revdelete-nooldid-text' );
27 return;
28 }
29
30 $form = new RevisionDeleteForm( $wgRequest );
31 if( $wgRequest->wasPosted() ) {
32 $form->submit( $wgRequest );
33 } else {
34 $form->show( $wgRequest );
35 }
36 }
37
38 /**
39 * Implements the GUI for Revision Deletion.
40 * @addtogroup SpecialPage
41 */
42 class RevisionDeleteForm {
43 /**
44 * @param Title $page
45 * @param int $oldid
46 */
47 function __construct( $request ) {
48 global $wgUser;
49
50 $target = $request->getVal( 'target' );
51 $this->page = Title::newFromUrl( $target );
52
53 $this->revisions = $request->getIntArray( 'oldid', array() );
54
55 $this->skin = $wgUser->getSkin();
56 $this->checks = array(
57 array( 'revdelete-hide-text', 'wpHideText', Revision::DELETED_TEXT ),
58 array( 'revdelete-hide-comment', 'wpHideComment', Revision::DELETED_COMMENT ),
59 array( 'revdelete-hide-user', 'wpHideUser', Revision::DELETED_USER ),
60 array( 'revdelete-hide-restricted', 'wpHideRestricted', Revision::DELETED_RESTRICTED ) );
61 }
62
63 /**
64 * @param WebRequest $request
65 */
66 function show( $request ) {
67 global $wgOut, $wgUser;
68
69 $wgOut->addWikiText( wfMsg( 'revdelete-selected', $this->page->getPrefixedText() ) );
70
71 $wgOut->addHtml( "<ul>" );
72 foreach( $this->revisions as $revid ) {
73 $rev = Revision::newFromTitle( $this->page, $revid );
74 if( !isset( $rev ) ) {
75 $wgOut->showErrorPage( 'revdelete-nooldid-title', 'revdelete-nooldid-text' );
76 return;
77 }
78 $wgOut->addHtml( $this->historyLine( $rev ) );
79 $bitfields[] = $rev->mDeleted; // FIXME
80 }
81 $wgOut->addHtml( "</ul>" );
82
83 $wgOut->addWikiText( wfMsg( 'revdelete-text' ) );
84
85 $items = array(
86 wfInputLabel( wfMsg( 'revdelete-log' ), 'wpReason', 'wpReason', 60 ),
87 wfSubmitButton( wfMsg( 'revdelete-submit' ) ) );
88 $hidden = array(
89 wfHidden( 'wpEditToken', $wgUser->editToken() ),
90 wfHidden( 'target', $this->page->getPrefixedText() ) );
91 foreach( $this->revisions as $revid ) {
92 $hidden[] = wfHidden( 'oldid[]', $revid );
93 }
94
95 $special = SpecialPage::getTitleFor( 'Revisiondelete' );
96 $wgOut->addHtml( wfElement( 'form', array(
97 'method' => 'post',
98 'action' => $special->getLocalUrl( 'action=submit' ) ),
99 null ) );
100
101 $wgOut->addHtml( '<fieldset><legend>' . wfMsgHtml( 'revdelete-legend' ) . '</legend>' );
102 foreach( $this->checks as $item ) {
103 list( $message, $name, $field ) = $item;
104 $wgOut->addHtml( '<div>' .
105 wfCheckLabel( wfMsg( $message), $name, $name, $rev->isDeleted( $field ) ) .
106 '</div>' );
107 }
108 $wgOut->addHtml( '</fieldset>' );
109 foreach( $items as $item ) {
110 $wgOut->addHtml( '<p>' . $item . '</p>' );
111 }
112 foreach( $hidden as $item ) {
113 $wgOut->addHtml( $item );
114 }
115
116 $wgOut->addHtml( '</form>' );
117 }
118
119 /**
120 * @param Revision $rev
121 * @returns string
122 */
123 function historyLine( $rev ) {
124 global $wgContLang;
125 $date = $wgContLang->timeanddate( $rev->getTimestamp() );
126 return
127 "<li>" .
128 $this->skin->makeLinkObj( $this->page, $date, 'oldid=' . $rev->getId() ) .
129 " " .
130 $this->skin->revUserLink( $rev ) .
131 " " .
132 $this->skin->revComment( $rev ) .
133 "</li>";
134 }
135
136 /**
137 * @param WebRequest $request
138 */
139 function submit( $request ) {
140 $bitfield = $this->extractBitfield( $request );
141 $comment = $request->getText( 'wpReason' );
142 if( $this->save( $bitfield, $comment ) ) {
143 return $this->success( $request );
144 } else {
145 return $this->show( $request );
146 }
147 }
148
149 function success( $request ) {
150 global $wgOut;
151 $wgOut->addWikiText( 'woo' );
152 }
153
154 /**
155 * Put together a rev_deleted bitfield from the submitted checkboxes
156 * @param WebRequest $request
157 * @return int
158 */
159 function extractBitfield( $request ) {
160 $bitfield = 0;
161 foreach( $this->checks as $item ) {
162 list( /* message */ , $name, $field ) = $item;
163 if( $request->getCheck( $name ) ) {
164 $bitfield |= $field;
165 }
166 }
167 return $bitfield;
168 }
169
170 function save( $bitfield, $reason ) {
171 $dbw = wfGetDB( DB_MASTER );
172 $deleter = new RevisionDeleter( $dbw );
173 $deleter->setVisibility( $this->revisions, $bitfield, $reason );
174 }
175 }
176
177 /**
178 * Implements the actions for Revision Deletion.
179 * @addtogroup SpecialPage
180 */
181 class RevisionDeleter {
182 function __construct( $db ) {
183 $this->db = $db;
184 }
185
186 /**
187 * @param array $items list of revision ID numbers
188 * @param int $bitfield new rev_deleted value
189 * @param string $comment Comment for log records
190 */
191 function setVisibility( $items, $bitfield, $comment ) {
192 $pages = array();
193
194 // To work!
195 foreach( $items as $revid ) {
196 $rev = Revision::newFromId( $revid );
197 if( !isset( $rev ) ) {
198 return false;
199 }
200 $this->updateRevision( $rev, $bitfield );
201 $this->updateRecentChanges( $rev, $bitfield );
202
203 // For logging, maintain a count of revisions per page
204 $pageid = $rev->getPage();
205 if( isset( $pages[$pageid] ) ) {
206 $pages[$pageid]++;
207 } else {
208 $pages[$pageid] = 1;
209 }
210 }
211
212 // Clear caches...
213 foreach( $pages as $pageid => $count ) {
214 $title = Title::newFromId( $pageid );
215 $this->updatePage( $title );
216 $this->updateLog( $title, $count, $bitfield, $comment );
217 }
218
219 return true;
220 }
221
222 /**
223 * Update the revision's rev_deleted field
224 * @param Revision $rev
225 * @param int $bitfield new rev_deleted bitfield value
226 */
227 function updateRevision( $rev, $bitfield ) {
228 $this->db->update( 'revision',
229 array( 'rev_deleted' => $bitfield ),
230 array( 'rev_id' => $rev->getId() ),
231 'RevisionDeleter::updateRevision' );
232 }
233
234 /**
235 * Update the revision's recentchanges record if fields have been hidden
236 * @param Revision $rev
237 * @param int $bitfield new rev_deleted bitfield value
238 */
239 function updateRecentChanges( $rev, $bitfield ) {
240 $this->db->update( 'recentchanges',
241 array(
242 'rc_user' => ($bitfield & Revision::DELETED_USER) ? 0 : $rev->getUser(),
243 'rc_user_text' => ($bitfield & Revision::DELETED_USER) ? wfMsg( 'rev-deleted-user' ) : $rev->getUserText(),
244 'rc_comment' => ($bitfield & Revision::DELETED_COMMENT) ? wfMsg( 'rev-deleted-comment' ) : $rev->getComment() ),
245 array(
246 'rc_this_oldid' => $rev->getId() ),
247 'RevisionDeleter::updateRecentChanges' );
248 }
249
250 /**
251 * Touch the page's cache invalidation timestamp; this forces cached
252 * history views to refresh, so any newly hidden or shown fields will
253 * update properly.
254 * @param Title $title
255 */
256 function updatePage( $title ) {
257 $title->invalidateCache();
258 }
259
260 /**
261 * Record a log entry on the action
262 * @param Title $title
263 * @param int $count the number of revisions altered for this page
264 * @param int $bitfield the new rev_deleted value
265 * @param string $comment
266 */
267 function updateLog( $title, $count, $bitfield, $comment ) {
268 $log = new LogPage( 'delete' );
269 $reason = "changed $count revisions to $bitfield";
270 $reason .= ": $comment";
271 $log->addEntry( 'revision', $title, $reason );
272 }
273 }
274
275