Embeddable RSS feed for Plone

For some time now I have wanted to make more effective use of RSS feed on our website. The SSDS website is part of the University of Leicesters Plone CMS. Until now we have only been able to pull RSS feeds into the CMS using a Feed Mixer Portlet, which although is suitable only presents feeds in a small portlet on the edge of the webpage.

What I would really like is to have RSS feeds as the main body item on a page. The solution of an embeddable widget seemed the most sensible solution. Having Googled this most solutions are sponsored by adverts, which would look very professional on an educational site. So I set my intern Muhammed Khalifa, who has a programming background the challenge. Initially his solution required a PHP server, which unfortunately we do not have at the university. His next solution was to script it in Python, this solution worked nicely.

Here are two example of the solution:

To embed this we use an iframe, with this embed code:

<iframe style="border:0; width: 100%; height:600px;  overflow-x:hidden; overflow-y:auto;"  src="http://www.le.ac.uk/ssds/rssfeeds/?rss=http://feeds.delicious.com/v2/rss/uolssds/careers?count=15"> Frames are not supported  by your Browser. </iframe>

To personalise this:

Width and height can be adjusted: by changing ‘width: 100%; height:600px;

For hosting and the RSS feed:

src="http://www.le.ac.uk/ssds/rssfeeds/?rss=http://feeds.delicious.com/v2/rss/uolssds/careers?count=15"
  • http://www.le.ac.uk/ssds/rssfeeds/?‘ is where we are hosting our python code
  • http://feeds.delicious.com/v2/rss/uolssds/careers?count=15‘ is the source of your feed, change the number ‘15‘ to the number of results you want to display.

Technical explaination

Here is the code the python code:

from mod_python import apache
import re

def remove_html_tags(data):
 p = re.compile(r'</?p>')
 return p.sub('', data)

def handler(req):

 import feedparser
 from urlparse import urlparse
 from mod_python import util

 # Max number of items. Default = 1000
 itemsLimit = 1000;

 req.content_type = "text/html"

 # parse GET params
 form = util.FieldStorage(req, keep_blank_values=1)
 feedurl = form.getfirst("rss")
 lim = form.getfirst("limit");

 if (feedurl == None or feedurl == ""):
 feedurl = "http://feeds.delicious.com/v2/rss/mjmobbs?count=10"

 if (lim == None):
 lim = itemsLimit

 lim = int(lim) -1

 if (lim < 0 or lim > itemsLimit):
  lim = itemsLimit

 # parse RSS link data
 d = feedparser.parse(feedurl)
 if (len(d.entries) == 0): req.write('No RSS Feeds available.')
 feed = "<ul>"

 aClass = "even"

 for i in range(0, len(d.entries)):

  feed += "<li> <a class='" + ("odd" if (i%2) else "even") + "' target='_parent' href='" + d.entries[i].link  + "' >"
  feed += "<div class='title' > " + d.entries[i].title + " </div>"
  if d.entries[i].has_key('description'):
   feed += "<div class='description' > " + remove_html_tags(d.entries[i].description)  + " </div>"

 feed += "</a> </li>\n"

 if (i >= lim): break;

 feed += "</ul>"

 s = """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
 <style>
 * {
 font-family: Verdana, arial,tahoma;
 font-size: 9pt;
 padding:0;
 margin:0;
 }

 #navcontainer { width: 100%%; }

 #navcontainer ul
 {
 padding: 5px;
 list-style-type: none;
 font-family: Arial, Helvetica, sans-serif;
 }

 #navcontainer a
 {

 display: block;
 padding: 5px;
 width: 100%%;
 border-bottom: 1px dotted #cccccc;
 }

 #navcontainer a.odd
 {
 background-color: #f6f6f6;
 }

 #navcontainer a.even
 {
 background-color: #ffffff;
 }

 #navcontainer a:link, #navlist a:visited
 {
 color: #333333;
 text-decoration: none;
 }

 #navcontainer a:hover
 {
 background-color: #369;
 color: #ffffff;
 }

 .title { font-size: 1.4em; width:100%%;}
 .description { font-size: 1em; color:gray;}
 </style>

 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 <title>Delicious Links</title>
 </head>
 <div id="navcontainer">

 %s

 </div>
 <body>
 </body>
 </html>
 """ % (feed)

 req.write(s.encode('utf-8'))

 return apache.OK

To interrupt this code:

1. Tell the server to use python and import regular expressions

from mod_python import apache
import re

2. Remove all the HTML from the income feed and convert each feed item to an integer.

def remove_html_tags(data):
 p = re.compile(r'</?p>')
 return p.sub('', data)

3. Define the source of the RSS feed and how to display

def handler(req):

a) Use a feedparser. This is a standard feed define used for any RSS feed. It can be downloaded from http://www.feedparser.org/. The individual part of the RSS feed are then separated out of feedparser.

 import feedparser
 from urlparse import urlparse
 from mod_python import util

b) Define the limit of feed results

 # Max number of items. Default = 1000
 itemsLimit = 1000;

c) Request only text or HTML content

 req.content_type = "text/html"

d) Tell the server where to GET the feed data from. In python there are two methods of collecting data GET or POST. With GET the source can be written in a short string like a URL. POST is used for large amounts of data. In this code with also defined our variable ‘feedurl‘ and also allow the user to set the own limit (‘lim’) on the number of results shown.

 # parse GET params
 form = util.FieldStorage(req, keep_blank_values=1)
 feedurl = form.getfirst("rss")
 lim = form.getfirst("limit");

e) Run error checks. In this case if no feed is present it displays a default feed from my delicious account. This code also test the limits set.

if (feedurl == None or feedurl == ""):
 feedurl = "http://feeds.delicious.com/v2/rss/mjmobbs?count=10"

 if (lim == None):
 lim = itemsLimit

 lim = int(lim) -1

 if (lim < 0 or lim > itemsLimit):
 lim = itemsLimit

f) define the feed results as ‘d’, but is no results are found display “No RSS Feed”

 # parse RSS link data
 d = feedparser.parse(feedurl)
 if (len(d.entries) == 0): req.write('No RSS Feeds available.')
 feed = "<ul>"

4. Convert the python result to HTML. The code is a list <ul> and each feed is a list-item <li>. Here we are displaying in two classes ‘odd’ and ‘even’. The class is calculated used ‘i%2’ mean divide the integer by two and add the remainder.  It then creates a hyper link <a> and displays the Title of the feed and Description.

feed = "<ul>"

aClass = "even"

for i in range(0, len(d.entries)):
feed += "<li> <a class='" + ("odd" if (i%2) else "even") + "' target='_parent' href='" + d.entries[i].link  + "' >"
feed += "<div class='title' > " + d.entries[i].title + " </div>"
if d.entries[i].has_key('description'):
feed += "<div class='description' > " + remove_html_tags(d.entries[i].description)  + " </div>"
feed += "</a> </li>\n"

5. Repeat until the limit is hit

 if (i >= lim): break;

 feed += "</ul>"

6. Finally write the style sheet for HTML and import the python result at ‘s

s = """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
 <style>
 * {
 font-family: Verdana, arial,tahoma;
 font-size: 9pt;
 padding:0;
 margin:0;
 }

 #navcontainer { width: 100%%; }

 #navcontainer ul
 {
 padding: 5px;
 list-style-type: none;
 font-family: Arial, Helvetica, sans-serif;
 }

 #navcontainer a
 {

 display: block;
 padding: 5px;
 width: 100%%;
 border-bottom: 1px dotted #cccccc;
 }

 #navcontainer a.odd
 {
 background-color: #f6f6f6;
 }

 #navcontainer a.even
 {
 background-color: #ffffff;
 }

 #navcontainer a:link, #navlist a:visited
 {
 color: #333333;
 text-decoration: none;
 }

 #navcontainer a:hover
 {
 background-color: #369;
 color: #ffffff;
 }

 .title { font-size: 1.4em; width:100%%;}
 .description { font-size: 1em; color:gray;}
 </style>

 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 <title>Delicious Links</title>
 </head>
 <div id="navcontainer">

 %s

 </div>
 <body>
 </body>
 </html>
 """ % (feed)

 req.write(s.encode('utf-8'))

 return apache.OK

Lessons learnt about python

Python works as a series of routines, any subroutines are defined by a tab indent. Also python uses % as code, some if ‘%’ is required in html %% needs to be used.

4 Comments


  1. Nice one! Well done you and Mohammed.

    Reply

    1. Cheers Brendan, Next we are trying to develop a form which you can enter your feed URL and the number of results you want and it will give you the embed code

      Reply

  2. Matt,

    Can you expand on the idea of the form as I'm not sure what it would add. Is it not just a case of educating people to change the src="XXX" bit within the iframe e.g. the following works:
    http://www.le.ac.uk/ssds/rssfeeds/?rss=http://new

    I suppose a form that allows further customisation of the HTML style sheet would be good – but I would have thought that this would be a case of adding further parameters to the src URL – wouldn't it?

    Reply

  3. That's genius! Well done both of you. So are we going to use the Jobsonline one for the vacancies? Do you know if they allow us to tag certain vacancies so we can pull out just, say, WRL placements?

    Reply

Leave a Reply