Newer posts are loading.
You are at the newest post.
Click here to check if anything new just came in.

July 26 2013

13:05

10 Best Alternatives To Google Reader

If you are worry about the replacement for the Google Reader then you do not have to worry anymore because this collection is all about alternatives to Google Reader. No doubt Google Reader is one of the most important and useful tools from Google apps. So what if Google Reader is closing or shutting down, these days lots of good and outstanding replacements for the Google Reader are available in the market like the Old Reader, News Blur, Feedly, net Vibes and Pulse and so many.

In this compilation we are showcasing 10 free and best Alternatives to Google Reader. With these quality replacements of Google Reader, you do not have to worry if Google Reader is closing or not because you have good alternatives in hand.

You can also tell us what you think about this collection and share your precious opinions with us via comment section below. Do have a look at this collection and chime in with recommendations as well. Enjoy!!!

Feedly

Organize your favorite blogs, news sites, podcasts and Youtube channels and access them all in one place (or sync with Google Reader).

RSS Owl

RSSOwl is a free and powerful news feed reader. RSSOwl lets you gather, organize and search news in a convenient, easy to use interface with endless flexibility.

GreatNews

GreatNews displays full pages of news articles across rss feeds, optimized for fast reading. So you can skim through pages in seconds, and pick interesting ones to dig in.

FeedReader

You just sign up, add your favorite feeds, and arrange them into a personal list that you can then access at any moment from any other computer simply by logging into your account in a web browser.

FeedDemon

FeedDemon is the most popular RSS reader for Windows, with an easy-to-use interface that makes it a snap to stay informed with the latest news and information.

Taptu

Taptu provides instant access to all your interests in one beautiful little app.

Pulse

Pulse is a fast and beautiful way to read your favorite blogs, magazines, social networks and newspapers.

Newsblur

Bringing people together to talk about the world.

Bloglines

Bloglines is the fastest way to find and track your favorite websites and blogs in real-time. Easily customize your dashboard with multiple view options, drag and drop organization, and exclusive widgets. Get the latest news on all your interests and trending topics exactly the way you want it, with the new Bloglines Reader!

Omea Reader

Free and easy to use RSS reader, NNTP news reader, and web bookmark manager. It’s fast, it aggregates, and it keeps you organized. Check out the additional features: lightning-fast desktop search, flexible information organizer, contextual access, and quick extensibility for developers who want more.

June 24 2013

07:00

How I Increased My Subscriber Rate by 10x Overnight

A few weeks ago I explained how I migrated my Feedburner email subscribers to a newsletter service. Since then I’ve been busy experimenting and optimising my new mailing list to continue growing my sites into the future. After implementing a couple of popular techniques I saw my daily subscribers dramatically rise from around 10 per day to a healthy 100 per day, so I thought I’d share these tips to help others grow their audience.

AWeber daily growth graph

A little background…

I own and create content for two design blogs. The first, Blog.SpoonGraphics is home to my Illustrator & Photoshop tutorials, as well as a range of inspirational showcases and free design resources. You’re reading my second blog right now here on Line25, this site has a specific focus on web design and is home to HTML/CSS tutorials and roundups of cool website designs and trends.

RSS Subscribers breakdown

As I described in my Feedburner to newsletter post, I wanted to begin growing my subscribers beyond RSS due to the demise of Google Reader and the possibility that Feedburner will follow a similar fate. I signed up with AWeber and spent many hours creating my custom email templates, which was actually one major advantage of a newsletter service as opposed to Feedburner. Customising the appearance and content of your emails not only improves the user’s experience, it also results in more click throughs and engagement in your broadcasts.

At first I just switched over my Feedburner email subscriptions signup link to an AWeber form and saw my daily subscriber rate average at up to 10 per day. It wasn’t until after I had a chat with fellow design blog owner and all round nice guy Tom Ross that I realised I could drastically improve this figure. Tom gave me some fantastic tips and techniques and after implementing them I saw that 10 subscribers per day figure rise to between 80-100 per day! If you’re looking for some email marketing consultation of your own I’d definitely give Tom a recommendation, otherwise I know he has some great projects of his own in the works, so give him a follow on Twitter to keep up to date with his online ventures.

Sign up form placement

Initially I only had a sign up form on my subscribe page, but one obvious step to increasing the number of sign ups is to put your form right in front of your users in various places on your site. I created 3 more sign up forms and placed in them in the following areas:

Sidebar signup form

Sidebar sign up form

Despite having a link for users to subscribe by email right at the top of the page, I didn’t have an actual complete signup form visible on the page. Tom recommended I revised the layout of my sidebar menu and use the prominent space for an eye catching form to greet new visitors.

End of post signup form

End of post signup form

One of the forms that has proved really effective on my sites is the placement at the end of every post. New visitors who have stumbled across your content and made it all the way to the end of the article have a high potential of wanting to stick around for more, so it makes sense to prompt them with the option of subscribing with a conveniently placed signup form.

About page signup form

About page signup form

Your about page is probably one of your least trafficked pages, but those who are browsing it have a particular interest in you, your website or your company. A strategically placed signup form slap bang in the middle of your bio is really effective to recruit those interested to receive more of your content. Despite having much lower display rates, my about page signup forms have amazing conversion figures (30x higher than the other forms!).

Free gift incentive

The additional sign up forms on my site no doubt prompted a decent number of readers to sign up, but the one technique that really boosted my daily subscriber rate was the creation of a free gift. Everyone loves freebies, and the offer of a gift to every new subscriber is enough to grab the attention of the average viewer and give them the incentive to subscribe for more. Sure, some people will sign up just to get their hands on the free goodies and have no interest in your new post notifications, but every day you’re growing your audience with people who had an interest in your site, who would have otherwise left and never returned again had it not been for the free gift hook.

Free web shadows pack gift

My Blog.SpoonGraphics site has been around since 2007 and during that time I’ve posted numerous freebies such as Photoshop brushes, textures and vectors. It would take a visitor ages to dig through the archives and download all this stuff, so I used this as an opportunity and created a bundle of my design resources as my free gift. For Line25′s free gift I was a little stuck for ideas, so I browsed the stock resources websites and gained inspiration from the most purchased items lists. Presentation is key, so I spent time mocking up the products and creating eye catching banners to tie the free gift promotion to the sign up forms.

How to set up a free gift download with AWeber

After all the hard work of creating your free gift and its promotional material the actual process of adding it to your mailing list is pretty simple, you essentially just create a special page on your site containing a download button and set this page as your mailing list’s opt-in success page.

AWeber's success page field

In AWeber, this option is found under List Settings > Confirmed Opt-In. It’s the Success Page field right at the bottom of the page. Users will be automatically sent to this page once they’ve clicked the activation link in their email.

Button wording

Split testing Download Now vs Get Email Updates

Recently I’ve been playing around with a split test of one of my signup forms. With the free gift being featured alongside the ability to receive new posts by email I wondered if there would be any difference in performance between the words “Get email updates” and “Download now!”. I expected the “Download now!” button to be more effective at attracting signups purely for the free gift, but the results were pretty extraordinary! The split test was run for around 40,000 impressions and during that time the “Download now!” button achieved 3 times more signups than the words “Get email updates”. It would be interesting if there’s any way to keep an eye on unsubscribe rates relative to each form to compare the quality of subscribers, but at the end of the day it’s 3x more people who are potentially viewing your content.

Final words

These tips are really just scratching the surface when it comes to growing an email list, but they’re all effective techniques that could be implemented by any website owner to optimise their email subscription signups. Most importantly these techniques aren’t deviant or intrusive, and help grow your mailing list with true followers who have a long and happy experience receiving your free content. While newsletter services do incur monthly fees, the ability to convert this investment into additional traffic and readership is definitely worthwhile.

Sponsored post
soup-sponsored
20:53

May 27 2013

07:00

How to Move From Feedburner to a Newsletter Service

Things are going downhill fast for RSS. First Google shut down Feedburner’s API in 2012, then they announced the closure of Google Reader earlier this year (2013). The future isn’t looking too bright for the Feedburner service as a whole and chances are it will also be completely shut down sometime in the near future. In today’s post I’ll show you how I’ve personally prepared for doomsday and gained a bunch of new subscribers in the process by migrating from Feedburner to an email newsletter service.

The future of Feedburner

If you’re a long time blog owner you’ll likely have used Feedburner since well before it was acquired by Google. By burning your original feed with Feedburner you gained a bunch of handy features such as user friendly subscribe pages, subscriber counts & stats and the all important email delivery option. Google bought Feedburner in 2007 then shut down the API in 2012. Feedburner’s interface has remained unchanged in that time and with the recent demise of the popular Google Reader you would be wise to expect the same fate for this small niche service.

How to prepare for doomsday

Every blog will see a dip in readership after 1st July 2013 when Google Reader is officially laid to rest. Google Reader is the tool over 94% of my readers use to stay up to date with my content. Even if you publicly prompt users to switch to an alternative service such as Feedly, you can bet that a substantial proportion of your readers will be lost. We’re just going to have to take that one on the chin, but the knock out blow will come if Feedburner is shut down too. The second largest group of subscribers after Google Reader users, our Feedburner email subscribers, will then be lost too. Therefore, it makes sense to start building a reserve readership today and rescue our Feedburner email subscribers from their potential doom by creating an alternative email mailing list with a newsletter service.

To me, email newsletters seemed a little outdated compared to RSS. I personally use email for emailing and RSS for content reading, but email newsletters are still really popular and bring along some new opportunities for your blog. Here’s a few pros and cons:

Pros

  • Customise the appearance of your email updates
  • Allow users to sign up using forms on your site
  • Stay in touch with readers with follow ups & updates
  • Run much more detailed reports on delivery and readership

Cons

  • It takes a fair amount of time to set up
  • You pay a monthly fee for the service

The three big names in the email newsletter business are AWeber, MailChimp and Campaign Monitor. Comprehensive comparisons of all three can be found elsewhere on the web, but I chose AWeber for my own mailing lists.

How to migrate your Feedburner email subscribers

We already have a core bunch of subscribers who signed up to receive our content by email, so rather than start from scratch with a newsletter service, we might as well migrate our users over.

To begin, you’ll need to export your subscriber list from Feedburner. Log in at feedburner.google.com, head to Subscribers > Feedburner Email Subscriptions. In the fly out options select Manage Your Subscriber List then export as a CSV.

Your Feedburner list contains both verified and unverified addresses. Some newsletter services may allow you to import your unverified users, but AWeber in particular request that these entries are removed, seeing as they didn’t go through the complete double opt-in process. You can do this with some spreadsheet trickery in Excel or Open Office by filtering out the rows with the word “Active”. Save your edited list as a new CSV file.

I know MailChimp allows you to import lists without the subscribers having to opt-in, but AWeber will send out a confirmation to everyone (again). However, seeing as our Feedburner subs have already confirmed their subscription and we’re providing the same kind of newsletter service a quick email to AWeber’s support team will mean you can import your lists without the second double opt-in after a few quick checks.
Once your subscribers have been imported it’s time to create your newsletter template designs and set up your broadcast messages. AWeber’s templates are pretty ugly, whereas MailChimp and Campaign Monitor offer some really nice off the shelf layouts. Being an AWeber user myself this article is going to focus on their features, so I’d advise finding a ready made newsletter template elsewhere on the web and customise it.

To replicate Feedburner’s functionality of sending out an automatic email copy of your RSS posts you’ll need to head to Blog Broadcast in AWeber’s menu structure. Seeing as Feedburner is still alive and kicking you can safely enter your FB feed URL, this will mean your newsletter subscribers will be added to your total RSS subscriber count. If Feedburner is ever shut down, just change this to your original RSS feed. Use AWeber’s RSS tags to specify where it should inject the relevant content into your newsletter layout. For the full blog post, make sure you use {!rss_item_content}, for the excerpt use {!rss_item_description} (their templates seem to use the description by default).

Set the option to send updates when the number of items reaches 1, then disable Send Automatically for now (so you don’t end up spamming everyone while setting things up). AWeber’s process is a little confusing, it “sends” updates to your Broadcast page where these entries will wait until you press the Queue button, unless you check “Send Automatically”. The problem is when you save your Blog Broadcast it generates 10+ emails from your RSS feed. You definitely don’t want these being sent out to your entire list, so delete them, test your setup then turn on the Send Automatically option when you’re confident it’s all working correctly. This will set the process to autopilot when new content is posted and work just like Feedburner did (but with much prettier emails!).

Once your new AWeber mailing list is set up with an automatic blog broadcast you’ll want to head back into Feedburner and turn off Email Subscriptions, otherwise your readers will receive the same update twice from the two separate services.

In order to continue growing your list, make sure you add sign up forms to your website. Don’t forget you also have all kinds of options and features at your fingertips. Set up a welcome email and a follow up series to interact with your readers and provide additional content; send out standard Broadcast messages to spread the word about new updates and news; or use marketing strategies to dramatically increase your subscriber count such as providing a free gift or a sign up incentive.

Now you’re paying for your newsletter service you’ll want to give it a spring clean to keep the costs down. All services charge based on the total number of users in your list, but sometimes these users aren’t even receiving your messages. Run searches and remove any entries that have never opened an email to decrease your bill and increase your performance stats.

November 10 2010

16:04

How to Build an RSS Feed with ASP.NET


Over the course of this tutorial, we’ll review how to create an RSS Feed with the ASP.NET framework. Having an RSS feed for your site has become a necessity in recent years. With blogs or news sites being updated frequently, and with the vast amount of blogs and news websites out there, RSS has allowed readers to keep up with new content without being forced to visit them. Once you’ve completed this tutorial, you’ll know how to create an RSS feed with ASP.NET, and how to render XML documents on ASP.NET Web pages.


Step 0: RSS 2.0 Introduction

RSS is a web content syndication format. It stands for “Really Simple Syndication,” and is written in XML. All RSS files must conform to the XML 1.0 specification, as published by the World Wide Web Consortium (W3C). With RSS, it is possible to distribute up-to-date web content from one web site to thousands of others around the world. To simplify the term, RSS allows fast browsing for news and updates.

The syntax rules of RSS 2.0 are quite simple, though strict. Here is a simple RSS document:

<?xml version="1.0" encoding="utf-8" ?>
<rss version="2.0">
  <channel>
    <title>Your Website Name</title>
    <link>http://www.yourdomain.com</link>
    <description>Free RSS Tutorial</description>
    <item>
      <title>First Article</title>
      <link>http://www.yourdomain.com/article.aspx?ID=1</link>
      <description>The description of the first article.</description>
    </item>
    <item>
      <title>Second Article</title>
      <link>http://www.yourdomain.com/article.aspx?ID=2</link>
      <description>The description of the second article.</description>
    </item>
  </channel>
</rss>

The first line in the document – the XML declaration – defines the XML version and the character encoding used in the document. In this case, the document conforms to the 1.0 specification of XML, and uses the utf-8 character set. The next line is the RSS declaration, which identifies that this is, in fact, an RSS document (more specifically, RSS version 2.0).

The next line contains the <channel> element. This element is used to describe the RSS feed. The <channel> element has three required child elements:

  • <title> – Defines the title of the channel (e.g. Your Website name)
  • <link> – Defines the hyperlink to the channel (e.g. http://www.yourdomain.com)
  • <description> – Describes the channel (e.g. Free RSS Tutorial)

Each <channel> element can have one or more <item> elements. Each <item> element defines an article or "story" within the RSS feed.

The <item> element requires three child elements:

  • <title> – Defines the title of the item (e.g. First Article)
  • <link> – Defines the hyperlink to the item (e.g. http://www.yourdomain.com/article.aspx?ID=1)
  • <description> – Describes the item (e.g. The description of the first article)

When viewed in Firefox, the RSS document above should look similar to:

RSS document

Step 1: Getting Started

To follow this tutorial, imagine that you work as a web developer, and are building a news website where all of the news stories are stored in Microsoft’s SQL Server database. Specifically, the articles are stored in a table called News, which contains the following fields:

  • ArticleID – an auto-increment primary key integer field uniquely identifying each article
  • Title – a nvarchar(256), specifying the title of the article
  • Author – a nvarchar(50), specifying the author of the article
  • Description – a nvarchar(500), providing a more in-depth description of the article
  • DatePublished – a datetime, indicating the date the article was published

Note that there might be other fields in the News table, but those listed above are the only ones we are interested in using — at least for the purposes of an RSS feed.

We’ll use Visual Studio to finish the example. If you don’t have the full version of VS, you can grab the free Express Edition.


Step 2: Creating an rss.aspx Page

Our next step is to create an ASP.NET Web page (rss.aspx) that will display a list of the most recent news items as a properly formatted RSS 2.0 document. In Solution Explorer, right-click the project name, and then click Add New Item. In thia dialog box, under Visual Studio installed templates, click Web Form.

Add New Page

 In the Name box, type a name for the new Web page (rss.aspx), and then click Add. The new ASP.NET web page (rss.aspx) is created and displayed, accordingly.

Because we want the rss.aspx page to produce XML output, the first thing that we must do is remove all HTML markup or web controls from the page, and then set the ContentType property of @page directive to "text/xml".

After clearing all HTML markup from rss.aspx page, add a Repeater control into the page. We'll use Repeater control to render RSS document on rss.aspx page.

Here is the source view of the rss.aspx page, after we’ve made some changes:

<%@ Page Language="C#" ContentType="text/xml" AutoEventWireup="true" CodeBehind="rss.aspx.cs" Inherits="NettutsTutorial2.rss" %>

<asp:Repeater ID="RepeaterRSS" runat="server">
</asp:Repeater>

Step 3: Setup The Connection String

Next, we want to setup the connection string of the data source, within the Web.config file. By saving the connection string information, we'll avoid having to hard-code it in the code-behind file. This simplifies things, if the connection string information changes in the future. Place it in the <connectionStrings> section under configuration element, like so:

<connectionStrings>
  <add name="ConnectionString" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\Nettuts.mdf;Integrated Security=True;User Instance=True"
   providerName="System.Data.SqlClient" />
 </connectionStrings>

We will access this information from the code-behind file later when we want to retrieve data from database.


Step 4: Removing Illegal Characters from the XML Document

Some characters have special meaning in XML. If you place a character, such as "<", inside an XML element, it will generate an error, because the parser interprets it as the start of a new element. To avoid this error, replace the "<" character with its entity reference (&lt;).

There are five predefined entity references in XML:

&lt; < less than &gt; > greater than &amp; & ampersand  &apos; ' apostrophe &quot; " quotation mark

To avoid our RSS feed producing an error, we have to change all illegal characters in an RSS document with their entity references. Add the following code into the code-behind file of the rss.aspx page (rss.aspx.cs):

protected string RemoveIllegalCharacters(object input)
{
    // cast the input to a string
    string data = input.ToString();      

    // replace illegal characters in XML documents with their entity references
    data = data.Replace("&", "&amp;");
    data = data.Replace("\"", "&quot;");
    data = data.Replace("'", "&apos;");
    data = data.Replace("<", "&lt;");
    data = data.Replace(">", "&gt;");

    return data;
}

The RemoveIllegalCharacters() function performs a few simple string replacements, if needed. We will call this function from the rss.aspx page shortly.


Step 5: Retrieving Data from the Database

Add the following code into the Page_Load event handler:

protected void Page_Load(object sender, EventArgs e)
{
    // Get connection string from the web.config file
    string connString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;

    // Create SqlConnection object
    SqlConnection sqlConn = new SqlConnection();
    sqlConn.ConnectionString = connString;

    // SQL query to retrieve data from database
    string sqlQuery = "SELECT TOP 10 ArticleID, Title, Author, Description, DatePublished FROM News ORDER BY DatePublished DESC";

    // Create SqlCommand object
    SqlCommand cmd = new SqlCommand();
    cmd.Connection = sqlConn;
    cmd.CommandType = CommandType.Text;
    cmd.CommandText = sqlQuery;

    // open connection and then binding data into RepeaterRSS control
    sqlConn.Open();
    RepeaterRSS.DataSource = cmd.ExecuteReader();
    RepeaterRSS.DataBind();
    sqlConn.Close();
}

The code above will attempt to retrieve the ten most recent news items from your database. It will then bind them to a Repeater control (RepeaterRSS) when the page is loaded.


Step 6: Rendering Data

This is our last step. It's time to render items from the News table into an appropriate RSS 2.0 syndication file. The simplest and quickest way to display the database data as XML is to use a Repeater control (RepeaterRSS). Specifically, the Repeater will display the <rss>, <channel>, and Web site related element tags in its HeaderTemplate and FooterTemplate templates, and the <item> elements in the ItemTemplate template. Don't forget to call the helper RemoveIllegalCharacters() function to replace unwanted characters from the string output.

The following is the HTML portion of our ASP.NET Web page (rss.aspx):

<%@ Page Language="C#" ContentType="text/xml" AutoEventWireup="true" CodeBehind="rss.aspx.cs" Inherits="NettutsTutorial2.rss" %>

<asp:Repeater ID="RepeaterRSS" runat="server">
        <HeaderTemplate>
           <rss version="2.0">
                <channel>
                    <title>Name of the Website</title>
                    <link>http://www.yourdomain.com</link>
                    <description>
                    Short description of the website.
                    </description>
        </HeaderTemplate>
        <ItemTemplate>
            <item>
                <title><%# RemoveIllegalCharacters(DataBinder.Eval(Container.DataItem, "Title")) %></title>
                <link>http://www.yourdomain.com/news.aspx?ID=<%# DataBinder.Eval(Container.DataItem, "ArticleID") %></link>
                <author><%# RemoveIllegalCharacters(DataBinder.Eval(Container.DataItem, "Author"))%></author>
                <pubDate><%# String.Format("{0:R}", DataBinder.Eval(Container.DataItem, "DatePublished"))%></pubDate>
                <description><%# RemoveIllegalCharacters(DataBinder.Eval(Container.DataItem, "Description"))%></description>
        </item>
        </ItemTemplate>
        <FooterTemplate>
                </channel>
            </rss>
        </FooterTemplate>
</asp:Repeater>

The first thing worth noting here is that the rss.aspx file above contains only the Repeater control (RepeaterRSS), and no other HTML markup or Web controls. This is because we want the page to emit nothing but XML output. We set the ContentType of the @page directive to "text/xml" to indicate that the output of the rss.aspx page is an XML document.

In the HeaderTemplate template of the Repeater control, we place the <rss> and <channel> elements.

Next, in the ItemTemplate, we place the <item> elements, which contain the <title>, <link>, <author>, <pubDate>, and <description> elements, as the place for database fields. We call the RemoveIllegalCharacters() function when adding the Title, Author, and Description database fields to the XML output. Remember, this function simply replaces any illegal XML characters with their  entity references.

Finally, the DatePublished database field, entered into the <pubDate> element, is formatted using the String.Format method. The standard format specifier, R, formats the DatePublished value appropriately according to RFC 822, Date and Time Specification. This starts with an optional three-letter day abbreviation and comma, followed by a required day, the three-letter abbreviated month, and then the year, followed by a time with the time-zone name, such as Thu, 04 Nov 2010 20:50:26 GMT.


The Result

And there we have it! With minimal effort, we’ve created a custom RSS feed for an ASP.NET application.

The Resulting Feed

The Complete Code

The rss.aspx File

<%@ Page Language="C#" ContentType="text/xml" AutoEventWireup="true" CodeBehind="rss.aspx.cs" Inherits="NettutsTutorial2.rss" %>

<asp:Repeater ID="RepeaterRSS" runat="server">
        <HeaderTemplate>
           <rss version="2.0">
                <channel>
                    <title>Name of the Website</title>
                    <link>http://www.yourdomain.com</link>
                    <description>
                    Short description of the website.
                    </description>
        </HeaderTemplate>
        <ItemTemplate>
            <item>
                <title><%# RemoveIllegalCharacters(DataBinder.Eval(Container.DataItem, "Title")) %></title>
                <link>http://www.yourdomain.com/news.aspx?ID=<%# DataBinder.Eval(Container.DataItem, "ArticleID") %></link>
                <author><%# RemoveIllegalCharacters(DataBinder.Eval(Container.DataItem, "Author"))%></author>
                <pubDate><%# String.Format("{0:R}", DataBinder.Eval(Container.DataItem, "DatePublished"))%></pubDate>
                <description><%# RemoveIllegalCharacters(DataBinder.Eval(Container.DataItem, "Description"))%></description>
        </item>
        </ItemTemplate>
        <FooterTemplate>
                </channel>
            </rss>
        </FooterTemplate>
</asp:Repeater>

The rss.aspx.cs File

using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Data.SqlClient;

namespace NettutsTutorial2
{
    public partial class rss : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            // Get connection string from web.config file
            string connString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;

            // Create SqlConnection object
            SqlConnection sqlConn = new SqlConnection();
            sqlConn.ConnectionString = connString;

            // SQL query to retrieve data from database
            string sqlQuery = "SELECT TOP 10 ArticleID, Title, Author, Description, DatePublished FROM News ORDER BY DatePublished DESC";

            // Create SqlCommand object
            SqlCommand cmd = new SqlCommand();
            cmd.Connection = sqlConn;
            cmd.CommandType = CommandType.Text;
            cmd.CommandText = sqlQuery;

            // open connection and then binding data into RepeaterRSS control
            sqlConn.Open();
            RepeaterRSS.DataSource = cmd.ExecuteReader();
            RepeaterRSS.DataBind();
            sqlConn.Close();
        }

        protected string RemoveIllegalCharacters(object input)
        {
            // cast the input to a string
            string data = input.ToString();

            // replace illegal characters in XML documents with their entity references
            data = data.Replace("&", "&amp;");
            data = data.Replace("\"", "&quot;");
            data = data.Replace("'", "&apos;");
            data = data.Replace("<", "&lt;");
            data = data.Replace(">", "&gt;");

            return data;
        }
    }
}

The Web.config File

<?xml version="1.0"?>
<configuration>
	<configSections>
		<sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
			<sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
				<section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
				<sectionGroup name="webServices" type="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
					<section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="Everywhere"/>
					<section name="profileService" type="System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
					<section name="authenticationService" type="System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
					<section name="roleService" type="System.Web.Configuration.ScriptingRoleServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
				</sectionGroup>
			</sectionGroup>
		</sectionGroup>
	</configSections>
	<appSettings/>
	<connectionStrings>
		<add name="ConnectionString" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\Nettuts.mdf;Integrated Security=True;User Instance=True" providerName="System.Data.SqlClient"/>
	</connectionStrings>
	<system.web>
		<!--
            Set compilation debug="true" to insert debugging
            symbols into the compiled page. Because this
            affects performance, set this value to true only
            during development.
        -->
		<compilation debug="true">
			<assemblies>
				<add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
				<add assembly="System.Data.DataSetExtensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
				<add assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
				<add assembly="System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
			</assemblies>
		</compilation>
		<!--
            The <authentication> section enables configuration
            of the security authentication mode used by
            ASP.NET to identify an incoming user.
        -->
		<authentication mode="Windows"/>
		<!--
            The <customErrors> section enables configuration
            of what to do if/when an unhandled error occurs
            during the execution of a request. Specifically,
            it enables developers to configure html error pages
            to be displayed in place of a error stack trace.

        <customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
            <error statusCode="403" redirect="NoAccess.htm" />
            <error statusCode="404" redirect="FileNotFound.htm" />
        </customErrors>
        -->
		<pages>
			<controls>
				<add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
				<add tagPrefix="asp" namespace="System.Web.UI.WebControls" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
			</controls>
		</pages>
		<httpHandlers>
			<remove verb="*" path="*.asmx"/>
			<add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
			<add verb="*" path="*_AppService.axd" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
			<add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" validate="false"/>
		</httpHandlers>
		<httpModules>
			<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
		</httpModules>
	</system.web>
	<system.codedom>
		<compilers>
			<compiler language="c#;cs;csharp" extension=".cs" warningLevel="4" type="Microsoft.CSharp.CSharpCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
				<providerOption name="CompilerVersion" value="v3.5"/>
				<providerOption name="WarnAsError" value="false"/>
			</compiler>
		</compilers>
	</system.codedom>
	<!--
        The system.webServer section is required for running ASP.NET AJAX under Internet
        Information Services 7.0.  It is not necessary for previous version of IIS.
    -->
	<system.webServer>
		<validation validateIntegratedModeConfiguration="false"/>
		<modules>
			<remove name="ScriptModule"/>
			<add name="ScriptModule" preCondition="managedHandler" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
		</modules>
		<handlers>
			<remove name="WebServiceHandlerFactory-Integrated"/>
			<remove name="ScriptHandlerFactory"/>
			<remove name="ScriptHandlerFactoryAppServices"/>
			<remove name="ScriptResource"/>
			<add name="ScriptHandlerFactory" verb="*" path="*.asmx" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
			<add name="ScriptHandlerFactoryAppServices" verb="*" path="*_AppService.axd" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
			<add name="ScriptResource" preCondition="integratedMode" verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
		</handlers>
	</system.webServer>
	<runtime>
		<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
			<dependentAssembly>
				<assemblyIdentity name="System.Web.Extensions" publicKeyToken="31bf3856ad364e35"/>
				<bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0"/>
			</dependentAssembly>
			<dependentAssembly>
				<assemblyIdentity name="System.Web.Extensions.Design" publicKeyToken="31bf3856ad364e35"/>
				<bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0"/>
			</dependentAssembly>
		</assemblyBinding>
	</runtime>
</configuration>

Conclusion

Creating an RSS feed with ASP.NET is quite simple, isn't it? We only need to understand the RSS 2.0 specification. Beyond that, by using the .NET Repeater control, we can render items from a database into the RSS document. Most content management systems will have this capability by default. However, if you're a developer, and are building an ASP.NET website from scratch, you now have the skills to build your own custom feed.

Happy Coding!

October 18 2010

20:58

How to Build an RSS Reader with jQuery Mobile


In conjunction with the jQuery conference in Boston, John Resig announced and released the highly anticipated jQuery Mobile. Though currently in an alpha state, the framework is fantastic.

Today, we’ll dive in, and build a simple Tuts+ RSS reader, using PHP and jQuery Mobile. When we’re finished, you’ll have the ability to add this simple project to your iPhone or Android phone with the click of a button, as well as the skills to build your web apps!


Step 1: Outline the Project

It’s always helpful to first outline what you want your project to do/achieve.

  • Display a list of every Tuts+ site, along with its square logo
  • Display the feed for each site, when clicked on
  • Create a basic *article* stylesheet for rendering each posting
  • Create an Apple-touch icon for the users who add the “app” to their phone
  • Use YQL to retrieve the desired RSS feed
  • Implement a basic form of “text file” caching every three hours

Step 2: Begin

The next step is to begin creating our project. Go ahead and make a new folder — name it how you wish — and add a new header.php file. *Note that this project uses PHP. If you’re not familiar with this language, feel free to skip the PHP parts! Within this file, we’ll reference jQuery mobile, its stylesheet, and any other assets that we require. If only to stay organized, I’ve placed my header.php file within an includes/ folder.

<!DOCTYPE html>
<html>
   <head>
   <meta charset="utf-8">
   <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />

   <title> Tuts+ </title> 

   <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0a1/jquery.mobile-1.0a1.min.css" />
   <link rel="apple-touch-icon" href="img/tutsTouchIcon.png" />

   <script src="http://code.jquery.com/jquery-1.4.3.min.js"></script>
   <script src="http://code.jquery.com/mobile/1.0a1/jquery.mobile-1.0a1.min.js"></script>
</head>

There are a handful of things worth noting here.

  1. An HTML5 doctype is required. But you should be using that anyways!
  2. The X-UA-Compatible tag forces IE to use it most current rendering engine
  3. We need to reference jQuery Mobile’s stylesheet. You can use their CDN, and save on bandwidth!
  4. If you want to designate an icon for when users add your webpage to their iPhone (or Android) home screen, add a link tag, with a rel attribute of apple-touch-icon.
  5. We’re referencing the most recent version of jQuery: 1.4.3
  6. Finally, we’re loading the jQuery mobile script file (currently in Alpha 1)

The Basic Structure

The jQuery Mobile framework can be activated by applying unique data-* attributes to your code. The basic structure for most sites will look similar to:

<!-- Let's include the header file that we created above -->
<?php include('includes/header.php'); ?>
<body>
 <div data-role="page">

   <header data-role="header">

   </header>

   <div data-role="content">

   </div>

   <footer data-role="footer">

   </footer>

 </div>

</body>
</html>

Add the code above to a new index.php file, within the root of your project.

We have to tell jQuery about our project. For example, try not to think of each file as a page. Technically, you can create multiple pages at a time, by adding additional wrapping data-role="page" attributes. These are referred to as inner pages.

Further, the framework has specific settings and stylings in place for the header, main content area, and footer. To inform jQuery Mobile about the locations of these elements, we add the following attributes.

  • data-role="header"
  • data-role="content"
  • data-role="footer"
No Data Attributes Applied

No data-role attributes have been applied.

Data Applied

Data-role attributes applied.

Step 3: Listing the Tutorial Sites

Now that the structure of our index.php page is complete, we can populate each section with our Tuts+ specific mark-up.

<body>
 	<div>
	    <header data-role="header">
	    	<h1> <img src="img/TLogo.png" alt="Tuts+"/> </h1>
	    </header>

	    <div data-role="content">
			<ul>
				<li>
					<img src="img/ntLogo.jpg" alt="Nettuts" class="ui-li-icon"/>
					<a href="site.php?siteName=nettuts"> Nettuts+ </a>
				</li>
				<li>
					<img src="img/psdLogo.jpg" alt="Psdtuts" class="ui-li-icon"/>
					<a href="site.php?siteName=psdtuts"> Psdtuts+ </a>
				</li>
				<li>
					<img src="img/vectorLogo.jpg" alt="Vectortuts+" class="ui-li-icon"/>
					<a href="site.php?siteName=vectortuts"> Vectortuts+ </a>
				</li>
				<li>
					<img src="img/mobileLogo.png" alt="Mobiletuts+" class="ui-li-icon"/>
					<a href="site.php?siteName=mobiletuts"> Mobiletuts+ </a>
				</li>
				<li>
					<img src="img/aeLogo.jpg" alt="Aetuts+" class="ui-li-icon"/>
					<a href="site.php?siteName=aetuts"> Aetuts+ </a>
				</li>
				<li>
					<img src="img/photoLogo.jpg" alt="Phototuts+" class="ui-li-icon"/>
					<a href="site.php?siteName=phototuts"> Phototuts+ </a>
				</li>
				<li>
					<img src="img/cgLogo.jpg" alt="Cgtuts+" class="ui-li-icon"/>
					<a href="site.php?siteName=cgtuts"> Cgtuts+ </a>
				</li>
				<li>
					<img src="img/audioLogo.jpg" alt="Audiotuts+" class="ui-li-icon"/>
					<a href="site.php?siteName=audiotuts"> Audiotuts+ </a>
				</li>
				<li>
					<img src="img/wdLogo.jpg" alt="Webdesigntuts+" class="ui-li-icon"/>
					<a href="site.php?siteName=webdesigntutsplus"> Webdesigntuts+ </a>
				</li>
			</ul>
	    </div>

	    <footer data-role="footer">
			<h4> www.tutsplus.com </h4>
	    </footer>

	</div>

</body>
</html>
  • Header: In this section, we’re simply inserting the Tuts+ graphic, and providing alternate text if images are turned off.
  • Content: In the content area, we need to list all of the tutorial sites, and apply a unique icon next to each heading. We also link to a new page, site.php that will handle the process of retrieving the RSS feed. For convenience, when we link to site.php, we also pass through the name of the selected site, via the querystring: siteName=nettuts.
  • Footer: At the bottom, for now, we’ll simply add a link to Tuts+.

jQuery Mobile offers a plethora of helpful CSS classes, including ui-li-icon. When applied to an image, it’ll float it to the left, and apply 10px worth of margin-right.

Our project so far

At this point, our site should look like the above image.

Page Transitions

As jQuery will load local pages asynchronously with AJAX, we can specify any number of cool page transitions. The default is the basic slide-left or slide-right effect that most touch-phone users are aware of. To override the default, use the data-transition attribute on the anchor tag.

<a href="site.php?siteName=nettuts" data-transition="pop"> Nettuts+ </a>

Available Transitions

  • slide
  • slideup
  • slidedown
  • pop
  • flip
  • fade

Step 4: ListViews

Ehh – the image, shown above, still looks like a website. We need to make things a bit more phone-like. The answer is to use the data-role="listview" attribute. Watch what happens when we do nothing more than apply this attribute to the wrapping unordered list.

Using the listview data-role attribute

Wow – what an improvement! Even better, we have access to theme-roller, which allows us, with the change of a single letter, to switch color themes.

<ul data-role="listview" data-theme="a">
Theme A
<ul data-role="listview" data-theme="b">
Theme B
<ul data-role="listview" data-theme="e">
Theme E

List Dividers

Now, what if we wanted to divide this list of tutorial sites? In these situations, we can take advantage of data-role="list-divider", which can be applied to the <li> element.

List Divider

These, too, can receive lettered theme roller stylings. They can be set within the parent <ul>.

<ul data-role="listview" data-dividertheme="d">
Divider Theme
Learn more about list dividers.

Note that we won’t be using dividers for this particular application.


Step 5: CSS

jQuery Mobile takes care of a great deal of the formatting, however, we still, of course, need our own stylesheet for tweaking. For example, looking at the images above, we can see that the tutorial icons need to be pushed up a bit. Additionally, I’d like to use the Tuts+ red for the background color of the heading and footer, rather than the default black.

Create a new folder, CSS, and add a new stylesheet — I’ll call mine: mobile.css. Within this file, we’ll first fix the icon positioning:

 .ui-li-icon {
   top:  9px;
}

Next, we’ll create a handful of classes, named after their respective tutorial sites. These classes will contain any specific formatting/colors for the site. For example, Nettuts+ has a darker green color, while MobileTuts+ is yellow.


.tuts { background: #c24e00; }
.nettuts { background: #2d6b61; }
.psdtuts { background: #af1c00; }
.vectortuts { background: #1e468e; }
.aetuts { background: #4a3c59; }
.phototuts { background: #3798aa; }
.cgtuts { background: #723b4a; }
.audiotuts { background: #4b7e00; }
.webdesigntutsplus { background: #0d533f; }
.mobiletuts { background: #dba600; }

That should be fine for now. The last step for index.php is to apply the .tuts class to both the header and footer elements. That way, they’ll render the correct background color.

<header data-role="header" class="tuts"> ... </header>
...
<footer data-role="footer" class="tuts"> ... </footer>
Added the Tuts Color

Step 6: YQL, PHP, and Caching

Now, it’s time to step away from the layout, and work on the functionality. Each of the links we created directed to site.php?siteName="siteName". Let’s go ahead and create that file now.

Even though this is a relatively tiny app, we should still strive to follow best practices. In this case, it means that we should keep as little PHP in our document as possible. Instead, we’ll use site.php as a controller of sorts. This file will handle the initial logic, and will then, at the bottom, load in our HTML template.

Assigning the Site Name

In order to retrieve the desired RSS feed, we first need to capture the name of the site that the user clicked on initially. If you’ll refer to a previous step, when we linked to site.php, we also passed the name of the site through the querystring. With PHP, this can easily be retrieved, with $_GET['siteName']. However, what if, for some odd reason, this value doesn’t exist? Maybe site.php was accessed directly?? We should set a default site to compensate for these situations.

$siteName = empty($_GET['siteName']) ? 'nettuts' : $_GET['siteName'];

If $_GET['siteName'] is empty, we’ll set “nettuts” to the variable, $siteName. Otherwise, it’ll be equal to the name of the respective site.

Caching

Ultimately, we’ll be using the excellent YQL to perform our queries. Think of YQL as an API for APIs. Rather than having to learn twenty different APIs, YQL’s SQL-like syntax allows you to only learn one. However, though YQL does perform a bit of caching on its own, let’s also save the RSS feeds to a text file on our server. That way, we can improve performance a fair bit.

We begin by creating a new variable, $cache, and making it equal to the location of where the cached file will be stored.

$cache = dirname(__FILE__) . "/cache/$siteName";

The code above points to the current directory of the file, and then into a cache folder, and, finally, the name of the selected site.

I’ve decided that this cached file should be updated every three hours. As such, we can run a quick if statement, and determine the last time that the file was updated. If the file does not exist, or the update was longer than three hours ago, we query YQL.

$cache = dirname(__FILE__) . "/cache/$siteName";
// Re-cache every three hours
if( filemtime($cache) < (time() - 10800) ) {
 // grab the site's RSS feed, via YQL
}

YQL is ridiculously easy to work with. In our case, we'll use it for a very simple purpose: grab the RSS feed, in JSON form, of the site that was passed through the querystring, via siteName. You can experiment with the various commands by using the YQL console.

To query an RSS feed, we using the command: SELECT * FROM feed WHERE url="path/to/rss/feed".

Building the Path

For the sake of readability, we'll build up our YQL query in sections.

 // YQL query (SELECT * from feed ... ) // Split for readability
 $path = "http://query.yahooapis.com/v1/public/yql?q=";
 $path .= urlencode("SELECT * FROM feed WHERE url='http://feeds.feedburner.com/$siteName'");
 $path .= "&format=json";

The key is the second part above; when the page loaded, we grabbed the name of the site from the querystring. Now, we only need to insert it into the SELECT query. Luckily, all of the tutorial sites use Feedburner! Make sure that you urlencode the query to replace any special characters.

Okay, the path is ready; let's use file_get_contents() to grab the feed!

$feed = file_get_contents($path, true);

Assuming that $feed is now equal to the returned JSON, we can store the results in a text file. However, let's first ensure that data was returned. As long as something is returned from the query, $feed->query->count will be equal to a value greater than zero. If it is, we'll open the cached file, write the data to the file, and finally close it.

// If something was returned, cache
if ( $feed->query->count ) {
   $cachefile = fopen($cache, 'w');
   fwrite($cachefile, $feed);
   fclose($cachefile);
}

It seems confusing, but it's really not. The function fopen() accepts two parameters:

  • The file to open: We stored this path in the $cache variable at the top of the page. Note that, if this file doesn't exist, it will create the file for you.
  • Access privileges: Here, we can specify which privileges are available. w stands for "write."

Next, we open that file, and write the contents of $feed (the returned RSS JSON data) to the file, and close it.

Using the Cached File

Above, we first checked whether the cached file was greater than three hours old.

if( filemtime($cache) < (time() - 10800) ) {
 // grab the site's RSS feed, via YQL
}

But what if it wasn't? In that case, we run an else statement, and grab the contents of the text file, rather than using YQL.

if( filemtime($cache) < (time() - 10800) ) {
 // grab the site's RSS feed, via YQL
 ....
}
else {
// We already have local cache. Use that instead.
$feed = file_get_contents($cache);
}

Lastly, we can't do much with the JSON RSS feed until we decode it with PHP.

// Decode that shizzle
$feed = json_decode($feed);

And that should do it for our controller! With the logic out of the way, let's include our HTML template.

// Include the view
include('views/site.tmpl.php');

Here's our final site.php. Click on the expand icon to view it.

<?php

// If "siteName" isn't in the querystring, set the default site name to 'nettuts'
$siteName = empty($_GET["siteName"]) ? "nettuts" : $_GET["siteName"];

$cache = dirname(__FILE__) . "/cache/$siteName";
// Re-cache every three hours
if(filemtime($cache) < (time() - 10800))
{
   // Get from server
   if ( !file_exists(dirname(__FILE__) . "/cache") ) {
      mkdir(dirname(__FILE__) . "/cache", 0777);
   }
   // YQL query (SELECT * from feed ... ) // Split for readability
   $path = "http://query.yahooapis.com/v1/public/yql?q=";
   $path .= urlencode("SELECT * FROM feed WHERE url='http://feeds.feedburner.com/$siteName'");
   $path .= "&format=json";

   // Call YQL, and if the query didn't fail, cache the returned data
   $feed = file_get_contents($path, true);

   // If something was returned, cache
   if ( $feed->query->count ) {
      $cachefile = fopen($cache, "wb");
      fwrite($cachefile, $feed);
      fclose($cachefile);
   }
}
else
{
   // We already have local cache. Use that instead.
   $feed = file_get_contents($cache);
}

// Decode that shizzle
$feed = json_decode($feed);

// Include the view
include('views/site.tmpl.php');

Step 7: The Site Template

At the end of the previous step, we loaded in our template (or view). Go ahead and create that views folder, and site.tmpl.php file. Feel free to name it how you wish. Next, we'll insert our HTML.

<?php include('includes/header.php'); ?>
<body> 

<div data-role="page">

   <header data-role="header" class="<?php echo $siteName; ?>">
      <h1><?php echo ucwords($siteName).'+'; ?></h1>
   </header>

   <div data-role="content">
      <ul data-role="listview" data-theme="c" data-dividertheme="d" data-counttheme="e">

      </ul>
   </div>

   <footer data-role="footer" class="<?php echo $siteName; ?>">
      <h4> www.tutsplus.com</h4>
   </footer>
</div>

</body>
</html>

Points of Interest Above

  • Notice how we follow the same basic layout: header, content area, footer.
  • As this template will be used for every Tuts+ tutorial site, we need to set the title dynamically. Luckily, if you remember, the site name was passed through the querystring, and stored in the $siteName variable (like, "nettuts"). To capitalize the first letter, and apply the signature + after the name, we'll run the variable through ucwords() (uppercases the first letter of each word in the string), and append a "+": <h1><?php echo ucwords($siteName).'+'; ?></h1>
  • We'll soon be displaying the number of comments for each posting next to the title. We can, again, use ThemeRoller to style it, via the data-counttheme="e" attribute.
Site Template Thus Far

Filtering Through the Feed

At this point, we have access to the $feed object that contains our RSS feed. To dissect this object, you can either print_r($feed), or use the YQL console for a prettier view. We'll use the latter in this case. Check it out.

To grab the data for each posting, we need to filter through: $feed->query->results->item. PHP makes this a cinch with foreach().

Within the foreach() statement, we can now access the desired values with $item->title, or $item->comments, which will display the title, and the comment number, respectively. Add the following within the <ul> tags.

<ul data-role="listview" data-theme="c" data-dividertheme="d" data-counttheme="e">
<?php
    foreach($feed->query->results->item as $item) { ?>

    <li>
      <h2>
         <a href="article.php?siteName=<?php echo $siteName;?>&origLink=<?php echo $item->guid->content;?>">
               <?php echo $item->title; ?>
         </a>
      </h2>
      <span class="ui-li-count"><?php echo $item->comments; ?> </span>
   </li>

<?php  } ?>
</ul>

In the code above, we build up a list item, containing the title of the posting, the number of comments, and a link to article.php that also contains the site name and the permanent link (to the original article on the Tuts+ site) in the query-string.

When we view the updated page in the browser, tada!

Recent article list

Notice how the comment count is in a yellow bubble, and is floated to the right? That's because we applied the data-counttheme="e" attribute to the wrapping unordered list. How convenient.

Hmm...I think the text is too large for these long titles. A quick visit to Firebug shows that I can target the h2 tags with a class of .ui-li-heading. Let's return to our stylesheet (mobile.css), and add a new rule:

.ui-li-heading { font-size: 12px; }

That's better.

Applying a smaller heading size

Step 8: Displaying the Full Posting

The final step is to build article.php, which will display the entire posting. As with site.php, article.php will serve as our controller, and will query the selected article with YQL, and load the appropriate view.

<?php

$siteName = $_GET['siteName'];
$origLink = $_GET['origLink'];

// YQL query (SELECT * from feed ... ) // Split for readability
$path = "http://query.yahooapis.com/v1/public/yql?q=";
$path .= urlencode("SELECT * FROM feed WHERE url='http://feeds.feedburner.com/$siteName' AND guid='$oigLink'");
$path .= "&format=json";

$feed = json_decode(file_get_contents($path));
$feed = $feed->query->results->item;

include('views/article.tmpl.php');

If you've been following along, the code above should look a bit more familiar to you. When we loaded this page, from site.php, we passed through two items, via the query string:

  • Site Name: Contains the name of the currently selected tutorial site
  • Orig Link: A link to the original posting on the tutorial site

The difference with the YQL query, this time, is that we match the guid (orig link) with the posting that the user clicked on (or pressed). This way, exactly one posting will be returned. Check out this sample YQL query to gain a better idea of what I mean.

New YQL query

Article Template

At the bottom of the code above, we loaded the template file for the article page: views/article.tmpl.php. We'll create that file now.

<?php include('includes/header.php'); ?>
<body> 

<div data-role="page">

   <header data-role="header" class="<?php echo $siteName; ?>">
      <h1> <?php echo ucWords($siteName).'+'; ?> </h1>
   </header>

   <div data-role="content">
        <h1> <?php echo $feed->title; ?> </h1>
        <div> <?php echo $feed->description; ?> </div>
   </div>

   <footer data-role="footer" class="<?php echo $siteName; ?>">
      <h4> <a href="<?php echo $feed->guid->content;?>"> Read on <?php echo ucWords($siteName); ?>+</a></h4>
   </footer>
</div>

</body>
</html>

Ah - so familiar. We've already gone over this template. The only difference is that, this time, because there's only one posting from the YQL query to display, we don't need to bother with a foreach() statement.

Unstyled Article Page
Unstyled article page

At this point, on your own, the next step would be to begin applying your desired styling to the article. I don't see a need to go over it in this tutorial, as it all comes down to personal taste. Here's my super-minimal version.

Minimally styled article page
Applying a font-size, line-height, padding, and image formatting.

Locked Footers

One last thing: in the footer section of the article, we link to the original posting on Nettuts+. In its current state, the reader will only see that when they reach the bottom of the article. Let's lock the footer to the bottom of the current view-point at all times. We can use the data-position attribute to achieve this.

   <footer data-role="footer" data-position="fixed" class="<?php echo $siteName; ?>">
      <h4> <a href="<?php echo $feed->guid->content;?>"> Read on <?php echo ucWords($siteName); ?>+</a></h4>
   </footer>

That's better!


We're Done!

And, with relatively little work, we've successfully built a mobile RSS reader for the Tuts+ sites. It can certainly be expanded to provide additional features, error checking, and performance improvements, but this will hopefully get you started! If you'd like to fork the project and make it better, by all means...do! Thanks for reading, and be sure to refer to the jQuery Mobile documentation for more details. I've no doubt that you'll come across more jQuery mobile tutorials on our sister site, Mobiletuts+.

Add the Reader to your iPhone Home Screen

September 13 2010

14:22

The Design Cubicle is on Facebook

After reading a few posts on the death of RSS lately, Bloglines closing its doors as of October 1st due to a 27% decrease year after year, and Facebook and Twitter being used as the “new” way to keep current with blogs and news websites, I decided to bite my tongue (after 2 years of holding out) and create a Facebook page for The Design Cubicle as a means to allow those unfamiliar with RSS or those who don’t use it in general to stay current with my writing and links I post in the Notebook section of this site.

Older posts are this way If this message doesn't go away, click anywhere on the page to continue loading posts.
Could not load more posts
Maybe Soup is currently being updated? I'll try again automatically in a few seconds...
Just a second, loading more posts...
You've reached the end.
(PRO)
No Soup for you

Don't be the product, buy the product!

close
YES, I want to SOUP ●UP for ...