Index: branches/liquidthreads/maintenance/lqt.sql |
— | — | @@ -15,7 +15,7 @@ |
16 | 16 | -- Special thread types such as schrodinger's thread: |
17 | 17 | thread_type int(4) unsigned NOT NULL default 0, |
18 | 18 | |
19 | | - thread_change_type int(4) unsigned NOT NULL default 0, |
| 19 | + thread_change_type int(4) unsigned NOT NULL, |
20 | 20 | thread_change_object int(8) unsigned NULL, |
21 | 21 | |
22 | 22 | PRIMARY KEY thread_id (thread_id), |
— | — | @@ -32,7 +32,7 @@ |
33 | 33 | hthread_id int(8) unsigned NOT NULL, |
34 | 34 | hthread_revision int(8) unsigned NOT NULL, |
35 | 35 | hthread_contents BLOB NOT NULL, |
36 | | - hthread_change_type int(4) unsigned NOT NULL default 0, |
| 36 | + hthread_change_type int(4) unsigned NOT NULL, |
37 | 37 | hthread_change_object int(8) unsigned NULL, |
38 | 38 | PRIMARY KEY hthread_id_revision (hthread_id, hthread_revision) |
39 | 39 | ) TYPE=InnoDB; |
Index: branches/liquidthreads/extensions/LqtExtension.php |
— | — | @@ -309,7 +309,7 @@ |
310 | 310 | if ($edit_type != 'editExisting' && $edit_type != 'summarize' && $e->didSave) { |
311 | 311 | if ( $edit_type == 'reply' ) { |
312 | 312 | $thread = Threads::newThread( $article, $this->article, $edit_applies_to ); |
313 | | - $edit_applies_to->commitRevision(); |
| 313 | + $edit_applies_to->commitRevision(Threads::CHANGE_REPLY_CREATED, $thread); |
314 | 314 | } else { |
315 | 315 | $thread = Threads::newThread( $article, $this->article ); |
316 | 316 | } |
— | — | @@ -317,7 +317,7 @@ |
318 | 318 | |
319 | 319 | if ($edit_type == 'summarize' && $e->didSave) { |
320 | 320 | $edit_applies_to->setSummary( $article ); |
321 | | - $edit_applies_to->commitRevision(); |
| 321 | + $edit_applies_to->commitRevision(Threads::CHANGE_EDITED_SUMMARY, $edit_applies_to); |
322 | 322 | } |
323 | 323 | |
324 | 324 | // Move the thread and replies if subject changed. |
— | — | @@ -329,7 +329,7 @@ |
330 | 330 | } |
331 | 331 | // this is unrelated to the subject change and is for all edits: |
332 | 332 | $thread->setRootRevision( Revision::newFromTitle($thread->root()->getTitle()) ); |
333 | | - $thread->commitRevision(); |
| 333 | + $thread->commitRevision( Threads::CHANGE_EDITED_ROOT, $thread ); |
334 | 334 | } |
335 | 335 | } |
336 | 336 | |
— | — | @@ -955,7 +955,7 @@ |
956 | 956 | function getQueryInfo() { |
957 | 957 | return array( |
958 | 958 | 'tables' => 'historical_thread', |
959 | | - 'fields' => 'hthread_id, hthread_revision, hthread_contents', |
| 959 | + 'fields' => 'hthread_id, hthread_revision, hthread_contents, hthread_change_type, hthread_change_object', |
960 | 960 | 'conds' => array('hthread_id' => $this->thread->id() ), |
961 | 961 | 'options' => array() |
962 | 962 | ); |
— | — | @@ -979,8 +979,15 @@ |
980 | 980 | function historyLine( $row, $next, $counter = '', $notificationtimestamp = false, $latest = false, $firstInList = false ) { |
981 | 981 | /* TODO: best not to refer to LqtView class directly. */ |
982 | 982 | /* We don't use oldid because that has side-effects. */ |
| 983 | + $result = array(); |
| 984 | + $change_names = array(Threads::CHANGE_EDITED_ROOT => "Comment text edited", |
| 985 | + Threads::CHANGE_EDITED_SUMMARY => "Summary changed", |
| 986 | + Threads::CHANGE_REPLY_CREATED => "New reply created", |
| 987 | + Threads::CHANGE_NEW_THREAD => "New thread created"); |
983 | 988 | $url = LqtView::permalinkUrlWithQuery( $this->thread, 'lqt_oldid=' . $row->hthread_revision ); |
984 | | - return "<tr><td><a href=\"$url\">" . $row->hthread_revision . '</a></td></tr>'; |
| 989 | + $result[] = "<tr><td><a href=\"$url\">" . $row->hthread_revision . '</a></td></tr>'; |
| 990 | + $result[] = "<tr><td>Revision type: {$change_names[$row->hthread_change_type]}."; |
| 991 | + return implode('', $result); |
985 | 992 | } |
986 | 993 | function getNotificationTimestamp() { |
987 | 994 | return "foo"; |
Index: branches/liquidthreads/extensions/LqtModel.php |
— | — | @@ -110,6 +110,8 @@ |
111 | 111 | $this->path = $t->path; |
112 | 112 | $this->id = $t->id; |
113 | 113 | $this->revisionNumber = $t->revisionNumber; |
| 114 | + $this->changeType = $t->changeType; |
| 115 | + $this->changeObject = $t->changeObject; |
114 | 116 | |
115 | 117 | $this->replies = array(); |
116 | 118 | foreach ($t->replies as $r) { |
— | — | @@ -130,7 +132,9 @@ |
131 | 133 | $res = $dbr->insert( 'historical_thread', array( |
132 | 134 | 'hthread_id'=>$tmt->id(), |
133 | 135 | 'hthread_revision'=>$tmt->revisionNumber(), |
134 | | - 'hthread_contents'=>$contents), __METHOD__ ); |
| 136 | + 'hthread_contents'=>$contents, |
| 137 | + 'hthread_change_type'=>$tmt->changeType(), |
| 138 | + 'hthread_change_object'=>$tmt->changeObject()), __METHOD__ ); |
135 | 139 | } |
136 | 140 | static function withIdAtRevision( $id, $rev ) { |
137 | 141 | $dbr =& wfGetDB( DB_SLAVE ); |
— | — | @@ -144,7 +148,9 @@ |
145 | 149 | else |
146 | 150 | return null; |
147 | 151 | } |
148 | | - function isHistorical() { return true; } |
| 152 | + function isHistorical() { |
| 153 | + return true; |
| 154 | + } |
149 | 155 | } |
150 | 156 | |
151 | 157 | class Thread { |
— | — | @@ -172,6 +178,9 @@ |
173 | 179 | protected $revisionNumber; |
174 | 180 | protected $type; |
175 | 181 | |
| 182 | + protected $changeType; |
| 183 | + protected $changeObject; |
| 184 | + |
176 | 185 | /* Only used by $double to be saved into a historical thread. */ |
177 | 186 | protected $rootRevision; |
178 | 187 | |
— | — | @@ -181,7 +190,9 @@ |
182 | 191 | |
183 | 192 | protected $replies; |
184 | 193 | |
185 | | - function isHistorical() {return false;} |
| 194 | + function isHistorical() { |
| 195 | + return false; |
| 196 | + } |
186 | 197 | |
187 | 198 | function revisionNumber() { |
188 | 199 | return $this->revisionNumber; |
— | — | @@ -205,25 +216,30 @@ |
206 | 217 | return $results; |
207 | 218 | } |
208 | 219 | |
209 | | - private function bumpRevisions() { |
210 | | - $this->revisionNumber += 1; |
| 220 | + private function bumpRevisionsOnAncestors($change_type, $change_object) { |
| 221 | + $this->revisionNumber += 1; |
| 222 | + $this->setChangeType($change_type); |
| 223 | + $this->setChangeObject($change_object); |
| 224 | + |
211 | 225 | if( $this->hasSuperthread() ) |
212 | | - $this->superthread()->bumpRevisions(); |
| 226 | + $this->superthread()->bumpRevisionsOnAncestors($change_type, $change_object); |
213 | 227 | $dbr =& wfGetDB( DB_MASTER ); |
214 | 228 | $res = $dbr->update( 'thread', |
215 | | - /* SET */ array('thread_revision' => $this->revisionNumber), |
| 229 | + /* SET */ array('thread_revision' => $this->revisionNumber, |
| 230 | + 'thread_change_type'=>$this->changeType, |
| 231 | + 'thread_change_object'=>$this->changeObject), |
216 | 232 | /* WHERE */ array( 'thread_id' => $this->id ), |
217 | 233 | __METHOD__); |
218 | 234 | } |
219 | 235 | |
220 | | - function commitRevision() { |
| 236 | + function commitRevision($change_type, $change_object = null) { |
221 | 237 | global $wgUser; // TODO global. |
222 | 238 | |
223 | 239 | // TODO open a transaction. |
224 | 240 | HistoricalThread::create( $this->double ); |
225 | 241 | |
226 | | - $this->bumpRevisions(); |
227 | | - |
| 242 | + $this->bumpRevisionsOnAncestors($change_type, $change_object); |
| 243 | + |
228 | 244 | $dbr =& wfGetDB( DB_MASTER ); |
229 | 245 | $res = $dbr->update( 'thread', |
230 | 246 | /* SET */array( 'thread_root' => $this->rootId, |
— | — | @@ -233,7 +249,9 @@ |
234 | 250 | 'thread_timestamp' => wfTimestampNow(), |
235 | 251 | 'thread_revision' => $this->revisionNumber, |
236 | 252 | 'thread_article_namespace' => $this->articleNamespace, |
237 | | - 'thread_article_title' => $this->articleTitle), |
| 253 | + 'thread_article_title' => $this->articleTitle, |
| 254 | + 'thread_change_type' => $this->changeType, |
| 255 | + 'thread_change_object' => $this->changeObject), |
238 | 256 | /* WHERE */ array( 'thread_id' => $this->id, ), |
239 | 257 | __METHOD__); |
240 | 258 | |
— | — | @@ -329,8 +347,10 @@ |
330 | 348 | $this->timestamp = $line->thread_timestamp; |
331 | 349 | $this->revisionNumber = $line->thread_revision; |
332 | 350 | $this->type = $line->thread_type; |
| 351 | + $this->changeType = $line->thread_change_type; |
| 352 | + $this->changeObject = $line->thread_change_object; |
| 353 | + |
333 | 354 | $this->replies = $children; |
334 | | - |
335 | 355 | |
336 | 356 | $this->double = clone $this; |
337 | 357 | |
— | — | @@ -511,31 +531,75 @@ |
512 | 532 | function type() { |
513 | 533 | return $this->type; |
514 | 534 | } |
| 535 | + |
| 536 | + function changeType() { |
| 537 | + return $this->changeType; |
| 538 | + } |
| 539 | + |
| 540 | + function changeObject() { |
| 541 | + return $this->changeObject; |
| 542 | + } |
| 543 | + |
| 544 | + function setChangeType($t) { |
| 545 | + if (in_array($t, Threads::$VALID_CHANGE_TYPES)) { |
| 546 | + $this->changeType = $t; |
| 547 | + } else { |
| 548 | + throw new MWException( __METHOD__ . ": invalid changeType $t." ); |
| 549 | + } |
| 550 | + } |
| 551 | + |
| 552 | + function setChangeObject($o) { |
| 553 | + # we assume $o to be a Thread. |
| 554 | + if($o === null) { |
| 555 | + $this->changeObject = null; |
| 556 | + } else { |
| 557 | + $this->changeObject = $o->id(); |
| 558 | + } |
| 559 | + } |
515 | 560 | } |
516 | 561 | |
| 562 | + |
517 | 563 | /** Module of factory methods. */ |
518 | 564 | class Threads { |
519 | 565 | |
520 | 566 | const TYPE_NORMAL = 0; |
521 | 567 | const TYPE_MOVED = 1; |
| 568 | + static $VALID_TYPES = array(self::TYPE_NORMAL, self::TYPE_MOVED); |
| 569 | + |
| 570 | + const CHANGE_NEW_THREAD = 0; |
| 571 | + const CHANGE_REPLY_CREATED = 1; |
| 572 | + const CHANGE_EDITED_ROOT = 2; |
| 573 | + const CHANGE_EDITED_SUMMARY = 3; |
| 574 | + static $VALID_CHANGE_TYPES = array(self::CHANGE_EDITED_SUMMARY, self::CHANGE_EDITED_ROOT, |
| 575 | + self::CHANGE_REPLY_CREATED, self::CHANGE_NEW_THREAD); |
522 | 576 | |
523 | 577 | static $loadedThreads = array(); |
524 | 578 | |
525 | | - static function newThread( $root, $article, $superthread = null, |
526 | | - $type = self::TYPE_NORMAL, $log = null ) { |
527 | | - /* TODO log is ignored. */ |
| 579 | + static function newThread( $root, $article, $superthread = null, $type = self::TYPE_NORMAL ) { |
| 580 | + |
528 | 581 | $dbr =& wfGetDB( DB_MASTER ); |
529 | | - |
| 582 | + |
| 583 | + if ( !in_array($type, self::$VALID_TYPES) ) { |
| 584 | + throw new MWException(__METHOD__ . ": invalid type $type."); |
| 585 | + } |
| 586 | + |
530 | 587 | if( $article->exists() ) { |
531 | 588 | $aclause = array("thread_article" => $article->getID()); |
532 | 589 | } else { |
533 | 590 | $aclause = array("thread_article_namespace" => $article->getTitle()->getNamespace(), |
534 | 591 | "thread_article_title" => $article->getTitle()->getDBkey()); |
535 | 592 | } |
| 593 | + |
| 594 | + if ($superthread) { |
| 595 | + $change_type = self::CHANGE_REPLY_CREATED; |
| 596 | + } else { |
| 597 | + $change_type = self::CHANGE_NEW_THREAD; |
| 598 | + } |
536 | 599 | |
537 | 600 | $res = $dbr->insert('thread', |
538 | 601 | array('thread_root' => $root->getID(), |
539 | 602 | 'thread_timestamp' => wfTimestampNow(), |
| 603 | + 'thread_change_type' => $change_type, |
540 | 604 | 'thread_type' => $type) + $aclause, |
541 | 605 | __METHOD__); |
542 | 606 | |
— | — | @@ -543,11 +607,14 @@ |
544 | 608 | |
545 | 609 | if( $superthread ) { |
546 | 610 | $newpath = $superthread->path() . '.' . $newid; |
| 611 | + $change_object_clause = 'thread_change_object = ' . $newid; |
547 | 612 | } else { |
548 | 613 | $newpath = $newid; |
| 614 | + $change_object_clause = 'thread_change_object = null'; |
549 | 615 | } |
550 | 616 | $res = $dbr->update( 'thread', |
551 | | - /* SET */ array( 'thread_path' => $newpath ), |
| 617 | + /* SET */ array( 'thread_path' => $newpath, |
| 618 | + $change_object_clause ), |
552 | 619 | /* WHERE */ array( 'thread_id' => $newid, ), |
553 | 620 | __METHOD__); |
554 | 621 | |