r79665 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r79664‎ | r79665 | r79666 >
Date:20:45, 5 January 2011
Author:btongminh
Status:deferred
Tags:
Comment:
Fixes for Sqlite
Modified paths:
  • /trunk/extensions/Drafts/Drafts.classes.php (modified) (history)
  • /trunk/extensions/Drafts/Drafts.sql (modified) (history)

Diff [purge]

Index: trunk/extensions/Drafts/Drafts.sql
@@ -5,7 +5,7 @@
66 -- users have yet to commit.
77 CREATE TABLE /*_*/drafts (
88 -- Unique ID for drafts
9 - draft_id INTEGER AUTO_INCREMENT,
 9+ draft_id INTEGER PRIMARY KEY AUTO_INCREMENT,
1010 -- Unique value generated at edit time to prevent duplicate submissions
1111 draft_token VARBINARY(255),
1212 -- User who made the draft, 0 for anons
@@ -29,16 +29,10 @@
3030 draft_text mediumblob NOT NULL,
3131 draft_summary TINYBLOB,
3232 -- Is this a minor edit?
33 - draft_minoredit BOOL,
34 - PRIMARY KEY (draft_id),
35 - INDEX draft_user_savetime ( draft_user, draft_savetime ),
36 - INDEX draft_user_page_savetime (
37 - draft_user,
38 - draft_page,
39 - draft_namespace,
40 - draft_title,
41 - draft_savetime
42 - ),
43 - INDEX draft_savetime (draft_savetime),
44 - INDEX draft_page (draft_page)
 33+ draft_minoredit BOOL
4534 ) /*$wgDBTableOptions*/;
 35+
 36+CREATE INDEX /*i*/draft_user_savetime ( draft_user, draft_savetime );
 37+CREATE INDEX /*i*/draft_user_page_savetime ON /*_*/drafts ( draft_user, draft_page, draft_namespace, draft_title, draft_savetime );
 38+CREATE INDEX /*i*/draft_savetime ON /*_*/drafts (draft_savetime);
 39+CREATE INDEX /*i*/draft_page ON /*_*/drafts (draft_page);
Index: trunk/extensions/Drafts/Drafts.classes.php
@@ -1,697 +1,360 @@
22 <?php
33 /**
4 - * Classes for Drafts extension
 4+ * Hooks for Drafts extension
55 *
66 * @file
77 * @ingroup Extensions
88 */
99
10 -abstract class Drafts {
 10+class DraftHooks {
1111
1212 /* Static Functions */
 13+ public static function schema( $updater = null ) {
 14+ if ( $updater === null ) {
 15+ global $wgExtNewTables, $wgExtModifiedFields, $wgDBtype;
1316
14 - private static function getDraftAgeCutoff() {
15 - global $egDraftsLifeSpan;
16 - if ( !$egDraftsLifeSpan ) {
17 - // Drafts stay forever
18 - return 0;
19 - }
20 - return wfTimestamp( TS_UNIX ) - ( $egDraftsLifeSpan * 60 * 60 * 24 );
21 - }
22 -
23 - /**
24 - * Counts the number of existing drafts for a specific user
25 - *
26 - * @param $title Object: [optional] Title of article, defaults to all articles
27 - * @param $userID Integer: [optional] ID of user, defaults to current user
28 - * @return Number of drafts which match condition parameters
29 - */
30 - public static function num( $title = null, $userID = null ) {
31 - global $wgUser;
32 - // Get database connection
33 - $dbr = wfGetDB( DB_SLAVE );
34 - // Builds where clause
35 - $where = array(
36 - 'draft_savetime > ' . $dbr->addQuotes(
37 - $dbr->timestamp( self::getDraftAgeCutoff() )
38 - )
39 - );
40 - // Checks if a specific title was given
41 - if ( $title !== null ) {
42 - // Get page id from title
43 - $pageId = $title->getArticleId();
44 - // Checks if page id exists
45 - if ( $pageId ) {
46 - // Adds specific page id to conditions
47 - $where['draft_page'] = $pageId;
48 - } else {
49 - // Adds new page information to conditions
50 - $where['draft_page'] = 0; // page not created yet
51 - $where['draft_namespace'] = $title->getNamespace();
52 - $where['draft_title'] = $title->getDBkey();
 17+ $wgExtNewTables[] = array(
 18+ 'drafts',
 19+ dirname( __FILE__ ) . '/Drafts.sql'
 20+ );
 21+ if ( $wgDBtype != 'sqlite' ) {
 22+ $wgExtModifiedFields[] = array(
 23+ 'drafts',
 24+ 'draft_token',
 25+ dirname( __FILE__ ) . '/patch-draft_token.sql'
 26+ );
5327 }
54 - }
55 - // Checks if specific user was given
56 - if ( $userID !== null ) {
57 - // Adds specific user to condition
58 - $where['draft_user'] = $userID;
5928 } else {
60 - // Adds current user as condition
61 - $where['draft_user'] = $wgUser->getID();
 29+ $updater->addExtensionUpdate( array( 'addTable', 'drafts',
 30+ dirname( __FILE__ ) . '/Drafts.sql', true ) );
 31+ if ( $updater->getDb()->getType() != 'sqlite' ) {
 32+ $updater->addExtensionUpdate( array( 'modifyField', 'drafts', 'draft_token',
 33+ dirname( __FILE__ ) . '/patch-draft_token.sql', true ) );
 34+ }
6235 }
63 - // Get a list of matching drafts
64 - return $dbr->selectField( 'drafts', 'count(*)', $where, __METHOD__ );
65 - }
6636
67 - /**
68 - * Removes drafts which have not been modified for a period of time defined
69 - * by $egDraftsCleanRatio
70 - */
71 - public static function clean() {
72 - global $egDraftsCleanRatio;
73 -
74 - // Only perform this action a fraction of the time
75 - if ( rand( 0, $egDraftsCleanRatio ) == 0 ) {
76 - // Get database connection
77 - $dbw = wfGetDB( DB_MASTER );
78 - // Removes expired drafts from database
79 - $dbw->delete( 'drafts',
80 - array(
81 - 'draft_savetime < ' .
82 - $dbw->addQuotes(
83 - $dbw->timestamp( self::getDraftAgeCutoff() )
84 - )
85 - ),
86 - __METHOD__
87 - );
88 - }
 37+ return true;
8938 }
9039
9140 /**
92 - * Re-titles drafts which point to a particlar article, as a response to the
93 - * article being moved.
 41+ * SpecialMovepageAfterMove hook
9442 */
95 - public static function move( $oldTitle, $newTitle ) {
96 - // Get database connection
97 - $dbw = wfGetDB( DB_MASTER );
98 - // Updates title and namespace of drafts upon moving
99 - $dbw->update( 'drafts',
100 - array(
101 - 'draft_namespace' => $newTitle->getNamespace(),
102 - 'draft_title' => $newTitle->getDBkey()
103 - ),
104 - array(
105 - 'draft_page' => $newTitle->getArticleId()
106 - ),
107 - __METHOD__
108 - );
 43+ public static function move( $this, $ot, $nt ) {
 44+ // Update all drafts of old article to new article for all users
 45+ Drafts::move( $ot, $nt );
 46+ // Continue
 47+ return true;
10948 }
11049
11150 /**
112 - * Gets a list of existing drafts for a specific user
113 - *
114 - * @param $title Object: [optional] Title of article, defaults to all articles
115 - * @param $userID Integer: [optional] ID of user, defaults to current user
116 - * @return List of drafts or null
 51+ * ArticleSaveComplete hook
11752 */
118 - public static function get( $title = null, $userID = null ) {
119 - global $wgUser;
120 - // Removes expired drafts for a more accurate list
121 - Drafts::clean();
122 - // Gets database connection
123 - $dbw = wfGetDB( DB_MASTER );
124 - // Builds where clause
125 - $where = array(
126 - 'draft_savetime > ' . $dbw->addQuotes(
127 - $dbw->timestamp( self::getDraftAgeCutoff() )
128 - )
129 - );
130 - // Checks if specific title was given
131 - if ( $title !== null ) {
132 - // Get page id from title
133 - $pageId = $title->getArticleId();
134 - // Checks if page id exists
135 - if ( $pageId ) {
136 - // Adds specific page id to conditions
137 - $where['draft_page'] = $pageId;
138 - } else {
139 - // Adds new page information to conditions
140 - $where['draft_namespace'] = $title->getNamespace();
141 - $where['draft_title'] = $title->getDBkey();
142 - }
 53+ public static function discard( $article, $user, $text, $summary, $m,
 54+ $watchthis, $section, $flags, $rev
 55+ ) {
 56+ global $wgRequest;
 57+ // Check if the save occured from a draft
 58+ $draft = Draft::newFromID( $wgRequest->getIntOrNull( 'wpDraftID' ) );
 59+ if ( $draft->exists() ) {
 60+ // Discard the draft
 61+ $draft->discard( $user );
14362 }
144 - // Checks if a specific user was given
145 - if ( $userID !== null ) {
146 - // Adds specific user to conditions
147 - $where['draft_user'] = $userID;
148 - } else {
149 - // Adds current user to conditions
150 - $where['draft_user'] = $wgUser->getID();
151 - }
152 - // Gets matching drafts from database
153 - $result = $dbw->select( 'drafts', '*', $where, __METHOD__ );
154 - if ( $result ) {
155 - // Creates an array of matching drafts
156 - $drafts = array();
157 - while ( $row = $dbw->fetchRow( $result ) ) {
158 - // Adds a new draft to the list from the row
159 - $drafts[] = Draft::newFromRow( $row );
160 - }
161 - }
162 - // Returns array of matching drafts or null if there were none
163 - return count( $drafts ) ? $drafts : null;
 63+ // Continue
 64+ return true;
16465 }
16566
16667 /**
167 - * Outputs a table of existing drafts
168 - *
169 - * @param $title Object: [optional] Title of article, defaults to all articles
170 - * @param $userID Integer: [optional] ID of user, defaults to current user
171 - * @return Number of drafts in the table
 68+ * EditPage::showEditForm:initial hook
 69+ * Load draft...
17270 */
173 - public static function display( $title = null, $userID = null ) {
174 - global $wgOut, $wgRequest, $wgUser, $wgLang;
175 - // Gets draftID
176 - $currentDraft = Draft::newFromID( $wgRequest->getIntOrNull( 'draft' ) );
177 - // Output HTML for list of drafts
178 - $drafts = Drafts::get( $title, $userID );
179 - if ( count( $drafts ) > 0 ) {
180 - global $egDraftsLifeSpan;
181 - // Internationalization
182 - wfLoadExtensionMessages( 'Drafts' );
183 - // Add a summary, on Special:Drafts only
184 - if( !$title || $title->getNamespace() == NS_SPECIAL ) {
185 - $wgOut->wrapWikiMsg(
186 - '<div class="mw-drafts-summary">$1</div>',
187 - array(
188 - 'drafts-view-summary',
189 - $wgLang->formatNum( $egDraftsLifeSpan )
190 - )
191 - );
 71+ public static function loadForm( $editpage ) {
 72+ global $wgUser, $wgRequest, $wgOut, $wgTitle, $wgLang;
 73+ // Check permissions
 74+ if ( $wgUser->isAllowed( 'edit' ) && $wgUser->isLoggedIn() ) {
 75+ // Get draft
 76+ $draft = Draft::newFromID( $wgRequest->getIntOrNull( 'draft' ) );
 77+ // Load form values
 78+ if ( $draft->exists() ) {
 79+ // Override initial values in the form with draft data
 80+ $editpage->textbox1 = $draft->getText();
 81+ $editpage->summary = $draft->getSummary();
 82+ $editpage->scrolltop = $draft->getScrollTop();
 83+ $editpage->minoredit = $draft->getMinorEdit() ? true : false;
19284 }
193 - // Build XML
194 - $wgOut->addHTML(
195 - Xml::openElement( 'table',
196 - array(
197 - 'cellpadding' => 5,
198 - 'cellspacing' => 0,
199 - 'width' => '100%',
200 - 'border' => 0,
201 - 'id' => 'drafts-list-table'
202 - )
203 - )
204 - );
205 - $wgOut->addHTML( Xml::openElement( 'tr' ) );
206 - $wgOut->addHTML(
207 - Xml::element( 'th',
208 - array( 'width' => '75%', 'nowrap' => 'nowrap' ),
209 - wfMsg( 'drafts-view-article' )
210 - )
211 - );
212 - $wgOut->addHTML(
213 - Xml::element( 'th',
214 - null,
215 - wfMsg( 'drafts-view-saved' )
216 - )
217 - );
218 - $wgOut->addHTML( Xml::element( 'th' ) );
219 - $wgOut->addHTML( Xml::closeElement( 'tr' ) );
220 - // Add existing drafts for this page and user
221 - foreach ( $drafts as $draft ) {
222 - // Get article title text
223 - $htmlTitle = $draft->getTitle()->getEscapedText();
224 - // Build Article Load link
225 - $urlLoad = $draft->getTitle()->getFullURL(
226 - 'action=edit&draft=' . urlencode( $draft->getID() )
227 - );
228 - // Build discard link
229 - $urlDiscard = SpecialPage::getTitleFor( 'Drafts' )->getFullURL(
230 - sprintf( 'discard=%s&token=%s',
231 - urlencode( $draft->getID() ),
232 - urlencode( $wgUser->editToken() )
233 - )
234 - );
235 - // If in edit mode, return to editor
236 - if (
237 - $wgRequest->getText( 'action' ) == 'edit' ||
238 - $wgRequest->getText( 'action' ) == 'submit'
239 - ) {
240 - $urlDiscard .= '&returnto=' . urlencode( 'edit' );
241 - }
242 - // Append section to titles and links
243 - if ( $draft->getSection() !== null ) {
244 - // Detect section name
245 - $lines = explode( "\n", $draft->getText() );
24685
247 - // If there is any content in the section
248 - if ( count( $lines ) > 0 ) {
249 - $htmlTitle .= '#' . htmlspecialchars(
250 - trim( trim( substr( $lines[0], 0, 255 ), '=' ) )
251 - );
252 - }
253 - // Modify article link and title
254 - $urlLoad .= '&section=' . urlencode( $draft->getSection() );
255 - $urlDiscard .= '&section=' .
256 - urlencode( $draft->getSection() );
 86+ // Save draft on non-save submission
 87+ if ( $wgRequest->getVal( 'action' ) == 'submit' &&
 88+ $wgUser->editToken() == $wgRequest->getText( 'wpEditToken' ) )
 89+ {
 90+ // If the draft wasn't specified in the url, try using a
 91+ // form-submitted one
 92+ if ( !$draft->exists() ) {
 93+ $draft = Draft::newFromID(
 94+ $wgRequest->getIntOrNull( 'wpDraftID' )
 95+ );
25796 }
258 - // Build XML
259 - $wgOut->addHTML( Xml::openElement( 'tr' ) );
260 - $wgOut->addHTML(
261 - Xml::openElement( 'td' )
 97+ // Load draft with info
 98+ $draft->setTitle( Title::newFromText(
 99+ $wgRequest->getText( 'wpDraftTitle' ) )
262100 );
263 - $wgOut->addHTML(
264 - Xml::element( 'a',
265 - array(
266 - 'href' => $urlLoad,
267 - 'style' => 'font-weight:' .
268 - (
269 - $currentDraft->getID() == $draft->getID() ?
270 - 'bold' : 'normal'
271 - )
272 - ),
273 - $htmlTitle
274 - )
 101+ $draft->setSection( $wgRequest->getInt( 'wpSection' ) );
 102+ $draft->setStartTime( $wgRequest->getText( 'wpStarttime' ) );
 103+ $draft->setEditTime( $wgRequest->getText( 'wpEdittime' ) );
 104+ $draft->setSaveTime( wfTimestampNow() );
 105+ $draft->setScrollTop( $wgRequest->getInt( 'wpScrolltop' ) );
 106+ $draft->setText( $wgRequest->getText( 'wpTextbox1' ) );
 107+ $draft->setSummary( $wgRequest->getText( 'wpSummary' ) );
 108+ $draft->setMinorEdit( $wgRequest->getInt( 'wpMinoredit', 0 ) );
 109+ // Save draft
 110+ $draft->save();
 111+ // Use the new draft id
 112+ $wgRequest->setVal( 'draft', $draft->getID() );
 113+ }
 114+ }
 115+ // Internationalization
 116+ wfLoadExtensionMessages( 'Drafts' );
 117+ $numDrafts = Drafts::num( $wgTitle );
 118+ // Show list of drafts
 119+ if ( $numDrafts > 0 ) {
 120+ if ( $wgRequest->getText( 'action' ) !== 'submit' ) {
 121+ $wgOut->addHTML( Xml::openElement(
 122+ 'div', array( 'id' => 'drafts-list-box' ) )
275123 );
276 - $wgOut->addHTML( Xml::closeElement( 'td' ) );
277 - $wgOut->addHTML(
278 - Xml::element( 'td',
279 - null,
280 - $wgLang->timeanddate( $draft->getSaveTime() )
281 - )
 124+ $wgOut->addHTML( Xml::element(
 125+ 'h3', null, wfMsg( 'drafts-view-existing' ) )
282126 );
283 - $wgOut->addHTML(
284 - Xml::openElement( 'td' )
285 - );
286 - $jsClick = "if( wgDraft.getState() !== 'unchanged' )" .
287 - "return confirm('" .
 127+ Drafts::display( $wgTitle );
 128+ $wgOut->addHTML( Xml::closeElement( 'div' ) );
 129+ } else {
 130+ $jsWarn = "if( !wgAjaxSaveDraft.insync ) return confirm('" .
288131 Xml::escapeJsString( wfMsgHTML( 'drafts-view-warn' ) ) .
289132 "')";
290 - $wgOut->addHTML(
291 - Xml::element( 'a',
292 - array(
293 - 'href' => $urlDiscard,
294 - 'onclick' => $jsClick
295 - ),
296 - wfMsg( 'drafts-view-discard' )
 133+ $link = Xml::element( 'a',
 134+ array(
 135+ 'href' => $wgTitle->getFullURL( 'action=edit' ),
 136+ 'onclick' => $jsWarn
 137+ ),
 138+ wfMsgExt(
 139+ 'drafts-view-notice-link',
 140+ array( 'parsemag' ),
 141+ $wgLang->formatNum( $numDrafts )
297142 )
298143 );
299 - $wgOut->addHTML( Xml::closeElement( 'td' ) );
300 - $wgOut->addHTML( Xml::closeElement( 'tr' ) );
 144+ $wgOut->addHTML( wfMsgHTML( 'drafts-view-notice', $link ) );
301145 }
302 - $wgOut->addHTML( Xml::closeElement( 'table' ) );
303 - // Return number of drafts
304 - return count( $drafts );
305146 }
306 - return 0;
 147+ // Continue
 148+ return true;
307149 }
308 -}
309150
310 -class Draft {
311 -
312 - /* Members */
313 - private $exists = false;
314 - private $id;
315 - private $token;
316 - private $userID;
317 - private $title;
318 - private $section;
319 - private $starttime;
320 - private $edittime;
321 - private $savetime;
322 - private $scrolltop;
323 - private $text;
324 - private $summary;
325 - private $minoredit;
326 -
327 - /* Static Functions */
328 -
329151 /**
330 - * Creates a new Draft object from a draft ID
331 - *
332 - * @param $id Integer: ID of draft
333 - * @param $autoload Boolean: [optional] Whether to load draft information
334 - * @return New Draft object
 152+ * EditFilter hook
 153+ * Intercept the saving of an article to detect if the submission was from
 154+ * the non-javascript save draft button
335155 */
336 - public static function newFromID( $id, $autoload = true ) {
337 - return new Draft( $id, $autoload );
 156+ public static function interceptSave( $editor, $text, $section, $error ) {
 157+ global $wgRequest;
 158+ // Don't save if the save draft button caused the submit
 159+ if ( $wgRequest->getText( 'wpDraftSave' ) !== '' ) {
 160+ // Modify the error so it's clear we want to remain in edit mode
 161+ $error = ' ';
 162+ }
 163+ // Continue
 164+ return true;
338165 }
339166
340167 /**
341 - * Creates a new Draft object from a database row
342 - *
343 - * @param $row Array: Database row to create Draft object with
344 - * @return New Draft object
 168+ * EditPageBeforeEditButtons hook
 169+ * Add draft saving controls
345170 */
346 - public static function newFromRow( $row ) {
347 - $draft = new Draft( $row['draft_id'], false );
348 - $draft->setToken( $row['draft_token'] );
349 - $draft->setTitle(
350 - Title::makeTitle( $row['draft_namespace'], $row['draft_title'] )
351 - );
352 - $draft->setSection( $row['draft_section'] );
353 - $draft->setStartTime( $row['draft_starttime'] );
354 - $draft->setEditTime( $row['draft_edittime'] );
355 - $draft->setSaveTime( $row['draft_savetime'] );
356 - $draft->setScrollTop( $row['draft_scrolltop'] );
357 - $draft->setText( $row['draft_text'] );
358 - $draft->setSummary( $row['draft_summary'] );
359 - $draft->setMinorEdit( $row['draft_minoredit'] );
360 - return $draft;
 171+ public static function controls( $editpage, $buttons ) {
 172+ global $wgUser, $wgTitle, $wgRequest;
 173+ global $egDraftsAutoSaveWait, $egDraftsAutoSaveTimeout;
 174+ // Check permissions
 175+ if ( $wgUser->isAllowed( 'edit' ) && $wgUser->isLoggedIn() ) {
 176+ // Internationalization
 177+ wfLoadExtensionMessages( 'Drafts' );
 178+ // Build XML
 179+ $buttons['savedraft'] = Xml::openElement( 'script',
 180+ array(
 181+ 'type' => 'text/javascript',
 182+ 'language' => 'javascript'
 183+ )
 184+ );
 185+ $buttonAttribs = array(
 186+ 'id' => 'wpDraftSave',
 187+ 'name' => 'wpDraftSave',
 188+ 'tabindex' => 8,
 189+ 'value' => wfMsg( 'drafts-save-save' ),
 190+ );
 191+ $accesskey = $wgUser->getSkin()->accesskey( 'drafts-save' );
 192+ if ( $accesskey !== false ) {
 193+ $buttonAttribs['accesskey'] = $accesskey;
 194+ }
 195+ $tooltip = $wgUser->getSkin()->titleAttrib(
 196+ 'drafts-save', 'withaccess'
 197+ );
 198+ if ( $tooltip !== false ) {
 199+ $buttonAttribs['title'] = $tooltip;
 200+ }
 201+ $ajaxButton = Xml::escapeJsString(
 202+ Xml::element( 'input',
 203+ array( 'type' => 'button' ) + $buttonAttribs
 204+ + ( $wgRequest->getText( 'action' ) !== 'submit' ?
 205+ array ( 'disabled' => 'disabled' )
 206+ : array()
 207+ )
 208+ )
 209+ );
 210+ $buttons['savedraft'] .= "document.write( '{$ajaxButton}' );";
 211+ $buttons['savedraft'] .= Xml::closeElement( 'script' );
 212+ $buttons['savedraft'] .= Xml::openElement( 'noscript' );
 213+ $buttons['savedraft'] .= Xml::element( 'input',
 214+ array( 'type' => 'submit' ) + $buttonAttribs
 215+ );
 216+ $buttons['savedraft'] .= Xml::closeElement( 'noscript' );
 217+ $buttons['savedraft'] .= Xml::element( 'input',
 218+ array(
 219+ 'type' => 'hidden',
 220+ 'name' => 'wpDraftAutoSaveWait',
 221+ 'value' => $egDraftsAutoSaveWait
 222+ )
 223+ );
 224+ $buttons['savedraft'] .= Xml::element( 'input',
 225+ array(
 226+ 'type' => 'hidden',
 227+ 'name' => 'wpDraftAutoSaveTimeout',
 228+ 'value' => $egDraftsAutoSaveTimeout
 229+ )
 230+ );
 231+ $buttons['savedraft'] .= Xml::element( 'input',
 232+ array(
 233+ 'type' => 'hidden',
 234+ 'name' => 'wpDraftToken',
 235+ 'value' => wfGenerateToken()
 236+ )
 237+ );
 238+ $buttons['savedraft'] .= Xml::element( 'input',
 239+ array(
 240+ 'type' => 'hidden',
 241+ 'name' => 'wpDraftID',
 242+ 'value' => $wgRequest->getInt( 'draft', '' )
 243+ )
 244+ );
 245+ $buttons['savedraft'] .= Xml::element( 'input',
 246+ array(
 247+ 'type' => 'hidden',
 248+ 'name' => 'wpDraftTitle',
 249+ 'value' => $wgTitle->getPrefixedText()
 250+ )
 251+ );
 252+ $buttons['savedraft'] .= Xml::element( 'input',
 253+ array(
 254+ 'type' => 'hidden',
 255+ 'name' => 'wpMsgSaved',
 256+ 'value' => wfMsg( 'drafts-save-saved' )
 257+ )
 258+ );
 259+ $buttons['savedraft'] .= Xml::element( 'input',
 260+ array(
 261+ 'type' => 'hidden',
 262+ 'name' => 'wpMsgSaving',
 263+ 'value' => wfMsg( 'drafts-save-saving' )
 264+ )
 265+ );
 266+ $buttons['savedraft'] .= Xml::element( 'input',
 267+ array(
 268+ 'type' => 'hidden',
 269+ 'name' => 'wpMsgSaveDraft',
 270+ 'value' => wfMsg( 'drafts-save-save' )
 271+ )
 272+ );
 273+ $buttons['savedraft'] .= Xml::element( 'input',
 274+ array(
 275+ 'type' => 'hidden',
 276+ 'name' => 'wpMsgError',
 277+ 'value' => wfMsg( 'drafts-save-error' )
 278+ )
 279+ );
 280+ }
 281+ // Continue
 282+ return true;
361283 }
362284
363 - /* Properties */
364 -
365285 /**
366 - * @return Whether draft exists in database
 286+ * AjaxAddScript hook
 287+ * Add AJAX support script
367288 */
368 - public function exists() {
369 - return $this->exists;
 289+ public static function addJS( $out ) {
 290+ global $wgScriptPath, $wgJsMimeType, $wgDraftsStyleVersion;
 291+ // FIXME: assumes standard dir structure
 292+ // Add JavaScript to support AJAX draft saving
 293+ $out->addScript(
 294+ Xml::element(
 295+ 'script',
 296+ array(
 297+ 'type' => $wgJsMimeType,
 298+ 'src' => $wgScriptPath . '/extensions/Drafts/Drafts.js?' .
 299+ $wgDraftsStyleVersion
 300+ ),
 301+ '',
 302+ false
 303+ )
 304+ );
 305+ // Continue
 306+ return true;
370307 }
371308
372309 /**
373 - * @return Draft ID
 310+ * BeforePageDisplay hook
 311+ * Add CSS style sheet
374312 */
375 - public function getID() {
376 - return $this->id;
377 - }
378 -
379 - /**
380 - * @return Edit token
381 - */
382 - public function getToken() {
383 - return $this->token;
384 - }
385 -
386 - /**
387 - * Sets the edit token, like one generated by wfGenerateToken()
388 - * @param $token String
389 - */
390 - public function setToken( $token ) {
391 - $this->token = $token;
392 - }
393 -
394 - /**
395 - * @return User ID of draft creator
396 - */
397 - public function getUserID() {
398 - return $this->userID;
399 - }
400 -
401 - /**
402 - * Sets user ID of draft creator
403 - * @param $userID Integer: user ID
404 - */
405 - public function setUserID( $userID ) {
406 - $this->userID = $userID;
407 - }
408 -
409 - /**
410 - * @return Title of article of draft
411 - */
412 - public function getTitle() {
413 - return $this->title;
414 - }
415 -
416 - /**
417 - * Sets title of article of draft
418 - * @param $title Object
419 - */
420 - public function setTitle( $title ) {
421 - $this->title = $title;
422 - }
423 -
424 - /**
425 - * @return Section of the article of draft
426 - */
427 - public function getSection() {
428 - return $this->section;
429 - }
430 -
431 - /**
432 - * Sets section of the article of draft
433 - * @param $section Integer
434 - */
435 - public function setSection( $section ) {
436 - $this->section = $section;
437 - }
438 -
439 - /**
440 - * @return Time when draft of the article started
441 - */
442 - public function getStartTime() {
443 - return $this->starttime;
444 - }
445 -
446 - /**
447 - * Sets time when draft of the article started
448 - * @param $starttime String
449 - */
450 - public function setStartTime( $starttime ) {
451 - $this->starttime = $starttime;
452 - }
453 -
454 - /**
455 - * @return Time of most recent revision of article when this draft started
456 - */
457 - public function getEditTime() {
458 - return $this->edittime;
459 - }
460 -
461 - /**
462 - * Sets time of most recent revision of article when this draft started
463 - * @param $edittime String
464 - */
465 - public function setEditTime( $edittime ) {
466 - $this->edittime = $edittime;
467 - }
468 -
469 - /**
470 - * @return Time when draft was last modified
471 - */
472 - public function getSaveTime() {
473 - return $this->savetime;
474 - }
475 -
476 - /**
477 - * Sets time when draft was last modified
478 - * @param $savetime String
479 - */
480 - public function setSaveTime( $savetime ) {
481 - $this->savetime = $savetime;
482 - }
483 -
484 - /**
485 - * @return Scroll position of editor when draft was last modified
486 - */
487 - public function getScrollTop() {
488 - return $this->scrolltop;
489 - }
490 -
491 - /**
492 - * Sets scroll position of editor when draft was last modified
493 - * @param $scrolltop Integer
494 - */
495 - public function setScrollTop( $scrolltop ) {
496 - $this->scrolltop = $scrolltop;
497 - }
498 -
499 - /**
500 - * @return Text of draft version of article
501 - */
502 - public function getText() {
503 - return $this->text;
504 - }
505 -
506 - /**
507 - * Sets text of draft version of article
508 - * @param $text String
509 - */
510 - public function setText( $text ) {
511 - $this->text = $text;
512 - }
513 -
514 - /**
515 - * @return Summary of changes
516 - */
517 - public function getSummary() {
518 - return $this->summary;
519 - }
520 -
521 - /**
522 - * Sets summary of changes
523 - * @param $summary String
524 - */
525 - public function setSummary( $summary ) {
526 - $this->summary = $summary;
527 - }
528 -
529 - /**
530 - * @return Whether edit is considdered to be a minor change
531 - */
532 - public function getMinorEdit() {
533 - return $this->minoredit;
534 - }
535 -
536 - /**
537 - * Sets whether edit is considdered to be a minor change
538 - * @param boolean $minoredit
539 - */
540 - public function setMinorEdit(
541 - $minoredit
542 - ) {
543 - $this->minoredit = $minoredit;
544 - }
545 -
546 - /* Functions */
547 -
548 - /**
549 - * Generic constructor
550 - * @param $id Integer: [optional] ID to use
551 - * @param $autoload Boolean: [optional] Whether to load from database
552 - */
553 - public function __construct( $id = null, $autoload = true ) {
554 - // If an ID is a number the existence is actually checked on load
555 - // If an ID is false the existance is always false during load
556 - $this->id = $id;
557 - // Load automatically
558 - if ( $autoload ) {
559 - $this->load();
560 - }
561 - }
562 -
563 - /**
564 - * Selects draft row from database and populates object properties
565 - */
566 - private function load() {
567 - global $wgUser;
568 - // Checks if the ID of the draft was set
569 - if ( $this->id === null ) {
570 - // Exists immediately
571 - return;
572 - }
573 - // Gets database connection
574 - $dbw = wfGetDB( DB_MASTER );
575 - // Gets drafts for this article and user from database
576 - $result = $dbw->select( 'drafts',
577 - array( '*' ),
 313+ public static function addCSS( $out ) {
 314+ global $wgScriptPath, $wgDraftsStyleVersion;
 315+ // FIXME: assumes standard dir structure
 316+ // Add CSS for various styles
 317+ $out->addLink(
578318 array(
579 - 'draft_id' => (int) $this->id,
580 - 'draft_user' => (int) $wgUser->getID()
581 - ),
582 - __METHOD__
 319+ 'rel' => 'stylesheet',
 320+ 'type' => 'text/css',
 321+ 'href' => $wgScriptPath . '/extensions/Drafts/Drafts.css?' .
 322+ $wgDraftsStyleVersion,
 323+ )
583324 );
584 - // Checks if query returned any results
585 - if ( $result === false ) {
586 - // Exists immediately
587 - return;
588 - }
589 - // Fetches the row of the draft from the result
590 - $row = $dbw->fetchRow( $result );
591 - // Checks if the row is not an array or is an empty array
592 - if ( !is_array( $row ) || count( $row ) == 0 ) {
593 - // Exists immediately
594 - return;
595 - }
596 - // Synchronizes data
597 - $this->token = $row['draft_token'];
598 - $this->title = Title::makeTitle(
599 - $row['draft_namespace'], $row['draft_title']
600 - );
601 - $this->section = $row['draft_section'];
602 - $this->starttime = $row['draft_starttime'];
603 - $this->edittime = $row['draft_edittime'];
604 - $this->savetime = $row['draft_savetime'];
605 - $this->scrolltop = $row['draft_scrolltop'];
606 - $this->text = $row['draft_text'];
607 - $this->summary = $row['draft_summary'];
608 - $this->minoredit = $row['draft_minoredit'];
609 - // Updates state
610 - $this->exists = true;
 325+ // Continue
 326+ return true;
611327 }
612328
613329 /**
614 - * Inserts or updates draft row in database
 330+ * AJAX function export DraftHooks::AjaxSave
 331+ * Respond to AJAX queries
615332 */
616 - public function save() {
 333+ public static function save( $dtoken, $etoken, $id, $title, $section,
 334+ $starttime, $edittime, $scrolltop, $text, $summary, $minoredit
 335+ ) {
617336 global $wgUser, $wgRequest;
618 - // Gets database connection
619 - $dbw = wfGetDB( DB_MASTER );
620 - $dbw->begin();
621 - // Builds insert/update information
622 - $data = array(
623 - 'draft_token' => (string) $this->getToken(),
624 - 'draft_user' => (int) $wgUser->getID(),
625 - 'draft_namespace' => (int) $this->title->getNamespace(),
626 - 'draft_title' => (string) $this->title->getDBkey(),
627 - 'draft_page' => (int) $this->title->getArticleId(),
628 - 'draft_section' =>
629 - $this->section == '' ? null : (int) $this->section,
630 - 'draft_starttime' => $dbw->timestamp( $this->starttime ),
631 - 'draft_edittime' => $dbw->timestamp( $this->edittime ),
632 - 'draft_savetime' => $dbw->timestamp( $this->savetime ),
633 - 'draft_scrolltop' => (int) $this->scrolltop,
634 - 'draft_text' => (string) $this->text,
635 - 'draft_summary' => (string) $this->summary,
636 - 'draft_minoredit' => (int) $this->minoredit
637 - );
638 - // Checks if draft already exists
639 - if ( $this->exists === true ) {
640 - // Updates draft information
641 - $dbw->update( 'drafts',
642 - $data,
643 - array(
644 - 'draft_id' => (int) $this->id,
645 - 'draft_user' => (int) $wgUser->getID()
646 - ),
647 - __METHOD__
648 - );
 337+ // Verify token
 338+ if ( $wgUser->editToken() == $etoken ) {
 339+ // Create Draft
 340+ $draft = Draft::newFromID( $id );
 341+ // Load draft with info
 342+ $draft->setToken( $dtoken );
 343+ $draft->setTitle( Title::newFromText( $title ) );
 344+ $draft->setSection( $section == '' ? null : $section );
 345+ $draft->setStartTime( $starttime );
 346+ $draft->setEditTime( $edittime );
 347+ $draft->setSaveTime( wfTimestampNow() );
 348+ $draft->setScrollTop( $scrolltop );
 349+ $draft->setText( $text );
 350+ $draft->setSummary( $summary );
 351+ $draft->setMinorEdit( $minoredit );
 352+ // Save draft
 353+ $draft->save();
 354+ // Return draft id to client (used for next save)
 355+ return (string) $draft->getID();
649356 } else {
650 - // Gets a draft token exists for the current user and article
651 - $existingRow = $dbw->selectField( 'drafts', 'draft_token',
652 - array(
653 - 'draft_user' => $data['draft_user'],
654 - 'draft_namespace' => $data['draft_namespace'],
655 - 'draft_title' => $data['draft_title'],
656 - 'draft_token' => $data['draft_token']
657 - ),
658 - __METHOD__
659 - );
660 - // Checks if token existed, meaning it has been used already for
661 - // this article
662 - if ( $existingRow === false ) {
663 - // Inserts row in the database
664 - $dbw->insert( 'drafts', $data, __METHOD__ );
665 - // Gets the id of the newly inserted row
666 - $this->id = $dbw->insertId();
667 - // Updates state
668 - $this->exists = true;
669 - }
 357+ // Return failure
 358+ return '-1';
670359 }
671 - // Commits any processed changes
672 - $dbw->commit();
673 - // Returns success
674 - return true;
675360 }
676 -
677 - /**
678 - * Deletes draft row from database
679 - * @param $user Integer: [optional] User ID, defaults to current user ID
680 - */
681 - public function discard( $user = null ) {
682 - global $wgUser;
683 - // Uses $wgUser as a fallback
684 - $user = $user === null ? $wgUser : $user;
685 - // Gets database connection
686 - $dbw = wfGetDB( DB_MASTER );
687 - // Deletes draft from database verifying propper user to avoid hacking!
688 - $dbw->delete( 'drafts',
689 - array(
690 - 'draft_id' => $this->id,
691 - 'draft_user' => $user->getID()
692 - ),
693 - __METHOD__
694 - );
695 - // Updates state
696 - $this->exists = false;
697 - }
698361 }

Follow-up revisions

RevisionCommit summaryAuthorDate
r79666Fix r79665 copy-paste along working copies failbtongminh20:48, 5 January 2011

Status & tagging log