Index: branches/liquidthreads/maintenance/lqt.sql |
— | — | @@ -2,12 +2,14 @@ |
3 | 3 | thread_id int(8) unsigned NOT NULL auto_increment, |
4 | 4 | thread_root int(8) unsigned NOT NULL, |
5 | 5 | thread_article int(8) unsigned NOT NULL, |
6 | | - thread_path varchar(1000000) NOT NULL, |
| 6 | + thread_path text NOT NULL, |
7 | 7 | thread_summary_page int(8) unsigned NULL, |
8 | 8 | thread_touched char(14) binary NOT NULL default '', |
9 | 9 | |
10 | 10 | PRIMARY KEY thread_id (thread_id), |
11 | | - UNIQUE INDEX thread_id (thread_id) |
| 11 | + UNIQUE INDEX thread_id (thread_id), |
| 12 | + INDEX( thread_path(255) ), |
| 13 | + INDEX thread_touched (thread_touched) |
12 | 14 | ) TYPE=InnoDB; |
13 | 15 | |
14 | 16 | /* |
Index: branches/liquidthreads/extensions/LqtExtension.php |
— | — | @@ -84,21 +84,21 @@ |
85 | 85 | $startdate = Date::now()->nDaysAgo($this->archive_start_days)->midnight(); |
86 | 86 | $recentstartdate = $startdate->nDaysAgo($this->archive_recent_days); |
87 | 87 | $g->addQuery('fresh', |
88 | | - array('thread_article' => $this->article->getID(), |
89 | | - 'thread_subthread_of is null', |
90 | | - '(thread_touched >= ' . $startdate->text() . |
91 | | - ' OR thread_summary_page is NULL)'), |
92 | | - array('ORDER BY' => 'thread_touched DESC' )); |
| 88 | + array('thread.thread_article' => $this->article->getID(), |
| 89 | + 'instr(thread.thread_path, ".")' => '0', |
| 90 | + '(thread.thread_timestamp >= ' . $startdate->text() . |
| 91 | + ' OR thread.thread_summary_page is NULL)'), |
| 92 | + array('ORDER BY thread.thread_timestamp DESC')); |
93 | 93 | $g->addQuery('archived', |
94 | | - array('thread_article' => $this->article->getID(), |
95 | | - 'thread_subthread_of is null', |
96 | | - 'thread_summary_page is not null', |
97 | | - 'thread_touched < ' . $startdate->text()), |
98 | | - array('ORDER BY' => 'thread_touched DESC')); |
| 94 | + array('thread.thread_article' => $this->article->getID(), |
| 95 | + 'instr(thread.thread_path, ".")' => '0', |
| 96 | + 'thread.thread_summary_page is not null', |
| 97 | + 'thread.thread_timestamp < ' . $startdate->text()), |
| 98 | + array('ORDER BY thread.thread_timestamp DESC')); |
99 | 99 | $g->extendQuery('archived', 'recently-archived', |
100 | | - array('( thread_touched >=' . $recentstartdate->text() . |
| 100 | + array('( thread.thread_timestamp >=' . $recentstartdate->text() . |
101 | 101 | ' OR rev_timestamp >= ' . $recentstartdate->text() . ')', |
102 | | - 'page_id = thread_summary_page', 'page_latest = rev_id'), |
| 102 | + 'page_id = thread.thread_summary_page', 'page_latest = rev_id'), |
103 | 103 | array(), |
104 | 104 | array('page', 'revision')); |
105 | 105 | return $g; |
— | — | @@ -131,7 +131,7 @@ |
132 | 132 | function permalinkUrl( $thread, $method = null, $operand = null ) { |
133 | 133 | $query = $method ? "lqt_method=$method" : ""; |
134 | 134 | $query = $operand ? "$query&lqt_operand={$operand->id()}" : $query; |
135 | | - return $thread->rootPost()->getTitle()->getFullUrl($query); |
| 135 | + return $thread->root()->getTitle()->getFullUrl($query); |
136 | 136 | } |
137 | 137 | |
138 | 138 | function talkpageUrl( $title, $method = null, $operand = null ) { |
— | — | @@ -197,7 +197,7 @@ |
198 | 198 | } |
199 | 199 | $article = new Article($t); |
200 | 200 | } else { |
201 | | - $article = $thread->rootPost(); |
| 201 | + $article = $thread->root(); |
202 | 202 | } |
203 | 203 | |
204 | 204 | $e = new EditPage($article); |
— | — | @@ -261,7 +261,7 @@ |
262 | 262 | } |
263 | 263 | |
264 | 264 | function renameThread($t,$s) { |
265 | | - $this->simplePageMove($t->rootPost()->getTitle(),$s); |
| 265 | + $this->simplePageMove($t->root()->getTitle(),$s); |
266 | 266 | // TODO here create a redirect from old page to new. |
267 | 267 | foreach( $t->subthreads() as $st ) { |
268 | 268 | $this->renameThread($st, $s); |
— | — | @@ -354,16 +354,16 @@ |
355 | 355 | } |
356 | 356 | |
357 | 357 | function showThreadFooter( $thread ) { |
358 | | - $color_number = $this->selectNewUserColor( $thread->rootPost()->originalAuthor() ); |
| 358 | + $color_number = $this->selectNewUserColor( $thread->root()->originalAuthor() ); |
359 | 359 | $this->output->addHTML(wfOpenElement('ul', array('class'=>"lqt_footer" ))); |
360 | 360 | |
361 | 361 | $this->output->addHTML( wfOpenElement( 'li', array('class'=>"lqt_author_sig lqt_post_color_$color_number") ) ); |
362 | | - $p = new Parser(); $sig = $p->getUserSig( $thread->rootPost()->originalAuthor() ); |
| 362 | + $p = new Parser(); $sig = $p->getUserSig( $thread->root()->originalAuthor() ); |
363 | 363 | $this->output->addWikitext( $sig, false ); |
364 | 364 | $this->output->addHTML( wfCloseElement( 'li' ) ); |
365 | 365 | |
366 | 366 | $this->output->addHTML( wfOpenElement( 'li' ) ); |
367 | | - $d = new Date($thread->touched()); |
| 367 | + $d = new Date($thread->timestamp()); |
368 | 368 | $this->output->addHTML( $d->lastMonth()->text() ); |
369 | 369 | $this->output->addHTML( wfCloseElement( 'li' ) ); |
370 | 370 | |
— | — | @@ -394,9 +394,9 @@ |
395 | 395 | } |
396 | 396 | |
397 | 397 | function showRootPost( $thread ) { |
398 | | - $post = $thread->rootPost(); |
| 398 | + $post = $thread->root(); |
399 | 399 | |
400 | | -/* $color_number = $this->selectNewUserColor( $thread->rootPost()->originalAuthor() ); |
| 400 | +/* $color_number = $this->selectNewUserColor( $thread->root()->originalAuthor() ); |
401 | 401 | $this->openDiv( "lqt_post lqt_post_color_$color_number" );*/ |
402 | 402 | $this->openDiv( 'lqt_post' ); |
403 | 403 | |
— | — | @@ -429,10 +429,10 @@ |
430 | 430 | function showThread( $thread ) { |
431 | 431 | $this->showThreadHeading( $thread ); |
432 | 432 | |
433 | | - $touched = new Date($thread->touched()); |
| 433 | + $timestamp = new Date($thread->timestamp()); |
434 | 434 | if( $thread->summary() ) { |
435 | 435 | $this->showSummary($thread); |
436 | | - } else if ( $touched->isBefore(Date::now()->nDaysAgo($this->archive_start_days)) |
| 436 | + } else if ( $timestamp->isBefore(Date::now()->nDaysAgo($this->archive_start_days)) |
437 | 437 | && !$thread->summary() && !$thread->superthread() ) { |
438 | 438 | $this->output->addHTML("<p class=\"lqt_summary_notice\">If this discussion seems to be concluded, you are encouraged to <a href=\"{$this->permalinkUrl($thread, 'summarize')}\">write a summary</a>. There have been no changes here for at least $this->archive_start_days days.</p>"); |
439 | 439 | } |
— | — | @@ -596,8 +596,8 @@ |
597 | 597 | $this->output->setPageTitle( "Talk:" . $this->title->getText() ); // TODO non-main namespaces. |
598 | 598 | $this->addJSandCSS(); |
599 | 599 | |
600 | | - lqtCheapTest( ); |
601 | | - return; |
| 600 | + // lqtCheapTest( ); |
| 601 | + // return; |
602 | 602 | |
603 | 603 | $this->showHeader(); |
604 | 604 | |
— | — | @@ -643,8 +643,8 @@ |
644 | 644 | $where = array('thread_article' => $this->article->getID(), |
645 | 645 | 'thread_subthread_of is null', |
646 | 646 | 'thread_summary_page is not null', |
647 | | - 'thread_touched < ' . $startdate->text()); |
648 | | - $options = array('ORDER BY' => 'thread_touched DESC'); |
| 647 | + 'thread_timestamp < ' . $startdate->text()); |
| 648 | + $options = array('ORDER BY' => 'thread_timestamp DESC'); |
649 | 649 | |
650 | 650 | $annotations = array("Searching for threads"); |
651 | 651 | |
— | — | @@ -662,13 +662,13 @@ |
663 | 663 | if ($s && ctype_digit($s) && strlen($s) == 6 && !$ignore_dates) { |
664 | 664 | $this->selstart = new Date( "{$s}01000000" ); |
665 | 665 | $this->starti = array_search($s, $months); |
666 | | - $where[] = 'thread_touched >= ' . $this->selstart->text(); |
| 666 | + $where[] = 'thread_timestamp >= ' . $this->selstart->text(); |
667 | 667 | } |
668 | 668 | $e = $r->getVal('lqt_archive_end'); |
669 | 669 | if ($e && ctype_digit($e) && strlen($e) == 6 && !$ignore_dates) { |
670 | 670 | $this->selend = new Date("{$e}01000000"); |
671 | 671 | $this->endi = array_search($e, $months); |
672 | | - $where[] = 'thread_touched < ' . $this->selend->nextMonth()->text(); |
| 672 | + $where[] = 'thread_timestamp < ' . $this->selend->nextMonth()->text(); |
673 | 673 | } |
674 | 674 | if ( isset($this->selstart) && isset($this->selend) ) { |
675 | 675 | |
— | — | @@ -847,10 +847,10 @@ |
848 | 848 | $this->article = $t->article(); # for creating reply threads. |
849 | 849 | |
850 | 850 | // Make a link back to the talk page, including the correct archive month. |
851 | | - if (Date::now()->nDaysAgo(30)->midnight()->isBefore( new Date($t->touched()) )) |
| 851 | + if (Date::now()->nDaysAgo(30)->midnight()->isBefore( new Date($t->timestamp()) )) |
852 | 852 | $query = ''; |
853 | 853 | else |
854 | | - $query = 'lqt_archive_month=' . substr($t->touched(),0,6); |
| 854 | + $query = 'lqt_archive_month=' . substr($t->timestamp(),0,6); |
855 | 855 | |
856 | 856 | $talkpage = $t->article()->getTitle()->getTalkpage(); |
857 | 857 | $talkpage_link = $this->user->getSkin()->makeKnownLinkObj($talkpage, '', $query); |
Index: branches/liquidthreads/extensions/LqtModel.php |
— | — | @@ -102,26 +102,165 @@ |
103 | 103 | } |
104 | 104 | |
105 | 105 | class LiveThread { |
| 106 | + /* ID references to other objects that are loaded on demand: */ |
| 107 | + protected $rootId; |
| 108 | + protected $articleId; |
| 109 | + protected $summaryId; |
| 110 | + protected $superthreadId; |
106 | 111 | |
| 112 | + /* Actual objects loaded on demand from the above when accessors are called: */ |
| 113 | + protected $root; |
| 114 | + protected $article; |
| 115 | + protected $summary; |
| 116 | + protected $superthread; |
| 117 | + |
| 118 | + /* Simple strings: */ |
| 119 | + protected $subject; |
| 120 | + protected $timestamp; |
| 121 | + |
| 122 | + /* Identity */ |
| 123 | + protected $id; |
| 124 | + |
| 125 | + function __construct($line, $children) { |
| 126 | + $this->id = $line->thread_id; |
| 127 | + $this->rootId = $line->thread_root; |
| 128 | + $this->articleId = $line->thread_article; |
| 129 | + $this->summaryId = $line->thread_summary_page; |
| 130 | + $this->path = $line->thread_path; |
| 131 | + $this->timestamp = $line->thread_timestamp; |
| 132 | + $this->replies = $children; |
| 133 | + } |
| 134 | + |
| 135 | + function setSuperthread($thread) { |
| 136 | + $this->superthreadId = $thread->id(); |
| 137 | + $this->touch(); |
| 138 | + } |
| 139 | + |
| 140 | + function superthread() { |
| 141 | + if ( !$this->superthreadId ) return null; |
| 142 | + if ( !$this->superthread ) $this->superthread = Thread::newFromId($this->superthreadId); |
| 143 | + return $this->superthread; |
| 144 | + } |
| 145 | + |
| 146 | + function topmostThread() { |
| 147 | + if ( !$this->superthread() ) return $this; |
| 148 | + else return $this->superthread()->topmostThread(); |
| 149 | + } |
| 150 | + |
| 151 | + function setArticle($a) { |
| 152 | + $this->articleId = $a->getID(); |
| 153 | + $this->touch(); |
| 154 | + } |
| 155 | + |
| 156 | + function article() { |
| 157 | + if ( !$this->articleId ) return null; |
| 158 | + if ( !$this->article ) $this->article = new Article(Title::newFromID($this->articleId)); |
| 159 | + return $this->article; |
| 160 | + } |
| 161 | + |
| 162 | + function id() { |
| 163 | + return $this->id; |
| 164 | + } |
| 165 | + |
| 166 | + function root() { |
| 167 | + if ( !$this->rootId ) return null; |
| 168 | + if ( !$this->root ) $this->root = new Post( Title::newFromID( $this->rootId ) ); |
| 169 | + return $this->root; |
| 170 | + } |
| 171 | + |
| 172 | + function summary() { |
| 173 | + if ( !$this->summaryId ) return null; |
| 174 | + if ( !$this->summary ) $this->summary = new Post( Title::newFromID( $this->summaryId ) ); |
| 175 | + return $this->summary; |
| 176 | + } |
| 177 | + |
| 178 | + function setSummary( $post ) { |
| 179 | + $this->summaryId = $post->getID(); |
| 180 | + $this->updateRecord(); |
| 181 | + } |
| 182 | + |
| 183 | + function wikilink() { |
| 184 | + return $this->root()->getTitle()->getPrefixedText(); |
| 185 | + } |
| 186 | + |
| 187 | + function wikilinkWithoutIncrement() { |
| 188 | + $foo = explode( ' ', $this->wikilink() ); |
| 189 | + array_pop($foo); |
| 190 | + return implode( ' ', $foo ); |
| 191 | + } |
| 192 | + |
| 193 | + function hasDistinctSubject() { |
| 194 | + if( $this->superthread() ) { |
| 195 | + return $this->superthread()->subjectWithoutIncrement() |
| 196 | + != $this->subjectWithoutIncrement(); |
| 197 | + } else { |
| 198 | + return true; |
| 199 | + } |
| 200 | + } |
| 201 | + |
| 202 | + function subject() { |
| 203 | + return $this->root()->getTitle()->getText(); |
| 204 | + return $this->subject; |
| 205 | + } |
| 206 | + |
| 207 | + function subjectWithoutIncrement() { |
| 208 | + $foo = explode( ' ', $this->subject() ); |
| 209 | + array_pop($foo); |
| 210 | + return implode( ' ', $foo ); |
| 211 | + } |
| 212 | + |
| 213 | + function increment() { |
| 214 | + return array_pop( explode(' ', $this->subject()) ); |
| 215 | + } |
| 216 | + |
| 217 | + function hasSubthreads() { |
| 218 | + return count($replies) != 0; |
| 219 | + } |
| 220 | + |
| 221 | + function subthreads() { |
| 222 | + return $this->replies; |
| 223 | + } |
| 224 | + |
| 225 | + function timestamp() { |
| 226 | + return $this->timestamp; |
| 227 | + } |
| 228 | + |
| 229 | + protected function updateRecord() { |
| 230 | + $dbr =& wfGetDB( DB_MASTER ); |
| 231 | + $res = $dbr->update( 'lqt_thread', |
| 232 | + /* SET */ array( 'thread_root_post' => $this->rootId, |
| 233 | + 'thread_article' => $this->articleId, |
| 234 | + 'thread_subthread_of' => $this->superthreadId, |
| 235 | + 'thread_summary_page' => $this->summaryId, |
| 236 | + 'thread_subject' => $this->subject, |
| 237 | + 'thread_timestamp' => $this->timestamp ), |
| 238 | + /* WHERE */ array( 'thread_id' => $this->id, ), |
| 239 | + __METHOD__); |
| 240 | + } |
107 | 241 | } |
108 | 242 | |
109 | 243 | /** Module of factory methods. */ |
110 | 244 | class Threads { |
111 | 245 | |
112 | | - static function threadsWhere( $where_clause, $options = array(), $extra_tables = array() ) { |
| 246 | + static function threadsWhere( $where, $options = array(), $extra_tables = array() ) { |
113 | 247 | $dbr =& wfGetDB( DB_SLAVE ); |
114 | | - if ( is_array($where_clause) ) { |
115 | | - $where = implode( 'and ', $where_clause ); |
| 248 | + if ( is_array($where) ) $where = $dbr->makeList( $where, LIST_AND ); |
| 249 | + if ( is_array($options) ) $options = implode(',', $options); |
| 250 | + if( is_array($extra_tables) && count($extra_tables) != 0 ) { |
| 251 | + $tables = implode(',', $extra_tables) . ', '; |
| 252 | + } else if ( is_string( $extra_tables ) ) { |
| 253 | + $tables = $extra_tables . ', '; |
116 | 254 | } else { |
117 | | - $where = $where_clause; |
| 255 | + $tables = ""; |
118 | 256 | } |
119 | | - |
| 257 | + |
120 | 258 | /* Select the client's threads, AND all their children: */ |
121 | 259 | |
122 | 260 | $sql = <<< SQL |
123 | | -SELECT children.* FROM thread, thread children |
| 261 | +SELECT children.* FROM $tables thread, thread children |
124 | 262 | WHERE $where AND |
125 | 263 | children.thread_path LIKE CONCAT(thread.thread_path, "%") |
| 264 | +$options |
126 | 265 | SQL; |
127 | 266 | $res = $dbr->query($sql); |
128 | 267 | |
— | — | @@ -137,23 +276,25 @@ |
138 | 277 | Threads::setDeepArray( $tree, $line, $path ); |
139 | 278 | |
140 | 279 | } |
141 | | - function createThreads( $thread ) { |
142 | | - $subthreads = array(); |
143 | | - foreach( $thread as $key => $val ) { |
144 | | - if ( $key != 'root' ) { |
145 | | - $subthreads[] = createThreads( $val ); |
146 | | - } |
147 | | - } |
148 | | - return Threads::newLiveThreadFromDBLine( $thread['root'], $subthreads ); |
149 | | - } |
| 280 | + |
150 | 281 | $threads = array(); |
151 | 282 | foreach( $tree as $root ) { |
152 | | - $threads[] = createThreads($root); |
| 283 | + $threads[] = Threads::createThreads($root); |
153 | 284 | } |
154 | 285 | |
155 | 286 | return $threads; |
156 | 287 | } |
157 | 288 | |
| 289 | + private static function createThreads( $thread ) { |
| 290 | + $subthreads = array(); |
| 291 | + foreach( $thread as $key => $val ) { |
| 292 | + if ( $key != 'root' ) { |
| 293 | + $subthreads[] = Threads::createThreads( $val ); |
| 294 | + } |
| 295 | + } |
| 296 | + return new LiveThread( $thread['root'], $subthreads ); |
| 297 | + } |
| 298 | + |
158 | 299 | /** setDeepArray( $a, $v, array(1,2,3) ) <=> $a[1][2][3]['root'] = $v; */ |
159 | 300 | private static function setDeepArray( &$a, $v, $p ) { |
160 | 301 | if( count($p) == 1 ) { |
— | — | @@ -163,27 +304,14 @@ |
164 | 305 | $a[$p[0]] = array(); |
165 | 306 | Threads::setDeepArray( $a[$p[0]], $v, array_slice($p, 1) ); |
166 | 307 | } |
167 | | - } |
168 | | - |
169 | | - static function newLiveThreadFromDBLine( $line, $children ) { |
170 | | - $t = new LiveThread(); |
171 | | - $t->id = $line->thread_id; |
172 | | - $t->rootId = $line->thread_root; |
173 | | - $t->articleId = $line->thread_article; |
174 | | - $t->summaryId = $line->thread_summary_page; |
175 | | - $t->path = $line->thread_path; |
176 | | - $t->touched = $line->thread_touched; |
177 | | - $t->replies = $children; |
178 | | - return $t; |
179 | | - } |
180 | | - |
| 308 | + } |
181 | 309 | } |
182 | | - |
| 310 | +/* |
183 | 311 | function lqtCheapTest() { |
184 | | - $threads = Threads::threadsWhere( "thread.thread_id = 1" ); |
| 312 | + $threads = Threads::threadsWhere( "thread.thread_id = 1", "order by thread_timestamp" ); |
185 | 313 | function cheapShowThread($t) { |
186 | 314 | global $wgOut; |
187 | | - $wgOut->addHTML($t->id); |
| 315 | + $wgOut->addHTML($t->id()); |
188 | 316 | $wgOut->addHTML('<dl><dd>'); |
189 | 317 | foreach( $t->replies as $r ) { |
190 | 318 | cheapShowThread($r); |
— | — | @@ -194,7 +322,7 @@ |
195 | 323 | cheapShowThread($t); |
196 | 324 | } |
197 | 325 | } |
198 | | - |
| 326 | +*/ |
199 | 327 | class QueryGroup { |
200 | 328 | protected $queries; |
201 | 329 | |
— | — | @@ -221,7 +349,7 @@ |
222 | 350 | function query($name) { |
223 | 351 | if ( !array_key_exists($name,$this->queries) ) return array(); |
224 | 352 | list($where, $options, $extra_tables) = $this->queries[$name]; |
225 | | - return Thread::threadsWhere($where, $options, $extra_tables); |
| 353 | + return Threads::threadsWhere($where, $options, $extra_tables); |
226 | 354 | } |
227 | 355 | } |
228 | 356 | |