r87190 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r87189‎ | r87190 | r87191 >
Date:14:04, 1 May 2011
Author:werdna
Status:deferred (Comments)
Tags:
Comment:
LiquidThreads backend rewrite: Progress commit on LiquidThreadsFormatter, its friend LiquidThreadsFormatterContext and their subclasses.
A LiquidThreadsFormatter takes a LiquidThreadsObject and turns it into HTML for display. Its main entry point, getHTML(), takes a LiquidThreadsFormatterContext, which contains contextual information such as language, user, nesting level and what not.
There is currently no formatter for the LiquidThreadsTopic object.
Also includes a few data model changes (notably, added a field for the timestamp attributed to a post in the signature)
Modified paths:
  • /branches/lqt-updates/extensions/LiquidThreads/LiquidThreads.php (modified) (history)
  • /branches/lqt-updates/extensions/LiquidThreads/classes/model/Post.php (modified) (history)
  • /branches/lqt-updates/extensions/LiquidThreads/classes/model/PostVersion.php (modified) (history)
  • /branches/lqt-updates/extensions/LiquidThreads/classes/view (added) (history)
  • /branches/lqt-updates/extensions/LiquidThreads/classes/view/Formatter.php (added) (history)
  • /branches/lqt-updates/extensions/LiquidThreads/classes/view/PostFormatter.php (added) (history)
  • /branches/lqt-updates/extensions/LiquidThreads/new-schema.sql (modified) (history)

Diff [purge]

Index: branches/lqt-updates/extensions/LiquidThreads/new-schema.sql
@@ -141,6 +141,11 @@
142142 lpv_parent_post bigint(10) unsigned null,
143143
144144 -- Signature
 145+ lpv_signature TINYBLOB NOT NULL,
 146+
 147+ -- Attributed date/time
 148+ lpv_post_time varbinary(14) not null,
 149+
145150 lpv_signature TINYBLOB NOT NULL
146151 ) /*$wgDBTableOptions*/;
147152
Index: branches/lqt-updates/extensions/LiquidThreads/LiquidThreads.php
@@ -246,6 +246,7 @@
247247 // // Path to the LQT directory
248248 // $wgLiquidThreadsExtensionPath = "{$wgScriptPath}/extensions/LiquidThreads";
249249
 250+// Data model
250251 $wgAutoloadClasses['LiquidThreadsObject'] = "$dir/classes/model/Object.php";
251252 $wgAutoloadClasses['LiquidThreadsVersionedObject'] = "$dir/classes/model/VersionedObject.php";
252253 $wgAutoloadClasses['LiquidThreadsPost'] = "$dir/classes/model/Post.php";
@@ -254,6 +255,12 @@
255256 $wgAutoloadClasses['LiquidThreadsTopicVersion'] = "$dir/classes/model/TopicVersion.php";
256257 $wgAutoloadClasses['LiquidThreadsChannel'] = "$dir/classes/model/Channel.php";
257258
 259+// Formatters
 260+$wgAutoloadClasses['LiquidThreadsFormatter'] = "$dir/classes/view/Formatter.php";
 261+$wgAutoloadClasses['LiquidThreadsFormatterContext'] = "$dir/classes/view/Formatter.php";
 262+$wgAutoloadClasses['LiquidThreadsPostFormatter'] = "$dir/classes/view/PostFormatter.php";
 263+$wgAutoloadClasses['LiquidThreadsPostFormatterContext'] = "$dir/classes/view/PostFormatter.php";
 264+
258265 /** CONFIGURATION SECTION */
259266
260267 $wgDefaultUserOptions['lqt-watch-threads'] = true;
Index: branches/lqt-updates/extensions/LiquidThreads/classes/model/PostVersion.php
@@ -14,6 +14,9 @@
1515 /** ID of the post that this version applies to **/
1616 protected $postID;
1717
 18+ /** The Post that this version applies to **/
 19+ protected $post;
 20+
1821 /** User object for the person who created this *VERSION* **/
1922 protected $versionUser;
2023
@@ -41,6 +44,9 @@
4245 /** Actual text **/
4346 protected $text = null;
4447
 48+ /** Actual text, parsed to HTML **/
 49+ protected $contentHTML = null;
 50+
4551 /** Whether or not the text has been modified directly in $text
4652 * (and therefore needs to be saved to ES). **/
4753 protected $textDirty = false;
@@ -54,6 +60,9 @@
5561 /** The signature attached to this post **/
5662 protected $signature = null;
5763
 64+ /** Attributed timestamp for this post **/
 65+ protected $postTime = null;
 66+
5867 /* Ancestry information, for not-saved errors */
5968
6069 /** Where this object was instantiated. Used for not-saved errors **/
@@ -121,9 +130,10 @@
122131 /**
123132 * Factory method to retrieve a PostVersion from a Post and point in time.
124133 * If the point in time is blank, it retrieves the most recent one.
 134+ * Throws an exception on failure.
125135 * @param $post LiquidThreadsPost: The Post to retrieve a version for.
126136 * @param $timestamp String: A timestamp for the point in time (optional)
127 - * @return LiquidThreadsPostVersion: The version for that post at that point in time, or null.
 137+ * @return LiquidThreadsPostVersion: The version for that post at that point in time.
128138 */
129139 public static function newPointInTime( $post, $timestamp = null ) {
130140 if ( ! $post instanceof LiquidThreadsPost ) {
@@ -139,12 +149,15 @@
140150 $dbr->addQuotes( $dbr->timestamp( $timestamp ) );
141151 }
142152
143 - $row = $dbr->selectRow( 'lqt_post_version', '*', $conds, __METHOD__ );
 153+ $row = $dbr->selectRow( 'lqt_post_version', '*', $conds, __METHOD__,
 154+ array( 'ORDER BY' => 'lpv_timestamp DESC' ) );
144155
145156 if ( $row ) {
146 - return self::newFromRow( $row );
 157+ $obj = self::newFromRow( $row );
 158+ $obj->setPost( $post );
 159+ return $obj;
147160 } else {
148 - return null;
 161+ throw new MWException( "No version available at this point in time" );
149162 }
150163 }
151164
@@ -247,6 +260,7 @@
248261 $this->comment = $row->lpv_comment;
249262 $this->timestamp = wfTimestamp( TS_MW, $row->lpv_timestamp );
250263 $this->postID = $row->lpv_post;
 264+ $this->postTime = $row->lpv_post_time;
251265
252266 // Real version data loading
253267 $user = null;
@@ -325,6 +339,7 @@
326340 $this->textRow = null;
327341 $this->textDirty = true;
328342 $this->topicID = $topic->getID();
 343+ $this->signature = '';
329344
330345 if ( $parent ) {
331346 $this->parentID = $parent->getID();
@@ -342,6 +357,13 @@
343358 }
344359
345360 /**
 361+ * @return true if this is the current version of the post, false otherwise.
 362+ */
 363+ public function isCurrent() {
 364+ $post = $this->getPost();
 365+ }
 366+
 367+ /**
346368 * Set the text of this version.
347369 * @param $newtext String: The new text for this version.
348370 */
@@ -416,6 +438,14 @@
417439 }
418440
419441 /**
 442+ * Sets the timestamp attributed to the post in this version.
 443+ * @param $timestamp The new timestamp.
 444+ */
 445+ public function setPostTime( $timestamp ) {
 446+ $this->postTime = $timestamp;
 447+ }
 448+
 449+ /**
420450 * Saves this Version to the database.
421451 * @param $comment String: (optional) The edit comment for this version.
422452 */
@@ -436,6 +466,8 @@
437467 'lpv_timestamp' => $dbw->timestamp( wfTimestampNow() ),
438468 'lpv_comment' => $this->comment,
439469 'lpv_topic' => $this->topicID,
 470+ 'lpv_signature' => $this->signature,
 471+ 'lpv_post_time' => $this->postTime,
440472 );
441473
442474 $this->timestamp = $row['lpv_timestamp'];
@@ -556,6 +588,39 @@
557589 }
558590
559591 /**
 592+ * Gets the parsed HTML for this Post Version's text.
 593+ */
 594+ public function getContentHTML() {
 595+ if ( !is_null($this->contentHTML) ) {
 596+ return $this->contentHTML;
 597+ }
 598+
 599+ if ( !$this->isMutable() ) {
 600+ global $wgMemc;
 601+
 602+ $memcKey = wfMemcKey( 'lqt', 'post-version', $this->getID(), 'html' );
 603+
 604+ $html = $wgMemc->get( $memcKey );
 605+ if ( $html ) {
 606+ $this->contentHTML = $html;
 607+ return $html;
 608+ }
 609+ }
 610+
 611+ global $wgParser, $wgOut;
 612+
 613+ $html = $wgOut->parse( $this->getText() );
 614+ $this->contentHTML = $html;
 615+
 616+ if ( ! $this->isMutable() ) {
 617+ // 7 day cache
 618+ $wgMemc->set( $memcKey, $html, 86400 * 7 );
 619+ }
 620+
 621+ return $html;
 622+ }
 623+
 624+ /**
560625 * Retrieves the ID of the parent topic associated with this Post Version.
561626 */
562627 public function getTopicID() {
@@ -577,6 +642,33 @@
578643 }
579644
580645 /**
 646+ * Retrieves the timestamp attributed to the post in this version.
 647+ */
 648+ public function getPostTime() {
 649+ return $this->postTime;
 650+ }
 651+
 652+ /**
 653+ * Gets the ID of the post.
 654+ * @return The Post ID that this version is for.
 655+ */
 656+ public function getPostID() {
 657+ return $this->postID;
 658+ }
 659+
 660+ /**
 661+ * Gets the post that this version is for.
 662+ * @return The LiquidThreadsPost that this version applies to.
 663+ */
 664+ public function getPost() {
 665+ if ( is_null($this->post) ) {
 666+ $this->post = LiquidThreadsPost::newFromID( $this->getPostID() );
 667+ }
 668+
 669+ return $this->post;
 670+ }
 671+
 672+ /**
581673 * Lets you set the post ID, once.
582674 * Only valid use is from LiquidThreadsPost::save(), for a new LiquidThreadsPost
583675 * @param $id Integer: The post ID that this version applies to.
@@ -593,4 +685,16 @@
594686 array( 'lpv_id' => $this->getID() ),
595687 __METHOD__ );
596688 }
 689+
 690+ /**
 691+ * Lets you provide the Post object to save loading
 692+ * Validity checking *is* done.
 693+ */
 694+ public function setPost( $post ) {
 695+ if ( $post->getID() != $this->getPostID() ) {
 696+ throw new MWException( "Invalid argument to ".__METHOD__ );
 697+ }
 698+
 699+ $this->post = $post;
 700+ }
597701 }
Index: branches/lqt-updates/extensions/LiquidThreads/classes/model/Post.php
@@ -25,6 +25,9 @@
2626 /** The LiquidThreadsPost that this is found underneath. **/
2727 protected $parent;
2828
 29+ /** Replies **/
 30+ protected $replies = null;
 31+
2932 /* FACTORY METHODS */
3033
3134 /**
@@ -427,5 +430,24 @@
428431 public function setSignature( $sig ) {
429432 $this->getPendingVersion()->setSignature( $sig );
430433 }
 434+
 435+ /**
 436+ * Gets the timestamp attributed to this post.
 437+ */
 438+ public function getPostTime() {
 439+ return wfTimestamp( TS_MW, $this->getCurrentVersion()->getPostTime() );
 440+ }
 441+
 442+ /**
 443+ * Gets replies to this post.
 444+ */
 445+ public function getReplies() {
 446+ if ( is_null( $this->replies ) ) {
 447+ $conditions = array( 'lqp_parent_post' => $this->getID() );
 448+ $this->replies = self::loadFromConditions( $conditions, true );
 449+ }
 450+
 451+ return $this->replies;
 452+ }
431453 }
432454
Index: branches/lqt-updates/extensions/LiquidThreads/classes/view/PostFormatter.php
@@ -0,0 +1,455 @@
 2+<?php
 3+
 4+class LiquidThreadsPostFormatter extends LiquidThreadsFormatter {
 5+ /**
 6+ * Returns the correct class for the context object
 7+ */
 8+ public function getContextClass() {
 9+ return 'LiquidThreadsPostFormatterContext';
 10+ }
 11+
 12+ /**
 13+ * Returns the class that can be formatted by this object.
 14+ */
 15+ public function getObjectClass() {
 16+ return 'LiquidThreadsPost';
 17+ }
 18+
 19+ /**
 20+ * Convert a LiquidThreadsPost to HTML.
 21+ * @param $object The LiquidThreadsPost to convert.
 22+ * @param $context LiquidThreadsPostFormatterContext object.
 23+ */
 24+ public function getHTML( $object, $context = null )
 25+ {
 26+ // Error checking
 27+ $this->checkArguments( $object, $context );
 28+
 29+ if ( ! $context->get('language') ) {
 30+ global $wgLang;
 31+ $context->set( 'language', $wgLang );
 32+ }
 33+
 34+ if ( ! $context->get('user') ) {
 35+ global $wgUser;
 36+ $context->set('user', $wgUser );
 37+ }
 38+
 39+ if ( ! $context->get('nesting-level') ) {
 40+ $context->set('nesting-level', 0);
 41+ }
 42+
 43+ $timestamp = $context->get('timestamp');
 44+ if ( !is_null($timestamp) ) {
 45+ $version = PostVersion::newPointInTime( $post, $timestamp );
 46+ } else {
 47+ $version = $object->getCurrentVersion();
 48+ }
 49+
 50+ if ( $context->get('single') ) {
 51+ return $this->formatSingleComment( $object, $version, $context );
 52+ } else {
 53+ return $this->formatCommentTree( $object, $version, $context );
 54+ }
 55+ }
 56+
 57+ /**
 58+ * Get the HTML for a full tree of comments.
 59+ * @param $object The LiquidThreadsPost to show.
 60+ * @param $version The LiquidThreadsPostVersion at which to show $object
 61+ * @param $context The LiquidThreadsPostFormatterContext to use for context.
 62+ * @return String: HTML result.
 63+ */
 64+ protected function formatCommentTree( $object, $version, $context ) {
 65+ $context->increment('nesting-level');
 66+
 67+ $wrapperClasses = array('lqt-post-tree-wrapper');
 68+
 69+ // FIXME shows children at all times, should only show appropriate children for the time.
 70+ $children = $object->getReplies();
 71+
 72+ if ( count($children) ) {
 73+ $wrapperClasses[] = 'lqt-post-with-replies';
 74+ } else {
 75+ $wrapperClasses[] = 'lqt-post-without-replies';
 76+ }
 77+
 78+ $html = Xml::openElement( 'div',
 79+ array(
 80+ 'class' => implode(' ', $wrapperClasses ),
 81+ 'id' => "lqt-post-id-".$object->getID(),
 82+ ) );
 83+ $html .= Xml::element( 'a', array('name' => $this->getAnchor($object) ) );
 84+
 85+ $html .= $this->formatSingleComment( $object, $version, $context );
 86+
 87+ if ( count($children) ) {
 88+ $html .= Xml::openElement( 'div', array( 'class' => 'lqt-replies' ) );
 89+ foreach( $children as $child ) {
 90+ $html .= $this->formatSingleComment( $child );
 91+ }
 92+ $html .= Xml::closeElement( 'div' );
 93+ }
 94+
 95+ $context->decrement('nesting-level');
 96+
 97+ return $html;
 98+ }
 99+
 100+ /**
 101+ * Get the HTML for a *single comment*.
 102+ * This is basically the guts of this formatter, without tree handling etc.
 103+ * @param $object The LiquidThreadsPost to show.
 104+ * @param $version The LiquidThreadsPostVersion at which to show $object
 105+ * @param $context The LiquidThreadsPostFormatterContext to use for context.
 106+ * @return String: HTML result.
 107+ */
 108+ protected function formatSingleComment( $object, $version, $context ) {
 109+
 110+ $divClass = $this->getDivClass( $object, $context );
 111+
 112+ $html = '';
 113+
 114+ $showAnything = wfRunHooks( 'LiquidThreadsShowPostBody',
 115+ array( $object, $context, &$html ) );
 116+
 117+ if ( $showAnything && $this->runCallback( $object, $context ) ) {
 118+ $html .= Xml::openElement( 'div', array( 'class' => $divClass ) );
 119+
 120+ $text = $version->getContentHTML();
 121+
 122+ $html .= $text;
 123+
 124+ $html .= Xml::closeElement( 'div' );
 125+
 126+ $html .= $this->getToolbar( $object, $version, $context );
 127+ $html .= $this->getPostSignature( $object, $version, $context );
 128+ $html .= Xml::closeElement( 'div' );
 129+ }
 130+
 131+ $html = Xml::tags( 'div', array( 'class' => 'lqt-post-wrapper' ), $html );
 132+
 133+ return $html;
 134+ }
 135+
 136+ /**
 137+ * Get the class for the div surrounding the comment *text*.
 138+ * @param $object The LiquidThreadsPost object
 139+ * @param $context The LiquidThreadsPostFormatterContext object.
 140+ * @return String: Space-separated list of classes.
 141+ */
 142+ protected function getDivClass( $object, $context ) {
 143+ $classes = array('lqt-post-content');
 144+
 145+ $nesting_level = $context->get('nesting-level');
 146+ if ( !is_null( $nesting_level ) ) {
 147+ $classes[] = 'lqt-post-nest-' . $nesting_level;
 148+ $alternatingType = ( $nesting_level % 2 ) ? 'odd' : 'even';
 149+ $classes[] = "lqt-post-$alternatingType";
 150+ }
 151+
 152+ return implode( ' ', $classes );
 153+ }
 154+
 155+ /**
 156+ * Generates a toolbar for a post.
 157+ * @param $post The LiquidThreadsPost being shown.
 158+ * @param $version The LiquidThreadsPostVersion to show the dropdown for.
 159+ * @param $context A LiquidThreadsPostFormatterContext object.
 160+ * @return HTML
 161+ */
 162+ protected function getToolbar( $post, $version, $context ) {
 163+ $html = '';
 164+
 165+ $headerParts = array();
 166+
 167+ foreach ( $this->getMajorCommands( $post, $version, $context ) as $key => $cmd ) {
 168+ $content = $this->formatCommand( $cmd, false /* No icon divs */ );
 169+ $headerParts[] = Xml::tags( 'li',
 170+ array( 'class' => "lqt-command lqt-command-$key" ),
 171+ $content );
 172+ }
 173+
 174+ // Drop-down menu
 175+ $commands = $this->getMinorCommands( $post, $version, $context );
 176+ $menuHTML = Xml::tags( 'ul', array( 'class' => 'lqt-thread-toolbar-command-list' ),
 177+ $this->formatCommands( $commands ) );
 178+
 179+ $triggerText = Xml::tags( 'a', array( 'class' => 'lqt-thread-actions-icon',
 180+ 'href' => '#' ),
 181+ wfMsgHTML( 'lqt-menu-trigger' ) );
 182+ $dropDownTrigger = Xml::tags( 'div',
 183+ array( 'class' => 'lqt-thread-actions-trigger ' .
 184+ 'lqt-command-icon', 'style' => 'display: none;' ),
 185+ $triggerText );
 186+
 187+ if ( count( $commands ) ) {
 188+ $headerParts[] = Xml::tags( 'li',
 189+ array( 'class' => 'lqt-thread-toolbar-menu' ),
 190+ $dropDownTrigger );
 191+ }
 192+
 193+ $html .= implode( ' ', $headerParts );
 194+
 195+ $html = Xml::tags( 'ul', array( 'class' => 'lqt-thread-toolbar-commands' ), $html );
 196+
 197+ $html = Xml::tags( 'div', array( 'class' => 'lqt-thread-toolbar' ), $html ) .
 198+ $menuHTML;
 199+
 200+ return $html;
 201+ }
 202+
 203+ /**
 204+ * Formats a list of toolbar commands.
 205+ * @param $commands Associative array of commands.
 206+ * @return HTML
 207+ * @see LiquidThreadsPostFormatter::formatCommand
 208+ */
 209+ function formatCommands( $commands ) {
 210+ $result = array();
 211+ foreach ( $commands as $key => $command ) {
 212+ $thisCommand = $this->formatCommand( $command );
 213+
 214+ $thisCommand = Xml::tags(
 215+ 'li',
 216+ array( 'class' => 'lqt-command lqt-command-' . $key ),
 217+ $thisCommand
 218+ );
 219+
 220+ $result[] = $thisCommand;
 221+ }
 222+ return join( ' ', $result );
 223+ }
 224+
 225+ /**
 226+ * Formats a toolbar command
 227+ * @param $command Associative array describing this command
 228+ * Valid keys:
 229+ * label: The text to show for this command.
 230+ * href: The URL to link to.
 231+ * enabled: Whether or not this command is enabled.
 232+ * tooltip: If specified, the tooltip to show for this command.
 233+ * icon: If specified, an icon is shown.
 234+ * showlabel: Whether or not to show the label. Default: on.
 235+ * @param $icon_divs Boolean: If false, do not insert <divs> to style with an icon.
 236+ * @return HTML: Command formatted in a <div>
 237+ */
 238+ function formatCommand( $command, $icon_divs = true ) {
 239+ $label = $command['label'];
 240+ $href = $command['href'];
 241+ $enabled = $command['enabled'];
 242+ $tooltip = isset( $command['tooltip'] ) ? $command['tooltip'] : '';
 243+
 244+ if ( isset( $command['icon'] ) ) {
 245+ $icon = Xml::tags( 'div', array( 'title' => $label,
 246+ 'class' => 'lqt-command-icon' ), '&#160;' );
 247+ if ( $icon_divs ) {
 248+ if ( !empty( $command['showlabel'] ) ) {
 249+ $label = $icon . '&#160;' . $label;
 250+ } else {
 251+ $label = $icon;
 252+ }
 253+ } else {
 254+ if ( empty( $command['showlabel'] ) ) {
 255+ $label = '';
 256+ }
 257+ }
 258+ }
 259+
 260+ if ( $enabled ) {
 261+ $thisCommand = Xml::tags( 'a', array( 'href' => $href, 'title' => $tooltip ),
 262+ $label );
 263+ } else {
 264+ $thisCommand = Xml::tags( 'span', array( 'class' => 'lqt_command_disabled',
 265+ 'title' => $tooltip ), $label );
 266+ }
 267+
 268+ return $thisCommand;
 269+ }
 270+
 271+ /**
 272+ * Gets the commands to show in the dropdown of a post.
 273+ * @param $post The LiquidThreadsPost to show a dropdown for.
 274+ * @param $version The LiquidThreadsPostVersion to show the dropdown for.
 275+ * @param $context A LiquidThreadsPostFormatterContext object.
 276+ * @return An associative array of arguments suitable for
 277+ * LiquidThreadsPostFormatter::formatCommand
 278+ * @see LiquidThreadsPostFormatter::formatCommand
 279+ */
 280+ function getMinorCommands( $post, $version, $context ) {
 281+ $commands = array();
 282+
 283+ // TODO make this link operate properly
 284+ $history_url = SpecialPage::getTitleFor( 'PostHistory', $post->getID() );
 285+ $commands['history'] = array(
 286+ 'label' => wfMsgExt( 'history_short', 'parseinline' ),
 287+ 'href' => $history_url,
 288+ 'enabled' => true,
 289+ );
 290+
 291+ // TODO permissions checking
 292+ $edit_url = SpecialPage::getTitleFor( 'EditPost', $post->getID() );
 293+ $commands['edit'] = array(
 294+ 'label' => wfMsgExt( 'edit', 'parseinline' ),
 295+ 'href' => $edit_url,
 296+ 'enabled' => true
 297+ );
 298+
 299+ if ( $context->get('user')->isAllowed( 'lqt-split' ) ) {
 300+ $splitUrl = SpecialPage::getTitleFor( 'SplitThread', $post->getID() )->getFullURL();
 301+ $commands['split'] = array(
 302+ 'label' => wfMsgExt( 'lqt-thread-split', 'parseinline' ),
 303+ 'href' => $splitUrl,
 304+ 'enabled' => true
 305+ );
 306+ }
 307+
 308+ // TODO implement merging
 309+// if ( $context->get('user')->isAllowed( 'lqt-merge' ) ) {
 310+// $mergeParams = $_GET;
 311+// $mergeParams['lqt_merge_from'] = $thread->id();
 312+//
 313+// unset( $mergeParams['title'] );
 314+//
 315+// $mergeUrl = $this->title->getLocalURL( wfArrayToCGI( $mergeParams ) );
 316+// $label = wfMsgExt( 'lqt-thread-merge', 'parseinline' );
 317+//
 318+// $commands['merge'] = array(
 319+// 'label' => $label,
 320+// 'href' => $mergeUrl,
 321+// 'enabled' => true
 322+// );
 323+// }
 324+
 325+ $commands['link'] = array(
 326+ 'label' => wfMsgExt( 'lqt_permalink', 'parseinline' ),
 327+ 'href' => SpecialPage::getTitleFor( 'Post', $post->getID() ),
 328+ 'enabled' => true,
 329+ 'showlabel' => true,
 330+ 'tooltip' => wfMsgExt( 'lqt_permalink', 'parseinline' )
 331+ );
 332+
 333+ wfRunHooks( 'LiquidThreadsPostMinorCommands',
 334+ array( $post, $version, $context, &$commands ) );
 335+
 336+ return $commands;
 337+ }
 338+
 339+ /**
 340+ * Gets the main commands in the main (non-dropdown) part of the toolbar.
 341+ * @param $post The LiquidThreadsPost to show a toolbar for.
 342+ * @param $version The LiquidThreadsPostVersion to show the toolbar for.
 343+ * @param $context A LiquidThreadsPostFormatterContext object.
 344+ * @return An associative array of arguments suitable for
 345+ * LiquidThreadsPostFormatter::formatCommand
 346+ * @see LiquidThreadsPostFormatter::formatCommand
 347+ */
 348+ function getMajorCommands( $post, $version, $context ) {
 349+ $commands = array();
 350+
 351+// if ( $this->user->isAllowed( 'lqt-merge' ) &&
 352+// $this->request->getCheck( 'lqt_merge_from' ) ) {
 353+// $srcThread = Threads::withId( $this->request->getVal( 'lqt_merge_from' ) );
 354+// $par = $srcThread->title()->getPrefixedText();
 355+// $mergeTitle = SpecialPage::getTitleFor( 'MergeThread', $par );
 356+// $mergeUrl = $mergeTitle->getLocalURL( 'dest=' . $thread->id() );
 357+// $label = wfMsgExt( 'lqt-thread-merge-to', 'parseinline' );
 358+//
 359+// $commands['merge-to'] = array(
 360+// 'label' => $label, 'href' => $mergeUrl,
 361+// 'enabled' => true,
 362+// 'tooltip' => $label
 363+// );
 364+// }
 365+
 366+ // TODO permissions checking, proper URL
 367+ $commands['reply'] = array(
 368+ 'label' => wfMsgExt( 'lqt_reply', 'parseinline' ),
 369+ 'href' => SpecialPage::getTitleFor('Reply', $post->getID() )->getFullURL(),
 370+ 'enabled' => true,
 371+ 'showlabel' => 1,
 372+ 'tooltip' => wfMsg( 'lqt_reply' ),
 373+ 'icon' => 'reply.png',
 374+ );
 375+
 376+ // Parent post link
 377+ if ( $version->getParentID() ) {
 378+ $parentID = $version->getParentID();
 379+
 380+ $commands['parent'] = array(
 381+ 'label' => wfMsgExt( 'lqt-parent', 'parseinline' ),
 382+ 'href' => '#' . $this->getAnchor($parentID),
 383+ 'enabled' => true,
 384+ 'showlabel' => 1,
 385+ );
 386+ }
 387+
 388+ wfRunHooks( 'LiquidThreadsPostMajorCommands',
 389+ array( $post, $version, $context, &$commands ) );
 390+
 391+ return $commands;
 392+ }
 393+
 394+ function getPostSignature( $post, $version, $context ) {
 395+ $lang = $context->get('language');
 396+
 397+ $signature = $version->getSignature();
 398+ $signature = self::parseSignature( $signature );
 399+
 400+ $signature = Xml::tags( 'span',
 401+ array( 'class' => 'lqt-thread-user-signature' ),
 402+ $signature );
 403+
 404+ $timestamp = $lang->timeanddate( $version->getPostTime(), true );
 405+ $signature .= Xml::element( 'span',
 406+ array( 'class' => 'lqt-thread-toolbar-timestamp' ),
 407+ $timestamp );
 408+
 409+ wfRunHooks( 'LiquidThreadsPostSignature', array( $post, $version, &$signature ) );
 410+
 411+ $signature = Xml::tags( 'div', array( 'class' => 'lqt-thread-signature' ),
 412+ $signature );
 413+
 414+ return $signature;
 415+ }
 416+
 417+ static function parseSignature( $sig ) {
 418+ global $wgParser, $wgOut;
 419+
 420+ static $parseCache = array();
 421+ $sigKey = md5( $sig );
 422+
 423+ if ( isset( $parseCache[$sigKey] ) ) {
 424+ return $parseCache[$sigKey];
 425+ }
 426+
 427+ $sig = $wgOut->parseInline( $sig );
 428+
 429+ $parseCache[$sigKey] = $sig;
 430+
 431+ return $sig;
 432+ }
 433+}
 434+
 435+/**
 436+ * This class provides context for formatting a LiquidThreadsPost object.
 437+ * Valid fields are:
 438+ * timestamp: The point in time at which to show this LiquidThreadsPost.
 439+ * user: The User object for which to show this LiquidThreadsPost.
 440+ * single: If this is set, we will only show that comment and nothing else.
 441+ * language: Override the Language object that the thread is shown in.
 442+ * nesting-level: If set, specifies the nesting level. Outermost post should be 0.
 443+ * post-callbacks: If set, an associative array of callbacks used to replace display of
 444+ * individual posts. Key is the post ID, value is a callback. Used for
 445+ * edit forms and the like.
 446+ */
 447+class LiquidThreadsPostFormatterContext extends LiquidThreadsFormatterContext {
 448+ protected $validFields = array(
 449+ 'timestamp',
 450+ 'user',
 451+ 'single',
 452+ 'language',
 453+ 'nesting-level',
 454+ 'post-callbacks',
 455+ );
 456+}
Index: branches/lqt-updates/extensions/LiquidThreads/classes/view/Formatter.php
@@ -0,0 +1,160 @@
 2+<?php
 3+
 4+/**
 5+ * This is the abstract base class for a LiquidThreads "Formatter".
 6+ * A formatter takes a LiquidThreads object and converts it to HTML for display.
 7+ */
 8+abstract class LiquidThreadsFormatter {
 9+
 10+ /**
 11+ * Convert an object to HTML.
 12+ * @param $object Object: The object to convert.
 13+ * @param $context LiquidThreadsFormatterContext object appropriate to this class.
 14+ * See subclass documentation for valid fields.
 15+ * @return String: HTML representing this object, for display to users.
 16+ */
 17+ abstract public function getHTML( $object, $context = null );
 18+
 19+ /**
 20+ * Checks arguments to getHTML. Throws an exception if there is a problem.
 21+ * @param $object The object being formatted.
 22+ * @param $context The context object.
 23+ */
 24+ protected function checkArguments( &$object, &$context ) {
 25+ if ( $context == null ) {
 26+ $contextClass = $this->getContextClass();
 27+ $context = new $contextClass;
 28+ } elseif ( ! is_a( $context, $this->getContextClass() ) ) {
 29+ throw new MWException( "Invalid context argument" );
 30+ }
 31+
 32+ if ( ! is_a( $object, $this->getObjectClass() ) ) {
 33+ throw new MWException( "Invalid object for this formatter" );
 34+ }
 35+ }
 36+
 37+ /**
 38+ * Runs the callback hooks in the $context object.
 39+ * @param $object The LiquidThreadsObject to run hooks for.
 40+ * @param $contest The LiquidThreadsFormatterContext object.
 41+ * @return Similar to a MediaWiki hook:
 42+ * true if no callback is run and the method should continue as usual.
 43+ * false if a callback was run and no further processing is necessary.
 44+ */
 45+ protected function runCallback( $object, $context ) {
 46+ $callbacks = $context->get('post-callbacks');
 47+
 48+ if ( !is_array($callbacks) ) {
 49+ return true;
 50+ }
 51+
 52+ if ( isset( $callbacks[$object->getID()] ) ) {
 53+ $callback = $callbacks[$object->getID()];
 54+ call_user_func_array( $callback, array( $object, $context ) );
 55+ return false;
 56+ } else {
 57+ return true;
 58+ }
 59+ }
 60+
 61+ /**
 62+ * Returns the correct class for the context object
 63+ */
 64+ abstract public function getContextClass();
 65+
 66+ /**
 67+ * Returns the class that can be formatted by this object.
 68+ */
 69+ abstract public function getObjectClass();
 70+
 71+ /**
 72+ * Returns whether or not this class can format the given object.
 73+ */
 74+ public function canFormat( $object ) {
 75+ return is_a( $object, $this->getObjectClass() );
 76+ }
 77+
 78+ /**
 79+ * Generates an anchor name for a LiquidThreads object
 80+ * @param $object Mixed: A LiquidThreads object, or an integer.
 81+ * @param $type Mixed: If specified, allows $object to be an integer.
 82+ * Valid values:
 83+ * post: $object is a post ID
 84+ * topic: $object is a topic ID
 85+ * @return String The anchor name, without the #
 86+ */
 87+ public function getAnchor( $object, $type = null ) {
 88+ if ( $object instanceof LiquidThreadsPost ) {
 89+ return 'comment_'.$object->getID();
 90+ } elseif ( $type == 'post' ) {
 91+ return 'comment_'.$object;
 92+ } elseif ( $object instanceof LiquidThreadsTopic ) {
 93+ return 'topic_'.$object->getID();
 94+ } elseif ( $type == 'topic' ) {
 95+ return "topic_$object";
 96+ } else {
 97+ throw new MWException( "Unknown arguments to ".__METHOD__ );
 98+ }
 99+ }
 100+}
 101+
 102+abstract class LiquidThreadsFormatterContext {
 103+ protected $data;
 104+ protected $requiredFields = array();
 105+ protected $validFields = array();
 106+
 107+ /**
 108+ * Returns true if the field name is valid for this context class.
 109+ * @param $field The name of the field.
 110+ * @return Boolean: True if the field name is valid, false otherwise.
 111+ */
 112+ protected function isValidField( $field ) {
 113+ return in_array( $field, $this->validFields );
 114+ }
 115+
 116+ /**
 117+ * Returns the value of a field, or null if it is not set.
 118+ * @param $field String: The field to retrieve.
 119+ * @return Mixed: The value of the field.
 120+ */
 121+ public function get( $field ) {
 122+ if ( ! $this->isValidField( $field ) ) {
 123+ throw new MWException( "Attempt to retrieve invalid item" );
 124+ }
 125+
 126+ if ( isset( $this->data[$field] ) ) {
 127+ return $this->data[$field];
 128+ } else {
 129+ return null;
 130+ }
 131+ }
 132+
 133+ /**
 134+ * Sets the value of a field.
 135+ * @param $field String: The field to set.
 136+ * @param $value Mixed: The new value for the field.
 137+ */
 138+ public function set( $field, $value ) {
 139+ if ( ! $this->isValidField( $field ) ) {
 140+ throw new MWException( "Attempt to set invalid item" );
 141+ }
 142+
 143+ $this->data[$field] = $value;
 144+ }
 145+
 146+ /**
 147+ * Increments a field.
 148+ * @param $field String: The field to increment.
 149+ */
 150+ public function increment( $field ) {
 151+ $this->set( $field, $this->get($field) + 1);
 152+ }
 153+
 154+ /**
 155+ * Decrements a field.
 156+ * @param $field String: The field to decrement.
 157+ */
 158+ public function decrement( $field ) {
 159+ $this->set( $field, $this->get($field) - 1);
 160+ }
 161+}

Follow-up revisions

RevisionCommit summaryAuthorDate
r87230Accidentally added two lpv_signature fields in resolving merge conflict on r8...werdna23:33, 1 May 2011

Comments

#Comment by Krinkle (talk | contribs)   14:19, 1 May 2011

 	-- Signature
+	lpv_signature TINYBLOB NOT NULL,
+	
+	-- Attributed date/time
+	lpv_post_time varbinary(14) not null,
+	
 	lpv_signature TINYBLOB NOT NULL
 ) /*$wgDBTableOptions*/;
 

There's two lpv_signature columns now ?

#Comment by Werdna (talk | contribs)   23:33, 1 May 2011

Thanks for pointing that out. It's been fixed in r87230.

Status & tagging log