Index: trunk/extensions/Drafts/Drafts.classes.php |
— | — | @@ -5,220 +5,47 @@ |
6 | 6 | * @file |
7 | 7 | * @ingroup Extensions |
8 | 8 | */ |
9 | | - |
10 | | -// Draft Class |
11 | | -class Draft { |
12 | | - |
13 | | - /* Fields */ |
14 | 9 | |
15 | | - private $_exists = false; |
16 | | - private $_id; |
17 | | - private $_token; |
18 | | - private $_userID; |
19 | | - private $_title; |
20 | | - private $_section; |
21 | | - private $_starttime; |
22 | | - private $_edittime; |
23 | | - private $_savetime; |
24 | | - private $_scrolltop ; |
25 | | - private $_text; |
26 | | - private $_summary; |
27 | | - private $_minoredit; |
| 10 | +abstract class Drafts { |
28 | 11 | |
29 | | - /* Functions */ |
| 12 | + /* Static Functions */ |
30 | 13 | |
31 | | - public function __construct( $id = null, $autoload = true ) { |
32 | | - // If an ID is a number the existence is actually checked on load |
33 | | - // If an ID is false the existance is always false durring load |
34 | | - $this->_id = $id; |
35 | | - |
36 | | - # Load automatically |
37 | | - if ( $autoload ) { |
38 | | - $this->load(); |
39 | | - } |
40 | | - } |
41 | | - |
42 | | - private function load() { |
| 14 | + public static function num( |
| 15 | + &$title = null, |
| 16 | + $userID = null |
| 17 | + ) { |
43 | 18 | global $wgUser; |
44 | | - |
45 | | - // Verify the ID has been set |
46 | | - if ( $this->_id === null ) { |
47 | | - return; |
48 | | - } |
49 | | - |
50 | | - // Get db connection |
51 | | - $dbw = wfGetDB( DB_MASTER ); |
52 | | - |
53 | | - // Select drafts from the database matching ID - can be 0 or 1 results |
54 | | - $result = $dbw->select( 'drafts', |
55 | | - array( '*' ), |
56 | | - array( |
57 | | - 'draft_id' => (int) $this->_id, |
58 | | - 'draft_user' => (int) $wgUser->getID() |
59 | | - ), |
60 | | - __METHOD__ |
61 | | - ); |
62 | | - if ( $result === false ) { |
63 | | - return; |
64 | | - } |
65 | | - |
66 | | - // Get the row |
67 | | - $row = $dbw->fetchRow( $result ); |
68 | | - if ( !is_array( $row ) || count( $row ) == 0 ) { |
69 | | - return; |
70 | | - } |
71 | | - |
72 | | - // Synchronize data |
73 | | - $this->_token = $row['draft_token']; |
74 | | - $this->_title = Title::makeTitle( $row['draft_namespace'], $row['draft_title'] ); |
75 | | - $this->_section = $row['draft_section']; |
76 | | - $this->_starttime = $row['draft_starttime']; |
77 | | - $this->_edittime = $row['draft_edittime']; |
78 | | - $this->_savetime = $row['draft_savetime']; |
79 | | - $this->_scrolltop = $row['draft_scrolltop']; |
80 | | - $this->_text = $row['draft_text']; |
81 | | - $this->_summary = $row['draft_summary']; |
82 | | - $this->_minoredit = $row['draft_minoredit']; |
83 | | - |
84 | | - // Update state |
85 | | - $this->_exists = true; |
86 | | - |
87 | | - return; |
88 | | - } |
89 | | - |
90 | | - public function save() { |
91 | | - global $wgUser, $wgRequest; |
92 | | - |
93 | | - // Get db connection |
94 | | - $dbw = wfGetDB( DB_MASTER ); |
95 | | - $dbw->begin(); |
96 | | - |
97 | | - // Build data |
98 | | - $data = array( |
99 | | - 'draft_token' => (int) $this->getToken(), |
100 | | - 'draft_user' => (int) $wgUser->getID(), |
101 | | - 'draft_namespace' => (int) $this->_title->getNamespace(), |
102 | | - 'draft_title' => (string) $this->_title->getDBKey(), |
103 | | - 'draft_page' => (int) $this->_title->getArticleId(), |
104 | | - 'draft_section' => $this->_section == '' ? null : (int) $this->_section, |
105 | | - 'draft_starttime' => $dbw->timestamp( $this->_starttime ), |
106 | | - 'draft_edittime' => $dbw->timestamp( $this->_edittime ), |
107 | | - 'draft_savetime' => $dbw->timestamp( $this->_savetime ), |
108 | | - 'draft_scrolltop' => (int) $this->_scrolltop, |
109 | | - 'draft_text' => (string) $this->_text, |
110 | | - 'draft_summary' => (string) $this->_summary, |
111 | | - 'draft_minoredit' => (int) $this->_minoredit |
112 | | - ); |
113 | | - |
114 | | - // Save data |
115 | | - if ( $this->_exists === true ) { |
116 | | - $dbw->update( 'drafts', |
117 | | - $data, |
118 | | - array( |
119 | | - 'draft_id' => (int) $this->_id, |
120 | | - 'draft_user' => (int) $wgUser->getID() |
121 | | - ), |
122 | | - __METHOD__ |
123 | | - ); |
124 | | - } else { |
125 | | - $existingRow = $dbw->selectField( 'drafts', 'draft_token', |
126 | | - array( |
127 | | - 'draft_namespace' => $data['draft_namespace'], |
128 | | - 'draft_title' => $data['draft_title'], |
129 | | - 'draft_user' => $data['draft_user'], |
130 | | - 'draft_token' => $data['draft_token'] |
131 | | - ), |
132 | | - __METHOD__ |
133 | | - ); |
134 | | - |
135 | | - // Check if token has been used already for this article |
136 | | - if ( $existingRow === false ) { |
137 | | - $dbw->insert( 'drafts', $data, __METHOD__ ); |
138 | | - $this->_id = $dbw->insertId(); |
139 | | - // Update state |
140 | | - $this->_exists = true; |
141 | | - } |
142 | | - } |
143 | | - |
144 | | - $dbw->commit(); |
145 | | - |
146 | | - // Return success |
147 | | - return true; |
148 | | - } |
149 | | - |
150 | | - public function discard( $user = null ) { |
151 | | - global $wgUser; |
152 | | - |
153 | | - // Use $wgUser as a fallback |
154 | | - $user = $user === null ? $wgUser : $user; |
155 | | - |
156 | | - // Get db connection |
157 | | - $dbw = wfGetDB( DB_MASTER ); |
158 | | - |
159 | | - // Delete data |
160 | | - $dbw->delete( 'drafts', |
161 | | - array( |
162 | | - 'draft_id' => $this->_id, |
163 | | - // FIXME: ID is already a primary key |
164 | | - 'draft_user' => $user->getID() |
165 | | - ), |
166 | | - __METHOD__ |
167 | | - ); |
168 | | - |
169 | | - $this->_exists = false; |
170 | | - } |
171 | | - |
172 | | - public static function newFromID( $id, $autoload = true ) { |
173 | | - return new Draft( $id, $autoload ); |
174 | | - } |
175 | | - |
176 | | - public static function newFromRow( $row ) { |
177 | | - $draft = new Draft( $row['draft_id'], false ); |
178 | | - $draft->setToken( $row['draft_token'] ); |
179 | | - $draft->setTitle( Title::makeTitle( $row['draft_namespace'], $row['draft_title'] ) ); |
180 | | - $draft->setSection( $row['draft_section'] ); |
181 | | - $draft->setStartTime( $row['draft_starttime'] ); |
182 | | - $draft->setEditTime( $row['draft_edittime'] ); |
183 | | - $draft->setSaveTime( $row['draft_savetime'] ); |
184 | | - $draft->setScrollTop( $row['draft_scrolltop'] ); |
185 | | - $draft->setText( $row['draft_text'] ); |
186 | | - $draft->setSummary( $row['draft_summary'] ); |
187 | | - $draft->setMinorEdit( $row['draft_minoredit'] ); |
188 | | - return $draft; |
189 | | - } |
190 | | - |
191 | | - public static function countDrafts( &$title = null, $userID = null ) { |
192 | | - global $wgUser; |
193 | | - |
194 | | - Draft::cleanDrafts(); |
195 | | - |
196 | | - // Get db connection |
| 19 | + // Removes expired drafts for a more accurate count |
| 20 | + self::clean(); |
| 21 | + // Get database connection |
197 | 22 | $dbr = wfGetDB( DB_SLAVE ); |
198 | | - |
199 | | - // Build where clause |
| 23 | + // Builds where clause |
200 | 24 | $where = array(); |
| 25 | + // Checks if a specific title was given |
201 | 26 | if ( $title !== null ) { |
| 27 | + // Adds specific title to conditions |
202 | 28 | $where['draft_namespace'] = $title->getNamespace(); |
203 | 29 | $where['draft_title'] = $title->getDBKey(); |
204 | 30 | } |
| 31 | + // Checks if specific user was given |
205 | 32 | if ( $userID !== null ) { |
| 33 | + // Adds specific user to condition |
206 | 34 | $where['draft_user'] = $userID; |
207 | 35 | } else { |
| 36 | + // Adds current user as condition |
208 | 37 | $where['draft_user'] = $wgUser->getID(); |
209 | 38 | } |
210 | | - |
211 | 39 | // Get a list of matching drafts |
212 | 40 | return $dbr->selectField( 'drafts', 'count(*)', $where, __METHOD__ ); |
213 | 41 | } |
214 | | - |
215 | | - public static function cleanDrafts() { |
| 42 | + |
| 43 | + public static function clean() { |
216 | 44 | global $egDraftsLifeSpan; |
217 | | - |
218 | | - // Get db connection |
| 45 | + // Get database connection |
219 | 46 | $dbw = wfGetDB( DB_MASTER ); |
220 | | - |
221 | | - // Remove drafts that are more than $wgDraftsLifeSpan days old |
| 47 | + // Sets cuttoff as age longer than $wgDraftsLifeSpan days old |
222 | 48 | $cutoff = wfTimestamp( TS_UNIX ) - ( $egDraftsLifeSpan * 60 * 60 * 24 ); |
| 49 | + // Removes expired drafts from database |
223 | 50 | $dbw->delete( 'drafts', |
224 | 51 | array( |
225 | 52 | 'draft_savetime < ' . $dbw->addQuotes( $dbw->timestamp( $cutoff ) ) |
— | — | @@ -226,66 +53,78 @@ |
227 | 54 | __METHOD__ |
228 | 55 | ); |
229 | 56 | } |
230 | | - |
231 | | - public static function getDrafts( $title = null, $userID = null ) { |
| 57 | + |
| 58 | + public static function get( |
| 59 | + $title = null, |
| 60 | + $userID = null |
| 61 | + ) { |
232 | 62 | global $wgUser; |
233 | | - |
234 | | - Draft::cleanDrafts(); |
235 | | - |
236 | | - // Get db connection |
| 63 | + // Removes expired drafts for a more accurate list |
| 64 | + Drafts::clean(); |
| 65 | + // Gets database connection |
237 | 66 | $dbw = wfGetDB( DB_MASTER ); |
238 | | - |
239 | | - // Build where clause |
| 67 | + // Builds where clause |
240 | 68 | $where = array(); |
| 69 | + // Checks if specific title was given |
241 | 70 | if ( $title !== null ) { |
| 71 | + // Gets page id from title |
242 | 72 | $pageId = $title->getArticleId(); |
| 73 | + // Checks if page id exists |
243 | 74 | if ( $pageId ) { |
| 75 | + // Adds specific page id to conditions |
244 | 76 | $where['draft_page'] = $pageId; |
245 | 77 | } else { |
| 78 | + // Adds new page information to conditions |
246 | 79 | $where['draft_page'] = 0; // page not created yet |
247 | 80 | $where['draft_namespace'] = $title->getNamespace(); |
248 | 81 | $where['draft_title'] = $title->getDBKey(); |
249 | 82 | } |
250 | 83 | } |
| 84 | + // Checks if a specific user was given |
251 | 85 | if ( $userID !== null ) { |
| 86 | + // Adds specific user to conditions |
252 | 87 | $where['draft_user'] = $userID; |
253 | 88 | } else { |
| 89 | + // Adds current user to conditions |
254 | 90 | $where['draft_user'] = $wgUser->getID(); |
255 | 91 | } |
256 | | - |
257 | | - // Create an array of matching drafts |
258 | | - $drafts = array(); |
| 92 | + // Gets matching drafts from database |
259 | 93 | $result = $dbw->select( 'drafts', '*', $where, __METHOD__ ); |
260 | 94 | if ( $result ) { |
| 95 | + // Creates an array of matching drafts |
| 96 | + $drafts = array(); |
261 | 97 | while ( $row = $dbw->fetchRow( $result ) ) { |
262 | | - // Add a new draft to the list from the row |
| 98 | + // Adds a new draft to the list from the row |
263 | 99 | $drafts[] = Draft::newFromRow( $row ); |
264 | 100 | } |
265 | 101 | } |
266 | | - |
267 | | - // Return array of matching drafts |
| 102 | + // Returns array of matching drafts or null id there were none |
268 | 103 | return count( $drafts ) ? $drafts : null; |
269 | 104 | } |
270 | | - |
271 | | - public static function listDrafts( &$title = null, $user = null ) { |
| 105 | + |
| 106 | + public static function display( |
| 107 | + &$title = null, |
| 108 | + $user = null |
| 109 | + ) { |
272 | 110 | global $wgOut, $wgRequest, $wgUser, $wgLang; |
273 | | - |
274 | | - // Get draftID |
| 111 | + // Gets draftID |
275 | 112 | $currentDraft = Draft::newFromID( $wgRequest->getIntOrNull( 'draft' ) ); |
276 | | - |
277 | 113 | // Output HTML for list of drafts |
278 | | - $drafts = Draft::getDrafts( $title, $user ); |
| 114 | + $drafts = Drafts::get( $title, $user ); |
279 | 115 | if ( count( $drafts ) > 0 ) { |
280 | 116 | global $egDraftsLifeSpan; |
281 | | - |
282 | 117 | // Internationalization |
283 | 118 | wfLoadExtensionMessages( 'Drafts' ); |
284 | | - |
285 | 119 | // Add a summary, on Special:Drafts only |
286 | 120 | if( !$title || $title->getNamespace() == NS_SPECIAL ) { |
287 | | - $wgOut->wrapWikiMsg( '<div class="mw-drafts-summary">$1</div>', array( 'drafts-view-summary', $wgLang->formatNum( $egDraftsLifeSpan ) ) ); |
| 121 | + $wgOut->wrapWikiMsg( |
| 122 | + '<div class="mw-drafts-summary">$1</div>', |
| 123 | + array( |
| 124 | + 'drafts-view-summary', |
| 125 | + $wgLang->formatNum( $egDraftsLifeSpan ) |
| 126 | + ) |
| 127 | + ); |
288 | 128 | } |
289 | | - |
290 | 129 | // Build XML |
291 | 130 | $wgOut->addHTML( |
292 | 131 | Xml::openElement( 'table', |
— | — | @@ -320,15 +159,14 @@ |
321 | 160 | ); |
322 | 161 | $wgOut->addHTML( Xml::element( 'th' ) ); |
323 | 162 | $wgOut->addHTML( Xml::closeElement( 'tr' ) ); |
324 | | - |
325 | 163 | // Add existing drafts for this page and user |
326 | 164 | foreach ( $drafts as $draft ) { |
327 | 165 | // Get article title text |
328 | 166 | $htmlTitle = $draft->getTitle()->getEscapedText(); |
329 | | - |
330 | 167 | // Build Article Load link |
331 | | - $urlLoad = $draft->getTitle()->getFullUrl( 'action=edit&draft=' . urlencode( $draft->getID() ) ); |
332 | | - |
| 168 | + $urlLoad = $draft->getTitle()->getFullUrl( 'action=edit&draft=' . |
| 169 | + urlencode( $draft->getID() ) |
| 170 | + ); |
333 | 171 | // Build discard link |
334 | 172 | $urlDiscard = sprintf( '%s?discard=%s&token=%s', |
335 | 173 | SpecialPage::getTitleFor( 'Drafts' )->getFullUrl(), |
— | — | @@ -336,27 +174,28 @@ |
337 | 175 | urlencode( $wgUser->editToken() ) |
338 | 176 | ); |
339 | 177 | // If in edit mode, return to editor |
340 | | - if ( $wgRequest->getText( 'action' ) == 'edit' || $wgRequest->getText( 'action' ) == 'submit' ) { |
| 178 | + if ( |
| 179 | + $wgRequest->getText( 'action' ) == 'edit' || |
| 180 | + $wgRequest->getText( 'action' ) == 'submit' |
| 181 | + ) { |
341 | 182 | $urlDiscard .= '&returnto=' . urlencode( 'edit' ); |
342 | 183 | } |
343 | | - |
344 | 184 | // Append section to titles and links |
345 | 185 | if ( $draft->getSection() !== null ) { |
346 | 186 | // Detect section name |
347 | 187 | $lines = explode( "\n", $draft->getText() ); |
348 | | - |
| 188 | + |
349 | 189 | // If there is any content in the section |
350 | 190 | if ( count( $lines ) > 0 ) { |
351 | 191 | $htmlTitle .= '#' . htmlspecialchars( |
352 | 192 | trim( trim( substr( $lines[0], 0, 255 ), '=' ) ) |
353 | 193 | ); |
354 | 194 | } |
355 | | - |
356 | 195 | // Modify article link and title |
357 | 196 | $urlLoad .= '§ion=' . urlencode( $draft->getSection() ); |
358 | | - $urlDiscard .= '§ion=' . urlencode( $draft->getSection() ); |
| 197 | + $urlDiscard .= '§ion=' . |
| 198 | + urlencode( $draft->getSection() ); |
359 | 199 | } |
360 | | - |
361 | 200 | // Build XML |
362 | 201 | $wgOut->addHTML( Xml::openElement( 'tr' ) ); |
363 | 202 | $wgOut->addHTML( |
— | — | @@ -371,7 +210,11 @@ |
372 | 211 | Xml::element( 'a', |
373 | 212 | array( |
374 | 213 | 'href' => $urlLoad, |
375 | | - 'style' => 'font-weight:' . ( $currentDraft->getID() == $draft->getID() ? 'bold' : 'normal' ) |
| 214 | + 'style' => 'font-weight:' . |
| 215 | + ( |
| 216 | + $currentDraft->getID() == $draft->getID() ? |
| 217 | + 'bold' : 'normal' |
| 218 | + ) |
376 | 219 | ), |
377 | 220 | $htmlTitle |
378 | 221 | ) |
— | — | @@ -394,11 +237,15 @@ |
395 | 238 | ) |
396 | 239 | ) |
397 | 240 | ); |
| 241 | + $jsClick = "if( wgDraft.getState() !== 'unchanged' )" . |
| 242 | + "return confirm('" . |
| 243 | + Xml::escapeJsString( wfMsgHTML( 'drafts-view-warn' ) ) . |
| 244 | + "')"; |
398 | 245 | $wgOut->addHTML( |
399 | 246 | Xml::element( 'a', |
400 | 247 | array( |
401 | 248 | 'href' => $urlDiscard, |
402 | | - 'onclick' => "if( !wgAjaxSaveDraft.insync ) return confirm('" . Xml::escapeJsString( wfMsgHTML( 'drafts-view-warn' ) ) . "')" |
| 249 | + 'onclick' => $jsClick |
403 | 250 | ), |
404 | 251 | wfMsg( 'drafts-view-discard' ) |
405 | 252 | ) |
— | — | @@ -407,102 +254,310 @@ |
408 | 255 | $wgOut->addHTML( Xml::closeElement( 'tr' ) ); |
409 | 256 | } |
410 | 257 | $wgOut->addHTML( Xml::closeElement( 'table' ) ); |
411 | | - |
412 | 258 | // Return number of drafts |
413 | 259 | return count( $drafts ); |
414 | 260 | } |
415 | 261 | return 0; |
416 | 262 | } |
417 | | - |
| 263 | +} |
| 264 | + |
| 265 | +class Draft { |
| 266 | + |
| 267 | + /* Members */ |
| 268 | + |
| 269 | + private $exists = false; |
| 270 | + private $id; |
| 271 | + private $token; |
| 272 | + private $userID; |
| 273 | + private $title; |
| 274 | + private $section; |
| 275 | + private $starttime; |
| 276 | + private $edittime; |
| 277 | + private $savetime; |
| 278 | + private $scrolltop ; |
| 279 | + private $text; |
| 280 | + private $summary; |
| 281 | + private $minoredit; |
| 282 | + |
| 283 | + /* Static Functions */ |
| 284 | + |
| 285 | + public static function newFromID( |
| 286 | + $id, |
| 287 | + $autoload = true |
| 288 | + ) { |
| 289 | + return new Draft( $id, $autoload ); |
| 290 | + } |
| 291 | + |
| 292 | + public static function newFromRow( |
| 293 | + $row |
| 294 | + ) { |
| 295 | + $draft = new Draft( $row['draft_id'], false ); |
| 296 | + $draft->setToken( $row['draft_token'] ); |
| 297 | + $draft->setTitle( Title::makeTitle( $row['draft_namespace'], $row['draft_title'] ) ); |
| 298 | + $draft->setSection( $row['draft_section'] ); |
| 299 | + $draft->setStartTime( $row['draft_starttime'] ); |
| 300 | + $draft->setEditTime( $row['draft_edittime'] ); |
| 301 | + $draft->setSaveTime( $row['draft_savetime'] ); |
| 302 | + $draft->setScrollTop( $row['draft_scrolltop'] ); |
| 303 | + $draft->setText( $row['draft_text'] ); |
| 304 | + $draft->setSummary( $row['draft_summary'] ); |
| 305 | + $draft->setMinorEdit( $row['draft_minoredit'] ); |
| 306 | + return $draft; |
| 307 | + } |
| 308 | + |
418 | 309 | public static function newToken() { |
419 | 310 | return wfGenerateToken(); |
420 | 311 | } |
421 | | - |
422 | | - /* States */ |
| 312 | + |
| 313 | + /* Properties */ |
| 314 | + |
423 | 315 | public function exists() { |
424 | | - return $this->_exists; |
| 316 | + return $this->exists; |
425 | 317 | } |
426 | 318 | |
427 | | - /* Properties */ |
428 | | - |
429 | 319 | public function getID() { |
430 | | - return $this->_id; |
| 320 | + return $this->id; |
431 | 321 | } |
432 | 322 | |
433 | | - public function setToken( $token ) { |
434 | | - $this->_token = $token; |
| 323 | + public function setToken( |
| 324 | + $token |
| 325 | + ) { |
| 326 | + $this->token = $token; |
435 | 327 | } |
436 | 328 | public function getToken() { |
437 | | - return $this->_token; |
| 329 | + return $this->token; |
438 | 330 | } |
439 | 331 | |
440 | | - public function getUserID( $userID ) { |
441 | | - $this->_userID = $userID; |
| 332 | + public function getUserID( |
| 333 | + $userID |
| 334 | + ) { |
| 335 | + $this->userID = $userID; |
442 | 336 | } |
443 | 337 | public function setUserID() { |
444 | | - return $this->_userID; |
| 338 | + return $this->userID; |
445 | 339 | } |
446 | 340 | |
447 | 341 | public function getTitle() { |
448 | | - return $this->_title; |
| 342 | + return $this->title; |
449 | 343 | } |
450 | | - public function setTitle( $title ) { |
451 | | - $this->_title = $title; |
| 344 | + public function setTitle( |
| 345 | + $title |
| 346 | + ) { |
| 347 | + $this->title = $title; |
452 | 348 | } |
453 | 349 | |
454 | 350 | public function getSection() { |
455 | | - return $this->_section; |
| 351 | + return $this->section; |
456 | 352 | } |
457 | | - public function setSection( $section ) { |
458 | | - $this->_section = $section; |
| 353 | + public function setSection( |
| 354 | + $section |
| 355 | + ) { |
| 356 | + $this->section = $section; |
459 | 357 | } |
460 | 358 | |
461 | 359 | public function getStartTime() { |
462 | | - return $this->_starttime; |
| 360 | + return $this->starttime; |
463 | 361 | } |
464 | | - public function setStartTime( $starttime ) { |
465 | | - $this->_starttime = $starttime; |
| 362 | + public function setStartTime( |
| 363 | + $starttime |
| 364 | + ) { |
| 365 | + $this->starttime = $starttime; |
466 | 366 | } |
467 | 367 | |
468 | 368 | public function getEditTime() { |
469 | | - return $this->_edittime; |
| 369 | + return $this->edittime; |
470 | 370 | } |
471 | | - public function setEditTime( $edittime ) { |
472 | | - $this->_edittime = $edittime; |
| 371 | + public function setEditTime( |
| 372 | + $edittime |
| 373 | + ) { |
| 374 | + $this->edittime = $edittime; |
473 | 375 | } |
474 | 376 | |
475 | 377 | public function getSaveTime() { |
476 | | - return $this->_savetime; |
| 378 | + return $this->savetime; |
477 | 379 | } |
478 | | - public function setSaveTime( $savetime ) { |
479 | | - $this->_savetime = $savetime; |
| 380 | + public function setSaveTime( |
| 381 | + $savetime |
| 382 | + ) { |
| 383 | + $this->savetime = $savetime; |
480 | 384 | } |
481 | 385 | |
482 | 386 | public function getScrollTop() { |
483 | | - return $this->_scrolltop; |
| 387 | + return $this->scrolltop; |
484 | 388 | } |
485 | | - public function setScrollTop( $scrolltop ) { |
486 | | - $this->_scrolltop = $scrolltop; |
| 389 | + public function setScrollTop( |
| 390 | + $scrolltop |
| 391 | + ) { |
| 392 | + $this->scrolltop = $scrolltop; |
487 | 393 | } |
488 | 394 | |
489 | 395 | public function getText() { |
490 | | - return $this->_text; |
| 396 | + return $this->text; |
491 | 397 | } |
492 | | - public function setText( $text ) { |
493 | | - $this->_text = $text; |
| 398 | + public function setText( |
| 399 | + $text |
| 400 | + ) { |
| 401 | + $this->text = $text; |
494 | 402 | } |
495 | 403 | |
496 | 404 | public function getSummary() { |
497 | | - return $this->_summary; |
| 405 | + return $this->summary; |
498 | 406 | } |
499 | | - public function setSummary( $summary ) { |
500 | | - $this->_summary = $summary; |
| 407 | + public function setSummary( |
| 408 | + $summary |
| 409 | + ) { |
| 410 | + $this->summary = $summary; |
501 | 411 | } |
502 | 412 | |
503 | 413 | public function getMinorEdit() { |
504 | | - return $this->_minoredit; |
| 414 | + return $this->minoredit; |
505 | 415 | } |
506 | | - public function setMinorEdit( $minoredit ) { |
507 | | - $this->_minoredit = $minoredit; |
| 416 | + public function setMinorEdit( |
| 417 | + $minoredit |
| 418 | + ) { |
| 419 | + $this->minoredit = $minoredit; |
508 | 420 | } |
| 421 | + |
| 422 | + /* Functions */ |
| 423 | + |
| 424 | + public function __construct( |
| 425 | + $id = null, |
| 426 | + $autoload = true |
| 427 | + ) { |
| 428 | + // If an ID is a number the existence is actually checked on load |
| 429 | + // If an ID is false the existance is always false durring load |
| 430 | + $this->id = $id; |
| 431 | + // Load automatically |
| 432 | + if ( $autoload ) { |
| 433 | + $this->load(); |
| 434 | + } |
| 435 | + } |
| 436 | + |
| 437 | + private function load() { |
| 438 | + global $wgUser; |
| 439 | + // Checks if the ID of the draft was set |
| 440 | + if ( $this->id === null ) { |
| 441 | + // Exists immediately |
| 442 | + return; |
| 443 | + } |
| 444 | + // Gets database connection |
| 445 | + $dbw = wfGetDB( DB_MASTER ); |
| 446 | + // Gets drafts for this article and user from database |
| 447 | + $result = $dbw->select( 'drafts', |
| 448 | + array( '*' ), |
| 449 | + array( |
| 450 | + 'draft_id' => (int) $this->id, |
| 451 | + 'draft_user' => (int) $wgUser->getID() |
| 452 | + ), |
| 453 | + __METHOD__ |
| 454 | + ); |
| 455 | + // Checks if query returned any results |
| 456 | + if ( $result === false ) { |
| 457 | + // Exists immediately |
| 458 | + return; |
| 459 | + } |
| 460 | + // Fetches the row of the draft from the result |
| 461 | + $row = $dbw->fetchRow( $result ); |
| 462 | + // Checks if the row is not an array or is an empty array |
| 463 | + if ( !is_array( $row ) || count( $row ) == 0 ) { |
| 464 | + // Exists immediately |
| 465 | + return; |
| 466 | + } |
| 467 | + // Synchronizes data |
| 468 | + $this->token = $row['draft_token']; |
| 469 | + $this->title = Title::makeTitle( |
| 470 | + $row['draft_namespace'], $row['draft_title'] |
| 471 | + ); |
| 472 | + $this->section = $row['draft_section']; |
| 473 | + $this->starttime = $row['draft_starttime']; |
| 474 | + $this->edittime = $row['draft_edittime']; |
| 475 | + $this->savetime = $row['draft_savetime']; |
| 476 | + $this->scrolltop = $row['draft_scrolltop']; |
| 477 | + $this->text = $row['draft_text']; |
| 478 | + $this->summary = $row['draft_summary']; |
| 479 | + $this->minoredit = $row['draft_minoredit']; |
| 480 | + // Updates state |
| 481 | + $this->exists = true; |
| 482 | + } |
| 483 | + |
| 484 | + public function save() { |
| 485 | + global $wgUser, $wgRequest; |
| 486 | + // Gets database connection |
| 487 | + $dbw = wfGetDB( DB_MASTER ); |
| 488 | + $dbw->begin(); |
| 489 | + // Builds insert/update information |
| 490 | + $data = array( |
| 491 | + 'draft_token' => (int) $this->getToken(), |
| 492 | + 'draft_user' => (int) $wgUser->getID(), |
| 493 | + 'draft_namespace' => (int) $this->title->getNamespace(), |
| 494 | + 'draft_title' => (string) $this->title->getDBKey(), |
| 495 | + 'draft_page' => (int) $this->title->getArticleId(), |
| 496 | + 'draft_section' => |
| 497 | + $this->section == '' ? null : (int) $this->section, |
| 498 | + 'draft_starttime' => $dbw->timestamp( $this->starttime ), |
| 499 | + 'draft_edittime' => $dbw->timestamp( $this->edittime ), |
| 500 | + 'draft_savetime' => $dbw->timestamp( $this->savetime ), |
| 501 | + 'draft_scrolltop' => (int) $this->scrolltop, |
| 502 | + 'draft_text' => (string) $this->text, |
| 503 | + 'draft_summary' => (string) $this->summary, |
| 504 | + 'draft_minoredit' => (int) $this->minoredit |
| 505 | + ); |
| 506 | + // Checks if draft already exists |
| 507 | + if ( $this->exists === true ) { |
| 508 | + // Updates draft information |
| 509 | + $dbw->update( 'drafts', |
| 510 | + $data, |
| 511 | + array( |
| 512 | + 'draft_id' => (int) $this->id, |
| 513 | + 'draft_user' => (int) $wgUser->getID() |
| 514 | + ), |
| 515 | + __METHOD__ |
| 516 | + ); |
| 517 | + } else { |
| 518 | + // Gets a draft token exists for the current user and article |
| 519 | + $existingRow = $dbw->selectField( 'drafts', 'draft_token', |
| 520 | + array( |
| 521 | + 'draft_namespace' => $data['draft_namespace'], |
| 522 | + 'draft_title' => $data['draft_title'], |
| 523 | + 'draft_user' => $data['draft_user'], |
| 524 | + 'draft_token' => $data['draft_token'] |
| 525 | + ), |
| 526 | + __METHOD__ |
| 527 | + ); |
| 528 | + // Checks if token existed, meaning it has been used already for |
| 529 | + // this article |
| 530 | + if ( $existingRow === false ) { |
| 531 | + // Inserts row in the database |
| 532 | + $dbw->insert( 'drafts', $data, __METHOD__ ); |
| 533 | + // Gets the id of the newly inserted row |
| 534 | + $this->id = $dbw->insertId(); |
| 535 | + // Updates state |
| 536 | + $this->exists = true; |
| 537 | + } |
| 538 | + } |
| 539 | + // Commits any processed changes |
| 540 | + $dbw->commit(); |
| 541 | + // Returns success |
| 542 | + return true; |
| 543 | + } |
| 544 | + |
| 545 | + public function discard( |
| 546 | + $user = null |
| 547 | + ) { |
| 548 | + global $wgUser; |
| 549 | + // Uses $wgUser as a fallback |
| 550 | + $user = $user === null ? $wgUser : $user; |
| 551 | + // Gets database connection |
| 552 | + $dbw = wfGetDB( DB_MASTER ); |
| 553 | + // Deletes draft from database (verifying propper user to avoid hacking!) |
| 554 | + $dbw->delete( 'drafts', |
| 555 | + array( |
| 556 | + 'draft_id' => $this->id, |
| 557 | + 'draft_user' => $user->getID() |
| 558 | + ), |
| 559 | + __METHOD__ |
| 560 | + ); |
| 561 | + // Updates state |
| 562 | + $this->exists = false; |
| 563 | + } |
509 | 564 | } |
Index: trunk/extensions/Drafts/Drafts.i18n.php |
— | — | @@ -29,6 +29,7 @@ |
30 | 30 | 'drafts-save' => 'Save this as a draft', |
31 | 31 | 'drafts-save-save' => 'Save draft', |
32 | 32 | 'drafts-save-saved' => 'Saved', |
| 33 | + 'drafts-save-saving' => 'Saving', |
33 | 34 | 'drafts-save-error' => 'Error saving draft', |
34 | 35 | 'tooltip-drafts-save' => 'Save as a draft', |
35 | 36 | 'accesskey-drafts-save' => 'g', # do not translate or duplicate this message to other languages |
— | — | @@ -845,7 +846,7 @@ |
846 | 847 | 'drafts-view-nonesaved' => 'Nie masz obecnie zapisanych żadnych wersji roboczych.', |
847 | 848 | 'drafts-view-notice' => 'Posiadasz $1 dla tej strony.', |
848 | 849 | 'drafts-view-notice-link' => '$1 {{PLURAL:$1|wersja robocza|wersje robocze|wersji roboczych}}', |
849 | | - 'drafts-view-warn' => 'Opuszczenie tej strony spowoduje utratę wszystkich niezapisanych zmian w jej treści. |
| 850 | + 'drafts-view-warn' => 'Opuszczenie tej strony spowoduje utratę wszystkich niezapisanych zmian w jej treści. |
850 | 851 | Czy chcesz kontynuować?', |
851 | 852 | 'drafts-save' => 'Zapisz jako wersję roboczą', |
852 | 853 | 'drafts-save-save' => 'Zapisz wersję roboczą', |
Index: trunk/extensions/Drafts/Drafts.pages.php |
— | — | @@ -14,49 +14,46 @@ |
15 | 15 | public function __construct() { |
16 | 16 | // Initialize special page |
17 | 17 | SpecialPage::SpecialPage( 'Drafts' ); |
18 | | - |
19 | 18 | // Internationalization |
20 | 19 | wfLoadExtensionMessages( 'Drafts' ); |
21 | 20 | } |
22 | 21 | |
23 | 22 | public function execute( $par ) { |
24 | 23 | global $wgRequest, $wgOut, $wgUser; |
25 | | - |
26 | 24 | // Begin output |
27 | 25 | $this->setHeaders(); |
28 | | - |
29 | 26 | // Make sure the user is logged in |
30 | 27 | if ( !$wgUser->isLoggedIn() ) { |
31 | 28 | // If not, let them know they need to |
32 | 29 | $wgOut->loginToUse(); |
33 | | - |
34 | 30 | // Continue |
35 | 31 | return true; |
36 | 32 | } |
37 | | - |
38 | 33 | // Handle discarding |
39 | 34 | $draft = Draft::newFromID( $wgRequest->getIntOrNull( 'discard' ) ); |
40 | 35 | if ( $draft->exists() ) { |
41 | 36 | // Discard draft |
42 | 37 | $draft->discard(); |
43 | | - |
44 | 38 | // Redirect to the article editor or view if returnto was set |
45 | 39 | $section = $wgRequest->getIntOrNull( 'section' ); |
46 | 40 | $urlSection = $section !== null ? "§ion={$section}" : ''; |
47 | 41 | switch( $wgRequest->getText( 'returnto' ) ) { |
48 | 42 | case 'edit': |
49 | 43 | $title = Title::newFromDBKey( $draft->getTitle() ); |
50 | | - $wgOut->redirect( wfExpandURL( $title->getEditURL() . $urlSection ) ); |
| 44 | + $wgOut->redirect( |
| 45 | + wfExpandURL( $title->getEditURL() . $urlSection ) |
| 46 | + ); |
51 | 47 | break; |
52 | 48 | case 'view': |
53 | 49 | $title = Title::newFromDBKey( $draft->getTitle() ); |
54 | | - $wgOut->redirect( wfExpandURL( $title->getFullURL() . $urlSection ) ); |
| 50 | + $wgOut->redirect( |
| 51 | + wfExpandURL( $title->getFullURL() . $urlSection ) |
| 52 | + ); |
55 | 53 | break; |
56 | 54 | } |
57 | 55 | } |
58 | | - |
59 | 56 | // Show list of drafts, or a message that there are none |
60 | | - if ( Draft::listDrafts() == 0 ) { |
| 57 | + if ( Drafts::display() == 0 ) { |
61 | 58 | $wgOut->addHTML( wfMsgHTML( 'drafts-view-nonesaved' ) ); |
62 | 59 | } |
63 | 60 | } |
Index: trunk/extensions/Drafts/Drafts.php |
— | — | @@ -4,7 +4,7 @@ |
5 | 5 | * |
6 | 6 | * @file |
7 | 7 | * @ingroup Extensions |
8 | | - * |
| 8 | + * |
9 | 9 | * This file contains the main include file for the Drafts extension of |
10 | 10 | * MediaWiki. |
11 | 11 | * |
— | — | @@ -45,10 +45,15 @@ |
46 | 46 | // Use the value 0 to disable autosave |
47 | 47 | $egDraftsAutoSaveWait = 120; |
48 | 48 | |
| 49 | +// Seconds to wait until giving up on a response from the server |
| 50 | +// Use the value 0 to disable autosave |
| 51 | +$egDraftsAutoSaveTimeout = 10; |
| 52 | + |
49 | 53 | // Days to keep drafts around before automatic deletion |
50 | 54 | $egDraftsLifeSpan = 30; |
51 | 55 | |
52 | 56 | // Save and View components |
| 57 | +$wgAutoloadClasses['Drafts'] = $dir . 'Drafts.classes.php'; |
53 | 58 | $wgAutoloadClasses['Draft'] = $dir . 'Drafts.classes.php'; |
54 | 59 | $wgAutoloadClasses['DraftHooks'] = $dir . 'Drafts.hooks.php'; |
55 | 60 | |
— | — | @@ -74,7 +79,7 @@ |
75 | 80 | $wgHooks['EditPage::showEditForm:initial'][] = 'DraftHooks::loadForm'; |
76 | 81 | |
77 | 82 | // Register ajax response hook |
78 | | -$wgAjaxExportList[] = 'DraftHooks::AjaxSave'; |
| 83 | +$wgAjaxExportList[] = 'DraftHooks::save'; |
79 | 84 | |
80 | 85 | // Register ajax add script hook |
81 | 86 | $wgHooks['AjaxAddScript'][] = 'DraftHooks::addJS'; |
Index: trunk/extensions/Drafts/Drafts.hooks.php |
— | — | @@ -5,13 +5,23 @@ |
6 | 6 | * @file |
7 | 7 | * @ingroup Extensions |
8 | 8 | */ |
9 | | - |
| 9 | + |
10 | 10 | // Drafts hooks |
11 | 11 | class DraftHooks { |
12 | 12 | /** |
13 | 13 | * ArticleSaveComplete hook |
14 | 14 | */ |
15 | | - public static function discard( &$article, &$user, &$text, &$summary, &$m, &$watchthis, &$section, &$flags, $rev ) { |
| 15 | + public static function discard( |
| 16 | + &$article, |
| 17 | + &$user, |
| 18 | + &$text, |
| 19 | + &$summary, |
| 20 | + &$m, |
| 21 | + &$watchthis, |
| 22 | + &$section, |
| 23 | + &$flags, |
| 24 | + $rev |
| 25 | + ) { |
16 | 26 | global $wgRequest; |
17 | 27 | // Check if the save occured from a draft |
18 | 28 | $draft = Draft::newFromID( $wgRequest->getIntOrNull( 'wpDraftID' ) ); |
— | — | @@ -22,19 +32,19 @@ |
23 | 33 | // Continue |
24 | 34 | return true; |
25 | 35 | } |
26 | | - |
| 36 | + |
27 | 37 | /** |
28 | 38 | * EditPage::showEditForm:initial hook |
29 | 39 | * Load draft... |
30 | 40 | */ |
31 | | - public static function loadForm( &$editpage ) { |
| 41 | + public static function loadForm( |
| 42 | + &$editpage |
| 43 | + ) { |
32 | 44 | global $wgUser, $wgRequest, $wgOut, $wgTitle, $wgLang; |
33 | | - |
34 | 45 | // Check permissions |
35 | 46 | if ( $wgUser->isAllowed( 'edit' ) && $wgUser->isLoggedIn() ) { |
36 | 47 | // Get draft |
37 | 48 | $draft = Draft::newFromID( $wgRequest->getIntOrNull( 'draft' ) ); |
38 | | - |
39 | 49 | // Load form values |
40 | 50 | if ( $draft->exists() ) { |
41 | 51 | // Override initial values in the form with draft data |
— | — | @@ -43,18 +53,21 @@ |
44 | 54 | $editpage->scrolltop = $draft->getScrollTop(); |
45 | 55 | $editpage->minoredit = $draft->getMinorEdit() ? true : false; |
46 | 56 | } |
47 | | - |
| 57 | + |
48 | 58 | // Save draft on non-save submission |
49 | 59 | if ( $wgRequest->getVal( 'action' ) == 'submit' && |
50 | 60 | $wgUser->editToken() == $wgRequest->getText( 'wpEditToken' ) ) |
51 | 61 | { |
52 | 62 | // If the draft wasn't specified in the url, try using a form-submitted one |
53 | 63 | if ( !$draft->exists() ) { |
54 | | - $draft = Draft::newFromID( $wgRequest->getIntOrNull( 'wpDraftID' ) ); |
| 64 | + $draft = Draft::newFromID( |
| 65 | + $wgRequest->getIntOrNull( 'wpDraftID' ) |
| 66 | + ); |
55 | 67 | } |
56 | | - |
57 | 68 | // Load draft with info |
58 | | - $draft->setTitle( Title::newFromText( $wgRequest->getText( 'wpDraftTitle' ) ) ); |
| 69 | + $draft->setTitle( Title::newFromText( |
| 70 | + $wgRequest->getText( 'wpDraftTitle' ) ) |
| 71 | + ); |
59 | 72 | $draft->setSection( $wgRequest->getInt( 'wpSection' ) ); |
60 | 73 | $draft->setStartTime( $wgRequest->getText( 'wpStarttime' ) ); |
61 | 74 | $draft->setEditTime( $wgRequest->getText( 'wpEdittime' ) ); |
— | — | @@ -63,73 +76,83 @@ |
64 | 77 | $draft->setText( $wgRequest->getText( 'wpTextbox1' ) ); |
65 | 78 | $draft->setSummary( $wgRequest->getText( 'wpSummary' ) ); |
66 | 79 | $draft->setMinorEdit( $wgRequest->getInt( 'wpMinoredit', 0 ) ); |
67 | | - |
68 | 80 | // Save draft |
69 | 81 | $draft->save(); |
70 | | - |
71 | 82 | // Use the new draft id |
72 | 83 | $wgRequest->setVal( 'draft', $draft->getID() ); |
73 | 84 | } |
74 | 85 | } |
75 | | - |
76 | 86 | // Internationalization |
77 | 87 | wfLoadExtensionMessages( 'Drafts' ); |
78 | | - |
79 | | - $numDrafts = Draft::countDrafts( $wgTitle ); |
80 | | - |
| 88 | + $numDrafts = Drafts::num( $wgTitle ); |
81 | 89 | // Show list of drafts |
82 | 90 | if ( $numDrafts > 0 ) { |
83 | 91 | if ( $wgRequest->getText( 'action' ) !== 'submit' ) { |
84 | | - $wgOut->addHTML( Xml::openElement( 'div', array( 'id' => 'drafts-list-box' ) ) ); |
85 | | - $wgOut->addHTML( Xml::element( 'h3', null, wfMsg( 'drafts-view-existing' ) ) ); |
86 | | - Draft::ListDrafts( $wgTitle ); |
| 92 | + $wgOut->addHTML( Xml::openElement( |
| 93 | + 'div', array( 'id' => 'drafts-list-box' ) ) |
| 94 | + ); |
| 95 | + $wgOut->addHTML( Xml::element( |
| 96 | + 'h3', null, wfMsg( 'drafts-view-existing' ) ) |
| 97 | + ); |
| 98 | + Drafts::display( $wgTitle ); |
87 | 99 | $wgOut->addHTML( Xml::closeElement( 'div' ) ); |
88 | 100 | } else { |
| 101 | + $jsWarn = "if( !wgAjaxSaveDraft.insync ) return confirm('" . |
| 102 | + Xml::escapeJsString( wfMsgHTML( 'drafts-view-warn' ) ) . |
| 103 | + "')"; |
89 | 104 | $link = Xml::element( 'a', |
90 | 105 | array( |
91 | 106 | 'href' => $wgTitle->getFullURL( 'action=edit' ), |
92 | | - 'onclick' => "if( !wgAjaxSaveDraft.insync ) return confirm('" . Xml::escapeJsString( wfMsgHTML( 'drafts-view-warn' ) ) . "')" |
| 107 | + 'onclick' => $jsWarn |
93 | 108 | ), |
94 | | - wfMsgExt( 'drafts-view-notice-link', array( 'parsemag' ), $wgLang->formatNum( $numDrafts ) ) |
| 109 | + wfMsgExt( |
| 110 | + 'drafts-view-notice-link', |
| 111 | + array( 'parsemag' ), |
| 112 | + $wgLang->formatNum( $numDrafts ) |
| 113 | + ) |
95 | 114 | ); |
96 | 115 | $wgOut->addHTML( wfMsgHTML( 'drafts-view-notice', $link ) ); |
97 | 116 | } |
98 | 117 | } |
99 | | - |
100 | 118 | // Continue |
101 | 119 | return true; |
102 | 120 | } |
103 | | - |
| 121 | + |
104 | 122 | /** |
105 | 123 | * EditFilter hook |
106 | 124 | * Intercept the saving of an article to detect if the submission was from the non-javascript |
107 | 125 | * save draft button |
108 | 126 | */ |
109 | | - public static function interceptSave( $editor, $text, $section, &$error ) { |
| 127 | + public static function interceptSave( |
| 128 | + $editor, |
| 129 | + $text, |
| 130 | + $section, |
| 131 | + &$error |
| 132 | + ) { |
110 | 133 | global $wgRequest; |
111 | | - |
112 | 134 | // Don't save if the save draft button caused the submit |
113 | 135 | if ( $wgRequest->getText( 'wpDraftSave' ) !== '' ) { |
114 | 136 | // Modify the error so it's clear we want to remain in edit mode |
115 | 137 | $error = ' '; |
116 | 138 | } |
117 | | - |
118 | 139 | // Continue |
119 | 140 | return true; |
120 | 141 | } |
121 | | - |
| 142 | + |
122 | 143 | /** |
123 | 144 | * EditPageBeforeEditButtons hook |
124 | 145 | * Add draft saving controls |
125 | 146 | */ |
126 | | - public static function controls( &$editpage, &$buttons ) { |
127 | | - global $wgUser, $wgTitle, $wgRequest, $egDraftsAutoSaveWait; |
128 | | - |
| 147 | + public static function controls( |
| 148 | + &$editpage, |
| 149 | + &$buttons |
| 150 | + ) { |
| 151 | + global $wgUser, $wgTitle, $wgRequest; |
| 152 | + global $egDraftsAutoSaveWait, $egDraftsAutoSaveTimeout; |
129 | 153 | // Check permissions |
130 | 154 | if ( $wgUser->isAllowed( 'edit' ) && $wgUser->isLoggedIn() ) { |
131 | 155 | // Internationalization |
132 | 156 | wfLoadExtensionMessages( 'Drafts' ); |
133 | | - |
134 | 157 | // Build XML |
135 | 158 | $buttons['savedraft'] = Xml::openElement( 'script', |
136 | 159 | array( |
— | — | @@ -137,23 +160,22 @@ |
138 | 161 | 'language' => 'javascript' |
139 | 162 | ) |
140 | 163 | ); |
141 | | - |
142 | 164 | $buttonAttribs = array( |
143 | 165 | 'id' => 'wpDraftSave', |
144 | 166 | 'name' => 'wpDraftSave', |
145 | 167 | 'tabindex' => 8, |
146 | 168 | 'value' => wfMsg( 'drafts-save-save' ), |
147 | 169 | ); |
148 | | - |
149 | 170 | $accesskey = $wgUser->getSkin()->accesskey( 'drafts-save' ); |
150 | 171 | if ( $accesskey !== false ) { |
151 | 172 | $buttonAttribs['accesskey'] = $accesskey; |
152 | 173 | } |
153 | | - $tooltip = $wgUser->getSkin()->titleAttrib( 'drafts-save', 'withaccess' ); |
| 174 | + $tooltip = $wgUser->getSkin()->titleAttrib( |
| 175 | + 'drafts-save', 'withaccess' |
| 176 | + ); |
154 | 177 | if ( $tooltip !== false ) { |
155 | 178 | $buttonAttribs['title'] = $tooltip; |
156 | 179 | } |
157 | | - |
158 | 180 | $ajaxButton = Xml::escapeJsString( |
159 | 181 | Xml::element( 'input', |
160 | 182 | array( 'type' => 'button' ) + $buttonAttribs |
— | — | @@ -180,6 +202,13 @@ |
181 | 203 | $buttons['savedraft'] .= Xml::element( 'input', |
182 | 204 | array( |
183 | 205 | 'type' => 'hidden', |
| 206 | + 'name' => 'wpDraftAutoSaveTimeout', |
| 207 | + 'value' => $egDraftsAutoSaveTimeout |
| 208 | + ) |
| 209 | + ); |
| 210 | + $buttons['savedraft'] .= Xml::element( 'input', |
| 211 | + array( |
| 212 | + 'type' => 'hidden', |
184 | 213 | 'name' => 'wpDraftToken', |
185 | 214 | 'value' => Draft::newToken() |
186 | 215 | ) |
— | — | @@ -208,6 +237,13 @@ |
209 | 238 | $buttons['savedraft'] .= Xml::element( 'input', |
210 | 239 | array( |
211 | 240 | 'type' => 'hidden', |
| 241 | + 'name' => 'wpMsgSaving', |
| 242 | + 'value' => wfMsg( 'drafts-save-saving' ) |
| 243 | + ) |
| 244 | + ); |
| 245 | + $buttons['savedraft'] .= Xml::element( 'input', |
| 246 | + array( |
| 247 | + 'type' => 'hidden', |
212 | 248 | 'name' => 'wpMsgSaveDraft', |
213 | 249 | 'value' => wfMsg( 'drafts-save-save' ) |
214 | 250 | ) |
— | — | @@ -220,62 +256,81 @@ |
221 | 257 | ) |
222 | 258 | ); |
223 | 259 | } |
224 | | - |
225 | 260 | // Continue |
226 | 261 | return true; |
227 | 262 | } |
228 | | - |
| 263 | + |
229 | 264 | /** |
230 | 265 | * AjaxAddScript hook |
231 | 266 | * Add ajax support script |
232 | 267 | */ |
233 | | - public static function addJS( $out ) { |
| 268 | + public static function addJS( |
| 269 | + $out |
| 270 | + ) { |
234 | 271 | global $wgScriptPath, $wgJsMimeType, $wgDraftsStyleVersion; |
235 | | - |
236 | 272 | // FIXME: assumes standard dir structure |
237 | 273 | // Add javascript to support ajax draft saving |
238 | | - $out->addInlineScript( "var wgDraftsStyleVersion = \"$wgDraftsStyleVersion\";\n" ); |
239 | | - $out->addScript( "<script type=\"$wgJsMimeType\" src=\"$wgScriptPath/extensions/Drafts/Drafts.js?$wgDraftsStyleVersion\"></script>\n" ); |
240 | | - |
| 274 | + $out->addInlineScript( |
| 275 | + "var wgDraftsStyleVersion = \"$wgDraftsStyleVersion\";\n" |
| 276 | + ); |
| 277 | + $out->addScript( |
| 278 | + Xml::element( |
| 279 | + 'script', |
| 280 | + array( |
| 281 | + 'type' => $wgJsMimeType, |
| 282 | + 'src' => $wgScriptPath . '/extensions/Drafts/Drafts.js?' . |
| 283 | + $wgDraftsStyleVersion |
| 284 | + ) |
| 285 | + ) |
| 286 | + ); |
241 | 287 | // Continue |
242 | 288 | return true; |
243 | 289 | } |
244 | | - |
| 290 | + |
245 | 291 | /** |
246 | 292 | * BeforePageDisplay hook |
247 | | - * Add ajax support script |
| 293 | + * Add css style sheet |
248 | 294 | */ |
249 | | - public static function addCSS( $out ) { |
| 295 | + public static function addCSS( |
| 296 | + $out |
| 297 | + ) { |
250 | 298 | global $wgScriptPath, $wgDraftsStyleVersion; |
251 | | - |
252 | 299 | // FIXME: assumes standard dir structure |
253 | 300 | // Add css for various styles |
254 | 301 | $out->addLink( |
255 | 302 | array( |
256 | 303 | 'rel' => 'stylesheet', |
257 | 304 | 'type' => 'text/css', |
258 | | - 'href' => "$wgScriptPath/extensions/Drafts/Drafts.css?$wgDraftsStyleVersion", |
| 305 | + 'href' => $wgScriptPath . '/extensions/Drafts/Drafts.css?' . |
| 306 | + $wgDraftsStyleVersion, |
259 | 307 | ) |
260 | 308 | ); |
261 | | - |
262 | 309 | // Continue |
263 | 310 | return true; |
264 | 311 | } |
265 | | - |
| 312 | + |
266 | 313 | /** |
267 | 314 | * AJAX function export DraftHooks::AjaxSave |
268 | 315 | * Respond to ajax queries |
269 | 316 | */ |
270 | | - public static function AjaxSave( $dtoken, $etoken, $id, $title, $section, $starttime, $edittime, |
271 | | - $scrolltop, $text, $summary, $minoredit ) |
272 | | - { |
| 317 | + public static function save( |
| 318 | + $dtoken, |
| 319 | + $etoken, |
| 320 | + $id, |
| 321 | + $title, |
| 322 | + $section, |
| 323 | + $starttime, |
| 324 | + $edittime, |
| 325 | + $scrolltop, |
| 326 | + $text, |
| 327 | + $summary, |
| 328 | + $minoredit |
| 329 | + ) { |
273 | 330 | global $wgUser, $wgRequest; |
274 | | - |
275 | 331 | // Verify token |
276 | 332 | if ( $wgUser->editToken() == $etoken ) { |
277 | 333 | // Create Draft |
278 | 334 | $draft = Draft::newFromID( $id ); |
279 | | - |
280 | 335 | // Load draft with info |
281 | 336 | $draft->setToken( $dtoken ); |
282 | 337 | $draft->setTitle( Title::newFromText( $title ) ); |
— | — | @@ -287,10 +342,8 @@ |
288 | 343 | $draft->setText( $text ); |
289 | 344 | $draft->setSummary( $summary ); |
290 | 345 | $draft->setMinorEdit( $minoredit ); |
291 | | - |
292 | 346 | // Save draft |
293 | 347 | $draft->save(); |
294 | | - |
295 | 348 | // Return draft id to client (used for next save) |
296 | 349 | return (string) $draft->getID(); |
297 | 350 | } else { |
Index: trunk/extensions/Drafts/Drafts.js |
— | — | @@ -1,128 +1,167 @@ |
2 | 2 | /* JavaScript for Drafts extension */ |
3 | 3 | |
4 | | -/* Classes */ |
5 | | - |
6 | | -var wgAjaxSaveDraft = {}; |
7 | | - |
8 | | -// Fields |
9 | | - |
10 | | -wgAjaxSaveDraft.inprogress = false; |
11 | | -wgAjaxSaveDraft.insync = true; |
12 | | -wgAjaxSaveDraft.autosavetimer = null; |
13 | | -wgAjaxSaveDraft.autosavewait = null; |
14 | | - |
15 | | -// Actions |
16 | | - |
17 | | -wgAjaxSaveDraft.save = function() { |
18 | | - wgAjaxSaveDraft.call( |
19 | | - document.editform.wpDraftToken.value, |
20 | | - document.editform.wpEditToken.value, |
21 | | - document.editform.wpDraftID.value, |
22 | | - document.editform.wpDraftTitle.value, |
23 | | - document.editform.wpSection.value, |
24 | | - document.editform.wpStarttime.value, |
25 | | - document.editform.wpEdittime.value, |
26 | | - document.editform.wpTextbox1.scrollTop, |
27 | | - document.editform.wpTextbox1.value, |
28 | | - document.editform.wpSummary.value, |
29 | | - document.editform.wpMinoredit.checked ? 1 : 0 |
30 | | - ); |
31 | | - |
32 | | - // Ensure timer is cleared in case we saved manually before it expired |
33 | | - clearTimeout( wgAjaxSaveDraft.autosavetimer ); |
34 | | -} |
35 | | - |
36 | | -wgAjaxSaveDraft.change = function() { |
37 | | - wgAjaxSaveDraft.insync = false; |
38 | | - wgAjaxSaveDraft.setControlsUnsaved(); |
39 | | - |
40 | | - // Clear if timer is pending |
41 | | - if( wgAjaxSaveDraft.autosavetimer ) { |
42 | | - clearTimeout( wgAjaxSaveDraft.autosavetimer ); |
| 4 | +function Draft() { |
| 5 | + |
| 6 | + /* Private Members */ |
| 7 | + |
| 8 | + // Reference to object's self |
| 9 | + var self = this; |
| 10 | + // Configuration settings |
| 11 | + var configuration = null; |
| 12 | + // Language specific messages |
| 13 | + var messages = null; |
| 14 | + // State of the draft as it pertains to asynchronous saving |
| 15 | + var state = 'unchanged'; |
| 16 | + // Timer handle for auto-saving |
| 17 | + var timer = null; |
| 18 | + // Reference to edit form draft is being edited with |
| 19 | + var form = null; |
| 20 | + |
| 21 | + /* Functions */ |
| 22 | + |
| 23 | + this.setState = function( |
| 24 | + newState |
| 25 | + ) { |
| 26 | + // Stores state information |
| 27 | + state = newState; |
| 28 | + // Updates UI elements |
| 29 | + switch ( state ) { |
| 30 | + case 'unchanged': |
| 31 | + form.wpDraftSave.disabled = true; |
| 32 | + form.wpDraftSave.value = messages.saveDraft; |
| 33 | + break; |
| 34 | + case 'changed': |
| 35 | + form.wpDraftSave.disabled = false; |
| 36 | + form.wpDraftSave.value = messages.saveDraft; |
| 37 | + break; |
| 38 | + case 'saved': |
| 39 | + form.wpDraftSave.disabled = true; |
| 40 | + form.wpDraftSave.value = messages.saved; |
| 41 | + break; |
| 42 | + case 'saving': |
| 43 | + form.wpDraftSave.disabled = true; |
| 44 | + form.wpDraftSave.value = messages.saving; |
| 45 | + break; |
| 46 | + case 'error': |
| 47 | + form.wpDraftSave.disabled = true; |
| 48 | + form.wpDraftSave.value = messages.error; |
| 49 | + break; |
| 50 | + default: break; |
| 51 | + } |
43 | 52 | } |
44 | | - // Set timer to save automatically |
45 | | - if( wgAjaxSaveDraft.autosavewait && wgAjaxSaveDraft.autosavewait > 0 ) { |
46 | | - wgAjaxSaveDraft.autosavetimer = setTimeout( |
47 | | - "wgAjaxSaveDraft.save()", |
48 | | - wgAjaxSaveDraft.autosavewait * 1000 |
| 53 | + |
| 54 | + this.getState = function() { |
| 55 | + return state; |
| 56 | + } |
| 57 | + |
| 58 | + this.save = function() { |
| 59 | + // Checks if a save is already taking place |
| 60 | + if ( state == 'saving' ) { |
| 61 | + // Exits function immediately |
| 62 | + return; |
| 63 | + } |
| 64 | + // Sets state to saving |
| 65 | + self.setState( 'saving' ); |
| 66 | + // Saves current request type |
| 67 | + var oldRequestType = sajax_request_type; |
| 68 | + // Changes request type to post |
| 69 | + sajax_request_type = "POST"; |
| 70 | + // Performs asynchronous save on server |
| 71 | + sajax_do_call( |
| 72 | + "DraftHooks::save", |
| 73 | + [ |
| 74 | + form.wpDraftToken.value, |
| 75 | + form.wpEditToken.value, |
| 76 | + form.wpDraftID.value, |
| 77 | + form.wpDraftTitle.value, |
| 78 | + form.wpSection.value, |
| 79 | + form.wpStarttime.value, |
| 80 | + form.wpEdittime.value, |
| 81 | + form.wpTextbox1.scrollTop, |
| 82 | + form.wpTextbox1.value, |
| 83 | + form.wpSummary.value, |
| 84 | + form.wpMinoredit.checked ? 1 : 0 |
| 85 | + ], |
| 86 | + new Function( "request", "wgDraft.respond( request )" ) |
49 | 87 | ); |
| 88 | + // Restores current request type |
| 89 | + sajax_request_type = oldRequestType; |
| 90 | + // Re-allow request if it is not done in 10 seconds |
| 91 | + self.timeoutID = window.setTimeout( |
| 92 | + "wgDraft.setState( 'changed' )", 10000 |
| 93 | + ); |
| 94 | + // Ensure timer is cleared in case we saved manually before it expired |
| 95 | + clearTimeout( timer ); |
50 | 96 | } |
51 | | -} |
52 | 97 | |
53 | | -wgAjaxSaveDraft.setControlsSaved = function() { |
54 | | - document.editform.wpDraftSave.disabled = true; |
55 | | - document.editform.wpDraftSave.value = document.editform.wpMsgSaved.value; |
56 | | -} |
57 | | -wgAjaxSaveDraft.setControlsUnsaved = function() { |
58 | | - document.editform.wpDraftSave.disabled = false; |
59 | | - document.editform.wpDraftSave.value = document.editform.wpMsgSaveDraft.value; |
60 | | -} |
61 | | -wgAjaxSaveDraft.setControlsError = function() { |
62 | | - document.editform.wpDraftSave.disabled = true; |
63 | | - document.editform.wpDraftSave.value = document.editform.wpMsgError.value; |
64 | | -} |
65 | | - |
66 | | -// Events |
67 | | - |
68 | | -wgAjaxSaveDraft.onLoad = function() { |
69 | | - // Check to see that the form and controls exist |
70 | | - if ( document.editform && document.editform.wpDraftSave ) { |
71 | | - // Handle saving |
72 | | - addHandler(document.editform.wpDraftSave, 'click', wgAjaxSaveDraft.save); |
73 | | - |
74 | | - // Detect changes |
75 | | - addHandler(document.editform.wpTextbox1, 'keypress', wgAjaxSaveDraft.change); |
76 | | - addHandler(document.editform.wpTextbox1, 'keyup', wgAjaxSaveDraft.change); |
77 | | - addHandler(document.editform.wpTextbox1, 'keydown', wgAjaxSaveDraft.change); |
78 | | - addHandler(document.editform.wpTextbox1, 'paste', wgAjaxSaveDraft.change); |
79 | | - addHandler(document.editform.wpTextbox1, 'cut', wgAjaxSaveDraft.change); |
80 | | - addHandler(document.editform.wpSummary, 'keypress', wgAjaxSaveDraft.change); |
81 | | - addHandler(document.editform.wpSummary, 'keyup', wgAjaxSaveDraft.change); |
82 | | - addHandler(document.editform.wpSummary, 'keydown', wgAjaxSaveDraft.change); |
83 | | - addHandler(document.editform.wpSummary, 'paste', wgAjaxSaveDraft.change); |
84 | | - addHandler(document.editform.wpSummary, 'cut', wgAjaxSaveDraft.change); |
85 | | - addHandler(document.editform.wpMinoredit, 'change', wgAjaxSaveDraft.change); |
| 98 | + this.change = function() { |
| 99 | + // Sets state to changed |
| 100 | + self.setState( 'changed' ); |
| 101 | + // Checks if timer is pending |
| 102 | + if ( timer ) { |
| 103 | + // Clears pending timer |
| 104 | + clearTimeout( timer ); |
| 105 | + } |
| 106 | + // Checks if auto-save wait time was set, and that it's greater than 0 |
| 107 | + if ( configuration.autoSaveWait && configuration.autoSaveWait > 0 ) { |
| 108 | + // Sets timer to save automatically after a period of time |
| 109 | + timer = setTimeout( |
| 110 | + "wgDraft.save()", configuration.autoSaveWait * 1000 |
| 111 | + ); |
| 112 | + } |
| 113 | + } |
86 | 114 | |
87 | | - // Use the configured autosave wait time |
88 | | - wgAjaxSaveDraft.autosavewait = document.editform.wpDraftAutoSaveWait.value; |
| 115 | + this.initialize = function() { |
| 116 | + // Cache edit form reference |
| 117 | + form = document.editform; |
| 118 | + // Check to see that the form and controls exist |
| 119 | + if ( form && form.wpDraftSave ) { |
| 120 | + // Handle manual draft saving through clicking the save draft button |
| 121 | + addHandler( form.wpDraftSave, 'click', self.save ); |
| 122 | + // Handle keeping track of state by watching for changes to fields |
| 123 | + addHandler( form.wpTextbox1, 'keypress', self.change ); |
| 124 | + addHandler( form.wpTextbox1, 'keyup', self.change ); |
| 125 | + addHandler( form.wpTextbox1, 'keydown', self.change ); |
| 126 | + addHandler( form.wpTextbox1, 'paste', self.change ); |
| 127 | + addHandler( form.wpTextbox1, 'cut', self.change ); |
| 128 | + addHandler( form.wpSummary, 'keypress', self.change ); |
| 129 | + addHandler( form.wpSummary, 'keyup', self.change ); |
| 130 | + addHandler( form.wpSummary, 'keydown', self.change ); |
| 131 | + addHandler( form.wpSummary, 'paste', self.change ); |
| 132 | + addHandler( form.wpSummary, 'cut', self.change ); |
| 133 | + addHandler( form.wpMinoredit, 'change', self.change ); |
| 134 | + // Gets configured specific values |
| 135 | + configuration = { |
| 136 | + autoSaveWait: form.wpDraftAutoSaveWait.value, |
| 137 | + autoSaveTimeout: form.wpDraftAutoSaveTimeout.value |
| 138 | + }; |
| 139 | + // Gets language-specific messages |
| 140 | + messages = { |
| 141 | + saveDraft: form.wpMsgSaveDraft.value, |
| 142 | + saving: form.wpMsgSaving.value, |
| 143 | + saved: form.wpMsgSaved.value, |
| 144 | + error: form.wpMsgError.value |
| 145 | + }; |
| 146 | + } |
89 | 147 | } |
90 | | -} |
91 | 148 | |
92 | | -wgAjaxSaveDraft.call = function( dtoken, etoken, id, title, section, starttime, edittime, scrolltop, text, summary, minoredit ) { |
93 | | - // If in progress, exit now |
94 | | - if( wgAjaxSaveDraft.inprogress ) |
95 | | - return; |
96 | | - |
97 | | - // Otherwise, declare we are now in progress |
98 | | - wgAjaxSaveDraft.inprogress = true; |
99 | | - |
100 | | - // Perform Ajax call |
101 | | - var old = sajax_request_type; |
102 | | - sajax_request_type = "POST"; |
103 | | - sajax_do_call( |
104 | | - "DraftHooks::AjaxSave", |
105 | | - [ dtoken, etoken, id, title, section, starttime, edittime, scrolltop, text, summary, minoredit ], |
106 | | - wgAjaxSaveDraft.processResult |
107 | | - ); |
108 | | - sajax_request_type = old; |
109 | | - |
110 | | - // Reallow request if it is not done in 2 seconds |
111 | | - wgAjaxSaveDraft.timeoutID = window.setTimeout( function() { |
112 | | - wgAjaxSaveDraft.inprogress = false; |
113 | | - }, 2000 ); |
114 | | -} |
115 | | - |
116 | | -wgAjaxSaveDraft.processResult = function( request ) { |
117 | | - // Change UI state |
118 | | - if( request.responseText > -1 ) { |
119 | | - wgAjaxSaveDraft.setControlsSaved(); |
120 | | - document.editform.wpDraftID.value = request.responseText; |
121 | | - } else { |
122 | | - wgAjaxSaveDraft.setControlsError(); |
| 149 | + this.respond = function( |
| 150 | + request |
| 151 | + ) { |
| 152 | + // Checks that an error did not occur |
| 153 | + if ( request.responseText > -1 ) { |
| 154 | + // Changes state to saved |
| 155 | + self.setState( 'saved' ); |
| 156 | + // Gets id of newly inserted draft (or updates if it already exists) |
| 157 | + // and stores it in a hidden form field |
| 158 | + form.wpDraftID.value = request.responseText; |
| 159 | + } else { |
| 160 | + // Changes state to error |
| 161 | + self.setState( 'error' ); |
| 162 | + } |
123 | 163 | } |
124 | | - |
125 | | - // Change object state |
126 | | - wgAjaxSaveDraft.inprogress = false; |
127 | 164 | } |
128 | | - |
129 | | -hookEvent( "load", wgAjaxSaveDraft.onLoad ); |
| 165 | +// Instantiates a draft object |
| 166 | +var wgDraft = new Draft(); |
| 167 | +// Registers hooks |
| 168 | +hookEvent( "load", wgDraft.initialize ); |