r52307 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r52306‎ | r52307 | r52308 >
Date:13:34, 23 June 2009
Author:werdna
Status:deferred
Tags:
Comment:
Massive LiquidThreads refactoring and reorganisation:
* Totally rewrote almost all user-facing code to automatically generate HTML instead of hard-coding it.
* Fixed at least two verified cross-site scripting vulnerabilities, with many other possible vectors plugged.
* General code quality, fixed issues like indentation, internal documentation, variable naming, line breaking.
* Changed to the strategy of "build HTML, then push it out" rather than pushing it to OutputPage line-by-line. This makes the code more modular, and makes it much easier to use the appropriate Xml functions for HTML generation.
* Reimplemented ThreadHistoryListingView and TalkpageArchiveView to use the appropriate subclasses of Pager (ReverseChronologicalPager and TablePager) instead of home-grown solutions.
* Fix a bug in NewMessages which was causing double-notification for edits to a user's talk page.
* Implement function for Article which grabs the article text, using caches where appropriate, without all the associated cruft in Article::view.
* Replace some functions defined in LiquidThreads which already exist in core (such as wfArrayToCGi and its reverse).
* Other related minor changes.
Modified paths:
  • /trunk/extensions/LiquidThreads/classes/LqtNewMessages.php (modified) (history)
  • /trunk/extensions/LiquidThreads/classes/LqtThread.php (modified) (history)
  • /trunk/extensions/LiquidThreads/classes/LqtThreads.php (modified) (history)
  • /trunk/extensions/LiquidThreads/classes/LqtView.php (modified) (history)
  • /trunk/extensions/LiquidThreads/i18n/Lqt.i18n.php (modified) (history)
  • /trunk/extensions/LiquidThreads/lqt.css (modified) (history)
  • /trunk/extensions/LiquidThreads/pages/IndividualThreadHistoryView.php (modified) (history)
  • /trunk/extensions/LiquidThreads/pages/NewUserMessagesView.php (modified) (history)
  • /trunk/extensions/LiquidThreads/pages/SpecialNewMessages.php (modified) (history)
  • /trunk/extensions/LiquidThreads/pages/SummaryPageView.php (modified) (history)
  • /trunk/extensions/LiquidThreads/pages/TalkpageArchiveView.php (modified) (history)
  • /trunk/extensions/LiquidThreads/pages/TalkpageHeaderView.php (modified) (history)
  • /trunk/extensions/LiquidThreads/pages/TalkpageView.php (modified) (history)
  • /trunk/extensions/LiquidThreads/pages/ThreadHistoricalRevisionView.php (modified) (history)
  • /trunk/extensions/LiquidThreads/pages/ThreadHistoryListingView.php (modified) (history)
  • /trunk/extensions/LiquidThreads/pages/ThreadPermalinkView.php (modified) (history)

Diff [purge]

Index: trunk/extensions/LiquidThreads/i18n/Lqt.i18n.php
@@ -147,6 +147,13 @@
148148 'lqt_rc_ellipsis' => ' ...',
149149 'lqt_rc_author_original' => '(original author)',
150150 'lqt_rc_author_others' => '(not the author)',
 151+ 'lqt-newmessages-context' => 'Full thread',
 152+ 'lqt-thread-created' => 'Created',
 153+ 'lqt-archive-subtitle' => 'Discussion archive',
 154+ 'lqt-archive-intro' => 'This is the discussion archive for [[$1]].',
 155+ 'lqt-history-time' => 'Time',
 156+ 'lqt-history-user' => 'User',
 157+ 'lqt-history-action' => 'Activity',
151158
152159 // Logging
153160 'lqt-log-name' => 'Threaded discussion log',
Index: trunk/extensions/LiquidThreads/lqt.css
@@ -460,3 +460,9 @@
461461 .lqt_toc td {
462462 border-bottom: 1px solid #aaaaaa;
463463 }
 464+
 465+.lqt-newmessages-left {
 466+ padding-right: 1em;
 467+ vertical-align: top;
 468+ padding-top: 1em;
 469+}
Index: trunk/extensions/LiquidThreads/classes/LqtThreads.php
@@ -28,6 +28,9 @@
2929
3030 static $cache_by_root = array();
3131 static $cache_by_id = array();
 32+
 33+ /** static cache of per-page archivestartdays setting */
 34+ static $archiveStartDays;
3235
3336 static function newThread( $root, $article, $superthread = null, $type = self::TYPE_NORMAL ) {
3437 // SCHEMA changes must be reflected here.
@@ -148,7 +151,6 @@
149152 $tables = "";
150153 }
151154
152 -
153155 $selection_sql = <<< SQL
154156 SELECT DISTINCT thread.* FROM ($tables {$wgDBprefix}thread thread)
155157 $joins
@@ -169,6 +171,8 @@
170172 } // List comprehensions, how I miss thee.
171173 $ancestor_clause = join( ', ', $ancestor_conds );
172174 $selection_clause = join( ', ', $selection_conds );
 175+
 176+ // TODO uses a subquery, unsupported on Wikimedia
173177
174178 $children_sql = <<< SQL
175179 SELECT DISTINCT thread.*, page.*,
@@ -262,30 +266,91 @@
263267 }
264268
265269 /**
266 - * Horrible, horrible!
267 - * List of months in which there are >0 threads, suitable for threadsOfArticleInMonth. */
 270+ * Horrible, horrible!
 271+ * List of months in which there are >0 threads, suitable for threadsOfArticleInMonth.
 272+ * Returned as an array of months in the format yyyymm
 273+ */
268274 static function monthsWhereArticleHasThreads( $article ) {
 275+ // FIXME this probably performs absolutely horribly for pages with lots of threads.
 276+
269277 $threads = Threads::where( Threads::articleClause( $article ) );
270278 $months = array();
 279+
271280 foreach ( $threads as $t ) {
272 - $m = substr( $t->modified(), 0, 6 );
273 - if ( !array_key_exists( $m, $months ) ) {
274 - if ( !in_array( $m, $months ) ) $months[] = $m;
275 - }
 281+ $month = substr( $t->modified(), 0, 6 );
 282+
 283+ $months[$month] = true;
276284 }
277 - return $months;
 285+
 286+ // Some code seems to assume that it's sorted by month, make sure it's true.
 287+ ksort( $months );
 288+
 289+ return array_keys($months);
278290 }
279291
280292 static function articleClause( $article ) {
281293 $dbr = wfGetDB( DB_SLAVE );
282 - $q_article = $dbr->addQuotes( $article->getTitle()->getDBkey() );
283 - return <<<SQL
284 -(thread.thread_article_title = $q_article
285 - AND thread.thread_article_namespace = {$article->getTitle()->getNamespace()})
286 -SQL;
 294+
 295+ $arr = array( 'thread_article_title' => $article->getTitle()->getDBKey(),
 296+ 'thread_article_namespace' => $article->getTitle()->getNamespace() );
 297+
 298+ return $dbr->makeList( $arr, LIST_AND );
287299 }
288300
289301 static function topLevelClause() {
290 - return 'thread.thread_parent is null';
 302+ $dbr = wfGetDB( DB_SLAVE );
 303+
 304+ $arr = array( 'thread_ancestor=thread_id', 'thread_parent' => null );
 305+
 306+ return $dbr->makeList( $arr, LIST_AND );
291307 }
 308+
 309+ static function getArticleArchiveStartDays( $article ) {
 310+ global $wgLqtThreadArchiveStartDays;
 311+
 312+ $article = $article->getId();
 313+
 314+ // Instance cache
 315+ if ( isset( self::$archiveStartDays[$article] ) ) {
 316+ $cacheVal = self::$archiveStartDays[$article];
 317+ if ( !is_null( $cacheVal ) ) {
 318+ return $cacheVal;
 319+ } else {
 320+ return $wgLqtThreadArchiveStartDays;
 321+ }
 322+ }
 323+
 324+ // Memcached: It isn't clear that this is needed yet, but since I already wrote the
 325+ // code, I might as well leave it commented out instead of deleting it.
 326+ // Main reason I've left this commented out is because it isn't obvious how to
 327+ // purge the cache when necessary.
 328+// global $wgMemc;
 329+// $key = wfMemcKey( 'lqt-archive-start-days', $article );
 330+// $cacheVal = $wgMemc->get( $key );
 331+// if ($cacheVal != false) {
 332+// if ( $cacheVal != -1 ) {
 333+// return $cacheVal;
 334+// } else {
 335+// return $wgLqtThreadArchiveStartDays;
 336+// }
 337+// }
 338+
 339+ // Load from the database.
 340+ $dbr = wfGetDB( DB_SLAVE );
 341+
 342+ $dbVal = $dbr->selectField( 'page_props', 'pp_value',
 343+ array( 'pp_propname' => 'lqt-archivestartdays',
 344+ 'pp_page' => $article ), __METHOD__ );
 345+
 346+ if ($dbVal) {
 347+ self::$archiveStartDays[$article] = $dbVal;
 348+# $wgMemc->set( $key, $dbVal, 1800 );
 349+ return $dbVal;
 350+ } else {
 351+ // Negative caching.
 352+ self::$archiveStartDays[$article] = null;
 353+# $wgMemc->set( $key, -1, 86400 );
 354+ return $wgLqtThreadArchiveStartDays;
 355+ }
 356+ }
292357 }
Index: trunk/extensions/LiquidThreads/classes/LqtView.php
@@ -131,15 +131,6 @@
132132 * (2) figuring out what page you're on and what you need to do.
133133 *************************/
134134
135 - static function queryStringFromArray( $vars ) {
136 - $q = '';
137 - if ( $vars && count( $vars ) != 0 ) {
138 - foreach ( $vars as $name => $value )
139 - $q .= "$name=$value&";
140 - }
141 - return $q;
142 - }
143 -
144135 function methodAppliesToThread( $method, $thread ) {
145136 return $this->request->getVal( 'lqt_method' ) == $method &&
146137 $this->request->getVal( 'lqt_operand' ) == $thread->id();
@@ -148,9 +139,12 @@
149140 return $this->request->getVal( 'lqt_method' ) == $method;
150141 }
151142
152 - static function permalinkUrl( $thread, $method = null, $operand = null ) {
 143+ static function permalinkUrl( $thread, $method = null, $operand = null,
 144+ $uquery = array() ) {
153145 list ($title, $query) = self::permalinkData( $thread, $method, $operand );
154146
 147+ $query = array_merge( $query, $uquery );
 148+
155149 $queryString = wfArrayToCGI( $query );
156150
157151 return $title->getFullUrl( $queryString );
@@ -173,12 +167,15 @@
174168 /* This is used for action=history so that the history tab works, which is
175169 why we break the lqt_method paradigm. */
176170 static function permalinkUrlWithQuery( $thread, $query ) {
177 - if ( is_array( $query ) ) $query = self::queryStringFromArray( $query );
178 - return $thread->root()->getTitle()->getFullUrl( $query );
 171+ if ( !is_array($query) ) {
 172+ $query = wfCGIToArray( $query );
 173+ }
 174+
 175+ return self::permalinkUrl( $thread, null, null, $query );
179176 }
180177
181178 static function permalink( $thread, $text = null, $method = null, $operand = null,
182 - $sk = null, $attribs = array() ) {
 179+ $sk = null, $attribs = array(), $uquery = array() ) {
183180 if ( is_null($sk) ) {
184181 global $wgUser;
185182 $sk = $wgUser->getSkin();
@@ -186,50 +183,105 @@
187184
188185 list( $title, $query ) = self::permalinkData( $thread, $method, $operand );
189186
 187+ $query = array_merge( $query, $uquery );
 188+
190189 return $sk->link( $title, $text, $attribs, $query );
191190 }
192 -
193 - static function permalinkUrlWithDiff( $thread ) {
 191+
 192+ static function diffQuery( $thread ) {
194193 $changed_thread = $thread->changeObject();
195194 $curr_rev_id = $changed_thread->rootRevision();
196195 $curr_rev = Revision::newFromTitle( $changed_thread->root()->getTitle(), $curr_rev_id );
197196 $prev_rev = $curr_rev->getPrevious();
198197 $oldid = $prev_rev ? $prev_rev->getId() : "";
199 - return self::permalinkUrlWithQuery( $changed_thread, array( 'lqt_method' => 'diff', 'diff' => $curr_rev_id, 'oldid' => $oldid ) );
 198+
 199+ $query = array( 'lqt_method' => 'diff',
 200+ 'diff' => $curr_rev_id,
 201+ 'oldid' => $oldid );
 202+
 203+ return $query;
200204 }
201205
202 - static function talkpageUrl( $title, $method = null, $operand = null, $includeFragment = true ) {
203 - global $wgRequest; // TODO global + ugly hack.
204 - $query = $method ? "lqt_method=$method" : "";
205 - $query = $operand ? "$query&lqt_operand={$operand->id()}" : $query;
206 - $oldid = $wgRequest->getVal( 'oldid', null ); if ( $oldid !== null ) {
 206+ static function permalinkUrlWithDiff( $thread ) {
 207+ $query = self::diffQuery( $thread );
 208+ return $this->permalinkUrl( $thread->changeObject(), null, null, $query );
 209+ }
 210+
 211+ static function diffPermalink( $thread, $text ) {
 212+ $query = self::diffQuery( $thread );
 213+ return $this->permalink( $thread, $text, null, null, null, array(), $query );
 214+ }
 215+
 216+ static function talkpageLink( $title, $text = null , $method=null, $operand=null,
 217+ $includeFragment=true, $attribs = array() ) {
 218+ list( $title, $query ) = self::talkpageLinkData( $title, $method, $operand,
 219+ $includeFragment );
 220+
 221+ global $wgUser;
 222+ $sk = $wgUser->getSkin();
 223+
 224+ return $sk->link( $title, $text, $attribs, $query );
 225+ }
 226+
 227+ static function talkpageLinkData( $title, $method = null, $operand = null,
 228+ $includeFragment = true ) {
 229+ global $wgRequest;
 230+ $query = array();
 231+
 232+ if ($method) {
 233+ $query['lqt_method'] = $method;
 234+ }
 235+
 236+ if ($operand) {
 237+ $query['lqt_operand'] = $operand->id();
 238+ }
 239+
 240+ $oldid = $wgRequest->getVal( 'oldid', null );
 241+
 242+ if ( $oldid !== null ) {
207243 // this is an immensely ugly hack to make editing old revisions work.
208 - $query = "$query&oldid=$oldid";
 244+ $query['oldid'] = $oldid;
209245 }
210 - return $title->getFullURL( $query ) . ( $operand && $includeFragment ? "#lqt_thread_{$operand->id()}" : "" );
 246+
 247+ // Add fragment if appropriate.
 248+ if ($operand && $includeFragment) {
 249+ $title->mFragment = 'lqt_thread_'.$operand->id();
 250+ }
 251+
 252+ return array( $title, $query );
211253 }
212254
 255+ static function talkpageUrl( $title, $method = null, $operand = null,
 256+ $includeFragment = true ) {
 257+ global $wgUser;
 258+ $sk = $wgUser->getSkin();
 259+
 260+ list( $title, $query ) =
 261+ self::talkpageLinkData( $title, $method, $operand, $includeFragment );
 262+
 263+ return $title->getLinkUrl( $query );
 264+ }
213265
 266+
214267 /**
215268 * Return a URL for the current page, including Title and query vars,
216269 * with the given replacements made.
217270 * @param $repls array( 'name'=>new_value, ... )
218271 */
219 - function queryReplace( $repls ) {
220 - $vs = $this->request->getValues();
221 - $rs = array();
222 -/* foreach ($vs as $k => $v) {
223 - if ( array_key_exists( $k, $repls ) ) {
224 - $rs[$k] = $repls[$k];
225 - } else {
226 - $rs[$k] = $vs[$k];
227 - }
 272+ function queryReplaceLink( $repls ) {
 273+ $query = $this->getReplacedQuery( $repls );
 274+
 275+ return $this->title->getFullURL( wfArrayToCGI( $vs ) );
 276+ }
 277+
 278+ function getReplacedQuery( $replacements ) {
 279+ $values = $this->request->getValues();
 280+
 281+ foreach ( $replacements as $k => $v ) {
 282+ $values[$k] = $v;
228283 }
229 -*/
230 - foreach ( $repls as $k => $v ) {
231 - $vs[$k] = $v;
232 - }
233 - return $this->title->getFullURL( self::queryStringFromArray( $vs ) );
 284+
 285+ return $values;
234286 }
235287
236288 /*************************************************************
@@ -510,7 +562,8 @@
511563
512564 $user_can_edit = $thread->root()->getTitle()->quickUserCan( 'edit' );
513565
514 - $commands[] = array( 'label' => $user_can_edit ? wfMsg( 'edit' ) : wfMsg( 'viewsource' ),
 566+ $commands[] = array( 'label' => $user_can_edit
 567+ ? wfMsg( 'edit' ) : wfMsg( 'viewsource' ),
515568 'href' => $this->talkpageUrl( $this->title, 'edit', $thread ),
516569 'enabled' => true );
517570
@@ -594,43 +647,17 @@
595648 $wgOut->addScript( $s );
596649 }
597650
598 - /* @return False if the article and revision do not exist and we didn't show it, true if we did. */
 651+ /* @return False if the article and revision do not exist. The HTML of the page to
 652+ * display if it exists. Note that this impacts the state out OutputPage by adding
 653+ * all the other relevant parts of the parser output. If you don't want this, call
 654+ * $post->getParserOutput. */
599655 function showPostBody( $post, $oldid = null ) {
600 - /* Why isn't this all encapsulated in Article somewhere? TODO */
601 - global $wgEnableParserCache;
602 -
603 - // Should the parser cache be used?
604 - $pcache = $wgEnableParserCache &&
605 - intval( $this->user->getOption( 'stubthreshold' ) ) == 0 &&
606 - $post->exists() &&
607 - $oldid === null;
608 - wfDebug( 'LqtView::showPostBody using parser cache: ' . ( $pcache ? 'yes' : 'no' ) . "\n" );
609 - if ( $this->user->getOption( 'stubthreshold' ) ) {
610 - wfIncrStats( 'pcache_miss_stub' );
611 - }
612 -
613 - $outputDone = false;
614 - if ( $pcache ) {
615 - $outputDone = $this->output->tryParserCache( $post, $this->user );
616 - }
617 -
618 - if ( !$outputDone ) {
619 - // Cache miss; parse and output it.
620 - $rev = Revision::newFromTitle( $post->getTitle(), $oldid );
621 - if ( $rev && $oldid ) {
622 - // don't save oldids in the parser cache.
623 - $this->output->addWikiText( $rev->getText() );
624 - return true;
625 - }
626 - else if ( $rev ) {
627 - $post->outputWikiText( $rev->getText(), true );
628 - return true;
629 - } else {
630 - return false;
631 - }
632 - } else {
633 - return true;
634 - }
 656+ global $wgOut;
 657+
 658+ $parserOutput = $post->getParserOutput( $oldid );
 659+ $wgOut->addParserOutputNoText( $parserOutput );
 660+
 661+ return $parserOutput->getText();
635662 }
636663
637664 function colorTest() {
@@ -677,7 +704,7 @@
678705
679706 $html = Xml::tags( 'ul', array( 'class' => 'lqt_footer' ), $html );
680707
681 - $this->output->addHTML( $html );
 708+ return $html;
682709 }
683710
684711 function listItemsForCommands( $commands ) {
@@ -709,6 +736,8 @@
710737 // or from oldid (which is a page rev). But oldid only applies to the
711738 // thread being requested, not any replies. TODO: eliminate the need
712739 // for article-level histories.
 740+ $divClass = $this->postDivClass( $thread );
 741+ $html = Xml::openElement( 'div', array( 'class' => $divClass ) );
713742 $page_rev = $this->request->getVal( 'oldid', null );
714743 if ( $page_rev !== null && $this->title->equals( $thread->root()->getTitle() ) ) {
715744 $oldid = $page_rev;
@@ -716,22 +745,30 @@
717746 $oldid = $thread->isHistorical() ? $thread->rootRevision() : null;
718747 }
719748
720 - $this->openDiv( $this->postDivClass( $thread ) );
721 -
722749 if ( $this->methodAppliesToThread( 'edit', $thread ) ) {
 750+ $this->output->addHTML( $html );
 751+ $html = '';
 752+
 753+ // No way am I refactoring EditForm to send its output as HTML.
 754+ // so I'm just flushing the HTML and displaying it as-is.
723755 $this->showPostEditingForm( $thread );
724756 } else {
725 - $this->showPostBody( $post, $oldid );
726 - $this->showThreadFooter( $thread );
 757+ $html .= $this->showPostBody( $post, $oldid );
 758+ $html .= $this->showThreadFooter( $thread );
727759 }
728760
729 - $this->closeDiv();
 761+ // wish I didn't have to use this open/closeElement cruft.
 762+ $html .= Xml::closeElement( 'div' );
730763
731764 if ( $this->methodAppliesToThread( 'reply', $thread ) ) {
732 - $this->indent( $thread );
 765+ // As with above, flush HTML to avoid refactoring EditPage.
 766+ $html .= $this->indent( $thread );
 767+ $this->output->addHTML( $html );
733768 $this->showReplyForm( $thread );
734 - $this->unindent( $thread );
 769+ $html = $this->unindent( $thread );
735770 }
 771+
 772+ $this->output->addHTML( $html );
736773
737774 $popts->setEditSection( $previous_editsection );
738775 $this->output->parserOptions( $popts );
@@ -753,8 +790,10 @@
754791 $html = Xml::tags( 'h'.$this->headerLevel, array( 'class' => 'lqt_header' ),
755792 $html ) . $commands_html;
756793
757 - $this->output->addHTML( $html );
 794+ return $html;
758795 }
 796+
 797+ return '';
759798 }
760799
761800 function postDivClass( $thread ) {
@@ -766,130 +805,169 @@
767806 }
768807
769808 function showThread( $thread ) {
770 - global $wgLang; # TODO global.
 809+ global $wgLang;
 810+
 811+ $sk = $this->user->getSkin();
 812+
 813+ $html = '';
771814
772815 // Safeguard
773816 if ( $thread->type() == Threads::TYPE_DELETED
774 - && ! $this->request->getBool( 'lqt_show_deleted_threads' ) )
775 - return;
 817+ && ! ($this->request->getBool( 'lqt_show_deleted_threads' )
 818+ && $this->user->isAllowed( 'deletedhistory' ) ) ) {
 819+ return;
 820+ }
776821
777822 if ( $this->lastUnindentedSuperthread ) {
778823 wfLoadExtensionMessages( 'LiquidThreads' );
779824 $tmp = $this->lastUnindentedSuperthread;
780 - $msg = wfMsg( 'lqt_in_response_to',
781 - '<a href="#lqt_thread_' . $tmp->id() . '">' . $tmp->title()->getText() . '</a>',
782 - $tmp->root()->originalAuthor()->getName() );
783 - $this->output->addHTML( '<span class="lqt_nonindent_message">&larr;' . $msg . '</span>' );
 825+ $replyLink = Xml::tags( 'a', array( 'href' => '#'.$this->anchorName( $tmp ) ),
 826+ $tmp->subject() );
 827+ $msg = wfMsgExt( 'lqt_in_response_to', array( 'parseinline', 'replaceafter' ),
 828+ array( $replyLink, $tmp->root()->originalAuthor()->getName() ) );
 829+
 830+ $html .= Xml::tags( 'span', array( 'class' => 'lqt_nonindent_message' ),
 831+ "&larr; $msg" );
784832 }
785833
786834
787 - $this->showThreadHeading( $thread );
 835+ $html .= $this->showThreadHeading( $thread );
788836
789 - $this->output->addHTML( "<a name=\"{$this->anchorName($thread)}\" ></a>" );
 837+ $html .= Xml::element( 'a', array( 'name' => $this->anchorName($thread) ), ' ' );
790838
791839 if ( $thread->type() == Threads::TYPE_MOVED ) {
792840 wfLoadExtensionMessages( 'LiquidThreads' );
 841+
793842 $revision = Revision::newFromTitle( $thread->title() );
794843 $target = Title::newFromRedirect( $revision->getText() );
795844 $t_thread = Threads::withRoot( new Article( $target ) );
796845 $author = $thread->root()->originalAuthor();
797 - $sig = $this->user->getSkin()->userLink( $author->getID(), $author->getName() ) .
798 - $this->user->getSkin()->userToolLinks( $author->getID(), $author->getName() );
799 - $this->output->addHTML( wfMsg( 'lqt_move_placeholder',
800 - '<a href="' . $target->getFullURL() . '">' . $target->getText() . '</a>',
801 - $sig,
802 - $wgLang->date( $thread->modified() ),
803 - $wgLang->time( $thread->modified() )
804 - ) );
805 - return;
 846+ $sig = $sk->userLink( $author->getID(), $author->getName() ) .
 847+ $sk->userToolLinks( $author->getID(), $author->getName() );
 848+
 849+ $html .=
 850+ wfMsgExt( 'lqt_move_placeholder', array( 'parseinline', 'replaceafter' ),
 851+ $sk->link( $target ),
 852+ $sig,
 853+ $wgLang->date( $thread->modified() ),
 854+ $wgLang->time( $thread->modified() )
 855+ );
 856+ return $html;
806857 }
807858
808859 if ( $thread->type() == Threads::TYPE_DELETED ) {
809860 wfLoadExtensionMessages( 'LiquidThreads' );
810861 if ( in_array( 'deletedhistory', $this->user->getRights() ) ) {
811 - $this->output->addHTML( '<p>' . wfMsg( 'lqt_thread_deleted_for_sysops' ) . '</p>' );
 862+ $html .= wfMsgExt( 'lqt_thread_deleted_for_sysops', 'parse' );
812863 }
813864 else {
814 - $this->output->addHTML( '<p><em>' . wfMsg( 'lqt_thread_deleted' ) . '</em></p>' );
815 - return;
 865+ $msg = wfMsgExt( 'lqt_thread_deleted', 'parseinline' );
 866+ $msg = Xml::tags( 'em', null, $msg );
 867+ $msg = Xml::tags( 'p', null, $msg );
 868+ $html .= $msg;
 869+ return $html;
816870 }
817871 }
818872 if ( $thread->summary() ) {
819 - $this->showSummary( $thread );
 873+ $html .= $this->showPostBody( $thread->summary() );
820874 } elseif( $thread->isArchiveEligible() )
821875 {
822876 wfLoadExtensionMessages( 'LiquidThreads' );
823877
824878 $permalink_text = wfMsgNoTrans( 'lqt_summary_notice_link' );
825879 $permalink = $this->permalink( $thread, $permalink_text );
826 - $html = wfMsgExt( 'lqt_summary_notice', array('parseinline', 'replaceafter'),
 880+ $msg = wfMsgExt( 'lqt_summary_notice', array('parseinline', 'replaceafter'),
827881 array( $permalink, $thread->getArchiveStartDays() ) );
828 - $html = Xml::tags( 'p', array( 'class' => 'lqt_summary_notice' ), $html );
 882+ $msg = Xml::tags( 'p', array( 'class' => 'lqt_summary_notice' ), $msg );
829883
830 - $this->output->addHTML( $html );
 884+ $html .= $msg;
831885 }
832886
833 - $this->openDiv( 'lqt_thread', "lqt_thread_id_{$thread->id()}" );
 887+ // Sigh.
 888+ $html .= Xml::openElement( 'div', array( 'class' => 'lqt_thread',
 889+ 'id' => 'lqt_thread_id_'. $thread->id() ) );
 890+
 891+ // Unfortunately, I can't rewrite showRootPost() to pass back HTML
 892+ // as it would involve rewriting EditPage, which I do NOT intend to do.
834893
 894+ $this->output->addHTML( $html );
 895+
835896 $this->showRootPost( $thread );
836897
837 - if ( $thread->hasSubthreads() ) $this->indent( $thread );
838 - foreach ( $thread->subthreads() as $st ) {
839 - $this->showThread( $st );
 898+ if ( $thread->hasSubthreads() ) {
 899+ $this->output->addHTML( $this->indent( $thread ) );
 900+
 901+ foreach ( $thread->subthreads() as $st ) {
 902+ $this->showThread( $st );
 903+ }
 904+
 905+ $this->output->addHTML( $this->unindent( $thread ) );
840906 }
841 - if ( $thread->hasSubthreads() ) $this->unindent( $thread );
842907
843 - $this->closeDiv();
 908+ $this->output->addHTML( Xml::closeElement( 'div' ) );
844909 }
845910
 911+ // FIXME does indentation need rethinking?
846912 function indent( $thread ) {
 913+ $result = '';
847914 if ( $this->headerLevel <= $this->maxIndentationLevel ) {
848 - $this->output->addHTML( '<dl class="lqt_replies"><dd>' );
 915+ $result = '<dl class="lqt_replies"><dd>';
849916 } else {
850 - $this->output->addHTML( '<div class="lqt_replies_without_indent">' );
 917+ $result = '<div class="lqt_replies_without_indent">';
851918 }
852919 $this->lastUnindentedSuperthread = null;
853920 $this->headerLevel += 1;
 921+
 922+ return $result;
854923 }
 924+
855925 function unindent( $thread ) {
 926+ $result = '';
856927 if ( $this->headerLevel <= $this->maxIndentationLevel + 1 ) {
857 - $this->output->addHTML( '</dd></dl>' );
 928+ $result = '</dd></dl>';
858929 } else {
859 - $this->output->addHTML( '</div>' );
 930+ $result = '</div>';
860931 }
861932 // See the beginning of showThread().
862933 $this->lastUnindentedSuperthread = $thread->superthread();
863934 $this->headerLevel -= 1;
 935+
 936+ return $result;
864937 }
865938
866 - function openDiv( $class = '', $id = '' ) {
867 - $this->output->addHTML( Xml::openElement( 'div', array( 'class' => $class, 'id' => $id ) ) );
868 - }
869 -
870 - function closeDiv() {
871 - $this->output->addHTML( Xml::closeElement( 'div' ) );
872 - }
873 -
874 - function showSummary( $t ) {
 939+ function getSummary( $t ) {
875940 if ( !$t->summary() ) return;
876941 wfLoadExtensionMessages( 'LiquidThreads' );
877 - $label = wfMsg( 'lqt_summary_label' );
878 - $edit = strtolower( wfMsg( 'edit' ) );
879 - $link = strtolower( wfMsg( 'lqt_permalink' ) );
880 - $this->output->addHTML( <<<HTML
881 - <div class='lqt_thread_permalink_summary'>
882 - <span class="lqt_thread_permalink_summary_title">
883 - $label
884 - </span><span class="lqt_thread_permalink_summary_edit">
885 - [<a href="{$t->summary()->getTitle()->getFullURL()}">$link</a>]
886 - [<a href="{$this->permalinkUrl($t,'summarize')}">$edit</a>]
887 - </span>
888 -HTML
889 - );
890 - $this->openDiv( 'lqt_thread_permalink_summary_body' );
891 - $this->showPostBody( $t->summary() );
892 - $this->closeDiv();
893 - $this->closeDiv();
 942+ global $wgUser;
 943+ $sk = $wgUser->getSkin();
 944+
 945+ $label = wfMsgExt( 'lqt_summary_label', 'parseinline' );
 946+ $edit_text = wfMsgExt( 'edit', 'parseinline' );
 947+ $link_text = wfMsg( 'lqt_permalink', 'parseinline' );
 948+
 949+ $html = '';
 950+
 951+ $html .= Xml::tags( 'span',
 952+ array( 'class' => 'lqt_thread_permalink_summary_title' ),
 953+ $label );
 954+
 955+ $link = $sk->link( $t->summary()->getTitle(), $link_text );
 956+ $edit_link = $this->permalink( $t, $edit_text, 'summarize' );
 957+ $links = "[$link]\n[$edit_link]";
 958+ $html .= Xml::tags( 'span', array( 'class' => 'lqt_thread_permalink_summary_edit' ),
 959+ $links );
 960+
 961+ $summary_body = $this->showPostBody( $t->summary() );
 962+ $html .= Xml::tags( 'div', array( 'class' => 'lqt_thread_permalink_summary_body' ),
 963+ $summary_body );
 964+
 965+ $html = Xml::tags( 'div', array( 'class' => 'lqt_thread_permalink_summary' ), $html );
 966+
 967+ return $html;
894968 }
 969+
 970+ function showSummary( $t ) {
 971+ $this->output->addHTML( $this->getSummary( $t ) );
 972+ }
895973
896974 }
Index: trunk/extensions/LiquidThreads/classes/LqtNewMessages.php
@@ -47,15 +47,12 @@
4848
4949 $dbw =& wfGetDB( DB_MASTER );
5050
51 - $talkpage_t = $t->article()->getTitle()->getSubjectPage();
 51+ $tpTitle = $t->article()->getTitle();
5252 $root_t = $t->root()->getTitle();
5353
54 - $q_talkpage_t = $dbw->addQuotes( $talkpage_t->getDBkey() );
55 - $q_root_t = $dbw->addQuotes( $root_t->getDBkey() );
56 -
5754 // Select any applicable watchlist entries for the thread.
58 - $talkpageWhere = array( 'wl_namespace' => $talkpage_t->getNamespace(),
59 - 'wl_title' => $talkpage_t->getDBkey() );
 55+ $talkpageWhere = array( 'wl_namespace' => $tpTitle->getNamespace(),
 56+ 'wl_title' => $tpTitle->getDBkey() );
6057 $rootWhere = array( 'wl_namespace' => $root_t->getNamespace(),
6158 'wl_title' => $root_t->getDBkey() );
6259
@@ -64,9 +61,6 @@
6562
6663 $where_clause = $dbw->makeList( array( $talkpageWhere, $rootWhere ), LIST_OR );
6764
68 - // it sucks to not have 'on duplicate key update'. first update users who already have a ums for this thread
69 - // and who have already read it, by setting their state to unread.
70 -
7165 // Pull users to update the message state for, including whether or not a
7266 // user_message_state row exists for them, and whether or not to send an email
7367 // notification.
@@ -126,7 +120,6 @@
127121 $notify_users[] = $user->getId();
128122 }
129123 }
130 -
131124 }
132125
133126 // Do the actual updates
@@ -216,7 +209,7 @@
217210 return Threads::where( array( 'ums_read_timestamp is null',
218211 'ums_user' => $user->getID(),
219212 'ums_thread = thread.thread_id',
220 - 'NOT (' . Threads::articleClause( new Article( $user->getUserPage() ) ) . ')' ),
 213+ 'NOT (' . Threads::articleClause( new Article( $user->getTalkPage() ) ) . ')' ),
221214 array(), array( 'user_message_state' ) );
222215 }
223216 }
Index: trunk/extensions/LiquidThreads/classes/LqtThread.php
@@ -47,9 +47,6 @@
4848 protected $double;
4949
5050 protected $replies;
51 -
52 - /** static cache of per-page archivestartdays setting */
53 - static $archiveStartDays;
5451
5552 function isHistorical() {
5653 return false;
@@ -400,6 +397,7 @@
401398 return $this->ancestorId;
402399 }
403400
 401+ // The 'root' is the page in the Thread namespace corresponding to this thread.
404402 function root() {
405403 if ( !$this->rootId ) return null;
406404 if ( !$this->root ) $this->root = new Post( Title::newFromID( $this->rootId ),
@@ -457,7 +455,7 @@
458456 return $this->root()->getTitle();
459457 }
460458
461 - private function splitIncrementFromSubject( $subject_string ) {
 459+ static function splitIncrementFromSubject( $subject_string ) {
462460 preg_match( '/^(.*) \((\d+)\)$/', $subject_string, $matches );
463461 if ( count( $matches ) != 3 )
464462 throw new MWException( __METHOD__ . ": thread subject has no increment: " . $subject_string );
@@ -474,15 +472,21 @@
475473 }
476474
477475 function wikilinkWithoutIncrement() {
478 - $tmp = $this->splitIncrementFromSubject( $this->wikilink() ); return $tmp[1];
 476+ $tmp = self::splitIncrementFromSubject( $this->wikilink() );
 477+
 478+ return $tmp[1];
479479 }
480480
481481 function subjectWithoutIncrement() {
482 - $tmp = $this->splitIncrementFromSubject( $this->subject() ); return $tmp[1];
 482+ $tmp = self::splitIncrementFromSubject( $this->subject() );
 483+
 484+ return $tmp[1];
483485 }
484486
485487 function increment() {
486 - $tmp = $this->splitIncrementFromSubject( $this->subject() ); return $tmp[2];
 488+ $tmp = self::splitIncrementFromSubject( $this->subject() );
 489+
 490+ return $tmp[2];
487491 }
488492
489493 function hasDistinctSubject() {
@@ -616,51 +620,6 @@
617621 }
618622
619623 function getArchiveStartDays() {
620 - global $wgLqtThreadArchiveStartDays;
621 -
622 - $article = $this->article()->getId();
623 -
624 - // Instance cache
625 - if ( isset( self::$archiveStartDays[$article] ) ) {
626 - $cacheVal = self::$archiveStartDays[$article];
627 - if ( !is_null( $cacheVal ) ) {
628 - return $cacheVal;
629 - } else {
630 - return $wgLqtThreadArchiveStartDays;
631 - }
632 - }
633 -
634 - // Memcached: It isn't clear that this is needed yet, but since I already wrote the
635 - // code, I might as well leave it commented out instead of deleting it.
636 - // Main reason I've left this commented out is because it isn't obvious how to
637 - // purge the cache when necessary.
638 -// global $wgMemc;
639 -// $key = wfMemcKey( 'lqt-archive-start-days', $article );
640 -// $cacheVal = $wgMemc->get( $key );
641 -// if ($cacheVal != false) {
642 -// if ( $cacheVal != -1 ) {
643 -// return $cacheVal;
644 -// } else {
645 -// return $wgLqtThreadArchiveStartDays;
646 -// }
647 -// }
648 -
649 - // Load from the database.
650 - $dbr = wfGetDB( DB_SLAVE );
651 -
652 - $dbVal = $dbr->selectField( 'page_props', 'pp_value',
653 - array( 'pp_propname' => 'lqt-archivestartdays',
654 - 'pp_page' => $article ), __METHOD__ );
655 -
656 - if ($dbVal) {
657 - self::$archiveStartDays[$article] = $dbVal;
658 -# $wgMemc->set( $key, $dbVal, 1800 );
659 - return $dbVal;
660 - } else {
661 - // Negative caching.
662 - self::$archiveStartDays[$article] = null;
663 -# $wgMemc->set( $key, -1, 86400 );
664 - return $wgLqtThreadArchiveStartDays;
665 - }
 624+ return Threads::getArticleArchiveStartDays( $this->article() );
666625 }
667626 }
Index: trunk/extensions/LiquidThreads/pages/NewUserMessagesView.php
@@ -10,76 +10,53 @@
1111
1212 protected function htmlForReadButton( $label, $title, $class, $ids ) {
1313 $ids_s = implode( ',', $ids );
14 - return <<<HTML
15 - <form method="POST" class="{$class}">
16 - <input type="hidden" name="lqt_method" value="mark_as_read" />
17 - <input type="hidden" name="lqt_operand" value="{$ids_s}" />
18 - <input type="submit" value="{$label}" name="lqt_read_button" title="{$title}" />
19 - </form>
20 -HTML;
 14+ $html = '';
 15+ $html .= Xml::hidden( 'lqt_method', 'mark_as_read' );
 16+ $html .= Xml::hidden( 'lqt_operand', $ids_s );
 17+ $html .= Xml::submitButton( $label, array( 'name' => 'lqt_read_button',
 18+ 'title' => $title ) );
 19+ $html = Xml::tags( 'form', array( 'method' => 'post', 'class' => $class ), $html );
 20+
 21+ return $html;
2122 }
2223
23 - function showReadAllButton( $threads ) {
 24+ function getReadAllButton( $threads ) {
2425 wfLoadExtensionMessages( 'LiquidThreads' );
25 - $ids = array_map( create_function( '$t', 'return $t->id();' ), $threads );
26 - $this->output->addHTML(
27 - $this->htmlForReadButton(
28 - wfMsg( 'lqt-read-all' ),
29 - wfMsg( 'lqt-read-all-tooltip' ),
30 - "lqt_newmessages_read_all_button",
31 - $ids )
32 - );
 26+ $ids = array_map( create_function( '$t', 'return $t->id();' ), $threads ); // ew
 27+ return $this->htmlForReadButton(
 28+ wfMsg( 'lqt-read-all' ),
 29+ wfMsg( 'lqt-read-all-tooltip' ),
 30+ "lqt_newmessages_read_all_button",
 31+ $ids
 32+ );
3333 }
3434
35 - function preShowThread( $t ) {
 35+ function getUndoButton( $ids ) {
3636 wfLoadExtensionMessages( 'LiquidThreads' );
37 - // $t_ids = implode(',', array_map(create_function('$t', 'return $t->id();'), $this->targets[$t->id()]));
38 - $read_button = $this->htmlForReadButton(
39 - wfMsg( 'lqt-read-message' ),
40 - wfMsg( 'lqt-read-message-tooltip' ),
41 - 'lqt_newmessages_read_button',
42 - $this->targets[$t->id()] );
43 - $this->output->addHTML( <<<HTML
44 -<table ><tr>
45 -<td style="padding-right: 1em; vertical-align: top; padding-top: 1em;" >
46 -$read_button
47 -</td>
48 -<td>
49 -HTML
50 - );
51 - }
52 -
53 - function postShowThread( $t ) {
54 - $this->output->addHTML( <<<HTML
55 -</td>
56 -</tr></table>
57 -HTML
58 - );
59 - }
60 -
61 - function showUndo( $ids ) {
62 - wfLoadExtensionMessages( 'LiquidThreads' );
 37+
6338 if ( count( $ids ) == 1 ) {
6439 $t = Threads::withId( $ids[0] );
6540 if ( !$t )
6641 return; // empty or just bogus operand.
67 - $msg = wfMsg( 'lqt-marked-read', $t->subject() );
 42+ $msg = wfMsgExt( 'lqt-marked-read', 'parseinline', array($t->subject()) );
6843 } else {
6944 $count = count( $ids );
70 - $msg = wfMsg( 'lqt-count-marked-read', $count );
 45+ $msg = wfMsgExt( 'lqt-count-marked-read', 'parseinline', array($count) );
7146 }
7247 $operand = implode( ',', $ids );
73 - $lqt_email_undo = wfMsg ( 'lqt-email-undo' );
74 - $lqt_info_undo = wfMsg ( 'lqt-email-info-undo' );
75 - $this->output->addHTML( <<<HTML
76 -<form method="POST" class="lqt_undo_mark_as_read">
77 -$msg
78 -<input type="hidden" name="lqt_method" value="mark_as_unread" />
79 -<input type="hidden" name="lqt_operand" value="{$operand}" />
80 -<input type="submit" value="{$lqt_email_undo}" name="lqt_read_button" title="{$lqt_info_undo}" />
81 -</form>
82 -HTML
83 - );
 48+
 49+ $html = '';
 50+ $html .= $msg;
 51+ $html .= Xml::hidden( 'lqt_method', 'mark_as_unread' );
 52+ $html .= Xml::hidden( 'lqt_operand', $operand );
 53+ $html .= Xml::submitButton( wfMsg('lqt-email-undo'), array( 'name' => 'lqt_read_button',
 54+ 'title' => wfMsg( 'lqt-email-info-undo' ) ) );
 55+
 56+ $html = Xml::tags( 'form',
 57+ array( 'method' => 'post', 'class' => 'lqt_undo_mark_as_read' ),
 58+ $html );
 59+
 60+ return $html;
8461 }
8562
8663 function postDivClass( $thread ) {
@@ -102,16 +79,15 @@
10380
10481 if ( $this->request->wasPosted() && $this->methodApplies( 'mark_as_unread' ) ) {
10582 $ids = explode( ',', $this->request->getVal( 'lqt_operand', '' ) );
 83+
10684 if ( $ids !== false ) {
10785 foreach ( $ids as $id ) {
10886 $tmp_thread = Threads::withId( $id ); if ( $tmp_thread )
109 - NewMessages::markThreadAsReadByUser( $tmp_thread, $this->user );
 87+ NewMessages::markThreadAsUnReadByUser( $tmp_thread, $this->user );
11088 }
11189 $this->output->redirect( $this->title->getFullURL() );
11290 }
113 - }
114 -
115 - else if ( $this->request->wasPosted() && $this->methodApplies( 'mark_as_read' ) ) {
 91+ } elseif ( $this->request->wasPosted() && $this->methodApplies( 'mark_as_read' ) ) {
11692 $ids = explode( ',', $this->request->getVal( 'lqt_operand' ) );
11793 if ( $ids !== false ) {
11894 foreach ( $ids as $id ) {
@@ -122,11 +98,9 @@
12399 $query = 'lqt_method=undo_mark_as_read&lqt_operand=' . implode( ',', $ids );
124100 $this->output->redirect( $this->title->getFullURL( $query ) );
125101 }
126 - }
127 -
128 - else if ( $this->methodApplies( 'undo_mark_as_read' ) ) {
 102+ } elseif ( $this->methodApplies( 'undo_mark_as_read' ) ) {
129103 $ids = explode( ',', $this->request->getVal( 'lqt_operand', '' ) );
130 - $this->showUndo( $ids );
 104+ $this->output->addHTML( $this->getUndoButton( $ids ) );
131105 }
132106 }
133107
@@ -159,13 +133,36 @@
160134 // each thread is going to have a different article... this is pretty ugly.
161135 $this->article = $t->article();
162136
163 - $this->preShowThread( $t );
164 - $this->showThread( $t );
165 - $this->postShowThread( $t );
 137+ $this->showWrappedThread( $t );
166138 }
167139 return false;
168140 }
 141+
 142+ function showWrappedThread( $t ) {
 143+ wfLoadExtensionMessages( 'LiquidThreads' );
 144+
 145+ $read_button = $this->htmlForReadButton(
 146+ wfMsg( 'lqt-read-message' ),
 147+ wfMsg( 'lqt-read-message-tooltip' ),
 148+ 'lqt_newmessages_read_button',
 149+ $this->targets[$t->id()] );
 150+
 151+ // Left-hand column � read button and context link to the full thread.
 152+ $topmostThread = $t->topmostThread();
 153+ $contextLink = $this->permalink( $topmostThread,
 154+ wfMsgExt( 'lqt-newmessages-context', 'parseinline' ) );
 155+ $leftColumn = Xml::tags( 'p', null, $read_button ) .
 156+ Xml::tags( 'p', null, $contextLink );
 157+ $leftColumn = Xml::tags( 'td', array( 'class' => 'mw-lqt-newmessages-left' ),
 158+ $leftColumn );
 159+ $html = "<table><tr>$leftColumn<td>";
 160+ $this->output->addHTML( $html );
169161
 162+ $this->showThread( $t );
 163+
 164+ $this->output->addHTML( "</td></tr></table>" );
 165+ }
 166+
170167 function setThreads( $threads ) {
171168 $this->threads = $threads;
172169 }
Index: trunk/extensions/LiquidThreads/pages/TalkpageHeaderView.php
@@ -26,15 +26,28 @@
2727
2828 if ( $wgRequest->getVal( 'action' ) === 'edit' ) {
2929 wfLoadExtensionMessages( 'LiquidThreads' );
30 - $warn_bold = '<strong>' . wfMsg( 'lqt_header_warning_bold' ) . '</strong>';
31 - $warn_link = '<a href="' . $this->talkpageUrl( $wgTitle, 'talkpage_new_thread' ) . '">' .
32 - wfMsg( 'lqt_header_warning_new_discussion' ) . '</a>';
33 - $wgOut->addHTML( '<p class="lqt_header_warning">' .
34 - wfMsg( 'lqt_header_warning_before_big', $warn_bold, $warn_link ) .
35 - '<big>' . wfMsg( 'lqt_header_warning_big', $warn_bold, $warn_link ) . '</big>' .
36 - wfMsg( 'word-separator' ) .
37 - wfMsg( 'lqt_header_warning_after_big', $warn_bold, $warn_link ) .
38 - '</p>' );
 30+
 31+ $html = '';
 32+
 33+ $warn_bold = Xml::tags( 'strong', null,
 34+ wfMsgExt( 'lqt_header_warning_bold', 'parseinline' ) );
 35+
 36+ $warn_link =
 37+ $this->talkpageLink( $wgTitle, wfMsgExt( 'lqt_header_warning_new_discussion',
 38+ 'parseinline' ), 'talkpage_new_thread' );
 39+
 40+ $html .= wfMsgExt( 'lqt_header_warning_before_big', 'parseinline',
 41+ array( $warn_bold, $warn_link ) );
 42+ $html .= Xml::tags( 'big', null,
 43+ wfMsgExt( 'lqt_header_warning_big', 'parseinline',
 44+ array( $warn_bold, $warn_link ) ) );
 45+ $html .= wfMsg( 'word-separator' );
 46+ $html .= wfMsgExt( 'lqt_header_warning_after_big', 'parseinline',
 47+ array( $warn_bold, $warn_link ) );
 48+
 49+ $html = Xml::tags( 'p', array( 'class' => 'lqt_header_warning' ), $html );
 50+
 51+ $wgOut->addHTML( $html );
3952 }
4053
4154 return true;
Index: trunk/extensions/LiquidThreads/pages/IndividualThreadHistoryView.php
@@ -15,9 +15,13 @@
1616 and of an old revision from getSubtitle() below. */
1717 function customizeSubtitle() {
1818 wfLoadExtensionMessages( 'LiquidThreads' );
19 - $msg = wfMsg( 'lqt_hist_view_whole_thread' );
20 - $threadhist = "<a href=\"{$this->permalinkUrl($this->thread->topmostThread(), 'thread_history')}\">$msg</a>";
21 - $this->output->setSubtitle( parent::getSubtitle() . '<br />' . $this->output->getSubtitle() . "<br />$threadhist" );
 19+ $msg = wfMsgExt( 'lqt_hist_view_whole_thread', 'parseinline' );
 20+ $threadhist = $this->permalink( $this->thread->topmostThread(),
 21+ $msg,
 22+ 'thread_history' );
 23+ $this->output->setSubtitle( parent::getSubtitle() . '<br />' .
 24+ $this->output->getSubtitle() .
 25+ "<br />$threadhist" );
2226 return true;
2327 }
2428
Index: trunk/extensions/LiquidThreads/pages/ThreadHistoryListingView.php
@@ -3,105 +3,138 @@
44 if ( !defined( 'MEDIAWIKI' ) ) die;
55
66 class ThreadHistoryListingView extends ThreadPermalinkView {
 7+ function show() {
 8+ global $wgHooks;
 9+ $wgHooks['SkinTemplateTabs'][] = array( $this, 'customizeTabs' );
710
8 - private function rowForThread( $t ) {
 11+ if ( ! $this->thread ) {
 12+ $this->showMissingThreadPage();
 13+ return false;
 14+ }
 15+ self::addJSandCSS();
 16+ wfLoadExtensionMessages( 'LiquidThreads' );
 17+
 18+ $this->output->setSubtitle( $this->getSubtitle() . '<br />' . wfMsg( 'lqt_hist_listing_subtitle' ) );
 19+
 20+ $this->showThreadHeading( $this->thread );
 21+
 22+ $pager = new ThreadHistoryPager( $this, $this->thread );
 23+
 24+ $html = $pager->getNavigationBar() .
 25+ $pager->getBody() .
 26+ $pager->getNavigationBar();
 27+
 28+ $this->output->addHTML( $html );
 29+
 30+ $this->showThread( $this->thread );
 31+
 32+ return false;
 33+ }
 34+
 35+ function rowForThread( $t ) {
936 global $wgLang, $wgOut; // TODO global.
1037 wfLoadExtensionMessages( 'LiquidThreads' );
1138 /* TODO: best not to refer to LqtView class directly. */
1239 /* We don't use oldid because that has side-effects. */
13 - $result = array();
14 - $change_names = array( Threads::CHANGE_EDITED_ROOT => wfMsg( 'lqt_hist_comment_edited' ),
15 - Threads::CHANGE_EDITED_SUMMARY => wfMsg( 'lqt_hist_summary_changed' ),
16 - Threads::CHANGE_REPLY_CREATED => wfMsg( 'lqt_hist_reply_created' ),
17 - Threads::CHANGE_NEW_THREAD => wfMsg( 'lqt_hist_thread_created' ),
18 - Threads::CHANGE_DELETED => wfMsg( 'lqt_hist_deleted' ),
19 - Threads::CHANGE_UNDELETED => wfMsg( 'lqt_hist_undeleted' ),
20 - Threads::CHANGE_MOVED_TALKPAGE => wfMsg( 'lqt_hist_moved_talkpage' ) );
21 - $change_label = array_key_exists( $t->changeType(), $change_names ) ? $change_names[$t->changeType()] : "";
 40+
 41+ $sk = $this->user->getSkin();
2242
23 - $url = LqtView::permalinkUrlWithQuery( $this->thread, 'lqt_oldid=' . $t->revisionNumber() );
 43+ $change_names =
 44+ array(
 45+ Threads::CHANGE_EDITED_ROOT => wfMsg( 'lqt_hist_comment_edited' ),
 46+ Threads::CHANGE_EDITED_SUMMARY => wfMsg( 'lqt_hist_summary_changed' ),
 47+ Threads::CHANGE_REPLY_CREATED => wfMsg( 'lqt_hist_reply_created' ),
 48+ Threads::CHANGE_NEW_THREAD => wfMsg( 'lqt_hist_thread_created' ),
 49+ Threads::CHANGE_DELETED => wfMsg( 'lqt_hist_deleted' ),
 50+ Threads::CHANGE_UNDELETED => wfMsg( 'lqt_hist_undeleted' ),
 51+ Threads::CHANGE_MOVED_TALKPAGE => wfMsg( 'lqt_hist_moved_talkpage' ),
 52+ );
 53+
 54+ $change_label = '';
 55+
 56+ if( array_key_exists( $t->changeType(), $change_names ) ) {
 57+ $change_label = $change_names[$t->changeType()];
 58+ }
2459
25 - $user_id = $t->changeUser()->getID(); # ever heard of a User object?
 60+ $user_id = $t->changeUser()->getID();
2661 $user_text = $t->changeUser()->getName();
27 - $sig = $this->user->getSkin()->userLink( $user_id, $user_text ) .
28 - $this->user->getSkin()->userToolLinks( $user_id, $user_text );
 62+ $userLinks = $sk->userLink( $user_id, $user_text ) .
 63+ $sk->userToolLinks( $user_id, $user_text );
2964
3065 $change_comment = $t->changeComment();
31 - if ( !empty( $change_comment ) )
32 - $change_comment = "<em>($change_comment)</em>";
33 -
34 - $result[] = "<tr>";
35 - $result[] = "<td><a href=\"$url\">" . $wgLang->timeanddate( $t->modified() ) . "</a></td>";
36 - $result[] = "<td>" . $sig . "</td>";
37 - $result[] = "<td>$change_label</td>";
38 - $result[] = "<td>$change_comment</td>";
39 - $result[] = "</tr>";
40 - return implode( '', $result );
41 - }
42 -
43 - function showHistoryListing( $t ) {
44 - wfLoadExtensionMessages( 'LiquidThreads' );
45 - $revisions = new ThreadHistoryIterator( $t, $this->perPage, $this->perPage * ( $this->page - 1 ) );
46 -
47 - $this->output->addHTML( '<table>' );
48 - foreach ( $revisions as $ht ) {
49 - $this->output->addHTML( $this->rowForThread( $ht ) );
 66+ if ( $change_comment != '' ) {
 67+ $change_comment = $sk->commentBlock( $change_comment );
5068 }
51 - $this->output->addHTML( '</table>' );
5269
53 - if ( count( $revisions ) == 0 && $this->page == 1 ) {
54 - $this->output->addHTML( '<p>' . wfMsg( 'lqt_hist_no_revisions_error' ) );
55 - }
56 - else if ( count( $revisions ) == 0 ) {
57 - // we could redirect to the previous page... yow.
58 - $this->output->addHTML( '<p>' . wfMsg( 'lqt_hist_past_last_page_error' ) );
59 - }
 70+ $html = '';
 71+
 72+ $linkText = $wgLang->timeanddate( $t->modified(), true );
 73+ $link = $this->permalink( $this->thread, $linkText, null, null, null, array(),
 74+ array( 'lqt_oldid' => $t->revisionNumber() ) );
 75+
 76+ $html .= Xml::tags( 'td', null, $link );
 77+ $html .= Xml::tags( 'td', null, $userLinks );
 78+ $html .= Xml::tags( 'td', null, $change_label );
 79+ $html .= Xml::tags( 'td', null, $change_comment );
6080
61 - if ( $this->page > 1 ) {
62 - $this->output->addHTML( '<a class="lqt_newer_older" href="' . $this->queryReplace( array( 'lqt_hist_page' => $this->page - 1 ) ) . '">' . wfMsg( 'lqt_newer' ) . '</a>' );
63 - } else {
64 - $this->output->addHTML( '<span class="lqt_newer_older_disabled" title="' . wfMsg( 'lqt_hist_tooltip_newer_disabled' ) . '">' . wfMsg( 'lqt_newer' ) . '</span>' );
65 - }
66 -
67 - $is_last_page = false;
68 - foreach ( $revisions as $r )
69 - if ( $r->changeType() == Threads::CHANGE_NEW_THREAD )
70 - $is_last_page = true;
71 - if ( $is_last_page ) {
72 - $this->output->addHTML( '<span class="lqt_newer_older_disabled" title="' . wfMsg( 'lqt_hist_tooltip_older_disabled' ) . '">' . wfMsg( 'lqt_older' ) . '</span>' );
73 - } else {
74 - $this->output->addHTML( '<a class="lqt_newer_older" href="' . $this->queryReplace( array( 'lqt_hist_page' => $this->page + 1 ) ) . '">' . wfMsg( 'lqt_older' ) . '</a>' );
75 - }
 81+ $html = Xml::tags( 'tr', null, $html );
 82+
 83+ return $html;
7684 }
 85+}
7786
78 - function __construct( &$output, &$article, &$title, &$user, &$request ) {
79 - parent::__construct( $output, $article, $title, $user, $request );
80 - $this->loadParametersFromRequest();
 87+class ThreadHistoryPager extends ReverseChronologicalPager {
 88+ function __construct( $view, $thread ) {
 89+ parent::__construct();
 90+
 91+ $this->thread = $thread;
 92+ $this->view = $view;
8193 }
82 -
83 - function loadParametersFromRequest() {
84 - $this->perPage = $this->request->getInt( 'lqt_hist_per_page', 10 );
85 - $this->page = $this->request->getInt( 'lqt_hist_page', 1 );
 94+
 95+ function getQueryInfo() {
 96+ $queryInfo =
 97+ array(
 98+ 'tables' => array( 'historical_thread' ),
 99+ 'fields' => array( 'hthread_contents', 'hthread_revision' ),
 100+ 'conds' => array( 'hthread_id' => $this->thread->id() ),
 101+ 'options' => array( 'order by' => 'hthread_revision desc' ),
 102+ );
 103+
 104+ return $queryInfo;
86105 }
 106+
 107+ function formatRow( $row ) {
 108+ $hthread = HistoricalThread::fromTextRepresentation( $row->hthread_contents );
 109+ return $this->view->rowForThread( $hthread );
 110+ }
 111+
 112+ function getStartBody() {
 113+ $headers = array(
 114+ 'lqt-history-time',
 115+ 'lqt-history-user',
 116+ 'lqt-history-action',
 117+ );
87118
88 - function show() {
89 - global $wgHooks;
90 - $wgHooks['SkinTemplateTabs'][] = array( $this, 'customizeTabs' );
91 -
92 - if ( ! $this->thread ) {
93 - $this->showMissingThreadPage();
94 - return false;
 119+ $html = '';
 120+
 121+ foreach( $headers as $header ) {
 122+ $html .= Xml::tags( 'th', null,
 123+ wfMsgExt( $header, 'parseinline' ) );
95124 }
96 - self::addJSandCSS();
97 - wfLoadExtensionMessages( 'LiquidThreads' );
98 -
99 - $this->output->setSubtitle( $this->getSubtitle() . '<br />' . wfMsg( 'lqt_hist_listing_subtitle' ) );
100 -
101 - $this->showThreadHeading( $this->thread );
102 - $this->showHistoryListing( $this->thread );
103 -
104 - $this->showThread( $this->thread );
105 -
106 - return false;
 125+
 126+ $html = Xml::tags( 'tr', null, $html );
 127+ $html = Xml::tags( 'thead', null, $html );
 128+ $html = "<table>$html<tbody>";
 129+
 130+ return $html;
107131 }
 132+
 133+ function getEndBody() {
 134+ return "</tbody></table>";
 135+ }
 136+
 137+ function getIndexField() {
 138+ return 'hthread_revision';
 139+ }
108140 }
 141+
Index: trunk/extensions/LiquidThreads/pages/SpecialNewMessages.php
@@ -6,7 +6,7 @@
77 private $user, $output, $request, $title;
88
99 function __construct() {
10 - SpecialPage::SpecialPage( 'Newmessages' );
 10+ SpecialPage::SpecialPage( 'NewMessages' );
1111 $this->includable( true );
1212 }
1313
@@ -42,15 +42,21 @@
4343 $wgOut->addWikitext( wfMsg( 'lqt-no-new-messages' ) );
4444 return;
4545 }
46 - $view->showReadAllButton( $both_sets ); // ugly hack.
 46+
 47+ $html = '';
 48+
 49+ $html .= $view->getReadAllButton( $both_sets ); // ugly hack.
4750
4851 $view->setHeaderLevel( 3 );
4952
50 - $this->output->addHTML( '<h2 class="lqt_newmessages_section">' . wfMsg ( 'lqt-messages-sent' ) . '</h2>' );
 53+ $html .= Xml::tags( 'h2', array( 'class' => 'lqt_newmessages_section' ),
 54+ wfMsgExt( 'lqt-messages-sent', 'parseinline' ) );
 55+ $wgOut->addHTML( $html );
5156 $view->setThreads( $first_set );
5257 $view->show();
5358
54 - $this->output->addHTML( '<h2 class="lqt_newmessages_section">' . wfMsg ( 'lqt-other-messages' ) . '</h2>' );
 59+ $wgOut->addHTML( Xml::tags( 'h2', array( 'class' => 'lqt_newmessages_section' ),
 60+ wfMsgExt( 'lqt-other-messages', 'parseinline' ) ) );
5561 $view->setThreads( $second_set );
5662 $view->show();
5763 }
Index: trunk/extensions/LiquidThreads/pages/TalkpageView.php
@@ -35,53 +35,64 @@
3636
3737 function showHeader() {
3838 /* Show the contents of the actual talkpage article if it exists. */
 39+
 40+ global $wgUser;
 41+ $sk = $wgUser->getSkin();
3942
4043 $article = new Article( $this->title );
4144 $revision = Revision::newFromId( $article->getLatest() );
4245 if ( $revision ) $article_text = $revision->getRawText();
4346
4447 $oldid = $this->request->getVal( 'oldid', null );
45 - $editlink = $this->title->getFullURL( 'action=edit' );
4648
4749 wfLoadExtensionMessages( 'LiquidThreads' );
4850 // If $article_text == "", the talkpage was probably just created
4951 // when the first thread was posted to make the links blue.
5052 if ( $article->exists() && $article_text != "" ) {
51 - $historylink = $this->title->getFullURL( 'action=history' );
52 - $this->openDiv( 'lqt_header_content' );
53 - $this->showPostBody( $article, $oldid );
54 - $this->outputList( 'ul', 'lqt_header_commands', null, array(
55 - "[<a href=\"$editlink\">" . wfMsg( 'edit' ) . "&uarr;</a>]",
56 - "[<a href=\"$historylink\">" . wfMsg( 'history_short' ) . "&uarr;</a>]"
57 - ) );
58 - $this->closeDiv();
 53+ $html = '';
 54+
 55+ $html .= $this->showPostBody( $article, $oldid );
 56+
 57+ $actionLinks = array();
 58+ $actionLinks[] = $sk->link( $this->title,
 59+ wfMsgExt( 'edit', 'parseinline' ) . "&uarr;",
 60+ array(), array( 'action' => 'edit' ) );
 61+ $actionLinks[] = $sk->link( $this->title,
 62+ wfMsgExt( 'history_short', 'parseinline' ) . "&uarr;",
 63+ array(), array( 'action' => 'history' ) );
 64+
 65+ $actions = '';
 66+ foreach( $actionLinks as $link ) {
 67+ $actions .= Xml::tags( 'li', null, "[$link]" ) . "\n";
 68+ }
 69+ $actions = Xml::tags( 'ul', array( 'class' => 'lqt_header_commands' ), $actions );
 70+ $html .= $actions;
 71+
 72+ $html = Xml::tags( 'div', array( 'class' => 'lqt_header_content' ), $html );
 73+
 74+ $this->output->addHTML( $html );
5975 } else {
60 - $this->output->addHTML( "<p class=\"lqt_header_notice\">[<a href=\"$editlink\">" . wfMsg( 'lqt_add_header' ) . "</a>]</p>" );
 76+
 77+ $editLink = $sk->link( $this->title, wfMsgExt( 'lqt_add_header', 'parseinline' ),
 78+ array(), array( 'action' => 'edit' ) );
 79+
 80+ $html = Xml::tags( 'p', array( 'class' => 'lqt_header_notice' ), "[$editLink]" );
 81+
 82+ $this->output->addHTML( $html );
6183 }
6284 }
63 -
64 - function outputList( $kind, $class, $id, $contents ) {
65 - $this->output->addHTML( Xml::openElement( $kind, array( 'class' => $class, 'id' => $id ) ) );
66 - foreach ( $contents as $li ) {
67 - $this->output->addHTML( Xml::openElement( 'li' ) );
68 - $this->output->addHTML( $li );
69 - $this->output->addHTML( Xml::closeElement( 'li' ) );
70 - }
71 - $this->output->addHTML( Xml::closeElement( $kind ) );
72 - }
7385
74 - function showTOC( $threads ) {
 86+ function getTOC( $threads ) {
7587 global $wgLang;
7688
7789 wfLoadExtensionMessages( 'LiquidThreads' );
7890
7991 $sk = $this->user->getSkin();
8092
81 - $title = Xml::tags( 'h2', null, wfMsgExt( 'lqt_contents_title', 'parseinline' ) );
82 - $this->output->addHTML( $title );
83 -
8493 $html = '';
8594
 95+ $html .= Xml::tags( 'h2', null, wfMsgExt( 'lqt_contents_title', 'parseinline' ) );
 96+
8697 // Header row
8798 $headerRow = '';
8899 $headers = array( 'lqt_toc_thread_title', 'lqt_toc_thread_author',
@@ -114,33 +125,46 @@
115126 $rows[] = $row;
116127 }
117128
118 - $html = $headerRow . "\n" . Xml::tags( 'tbody', null, implode( "\n", $rows ) );
 129+ $html .= $headerRow . "\n" . Xml::tags( 'tbody', null, implode( "\n", $rows ) );
119130 $html = Xml::tags( 'table', array( 'class' => 'lqt_toc' ), $html );
120131
121 - $this->output->addHTML( $html );
 132+ return $html;
122133 }
 134+
 135+ function getList( $kind, $class, $id, $contents ) {
 136+ $html = '';
 137+ foreach ( $contents as $li ) {
 138+ $html .= Xml::tags( 'li', null, $li );
 139+ }
 140+ $html = Xml::tags( $kind, array( 'class' => $class, 'id' => $id ), $html );
 141+
 142+ return $html;
 143+ }
123144
124 - function showArchiveWidget( $threads ) {
 145+ function getArchiveWidget( $threads ) {
125146 wfLoadExtensionMessages( 'LiquidThreads' );
126 -
 147+
127148 $threadlinks = $this->permalinksForThreads( $threads );
128 - $url = $this->talkpageUrl( $this->title, 'talkpage_archive' );
129149
130150 if ( count( $threadlinks ) > 0 ) {
131 - $this->openDiv( 'lqt_archive_teaser' );
132 - $this->output->addHTML( '<h2 class="lqt_recently_archived">' . wfMsg( 'lqt_recently_archived' ) . '</h2>' );
133 - $this->outputList( 'ul', '', '', $threadlinks );
134 - $this->closeDiv();
135 - } else {
 151+ $url = $this->talkpageUrl( $this->title, 'talkpage_archive' );
 152+
 153+ $html = '';
 154+ $html = Xml::tags( 'h2', array( 'class' => 'lqt_recently_archived' ),
 155+ wfMsgExt( 'lqt_recently_archived', 'parseinline' ) );
 156+ $html .= $this->getList( 'ul', '', '', $threadlinks );
 157+ $html = Xml::tags( 'div', array( 'class' => 'lqt_archive_teaser' ), $html );
 158+ return $html;
136159 }
 160+
 161+ return '';
137162 }
138163
139164 function showTalkpageViewOptions( $article ) {
140165 wfLoadExtensionMessages( 'LiquidThreads' );
141 - // TODO WTF who wrote this?
142166
143167 if ( $this->methodApplies( 'talkpage_sort_order' ) ) {
144 - $remember_sort_checked = $this->request->getBool( 'lqt_remember_sort' ) ? 'checked ' : '';
 168+ $remember_sort_checked = $this->request->getBool( 'lqt_remember_sort' );
145169 $this->user->setOption( 'lqt_sort_order', $this->sort_order );
146170 $this->user->saveSettings();
147171 } else {
@@ -148,56 +172,57 @@
149173 }
150174
151175 if ( $article->exists() ) {
152 - $nc_sort = $this->sort_order == LQT_NEWEST_CHANGES ? ' selected' : '';
153 - $nt_sort = $this->sort_order == LQT_NEWEST_THREADS ? ' selected' : '';
154 - $ot_sort = $this->sort_order == LQT_OLDEST_THREADS ? ' selected' : '';
155176 $newest_changes = wfMsg( 'lqt_sort_newest_changes' );
156177 $newest_threads = wfMsg( 'lqt_sort_newest_threads' );
157178 $oldest_threads = wfMsg( 'lqt_sort_oldest_threads' );
158179 $lqt_remember_sort = wfMsg( 'lqt_remember_sort' ) ;
159180 $form_action_url = $this->talkpageUrl( $this->title, 'talkpage_sort_order' );
160 - $lqt_sorting_order = wfMsg( 'lqt_sorting_order' );
161181 $lqt_sort_newest_changes = wfMsg( 'lqt_sort_newest_changes' );
162182 $lqt_sort_newest_threads = wfMsg( 'lqt_sort_newest_threads' );
163183 $lqt_sort_oldest_threads = wfMsg( 'lqt_sort_oldest_threads' );
164184 $go = wfMsg( 'go' );
 185+
 186+ $html = '';
 187+
 188+ $html .= Xml::label( wfMsg( 'lqt_sorting_order' ), 'lqt_sort_select' ) . ' ';
 189+
 190+ $sortOrderSelect =
 191+ new XmlSelect( 'lqt_order', 'lqt_sort_select', $this->sort_order );
 192+
 193+ $sortOrderSelect->setAttribute( 'class', 'lqt_sort_select' );
 194+ $sortOrderSelect->addOption( wfMsg( 'lqt_sort_newest_changes' ),
 195+ LQT_NEWEST_CHANGES );
 196+ $sortOrderSelect->addOption( wfMsg( 'lqt_sort_newest_changes' ),
 197+ LQT_NEWEST_THREADS );
 198+ $sortOrderSelect->addOption( wfMsg( 'lqt_sort_newest_changes' ),
 199+ LQT_OLDEST_THREADS );
 200+ $html .= $sortOrderSelect->getHTML();
 201+
165202 if ( $this->user->isLoggedIn() ) {
166 - $remember_sort =
167 - <<<HTML
168 -<br />
169 -<label for="lqt_remember_sort_checkbox">
170 -<input id="lqt_remember_sort_checkbox" name="lqt_remember_sort" type="checkbox" value="1" $remember_sort_checked />
171 -$lqt_remember_sort</label>
172 -HTML;
173 - } else {
174 - $remember_sort = '';
 203+ $html .= Xml::element( 'br' ) .
 204+ Xml::checkLabel( $lqt_remember_sort, 'lqt_remember_sort',
 205+ 'lqt_remember_sort', $remember_sort_checked );
175206 }
176 - if ( in_array( 'deletedhistory', $this->user->getRights() ) ) {
177 - $show_deleted_checked = $this->request->getBool( 'lqt_show_deleted_threads' ) ? 'checked ' : '';
178 - $show_deleted = "<br />\n" .
179 - "<label for=\"lqt_show_deleted_threads_checkbox\">\n" .
180 - "<input id=\"lqt_show_deleted_threads_checkbox\" name=\"lqt_show_deleted_threads\" type=\"checkbox\" value=\"1\" $show_deleted_checked />\n" .
181 - wfMsg( 'lqt_delete_show_checkbox' ) . "</label>\n";
182 - } else {
183 - $show_deleted = "";
 207+
 208+ if ( $this->user->isAllowed( 'deletedhistory' ) ) {
 209+ $show_deleted_checked = $this->request->getBool( 'lqt_show_deleted_threads' );
 210+
 211+ $html .= Xml::element( 'br' ) .
 212+ Xml::checkLabel( wfMsg('lqt_delete_show_checkbox' ),
 213+ 'lqt_show_deleted_threads',
 214+ 'lqt_show_deleted_threads',
 215+ $show_deleted_checked );
184216 }
185 - $this->openDiv( 'lqt_view_options' );
186 - $this->output->addHTML(
187 -
188 - <<<HTML
189 -<form name="lqt_sort" action="$form_action_url" method="post">$lqt_sorting_order
190 -<select name="lqt_order" class="lqt_sort_select">
191 -<option value="nc"$nc_sort>$lqt_sort_newest_changes</option>
192 -<option value="nt"$nt_sort>$lqt_sort_newest_threads</option>
193 -<option value="ot"$ot_sort>$lqt_sort_oldest_threads</option>
194 -</select>
195 -$remember_sort
196 -$show_deleted
197 -<input name="submitsort" type="submit" value="$go" class="lqt_go_sort"/>
198 -</form>
199 -HTML
200 - );
201 - $this->closeDiv();
 217+
 218+ $html .= Xml::submitButton( wfMsg( 'go' ), array( 'class' => 'lqt_go_sort',
 219+ 'name' => 'submitsort' ) );
 220+
 221+ $html = Xml::tags( 'form', array( 'action' => $form_action_url,
 222+ 'method' => 'post',
 223+ 'name' => 'lqt_sort' ), $html );
 224+ $html = Xml::tags( 'div', array( 'class' => 'lqt_view_options' ), $html );
 225+
 226+ $this->output->addHTML( $html );
202227 }
203228
204229 }
@@ -205,23 +230,28 @@
206231 function show() {
207232 global $wgHooks;
208233 wfLoadExtensionMessages( 'LiquidThreads' );
209 - // Why is a hook added here?
 234+ // FIXME Why is a hook added here?
210235 $wgHooks['SkinTemplateTabs'][] = array( $this, 'customizeTabs' );
211236
212237 $this->output->setPageTitle( $this->title->getPrefixedText() );
213238 self::addJSandCSS();
214 - $article = new Article( $this->title ); // Added in r29715 sorting. Why?
 239+ $article = new Article( $this->title );
215240
216 - // Removed in r29715 sorting. Again, why?
217241 $this->showHeader();
 242+
 243+ $html = '';
218244
219 - global $wgRequest; // TODO
 245+ global $wgRequest;
220246 if ( $this->methodApplies( 'talkpage_new_thread' ) ) {
221247 $this->showNewThreadForm();
222248 } else {
223249 $this->showTalkpageViewOptions( $article );
224 - $url = $this->talkpageUrl( $this->title, 'talkpage_new_thread' );
225 - $this->output->addHTML( "<strong><a class=\"lqt_start_discussion\" href=\"$url\">" . wfMsg( 'lqt_new_thread' ) . "</a></strong>" );
 250+ $newThreadLink = $this->talkpageLink( $this->title,
 251+ wfMsgExt( 'lqt_new_thread', 'parseinline' ),
 252+ 'talkpage_new_thread', null, true,
 253+ array( 'class' => 'lqt_start_discussion' ) );
 254+
 255+ $this->output->addHTML( Xml::tags( 'strong', null, $newThreadLink ) );
226256 }
227257
228258 $queryType =
@@ -229,24 +259,32 @@
230260 ? 'fresh' : 'fresh-undeleted';
231261 $threads = $this->queries->query( $queryType );
232262
233 - $this->openDiv( 'lqt_toc_archive_wrapper' );
234 -
235 - $this->openDiv( 'lqt_archive_teaser_empty' );
236 - $this->output->addHTML( "<div class=\"lqt_browse_archive\"><a href=\"{$this->talkpageUrl($this->title, 'talkpage_archive')}\">" .
237 - wfMsg( 'lqt_browse_archive_without_recent' ) . "</a></div>" );
238 - $this->closeDiv();
 263+ $archiveBrowseLink = $this->talkpageLink( $this->title,
 264+ wfMsgExt( 'lqt_browse_archive_without_recent', 'parseinline' ),
 265+ 'talkpage_archive' );
 266+ $archiveBrowseLink = Xml::tags( 'div', array( 'class' => 'lqt_browse_archive' ),
 267+ $archiveBrowseLink );
 268+ $archiveBrowseLink = Xml::tags( 'div', array( 'class' => 'lqt_archive_teaser_empty' ),
 269+ $archiveBrowseLink );
 270+
239271 $recently_archived_threads = $this->queries->query( 'recently-archived' );
240272 if ( count( $threads ) > 3 || count( $recently_archived_threads ) > 0 ) {
241 - $this->showTOC( $threads );
 273+ $toc = $this->getTOC( $threads );
242274 }
243 - $this->showArchiveWidget( $recently_archived_threads );
244 - $this->closeDiv();
245 - // Clear any floats
246 - $this->output->addHTML( '<br clear="all" />' );
 275+ $archiveWidget = $this->getArchiveWidget( $recently_archived_threads );
247276
 277+ $html = Xml::tags( 'div', array( 'class' => 'lqt_toc_archive_wrapper' ),
 278+ $archiveBrowseLink . $toc .
 279+ $archiveWidget );
 280+
 281+ $html .= Xml::element( 'br', array( 'style' => 'clear: both;' ) );
 282+
 283+ $this->output->addHTML( $html );
 284+
248285 foreach ( $threads as $t ) {
249286 $this->showThread( $t );
250287 }
 288+
251289 return false;
252290 }
253291 }
Index: trunk/extensions/LiquidThreads/pages/ThreadHistoricalRevisionView.php
@@ -8,33 +8,45 @@
99
1010 function postDivClass( $thread ) {
1111 $is_changed_thread = $thread->changeObject() &&
12 - $thread->changeObject()->id() == $thread->id();
13 - if ( $is_changed_thread )
14 - return 'lqt_post_changed_by_history';
15 - else
16 - return 'lqt_post';
 12+ ( $thread->changeObject()->id() == $thread->id() );
 13+ if ( $is_changed_thread ) {
 14+ return 'lqt_post_changed_by_history';
 15+ } else {
 16+ return 'lqt_post';
 17+ }
1718 }
1819
1920 function showHistoryInfo() {
20 - global $wgLang; // TODO global.
 21+ global $wgLang;
2122 wfLoadExtensionMessages( 'LiquidThreads' );
22 - $this->openDiv( 'lqt_history_info' );
23 - $this->output->addHTML( wfMsg( 'lqt_revision_as_of',
24 - $wgLang->timeanddate( $this->thread->modified() ),
25 - $wgLang->date( $this->thread->modified() ),
26 - $wgLang->time( $this->thread->modified() ) ) . '<br />' );
2723
 24+ $html = '';
 25+ $html .= wfMsgExt( 'lqt_revision_as_of', 'parseinline',
 26+ array(
 27+ $wgLang->timeanddate( $this->thread->modified() ),
 28+ $wgLang->date( $this->thread->modified() ),
 29+ $wgLang->time( $this->thread->modified() )
 30+ )
 31+ );
 32+
 33+ $html .= '<br/>';
 34+
2835 $ct = $this->thread->changeType();
2936 if ( $ct == Threads::CHANGE_NEW_THREAD ) {
30 - $msg = wfMsg( 'lqt_change_new_thread' );
 37+ $msg = wfMsgExt( 'lqt_change_new_thread', 'parseinline' );
3138 } else if ( $ct == Threads::CHANGE_REPLY_CREATED ) {
32 - $msg = wfMsg( 'lqt_change_reply_created' );
 39+ $msg = wfMsgExt( 'lqt_change_reply_created', 'parseinline' );
3340 } else if ( $ct == Threads::CHANGE_EDITED_ROOT ) {
34 - $diff_url = $this->permalinkUrlWithDiff( $this->thread );
35 - $msg = wfMsg( 'lqt_change_edited_root' ) . " [<a href=\"$diff_url\">" . wfMsg( 'diff' ) . '</a>]';
 41+ $diff_link = $this->diffPermalink( $this->thread,
 42+ wfMsgExt( 'diff', 'parseinline' ) );
 43+ $msg = wfMsgExt( 'lqt_change_edited_root', 'parseinline' ) .
 44+ " [$diff_link]";
3645 }
37 - $this->output->addHTML( $msg );
38 - $this->closeDiv();
 46+ $html .= $msg;
 47+
 48+ $html = Xml::tags( 'div', array( 'class' => 'lqt_history_info' ), $html );
 49+
 50+ $this->output->addHTML( $html );
3951 }
4052
4153 function show() {
Index: trunk/extensions/LiquidThreads/pages/SummaryPageView.php
@@ -7,11 +7,13 @@
88 wfLoadExtensionMessages( 'LiquidThreads' );
99 $thread = Threads::withSummary( $this->article );
1010 if ( $thread ) {
11 - $url = $thread->root()->getTitle()->getFullURL();
12 - $name = $thread->root()->getTitle()->getPrefixedText();
 11+ global $wgUser;
 12+
 13+ $t = $thread->root()->getTitle();
 14+ $link = $wgUser->getSkin->link( $t );
1315 $this->output->setSubtitle(
14 - wfMsg( 'lqt_summary_subtitle',
15 - '<a href="' . $url . '">' . $name . '</a>' ) );
 16+ wfMsgExt( 'lqt_summary_subtitle', array( 'parseinline', 'replaceafter' ),
 17+ $link ) );
1618 }
1719 return true;
1820 }
Index: trunk/extensions/LiquidThreads/pages/TalkpageArchiveView.php
@@ -5,225 +5,130 @@
66 class TalkpageArchiveView extends TalkpageView {
77 function __construct( &$output, &$article, &$title, &$user, &$request ) {
88 parent::__construct( $output, $article, $title, $user, $request );
9 - $this->loadQueryFromRequest();
109 }
1110
12 - function showThread( $t ) {
13 - $this->output->addHTML( <<<HTML
14 -<tr>
15 -<td><a href="{$this->permalinkUrl($t)}">{$t->subjectWithoutIncrement()}</a></td>
16 -<td>
17 -HTML
18 - ); if ( $t->hasSummary() ) {
19 - $this->showPostBody( $t->summary() );
20 - } else if ( $t->type() == Threads::TYPE_MOVED ) {
21 - $rthread = $t->redirectThread();
22 - if ( $rthread && $rthread->summary() ) {
23 - $this->showPostBody( $rthread->summary() );
24 - }
25 - }
26 - $this->output->addHTML( <<<HTML
27 -</td>
28 -</tr>
29 -HTML
30 - );
31 - }
 11+ function show() {
 12+ global $wgHooks, $wgOut;
 13+ $wgHooks['SkinTemplateTabs'][] = array( $this, 'customizeTabs' );
3214
33 - function loadQueryFromRequest() {
3415 wfLoadExtensionMessages( 'LiquidThreads' );
35 - // Begin with with the requirements for being *in* the archive.
36 - global $wgLqtThreadArchiveStartDays;
37 - $startdate = Date::now()->nDaysAgo( $wgLqtThreadArchiveStartDays )->midnight();
38 - $where = array( Threads::articleClause( $this->article ),
39 - 'thread.thread_parent is null',
40 - '(thread.thread_summary_page is not null' .
41 - ' OR thread.thread_type = ' . Threads::TYPE_MOVED . ')',
42 - 'thread.thread_modified < ' . $startdate->text() );
43 - $options = array( 'ORDER BY thread.thread_modified DESC' );
 16+ $this->output->setPageTitle( $this->title->getPrefixedText() );
 17+ $this->output->setSubtitle( wfMsg( 'lqt-archive-subtitle' ) );
 18+ self::addJSandCSS();
 19+
 20+ $this->output->addWikiMsg( 'lqt-archive-intro',
 21+ $this->article->getTitle()->getPrefixedText() );
4422
45 - $annotations = array( wfMsg ( 'lqt-searching' ) );
 23+ $pager = new TalkpageArchivePager( $this->article, $this );
 24+
 25+ $html = $pager->getNavigationBar() .
 26+ $pager->getBody() .
 27+ $pager->getNavigationBar();
 28+
 29+ $wgOut->addHTML( $html );
4630
47 - $r = $this->request;
48 -
49 - /* START AND END DATES */
50 - // $this->start and $this->end are clipped into the range of available
51 - // months, for use in the actual query and the selects. $this->raw* are
52 - // as actually provided, for use by the 'older' and 'newer' buttons.
53 - $ignore_dates = ! $r->getVal( 'lqt_archive_filter_by_date', true );
54 - if ( !$ignore_dates ) {
55 - $months = Threads::monthsWhereArticleHasThreads( $this->article );
56 - }
57 - $s = $r->getVal( 'lqt_archive_start' );
58 - if ( $s && ctype_digit( $s ) && strlen( $s ) == 6 && !$ignore_dates ) {
59 - $this->selstart = new Date( "{$s}01000000" );
60 - $this->starti = array_search( $s, $months );
61 - $where[] = 'thread.thread_modified >= ' . $this->selstart->text();
62 - }
63 - $e = $r->getVal( 'lqt_archive_end' );
64 - if ( $e && ctype_digit( $e ) && strlen( $e ) == 6 && !$ignore_dates ) {
65 - $this->selend = new Date( "{$e}01000000" );
66 - $this->endi = array_search( $e, $months );
67 - $where[] = 'thread.thread_modified < ' . $this->selend->nextMonth()->text();
68 - }
69 - if ( isset( $this->selstart ) && isset( $this->selend ) ) {
70 -
71 - $this->datespan = $this->starti - $this->endi;
72 -
73 - $formattedFrom = $this->formattedMonth( $this->selstart->text() );
74 - $formattedTo = $this->formattedMonth( $this->selend->text() );
75 -
76 - if ( $this->datespan == 0 ) {
77 - $annotations[] = wfMsg( 'lqt_archive_month_annotation', $formattedFrom );
78 - } else {
79 - $annotations[] = wfMsg( 'lqt_archive_month_range_annotation', $formattedFrom, $formattedTo );
80 - }
81 - } else if ( isset( $this->selstart ) ) {
82 - $annotations[] = "after {$this->selstart->text()}";
83 - } else if ( isset( $this->selend ) ) {
84 - $annotations[] = "before {$this->selend->text()}";
85 - }
86 -
87 - $this->where = $where;
88 - $this->options = $options;
89 - $this->annotations = implode( "<br />\n", $annotations );
 31+ return false;
9032 }
 33+}
9134
92 - function threads() {
93 - return Threads::where( $this->where, $this->options );
 35+class TalkpageArchivePager extends TablePager {
 36+
 37+ public function __construct( $article, $view ) {
 38+ parent::__construct();
 39+ $this->article = $article;
 40+ $this->view = $view;
9441 }
9542
96 - function formattedMonth( $yyyymm ) {
97 - global $wgLang; // TODO global.
98 - return $wgLang->getMonthName( substr( $yyyymm, 4, 2 ) ) . ' ' . substr( $yyyymm, 0, 4 );
 43+ public function isFieldSortable( $field ) {
 44+ $sortable = array( 'page_title', 'thread_created', 'thread_modified' );
 45+
 46+ return in_array( $field, $sortable );
9947 }
100 -
101 - function monthSelect( $months, $name ) {
102 - $selection = $this->request->getVal( $name );
103 -
104 - // Silently adjust to stay in range.
105 - $selection = max( min( $selection, $months[0] ), $months[count( $months ) - 1] );
106 -
107 - $options = array();
108 - foreach ( $months as $m ) {
109 - $options[$this->formattedMonth( $m )] = $m;
 48+
 49+ public function formatValue( $field, $value ) {
 50+ global $wgUser, $wgLang;
 51+
 52+ $sk = $wgUser->getSkin();
 53+
 54+ switch( $field ) {
 55+ case 'page_title':
 56+ $title = Title::makeTitle( NS_LQT_THREAD, $value );
 57+ $split = Thread::splitIncrementFromSubject( $title->getText() );
 58+ return $sk->link( $title, $split[1] );
 59+ case 'thread_summary_page':
 60+ $summary = '';
 61+
 62+ if ($value) {
 63+ $page = Article::newFromId( $value );
 64+ $summary = $this->view->showPostBody( $page );
 65+ } elseif ($this->mCurrentRow->thread_type == Threads::TYPE_MOVED) {
 66+ $thread = new Thread( $this->mCurrentRow, array() );
 67+
 68+ $rt = $thread->redirectThread();
 69+ if ( $rt->hasSummary() ) {
 70+ $summaryPage = $rt->summary();
 71+ $summary = $this->view->showPostBody( $summaryPage );
 72+ }
 73+ }
 74+ return $summary;
 75+ case 'thread_created':
 76+ case 'thread_modified':
 77+ return $wgLang->timeanddate( $value, true );
 78+ case 'rev_user_text':
 79+ $uid = $this->mCurrentRow->rev_user;
 80+ return $sk->userLink( $uid, $value ) . $sk->userToolLinks( $uid, $value );
 81+ default:
 82+ return $value;
11083 }
111 - $result = "<select name=\"$name\" id=\"$name\">";
112 - foreach ( $options as $label => $value ) {
113 - $selected = $selection == $value ? 'selected="true"' : '';
114 - $result .= "<option value=\"$value\" $selected>$label";
115 - }
116 - $result .= "</select>";
117 - return $result;
11884 }
119 -
120 - function clip( $vals, $min, $max ) {
121 - $res = array();
122 - foreach ( $vals as $val ) $res[] = max( min( $val, $max ), $min );
123 - return $res;
 85+
 86+ public function getDefaultSort() {
 87+ return 'thread_created';
12488 }
125 -
126 - /* @return True if there are no threads to show, false otherwise.
127 - TODO is is somewhat bizarre. */
128 - function showSearchForm() {
129 - $months = Threads::monthsWhereArticleHasThreads( $this->article );
130 - if ( count( $months ) == 0 ) {
131 - return true;
132 - }
133 - wfLoadExtensionMessages( 'LiquidThreads' );
134 -
135 - $use_dates = $this->request->getVal( 'lqt_archive_filter_by_date', null );
136 - if ( $use_dates === null ) {
137 - $use_dates = $this->request->getBool( 'lqt_archive_start', false ) ||
138 - $this->request->getBool( 'lqt_archive_end', false );
139 - }
140 - $any_date_check = !$use_dates ? 'checked="1"' : '';
141 - $these_dates_check = $use_dates ? 'checked="1"' : '';
142 - $any_date = wfMsg ( 'lqt-any-date' );
143 - $only_date = wfMsg ( 'lqt-only-date' );
144 - $date_from = wfMsg ( 'lqt-date-from' );
145 - $date_to = wfMsg ( 'lqt-date-to' );
146 - $date_info = wfMsg ( 'lqt-date-info' );
147 - if ( isset( $this->datespan ) ) {
148 - $oatte = $this->starti + 1;
149 - $oatts = $this->starti + 1 + $this->datespan;
150 -
151 - $natts = $this->endi - 1;
152 - $natte = $this->endi - 1 - $this->datespan;
153 -
154 - list( $oe, $os, $ns, $ne ) =
155 - $this->clip( array( $oatte, $oatts, $natts, $natte ),
156 - 0, count( $months ) - 1 );
157 -
158 - $older = '<a class="lqt_newer_older" href="' . $this->queryReplace( array(
159 - 'lqt_archive_filter_by_date' => '1',
160 - 'lqt_archive_start' => $months[$os],
161 - 'lqt_archive_end' => $months[$oe] ) )
162 - . '">«' . wfMsg ( 'lqt-older' ) . '</a>';
163 - $newer = '<a class="lqt_newer_older" href="' . $this->queryReplace( array(
164 - 'lqt_archive_filter_by_date' => '1',
165 - 'lqt_archive_start' => $months[$ns],
166 - 'lqt_archive_end' => $months[$ne] ) )
167 - . '">' . wfMsg ( 'lqt-newer' ) . '»</a>';
168 - }
169 - else {
170 - $older = '<span class="lqt_newer_older_disabled" title="' . wfMsg ( 'lqt-date-info' ) . '">«' . wfMsg ( 'lqt-older' ) . '</span>';
171 - $newer = '<span class="lqt_newer_older_disabled" title="' . wfMsg ( 'lqt-date-info' ) . '">' . wfMsg ( 'lqt-newer' ) . '»</span>';
172 - }
173 -
174 - $this->output->addHTML( <<<HTML
175 -<form id="lqt_archive_search_form" action="{$this->title->getLocalURL()}">
176 -<input type="hidden" name="lqt_method" value="talkpage_archive">
177 -<input type="hidden" name="title" value="{$this->title->getPrefixedURL()}"
178 -
179 -<input type="radio" id="lqt_archive_filter_by_date_no"
180 -name="lqt_archive_filter_by_date" value="0" {$any_date_check}>
181 -<label for="lqt_archive_filter_by_date_no">{$any_date}</label> <br />
182 -<input type="radio" id="lqt_archive_filter_by_date_yes"
183 -name="lqt_archive_filter_by_date" value="1" {$these_dates_check}>
184 -<label for="lqt_archive_filter_by_date_yes">{$only_date}</label> <br />
185 -
186 -<table>
187 -<tr><td><label for="lqt_archive_start">{$date_from}</label>
188 -<td>{$this->monthSelect($months, 'lqt_archive_start')} <br />
189 -<tr><td><label for="lqt_archive_end">{$date_to}</label>
190 -<td>{$this->monthSelect($months, 'lqt_archive_end')}
191 -</table>
192 -<input type="submit">
193 -$older $newer
194 -</form>
195 -HTML
196 - );
197 - return false;
 89+
 90+ public function getFieldNames() {
 91+ $fieldToMsg =
 92+ array(
 93+ 'thread_created' => 'lqt-thread-created',
 94+ 'page_title' => 'lqt-title',
 95+ 'thread_modified' => 'lqt_toc_thread_modified',
 96+ 'rev_user_text' => 'lqt_toc_thread_author',
 97+ 'thread_summary_page' => 'lqt-summary',
 98+ );
 99+
 100+ return array_map( 'wfMsg', $fieldToMsg );
198101 }
 102+
 103+ public function getQueryInfo() {
 104+ $dbr = wfGetDB( DB_SLAVE );
 105+
 106+ $hasSummaryClauses = array( 'thread_summary_page is not null',
 107+ 'thread_type' => Threads::TYPE_MOVED );
 108+ $hasSummaryClause = $dbr->makeList( $hasSummaryClauses, LIST_OR );
 109+
 110+ $startDays = Threads::getArticleArchiveStartDays( $this->article );
 111+ $startdate = Date::now()->nDaysAgo( $startDays )->midnight();
 112+
 113+ $queryInfo =
 114+ array(
 115+ 'tables' => array( 'thread', 'page', 'revision' ),
 116+ 'fields' => '*',
 117+ 'conds' =>
 118+ array(
 119+ Threads::articleClause( $this->article ),
 120+ Threads::topLevelClause(),
 121+ $hasSummaryClause,
 122+ 'thread_modified < '. $dbr->addQuotes( $startdate->text() ),
 123+ 'rev_parent_id' => 0,
 124+ ),
 125+ 'join_conds' =>
 126+ array(
 127+ 'page' => array( 'left join', 'thread_root=page_id' ),
 128+ 'revision' => array( 'left join', 'rev_page=page_id' ),
 129+ ),
 130+ );
 131+
199132
200 - function show() {
201 - global $wgHooks;
202 - $wgHooks['SkinTemplateTabs'][] = array( $this, 'customizeTabs' );
203 -
204 - $this->output->setPageTitle( $this->title->getPrefixedText() );
205 - self::addJSandCSS();
206 - wfLoadExtensionMessages( 'LiquidThreads' );
207 -
208 - $empty = $this->showSearchForm();
209 - if ( $empty ) {
210 - $this->output->addHTML( '<p><br /><b>' . wfMsg( 'lqt-nothread' ) . '</b></p>' );
211 - return false;
212 - }
213 - $lqt_title = wfMsg ( 'lqt-title' );
214 - $lqt_summary = wfMsg ( 'lqt-summary' );
215 - $this->output->addHTML( <<<HTML
216 -<p class="lqt_search_annotations">{$this->annotations}</p>
217 -<table class="lqt_archive_listing">
218 -<col class="lqt_titles" />
219 -<col class="lqt_summaries" />
220 -<tr><th>{$lqt_title}<th>{$lqt_summary}</tr>
221 -HTML
222 - );
223 - foreach ( $this->threads() as $t ) {
224 - $this->showThread( $t );
225 - }
226 - $this->output->addHTML( '</table>' );
227 -
228 - return false;
 133+ return $queryInfo;
229134 }
230135 }
Index: trunk/extensions/LiquidThreads/pages/ThreadPermalinkView.php
@@ -17,25 +17,33 @@
1818 return true;
1919 }
2020
21 - efInsertIntoAssoc( 'article', array(
22 - 'text' => wfMsg( $article_t->getNamespaceKey() ),
23 - 'href' => $article_t->getFullURL(),
24 - 'class' => $article_t->exists() ? '' : 'new' ),
25 - 'nstab-thread', $content_actions );
26 - efInsertIntoAssoc( 'not_talk', array(
27 - // talkpage certainly exists since this thread is from it.
28 - 'text' => wfMsg( 'talk' ),
29 - 'href' => $talk_t->getFullURL() ),
30 - 'nstab-thread', $content_actions );
 21+ $articleTab =
 22+ array(
 23+ 'text' => wfMsg( $article_t->getNamespaceKey() ),
 24+ 'href' => $article_t->getFullURL(),
 25+ 'class' => $article_t->exists() ? '' : 'new'
 26+ );
 27+ efInsertIntoAssoc( 'article', $articleTab, 'nstab-thread', $content_actions );
 28+
 29+ $talkTab =
 30+ array(
 31+ // talkpage certainly exists since this thread is from it.
 32+ 'text' => wfMsg( 'talk' ),
 33+ 'href' => $talk_t->getFullURL()
 34+ );
 35+
 36+ efInsertIntoAssoc( 'not_talk', $talkTab, 'nstab-thread', $content_actions );
3137
3238 unset( $content_actions['edit'] );
3339 unset( $content_actions['viewsource'] );
3440 unset( $content_actions['talk'] );
 41+
3542 if ( array_key_exists( 'move', $content_actions ) && $this->thread ) {
3643 $content_actions['move']['href'] =
3744 SpecialPage::getTitleFor( 'MoveThread' )->getFullURL() . '/' .
3845 $this->thread->title()->getPrefixedURL();
3946 }
 47+
4048 if ( array_key_exists( 'delete', $content_actions ) && $this->thread ) {
4149 $content_actions['delete']['href'] =
4250 SpecialPage::getTitleFor( 'DeleteThread' )->getFullURL() . '/' .
@@ -68,20 +76,27 @@
6977 function showMissingThreadPage() {
7078 wfLoadExtensionMessages( 'LiquidThreads' );
7179 $this->output->setPageTitle( wfMsg( 'lqt_nosuchthread_title' ) );
72 - $this->output->addHTML( wfMsg( 'lqt_nosuchthread' ) );
 80+ $this->output->addWikiMsg( 'lqt_nosuchthread' );
7381 }
7482
7583 function getSubtitle() {
7684 wfLoadExtensionMessages( 'LiquidThreads' );
77 - // TODO the archive month part is obsolete.
78 - if ( Date::now()->nDaysAgo( 30 )->midnight()->isBefore( new Date( $this->thread->modified() ) ) )
79 - $query = '';
80 - else
81 - $query = 'lqt_archive_month=' . substr( $this->thread->modified(), 0, 6 );
 85+
 86+ if ( $this->thread->isHistorical() ) {
 87+ // TODO: Point to the relevant part of the archive.
 88+ $query = '';
 89+ } else {
 90+ $query = '';
 91+ }
 92+
8293 $talkpage = $this->thread->article()->getTitle();
83 - $talkpage_link = $this->user->getSkin()->makeKnownLinkObj( $talkpage, '', $query );
 94+ $talkpage_link = $this->user->getSkin()->link( $talkpage );
 95+
8496 if ( $this->thread->hasSuperthread() ) {
85 - return wfMsg( 'lqt_fragment', "<a href=\"{$this->permalinkUrl($this->thread->topmostThread())}\">" . wfMsg( 'lqt_discussion_link' ) . "</a>", $talkpage_link );
 97+ $permalink = $this->permalink( $this->thread->topmostThread(),
 98+ wfMsg( 'lqt_discussion_link' ) );
 99+
 100+ return wfMsg( 'lqt_fragment', $permalink, $talkpage_link );
86101 } else {
87102 return wfMsg( 'lqt_from_talk', $talkpage_link );
88103 }
@@ -92,11 +107,17 @@
93108 parent::__construct( $output, $article, $title, $user, $request );
94109
95110 $t = Threads::withRoot( $this->article );
96 - $r = $this->request->getVal( 'lqt_oldid', null ); if ( $r ) {
97 - $t = $t->atRevision( $r );
98 - if ( !$t ) { $this->noSuchRevision(); return; }
99 -
 111+ $oldid = $this->request->getVal( 'lqt_oldid', null );
 112+
 113+ if ( $oldid ) {
 114+ $t = $t->atRevision( $oldid );
 115+
 116+ if ( !$t ) {
 117+ $this->noSuchRevision();
 118+ return;
 119+ }
100120 }
 121+
101122 $this->thread = $t;
102123 if ( !$t ) {
103124 return; // error reporting is handled in show(). this kinda sucks.

Follow-up revisions

RevisionCommit summaryAuthorDate
r52308Follow-up to r52307, fix mix of static and instance methodswerdna13:54, 23 June 2009
r52326Core changes for r52307werdna21:52, 23 June 2009
r84360Fix undefined variable from r52307reedy00:00, 20 March 2011

Status & tagging log