Extension:RSS Reader

From mediawiki.org
MediaWiki extensions manual
RSS Reader
Release status: unmaintained
Implementation Tag
Description Provides the <rss> tag to display one or more rss feeds on a page
Author(s) Artem Kaznatcheev (DFRussiatalk)
Latest version 0.2.6 (2012-12-29)
MediaWiki various
Database changes No
License GNU General Public License 2.0 or later
Download from page
  • $egCache
  • $egCacheDir
  • $egCacheTime
  • $egCacheTimeMin
  • $egCacheTimeMax
  • $egNoFollow
  • $egWidthMin
  • $egWidthMax
‎<rss>
  • makesysop
  • steward

This RSS Reader extension tries to provide a tool for including RSS feeds inside various articles across your wiki. It is best used on specialty pages made so that users can easily track the news, etc. This extension was made because all the other RSS extensions did not work or performed poorly on MediaWiki 1.10.

Usage[edit]

This extension is a parser extension which can be used by any wiki user. The function adds an ‎<rss> tag to use inside articles. Some modification of its behaviour is possible from LocalSettings.php as well.

LocalSettings.php[edit]

All the global variables used by the extension and modifiable by the user are listed here. If you modify these variables do so after the require_once statement:

$egCache
The boolean value that determines if caching should be done (true) or not (false); enabled by default, although distributions possibly need to disable this by default or change egCacheDir.
$egCacheDir
The path where parsed feeds are cached if egCache is enabled; it must obviously be writable to the www-data user.
$egCacheTime
The default amount of time (in seconds) between cachings of a feed. By default it is set to 3600 (1 hour). If a user does not specify a time argument or their argument is somehow corrupt, this value will be used.
$egCacheTimeMin
The minimum amount of time (in seconds) between cachings of a feed. By default it is set to 1800 (30 minutes). This is the most often a user can ask a feed to be refetched. Settings this time too low on busy servers might eat up a lot of server resources
$egCacheTimeMax
The maximum amount of time (in seconds) between cachings of a feed. By default it is set to 7200 (2 hours). This is the least often a user can ask a feed to be refetched.
$egNoFollow
Determines if all links created by the extension should have the rel="nofollow" attribute. Default value is false, if set to true then the nofollow attribute will be added. Keep in mind, that some popular search engines (Yahoo! and Ask, for instance) will still follow, index and rank the links.
$egWidthMin
The minimum size (in pixels) of the width to which the size of a floating RSSbar can be set.
$egWidthMax
The maximum size (in pixels) of the width to which the size of a floating RSSbar can be set.

The <rss> tag[edit]

The custom tag is called <rss>. For instance, if you put <rss>http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/world/rss.xml</rss> then you will create a feed to BBC World news.

multiple feeds
You can add more than one feed in a single <rss> tag to have them display in columns. Do so by adding "|" between the feed URLs. Thus, <rss>firsturl|secondurl|thirdurl</rss> will create a reader with 3 columns, displaying items from all 3 feeds. Inside the <rss> tag you can include optional settings, which are detailed below:
number
By default, the reader will display all the items in the feed. If you want to display up to a maximum of a certain number of items, then add number=n (where n is the max number) inside the rss tag:
<rss number=10>http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/world/rss.xml</rss>
will display the last 10 stories from BBC World.
time
By default, the reader will take the value of $egCacheTime as the amount of time (in seconds) between each local caching of the RSS feed. If you want a certain feed to refresh its cache more or less frequently, then add time=t (where t is the refresh time in seconds) inside the rss tag:
<rss time=7200>http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/world/rss.xml</rss>
will refresh the cache of stories from BBC World every 7200 seconds (2 hours).
The max and min time allowed for the time argument is governed by $egCacheTimeMin and $egCacheTimeMax in LocalSettings.php. If the specified time falls outside this range, then it will revert to the default $egCacheTime value.
desc=off
Hides the description provided by the feed.
width
If set to a legal value (between $egWidthMin and $egWidthMax) it turns the RSS section into a floating div, aligned to the right, of the width specified (in pixels)
title=off
Hides the title (and description in desc=off) of the feed.
text
By default, the reader will display the items as a bullet list of title links, but often the desired format is for each item to contain the description text. Add the "text" parameter to render the items in a div tag of class "rss", for example:
<rss text>http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/world/rss.xml</rss>
See OrganicDesign:Home for an example of this format, the CSS rules used in this example are shown in the CSS section below.

Installing[edit]

Pre-Requisites[edit]

Ensure you have php_curl installed and configured.

Download the copy of the win32 PHP (not the installer one) from php.net (whichever PHP version that you want running on your webserver). Then from dll folder, copy these two files: libeay32.dll and ssleay32.dll to your Windows/System32 (whichever Windows you have).

Then from the extensions folder: get your php_curl.dll and place it under your PHP/extensions folder.

Change your php.ini where it specifies the extension folder for example: extension_dir = "./extensions/"

And uncomment your extension to php_curl.dll for example: extension=php_curl.dll

Install[edit]

  1. Create a folder called RSSReader in your extensions directory
  2. Download the lastRSScode and save it in your newly created folder as lastRSS.php
  3. Patch lastRSS.php with this patch
  4. Create a file called RSSReader.php in your new folder
  5. Copy and paste the RSSReader.php code into your new file
  6. Create a file called RSSReader.css in your new folder
  7. Copy and paste the RSSReader.css code into your new file
  8. Create a folder called cache in your RSSReader folder and set write permission on it
  9. In LocalSettings.php, add the following line:
    require_once( "$IP/extensions/RSSReader/RSSReader.php");
    
  10. Modify RSSReader.css to your liking to better fit your skin

If you want to install RSS Reader to use database-centric caching instead of file-centric caching, refer to alternative RSS parser below.

lastRSS_patch[edit]

--- a/dist/mediawiki-extensions-base/usr/share/mediawiki-extensions/base/RSS_Reader/lastRSS.php
+++ b/dist/mediawiki-extensions-base/usr/share/mediawiki-extensions/base/RSS_Reader/lastRSS.php
@@ -149,14 +149,14 @@ class lastRSS {
 				{ $this->rsscp = $this->default_cp; } // This is used in my_preg_match()
 
 			// Parse CHANNEL info
-			preg_match("'<channel.*?>(.*?)</channel>'si", $rss_content, $out_channel);
+			preg_match("'<channel.*?>(.*)</channel>'si", $rss_content, $out_channel);
 			foreach($this->channeltags as $channeltag)
 			{
 				$temp = $this->my_preg_match("'<$channeltag.*?>(.*?)</$channeltag>'si", $out_channel[1]);
 				if ($temp != '') $result[$channeltag] = $temp; // Set only if not empty
 			}
 			// If date_format is specified and lastBuildDate is valid
-			if ($this->date_format != '' && ($timestamp = strtotime($result['lastBuildDate'])) !==-1) {
+			if ($this->date_format != '' && isset($result['lastBuildDate']) && ($timestamp = strtotime($result['lastBuildDate'])) !==-1) {
 						// convert lastBuildDate to specified date format
 						$result['lastBuildDate'] = date($this->date_format, $timestamp);
 			}
@@ -198,7 +198,7 @@ class lastRSS {
 					if ($this->stripHTML && $result['items'][$i]['title'])
 						$result['items'][$i]['title'] = strip_tags($this->unhtmlentities(strip_tags($result['items'][$i]['title'])));
 					// If date_format is specified and pubDate is valid
-					if ($this->date_format != '' && ($timestamp = strtotime($result['items'][$i]['pubDate'])) !==-1) {
+					if ($this->date_format != '' && isset($result['items'][$i]['pubDate']) && ($timestamp = strtotime($result['items'][$i]['pubDate'])) !== -1) {
 						// convert pubDate to specified date format
 						$result['items'][$i]['pubDate'] = date($this->date_format, $timestamp);
 					}

Code[edit]

RSSReader.php[edit]

Create a folder called RSSReader. Inside this folder create the file: RSSReader.php

<?php
/* RSSReader 0.2.6 - a parser hook for MediaWiki
 * Copyright © 2008  Artem Kaznatcheev
 * Copyright © 2012  Thorsten Glaser
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

# Not a valid entry point, skip unless MEDIAWIKI is defined
if ( !defined('MEDIAWIKI') ) {
	exit( 1 );
}

$wgExtensionFunctions[] = 'efRSSReader';
$wgRSSReaderExtVersion = '0.2.6';

$wgExtensionCredits['parserhook'][] = array(
        'path' => __FILE__,
	'name' => 'RSS Reader',
	'version' => $wgRSSReaderExtVersion,
	'author' => 'Artem Kaznatcheev',
	'description' => 'Provides the <code>&lt;rss&gt;</code> tag that allows to display one or more RSS feeds on a page',
	'url' => 'https://www.mediawiki.org/wiki/Extension:RSS_Reader'
);

### Global Variables ###
//path to follow for server scripts
$egRSSReaderPath  = $wgScriptPath."/extensions/RSSReader";
$egCacheTime      = 3600; //default cache time in seconds
$egCacheTimeMin   = 1800; //minimum cache time in seconds
$egCacheTimeMax   = 7200; //maximum cache time in seconds
$egCache          = true; //boolean to determine if caching should be done
$egCacheDir       = dirname( __FILE__ ).'/cache/'; //directory of cache
//boolean to determine if links created should have rel="nofollow"
$egNoFollow       = false;
$egWidthMin       = 200;  //minimim width in pixels
$egWidthMax       = 800;  //maximum width in pixels

/**
 * select if cURLRSS or wikiRSS or lastRSS should be loaded
 * set rssType to the proper object type
 * each object of rssType must have the same interface
 */
if (file_exists(dirname(__FILE__)."/cURLRSS.php")){
  require_once(dirname(__FILE__)."/cURLRSS.php"); //loads cURLRSS.php
  $rssType = new cURLRSS; //set rssType to cURLRSS
} else if (file_exists(dirname( __FILE__ )."/wikiRSS.php")) {
  require_once(dirname( __FILE__ )."/wikiRSS.php"); //loads wikiRSS.php
  $rssType = new wikiRSS; //set rssType to wikiRSS
} else if (file_exists(dirname( __FILE__ )."/lastRSS.php")) {
  require_once(dirname( __FILE__ )."/lastRSS.php"); //loads lastRSS.php
  $rssType = new lastRSS; //set rssType to lastRSS
} else {
  trigger_error("RSSReader is not properly set-up:".
    "need cURLRSS, wikiRSS or lastRSS");
}

function efRSSReader() {
  global $wgParser;
  $wgParser->setHook( 'rss', 'efCreateRSSReader' );
}

function efCreateRSSReader($input, $argv, $parser){
  global $wgOut, $egRSSReaderPath, $egCacheTime, $egCacheTimeMin,
    $egCacheDir, $wgRSSReaderExtVersion,
    $egCacheTimeMax, $egCache, $rssType, $egNoFollow, $egWidthMin,
    $egWidthMax;

  // initialise args incase PHP set to strict
  foreach (array('number', 'width', 'title', 'time', 'desc') as $k) if (!isset($argv[$k])) $argv[$k] = false;

   //disable cache so feed is fetched everytime page is visited
  $parser->disableCache();
  if (!$input){ //if no input do nothing
  } else {
    $fields = explode("|",$input); //get the URLs between the tags

    /*
     * Check if a "number=n" argument has been provided
     * if it has and is an int, then set the $number field to the proper
     * value else set $number field to zero (which means "all available")
     */
    if (!$argv["number"]){ //check if argument has been provided
      $number=0; //set default if no argument has been provided
    } else {
      //check if argument is an integer
      if ((int)$argv["number"]."" == $argv["number"]) {
        $number = $argv["number"]; //set $number field
      } else {
        $number=0; //if not an integer, then set default
      }
    }

    /*
     * Check if a "width=n" argument has been provided
     * if it has and is an int, then set the $width field to the proper
     * value else set $width field to zero (which means "no width")
     */
    if (!$argv["width"]){ //check if argument has been provided
      $width=0; //set default if no argument has been provided
    } else {
      //check if argument is an integer
      if ((int)$argv["width"]."" == $argv["width"]) {
        if (($argv["width"]>=$egWidthMin)&&($argv["width"]<=$egWidthMax)) {
          $width = $argv["width"]; //set $width field
        } else $width = 0; //if out of range, then set default
      } else {
        $width=0; //if not an integer, then set default
      }
    }

    /*
     * Check if a "time=n" argument has been provided
     * if it has and is between $egCacheTimeMin and $egCacheTimeMax
     * then set $cacheTime to the value
     * else set $cacheTime to $egCacheTime (the default CacheTime)
     */
    if (!$argv["time"]){ //check if argument has been provided
      //set default if no argument has been provided
      $cacheTime = $egCacheTime;
    } else {
      //check if argument is an integer
      if ((int)$argv["time"]."" == $argv["time"]) {
        //check if argument is in range
        if (($argv["time"]>=$egCacheTimeMin)
          &&($argv["time"]<=$egCacheTimeMax)) {
          $cacheTime = $argv["time"]; //set $cacheTime field
        } else {
          //set default if argument is outside range
          $cacheTime = $egCacheTime;
        }
      } else {
        $cacheTime=$egCacheTime; //set default if not an integer
      }
    }

    /* Check if a "desc=off" parameter has been provided and set desc */
    $desc = true; //set the default
    if ($argv["desc"]){ //check if argument has been provided
      if ($argv["desc"]=="off") $desc = false;
    }

    /* Check if "title=off" parameter was provided and set dispTitle */
    $dispTitle = true; //set the default
    if ($argv["title"]){ //check if argument has been provided
      if ($argv["title"]=="off") $dispTitle = false;
    }

    $wgOut->addLink(array(
	'rel' => 'stylesheet',
	'type' => 'text/css',
	'href' => "$egRSSReaderPath/RSSReader.css?$wgRSSReaderExtVersion",
    ));

    if (!$width) {
      $output = '
        <table class="RSSMainBody" style="background:inherit;">
        <tr>
      ';
    } else {
      $output = '
        <table class="RSSMainBody" style="background:inherit; float:right; width:'.$width.'">
        <tr>
      ';
    }

    //calculates the desired width for each feed and makes sure it is int
    $width = intval(100/sizeof($fields) - 5);

    // Create cURLRSS or wikiRSS or lastRSS object
    $rss = new $rssType; //initialize an object of rssType
    // Set public variables
    if (($rssType instanceof lastRSS) && $egCache) {
      $rss->cache_dir = $egCacheDir;
    }
    $rss->cache = $egCache; //cache attribute
    $rss->cache_time = $cacheTime; //refresh time in seconds
    $rss->date_format = 'l';

    foreach ($fields as $field) {
      //table cell that contains a single RSS feed
      $output .= '<td valign="top" style="width: '.$width.'%;">';
      if (($rssArray = $rss->get($field)) && (isset($rssArray['link']) || isset($rssArray['title']) || isset($rssArray['description']))) {
        if ($dispTitle) { //check if title should be displayed
          $output .=
            '<div class="RSSReader-head">'.
              '<h3><a href="'.
              $rssArray['link'].
              '"';
          //decide if nofollow is needed
          if ($egNoFollow) $output .= 'rel="nofollow"';
          $output .= '>'.
            $rssArray['title'].
            '</a></h3>';
          //decide if description is required
          if ($desc) $output .= $rssArray['description'];
          $output .= '</div>';
        }

        /* Outputs the items */
        $text = isset($argv["text"]);
        if (!$text) $output .= "<ul class='rss'>";
        $i = 0; //counter for number of items already displayed
        foreach ($rssArray['items'] as $item){
          $output .= $text ? "<div class='rss'><h3>" : "<li>";
          $output .= '<a href="'.$item['link'].'" ';
          //decide if nofollow is needed
          if ($egNoFollow) $output .= 'rel="nofollow"';
          $item_title=preg_replace("|\[rsslist:.+?\]|", "",
            htmlspecialchars(html_entity_decode(html_entity_decode($item['title'],
            ENT_QUOTES, "UTF-8"), ENT_QUOTES, "UTF-8"), ENT_QUOTES, "UTF-8"));
          $output .= '>'.$item_title.'</a>';
          if ($text) {
            $desc = preg_replace("|\[rsslist:.+?\]|", "",
              Sanitizer::removeHTMLtags(html_entity_decode($item['description'],
              ENT_QUOTES, "UTF-8"), null, array(),
              array('a', /* does not work */ 'img')));
            $output .= "</h3>\n$desc</div>\n";
          } else $output .= "</li>\n";
          /*if reached the number of desired display items stop working on
           *displaying more items*/
          if (++$i == $number) break;
          } //close foreach items
        if (!$text) $output .= "</ul>\n";
      } else { //output error if not possible to fetch RSS
        $output .= "Error: It's not possible to get $field...";
      }
      $output .= '</td>';
    } //close foreach fields
    $output .= "</tr></table>";
  } //close main "else"
  return $output;
}

RSSReader.css[edit]

/*
 * This file contains all the skinning rules for the RSSReader extension
 * if you want to change the look of the RSS feed to better suit your skin
 * then this is where you should make the changes. By default the reader
 * is coded to look good with monobook.
 * Alternatively to modifying the code in here, you could copy and paste it
 * into your wiki's [[MediaWiki:Common.css]] page and that way you can have 
 * different looks for different skins 
 */
 
.RSSReader-head {
  text-align: center;
  padding: 0.2em;
  border-bottom: 0.1em solid #aaa;
}

/*
 * Example CSS rules for use with the "text" parameter
 */
div.rss {
    margin: 0 0 20px 0;
    padding: 5px;
    border: 1px solid #D0D6F0;
}
div.rss h3 {
    background: #D0D6F0;
    padding: 2px 5px;
    margin: 0;
}
#content .rss h3 a { color: #4E507F }

Alternative RSS Parser[edit]

If you do not want to (or can't) use files for caching, then an alternative to lastRSS exists. The alternative, wikiRSS, is not recommended since benchmarks have shown it to be up to three times slower (not a super big deal since both are fast, but still a concern).

If you server does not allow_url_fopen, then you cannot run lastRSS. An alternative exists, cURLRSS, this uses the cURL library (which you need to be configured already) instead of allow_url_fopen. This is the perfect solution for DreamHost users.

Possible Issues[edit]

The error checking in this extension has improved substantially since its first version, but the code is still far from foolproof. Therefore, it is possible to run into a number of issues or errors that you would have to fix yourself or report in the discussion section of this article. Some issues that you might run into are listed below.

Different Directory[edit]

If you do not install this code in $IP/extensions/RSSReader/ then there could be some issues. If the RSSReader.css file is not placed in $IP/extensions/RSSReader/ then you should add a new remote path name to LocalSettings.php. For reference, the default value is:

$egRSSReaderPath  = $wgScriptPath."/extensions/RSSReader";

Do not forget the $wgScriptPath at the start of the path.

RSSReader.php and lastRSS.php have fewer restrictions on them than RSSReader.css. The only thing you need to ensure is that the two files are in the same directory, and RSSReader.php is properly referenced in LocalSettings.php.

PHP support for allow_url_fopen[edit]

Both lastRSS and wikiRSS require that your version of PHP include support for allow_url_fopen. You'll get an error like

Error: It's not possible to get http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/world/rss.xml

if your PHP is missing this option. (Run php -i to get information about your PHP binary.) Some managed hosting services have this option turned off by default, for security reasons.

Warnings[edit]

  • This extension turns off parser caching for all the pages on which the <rss> tag is present. This could result in server strain if rss tags are present on many high traffic pages.

Acknowledgement[edit]

  • lastRSS.php was not written by the author of this extension. lastRSS.php was written by Vojtech Semecky and licensed under GPL. If you want more information about lastRSS.php check its site

Changes[edit]

0.2.6[edit]

  • Update from Debian src:mediawiki-extensions 2.11 upload
  • Fixes a remotely exploitable user security hole wrt. HTML in feed titles and bodies (CVE-2012-6453)
  • Add $egCacheDir parameter, to allow people to point it to a writable directory to avoid having to disable $egCache when ./ is not writable to www-data (in Debian, we disable egCache by default; here on the Wiki page, it is enabled by default in the source code)
  • Fix installation path
  • Fix rendering path to CSS
  • Fix id="RSSMainBody" being used multiple times by making it into a CSS class that can be used for styling RSS output instead
  • Fix XHTML/1.0 Transitional validity of output
  • Fix a bunch of PHP warnings
  • Stop using $rss->unhtmlentities and sanitize RSS bodies correctly

0.2.5[edit]

  • Added a parameter called "text" which allows rendering of items in a div element of class "rss" which contains the item's description text (added by User:Nad).

0.2.4[edit]

  • Allowed user to hide title area
    • added new "title" attribute inside tag
  • Modified code formatting a little
  • Added recognition of cURLRSS

0.2.3[edit]

  • Gave more flexible control of placement and size of the RSS feed
    • added new "width" attribute inside tag
    • added new global variables $egWidthMin and $egWidthMax to govern the allowed values for width
  • Improved the data in $wgExtensionCredits

0.2.2[edit]

  • Added $egNoFollow global variable to determine if rel="nofollow" should be added to all links
  • Added a "desc=off" option when placing RSSReader on page

0.2.1[edit]

  • Added recognition if wikiRSS or lastRSS is present
  • Added dynamic object loading (wikiRSS or lastRSS)
  • Added $egCache global variable to determine if caching should be done
  • Moved object initialization outside of fields for loop
  • Fixed an error in the error output to show the url
  • Improvement to comments and white space placing

0.2[edit]

  • Added license
  • Decoupled HTML and CSS (creating a separate file for the CSS)
  • Gave more flexible control over time between cache
    • added new "time" attribute inside tag
  • Improved commenting in code
  • Improved error checking

Future Improvements[edit]

  • Expand on error checking
  • Provide more options for the user both inside the article and from LocalSettings.php
  • Provide more skinning options with CSS
  • Allow users to view an entry summary along with title
  • Add some fancy looking JavaScript
  • Allow parser functions along with tags
  • Allow users to pass a template to format the output

See other[edit]

Alternate extensions: