Index: branches/lqt-updates/extensions/LiquidThreads/LiquidThreads.php |
— | — | @@ -246,6 +246,9 @@ |
247 | 247 | $wgAutoloadClasses['ApiLqtForm'] = $dir.'/api/ApiLqtForm.php'; |
248 | 248 | $wgAPIModules['lqtform'] = 'ApiLqtForm'; |
249 | 249 | |
| 250 | +$wgAutoloadClasses['ApiLqtFormatter'] = $dir.'/api/ApiLqtFormatter.php'; |
| 251 | +$wgAPIModules['lqtformat'] = 'ApiLqtFormatter'; |
| 252 | + |
250 | 253 | // // Path to the LQT directory |
251 | 254 | // $wgLiquidThreadsExtensionPath = "{$wgScriptPath}/extensions/LiquidThreads"; |
252 | 255 | |
Index: branches/lqt-updates/extensions/LiquidThreads/classes/model/Object.php |
— | — | @@ -58,4 +58,23 @@ |
59 | 59 | * @return String |
60 | 60 | */ |
61 | 61 | abstract public function getUniqueIdentifier(); |
| 62 | + |
| 63 | + /** |
| 64 | + * Retrieves an object by unique identifier |
| 65 | + * @param $id String: Unique identifier returned by getUniqueIdentifier() |
| 66 | + * @return LiquidThreadsObject |
| 67 | + */ |
| 68 | + public static function retrieve($id) { |
| 69 | + static $classes = array( |
| 70 | + 'lqt-post' => 'LiquidThreadsPost', |
| 71 | + 'lqt-topic' => 'LiquidThreadsTopic', |
| 72 | + 'lqt-channel' => 'LiquidThreadsChannel', |
| 73 | + ); |
| 74 | + |
| 75 | + list($type, $id) = explode('_', $id); |
| 76 | + |
| 77 | + $class = $classes[$type]; |
| 78 | + |
| 79 | + return $class::newFromID( $id ); |
| 80 | + } |
62 | 81 | } |
Index: branches/lqt-updates/extensions/LiquidThreads/classes/view/ReplyForm.php |
— | — | @@ -0,0 +1,61 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * Form for posting a new reply to a topic |
| 6 | + */ |
| 7 | +class LiquidThreadsReplyForm extends LiquidThreadsEditForm { |
| 8 | + |
| 9 | + protected $topic; |
| 10 | + |
| 11 | + /** |
| 12 | + * Initialises a LiquidThreadsNewTopicForm. |
| 13 | + * @param $user The user viewing this form. |
| 14 | + * @param $topic The topic to reply to |
| 15 | + * @param $replyPost The post to reply to, or NULL |
| 16 | + */ |
| 17 | + public function __construct( $user, $topic, $replyPost = null ) { |
| 18 | + parent::__construct( $user ); |
| 19 | + |
| 20 | + if ( ! $topic instanceof LiquidThreadsTopic ) { |
| 21 | + throw new MWException( "Invalid argument to ".__METHOD__ ); |
| 22 | + } |
| 23 | + $this->topic = $topic; |
| 24 | + $this->replyPost = $replyPost; |
| 25 | + } |
| 26 | + |
| 27 | + /** |
| 28 | + * Gets the HTML for the form fields, excluding buttons. |
| 29 | + */ |
| 30 | + protected function getFormFieldsHTML() { |
| 31 | + $html = ''; |
| 32 | + |
| 33 | + $html .= $this->getTextbox('lqt-edit-content'); |
| 34 | + $html .= $this->getSignatureEditor( LqtView::getUserSignature($this->user) ); |
| 35 | + |
| 36 | + return $html; |
| 37 | + } |
| 38 | + |
| 39 | + public function submit( $request = null ) { |
| 40 | + $text = $request->getVal('lqt-edit-content'); |
| 41 | + $sig = $request->getVal('lqt-signature'); |
| 42 | + |
| 43 | + // Now add the first post |
| 44 | + $post = LiquidThreadsPost::create( $this->topic, $this->replyPost ); |
| 45 | + $post->getPendingVersion()->setEditor( $this->user ); |
| 46 | + $post->getPendingVersion()->setPoster( $this->user ); |
| 47 | + $post->setText( $text ); |
| 48 | + $post->setSignature( $sig ); |
| 49 | + |
| 50 | + $post->save(); |
| 51 | + |
| 52 | + return true; |
| 53 | + } |
| 54 | + |
| 55 | + public function validate( $request = null ) { |
| 56 | + if ( ! $request->getVal( 'lqt-edit-content' ) ) { |
| 57 | + return false; |
| 58 | + } |
| 59 | + |
| 60 | + return true; |
| 61 | + } |
| 62 | +} |
Index: branches/lqt-updates/extensions/LiquidThreads/classes/view/ChannelView.php |
— | — | @@ -0,0 +1,385 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +class LiquidThreadsChannelView extends LQTView { |
| 5 | + protected $channel; |
| 6 | + protected $mShowItems = array( 'options', 'toc' ); |
| 7 | + protected $baseUrl; |
| 8 | + |
| 9 | + /** |
| 10 | + * Default constructor |
| 11 | + * @param $channel The LiquidThreadsChannel to show. |
| 12 | + */ |
| 13 | + function __construct( $channel ) { |
| 14 | + $this->channel = $channel; |
| 15 | + |
| 16 | + // Assume we want to go to the current title. |
| 17 | + global $wgTitle, $wgRequest; |
| 18 | + $query = array(); |
| 19 | + $copyQueryElements = array( |
| 20 | + 'limit', |
| 21 | + 'offset', |
| 22 | + 'dir', |
| 23 | + 'order', |
| 24 | + 'sort', |
| 25 | + 'asc', |
| 26 | + 'desc' |
| 27 | + ); |
| 28 | + |
| 29 | + foreach( $copyQueryElements as $name ) { |
| 30 | + if ( $wgRequest->getCheck('name') ) { |
| 31 | + $query[$name] = $wgRequest->getVal($name); |
| 32 | + } |
| 33 | + } |
| 34 | + |
| 35 | + $this->baseUrl = $wgTitle->getFullURL( $query ); |
| 36 | + } |
| 37 | + |
| 38 | + /** |
| 39 | + * Shows this view. |
| 40 | + * @param $action Any action to take |
| 41 | + */ |
| 42 | + function show( $action = null ) { |
| 43 | + // Temporarily disabled |
| 44 | +// // Expose feed links. |
| 45 | +// global $wgFeedClasses; |
| 46 | +// $apiParams = array( 'action' => 'feedthreads', 'type' => 'replies|newthreads', |
| 47 | +// 'talkpage' => $this->title->getPrefixedText() ); |
| 48 | +// $urlPrefix = wfScript( 'api' ) . '?'; |
| 49 | +// foreach ( $wgFeedClasses as $format => $class ) { |
| 50 | +// $theseParams = $apiParams + array( 'feedformat' => $format ); |
| 51 | +// $url = $urlPrefix . wfArrayToCGI( $theseParams ); |
| 52 | +// $this->output->addFeedLink( $format, $url ); |
| 53 | +// } |
| 54 | + |
| 55 | + $html = ''; |
| 56 | + |
| 57 | + // Set up a per-page header for new threads, search box, and sorting stuff. |
| 58 | + |
| 59 | + $talkpageHeader = ''; |
| 60 | + |
| 61 | + if ( /*Thread::canUserPost( $this->user, $this->article )*/ true ) { |
| 62 | + $newThreadText = wfMsgExt( 'lqt_new_thread', 'parseinline' ); |
| 63 | + $query = array( |
| 64 | + 'lqt_action' => 'new-topic', |
| 65 | + 'lqt_target' => $this->channel->getUniqueIdentifier() |
| 66 | + ); |
| 67 | + |
| 68 | + $newThreadUrl = wfAppendQuery( $this->getBaseUrl(), $query ); |
| 69 | + |
| 70 | + $newThreadLink = Html::rawElement( |
| 71 | + 'a', |
| 72 | + array( |
| 73 | + 'lqt_channel' => $this->channel->getID(), |
| 74 | + 'href' => $newThreadUrl, |
| 75 | + ), |
| 76 | + $newThreadText |
| 77 | + ); |
| 78 | + |
| 79 | + $newThreadLink = Xml::tags( |
| 80 | + 'strong', |
| 81 | + array( 'class' => 'lqt_start_discussion' ), |
| 82 | + $newThreadLink |
| 83 | + ); |
| 84 | + |
| 85 | + $talkpageHeader .= $newThreadLink; |
| 86 | + } |
| 87 | + |
| 88 | + $talkpageHeader = Xml::tags( |
| 89 | + 'div', |
| 90 | + array( 'class' => 'lqt-talkpage-header' ), |
| 91 | + $talkpageHeader |
| 92 | + ); |
| 93 | + |
| 94 | + if ( $this->shouldShow('options') ) { |
| 95 | + $html .= $talkpageHeader; |
| 96 | + } elseif ( $this->shouldShow('simplenew') ) { |
| 97 | + $html .= $newThreadLink; |
| 98 | + } |
| 99 | + |
| 100 | + if ( count($action) > 0 && $action[0] == 'new-topic' ) { |
| 101 | + global $wgUser, $wgRequest; |
| 102 | + $form = new LiquidThreadsNewTopicForm( $wgUser, $this->channel ); |
| 103 | + $formOutput = $form->show( $wgRequest ); |
| 104 | + |
| 105 | + if ( $formOutput !== true ) { |
| 106 | + $html .= $formOutput; |
| 107 | + } else { |
| 108 | + $this->refresh(); |
| 109 | + return false; |
| 110 | + } |
| 111 | + } |
| 112 | + |
| 113 | + $pager = $this->getPager(); |
| 114 | + |
| 115 | + $topics = array(); |
| 116 | + foreach( $pager->getRows() as $row ) { |
| 117 | + $topics[$row->lqt_id] = LiquidThreadsTopic::newFromRow( $row ); |
| 118 | + } |
| 119 | + |
| 120 | + if ( count( $topics ) > 0 && $this->shouldShow('toc') ) { |
| 121 | + $html .= $this->getTOC( $topics ); |
| 122 | + } elseif ( count($topics) == 0 ) { |
| 123 | + $html .= Xml::tags( 'div', array( 'class' => 'lqt-no-threads' ), |
| 124 | + wfMsgExt( 'lqt-no-threads', 'parseinline' ) ); |
| 125 | + } |
| 126 | + |
| 127 | + $html .= $pager->getNavigationBar(); |
| 128 | + $html .= Xml::openElement( 'div', |
| 129 | + array( |
| 130 | + 'class' => 'lqt-threads lqt-talkpage-threads', |
| 131 | + 'id' => $this->channel->getUniqueIdentifier(), |
| 132 | + ) ); |
| 133 | + |
| 134 | + $formatter = LiquidThreadsTopicFormatter::singleton(); |
| 135 | + $context = new LiquidThreadsTopicFormatterContext; |
| 136 | + $doneReply = false; |
| 137 | + $replyPost = -1; |
| 138 | + $context->set( 'action', $action ); |
| 139 | + $context->set( 'base-url', $this->getBaseUrl() ); |
| 140 | + |
| 141 | + foreach ( $topics as $topic ) { |
| 142 | + $html .= $formatter->getHTML( $topic, $context ); |
| 143 | + } |
| 144 | + |
| 145 | + $html .= Xml::closeElement( 'div' ) . $pager->getNavigationBar(); |
| 146 | + |
| 147 | + global $wgOut; |
| 148 | + $wgOut->addModules( 'ext.liquidThreads' ); |
| 149 | + $wgOut->addHTML( $html ); |
| 150 | + |
| 151 | + return false; |
| 152 | + } |
| 153 | + |
| 154 | + function refresh() { |
| 155 | + global $wgOut; |
| 156 | + $wgOut->redirect( $this->channel->getTitle()->getFullURL() ); |
| 157 | + } |
| 158 | + |
| 159 | + function getTOC( $topics ) { |
| 160 | + global $wgLang; |
| 161 | + |
| 162 | + $html = ''; |
| 163 | + |
| 164 | + $h2_header = Xml::tags( 'h2', null, wfMsgExt( 'lqt_contents_title', 'parseinline' ) ); |
| 165 | + |
| 166 | + // Header row |
| 167 | + $headerRow = ''; |
| 168 | + $headers = array( 'lqt_toc_thread_title', |
| 169 | + 'lqt_toc_thread_replycount', 'lqt_toc_thread_modified' ); |
| 170 | + foreach ( $headers as $msg ) { |
| 171 | + $headerRow .= Xml::tags( 'th', null, wfMsgExt( $msg, 'parseinline' ) ); |
| 172 | + } |
| 173 | + $headerRow = Xml::tags( 'tr', null, $headerRow ); |
| 174 | + $headerRow = Xml::tags( 'thead', null, $headerRow ); |
| 175 | + |
| 176 | + // Table body |
| 177 | + $rows = array(); |
| 178 | + foreach ( $topics as $topic ) { |
| 179 | + $row = ''; |
| 180 | + $anchor = '#' . LiquidThreadsFormatter::getAnchor($topic); |
| 181 | + $subject = Xml::element( 'a', array( 'href' => $anchor ), |
| 182 | + $topic->getSubject() ); |
| 183 | + $row .= Xml::tags( 'td', null, $subject ); |
| 184 | + |
| 185 | + $replyCount = $wgLang->formatNum( $topic->getPostCount() ); |
| 186 | + $row .= Xml::element( 'td', null, $replyCount ); // TODO |
| 187 | + |
| 188 | +// $timestamp = 'timestamp'; |
| 189 | + $timestamp = $wgLang->timeanddate( $topic->getTouchedTime(), true ); |
| 190 | + $row .= Xml::element( 'td', null, $timestamp ); |
| 191 | + |
| 192 | + $row = Xml::tags( 'tr', null, $row ); |
| 193 | + $rows[] = $row; |
| 194 | + } |
| 195 | + |
| 196 | + $html .= $headerRow . "\n" . Xml::tags( 'tbody', null, implode( "\n", $rows ) ); |
| 197 | + $html = $h2_header . Xml::tags( 'table', array( 'class' => 'lqt_toc' ), $html ); |
| 198 | + // wrap our output in a div for containment |
| 199 | + $html = Xml::tags( 'div', array( 'class' => 'lqt-contents-wrapper' ), $html ); |
| 200 | + |
| 201 | + return $html; |
| 202 | + } |
| 203 | + |
| 204 | + function getList( $kind, $class, $id, $contents ) { |
| 205 | + $html = ''; |
| 206 | + foreach ( $contents as $li ) { |
| 207 | + $html .= Xml::tags( 'li', null, $li ); |
| 208 | + } |
| 209 | + $html = Xml::tags( $kind, array( 'class' => $class, 'id' => $id ), $html ); |
| 210 | + |
| 211 | + return $html; |
| 212 | + } |
| 213 | + |
| 214 | + function getPager() { |
| 215 | + return new LiquidThreadsChannelPager( $this->channel ); |
| 216 | + } |
| 217 | + |
| 218 | + // Hide a number of items from the view |
| 219 | + // Valid values: toc, options, header |
| 220 | + function hideItems( $items ) { |
| 221 | + $this->mShowItems = array_diff( $this->mShowItems, (array)$items ); |
| 222 | + } |
| 223 | + |
| 224 | + // Show a number of items in the view |
| 225 | + // Valid values: toc, options, header |
| 226 | + function showItems( $items ) { |
| 227 | + $this->mShowItems = array_merge( $this->mShowItems, (array)$items ); |
| 228 | + } |
| 229 | + |
| 230 | + // Whether or not to show an item |
| 231 | + function shouldShow( $item ) { |
| 232 | + return in_array( $item, $this->mShowItems ); |
| 233 | + } |
| 234 | + |
| 235 | + // Set the items shown |
| 236 | + function setShownItems( $items ) { |
| 237 | + $this->mShowItems = $items; |
| 238 | + } |
| 239 | + |
| 240 | + /** |
| 241 | + * Gets the base URL to return to this specific page. |
| 242 | + */ |
| 243 | + public function getBaseURL() { |
| 244 | + return $this->baseUrl; |
| 245 | + } |
| 246 | + |
| 247 | + /** |
| 248 | + * Sets the base URL to return to this specific page. |
| 249 | + */ |
| 250 | + public function setBaseURL( $baseUrl ) { |
| 251 | + $this->baseUrl = $baseUrl; |
| 252 | + } |
| 253 | +} |
| 254 | + |
| 255 | +class LiquidThreadsChannelPager extends IndexPager { |
| 256 | + function __construct( $channel ) { |
| 257 | + $this->channel = $channel; |
| 258 | + |
| 259 | + parent::__construct(); |
| 260 | + |
| 261 | + $this->mLimit = $this->getPageLimit(); |
| 262 | + } |
| 263 | + |
| 264 | + function getPageLimit() { |
| 265 | + global $wgRequest; |
| 266 | + $requestedLimit = $wgRequest->getVal( 'limit', null ); |
| 267 | + if ( $requestedLimit ) { |
| 268 | + return $requestedLimit; |
| 269 | + } |
| 270 | + |
| 271 | + global $wgLiquidThreadsDefaultPageLimit; |
| 272 | + return $wgLiquidThreadsDefaultPageLimit; |
| 273 | + } |
| 274 | + |
| 275 | + function getQueryInfo() { |
| 276 | + $queryInfo = array( |
| 277 | + 'tables' => array( 'lqt_topic', 'lqt_topic_version' ), |
| 278 | + 'fields' => '*', |
| 279 | + 'conds' => array( |
| 280 | + 'lqt_channel' => $this->channel->getID(), |
| 281 | + ), |
| 282 | + 'join_conds' => array( |
| 283 | + 'lqt_topic_version' => array( 'left join', |
| 284 | + array( 'lqt_current_version=ltv_id' ) ), |
| 285 | + ), |
| 286 | + ); |
| 287 | + |
| 288 | + return $queryInfo; |
| 289 | + } |
| 290 | + |
| 291 | + // Adapted from getBody(). |
| 292 | + function getRows() { |
| 293 | + if ( !$this->mQueryDone ) { |
| 294 | + $this->doQuery(); |
| 295 | + } |
| 296 | + |
| 297 | + # Don't use any extra rows returned by the query |
| 298 | + $numRows = min( $this->mResult->numRows(), $this->mLimit ); |
| 299 | + |
| 300 | + $rows = array(); |
| 301 | + |
| 302 | + if ( $numRows ) { |
| 303 | + if ( $this->mIsBackwards ) { |
| 304 | + for ( $i = $numRows - 1; $i >= 0; $i-- ) { |
| 305 | + $this->mResult->seek( $i ); |
| 306 | + $row = $this->mResult->fetchObject(); |
| 307 | + $rows[] = $row; |
| 308 | + } |
| 309 | + } else { |
| 310 | + $this->mResult->seek( 0 ); |
| 311 | + for ( $i = 0; $i < $numRows; $i++ ) { |
| 312 | + $row = $this->mResult->fetchObject(); |
| 313 | + $rows[] = $row; |
| 314 | + } |
| 315 | + } |
| 316 | + } |
| 317 | + |
| 318 | + return $rows; |
| 319 | + } |
| 320 | + |
| 321 | + function formatRow( $row ) { |
| 322 | + // No-op, we get the list of rows from getRows() |
| 323 | + } |
| 324 | + |
| 325 | + function getIndexField() { |
| 326 | + return 'lqt_touched'; |
| 327 | + } |
| 328 | + |
| 329 | + function getDefaultDirections() { |
| 330 | + return true; // Descending |
| 331 | + } |
| 332 | + |
| 333 | + /** |
| 334 | + * A navigation bar with images |
| 335 | + * Stolen from TablePager because it's pretty. |
| 336 | + */ |
| 337 | + function getNavigationBar() { |
| 338 | + global $wgStylePath, $wgContLang; |
| 339 | + |
| 340 | + if ( method_exists( $this, 'isNavigationBarShown' ) && |
| 341 | + !$this->isNavigationBarShown() ) |
| 342 | + return ''; |
| 343 | + |
| 344 | + $path = "$wgStylePath/common/images"; |
| 345 | + $labels = array( |
| 346 | + 'first' => 'table_pager_first', |
| 347 | + 'prev' => 'table_pager_prev', |
| 348 | + 'next' => 'table_pager_next', |
| 349 | + 'last' => 'table_pager_last', |
| 350 | + ); |
| 351 | + $images = array( |
| 352 | + 'first' => $wgContLang->isRTL() ? 'arrow_last_25.png' : 'arrow_first_25.png', |
| 353 | + 'prev' => $wgContLang->isRTL() ? 'arrow_right_25.png' : 'arrow_left_25.png', |
| 354 | + 'next' => $wgContLang->isRTL() ? 'arrow_left_25.png' : 'arrow_right_25.png', |
| 355 | + 'last' => $wgContLang->isRTL() ? 'arrow_first_25.png' : 'arrow_last_25.png', |
| 356 | + ); |
| 357 | + $disabledImages = array( |
| 358 | + 'first' => $wgContLang->isRTL() ? 'arrow_disabled_last_25.png' : 'arrow_disabled_first_25.png', |
| 359 | + 'prev' => $wgContLang->isRTL() ? 'arrow_disabled_right_25.png' : 'arrow_disabled_left_25.png', |
| 360 | + 'next' => $wgContLang->isRTL() ? 'arrow_disabled_left_25.png' : 'arrow_disabled_right_25.png', |
| 361 | + 'last' => $wgContLang->isRTL() ? 'arrow_disabled_first_25.png' : 'arrow_disabled_last_25.png', |
| 362 | + ); |
| 363 | + |
| 364 | + $linkTexts = array(); |
| 365 | + $disabledTexts = array(); |
| 366 | + foreach ( $labels as $type => $label ) { |
| 367 | + $msgLabel = wfMsgHtml( $label ); |
| 368 | + $linkTexts[$type] = "<img src=\"$path/{$images[$type]}\" alt=\"$msgLabel\"/><br />$msgLabel"; |
| 369 | + $disabledTexts[$type] = "<img src=\"$path/{$disabledImages[$type]}\" alt=\"$msgLabel\"/><br />$msgLabel"; |
| 370 | + } |
| 371 | + $links = $this->getPagingLinks( $linkTexts, $disabledTexts ); |
| 372 | + |
| 373 | + $navClass = htmlspecialchars( $this->getNavClass() ); |
| 374 | + $s = "<table class=\"$navClass\" align=\"center\" cellpadding=\"3\"><tr>\n"; |
| 375 | + $cellAttrs = 'valign="top" align="center" width="' . 100 / count( $links ) . '%"'; |
| 376 | + foreach ( $labels as $type => $label ) { |
| 377 | + $s .= "<td $cellAttrs>{$links[$type]}</td>\n"; |
| 378 | + } |
| 379 | + $s .= "</tr></table>\n"; |
| 380 | + return $s; |
| 381 | + } |
| 382 | + |
| 383 | + function getNavClass() { |
| 384 | + return 'TalkpagePager_nav'; |
| 385 | + } |
| 386 | +} |
Property changes on: branches/lqt-updates/extensions/LiquidThreads/classes/view/ChannelView.php |
___________________________________________________________________ |
Added: svn:executable |
1 | 387 | + * |