https://www.mediawiki.org/wiki/Special:Code/MediaWiki/112155

Revision: 112155
Author:   wikinaut
Date:     2012-02-22 22:13:45 +0000 (Wed, 22 Feb 2012)
Log Message:
-----------
rewritten into 4-file version. Introducing the new class WikiArticleFeeds. 
Added the pagetitle to the auto-discovery feed links (meta tag)

Modified Paths:
--------------
    trunk/extensions/WikiArticleFeeds/WikiArticleFeeds.php

Added Paths:
-----------
    trunk/extensions/WikiArticleFeeds/WikiArticleFeeds_body.php

Modified: trunk/extensions/WikiArticleFeeds/WikiArticleFeeds.php
===================================================================
--- trunk/extensions/WikiArticleFeeds/WikiArticleFeeds.php      2012-02-22 
21:57:41 UTC (rev 112154)
+++ trunk/extensions/WikiArticleFeeds/WikiArticleFeeds.php      2012-02-22 
22:13:45 UTC (rev 112155)
@@ -1,11 +1,12 @@
 <?php
 /**
- * WikiArticleFeeds.php - A MediaWiki extension for converting regular pages 
into feeds.
+ * WikiArticleFeeds.php - a MediaWiki extension for converting regular pages 
into feeds.
  * @author Jim R. Wilson, Thomas Gries
  * @maintainer Thomas Gries
  *
- * @version 0.690
+ * @version 0.700
  * @copyright Copyright (C) 2007 Jim R. Wilson
+ * @copyright Copyright (C) 2012 Thomas Gries
  * @license The MIT License - 
http://www.opensource.org/licenses/mit-license.php
  *
  * Description
@@ -15,13 +16,13 @@
  *
  * Requirements
  *
- * MediaWiki 1.12.x or higher
- * PHP 4.x, 5.x or higher
+ * MediaWiki 1.19 or higher
+ * PHP 5.x or higher
  *
  * Installation
  *
  * 1. Drop this script (WikiArticleFeeds.php) in $IP/extensions
- *    Note: $IP is your MediaWiki install dir.
+ *    Note: $IP is your MediaWiki install directory.
  * 2. Enable the extension by adding this line to your LocalSettings.php:
  *    require_once('extensions/WikiArticleFeeds.php');
  *
@@ -30,8 +31,11 @@
  * Once installed, you may utilize WikiArticleFeeds by invoking the 'feed' 
action of an article:
  * $wgScript?title=Some_Article&action=feed
  *
- * Note: You may optionally supply a feed type.  Acceptable values inculde 
'rss' and 'atom'.
- * If no feed type is specified, the default is 'atom'.  For example:
+ * You may optionally supply a feed type.
+ * Acceptable values include 'rss' and 'atom'.
+ * If no feed type is specified, the default is 'atom'.
+ *
+ * Example:
  * $wgScript?title=Some_Article&action=feed&feed=atom
  *
  * Creating a Feed
@@ -52,6 +56,8 @@
  *
  * Versions
  *
+ * 0.700   rewritten into a four-file version with class
+ *         auto-discovery rss feedlinks come with the page title in it
  * 0.672   changed certain !empty() to isset()
  * 0.671   added ob_start() to prevent headers-already sent problem
  *         added check for undefined variable
@@ -116,7 +122,7 @@
        die( "This is not a valid entry point.\n" );
 }
 
-define( 'WIKIARTICLEFEEDS_VERSION', '0.690 20120219' );
+define( 'VERSION', '0.700 20120222' );
 
 # Bring in supporting classes
 require_once( "$IP/includes/Feed.php" );
@@ -126,484 +132,27 @@
 $wgExtensionCredits['specialpage'][] = array(
        'path' => __FILE__,
        'name' => 'WikiArticleFeeds',
-       'author' => array( 'Jim Wilson (wilson.jim.r&lt;at&gt;gmail.com)', 
'Thomas Gries' ),
+       'author' => array( 'Jim Wilson', 'Thomas Gries' ),
        'url' => '//www.mediawiki.org/wiki/Extension:WikiArticleFeeds',
        'descriptionmsg' => 'wikiarticlefeeds-desc',
-       'version' => WIKIARTICLEFEEDS_VERSION
+       'version' => VERSION,
 );
 
 $dir = dirname( __FILE__ ) . '/';
 $wgExtensionMessagesFiles['WikiArticleFeeds'] = $dir . 
'WikiArticleFeeds.i18n.php';
 $wgExtensionMessagesFiles['WikiArticleFeedsMagic'] = $dir . 
'WikiArticleFeeds.i18n.magic.php';
 
-/**
- * Wrapper class for consolidating all WikiArticleFeeds related parser methods
- */
-class WikiArticleFeedsParser {
-       function feedStart( $text, $params = array() ) {
-               return '<!-- FEED_START -->';
-       }
 
-       function feedEnd( $text, $params = array() ) {
-               return '<!-- FEED_END -->';
-       }
-
-       function burnFeed( $text, $params = array() ) {
-               return ( $params['name'] ? '<!-- BURN_FEED ' . base64_encode( 
serialize( $params['name'] ) ) . ' -->':'' );
-       }
-
-       function itemTagsTag( $text, $params = array() ) {
-               return ( $text ? '<!-- ITEM_TAGS ' . base64_encode( serialize( 
$text ) ) . ' -->':'' );
-       }
-
-       function itemTagsFunction( $parser ) {
-               $tags = func_get_args();
-               array_shift( $tags );
-               return ( !empty( $tags ) ? '<pre>@ITEMTAGS@' . base64_encode( 
serialize( implode( ',', $tags ) ) ) . '@ITEMTAGS@</pre>':'' );
-       }
-
-       function itemTagsPlaceholderCorrections( $parser, &$text ) {
-               $text = preg_replace(
-                                                        
'|<pre>@ITEMTAGS@([0-9a-zA-Z\\+\\/]+=*)@ITEMTAGS@</pre>|',
-                                                        '<!-- ITEM_TAGS $1 
-->',
-                                                        $text
-                                                        );
-               return true;
-       }
-}
-
 # Create global instance
-$wgWikiArticleFeedsParser = new WikiArticleFeedsParser();
-$wgHooks['ParserBeforeTidy'][] = array( $wgWikiArticleFeedsParser, 
'itemTagsPlaceholderCorrections' );
+$wgAutoloadClasses['WikiArticleFeeds'] = $dir . 'WikiArticleFeeds_body.php';
 
-# Add Extension Functions
-$wgHooks['ParserFirstCallInit'][] = 'wfWikiArticleFeedsParserSetup';
-
-# Sets up the WikiArticleFeeds Parser hooks
-function wfWikiArticleFeedsParserSetup( $parser ) {
-       global $wgWikiArticleFeedsParser;
-
-       $parser->setHook( 'startFeed', array( $wgWikiArticleFeedsParser, 
'feedStart' ) );
-       $parser->setHook( 'endFeed', array( $wgWikiArticleFeedsParser, 
'feedEnd' ) );
-       $parser->setHook( 'feedBurner', array( $wgWikiArticleFeedsParser, 
'burnFeed' ) );
-       $parser->setHook( 'itemTags', array( $wgWikiArticleFeedsParser, 
'itemTagsTag' ) );
-
-       $parser->setFunctionHook( 'itemtags', array( $wgWikiArticleFeedsParser, 
'itemTagsFunction' ) );
-       return true;
-}
-
 # Attach Hooks
-$wgHooks['OutputPageBeforeHTML'][] = 'wfAddWikiFeedHeaders';
-$wgHooks['SkinTemplateToolboxEnd'][] = 'wfWikiArticleFeedsToolboxLinks'; // 
introduced in 1.13
-$wgHooks['LinkEnd'][] = 'wfWikiArticleFeedsAddSignatureMarker';
-$wgHooks['UnknownAction'][] = 'wfWikiArticleFeedsAction';
-$wgHooks['ArticlePurge'][] = 'wfPurgeFeedsOnArticlePurge';
+$wgHooks['ParserFirstCallInit'][] = 
'WikiArticleFeeds::wfWikiArticleFeedsSetup';
+$wgHooks['SkinTemplateToolboxEnd'][] = 
'WikiArticleFeeds::wfWikiArticleFeedsToolboxLinks';
+$wgHooks['OutputPageBeforeHTML'][] = 'WikiArticleFeeds::wfAddWikiFeedHeaders';
+$wgHooks['LinkEnd'][] = 
'WikiArticleFeeds::wfWikiArticleFeedsAddSignatureMarker';
+$wgHooks['UnknownAction'][] = 'WikiArticleFeeds::wfWikiArticleFeedsAction';
+$wgHooks['ArticlePurge'][] = 'WikiArticleFeeds::wfPurgeFeedsOnArticlePurge';
 
-/**
- * Adds the Wiki feed link headers.
- *
- * Usage: $wgHooks['OutputPageBeforeHTML'][] = 'wfAddWikiFeedHeaders';
- * @param $out Handle to an OutputPage object (presumably $wgOut).
- * @param $text Article/Output text.
- */
-function wfAddWikiFeedHeaders( $out, $text ) {
-
-       # Short-circuit if this article contains no feeds
-       if ( !preg_match( '/<!-- FEED_START -->/m', $text ) ) return true;
-
-       $rssArr = array(
-                                       'rel' => 'alternate',
-                                       'type' => 'application/rss+xml',
-                                       'title' => 'RSS 2.0',
-                                       );
-       $atomArr = array(
-                                        'rel' => 'alternate',
-                                        'type' => 'application/atom+xml',
-                                        'title' => 'Atom 0.3',
-                                        );
-
-       # Test for feedBurner presence
-       if ( preg_match( '/<!-- BURN_FEED ([0-9a-zA-Z\\+\\/]+=*) -->/m', $text, 
$matches ) ) {
-               $name = @unserialize( @base64_decode( $matches[1] ) );
-               if ( $name ) {
-                       $rssArr['href'] = 'http://feeds.feedburner.com/' . 
urlencode( $name ) . '?format=xml';
-                       $atomArr['href'] = 'http://feeds.feedburner.com/' . 
urlencode( $name ) . '?format=xml';
-               }
-       }
-
-       # If its not being fed by feedBurner, do it ourselves!
-       if ( !array_key_exists( 'href', $rssArr ) || !$rssArr['href'] ) {
-
-               global $wgServer, $wgScript;
-
-               $baseUrl = $wgServer . $wgScript . '?title=' . 
$out->getTitle()->getDBkey() . '&action=feed&feed=';
-               $rssArr['href'] = $baseUrl . 'rss';
-               $atomArr['href'] = $baseUrl . 'atom';
-       }
-
-       $out->addLink( $rssArr );
-       $out->addLink( $atomArr );
-
-       global $wgWikiFeedPresent;
-       $wgWikiFeedPresent = true;
-
-       # True to indicate that other action handlers should continue to 
process this page
-       return true;
-}
-
-/**
- * Add additional attributes to links to User- or User_talk pages.
- * These are used later in wfGenerateWikiFeed to determine signatures with 
timestamps
- * for attributing author and timestamp values to the feed item.
- *
- * Currently this method works only if the user page exists.
- */
-# https://www.mediawiki.org/wiki/Manual:Hooks/LinkEnd
-function wfWikiArticleFeedsAddSignatureMarker( $skin, $title, $options, $text, 
&$attribs, $ret ) {
-       if ( $title->getNamespace() == NS_USER) {
-               $attribs['userpage-link'] = 'true';
-       } elseif ( $title->getNamespace() == NS_USER_TALK ) {
-               $attribs['usertalkpage-link'] = 'true';
-       }
-       return true;
-}
-
-/**
- * Adds the Wiki feed links to the bottom of the toolbox in Monobook or 
like-minded skins.
- *
- * Usage: $wgHooks['SkinTemplateToolboxEnd'][] = 
'wfWikiArticleFeedsToolboxLinks';
- * @param QuickTemplate $template Instance of MonoBookTemplate or other 
QuickTemplate
- */
-function wfWikiArticleFeedsToolboxLinks( $template ) {
-       global $wgServer, $wgScript, $wgWikiFeedPresent;
-
-       # Short-circuit if there are no Feeds present
-       if ( !$wgWikiFeedPresent ) return true;
-
-       if ( is_callable( $template, 'getSkin' ) ) {
-               $title = $template->getSkin()->getTitle();
-       } else {
-               global $wgTitle;
-               $title = $wgTitle;
-       }
-
-       if ( $title->getNamespace() < NS_MAIN ) {
-               return true;
-       }
-
-       $result = '<li id="feedlinks">';
-
-       # Test for feedBurner presence
-       $burned = false;
-       ob_start();
-       $template->html( 'bodytext' );
-       $text = ob_get_contents();
-       ob_end_clean();
-       if ( preg_match( '/<!-- BURN_FEED ([0-9a-zA-Z\\+\\/]+=*) -->/m', $text, 
$matches ) ) {
-               $feedBurnerName = @unserialize( @base64_decode( $matches[1] ) );
-               if ( $feedBurnerName ) {
-                       $feeds = array( 'rss' => 'RSS', 'atom' => 'Atom' );
-                       foreach ( $feeds as $feed => $name ) {
-                               $result .=
-                               '<span id="feed-' . htmlspecialchars( $feed ) . 
'">' .
-                               '<a href="http://feeds.feedburner.com/' . 
urlencode( $feedBurnerName ) . '?format=xml">' .
-                               htmlspecialchars( $name ) . '</a>&#160;</span>';
-                       }
-                       $burned = true;
-               }
-       }
-
-       # Generate regular RSS and Atom feeds if not fed by feedBurner
-       if ( !$burned ) {
-               $dbKey = $title->getPrefixedDBkey();
-               $baseUrl = $wgServer . $wgScript . '?title=' . $dbKey . 
'&action=feed&feed=';
-               $feeds = array( 'rss' => 'RSS', 'atom' => 'Atom' );
-               foreach ( $feeds as $feed => $name ) {
-                       $result .=
-                       '<span id="feed-' . htmlspecialchars( $feed ) . '">' .
-                       '<a href="' . htmlspecialchars( $baseUrl . $feed ) . 
'">' .
-                       htmlspecialchars( $name ) . '</a>&#160;</span>';
-               }
-       }
-
-       echo ( $result . '</li>' );
-
-       # True to indicate that other action handlers should continue to 
process this page
-       return true;
-}
-
-/**
- * Injects handling of the 'feed' action.
- *
- * Usage: $wgHooks['UnknownAction'][] = 'wfWikiArticleFeedsAction';
- * @param $action Handle to an action string (presumably same as global 
$action).
- * @param $article Article to be converted to rss or atom feed
- */
-function wfWikiArticleFeedsAction( $action, $article ) {
-
-       # If some other action is in the works, cut and run!
-       if ( $action != 'feed' ) return true;
-
-       global $wgOut, $wgRequest, $wgFeedClasses, $wgFeedCacheTimeout, 
$wgDBname, $messageMemc, $wgSitename;
-
-       # Get query parameters
-       $feedFormat = $wgRequest->getVal( 'feed', 'atom' );
-       $filterTags = $wgRequest->getVal( 'tags', null );
-
-       # Process requested tags for use in keys
-       if ( $filterTags ) {
-               $filterTags = explode( ',', $filterTags );
-               array_walk( $filterTags, 'trim' );
-               sort( $filterTags );
-       }
-
-       if ( !isset( $wgFeedClasses[$feedFormat] ) ) {
-               wfHttpError( 500, "Internal Server Error", "Unsupported feed 
type." );
-               return false;
-       }
-
-       # Setup cache-checking vars
-       $title = $article->getTitle();
-       $titleDBkey = $title->getPrefixedDBkey();
-       $tags = ( is_array( $filterTags ) ? ':' . implode( ',', $filterTags 
):'' );
-       $key = 
"{$wgDBname}:wikiarticlefeedsextension:{$titleDBkey}:{$feedFormat}{$tags}";
-       $timekey = $key . ":timestamp";
-       $cachedFeed = false;
-       $feedLastmod = $messageMemc->get( $timekey );
-
-       # Dermine last modification time for either the article itself or an 
included template
-       $lastmod = $article->getTimestamp();
-       $templates = $article->getUsedTemplates();
-       foreach ( $templates as $tTitle ) {
-               $tArticle = new Article( $tTitle );
-               $tmod = $tArticle->getTimestamp();
-               $lastmod = ( $lastmod > $tmod ? $lastmod:$tmod );
-       }
-
-       # Check for availability of existing cached version
-       if ( ( $wgFeedCacheTimeout > 0 ) && $feedLastmod ) {
-               $feedLastmodTS = wfTimestamp( TS_UNIX, $feedLastmod );
-               if (
-                       time() - $feedLastmodTS < $wgFeedCacheTimeout ||
-                       $feedLastmodTS > wfTimestamp( TS_UNIX, $lastmod )
-                       ) {
-                       wfDebug( "WikiArticleFeedsExtension: Loading feed from 
cache ($key; $feedLastmod; $lastmod)...\n" );
-                       $cachedFeed = $messageMemc->get( $key );
-               } else {
-                       wfDebug( "WikiArticleFeedsExtension: Cached feed 
timestamp check failed ($feedLastmod; $lastmod)\n" );
-               }
-       }
-
-       # Display cachedFeed, or generate one from scratch
-       global $wgWikiArticleFeedsSkipCache;
-       if ( !$wgWikiArticleFeedsSkipCache && is_string( $cachedFeed ) ) {
-               wfDebug( "WikiArticleFeedsExtension: Outputting cached feed\n" 
);
-               $feed = new $wgFeedClasses[$feedFormat]( $title->getText(), '', 
$title->getFullURL(). " - Feed" );
-               ob_start();
-               $feed->httpHeaders();
-               echo $cachedFeed;
-       } else {
-               wfDebug( "WikiArticleFeedsExtension: Rendering new feed and 
caching it\n" );
-               ob_start();
-               wfGenerateWikiFeed( $article, $feedFormat, $filterTags );
-               $cachedFeed = ob_get_contents();
-               ob_end_flush();
-
-               $expire = 3600; # One hour
-               $messageMemc->set( $key, $cachedFeed );
-               $messageMemc->set( $timekey, wfTimestamp( TS_MW ), $expire );
-       }
-
-       # False to indicate that other action handlers should not process this 
page
-       return false;
-}
-
-/**
- * Purges all associated feeds when an Article is purged.
- *
- * Usage: $wgHooks['ArticlePurge'][] = 'wfPurgeFeedsOnArticlePurge';
- * @param Article $article The Article which is being purged.
- * @return boolean Always true to permit additional hook processing.
- */
-function wfPurgeFeedsOnArticlePurge( $article ) {
-       global $messageMemc, $wgDBname;
-       $titleDBKey = $article->mTitle->getPrefixedDBkey();
-       $keyPrefix = "{$wgDBname}:wikiarticlefeedsextension:{$titleDBKey}";
-       $messageMemc->delete( "{$keyPrefix}:atom:timestamp" );
-       $messageMemc->delete( "{$keyPrefix}:atom" );
-       $messageMemc->delete( "{$keyPrefix}:rss" );
-       $messageMemc->delete( "{$keyPrefix}:rss:timestamp" );
-       return true;
-}
-
-/**
- * Converts an MediaWiki article into a feed, echoing generated content 
directly.
- *
- * @param Article $article Article to be converted to RSS or Atom feed.
- * @param String $feedFormat A format type - must be either 'rss' or 'atom'
- * @param Array $filterTags Tags to use in filtering out items.
- */
-function wfGenerateWikiFeed( $article, $feedFormat = 'atom', $filterTags = 
null ) {
-       global $wgOut, $wgServer, $wgFeedClasses, $wgVersion, $wgSitename;
-
-       # Setup, handle redirects
-       if ( $article->isRedirect() ) {
-               $rtitle = Title::newFromRedirect( $article->getContent() );
-               if ( $rtitle ) {
-                       $article = new Article( $rtitle );
-               }
-       }
-       $title = $article->getTitle();
-       $feedUrl = $title->getFullUrl();
-
-       # Parse page into feed items.
-       $content = $wgOut->parse( $article->getContent() . "\n__NOEDITSECTION__ 
__NOTOC__" );
-       preg_match_all(
-               '/<!--\\s*FEED_START\\s*-->(.*?)<!--\\s*FEED_END\\s*-->/s',
-               $content,
-               $matches
-       );
-       $feedContentSections = $matches[1];
-
-       # Parse and process all feeds, collecting feed items
-       $items = array();
-       $feedDescription = '';
-       foreach ( $feedContentSections as $feedKey => $feedContent ) {
-
-               # Determine Feed item depth (what header level defines a feed)
-               preg_match_all( '/<h(\\d)>/m', $feedContent, $matches );
-               if ( !isset( $matches[1] ) ) next;
-               $lvl = $matches[1][0];
-               foreach ( $matches[1] as $match ) {
-                       if ( $match < $lvl ) $lvl = $match;
-               }
-
-               $sectionRegExp = '#<h' . $lvl . 
'>\s*<span.+?id="(.*?)">\s*(.*?)\s*</span>\s*</h' . $lvl . '>#m';
-
-               # Determine the item titles and default item links
-               preg_match_all(
-                                          $sectionRegExp,
-                                          $feedContent,
-                                          $matches
-                                          );
-               $itemLinks = $matches[1];
-               $itemTitles = $matches[2];
-
-               # Split content into segments
-               $segments = preg_split( $sectionRegExp, $feedContent );
-               $segDesc = trim( strip_tags( array_shift( $segments ) ) );
-               if ( $segDesc ) {
-                       if ( !$feedDescription ) {
-                               $feedDescription = $segDesc;
-                       } else {
-                               $feedDescription = wfMsg( 
'wikiarticlefeeds_combined_description' );
-                       }
-               }
-
-               # Loop over parsed segments and add all items to item array
-               foreach ( $segments as $key => $seg ) {
-
-                       # Filter by tag (if any are present)
-                       $skip = false;
-                       $tags = null;
-                       if ( is_array( $filterTags ) && !empty( $filterTags ) ) 
{
-                               if ( preg_match_all( '/<!-- ITEM_TAGS 
([0-9a-zA-Z\\+\\/]+=*) -->/m', $seg, $matches ) ) {
-                                       $tags = array();
-                                       foreach ( $matches[1] as $encodedString 
) {
-                                               $t = @unserialize( 
@base64_decode( $encodedString ) );
-                                               if ( $t ) {
-                                                       $t = explode( ',', $t );
-                                                       array_walk( $t, 'trim' 
);
-                                                       sort( $t );
-                                                       $tags = array_merge( 
$tags, $t );
-                                               }
-                                       }
-                                       $tags = array_unique( $tags );
-                                       if ( !count( array_intersect( $tags, 
$filterTags ) ) ) $skip = true;
-                                       $seg = preg_replace( '/<!-- ITEM_TAGS 
([0-9a-zA-Z\\+\\/]+=*) -->/m', '', $seg );
-                               } else {
-                                       $skip = true;
-                               }
-                       }
-                       if ( $skip ) continue;
-
-                       # Try hard to determine the item author and date
-                       # Look for a regular signatures of the layout
-                       # userpage-link [optional user_talk page link] date 
(with a delimiting timezone code in parentheses)
-
-                       $author = null;
-                       $date = null;
-
-                       $signatureRegExp = '#<a href=".+?User:.+?" 
title="User:.+?">(.*?)</a> (\d\d):(\d\d), (\d+) ([a-z]+) (\d{4}) 
(\([A-Z]+\))#im';
-
-                       $signatureRegExp1 = 
'#<.*userpage-link.*>(.*?)</a>.*<.*usertalkpage-link.*>.*</a>\) 
(.*\([A-Z]+\))#im';
-                       $signatureRegExp2 = '#<.*userpage-link.*>(.*?)</a> 
(.*\([A-Z]+\))#im';
-                       $signatureRegExp3 = '#<.*usertalkpage-link.*>(.*?)</a> 
(.*\([A-Z]+\))#im';
-                       
-                       $isAttributable = ( preg_match( $signatureRegExp1, 
$seg, $matches )
-                               || preg_match( $signatureRegExp2, $seg, 
$matches )
-                               || preg_match( $signatureRegExp3, $seg, 
$matches ) );
-
-                       if ( $isAttributable ) {
-
-                               list( $author, $timestring ) = array_slice( 
$matches, 1 );
-                       
-                               $tempTimezone = date_default_timezone_get();
-                               date_default_timezone_set( 'UTC' );
-                               $date = date( "YmdHis" , strtotime( $timestring 
) );
-                               date_default_timezone_set( $tempTimezone );
-
-                       }
-
-                       # Set default 'article section' feed-link
-                       $url = $feedUrl . '#' . $itemLinks[$key];
-
-                       # Look for an alternative to the default link (unless 
default 'section linking' has been forced)
-                       global $wgForceArticleFeedSectionLinks;
-                       if ( !$wgForceArticleFeedSectionLinks ) {
-                               $strippedSeg = preg_replace($signatureRegExp, 
'', $seg );
-                               preg_match(
-                                       '#<a 
[^>]*href=([\'"])(.*?)\\1[^>]*>(.*?)</a>#m',
-                                       $strippedSeg,
-                                       $matches
-                                       );
-                               if ( isset( $matches[2] ) ) {
-                                       $url = $matches[2];
-                                       if ( preg_match( '#^/#', $url ) ) {
-                                               $url = $wgServer . $url;
-                                       }
-                               }
-                       }
-
-                       # Create 'absolutified' segment - where all URLs are 
fully qualified
-                       $seg = preg_replace( '/ (href|src)=([\'"])\\//', ' 
$1=$2' . $wgServer . '/', $seg );
-
-                       # Create item and push onto item list
-                       $items[$date][] = new FeedItem( strip_tags( 
$itemTitles[$key] ), $seg, $url, $date, $author );
-               }
-       }
-
-       # Programmatically determine the feed title and id.
-       $feedTitle = $title->getPrefixedText() . " - Feed";
-       $feedId = $title->getFullUrl();
-
-       # Create feed
-       $feed = new $wgFeedClasses[$feedFormat]( $feedTitle, $feedDescription, 
$feedId );
-
-       # Push feed header
-       $tempWgVersion = $wgVersion;
-       $wgVersion .= ' via WikiArticleFeeds ' . WIKIARTICLEFEEDS_VERSION;
-       $feed->outHeader();
-       $wgVersion = $tempWgVersion;
-
-       # Sort all items by date and push onto feed
-       krsort( $items );
-       foreach ( $items as $itemGroup ) {
-               foreach ( $itemGroup as $item ) {
-                       $feed->outItem( $item );
-               }
-       }
-
-       # Feed footer
-       $feed->outFooter();
-}
+$wgWikiArticleFeeds = new WikiArticleFeeds();
+$wgHooks['ParserBeforeTidy'][] = array( $wgWikiArticleFeeds, 
'WikiArticleFeeds::itemTagsPlaceholderCorrections' );

Added: trunk/extensions/WikiArticleFeeds/WikiArticleFeeds_body.php
===================================================================
--- trunk/extensions/WikiArticleFeeds/WikiArticleFeeds_body.php                 
        (rev 0)
+++ trunk/extensions/WikiArticleFeeds/WikiArticleFeeds_body.php 2012-02-22 
22:13:45 UTC (rev 112155)
@@ -0,0 +1,469 @@
+<?php
+
+class WikiArticleFeeds{
+
+       function feedStart( $text, $params = array() ) {
+               return '<!-- FEED_START -->';
+       }
+
+       function feedEnd( $text, $params = array() ) {
+               return '<!-- FEED_END -->';
+       }
+
+       function burnFeed( $text, $params = array() ) {
+               return ( $params['name'] ? '<!-- BURN_FEED ' . base64_encode( 
serialize( $params['name'] ) ) . ' -->':'' );
+       }
+
+       function itemTagsTag( $text, $params = array() ) {
+               return ( $text ? '<!-- ITEM_TAGS ' . base64_encode( serialize( 
$text ) ) . ' -->':'' );
+       }
+
+       function itemTagsFunction( $parser ) {
+               $tags = func_get_args();
+               array_shift( $tags );
+               return ( !empty( $tags ) ? '<pre>@ITEMTAGS@' . base64_encode( 
serialize( implode( ',', $tags ) ) ) . '@ITEMTAGS@</pre>':'' );
+       }
+
+       function itemTagsPlaceholderCorrections( $parser, &$text ) {
+               $text = preg_replace(
+                       
'|<pre>@ITEMTAGS@([0-9a-zA-Z\\+\\/]+=*)@ITEMTAGS@</pre>|',
+                       '<!-- ITEM_TAGS $1 -->',
+                       $text
+               );
+               return true;
+       }
+
+       # Sets up the WikiArticleFeeds Parser hooks
+       static function wfWikiArticleFeedsSetup( $parser ) {
+               global $wgWikiArticleFeeds;
+
+               $parser->setHook( 'startFeed', array( $wgWikiArticleFeeds, 
'feedStart' ) );
+               $parser->setHook( 'endFeed', array( $wgWikiArticleFeeds, 
'feedEnd' ) );
+               $parser->setHook( 'feedBurner', array( $wgWikiArticleFeeds, 
'burnFeed' ) );
+               $parser->setHook( 'itemTags', array( $wgWikiArticleFeeds, 
'itemTagsTag' ) );
+               $parser->setFunctionHook( 'itemtags', array( 
$wgWikiArticleFeeds, 'itemTagsFunction' ) );
+
+               return true;
+       }
+
+       /**
+       * Adds the Wiki feed link headers.
+       *
+       * Usage: $wgHooks['OutputPageBeforeHTML'][] = 'wfAddWikiFeedHeaders';
+       * @param $out Handle to an OutputPage object (presumably $wgOut).
+       * @param $text Article/Output text.
+       */
+       static function wfAddWikiFeedHeaders( $out, $text ) {
+               global $wgTitle;
+       
+               # Short-circuit if this article contains no feeds
+               if ( !preg_match( '/<!-- FEED_START -->/m', $text ) ) return 
true;
+
+               $rssArr = array(
+                       'rel' => 'alternate',
+                       'type' => 'application/rss+xml',
+                       'title' => $wgTitle->getText() . ' - RSS 2.0',
+               );
+               $atomArr = array(
+                       'rel' => 'alternate',
+                       'type' => 'application/atom+xml',
+                       'title' => $wgTitle->getText() . ' - Atom 0.3',
+               );
+
+               # Test for feedBurner presence
+               if ( preg_match( '/<!-- BURN_FEED ([0-9a-zA-Z\\+\\/]+=*) 
-->/m', $text, $matches ) ) {
+                       $name = @unserialize( @base64_decode( $matches[1] ) );
+                       if ( $name ) {
+                               $rssArr['href'] = 
'http://feeds.feedburner.com/' . urlencode( $name ) . '?format=xml';
+                               $atomArr['href'] = 
'http://feeds.feedburner.com/' . urlencode( $name ) . '?format=xml';
+                       }
+               }
+
+               # If its not being fed by feedBurner, do it ourselves!
+               if ( !array_key_exists( 'href', $rssArr ) || !$rssArr['href'] ) 
{
+
+                       global $wgServer, $wgScript;
+
+                       $baseUrl = $wgServer . $wgScript . '?title=' . 
$out->getTitle()->getDBkey() . '&action=feed&feed=';
+                       $rssArr['href'] = $baseUrl . 'rss';
+                       $atomArr['href'] = $baseUrl . 'atom';
+               }
+
+               $out->addLink( $rssArr );
+               $out->addLink( $atomArr );
+
+               global $wgWikiFeedPresent;
+               $wgWikiFeedPresent = true;
+
+               # True to indicate that other action handlers should continue 
to process this page
+               return true;
+       }
+
+       /**
+       * Add additional attributes to links to User- or User_talk pages.
+       * These are used later in wfGenerateWikiFeed to determine signatures 
with timestamps
+       * for attributing author and timestamp values to the feed item.
+       *
+       * Currently this method works only if the user page exists.
+       */
+       # https://www.mediawiki.org/wiki/Manual:Hooks/LinkEnd
+       static function wfWikiArticleFeedsAddSignatureMarker( $skin, $title, 
$options, $text, &$attribs, $ret ) {
+               global $wgWikiFeedPresent;
+               // wfDebug( "wfWikiArticleFeedsAddSignatureMarker: Text:". 
$title->getText() . " Namespace:".$title->getNamespace(). " exists:". 
$title->exists() . "\n" );
+               if ( $title->getNamespace() == NS_USER) {
+                       $attribs['userpage-link'] = 'true';
+               } elseif ( $title->getNamespace() == NS_USER_TALK ) {
+                       $attribs['usertalkpage-link'] = 'true';
+               }
+               return true;
+       }
+
+       /**
+       * Adds the Wiki feed links to the bottom of the toolbox in Monobook or 
like-minded skins.
+       *
+       * Usage: $wgHooks['SkinTemplateToolboxEnd'][] = 
'wfWikiArticleFeedsToolboxLinks';
+       * @param QuickTemplate $template Instance of MonoBookTemplate or other 
QuickTemplate
+       */
+       static function wfWikiArticleFeedsToolboxLinks( $template ) {
+               global $wgServer, $wgScript, $wgWikiFeedPresent;
+
+               # Short-circuit if there are no Feeds present
+               if ( !$wgWikiFeedPresent ) {
+                       return true;
+               }
+
+               if ( is_callable( $template, 'getSkin' ) ) {
+                       $title = $template->getSkin()->getTitle();
+               } else {
+                       global $wgTitle;
+                       $title = $wgTitle;
+               }
+
+               if ( $title->getNamespace() < NS_MAIN ) {
+                       return true;
+               }
+
+               $result = '<li id="feedlinks">';
+
+               # Test for feedBurner presence
+               $burned = false;
+               ob_start();
+               $template->html( 'bodytext' );
+               $text = ob_get_contents();
+               ob_end_clean();
+               if ( preg_match( '/<!-- BURN_FEED ([0-9a-zA-Z\\+\\/]+=*) 
-->/m', $text, $matches ) ) {
+                       $feedBurnerName = @unserialize( @base64_decode( 
$matches[1] ) );
+                       if ( $feedBurnerName ) {
+                               $feeds = array( 'rss' => 'RSS', 'atom' => 
'Atom' );
+                               foreach ( $feeds as $feed => $name ) {
+                                       $result .=
+                                       '<span id="feed-' . htmlspecialchars( 
$feed ) . '">' .
+                                       '<a href="http://feeds.feedburner.com/' 
. urlencode( $feedBurnerName ) . '?format=xml">' .
+                                       htmlspecialchars( $name ) . 
'</a>&#160;</span>';
+                               }
+                               $burned = true;
+                       }
+               }
+
+               # Generate regular RSS and Atom feeds if not fed by feedBurner
+               if ( !$burned ) {
+                       $dbKey = $title->getPrefixedDBkey();
+                       $baseUrl = $wgServer . $wgScript . '?title=' . $dbKey . 
'&action=feed&feed=';
+                       $feeds = array( 'rss' => 'RSS', 'atom' => 'Atom' );
+                       foreach ( $feeds as $feed => $name ) {
+                               $result .=
+                               '<span id="feed-' . htmlspecialchars( $feed ) . 
'">' .
+                               '<a href="' . htmlspecialchars( $baseUrl . 
$feed ) . '">' .
+                               htmlspecialchars( $name ) . '</a>&#160;</span>';
+                       }
+               }
+
+               echo ( $result . '</li>' );
+
+               # True to indicate that other action handlers should continue 
to process this page
+               return true;
+       }
+
+       /**
+       * Injects handling of the 'feed' action.
+       *
+       * Usage: $wgHooks['UnknownAction'][] = 'wfWikiArticleFeedsAction';
+       * @param $action Handle to an action string (presumably same as global 
$action).
+       * @param $article Article to be converted to rss or atom feed
+       */
+       static function wfWikiArticleFeedsAction( $action, $article ) {
+
+               # If some other action is in the works, cut and run!
+               if ( $action != 'feed' ) {
+                       return true;
+               }
+
+               global  $wgOut, $wgRequest, $wgFeedClasses, 
$wgFeedCacheTimeout, $wgDBname,
+                       $messageMemc, $wgSitename;
+       
+               # Get query parameters
+               $feedFormat = $wgRequest->getVal( 'feed', 'atom' );
+               $filterTags = $wgRequest->getVal( 'tags', null );
+
+               # Process requested tags for use in keys
+               if ( $filterTags ) {
+                       $filterTags = explode( ',', $filterTags );
+                       array_walk( $filterTags, 'trim' );
+                       sort( $filterTags );
+               }
+
+               if ( !isset( $wgFeedClasses[$feedFormat] ) ) {
+                       wfHttpError( 500, "Internal Server Error", "Unsupported 
feed type." );
+                       return false;
+               }
+
+               # Setup cache-checking vars
+               $title = $article->getTitle();
+               $titleDBkey = $title->getPrefixedDBkey();
+               $tags = ( is_array( $filterTags ) ? ':' . implode( ',', 
$filterTags ):'' );
+               $key = 
"{$wgDBname}:wikiarticlefeedsextension:{$titleDBkey}:{$feedFormat}{$tags}";
+               $timekey = $key . ":timestamp";
+               $cachedFeed = false;
+               $feedLastmod = $messageMemc->get( $timekey );
+
+               # Dermine last modification time for either the article itself 
or an included template
+               $lastmod = $article->getTimestamp();
+               $templates = $article->getUsedTemplates();
+               foreach ( $templates as $tTitle ) {
+                       $tArticle = new Article( $tTitle );
+                       $tmod = $tArticle->getTimestamp();
+                       $lastmod = ( $lastmod > $tmod ? $lastmod:$tmod );
+               }
+
+               # Check for availability of existing cached **
+               if ( ( $wgFeedCacheTimeout > 0 ) && $feedLastmod ) {
+                       $feedLastmodTS = wfTimestamp( TS_UNIX, $feedLastmod );
+                       if ( time() - $feedLastmodTS < $wgFeedCacheTimeout
+                               || $feedLastmodTS > wfTimestamp( TS_UNIX, 
$lastmod ) 
+                               ) {
+                               wfDebug( "WikiArticleFeedsExtension: Loading 
feed from cache ($key; $feedLastmod; $lastmod)...\n" );
+                               $cachedFeed = $messageMemc->get( $key );
+                       } else {
+                               wfDebug( "WikiArticleFeedsExtension: Cached 
feed timestamp check failed ($feedLastmod; $lastmod)\n" );
+                       }
+               }
+
+               # Display cachedFeed, or generate one from scratch
+               global $wgWikiArticleFeedsSkipCache;
+               if ( !$wgWikiArticleFeedsSkipCache && is_string( $cachedFeed ) 
) {
+                       wfDebug( "WikiArticleFeedsExtension: Outputting cached 
feed\n" );
+                       $feed = new $wgFeedClasses[$feedFormat]( 
$title->getText(), '', $title->getFullURL(). " - Feed" );
+                       ob_start();
+                       $feed->httpHeaders();
+                       echo $cachedFeed;
+               } else {
+                       wfDebug( "WikiArticleFeedsExtension: Rendering new feed 
and caching it\n" );
+                       ob_start();
+                       WikiArticleFeeds::wfGenerateWikiFeed( $article, 
$feedFormat, $filterTags );
+                       $cachedFeed = ob_get_contents();
+                       ob_end_flush();
+
+                       $expire = 3600; # One hour
+                       $messageMemc->set( $key, $cachedFeed );
+                       $messageMemc->set( $timekey, wfTimestamp( TS_MW ), 
$expire );
+               }
+
+               # False to indicate that other action handlers should not 
process this page
+               return false;
+       }
+
+       /**
+       * Purges all associated feeds when an Article is purged.
+       *
+       * Usage: $wgHooks['ArticlePurge'][] = 'wfPurgeFeedsOnArticlePurge';
+       * @param Article $article The Article which is being purged.
+       * @return boolean Always true to permit additional hook processing.
+       */
+       static function wfPurgeFeedsOnArticlePurge( $article ) {
+               global $messageMemc, $wgDBname;
+               $titleDBKey = $article->mTitle->getPrefixedDBkey();
+               $keyPrefix = 
"{$wgDBname}:wikiarticlefeedsextension:{$titleDBKey}";
+               $messageMemc->delete( "{$keyPrefix}:atom:timestamp" );
+               $messageMemc->delete( "{$keyPrefix}:atom" );
+               $messageMemc->delete( "{$keyPrefix}:rss" );
+               $messageMemc->delete( "{$keyPrefix}:rss:timestamp" );
+               return true;
+       }
+
+       /**
+       * Converts an MediaWiki article into a feed, echoing generated content 
directly.
+       *
+       * @param Article $article Article to be converted to RSS or Atom feed.
+       * @param String $feedFormat A format type - must be either 'rss' or 
'atom'
+       * @param Array $filterTags Tags to use in filtering out items.
+       */
+       static function wfGenerateWikiFeed( $article, $feedFormat = 'atom', 
$filterTags = null ) {
+               global $wgOut, $wgServer, $wgFeedClasses, $wgVersion, 
$wgSitename;
+
+               # Setup, handle redirects
+               if ( $article->isRedirect() ) {
+                       $rtitle = Title::newFromRedirect( 
$article->getContent() );
+                       if ( $rtitle ) {
+                               $article = new Article( $rtitle );
+                       }
+               }
+               $title = $article->getTitle();
+               $feedUrl = $title->getFullUrl();
+
+               # Parse page into feed items.
+               $content = $wgOut->parse( $article->getContent() . 
"\n__NOEDITSECTION__ __NOTOC__" );
+               preg_match_all(
+                       
'/<!--\\s*FEED_START\\s*-->(.*?)<!--\\s*FEED_END\\s*-->/s',
+                       $content,
+                       $matches
+               );
+               $feedContentSections = $matches[1];
+
+               # Parse and process all feeds, collecting feed items
+               $items = array();
+               $feedDescription = '';
+               foreach ( $feedContentSections as $feedKey => $feedContent ) {
+
+                       # Determine Feed item depth (what header level defines 
a feed)
+                       preg_match_all( '/<h(\\d)>/m', $feedContent, $matches );
+                       if ( !isset( $matches[1] ) ) {
+                               next;
+                       }
+                       $lvl = $matches[1][0];
+                       foreach ( $matches[1] as $match ) {
+                               if ( $match < $lvl ) $lvl = $match;
+                       }
+
+                       $sectionRegExp = '#<h' . $lvl . 
'>\s*<span.+?id="(.*?)">\s*(.*?)\s*</span>\s*</h' . $lvl . '>#m';
+
+                       # Determine the item titles and default item links
+                       preg_match_all(
+                               $sectionRegExp,
+                               $feedContent,
+                               $matches
+                       );
+                       $itemLinks = $matches[1];
+                       $itemTitles = $matches[2];
+
+                       # Split content into segments
+                       $segments = preg_split( $sectionRegExp, $feedContent );
+                       $segDesc = trim( strip_tags( array_shift( $segments ) ) 
);
+                       if ( $segDesc ) {
+                               if ( !$feedDescription ) {
+                                       $feedDescription = $segDesc;
+                               } else {
+                                       $feedDescription = wfMsg( 
'wikiarticlefeeds_combined_description' );
+                               }
+                       }
+
+                       # Loop over parsed segments and add all items to item 
array
+                       foreach ( $segments as $key => $seg ) {
+
+                               # Filter by tag (if any are present)
+                               $skip = false;
+                               $tags = null;
+                               if ( is_array( $filterTags ) && !empty( 
$filterTags ) ) {
+                                       if ( preg_match_all( '/<!-- ITEM_TAGS 
([0-9a-zA-Z\\+\\/]+=*) -->/m', $seg, $matches ) ) {
+                                               $tags = array();
+                                               foreach ( $matches[1] as 
$encodedString ) {
+                                                       $t = @unserialize( 
@base64_decode( $encodedString ) );
+                                                       if ( $t ) {
+                                                               $t = explode( 
',', $t );
+                                                               array_walk( $t, 
'trim' );
+                                                               sort( $t );
+                                                               $tags = 
array_merge( $tags, $t );
+                                                       }
+                                               }
+                                               $tags = array_unique( $tags );
+                                               if ( !count( array_intersect( 
$tags, $filterTags ) ) ) {
+                                                       $skip = true;
+                                               }
+                                               $seg = preg_replace( '/<!-- 
ITEM_TAGS ([0-9a-zA-Z\\+\\/]+=*) -->/m', '', $seg );
+                                       } else {
+                                               $skip = true;
+                                       }
+                               }
+                               if ( $skip ) continue;
+
+                               # Try hard to determine the item author and date
+                               # Look for a regular signatures of the layout
+                               # userpage-link [optional user_talk page link] 
date (with a delimiting timezone code in parentheses)
+
+                               $author = null;
+                               $date = null;
+
+                               $signatureRegExp = '#<a href=".+?User:.+?" 
title="User:.+?">(.*?)</a> (\d\d):(\d\d), (\d+) ([a-z]+) (\d{4}) 
(\([A-Z]+\))#im';
+
+                               $signatureRegExp1 = 
'#<.*userpage-link.*>(.*?)</a>.*<.*usertalkpage-link.*>.*</a>\) 
(.*\([A-Z]+\))#im';
+                               $signatureRegExp2 = 
'#<.*userpage-link.*>(.*?)</a> (.*\([A-Z]+\))#im';
+                               $signatureRegExp3 = 
'#<.*usertalkpage-link.*>(.*?)</a> (.*\([A-Z]+\))#im';
+                       
+                               $isAttributable = ( preg_match( 
$signatureRegExp1, $seg, $matches )
+                                       || preg_match( $signatureRegExp2, $seg, 
$matches )
+                                       || preg_match( $signatureRegExp3, $seg, 
$matches ) );
+
+                               if ( $isAttributable ) {
+
+                                       list( $author, $timestring ) = 
array_slice( $matches, 1 );
+                       
+                                       $tempTimezone = 
date_default_timezone_get();
+                                       date_default_timezone_set( 'UTC' );
+                                       $date = date( "YmdHis" , strtotime( 
$timestring ) );
+                                       date_default_timezone_set( 
$tempTimezone );
+
+                               }
+
+                               # Set default 'article section' feed-link
+                               $url = $feedUrl . '#' . $itemLinks[$key];
+
+                               # Look for an alternative to the default link 
(unless default 'section linking' has been forced)
+                               global $wgForceArticleFeedSectionLinks;
+                               if ( !$wgForceArticleFeedSectionLinks ) {
+                                       $strippedSeg = 
preg_replace($signatureRegExp, '', $seg );
+                                       preg_match(
+                                               '#<a 
[^>]*href=([\'"])(.*?)\\1[^>]*>(.*?)</a>#m',
+                                               $strippedSeg,
+                                               $matches
+                                       );
+                                       if ( isset( $matches[2] ) ) {
+                                               $url = $matches[2];
+                                               if ( preg_match( '#^/#', $url ) 
) {
+                                                       $url = $wgServer . $url;
+                                               }
+                                       }
+                               }
+
+                               # Create 'absolutified' segment - where all 
URLs are fully qualified
+                               $seg = preg_replace( '/ 
(href|src)=([\'"])\\//', ' $1=$2' . $wgServer . '/', $seg );
+
+                               # Create item and push onto item list
+                               $items[$date][] = new FeedItem( strip_tags( 
$itemTitles[$key] ), $seg, $url, $date, $author );
+                       }
+               }
+
+               # Programmatically determine the feed title and id.
+               $feedTitle = $title->getPrefixedText() . " - Feed";
+               $feedId = $title->getFullUrl();
+
+               # Create feed
+               $feed = new $wgFeedClasses[$feedFormat]( $feedTitle, 
$feedDescription, $feedId );
+
+               # Push feed header
+               $tempWgVersion = $wgVersion;
+               $wgVersion .= ' via WikiArticleFeeds ' . VERSION;
+               $feed->outHeader();
+               $wgVersion = $tempWgVersion;
+
+               # Sort all items by date and push onto feed
+               krsort( $items );
+               foreach ( $items as $itemGroup ) {
+                       foreach ( $itemGroup as $item ) {
+                               $feed->outItem( $item );
+                       }
+               }
+
+               # Feed footer
+               $feed->outFooter();
+       }
+
+} /* class WikiArticleFeeds */


Property changes on: trunk/extensions/WikiArticleFeeds/WikiArticleFeeds_body.php
___________________________________________________________________
Added: svn:eol-style
   + native


_______________________________________________
MediaWiki-CVS mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs

Reply via email to