Index: trunk/extensions/Wikilog/WikilogFeed.php |
— | — | @@ -598,3 +598,220 @@ |
599 | 599 | } |
600 | 600 | } |
601 | 601 | } |
| 602 | + |
| 603 | +/** |
| 604 | + * Syndication feed generator for wikilog comments. |
| 605 | + */ |
| 606 | +class WikilogCommentFeed |
| 607 | + extends WikilogFeed |
| 608 | +{ |
| 609 | + /** |
| 610 | + * If displaying comments for a single article. |
| 611 | + */ |
| 612 | + protected $mSingleItem = false; |
| 613 | + |
| 614 | + /** |
| 615 | + * WikilogCommentFeed constructor. |
| 616 | + * |
| 617 | + * @param $title Title Feed title and URL. |
| 618 | + * @param $format string Feed format ('atom' or 'rss'). |
| 619 | + * @param $query WikilogCommentQuery Query parameters. |
| 620 | + * @param $limit integer Number of items to generate. |
| 621 | + */ |
| 622 | + public function __construct( Title $title, $format, |
| 623 | + WikilogCommentQuery $query, $limit = false ) |
| 624 | + { |
| 625 | + global $wgWikilogNumComments; |
| 626 | + |
| 627 | + if ( !$limit ) $limit = $wgWikilogNumComments; |
| 628 | + parent::__construct( $title, $format, $query, $limit ); |
| 629 | + } |
| 630 | + |
| 631 | + public function getIndexField() { |
| 632 | + return 'wlc_timestamp'; |
| 633 | + } |
| 634 | + |
| 635 | + public function getFeedObject() { |
| 636 | + if ( ( $item = $this->mQuery->getItem() ) ) { |
| 637 | + return $this->getItemFeedObject( $item ); |
| 638 | + } else { |
| 639 | + return $this->getSiteFeedObject(); |
| 640 | + } |
| 641 | + } |
| 642 | + |
| 643 | + /** |
| 644 | + * Generates and populates a WlSyndicationFeed object for the site. |
| 645 | + * |
| 646 | + * @return WlSyndicationFeed object. |
| 647 | + */ |
| 648 | + public function getSiteFeedObject() { |
| 649 | + global $wgContLanguageCode, $wgWikilogFeedClasses, $wgFavicon, $wgLogo; |
| 650 | + |
| 651 | + $title = wfMsgForContent( 'wikilog-feed-title', |
| 652 | + wfMsgForContent( 'wikilog-specialwikilogcomments-title' ), |
| 653 | + $wgContLanguageCode |
| 654 | + ); |
| 655 | + $subtitle = wfMsgExt( 'wikilog-comment-feed-description', array( 'parse', 'content' ) ); |
| 656 | + |
| 657 | + $updated = $this->mDb->selectField( 'wikilog_comments', |
| 658 | + 'MAX(wlc_updated)', false, __METHOD__ |
| 659 | + ); |
| 660 | + if ( !$updated ) $updated = wfTimestampNow(); |
| 661 | + |
| 662 | + $url = $this->mTitle->getFullUrl(); |
| 663 | + |
| 664 | + $feed = new $wgWikilogFeedClasses[$this->mFormat]( |
| 665 | + $url, $title, $updated, $url |
| 666 | + ); |
| 667 | + $feed->setSubtitle( new WlTextConstruct( 'html', $subtitle ) ); |
| 668 | + if ( $wgFavicon !== false ) { |
| 669 | + $feed->setIcon( wfExpandUrl( $wgFavicon ) ); |
| 670 | + } |
| 671 | + if ( $this->mCopyright ) { |
| 672 | + $feed->setRights( new WlTextConstruct( 'html', $this->mCopyright ) ); |
| 673 | + } |
| 674 | + return $feed; |
| 675 | + } |
| 676 | + |
| 677 | + /** |
| 678 | + * Generates and populates a WlSyndicationFeed object for the given |
| 679 | + * wikilog article. |
| 680 | + * |
| 681 | + * @param $item WikilogItem Wikilog article that owns this feed. |
| 682 | + * @return WlSyndicationFeed object, or NULL if not possible. |
| 683 | + */ |
| 684 | + public function getItemFeedObject( WikilogItem $item ) { |
| 685 | + global $wgContLanguageCode, $wgWikilogFeedClasses, $wgFavicon; |
| 686 | + |
| 687 | + $title = wfMsgForContent( 'wikilog-feed-title', |
| 688 | + wfMsgForContent( 'wikilog-title-comments', $item->mName ), |
| 689 | + $wgContLanguageCode |
| 690 | + ); |
| 691 | + $subtitle = wfMsgExt( 'wikilog-comment-feed-description', array( 'parse', 'content' ) ); |
| 692 | + $updated = $this->mDb->selectField( 'wikilog_comments', |
| 693 | + 'MAX(wlc_updated)', array( 'wlc_post' => $item->mID ), __METHOD__ |
| 694 | + ); |
| 695 | + if ( !$updated ) $updated = wfTimestampNow(); |
| 696 | + |
| 697 | + $url = $this->mTitle->getFullUrl(); |
| 698 | + |
| 699 | + $feed = new $wgWikilogFeedClasses[$this->mFormat]( |
| 700 | + $url, $title, $updated, $url |
| 701 | + ); |
| 702 | + $feed->setSubtitle( new WlTextConstruct( 'html', $subtitle ) ); |
| 703 | + if ( $wgFavicon !== false ) { |
| 704 | + $feed->setIcon( wfExpandUrl( $wgFavicon ) ); |
| 705 | + } |
| 706 | + if ( $this->mCopyright ) { |
| 707 | + $feed->setRights( new WlTextConstruct( 'html', $this->mCopyright ) ); |
| 708 | + } |
| 709 | + return $feed; |
| 710 | + } |
| 711 | + |
| 712 | + /** |
| 713 | + * Generates and returns a single feed entry. |
| 714 | + * @param $row The wikilog comment database entry. |
| 715 | + * @return A new WlSyndicationEntry object. |
| 716 | + */ |
| 717 | + function formatFeedEntry( $row ) { |
| 718 | + global $wgMimeType; |
| 719 | + |
| 720 | + # Create comment object. |
| 721 | + $item = $this->mSingleItem ? $this->mSingleItem : WikilogItem::newFromRow( $row ); |
| 722 | + $comment = WikilogComment::newFromRow( $item, $row ); |
| 723 | + |
| 724 | + # Prepare some strings. |
| 725 | + if ( $comment->mUserID ) { |
| 726 | + $usertext = $comment->mUserText; |
| 727 | + } else { |
| 728 | + $usertext = wfMsgForContent( 'wikilog-comment-anonsig', |
| 729 | + $comment->mUserText, ''/*talk*/, $comment->mAnonName |
| 730 | + ); |
| 731 | + } |
| 732 | + if ( $this->mSingleItem ) { |
| 733 | + $title = wfMsgForContent( 'wikilog-comment-feed-title1', |
| 734 | + $comment->mID, $usertext |
| 735 | + ); |
| 736 | + } else { |
| 737 | + $title = wfMsgForContent( 'wikilog-comment-feed-title2', |
| 738 | + $comment->mID, $usertext, $comment->mItem->mName |
| 739 | + ); |
| 740 | + } |
| 741 | + |
| 742 | + # Create new syndication entry. |
| 743 | + $entry = new WlSyndicationEntry( |
| 744 | + self::makeEntryId( $comment ), |
| 745 | + $title, |
| 746 | + $comment->mUpdated, |
| 747 | + $comment->getCommentArticleTitle()->getFullUrl() |
| 748 | + ); |
| 749 | + |
| 750 | + # Comment text. |
| 751 | + if ( $comment->mCommentRev ) { |
| 752 | + list( $article, $parserOutput ) = WikilogUtils::parsedArticle( $comment->mCommentTitle, true ); |
| 753 | + $content = Sanitizer::removeHTMLcomments( $parserOutput->getText() ); |
| 754 | + if ( $content ) { |
| 755 | + $entry->setContent( new WlTextConstruct( 'html', $content ) ); |
| 756 | + } |
| 757 | + } |
| 758 | + |
| 759 | + # Author. |
| 760 | + $usertitle = Title::makeTitle( NS_USER, $comment->mUserText ); |
| 761 | + $useruri = $usertitle->exists() ? $usertitle->getFullUrl() : null; |
| 762 | + $entry->addAuthor( $usertext, $useruri ); |
| 763 | + |
| 764 | + # Timestamp |
| 765 | + $entry->setPublished( $comment->mTimestamp ); |
| 766 | + |
| 767 | + return $entry; |
| 768 | + } |
| 769 | + |
| 770 | + /** |
| 771 | + * Performs the database query that returns the syndication feed entries |
| 772 | + * and store the result wrapper in $this->mResult. |
| 773 | + */ |
| 774 | + function doQuery() { |
| 775 | + # If displaying comments for a single item, save the item. |
| 776 | + # Otherwise, set query option to return items along with their |
| 777 | + # comments. |
| 778 | + if ( ( $item = $this->mQuery->getItem() ) ) { |
| 779 | + $this->mSingleItem = $item; |
| 780 | + } else { |
| 781 | + $this->mQuery->setOption( 'include-item' ); |
| 782 | + } |
| 783 | + return parent::doQuery(); |
| 784 | + } |
| 785 | + |
| 786 | + /** |
| 787 | + * Returns the keys for the timestamp and feed output in the object cache. |
| 788 | + */ |
| 789 | + function getCacheKeys() { |
| 790 | + if ( ( $item = $this->mQuery->getItem() ) ) { |
| 791 | + $title = $item->mTitle; |
| 792 | + } else { |
| 793 | + $title = null; |
| 794 | + } |
| 795 | + $id = $title ? 'id:' . $title->getArticleId() : 'site'; |
| 796 | + $ft = 'show:' . $this->mQuery->getModStatus() . |
| 797 | + ':limit:' . $this->mLimit; |
| 798 | + return array( |
| 799 | + wfMemcKey( 'wikilog', $this->mFormat, $id, 'timestamp' ), |
| 800 | + wfMemcKey( 'wikilog', $this->mFormat, $id, $ft ) |
| 801 | + ); |
| 802 | + } |
| 803 | + |
| 804 | + /** |
| 805 | + * Creates an unique ID for a feed entry. Tries to use $wgTaggingEntity |
| 806 | + * if possible in order to create an RFC 4151 tag, otherwise, we use the |
| 807 | + * page URL. |
| 808 | + */ |
| 809 | + public static function makeEntryId( WikilogComment $comment ) { |
| 810 | + global $wgTaggingEntity; |
| 811 | + if ( $wgTaggingEntity ) { |
| 812 | + $qstr = wfArrayToCGI( array( 'wk' => wfWikiID(), 'id' => $comment->getID() ) ); |
| 813 | + return "tag:{$wgTaggingEntity}:/MediaWiki/Wikilog/comment?{$qstr}"; |
| 814 | + } else { |
| 815 | + return $comment->getCommentArticleTitle()->getFullUrl(); |
| 816 | + } |
| 817 | + } |
| 818 | +} |
Index: trunk/extensions/Wikilog/Wikilog.php |
— | — | @@ -89,6 +89,7 @@ |
90 | 90 | // WikilogFeed.php |
91 | 91 | 'WikilogFeed' => $dir . 'WikilogFeed.php', |
92 | 92 | 'WikilogItemFeed' => $dir . 'WikilogFeed.php', |
| 93 | + 'WikilogCommentFeed' => $dir . 'WikilogFeed.php', |
93 | 94 | |
94 | 95 | // WikilogQuery.php |
95 | 96 | 'WikilogQuery' => $dir . 'WikilogQuery.php', |
Index: trunk/extensions/Wikilog/WikilogCommentsPage.php |
— | — | @@ -120,6 +120,18 @@ |
121 | 121 | return parent::view(); |
122 | 122 | } |
123 | 123 | |
| 124 | + # Create our query object. |
| 125 | + $query = new WikilogCommentQuery( $this->mItem ); |
| 126 | + |
| 127 | + if ( ( $feedFormat = $wgRequest->getVal( 'feed' ) ) ) { |
| 128 | + # RSS or Atom feed requested. Ignore all other options. |
| 129 | + global $wgWikilogNumComments; |
| 130 | + $query->setModStatus( WikilogCommentQuery::MS_ACCEPTED ); |
| 131 | + $feed = new WikilogCommentFeed( $this->mTitle, $feedFormat, $query, |
| 132 | + $wgRequest->getInt( 'limit', $wgWikilogNumComments ) ); |
| 133 | + return $feed->execute(); |
| 134 | + } |
| 135 | + |
124 | 136 | if ( $this->mSingleComment ) { |
125 | 137 | # Single comment view, show comment followed by its replies. |
126 | 138 | $params = $this->mFormatter->getCommentMsgParams( $this->mSingleComment ); |
— | — | @@ -153,20 +165,21 @@ |
154 | 166 | $wgOut->setSubtitle( wfMsg( 'wikilog-backlink', $link ) ); |
155 | 167 | |
156 | 168 | # Retrieve comments (or replies) from database and display them. |
157 | | - $this->viewComments(); |
| 169 | + $this->viewComments( $query ); |
| 170 | + |
| 171 | + # Add feed links. |
| 172 | + $wgOut->setSyndicated(); |
158 | 173 | } |
159 | 174 | |
160 | 175 | /** |
161 | 176 | * Wikilog comments view. Retrieve comments from database and display |
162 | 177 | * them in threads. |
163 | 178 | */ |
164 | | - protected function viewComments() { |
| 179 | + protected function viewComments( WikilogCommentQuery $query ) { |
165 | 180 | global $wgOut, $wgRequest; |
166 | 181 | |
167 | 182 | # Prepare query and pager objects. |
168 | 183 | $replyTo = $wgRequest->getInt( 'wlParent' ); |
169 | | - $query = new WikilogCommentQuery(); |
170 | | - $query->setItem( $this->mItem ); |
171 | 184 | $pager = new WikilogCommentThreadPager( $query, $this->mFormatter ); |
172 | 185 | |
173 | 186 | # Different behavior when displaying a single comment. |
Index: trunk/extensions/Wikilog/Wikilog.i18n.php |
— | — | @@ -118,6 +118,9 @@ |
119 | 119 | # Atom and RSS feeds |
120 | 120 | 'wikilog-feed-title' => '{{SITENAME}} - $1 [$2]', # $1 = title, $2 = content language |
121 | 121 | 'wikilog-feed-description' => 'Read the most recent posts in this feed.', |
| 122 | + 'wikilog-comment-feed-title1' => 'Comment by $2 (#$1)', |
| 123 | + 'wikilog-comment-feed-title2' => 'Comment by $2 to $3 (#$1)', |
| 124 | + 'wikilog-comment-feed-description' => 'Read the most recent comments in this feed.', |
122 | 125 | |
123 | 126 | # Item and comments page titles |
124 | 127 | 'wikilog-title-item-full' => '$1 - $2', # $1 = article title, $2 wikilog title |
— | — | @@ -310,6 +313,13 @@ |
311 | 314 | * $2 is the wikilog title', |
312 | 315 | 'wikilog-title-comments' => 'Parameters: |
313 | 316 | * $1 is a page title', |
| 317 | + 'wikilog-comment-feed-title1' => 'Parameters: |
| 318 | +* $1 is a comment number (to make unique titles for different comments) |
| 319 | +* $2 is the commenter name', |
| 320 | + 'wikilog-comment-feed-title2' => 'Parameters: |
| 321 | +* $1 is a comment number (to make unique titles for different comments) |
| 322 | +* $2 is the commenter name |
| 323 | +* $3 is the title of the article the comment was posted to', |
314 | 324 | 'wikilog-error-msg' => 'Parameters: |
315 | 325 | * $1 is an error message', |
316 | 326 | 'wikilog-invalid-param' => 'Parameters: |
Index: trunk/extensions/Wikilog/WikilogComment.php |
— | — | @@ -703,8 +703,7 @@ |
704 | 704 | ); |
705 | 705 | } else { |
706 | 706 | $authorPlain = htmlspecialchars( $comment->mAnonName ); |
707 | | - $authorFmt = wfMsgExt( 'wikilog-comment-anonsig', |
708 | | - array( 'content', 'parseinline', 'replaceafter' ), |
| 707 | + $authorFmt = wfMsgForContent( 'wikilog-comment-anonsig', |
709 | 708 | Xml::wrapClass( $this->mSkin->userLink( $comment->mUserID, $comment->mUserText ), 'wl-comment-author' ), |
710 | 709 | $this->mSkin->userTalkLink( $comment->mUserID, $comment->mUserText ), |
711 | 710 | htmlspecialchars( $comment->mAnonName ) |