r79666 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r79665‎ | r79666 | r79667 >
Date:20:48, 5 January 2011
Author:btongminh
Status:deferred
Tags:
Comment:
Fix r79665 copy-paste along working copies fail
Modified paths:
  • /trunk/extensions/Drafts/Drafts.classes.php (modified) (history)
  • /trunk/extensions/Drafts/Drafts.hooks.php (modified) (history)

Diff [purge]

Index: trunk/extensions/Drafts/Drafts.classes.php
@@ -1,360 +1,697 @@
22 <?php
33 /**
4 - * Hooks for Drafts extension
 4+ * Classes for Drafts extension
55 *
66 * @file
77 * @ingroup Extensions
88 */
99
10 -class DraftHooks {
 10+abstract class Drafts {
1111
1212 /* Static Functions */
13 - public static function schema( $updater = null ) {
14 - if ( $updater === null ) {
15 - global $wgExtNewTables, $wgExtModifiedFields, $wgDBtype;
1613
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 - );
 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();
2753 }
 54+ }
 55+ // Checks if specific user was given
 56+ if ( $userID !== null ) {
 57+ // Adds specific user to condition
 58+ $where['draft_user'] = $userID;
2859 } else {
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 - }
 60+ // Adds current user as condition
 61+ $where['draft_user'] = $wgUser->getID();
3562 }
 63+ // Get a list of matching drafts
 64+ return $dbr->selectField( 'drafts', 'count(*)', $where, __METHOD__ );
 65+ }
3666
37 - return true;
 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+ }
3889 }
3990
4091 /**
41 - * SpecialMovepageAfterMove hook
 92+ * Re-titles drafts which point to a particlar article, as a response to the
 93+ * article being moved.
4294 */
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;
 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+ );
48109 }
49110
50111 /**
51 - * ArticleSaveComplete hook
 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
52117 */
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 );
 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+ }
62143 }
63 - // Continue
64 - return true;
 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;
65164 }
66165
67166 /**
68 - * EditPage::showEditForm:initial hook
69 - * Load draft...
 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
70172 */
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;
 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+ );
84192 }
 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() );
85246
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 - );
 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() );
96257 }
97 - // Load draft with info
98 - $draft->setTitle( Title::newFromText(
99 - $wgRequest->getText( 'wpDraftTitle' ) )
 258+ // Build XML
 259+ $wgOut->addHTML( Xml::openElement( 'tr' ) );
 260+ $wgOut->addHTML(
 261+ Xml::openElement( 'td' )
100262 );
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' ) )
 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+ )
123275 );
124 - $wgOut->addHTML( Xml::element(
125 - 'h3', null, wfMsg( 'drafts-view-existing' ) )
 276+ $wgOut->addHTML( Xml::closeElement( 'td' ) );
 277+ $wgOut->addHTML(
 278+ Xml::element( 'td',
 279+ null,
 280+ $wgLang->timeanddate( $draft->getSaveTime() )
 281+ )
126282 );
127 - Drafts::display( $wgTitle );
128 - $wgOut->addHTML( Xml::closeElement( 'div' ) );
129 - } else {
130 - $jsWarn = "if( !wgAjaxSaveDraft.insync ) return confirm('" .
 283+ $wgOut->addHTML(
 284+ Xml::openElement( 'td' )
 285+ );
 286+ $jsClick = "if( wgDraft.getState() !== 'unchanged' )" .
 287+ "return confirm('" .
131288 Xml::escapeJsString( wfMsgHTML( 'drafts-view-warn' ) ) .
132289 "')";
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 )
 290+ $wgOut->addHTML(
 291+ Xml::element( 'a',
 292+ array(
 293+ 'href' => $urlDiscard,
 294+ 'onclick' => $jsClick
 295+ ),
 296+ wfMsg( 'drafts-view-discard' )
142297 )
143298 );
144 - $wgOut->addHTML( wfMsgHTML( 'drafts-view-notice', $link ) );
 299+ $wgOut->addHTML( Xml::closeElement( 'td' ) );
 300+ $wgOut->addHTML( Xml::closeElement( 'tr' ) );
145301 }
 302+ $wgOut->addHTML( Xml::closeElement( 'table' ) );
 303+ // Return number of drafts
 304+ return count( $drafts );
146305 }
147 - // Continue
148 - return true;
 306+ return 0;
149307 }
 308+}
150309
 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+
151329 /**
152 - * EditFilter hook
153 - * Intercept the saving of an article to detect if the submission was from
154 - * the non-javascript save draft button
 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
155335 */
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;
 336+ public static function newFromID( $id, $autoload = true ) {
 337+ return new Draft( $id, $autoload );
165338 }
166339
167340 /**
168 - * EditPageBeforeEditButtons hook
169 - * Add draft saving controls
 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
170345 */
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;
 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;
283361 }
284362
 363+ /* Properties */
 364+
285365 /**
286 - * AjaxAddScript hook
287 - * Add AJAX support script
 366+ * @return Whether draft exists in database
288367 */
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;
 368+ public function exists() {
 369+ return $this->exists;
307370 }
308371
309372 /**
310 - * BeforePageDisplay hook
311 - * Add CSS style sheet
 373+ * @return Draft ID
312374 */
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(
 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( '*' ),
318578 array(
319 - 'rel' => 'stylesheet',
320 - 'type' => 'text/css',
321 - 'href' => $wgScriptPath . '/extensions/Drafts/Drafts.css?' .
322 - $wgDraftsStyleVersion,
323 - )
 579+ 'draft_id' => (int) $this->id,
 580+ 'draft_user' => (int) $wgUser->getID()
 581+ ),
 582+ __METHOD__
324583 );
325 - // Continue
326 - return true;
 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;
327611 }
328612
329613 /**
330 - * AJAX function export DraftHooks::AjaxSave
331 - * Respond to AJAX queries
 614+ * Inserts or updates draft row in database
332615 */
333 - public static function save( $dtoken, $etoken, $id, $title, $section,
334 - $starttime, $edittime, $scrolltop, $text, $summary, $minoredit
335 - ) {
 616+ public function save() {
336617 global $wgUser, $wgRequest;
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();
 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+ );
356649 } else {
357 - // Return failure
358 - return '-1';
 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+ }
359670 }
 671+ // Commits any processed changes
 672+ $dbw->commit();
 673+ // Returns success
 674+ return true;
360675 }
 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+ }
361698 }
Index: trunk/extensions/Drafts/Drafts.hooks.php
@@ -11,22 +11,26 @@
1212 /* Static Functions */
1313 public static function schema( $updater = null ) {
1414 if ( $updater === null ) {
15 - global $wgExtNewTables, $wgExtModifiedFields;
 15+ global $wgExtNewTables, $wgExtModifiedFields, $wgDBtype;
1616
1717 $wgExtNewTables[] = array(
1818 'drafts',
1919 dirname( __FILE__ ) . '/Drafts.sql'
2020 );
21 - $wgExtModifiedFields[] = array(
22 - 'drafts',
23 - 'draft_token',
24 - dirname( __FILE__ ) . '/patch-draft_token.sql'
25 - );
 21+ if ( $wgDBtype != 'sqlite' ) {
 22+ $wgExtModifiedFields[] = array(
 23+ 'drafts',
 24+ 'draft_token',
 25+ dirname( __FILE__ ) . '/patch-draft_token.sql'
 26+ );
 27+ }
2628 } else {
2729 $updater->addExtensionUpdate( array( 'addTable', 'drafts',
2830 dirname( __FILE__ ) . '/Drafts.sql', true ) );
29 - $updater->addExtensionUpdate( array( 'modifyField', 'drafts', 'draft_token',
30 - dirname( __FILE__ ) . '/patch-draft_token.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+ }
3135 }
3236
3337 return true;

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r79665Fixes for Sqlitebtongminh20:45, 5 January 2011

Status & tagging log