Index: trunk/extensions/LiquidThreads/lqt-schema-change-5.sql |
— | — | @@ -3,7 +3,7 @@ |
4 | 4 | update thread join thread as child on child.thread_parent = thread.thread_id set thread.thread_editedness = 1; |
5 | 5 | |
6 | 6 | create temporary table counts ( thread_id int(8) unsigned, revision_count int(8) unsigned, author_count int(8) unsigned, rev_user int(8) unsigned ); |
7 | | - |
| 7 | + |
8 | 8 | insert into counts (thread_id, revision_count, author_count, rev_user) select thread_id, count(thread_id), count(distinct rev_user), rev_user from thread join revision on rev_page = thread_root group by thread_id; |
9 | 9 | |
10 | 10 | update thread join counts on thread.thread_id = counts.thread_id set thread_editedness = 2 where revision_count > 1; |
Index: trunk/extensions/LiquidThreads/Lqt.i18n.php |
— | — | @@ -104,7 +104,7 @@ |
105 | 105 | 'lqt_sort_newest_changes' => 'last modified first', |
106 | 106 | 'lqt_sort_newest_threads' => 'newest threads first', |
107 | 107 | 'lqt_sort_oldest_threads' => 'oldest threads first', |
108 | | - 'lqt-any-date' => 'Any date', |
| 108 | + 'lqt-any-date' => 'Any date', |
109 | 109 | 'lqt-only-date' => 'Only these dates:', |
110 | 110 | 'lqt-date-from' => 'From', |
111 | 111 | 'lqt-date-to' => 'To', |
— | — | @@ -746,7 +746,7 @@ |
747 | 747 | 'lqt_sort_newest_changes' => 'fils de discussion plus récemment modifiés en premier', |
748 | 748 | 'lqt_sort_newest_threads' => 'fils de discussion les plus récents en premier', |
749 | 749 | 'lqt_sort_oldest_threads' => 'fils de discussion les plus anciens en premier', |
750 | | - 'lqt-any-date' => 'Toutes les dates', |
| 750 | + 'lqt-any-date' => 'Toutes les dates', |
751 | 751 | 'lqt-only-date' => 'Uniquement ces dates :', |
752 | 752 | 'lqt-date-from' => 'Du', |
753 | 753 | 'lqt-date-to' => 'au', |
— | — | @@ -3094,4 +3094,3 @@ |
3095 | 3095 | 'lqt_header_warning_bold' => '討論頁頁頂', |
3096 | 3096 | 'lqt_header_warning_new_discussion' => '開始一個新的討論', |
3097 | 3097 | ); |
3098 | | - |
Index: trunk/extensions/LiquidThreads/LqtPages.php |
— | — | @@ -171,31 +171,31 @@ |
172 | 172 | $i++; |
173 | 173 | } |
174 | 174 | $toclines[] = $sk->tocUnindent(1); |
175 | | - |
| 175 | + |
176 | 176 | $this->openDiv('lqt_toc_wrapper'); |
177 | 177 | $this->output->addHTML('<h2 class="lqt_toc_title">'.wfMsg('lqt_contents_title').'</h2> <ul>'); |
178 | | - |
| 178 | + |
179 | 179 | foreach($threads as $t) { |
180 | 180 | $this->output->addHTML('<li><a href="#'.$this->anchorName($t).'">'.$t->subjectWithoutIncrement().'</a></li>'); |
181 | 181 | } |
182 | | - |
| 182 | + |
183 | 183 | $this->output->addHTML('</ul></div>'); |
184 | 184 | } |
185 | 185 | |
186 | 186 | function showArchiveWidget($threads) { |
187 | 187 | $threadlinks = $this->permalinksForThreads($threads); |
188 | 188 | $url = $this->talkpageUrl($this->title, 'talkpage_archive'); |
189 | | - |
| 189 | + |
190 | 190 | if ( count($threadlinks) > 0 ) { |
191 | 191 | $this->openDiv('lqt_archive_teaser'); |
192 | 192 | $this->output->addHTML('<h2 class="lqt_recently_archived">'.wfMsg('lqt_recently_archived').'</h2>'); |
193 | 193 | // $this->output->addHTML("<span class=\"lqt_browse_archive\">[<a href=\"$url\">".wfMsg('lqt_browse_archive_with_recent')."</a>]</span></h2>"); |
194 | 194 | $this->outputList('ul', '', '', $threadlinks); |
195 | 195 | $this->closeDiv(); |
196 | | - } else { |
| 196 | + } else { |
197 | 197 | } |
198 | 198 | } |
199 | | - |
| 199 | + |
200 | 200 | function show() { |
201 | 201 | global $wgHooks, $wgUser; |
202 | 202 | $wgHooks['SkinTemplateTabs'][] = array($this, 'customizeTabs'); |
— | — | @@ -203,7 +203,7 @@ |
204 | 204 | $this->output->setPageTitle( $this->title->getTalkpage()->getPrefixedText() ); |
205 | 205 | self::addJSandCSS(); |
206 | 206 | $article = new Article( $this->title ); |
207 | | - |
| 207 | + |
208 | 208 | if( $this->methodApplies('talkpage_sort_order') ) { |
209 | 209 | global $wgRequest; |
210 | 210 | $remember_sort_checked = $wgRequest->getVal('lqt_remember_sort') ? 'checked ' : ''; |
— | — | @@ -233,8 +233,8 @@ |
234 | 234 | $lqt_sort_newest_threads = wfMsg('lqt_sort_newest_threads'); |
235 | 235 | $lqt_sort_oldest_threads = wfMsg('lqt_sort_oldest_threads'); |
236 | 236 | $go=wfMsg('go'); |
237 | | - if($wgUser->isLoggedIn()) { |
238 | | - $remember_sort = |
| 237 | + if($wgUser->isLoggedIn()) { |
| 238 | + $remember_sort = |
239 | 239 | <<<HTML |
240 | 240 | <br /> |
241 | 241 | <label for="lqt_remember_sort_checkbox"> |
— | — | @@ -245,7 +245,7 @@ |
246 | 246 | $remember_sort = ''; |
247 | 247 | } |
248 | 248 | $this->openDiv('lqt_view_options'); |
249 | | - $this->output->addHTML( |
| 249 | + $this->output->addHTML( |
250 | 250 | |
251 | 251 | <<<HTML |
252 | 252 | <form name="lqt_sort" action="$form_action_url" method="post">$lqt_sorting_order |
— | — | @@ -268,7 +268,7 @@ |
269 | 269 | $threads = $this->queries->query('fresh'); |
270 | 270 | |
271 | 271 | $this->openDiv('lqt_toc_archive_wrapper'); |
272 | | - |
| 272 | + |
273 | 273 | $this->openDiv('lqt_archive_teaser_empty'); |
274 | 274 | $this->output->addHTML("<div class=\"lqt_browse_archive\"><a href=\"{$this->talkpageUrl($this->title, 'talkpage_archive')}\">".wfMsg('lqt_browse_archive_without_recent')."</a></div>"); |
275 | 275 | $this->closeDiv(); |
— | — | @@ -280,7 +280,7 @@ |
281 | 281 | $this->closeDiv(); |
282 | 282 | // Clear any floats |
283 | 283 | $this->output->addHTML('<br clear="all" />'); |
284 | | - |
| 284 | + |
285 | 285 | foreach($threads as $t) { |
286 | 286 | $this->showThread($t); |
287 | 287 | } |
— | — | @@ -441,7 +441,7 @@ |
442 | 442 | 'lqt_archive_end' => $months[$ne])) |
443 | 443 | . '">'.wfMsg ( 'lqt-newer' ).'»</a>'; |
444 | 444 | } |
445 | | - else { |
| 445 | + else { |
446 | 446 | $older = '<span class="lqt_newer_older_disabled" title="This link is disabled because you are viewing threads from all dates.">«'.wfMsg ( 'lqt-older' ).'</span>'; |
447 | 447 | $newer = '<span class="lqt_newer_older_disabled" title="This link is disabled because you are viewing threads from all dates.">'.wfMsg ( 'lqt-newer' ).'»</span>'; |
448 | 448 | } |
— | — | @@ -642,18 +642,18 @@ |
643 | 643 | function show() { |
644 | 644 | global $wgHooks, $wgOut, $wgTitle, $wgRequest; |
645 | 645 | $wgHooks['SkinTemplateTabs'][] = array($this, 'customizeTabs'); |
646 | | - |
| 646 | + |
647 | 647 | if( $wgRequest->getVal('action') === 'edit' ) { |
648 | 648 | $warn_bold = '<strong>' . wfMsg('lqt_header_warning_bold') . '</strong>'; |
649 | 649 | $warn_link = '<a href="'.$this->talkpageUrl($wgTitle, 'talkpage_new_thread').'">'. |
650 | 650 | wfMsg('lqt_header_warning_new_discussion').'</a>'; |
651 | 651 | $wgOut->addHTML('<p class="lqt_header_warning">' . |
652 | 652 | wfMsg('lqt_header_warning_before_big', $warn_bold, $warn_link) . |
653 | | - '<big>' . wfMsg('lqt_header_warning_big', $warn_bold, $warn_link) . '</big>' . |
| 653 | + '<big>' . wfMsg('lqt_header_warning_big', $warn_bold, $warn_link) . '</big>' . |
654 | 654 | wfMsg('lqt_header_warning_after_big', $warn_bold, $warn_link) . |
655 | 655 | '</p>'); |
656 | 656 | } |
657 | | - |
| 657 | + |
658 | 658 | return true; |
659 | 659 | } |
660 | 660 | } |
Index: trunk/extensions/LiquidThreads/LqtModel.php |
— | — | @@ -135,14 +135,14 @@ |
136 | 136 | |
137 | 137 | |
138 | 138 | class ThreadHistoryIterator extends ArrayIterator { |
139 | | - |
| 139 | + |
140 | 140 | function __construct($thread, $limit, $offset) { |
141 | 141 | $this->thread = $thread; |
142 | 142 | $this->limit = $limit; |
143 | 143 | $this->offset = $offset; |
144 | 144 | $this->loadRows(); |
145 | 145 | } |
146 | | - |
| 146 | + |
147 | 147 | private function loadRows() { |
148 | 148 | if( $this->offset == 0 ) { |
149 | 149 | $this->append( $this->thread ); |
— | — | @@ -150,7 +150,7 @@ |
151 | 151 | } else { |
152 | 152 | $this->offset -= 1; |
153 | 153 | } |
154 | | - |
| 154 | + |
155 | 155 | $dbr =& wfGetDB( DB_SLAVE ); |
156 | 156 | $res = $dbr->select( |
157 | 157 | 'historical_thread', |
— | — | @@ -188,7 +188,7 @@ |
189 | 189 | $this->changeUser = $t->changeUser; |
190 | 190 | $this->changeUserText = $t->changeUserText; |
191 | 191 | $this->editedness = $t->editedness; |
192 | | - |
| 192 | + |
193 | 193 | $this->replies = array(); |
194 | 194 | foreach ($t->replies as $r) { |
195 | 195 | $this->replies[] = new HistoricalThread($r); |
— | — | @@ -233,20 +233,20 @@ |
234 | 234 | |
235 | 235 | class Thread { |
236 | 236 | /* SCHEMA changes must be reflected here. */ |
237 | | - |
| 237 | + |
238 | 238 | /* ID references to other objects that are loaded on demand: */ |
239 | 239 | protected $rootId; |
240 | 240 | protected $articleId; |
241 | 241 | protected $summaryId; |
242 | 242 | protected $ancestorId; |
243 | 243 | protected $parentId; |
244 | | - |
| 244 | + |
245 | 245 | /* Actual objects loaded on demand from the above when accessors are called: */ |
246 | 246 | protected $root; |
247 | 247 | protected $article; |
248 | 248 | protected $summary; |
249 | 249 | protected $superthread; |
250 | | - |
| 250 | + |
251 | 251 | /* Subject page of the talkpage we're attached to: */ |
252 | 252 | protected $articleNamespace; |
253 | 253 | protected $articleTitle; |
— | — | @@ -254,45 +254,45 @@ |
255 | 255 | /* Timestamps: */ |
256 | 256 | protected $modified; |
257 | 257 | protected $created; |
258 | | - |
| 258 | + |
259 | 259 | protected $id; |
260 | 260 | protected $revisionNumber; |
261 | 261 | protected $type; |
262 | | - |
| 262 | + |
263 | 263 | /* Flag about who has edited or replied to this thread. */ |
264 | 264 | protected $editedness; |
265 | | - |
| 265 | + |
266 | 266 | /* Information about what changed in this revision. */ |
267 | 267 | protected $changeType; |
268 | 268 | protected $changeObject; |
269 | 269 | protected $changeComment; |
270 | 270 | protected $changeUser; |
271 | 271 | protected $changeUserText; |
272 | | - |
| 272 | + |
273 | 273 | /* Only used by $double to be saved into a historical thread. */ |
274 | 274 | protected $rootRevision; |
275 | | - |
| 275 | + |
276 | 276 | /* Copy of $this made when first loaded from database, to store the data |
277 | 277 | we will write to the history if a new revision is commited. */ |
278 | 278 | protected $double; |
279 | | - |
| 279 | + |
280 | 280 | protected $replies; |
281 | | - |
| 281 | + |
282 | 282 | function isHistorical() { |
283 | 283 | return false; |
284 | 284 | } |
285 | | - |
| 285 | + |
286 | 286 | function revisionNumber() { |
287 | 287 | return $this->revisionNumber; |
288 | 288 | } |
289 | | - |
| 289 | + |
290 | 290 | function atRevision($r) { |
291 | 291 | if ( $r == $this->revisionNumber() ) |
292 | 292 | return $this; |
293 | 293 | else |
294 | 294 | return HistoricalThread::withIdAtRevision($this->id(), $r); |
295 | 295 | } |
296 | | - |
| 296 | + |
297 | 297 | function historicalRevisions() { |
298 | 298 | $dbr =& wfGetDB( DB_SLAVE ); |
299 | 299 | $res = $dbr->select( |
— | — | @@ -306,7 +306,7 @@ |
307 | 307 | } |
308 | 308 | return $results; |
309 | 309 | } |
310 | | -/* |
| 310 | +/* |
311 | 311 | function ancestors() { |
312 | 312 | $id_clauses = array(); |
313 | 313 | foreach( explode('.', $this->path) as $id ) { |
— | — | @@ -315,17 +315,17 @@ |
316 | 316 | $where = implode(' OR ', $id_clauses); |
317 | 317 | return Threads::where($where); |
318 | 318 | } |
319 | | -*/ |
| 319 | +*/ |
320 | 320 | private function bumpRevisionsOnAncestors($change_type, $change_object, $change_reason, $timestamp) { |
321 | 321 | global $wgUser; // TODO global. |
322 | | - |
323 | | - $this->revisionNumber += 1; |
| 322 | + |
| 323 | + $this->revisionNumber += 1; |
324 | 324 | $this->setChangeType($change_type); |
325 | 325 | $this->setChangeObject($change_object); |
326 | 326 | $this->changeComment = $change_reason; |
327 | 327 | $this->changeUser = $wgUser->getID(); |
328 | 328 | $this->changeUserText = $wgUser->getName(); |
329 | | - |
| 329 | + |
330 | 330 | if( $this->hasSuperthread() ) |
331 | 331 | $this->superthread()->bumpRevisionsOnAncestors($change_type, $change_object, $change_reason, $timestamp); |
332 | 332 | $dbr =& wfGetDB( DB_MASTER ); |
— | — | @@ -340,7 +340,7 @@ |
341 | 341 | /* WHERE */ array( 'thread_id' => $this->id ), |
342 | 342 | __METHOD__); |
343 | 343 | } |
344 | | - |
| 344 | + |
345 | 345 | private static function setChangeOnDescendents($thread, $change_type, $change_object) { |
346 | 346 | // TODO this is ludicrously inefficient. |
347 | 347 | $thread->setChangeType($change_type); |
— | — | @@ -356,7 +356,7 @@ |
357 | 357 | self::setChangeOnDescendents($r, $change_type, $change_object); |
358 | 358 | return $thread; |
359 | 359 | } |
360 | | - |
| 360 | + |
361 | 361 | function commitRevision($change_type, $change_object = null, $reason = "") { |
362 | 362 | global $wgUser; // TODO global. |
363 | 363 | /* |
— | — | @@ -369,7 +369,7 @@ |
370 | 370 | |
371 | 371 | $this->bumpRevisionsOnAncestors($change_type, $change_object, $reason, wfTimestampNow()); |
372 | 372 | self::setChangeOnDescendents($this->topmostThread(), $change_type, $change_object); |
373 | | - |
| 373 | + |
374 | 374 | if( $change_type == Threads::CHANGE_REPLY_CREATED |
375 | 375 | && $this->editedness == Threads::EDITED_NEVER ) { |
376 | 376 | $this->editedness = Threads::EDITED_HAS_REPLY; |
— | — | @@ -381,9 +381,9 @@ |
382 | 382 | $this->editedness = Threads::EDITED_BY_AUTHOR; |
383 | 383 | } |
384 | 384 | } |
385 | | - |
| 385 | + |
386 | 386 | /* SCHEMA changes must be reflected here. */ |
387 | | - |
| 387 | + |
388 | 388 | $dbr =& wfGetDB( DB_MASTER ); |
389 | 389 | $res = $dbr->update( 'thread', |
390 | 390 | /* SET */array( 'thread_root' => $this->rootId, |
— | — | @@ -404,15 +404,15 @@ |
405 | 405 | ), |
406 | 406 | /* WHERE */ array( 'thread_id' => $this->id, ), |
407 | 407 | __METHOD__); |
408 | | - |
| 408 | + |
409 | 409 | NewMessages::writeMessageStateForUpdatedThread($this); |
410 | | - |
411 | | - |
| 410 | + |
| 411 | + |
412 | 412 | // RecentChange::notifyEdit( wfTimestampNow(), $this->root(), /*minor*/false, $wgUser, $summary, |
413 | 413 | // $lastRevision, $this->getModified(), $bot, '', $oldsize, $newsize, |
414 | 414 | // $revisionId ); |
415 | 415 | } |
416 | | - |
| 416 | + |
417 | 417 | function delete($reason) { |
418 | 418 | $this->type = Threads::TYPE_DELETED; |
419 | 419 | $this->revisionNumber += 1; |
— | — | @@ -423,13 +423,13 @@ |
424 | 424 | $this->revisionNumber += 1; |
425 | 425 | $this->commitRevision(Threads::CHANGE_UNDELETED, $this, $reason); |
426 | 426 | } |
427 | | - |
| 427 | + |
428 | 428 | function moveToSubjectPage($title, $reason, $leave_trace) { |
429 | 429 | $dbr =& wfGetDB( DB_MASTER ); |
430 | | - |
| 430 | + |
431 | 431 | $new_articleNamespace = $title->getNamespace(); |
432 | 432 | $new_articleTitle = $title->getDBkey(); |
433 | | - |
| 433 | + |
434 | 434 | foreach($this->replies as $r) { |
435 | 435 | $res = $dbr->update( 'thread', |
436 | 436 | /* SET */array( |
— | — | @@ -439,21 +439,21 @@ |
440 | 440 | /* WHERE */ array( 'thread_id' => $r->id(), ), |
441 | 441 | __METHOD__); |
442 | 442 | } |
443 | | - |
| 443 | + |
444 | 444 | $this->articleNamespace = $new_articleNamespace; |
445 | 445 | $this->articleTitle = $new_articleTitle; |
446 | 446 | $this->revisionNumber += 1; |
447 | 447 | $this->commitRevision(Threads::CHANGE_MOVED_TALKPAGE, null, $reason); |
448 | | - |
| 448 | + |
449 | 449 | if($leave_trace) { |
450 | 450 | $this->leaveTrace($reason); |
451 | 451 | } |
452 | 452 | } |
453 | | - |
| 453 | + |
454 | 454 | function leaveTrace($reason) { |
455 | 455 | /* Adapted from Title::moveToNewTitle. But now the new title exists on the old talkpage. */ |
456 | 456 | $dbw =& wfGetDB( DB_MASTER ); |
457 | | - |
| 457 | + |
458 | 458 | $mwRedir = MagicWord::get( 'redirect' ); |
459 | 459 | $redirectText = $mwRedir->getSynonym( 0 ) . ' [[' . $this->title()->getPrefixedText() . "]]\n"; |
460 | 460 | $redirectArticle = new Article( LqtView::incrementedTitle( $this->subjectWithoutIncrement(), |
— | — | @@ -488,12 +488,12 @@ |
489 | 489 | # The new title, and links to the new title, are purged in Article::onArticleCreate() |
490 | 490 | # $this-->purgeSquid(); |
491 | 491 | } |
492 | | - |
493 | | - |
494 | 492 | |
| 493 | + |
| 494 | + |
495 | 495 | function __construct($line, $children) { |
496 | 496 | /* SCHEMA changes must be reflected here. */ |
497 | | - |
| 497 | + |
498 | 498 | $this->id = $line->thread_id; |
499 | 499 | $this->rootId = $line->thread_root; |
500 | 500 | $this->articleNamespace = $line->thread_article_namespace; |
— | — | @@ -517,18 +517,18 @@ |
518 | 518 | $this->root->loadPageData($line); |
519 | 519 | $this->rootRevision = $this->root->mLatest; |
520 | 520 | } |
521 | | - |
| 521 | + |
522 | 522 | function initWithReplies( $children ) { |
523 | | - |
| 523 | + |
524 | 524 | $this->replies = $children; |
525 | | - |
| 525 | + |
526 | 526 | $this->double = clone $this; |
527 | 527 | } |
528 | | - |
| 528 | + |
529 | 529 | function __clone() { |
530 | 530 | // Cloning does not normally create a new array (but the clone keyword doesn't |
531 | 531 | // work on arrays -- go figure). |
532 | | - |
| 532 | + |
533 | 533 | // Update: this doesn't work for some reason, but why do we update the replies array |
534 | 534 | // in the first place after creating a new reply? |
535 | 535 | $new_array = array(); |
— | — | @@ -564,7 +564,7 @@ |
565 | 565 | function replies() { |
566 | 566 | return $this->replies; |
567 | 567 | } |
568 | | - |
| 568 | + |
569 | 569 | function setSuperthread($thread) { |
570 | 570 | $this->parentId = $thread->id(); |
571 | 571 | $this->ancestorId = $thread->ancestorId(); |
— | — | @@ -592,7 +592,7 @@ |
593 | 593 | else |
594 | 594 | return Threads::withId( $this->ancestorId ); |
595 | 595 | } |
596 | | - |
| 596 | + |
597 | 597 | function setArticle($a) { |
598 | 598 | $this->articleId = $a->getID(); |
599 | 599 | $this->articleNamespace = $a->getTitle()->getNamespace(); |
— | — | @@ -616,18 +616,18 @@ |
617 | 617 | function id() { |
618 | 618 | return $this->id; |
619 | 619 | } |
620 | | - |
| 620 | + |
621 | 621 | function ancestorId() { |
622 | 622 | return $this->ancestorId; |
623 | 623 | } |
624 | | - |
| 624 | + |
625 | 625 | function root() { |
626 | 626 | if ( !$this->rootId ) return null; |
627 | 627 | if ( !$this->root ) $this->root = new Post( Title::newFromID( $this->rootId ), |
628 | 628 | $this->rootRevision() ); |
629 | 629 | return $this->root; |
630 | 630 | } |
631 | | - |
| 631 | + |
632 | 632 | function setRootRevision($rr) { |
633 | 633 | if( (is_object($rr)) ) { |
634 | 634 | $this->rootRevision = $rr->getId(); |
— | — | @@ -635,30 +635,30 @@ |
636 | 636 | $this->rootRevision = $rr; |
637 | 637 | } |
638 | 638 | } |
639 | | - |
| 639 | + |
640 | 640 | function rootRevision() { |
641 | 641 | return $this->rootRevision; |
642 | 642 | } |
643 | | - |
| 643 | + |
644 | 644 | function editedness() { |
645 | 645 | return $this->editedness; |
646 | 646 | } |
647 | | - |
| 647 | + |
648 | 648 | function summary() { |
649 | 649 | if ( !$this->summaryId ) return null; |
650 | 650 | if ( !$this->summary ) $this->summary = new Post( Title::newFromID( $this->summaryId ) ); |
651 | 651 | return $this->summary; |
652 | 652 | } |
653 | | - |
| 653 | + |
654 | 654 | function hasSummary() { |
655 | 655 | return $this->summaryId != null; |
656 | 656 | } |
657 | | - |
| 657 | + |
658 | 658 | function setSummary( $post ) { |
659 | 659 | $this->summary = null; |
660 | 660 | $this->summaryId = $post->getID(); |
661 | 661 | } |
662 | | - |
| 662 | + |
663 | 663 | function title() { |
664 | 664 | return $this->root()->getTitle(); |
665 | 665 | } |
— | — | @@ -670,15 +670,15 @@ |
671 | 671 | else |
672 | 672 | return $matches; |
673 | 673 | } |
674 | | - |
| 674 | + |
675 | 675 | function wikilink() { |
676 | 676 | return $this->root()->getTitle()->getPrefixedText(); |
677 | 677 | } |
678 | | - |
| 678 | + |
679 | 679 | function subject() { |
680 | 680 | return $this->root()->getTitle()->getText(); |
681 | 681 | } |
682 | | - |
| 682 | + |
683 | 683 | function wikilinkWithoutIncrement() { |
684 | 684 | $tmp = $this->splitIncrementFromSubject($this->wikilink()); return $tmp[1]; |
685 | 685 | } |
— | — | @@ -686,11 +686,11 @@ |
687 | 687 | function subjectWithoutIncrement() { |
688 | 688 | $tmp = $this->splitIncrementFromSubject($this->subject()); return $tmp[1]; |
689 | 689 | } |
690 | | - |
| 690 | + |
691 | 691 | function increment() { |
692 | 692 | $tmp = $this->splitIncrementFromSubject($this->subject()); return $tmp[2]; |
693 | 693 | } |
694 | | - |
| 694 | + |
695 | 695 | function hasDistinctSubject() { |
696 | 696 | if( $this->hasSuperthread() ) { |
697 | 697 | return $this->superthread()->subjectWithoutIncrement() |
— | — | @@ -699,7 +699,7 @@ |
700 | 700 | return true; |
701 | 701 | } |
702 | 702 | } |
703 | | - |
| 703 | + |
704 | 704 | function hasSubthreads() { |
705 | 705 | return count($this->replies) != 0; |
706 | 706 | } |
— | — | @@ -711,19 +711,19 @@ |
712 | 712 | function modified() { |
713 | 713 | return $this->modified; |
714 | 714 | } |
715 | | - |
| 715 | + |
716 | 716 | function created() { |
717 | 717 | return $this->created; |
718 | 718 | } |
719 | | - |
| 719 | + |
720 | 720 | function type() { |
721 | 721 | return $this->type; |
722 | 722 | } |
723 | | - |
| 723 | + |
724 | 724 | function changeType() { |
725 | 725 | return $this->changeType; |
726 | 726 | } |
727 | | - |
| 727 | + |
728 | 728 | private function replyWithId($id) { |
729 | 729 | if( $this->id == $id ) return $this; |
730 | 730 | foreach ( $this->replies as $r ) { |
— | — | @@ -738,7 +738,7 @@ |
739 | 739 | function changeObject() { |
740 | 740 | return $this->replyWithId( $this->changeObject ); |
741 | 741 | } |
742 | | - |
| 742 | + |
743 | 743 | function setChangeType($t) { |
744 | 744 | if (in_array($t, Threads::$VALID_CHANGE_TYPES)) { |
745 | 745 | $this->changeType = $t; |
— | — | @@ -746,7 +746,7 @@ |
747 | 747 | throw new MWException( __METHOD__ . ": invalid changeType $t." ); |
748 | 748 | } |
749 | 749 | } |
750 | | - |
| 750 | + |
751 | 751 | function setChangeObject($o) { |
752 | 752 | # we assume $o to be a Thread. |
753 | 753 | if($o === null) { |
— | — | @@ -755,7 +755,7 @@ |
756 | 756 | $this->changeObject = $o->id(); |
757 | 757 | } |
758 | 758 | } |
759 | | - |
| 759 | + |
760 | 760 | function changeUser() { |
761 | 761 | if( $this->changeUser == 0 ) { |
762 | 762 | return User::newFromName($this->changeUserText, false); |
— | — | @@ -763,11 +763,11 @@ |
764 | 764 | return User::newFromId($this->changeUser); |
765 | 765 | } |
766 | 766 | } |
767 | | - |
| 767 | + |
768 | 768 | function changeComment() { |
769 | 769 | return $this->changeComment; |
770 | 770 | } |
771 | | - |
| 771 | + |
772 | 772 | function redirectThread() { |
773 | 773 | $rev = Revision::newFromId($this->root()->getLatest()); |
774 | 774 | $rtitle = Title::newFromRedirect($rev->getRawText()); |
— | — | @@ -775,7 +775,7 @@ |
776 | 776 | $rthread = Threads::withRoot(new Article($rtitle)); |
777 | 777 | return $rthread; |
778 | 778 | } |
779 | | - |
| 779 | + |
780 | 780 | // Called from hook in Title::isProtected. |
781 | 781 | static function getRestrictionsForTitle($title, $action, &$result) { |
782 | 782 | $thread = Threads::withRoot(new Post($title)); |
— | — | @@ -784,7 +784,7 @@ |
785 | 785 | else |
786 | 786 | return true; // not a thread; do normal protection check. |
787 | 787 | } |
788 | | - |
| 788 | + |
789 | 789 | // This only makes sense when called from the hook, because it uses the hook's |
790 | 790 | // default behavior to check whether this thread itself is protected, so you'll |
791 | 791 | // get false negatives if you use it from some other context. |
— | — | @@ -794,7 +794,7 @@ |
795 | 795 | } else { |
796 | 796 | $parent_restrictions = $this->article()->getTitle()->getTalkPage()->getRestrictions($action); |
797 | 797 | } |
798 | | - |
| 798 | + |
799 | 799 | // TODO this may not be the same as asking "are the parent restrictions more restrictive than |
800 | 800 | // our own restrictions?", which is what we really want. |
801 | 801 | if( count($parent_restrictions) == 0 ) { |
— | — | @@ -803,7 +803,7 @@ |
804 | 804 | $result = $parent_restrictions; |
805 | 805 | return false; |
806 | 806 | } |
807 | | - |
| 807 | + |
808 | 808 | } |
809 | 809 | } |
810 | 810 | |
— | — | @@ -815,7 +815,7 @@ |
816 | 816 | const TYPE_MOVED = 1; |
817 | 817 | const TYPE_DELETED = 2; |
818 | 818 | static $VALID_TYPES = array(self::TYPE_NORMAL, self::TYPE_MOVED, self::TYPE_DELETED); |
819 | | - |
| 819 | + |
820 | 820 | const CHANGE_NEW_THREAD = 0; |
821 | 821 | const CHANGE_REPLY_CREATED = 1; |
822 | 822 | const CHANGE_EDITED_ROOT = 2; |
— | — | @@ -826,7 +826,7 @@ |
827 | 827 | static $VALID_CHANGE_TYPES = array(self::CHANGE_EDITED_SUMMARY, self::CHANGE_EDITED_ROOT, |
828 | 828 | self::CHANGE_REPLY_CREATED, self::CHANGE_NEW_THREAD, self::CHANGE_DELETED, self::CHANGE_UNDELETED, |
829 | 829 | self::CHANGE_MOVED_TALKPAGE); |
830 | | - |
| 830 | + |
831 | 831 | // Possible values of Thread->editedness. |
832 | 832 | const EDITED_NEVER = 0; |
833 | 833 | const EDITED_HAS_REPLY = 1; |
— | — | @@ -835,25 +835,25 @@ |
836 | 836 | |
837 | 837 | static $cache_by_root = array(); |
838 | 838 | static $cache_by_id = array(); |
839 | | - |
| 839 | + |
840 | 840 | static function newThread( $root, $article, $superthread = null, $type = self::TYPE_NORMAL ) { |
841 | 841 | // SCHEMA changes must be reflected here. |
842 | 842 | // TODO: It's dumb that the commitRevision code isn't used here. |
843 | 843 | |
844 | 844 | $dbr =& wfGetDB( DB_MASTER ); |
845 | | - |
| 845 | + |
846 | 846 | if ( !in_array($type, self::$VALID_TYPES) ) { |
847 | 847 | throw new MWException(__METHOD__ . ": invalid type $type."); |
848 | 848 | } |
849 | | - |
| 849 | + |
850 | 850 | if ($superthread) { |
851 | 851 | $change_type = self::CHANGE_REPLY_CREATED; |
852 | 852 | } else { |
853 | 853 | $change_type = self::CHANGE_NEW_THREAD; |
854 | 854 | } |
855 | | - |
| 855 | + |
856 | 856 | global $wgUser; // TODO global. |
857 | | - |
| 857 | + |
858 | 858 | $timestamp = wfTimestampNow(); |
859 | 859 | |
860 | 860 | $res = $dbr->insert('thread', |
— | — | @@ -870,9 +870,9 @@ |
871 | 871 | 'thread_type' => $type, |
872 | 872 | 'thread_editedness' => self::EDITED_NEVER), |
873 | 873 | __METHOD__); |
874 | | - |
| 874 | + |
875 | 875 | $newid = $dbr->insertId(); |
876 | | - |
| 876 | + |
877 | 877 | if( $superthread ) { |
878 | 878 | $ancestor = $superthread->ancestorId(); |
879 | 879 | $change_object_clause = 'thread_change_object = ' . $newid; |
— | — | @@ -885,17 +885,17 @@ |
886 | 886 | $change_object_clause ), |
887 | 887 | /* WHERE */ array( 'thread_id' => $newid, ), |
888 | 888 | __METHOD__); |
889 | | - |
| 889 | + |
890 | 890 | // TODO we could avoid a query here. |
891 | 891 | $newthread = Threads::withId($newid); |
892 | 892 | if($superthread) { |
893 | 893 | $superthread->addReply( $newthread ); |
894 | 894 | } |
895 | | - |
| 895 | + |
896 | 896 | self::createTalkpageIfNeeded($article); |
897 | | - |
| 897 | + |
898 | 898 | NewMessages::writeMessageStateForUpdatedThread($newthread); |
899 | | - |
| 899 | + |
900 | 900 | return $newthread; |
901 | 901 | } |
902 | 902 | |
— | — | @@ -909,23 +909,23 @@ |
910 | 910 | if( ! $talkpage->exists() ) { |
911 | 911 | try { |
912 | 912 | $talkpage->doEdit( "", wfMsg('lqt_talkpage_autocreate_summary'), EDIT_NEW | EDIT_SUPPRESS_RC ); |
913 | | - |
| 913 | + |
914 | 914 | } catch( DBQueryError $e ) { |
915 | 915 | // The article already existed by now. No need to do anything. |
916 | 916 | wfDebug(__METHOD__ . ": Article already existed by the time we tried to create it."); |
917 | 917 | } |
918 | 918 | } |
919 | 919 | } |
920 | | - |
| 920 | + |
921 | 921 | static function where( $where, $options = array(), $extra_tables = array(), $joins = "" ) { |
922 | 922 | global $wgDBprefix; |
923 | 923 | $dbr = wfGetDB( DB_SLAVE ); |
924 | 924 | if ( is_array($where) ) $where = $dbr->makeList( $where, LIST_AND ); |
925 | 925 | if ( is_array($options) ) $options = implode(',', $options); |
926 | | - |
| 926 | + |
927 | 927 | if( is_array($extra_tables) && count($extra_tables) != 0 ) { |
928 | 928 | if(!empty($wgDBprefix)) { |
929 | | - foreach($extra_tables as $tablekey=>$extra_table) |
| 929 | + foreach($extra_tables as $tablekey=>$extra_table) |
930 | 930 | $extra_tables[$tablekey]=$wgDBprefix.$extra_table; |
931 | 931 | } |
932 | 932 | $tables = implode(',', $extra_tables) . ', '; |
— | — | @@ -934,7 +934,7 @@ |
935 | 935 | } else { |
936 | 936 | $tables = ""; |
937 | 937 | } |
938 | | - |
| 938 | + |
939 | 939 | /* Select the client's threads, AND all their children. |
940 | 940 | The ones the client actually asked for are marked with root_test. |
941 | 941 | In theory we could also grab the page and revision data, to avoid having |
— | — | @@ -952,7 +952,7 @@ |
953 | 953 | $options |
954 | 954 | SQL; |
955 | 955 | |
956 | | - $res = $dbr->query($sql); |
| 956 | + $res = $dbr->query($sql); |
957 | 957 | |
958 | 958 | $threads = array(); |
959 | 959 | $top_level_threads = array(); |
— | — | @@ -970,7 +970,7 @@ |
971 | 971 | $thread_children[$line->thread_parent][] = $new_thread; |
972 | 972 | } |
973 | 973 | } |
974 | | - |
| 974 | + |
975 | 975 | /* |
976 | 976 | The two clauses of the above loop used to be orthogonal, instead of exclusive. The reason |
977 | 977 | they are exclusive is not that 'is_root' indicates a top-level thread -- 'is_root' indicates |
— | — | @@ -986,7 +986,7 @@ |
987 | 987 | } else { |
988 | 988 | $thread->initWithReplies( array() ); |
989 | 989 | } |
990 | | - |
| 990 | + |
991 | 991 | self::$cache_by_root[$thread->root()->getID()] = $thread; |
992 | 992 | self::$cache_by_id[$thread->id()] = $thread; |
993 | 993 | } |
— | — | @@ -998,7 +998,7 @@ |
999 | 999 | echo("Corrupt liquidthreads database: $msg"); |
1000 | 1000 | die(); |
1001 | 1001 | } |
1002 | | - |
| 1002 | + |
1003 | 1003 | private static function assertSingularity( $threads, $attribute, $value ) { |
1004 | 1004 | if( count($threads) == 0 ) { return null; } |
1005 | 1005 | if( count($threads) == 1 ) { return $threads[0]; } |
— | — | @@ -1028,12 +1028,12 @@ |
1029 | 1029 | $ts = Threads::where( array('thread.thread_id' => $id ) ); |
1030 | 1030 | return self::assertSingularity($ts, 'thread_id', $id); |
1031 | 1031 | } |
1032 | | - |
| 1032 | + |
1033 | 1033 | static function withSummary( $article ) { |
1034 | 1034 | $ts = Threads::where( array('thread.thread_summary_page' => $article->getId())); |
1035 | 1035 | return self::assertSingularity($ts, 'thread_summary_page', $article->getId()); |
1036 | 1036 | } |
1037 | | - |
| 1037 | + |
1038 | 1038 | /** |
1039 | 1039 | * Horrible, horrible! |
1040 | 1040 | * List of months in which there are >0 threads, suitable for threadsOfArticleInMonth. */ |
— | — | @@ -1048,7 +1048,7 @@ |
1049 | 1049 | } |
1050 | 1050 | return $months; |
1051 | 1051 | } |
1052 | | - |
| 1052 | + |
1053 | 1053 | static function articleClause($article) { |
1054 | 1054 | $dbr = wfGetDB(DB_SLAVE); |
1055 | 1055 | $q_article= $dbr->addQuotes($article->getTitle()->getDBkey()); |
— | — | @@ -1057,7 +1057,7 @@ |
1058 | 1058 | AND thread.thread_article_namespace = {$article->getTitle()->getNamespace()}) |
1059 | 1059 | SQL; |
1060 | 1060 | } |
1061 | | - |
| 1061 | + |
1062 | 1062 | static function topLevelClause() { |
1063 | 1063 | return 'thread.thread_parent is null'; |
1064 | 1064 | } |
— | — | @@ -1067,15 +1067,15 @@ |
1068 | 1068 | |
1069 | 1069 | class QueryGroup { |
1070 | 1070 | protected $queries; |
1071 | | - |
| 1071 | + |
1072 | 1072 | function __construct() { |
1073 | 1073 | $this->queries = array(); |
1074 | 1074 | } |
1075 | | - |
| 1075 | + |
1076 | 1076 | function addQuery( $name, $where, $options = array(), $extra_tables = array() ) { |
1077 | 1077 | $this->queries[$name] = array($where, $options, $extra_tables); |
1078 | 1078 | } |
1079 | | - |
| 1079 | + |
1080 | 1080 | function extendQuery( $original, $newname, $where, $options = array(), $extra_tables=array() ) { |
1081 | 1081 | if (!array_key_exists($original,$this->queries)) return; |
1082 | 1082 | $q = $this->queries[$original]; |
— | — | @@ -1083,11 +1083,11 @@ |
1084 | 1084 | array_merge($q[1], $options), |
1085 | 1085 | array_merge($q[2], $extra_tables) ); |
1086 | 1086 | } |
1087 | | - |
| 1087 | + |
1088 | 1088 | function deleteQuery( $name ) { |
1089 | 1089 | unset ($this->queries[$name]); |
1090 | 1090 | } |
1091 | | - |
| 1091 | + |
1092 | 1092 | function query($name) { |
1093 | 1093 | if ( !array_key_exists($name,$this->queries) ) return array(); |
1094 | 1094 | list($where, $options, $extra_tables) = $this->queries[$name]; |
— | — | @@ -1097,27 +1097,27 @@ |
1098 | 1098 | |
1099 | 1099 | |
1100 | 1100 | class NewMessages { |
1101 | | - |
| 1101 | + |
1102 | 1102 | static function markThreadAsUnreadByUser($thread, $user) { |
1103 | 1103 | self::writeUserMessageState($thread, $user, null); |
1104 | 1104 | } |
1105 | | - |
| 1105 | + |
1106 | 1106 | static function markThreadAsReadByUser($thread, $user) { |
1107 | | - self::writeUserMessageState($thread, $user, wfTimestampNow()); |
| 1107 | + self::writeUserMessageState($thread, $user, wfTimestampNow()); |
1108 | 1108 | } |
1109 | | - |
| 1109 | + |
1110 | 1110 | private static function writeUserMessageState($thread, $user, $timestamp) { |
1111 | 1111 | global $wgDBprefix; |
1112 | 1112 | if( is_object($thread) ) $thread_id = $thread->id(); |
1113 | 1113 | else if( is_integer($thread) ) $thread_id = $thread; |
1114 | 1114 | else throw new MWException("writeUserMessageState expected Thread or integer but got $thread"); |
1115 | | - |
| 1115 | + |
1116 | 1116 | if( is_object($user) ) $user_id = $user->getID(); |
1117 | 1117 | else if( is_integer($user) ) $user_id = $user; |
1118 | 1118 | else throw new MWException("writeUserMessageState expected User or integer but got $user"); |
1119 | | - |
| 1119 | + |
1120 | 1120 | if ( $timestamp === null ) $timestamp = "NULL"; |
1121 | | - |
| 1121 | + |
1122 | 1122 | // use query() directly to pass in 'true' for don't-die-on-errors. |
1123 | 1123 | $dbr =& wfGetDB( DB_MASTER ); |
1124 | 1124 | $success = $dbr->query("insert into {$wgDBprefix}user_message_state values ($user_id, $thread_id, $timestamp)", |
— | — | @@ -1131,7 +1131,7 @@ |
1132 | 1132 | } |
1133 | 1133 | } |
1134 | 1134 | |
1135 | | - /** |
| 1135 | + /** |
1136 | 1136 | * Write a user_message_state for each user who is watching the thread. |
1137 | 1137 | * If the thread is on a user's talkpage, set that user's newtalk. |
1138 | 1138 | */ |
— | — | @@ -1147,15 +1147,15 @@ |
1148 | 1148 | $user->setNewtalk(true); |
1149 | 1149 | } |
1150 | 1150 | } |
1151 | | - |
| 1151 | + |
1152 | 1152 | $dbw =& wfGetDB( DB_MASTER ); |
1153 | | - |
| 1153 | + |
1154 | 1154 | $talkpage_t = $t->article()->getTitle(); |
1155 | 1155 | $root_t = $t->root()->getTitle(); |
1156 | | - |
| 1156 | + |
1157 | 1157 | $q_talkpage_t = $dbw->addQuotes($talkpage_t->getDBkey()); |
1158 | 1158 | $q_root_t = $dbw->addQuotes($root_t->getDBkey()); |
1159 | | - |
| 1159 | + |
1160 | 1160 | // Select any applicable watchlist entries for the thread. |
1161 | 1161 | $where_clause = <<<SQL |
1162 | 1162 | ( |
— | — | @@ -1163,14 +1163,14 @@ |
1164 | 1164 | or (wl_namespace = {$root_t->getNamespace()} and wl_title = $q_root_t ) |
1165 | 1165 | ) |
1166 | 1166 | SQL; |
1167 | | - |
| 1167 | + |
1168 | 1168 | // it sucks to not have 'on duplicate key update'. first update users who already have a ums for this thread |
1169 | 1169 | // and who have already read it, by setting their state to unread. |
1170 | 1170 | $dbw->query("update {$wgDBprefix}user_message_state, {$wgDBprefix}watchlist set ums_read_timestamp = null where ums_user = wl_user and ums_thread = {$t->id()} and $where_clause"); |
1171 | | - |
| 1171 | + |
1172 | 1172 | $dbw->query("insert ignore into {$wgDBprefix}user_message_state (ums_user, ums_thread) select user_id, {$t->id()} from {$wgDBprefix}user, {$wgDBprefix}watchlist where user_id = wl_user and $where_clause;"); |
1173 | 1173 | } |
1174 | | - |
| 1174 | + |
1175 | 1175 | static function newUserMessages($user) { |
1176 | 1176 | global $wgDBprefix; |
1177 | 1177 | return Threads::where( array('ums_read_timestamp is null', |
— | — | @@ -1187,5 +1187,3 @@ |
1188 | 1188 | } |
1189 | 1189 | |
1190 | 1190 | } |
1191 | | - |
1192 | | -?> |
Index: trunk/extensions/LiquidThreads/lqt.css |
— | — | @@ -196,7 +196,7 @@ |
197 | 197 | .lqt_archive_listing th { |
198 | 198 | text-align: left; |
199 | 199 | vertical-align: baseline; |
200 | | - font-weight: bold; |
| 200 | + font-weight: bold; |
201 | 201 | border-bottom: 1px solid #ddd; |
202 | 202 | } |
203 | 203 | |
— | — | @@ -330,7 +330,7 @@ |
331 | 331 | padding-left: 0.6em; |
332 | 332 | } |
333 | 333 | |
334 | | -#lqt_subject_field { |
| 334 | +#lqt_subject_field { |
335 | 335 | margin-top: 0.7em; |
336 | 336 | margin-bottom: 0.5em; |
337 | 337 | width: 80%; |
— | — | @@ -449,4 +449,4 @@ |
450 | 450 | } |
451 | 451 | .lqt_remember_sort { |
452 | 452 | font-size:85%; |
453 | | -} |
\ No newline at end of file |
| 453 | +} |
Index: trunk/extensions/LiquidThreads/lqt.js |
— | — | @@ -1,20 +1,20 @@ |
2 | 2 | // http://onlinetools.org/articles/unobtrusivejavascript/chapter4.html |
3 | | -function lqt_add_event(obj, evType, fn){ |
4 | | - if (obj.addEventListener){ |
5 | | - obj.addEventListener(evType, fn, false); |
6 | | - return true; |
7 | | - } else if (obj.attachEvent){ |
8 | | - var r = obj.attachEvent("on"+evType, fn); |
9 | | - return r; |
10 | | - } else { |
11 | | - return false; |
12 | | - } |
| 3 | +function lqt_add_event(obj, evType, fn){ |
| 4 | + if (obj.addEventListener){ |
| 5 | + obj.addEventListener(evType, fn, false); |
| 6 | + return true; |
| 7 | + } else if (obj.attachEvent){ |
| 8 | + var r = obj.attachEvent("on"+evType, fn); |
| 9 | + return r; |
| 10 | + } else { |
| 11 | + return false; |
| 12 | + } |
13 | 13 | } |
14 | 14 | |
15 | 15 | var LqtDateRangeRectifier = function( startsel, endsel ) { |
16 | 16 | this.startsel = startsel; |
17 | 17 | this.endsel = endsel; |
18 | | - |
| 18 | + |
19 | 19 | this.oldstart = this.startsel.selectedIndex; |
20 | 20 | this.oldend = this.endsel.selectedIndex; |
21 | 21 | |
— | — | @@ -60,8 +60,8 @@ |
61 | 61 | if ( !filter.checked ) { |
62 | 62 | start.selectedIndex = end.selectedIndex = 0; |
63 | 63 | } |
64 | | - |
| 64 | + |
65 | 65 | } |
66 | 66 | } |
67 | 67 | |
68 | | -addOnloadHook(lqt_on_load); |
\ No newline at end of file |
| 68 | +addOnloadHook(lqt_on_load); |
Index: trunk/extensions/LiquidThreads/LqtBaseView.php |
— | — | @@ -70,7 +70,7 @@ |
71 | 71 | // We are given a talkpage article and title. Find the associated |
72 | 72 | // non-talk article and pass that to the view. |
73 | 73 | $article = new Article($title->getSubjectPage()); |
74 | | - |
| 74 | + |
75 | 75 | if( $title->getSubjectPage()->getNamespace() == NS_LQT_THREAD ) { |
76 | 76 | // Threads don't have talk pages; redirect to the thread page. |
77 | 77 | $output->redirect($title->getSubjectPage()->getFullUrl()); |
— | — | @@ -98,7 +98,7 @@ |
99 | 99 | } |
100 | 100 | |
101 | 101 | static function threadPermalinkMain(&$output, &$article, &$title, &$user, &$request) { |
102 | | - |
| 102 | + |
103 | 103 | $action = $request->getVal('action'); |
104 | 104 | $lqt_method = $request->getVal('lqt_method'); |
105 | 105 | |
— | — | @@ -128,13 +128,13 @@ |
129 | 129 | $view = new $viewname( $output, $article, $title, $user, $request ); |
130 | 130 | return $view->show(); |
131 | 131 | } |
132 | | - |
| 132 | + |
133 | 133 | static function threadSummaryMain(&$output, &$article, &$title, &$user, &$request) { |
134 | 134 | $viewname = self::$views['SummaryPageView']; |
135 | 135 | $view = new $viewname( $output, $article, $title, $user, $request ); |
136 | 136 | return $view->show(); |
137 | 137 | } |
138 | | - |
| 138 | + |
139 | 139 | /** |
140 | 140 | * If the page we recieve is a Liquid Threads page of any kind, process it |
141 | 141 | * as needed and return True. If it's a normal, non-liquid page, return false. |
— | — | @@ -149,28 +149,28 @@ |
150 | 150 | } |
151 | 151 | return true; |
152 | 152 | } |
153 | | - |
| 153 | + |
154 | 154 | static function onPageMove( $movepage, $ot, $nt ) { |
155 | 155 | // We are being invoked on the subject page, not the talk page. |
156 | | - |
| 156 | + |
157 | 157 | $threads = Threads::where( array( Threads::articleClause(new Article($ot)), |
158 | 158 | Threads::topLevelClause() )); |
159 | | - |
| 159 | + |
160 | 160 | foreach ($threads as $t) { |
161 | 161 | $t->moveToSubjectPage( $nt, false ); |
162 | 162 | } |
163 | | - |
| 163 | + |
164 | 164 | return true; |
165 | 165 | } |
166 | | - |
| 166 | + |
167 | 167 | static function makeLinkObj( &$returnValue, &$linker, $nt, $text, $query, $trail, $prefix ) { |
168 | 168 | if( ! $nt->isTalkPage() ) |
169 | 169 | return true; |
170 | | - |
| 170 | + |
171 | 171 | // Talkpages with headers. |
172 | 172 | if( $nt->getArticleID() != 0 ) |
173 | 173 | return true; |
174 | | - |
| 174 | + |
175 | 175 | // Talkpages without headers -- check existance of threads. |
176 | 176 | $article = new Article($nt->getSubjectPage()); |
177 | 177 | $threads = Threads::where(Threads::articleClause($article), "LIMIT 1"); |
— | — | @@ -183,14 +183,14 @@ |
184 | 184 | $text = htmlspecialchars( $nt->getPrefixedText() ); |
185 | 185 | $style = $linker->getInternalLinkAttributesObj( $nt, $text, "yes" ); |
186 | 186 | list( $inside, $trail ) = Linker::splitTrail( $trail ); |
187 | | - $returnValue = "<a href=\"{$url}\"{$style}>{$prefix}{$text}{$inside}</a>{$trail}"; |
| 187 | + $returnValue = "<a href=\"{$url}\"{$style}>{$prefix}{$text}{$inside}</a>{$trail}"; |
188 | 188 | } |
189 | 189 | else { |
190 | 190 | $returnValue = $linker->makeKnownLinkObj( $nt, $text, $query, $trail, $prefix ); |
191 | 191 | } |
192 | 192 | return false; |
193 | 193 | } |
194 | | - |
| 194 | + |
195 | 195 | // One major place that doesn't use makeLinkObj is the tabs. So override known/unknown there too. |
196 | 196 | static function tabAction(&$skintemplate, $title, $message, $selected, $checkEdit, |
197 | 197 | &$classes, &$query, &$text, &$result) { |
— | — | @@ -199,7 +199,7 @@ |
200 | 200 | if( $title->getArticleID() != 0 ) { |
201 | 201 | $query = ""; |
202 | 202 | return true; |
203 | | - } |
| 203 | + } |
204 | 204 | // It's a talkpage without a header. Get rid of action=edit always, |
205 | 205 | // color as apropriate. |
206 | 206 | $query = ""; |
— | — | @@ -212,7 +212,7 @@ |
213 | 213 | } |
214 | 214 | return true; |
215 | 215 | } |
216 | | - |
| 216 | + |
217 | 217 | static function changesListArticleLink(&$changeslist, &$articlelink, &$s, &$rc, $unpatrolled, $watched) { |
218 | 218 | $thread = null; |
219 | 219 | if( $rc->getTitle()->getNamespace() == NS_LQT_THREAD ) { |
— | — | @@ -234,7 +234,7 @@ |
235 | 235 | } |
236 | 236 | return true; |
237 | 237 | } |
238 | | - |
| 238 | + |
239 | 239 | static function setNewtalkHTML($skintemplate, $tpl) { |
240 | 240 | global $wgUser, $wgTitle, $wgOut; |
241 | 241 | $newmsg_t = SpecialPage::getPage('Newmessages')->getTitle(); |
— | — | @@ -256,29 +256,29 @@ |
257 | 257 | } |
258 | 258 | } |
259 | 259 | |
260 | | - |
| 260 | + |
261 | 261 | class LqtView { |
262 | 262 | protected $article; |
263 | 263 | protected $output; |
264 | 264 | protected $user; |
265 | 265 | protected $title; |
266 | 266 | protected $request; |
267 | | - |
| 267 | + |
268 | 268 | protected $headerLevel = 2; /* h1, h2, h3, etc. */ |
269 | 269 | protected $maxIndentationLevel = 4; |
270 | 270 | protected $lastUnindentedSuperthread; |
271 | | - |
| 271 | + |
272 | 272 | protected $user_colors; |
273 | 273 | protected $user_color_index; |
274 | 274 | const number_of_user_colors = 6; |
275 | 275 | |
276 | 276 | protected $queries; |
277 | | - |
| 277 | + |
278 | 278 | public $archive_start_days = 14; |
279 | 279 | public $archive_recent_days = 5; |
280 | 280 | |
281 | 281 | protected $sort_order=LQT_NEWEST_CHANGES; |
282 | | - |
| 282 | + |
283 | 283 | function __construct(&$output, &$article, &$title, &$user, &$request) { |
284 | 284 | $this->article = $article; |
285 | 285 | $this->output = $output; |
— | — | @@ -289,11 +289,11 @@ |
290 | 290 | $this->user_color_index = 1; |
291 | 291 | $this->queries = $this->initializeQueries(); |
292 | 292 | } |
293 | | - |
| 293 | + |
294 | 294 | function setHeaderLevel($int) { |
295 | 295 | $this->headerLevel = $int; |
296 | 296 | } |
297 | | - |
| 297 | + |
298 | 298 | function initializeQueries() { |
299 | 299 | |
300 | 300 | if( $this->methodApplies('talkpage_sort_order') ) { |
— | — | @@ -335,7 +335,7 @@ |
336 | 336 | array($article_clause, |
337 | 337 | 'thread.thread_parent is null', |
338 | 338 | '(thread.thread_modified >= ' . $startdate->text() . |
339 | | - ' OR (thread.thread_summary_page is NULL' . |
| 339 | + ' OR (thread.thread_summary_page is NULL' . |
340 | 340 | ' AND thread.thread_type='.Threads::TYPE_NORMAL.'))'), |
341 | 341 | array($sort_clause)); |
342 | 342 | $g->addQuery('archived', |
— | — | @@ -355,12 +355,12 @@ |
356 | 356 | } |
357 | 357 | |
358 | 358 | static protected $occupied_titles = array(); |
359 | | - |
| 359 | + |
360 | 360 | /************************* |
361 | 361 | * (1) linking to liquidthreads pages and |
362 | 362 | * (2) figuring out what page you're on and what you need to do. |
363 | 363 | *************************/ |
364 | | - |
| 364 | + |
365 | 365 | static function queryStringFromArray( $vars ) { |
366 | 366 | $q = ''; |
367 | 367 | if ( $vars && count( $vars ) != 0 ) { |
— | — | @@ -390,7 +390,7 @@ |
391 | 391 | if ( is_array($query) ) $query = self::queryStringFromArray($query); |
392 | 392 | return $thread->root()->getTitle()->getFullUrl($query); |
393 | 393 | } |
394 | | - |
| 394 | + |
395 | 395 | static function permalinkUrlWithDiff( $thread ) { |
396 | 396 | $changed_thread = $thread->changeObject(); |
397 | 397 | $curr_rev_id = $changed_thread->rootRevision(); |
— | — | @@ -410,8 +410,8 @@ |
411 | 411 | } |
412 | 412 | return $title->getFullURL( $query ) . ($operand && $includeFragment ? "#lqt_thread_{$operand->id()}" : ""); |
413 | 413 | } |
414 | | - |
415 | | - |
| 414 | + |
| 415 | + |
416 | 416 | /** |
417 | 417 | * Return a URL for the current page, including Title and query vars, |
418 | 418 | * with the given replacements made. |
— | — | @@ -480,7 +480,7 @@ |
481 | 481 | $this->showEditingFormInGeneral( null, 'summarize', $thread ); |
482 | 482 | } |
483 | 483 | |
484 | | - private function showEditingFormInGeneral( $thread, $edit_type, $edit_applies_to ) { |
| 484 | + private function showEditingFormInGeneral( $thread, $edit_type, $edit_applies_to ) { |
485 | 485 | /* |
486 | 486 | EditPage needs an Article. If there isn't a real one, as for new posts, |
487 | 487 | replies, and new summaries, we need to generate a title. Auto-generated |
— | — | @@ -504,14 +504,14 @@ |
505 | 505 | } else { |
506 | 506 | $article = $thread->root(); |
507 | 507 | } |
508 | | - |
| 508 | + |
509 | 509 | $e = new EditPage($article); |
510 | | - |
| 510 | + |
511 | 511 | $e->suppressIntro = true; |
512 | 512 | $e->editFormTextBeforeContent .= |
513 | 513 | $this->perpetuate('lqt_method', 'hidden') . |
514 | 514 | $this->perpetuate('lqt_operand', 'hidden'); |
515 | | - |
| 515 | + |
516 | 516 | if ( $edit_type=='new' || ($thread && !$thread->hasSuperthread()) ) { |
517 | 517 | // This is a top-level post; show the subject line. |
518 | 518 | $db_subject = $thread ? $thread->subjectWithoutIncrement() : ''; |
— | — | @@ -526,7 +526,7 @@ |
527 | 527 | $e->edit(); |
528 | 528 | |
529 | 529 | // Override what happens in EditPage::showEditForm, called from $e->edit(): |
530 | | -// $wgOut->setArticleRelated( false ); |
| 530 | +// $wgOut->setArticleRelated( false ); |
531 | 531 | $this->output->setArticleFlag( false ); |
532 | 532 | |
533 | 533 | // For replies and new posts, insert the associated thread object into the DB. |
— | — | @@ -538,12 +538,12 @@ |
539 | 539 | $thread = Threads::newThread( $article, $this->article, null, $e->summary ); |
540 | 540 | } |
541 | 541 | } |
542 | | - |
| 542 | + |
543 | 543 | if ($edit_type == 'summarize' && $e->didSave) { |
544 | 544 | $edit_applies_to->setSummary( $article ); |
545 | 545 | $edit_applies_to->commitRevision(Threads::CHANGE_EDITED_SUMMARY, $edit_applies_to, $e->summary); |
546 | 546 | } |
547 | | - |
| 547 | + |
548 | 548 | // Move the thread and replies if subject changed. |
549 | 549 | if( $edit_type == 'editExisting' && $e->didSave ) { |
550 | 550 | $subject = $this->request->getVal('lqt_subject_field', ''); |
— | — | @@ -555,7 +555,7 @@ |
556 | 556 | $thread->setRootRevision( Revision::newFromTitle($thread->root()->getTitle()) ); |
557 | 557 | $thread->commitRevision( Threads::CHANGE_EDITED_ROOT, $thread, $e->summary ); |
558 | 558 | } |
559 | | - |
| 559 | + |
560 | 560 | // A redirect without $e->didSave will happen if the new text is blank (EditPage::attemptSave). |
561 | 561 | // This results in a new Thread object not being created for replies and new discussions, |
562 | 562 | // so $thread is null. In that case, just allow editpage to redirect back to the talk page. |
— | — | @@ -566,7 +566,7 @@ |
567 | 567 | $this->output->redirect( $edit_applies_to->title()->getFullURL() . '#' . 'lqt_thread_' . $edit_applies_to->id() ); |
568 | 568 | } |
569 | 569 | } |
570 | | - |
| 570 | + |
571 | 571 | function renameThread($t,$s,$reason) { |
572 | 572 | $this->simplePageMove($t->root()->getTitle(),$s, $reason); |
573 | 573 | // TODO here create a redirect from old page to new. |
— | — | @@ -574,7 +574,7 @@ |
575 | 575 | $this->renameThread($st, $s, $reason); |
576 | 576 | } |
577 | 577 | } |
578 | | - |
| 578 | + |
579 | 579 | function scratchTitle() { |
580 | 580 | $token = md5(uniqid(rand(), true)); |
581 | 581 | return Title::newFromText( "Thread:$token" ); |
— | — | @@ -644,7 +644,7 @@ |
645 | 645 | */ |
646 | 646 | function threadFooterCommands($thread) { |
647 | 647 | $commands = array(); |
648 | | - |
| 648 | + |
649 | 649 | $user_can_edit = $thread->root()->getTitle()->quickUserCan( 'edit' ); |
650 | 650 | |
651 | 651 | $commands[] = array( 'label' => $user_can_edit ? wfMsg('edit') : wfMsg('viewsource'), |
— | — | @@ -654,7 +654,7 @@ |
655 | 655 | $commands[] = array( 'label' => wfMsg('history_short'), |
656 | 656 | 'href' => $this->permalinkUrlWithQuery($thread, 'action=history'), |
657 | 657 | 'enabled' => true ); |
658 | | - |
| 658 | + |
659 | 659 | $commands[] = array( 'label' => wfMsg('lqt_permalink'), |
660 | 660 | 'href' => $this->permalinkUrl( $thread ), |
661 | 661 | 'enabled' => true ); |
— | — | @@ -666,21 +666,21 @@ |
667 | 667 | 'href' => $delete_url, |
668 | 668 | 'enabled' => true ); |
669 | 669 | } |
670 | | - |
| 670 | + |
671 | 671 | $commands[] = array( 'label' => '<b class="lqt_reply_link">' . wfMsg('lqt_reply') . '</b>', |
672 | 672 | 'href' => $this->talkpageUrl( $this->title, 'reply', $thread ), |
673 | 673 | 'enabled' => $user_can_edit ); |
674 | 674 | |
675 | 675 | return $commands; |
676 | 676 | } |
677 | | - |
| 677 | + |
678 | 678 | function topLevelThreadCommands($thread) { |
679 | 679 | $commands = array(); |
680 | | - |
| 680 | + |
681 | 681 | $commands[] = array( 'label' => wfMsg('history_short'), |
682 | 682 | 'href' => $this->permalinkUrl($thread, 'thread_history'), |
683 | 683 | 'enabled' => true ); |
684 | | - |
| 684 | + |
685 | 685 | if( in_array('move', $this->user->getRights()) ) { |
686 | 686 | $move_href = SpecialPage::getPage('Movethread')->getTitle()->getFullURL() |
687 | 687 | . '/' . $thread->title()->getPrefixedURL(); |
— | — | @@ -697,14 +697,14 @@ |
698 | 698 | 'href' => $this->permalinkUrlWithQuery($thread, 'action=unwatch'), |
699 | 699 | 'enabled' => true ); |
700 | 700 | } |
701 | | - |
| 701 | + |
702 | 702 | return $commands; |
703 | 703 | } |
704 | 704 | |
705 | 705 | /************************* |
706 | 706 | * Output methods * |
707 | 707 | *************************/ |
708 | | - |
| 708 | + |
709 | 709 | static function addJSandCSS() { |
710 | 710 | // Changed this to be static so that we can call it from |
711 | 711 | // wfLqtBeforeWatchlistHook. |
— | — | @@ -769,7 +769,7 @@ |
770 | 770 | |
771 | 771 | function showThreadFooter( $thread ) { |
772 | 772 | global $wgLang; // TODO global. |
773 | | - |
| 773 | + |
774 | 774 | $author = $thread->root()->originalAuthor(); |
775 | 775 | $color_number = $this->selectNewUserColor($author); |
776 | 776 | |
— | — | @@ -777,14 +777,14 @@ |
778 | 778 | $this->user->getSkin()->userToolLinks( $author->getID(), $author->getName() ); |
779 | 779 | |
780 | 780 | $timestamp = $wgLang->timeanddate($thread->created()); |
781 | | - |
| 781 | + |
782 | 782 | $this->output->addHTML(<<<HTML |
783 | 783 | <ul class="lqt_footer"> |
784 | 784 | <span class="lqt_footer_sig"> |
785 | 785 | <li class="lqt_author_sig lqt_post_color_{$color_number}">$sig</li> |
786 | 786 | HTML |
787 | 787 | ); |
788 | | - |
| 788 | + |
789 | 789 | if( $thread->editedness() == Threads::EDITED_BY_AUTHOR || |
790 | 790 | $thread->editedness() == Threads::EDITED_BY_OTHERS ) { |
791 | 791 | $editedness_url = $this->permalinkUrlWithQuery($thread, 'action=history'); |
— | — | @@ -792,9 +792,9 @@ |
793 | 793 | $color_number : ($color_number == self::number_of_user_colors ? 1 : $color_number + 1); |
794 | 794 | $this->output->addHTML("<li class=\"lqt_edited_notice lqt_post_color_{$editedness_color_number}\">".'<a href="'.$editedness_url.'">'.wfMsg('lqt_edited_notice').'</a>'.'</li>'); |
795 | 795 | } |
796 | | - |
| 796 | + |
797 | 797 | $this->output->addHTML("</span><li>$timestamp</li>"); |
798 | | - |
| 798 | + |
799 | 799 | $this->output->addHTML('<span class="lqt_footer_commands">' . |
800 | 800 | $this->listItemsForCommands($this->threadFooterCommands($thread)) . |
801 | 801 | '</span>'); |
— | — | @@ -808,7 +808,7 @@ |
809 | 809 | $label = $command['label']; |
810 | 810 | $href = $command['href']; |
811 | 811 | $enabled = $command['enabled']; |
812 | | - |
| 812 | + |
813 | 813 | if( $enabled ) { |
814 | 814 | $result[] = "<li><a href=\"$href\">$label</a></li>"; |
815 | 815 | } else { |
— | — | @@ -817,10 +817,10 @@ |
818 | 818 | } |
819 | 819 | return join("", $result); |
820 | 820 | } |
821 | | - |
| 821 | + |
822 | 822 | function selectNewUserColor( $user ) { |
823 | 823 | $userkey = $user->isAnon() ? "anon:" . $user->getName() : "user:" . $user->getId(); |
824 | | - |
| 824 | + |
825 | 825 | if( !array_key_exists( $userkey, $this->user_colors ) ) { |
826 | 826 | $this->user_colors[$userkey] = $this->user_color_index; |
827 | 827 | $this->user_color_index += 1; |
— | — | @@ -836,7 +836,7 @@ |
837 | 837 | $previous_editsection = $popts->getEditSection(); |
838 | 838 | $popts->setEditSection(false); |
839 | 839 | $this->output->parserOptions($popts); |
840 | | - |
| 840 | + |
841 | 841 | $post = $thread->root(); |
842 | 842 | |
843 | 843 | // This is a bit of a hack to have individual histories work. |
— | — | @@ -850,24 +850,24 @@ |
851 | 851 | } else { |
852 | 852 | $oldid = $thread->isHistorical() ? $thread->rootRevision() : null; |
853 | 853 | } |
854 | | - |
| 854 | + |
855 | 855 | $this->openDiv( $this->postDivClass($thread) ); |
856 | | - |
| 856 | + |
857 | 857 | if( $this->methodAppliesToThread( 'edit', $thread ) ) { |
858 | 858 | $this->showPostEditingForm( $thread ); |
859 | 859 | } else{ |
860 | 860 | $this->showPostBody( $post, $oldid ); |
861 | 861 | $this->showThreadFooter( $thread ); |
862 | 862 | } |
863 | | - |
| 863 | + |
864 | 864 | $this->closeDiv(); |
865 | | - |
| 865 | + |
866 | 866 | if( $this->methodAppliesToThread( 'reply', $thread ) ) { |
867 | 867 | $this->indent($thread); |
868 | 868 | $this->showReplyForm( $thread ); |
869 | 869 | $this->unindent($thread); |
870 | 870 | } |
871 | | - |
| 871 | + |
872 | 872 | $popts->setEditSection($previous_editsection); |
873 | 873 | $this->output->parserOptions($popts); |
874 | 874 | } |
— | — | @@ -880,7 +880,7 @@ |
881 | 881 | $lis = $this->listItemsForCommands($this->topLevelThreadCommands($thread)); |
882 | 882 | $commands_html = "<ul class=\"lqt_threadlevel_commands\">$lis</ul>"; |
883 | 883 | } |
884 | | - |
| 884 | + |
885 | 885 | $html = $thread->subjectWithoutIncrement() . |
886 | 886 | ' <span class="lqt_subject_increment">(' . |
887 | 887 | $thread->increment() . ')</span>'; |
— | — | @@ -888,11 +888,11 @@ |
889 | 889 | <span class=\"mw-headline\">" . $html . "</span></h{$this->headerLevel}>$commands_html" ); |
890 | 890 | } |
891 | 891 | } |
892 | | - |
| 892 | + |
893 | 893 | function postDivClass( $thread ) { |
894 | 894 | return 'lqt_post'; |
895 | 895 | } |
896 | | - |
| 896 | + |
897 | 897 | function anchorName($thread) { |
898 | 898 | return "lqt_thread_{$thread->id()}"; |
899 | 899 | } |
— | — | @@ -907,10 +907,10 @@ |
908 | 908 | $tmp->root()->originalAuthor()->getName()); |
909 | 909 | $this->output->addHTML('<span class="lqt_nonindent_message">←'.$msg.'</span>'); |
910 | 910 | } |
911 | | - |
912 | | - |
| 911 | + |
| 912 | + |
913 | 913 | $this->showThreadHeading( $thread ); |
914 | | - |
| 914 | + |
915 | 915 | $this->output->addHTML( "<a name=\"{$this->anchorName($thread)}\" ></a>" ); |
916 | 916 | |
917 | 917 | if ($thread->type() == Threads::TYPE_MOVED) { |
— | — | @@ -924,13 +924,13 @@ |
925 | 925 | '<a href="'.$target->getFullURL().'">'.$target->getText().'</a>', |
926 | 926 | $sig, |
927 | 927 | $wgLang->timeanddate($thread->modified()) |
928 | | - )); |
| 928 | + )); |
929 | 929 | return; |
930 | 930 | } |
931 | 931 | |
932 | 932 | if ( $thread->type() == Threads::TYPE_DELETED ) { |
933 | 933 | if ( in_array('deletedhistory', $this->user->getRights()) ) { |
934 | | - $this->output->addHTML('<p>'. wfMsg('lqt_thread_deleted_for_sysops', |
| 934 | + $this->output->addHTML('<p>'. wfMsg('lqt_thread_deleted_for_sysops', |
935 | 935 | '<b>'.wfMsg('lqt_thread_deleted_for_sysops_deleted').'</b>') .'</p>'); |
936 | 936 | } |
937 | 937 | else { |
— | — | @@ -951,19 +951,19 @@ |
952 | 952 | } |
953 | 953 | |
954 | 954 | |
955 | | - |
| 955 | + |
956 | 956 | $this->openDiv('lqt_thread', "lqt_thread_id_{$thread->id()}"); |
957 | | - |
| 957 | + |
958 | 958 | $this->showRootPost( $thread ); |
959 | | - |
| 959 | + |
960 | 960 | if( $thread->hasSubthreads() ) $this->indent($thread); |
961 | 961 | foreach( $thread->subthreads() as $st ) { |
962 | 962 | $this->showThread($st); |
963 | 963 | } |
964 | 964 | if( $thread->hasSubthreads() ) $this->unindent($thread); |
965 | | - |
| 965 | + |
966 | 966 | $this->closeDiv(); |
967 | | - |
| 967 | + |
968 | 968 | } |
969 | 969 | |
970 | 970 | function indent($thread) { |
— | — | @@ -993,7 +993,7 @@ |
994 | 994 | function closeDiv() { |
995 | 995 | $this->output->addHTML( wfCloseElement( 'div' ) ); |
996 | 996 | } |
997 | | - |
| 997 | + |
998 | 998 | function showSummary($t) { |
999 | 999 | if ( !$t->summary() ) return; |
1000 | 1000 | $label = wfMsg('lqt_summary_label'); |
— | — | @@ -1016,5 +1016,3 @@ |
1017 | 1017 | } |
1018 | 1018 | |
1019 | 1019 | } |
1020 | | - |
1021 | | -?> |
Index: trunk/extensions/LiquidThreads/lqt.sql |
— | — | @@ -47,6 +47,6 @@ |
48 | 48 | ums_user int unsigned NOT NULL, |
49 | 49 | ums_thread int(8) unsigned NOT NULL, |
50 | 50 | ums_read_timestamp varbinary(14), |
51 | | - |
| 51 | + |
52 | 52 | PRIMARY KEY (ums_user, ums_thread) |
53 | 53 | ) TYPE=InnoDB; |