diff options
-rwxr-xr-x | bin/canonicalise-csv | 28 | ||||
-rwxr-xr-x | bin/load-contacts | 4 | ||||
-rwxr-xr-x | web/alert.cgi | 86 | ||||
-rwxr-xr-x | web/confirm.cgi | 47 | ||||
-rw-r--r-- | web/css.css | 15 | ||||
-rw-r--r-- | web/i/email.png | bin | 0 -> 593 bytes | |||
-rw-r--r-- | web/i/feed.gif | bin | 652 -> 0 bytes | |||
-rw-r--r-- | web/i/feed.png | bin | 0 -> 764 bytes | |||
-rwxr-xr-x | web/index.cgi | 35 | ||||
-rw-r--r-- | web/js.js | 16 | ||||
-rwxr-xr-x | web/rss.cgi | 6 | ||||
-rw-r--r-- | web/xsl.xsl | 21 |
12 files changed, 182 insertions, 76 deletions
diff --git a/bin/canonicalise-csv b/bin/canonicalise-csv index 72502e2d8..6163b3741 100755 --- a/bin/canonicalise-csv +++ b/bin/canonicalise-csv @@ -6,7 +6,7 @@ # Copyright (c) 2006 UK Citizens Online Democracy. All rights reserved. # Email: matthew@mysociety.org. WWW: http://www.mysociety.org # -# $Id: canonicalise-csv,v 1.1 2006-10-13 15:37:48 matthew Exp $ +# $Id: canonicalise-csv,v 1.2 2007-01-26 22:48:30 matthew Exp $ use strict; require 5.8.0; @@ -82,20 +82,20 @@ foreach my $type (@$types) { my $areas_info = mySociety::MaPit::get_voting_areas_info($areas); foreach my $id (keys %$areas_info) { my $area_info = $areas_info->{$id}; - my $name = $area_info->{name}; - if ($name eq 'Durham City Council') { - $out{$id} = $councils{'Durham City'}; - next; - } elsif ($name eq 'Durham County Council') { - $out{$id} = $councils{'Durham County'}; - next; - } - $name =~ s/( (Borough|City|District|County))* Council//; + my $name = $area_info->{name}; + if ($name eq 'Durham City Council') { + $out{$id} = $councils{'Durham City'}; + next; + } elsif ($name eq 'Durham County Council') { + $out{$id} = $councils{'Durham County'}; + next; + } + $name =~ s/( (Borough|City|District|County))* Council//; if ($councils{$name} && $councils{$name} =~ /@/) { - $out{$id} = $councils{$name}; - } elsif ($councils{$name} || exists($councils{$name})) { - push @missing, $id; - } + $out{$id} = $councils{$name}; + } elsif ($councils{$name} || exists($councils{$name})) { + push @missing, $id; + } } } diff --git a/bin/load-contacts b/bin/load-contacts index bac8497d3..d3f0681e2 100755 --- a/bin/load-contacts +++ b/bin/load-contacts @@ -6,7 +6,7 @@ # Copyright (c) 2006 UK Citizens Online Democracy. All rights reserved. # Email: matthew@mysociety.org. WWW: http://www.mysociety.org # -# $Id: load-contacts,v 1.1 2006-10-13 15:37:48 matthew Exp $ +# $Id: load-contacts,v 1.2 2007-01-26 22:48:30 matthew Exp $ use strict; require 5.8.0; @@ -42,7 +42,7 @@ while (<FP>) { my ($id, $email) = split /,/; dbh()->do("INSERT INTO contacts (area_id, email, editor, whenedited, note) VALUES (?, ?, 'import', ms_current_timestamp(), 'Initial import')", - {}, $id, $email); + {}, $id, $email); } dbh()->commit(); close(FP); diff --git a/web/alert.cgi b/web/alert.cgi index e9a173273..724e1731f 100755 --- a/web/alert.cgi +++ b/web/alert.cgi @@ -6,7 +6,7 @@ # Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. # Email: matthew@mysociety.org. WWW: http://www.mysociety.org # -# $Id: alert.cgi,v 1.1 2007-01-26 01:01:23 matthew Exp $ +# $Id: alert.cgi,v 1.2 2007-01-26 22:48:31 matthew Exp $ use strict; require 5.8.0; @@ -15,12 +15,15 @@ require 5.8.0; use FindBin; use lib "$FindBin::Bin/../perllib"; use lib "$FindBin::Bin/../../perllib"; +use Digest::SHA1 qw(sha1_hex); use Page; use mySociety::Alert; use mySociety::AuthToken; use mySociety::Config; use mySociety::DBHandle qw(dbh select_all); +use mySociety::Util qw(is_valid_email); +use mySociety::Web qw(ent); BEGIN { mySociety::Config::set_file("$FindBin::Bin/../conf/general"); @@ -36,10 +39,22 @@ BEGIN { sub main { my $q = shift; my $out = ''; - if (my $token = $q->param('token')) { + if (my $signed_email = $q->param('signed_email')) { + my ($salt, $signed_email) = split /,/, $signed_email; + my $email = $q->param('email'); + my $id = $q->param('id'); + my $secret = scalar(dbh()->selectrow_array('select secret from secret')); + if ($signed_email eq sha1_hex("$id-$email-$salt-$secret")) { + my $alert_id = mySociety::Alert::create($email, 'new_updates', $id); + mySociety::Alert::confirm($alert_id); + $out .= '<p>You have successfully subscribed to that alert.</p>'; + } else { + $out = '<p>We could not validate that alert.</p>'; + } + } elsif (my $token = $q->param('token')) { my $data = mySociety::AuthToken::retrieve('alert', $token); - if (my $id = $data->{id}) { - my $type = $data->{type}; + if (my $id = $data->{id}) { + my $type = $data->{type}; if ($type eq 'subscribe') { mySociety::Alert::confirm($id); $out = '<p>You have successfully confirmed your alert.</p>'; @@ -55,24 +70,31 @@ and we'll look into it. EOF } } elsif (my $email = $q->param('email')) { - # XXX: Need to validate email - my $type = $q->param('type'); - my $alert_id; - if ($type eq 'updates') { - my $id = $q->param('id'); - $alert_id = mySociety::Alert::create($email, 'new_updates', $id); - } elsif ($type eq 'problems') { - $alert_id = mySociety::Alert::create($email, 'new_problems'); - } else { - throw mySociety::Alert::Error('Invalid type'); - } - my %h = (); - $h{url} = mySociety::Config::get('BASE_URL') . '/A/' - . mySociety::AuthToken::store('alert', { id => $alert_id, type => 'subscribe' } ); - dbh()->commit(); - $out = Page::send_email($email, undef, 'alert-confirm', %h); + my @errors; + push @errors, 'Please enter a valid email address' unless is_valid_email($email); + if (@errors) { + $out = display_form($q, @errors); + } else { + my $type = $q->param('type'); + my $alert_id; + if ($type eq 'updates') { + my $id = $q->param('id'); + $alert_id = mySociety::Alert::create($email, 'new_updates', $id); + } elsif ($type eq 'problems') { + $alert_id = mySociety::Alert::create($email, 'new_problems'); + } else { + throw mySociety::Alert::Error('Invalid type'); + } + my %h = (); + $h{url} = mySociety::Config::get('BASE_URL') . '/A/' + . mySociety::AuthToken::store('alert', { id => $alert_id, type => 'subscribe' } ); + dbh()->commit(); + $out = Page::send_email($email, undef, 'alert-confirm', %h); + } + } elsif ($q->param('id')) { + $out = display_form($q); } else { - $out = 'This should probably show some sort of subscribe page.'; + $out = '<p>Subscribe from a problem page!</p>'; } print Page::header($q, 'Confirmation'); @@ -81,3 +103,25 @@ EOF } Page::do_fastcgi(\&main); +# Updates only at present +sub display_form { + my ($q, @errors) = @_; + my @vars = qw(id email); + my %input = map { $_ => $q->param($_) || '' } @vars; + my %input_h = map { $_ => $q->param($_) ? ent($q->param($_)) : '' } @vars; + my $out = ''; + if (@errors) { + $out .= '<ul id="error"><li>' . join('</li><li>', @errors) . '</li></ul>'; + } + $out .= <<EOF; +<p>Receive email when updates are left on this problem. +<form action="alert" method="post"> +<label class="n" for="alert_email">Email:</label> +<input type="text" name="email" id="alert_email" value="$input_h{email}" size="30"> +<input type="hidden" name="id" value="$input_h{id}"> +<input type="hidden" name="type" value="updates"> +<input type="submit" value="Subscribe"> +</form> +EOF + return $out; +} diff --git a/web/confirm.cgi b/web/confirm.cgi index 10f2b9656..6184f418f 100755 --- a/web/confirm.cgi +++ b/web/confirm.cgi @@ -6,7 +6,7 @@ # Copyright (c) 2006 UK Citizens Online Democracy. All rights reserved. # Email: matthew@mysociety.org. WWW: http://www.mysociety.org # -# $Id: confirm.cgi,v 1.6 2007-01-26 01:05:35 matthew Exp $ +# $Id: confirm.cgi,v 1.7 2007-01-26 22:48:31 matthew Exp $ use strict; require 5.8.0; @@ -15,11 +15,13 @@ require 5.8.0; use FindBin; use lib "$FindBin::Bin/../perllib"; use lib "$FindBin::Bin/../../perllib"; +use Digest::SHA1 qw(sha1_hex); use Page; use mySociety::AuthToken; use mySociety::Config; use mySociety::DBHandle qw(dbh select_all); +use mySociety::Util qw(random_bytes); BEGIN { mySociety::Config::set_file("$FindBin::Bin/../conf/general"); @@ -42,22 +44,49 @@ sub main { if ($id) { if ($type eq 'update') { dbh()->do("update comment set state='confirmed' where id=?", {}, $id); - my ($id, $fixed, $reopen) = dbh()->selectrow_array("select problem_id,mark_fixed,mark_open from comment where id=?", {}, $id); + my ($email) = dbh()->selectrow_array("select email from comment where id=?", {}, $id); + my ($problem_id, $fixed, $reopen) = dbh()->selectrow_array("select problem_id,mark_fixed,mark_open from comment where id=?", {}, $id); if ($fixed) { - dbh()->do("update problem set state='fixed' where id=? and state='confirmed'", {}, $id); + dbh()->do("update problem set state='fixed' where id=? and state='confirmed'", {}, $problem_id); } elsif ($reopen) { - dbh()->do("update problem set state='confirmed' where id=? and state='fixed'", {}, $id); + dbh()->do("update problem set state='confirmed' where id=? and state='fixed'", {}, $problem_id); } - # XXX: Ask about email alert here, and RSS feed? Or should the form have another checkbox? + my $salt = unpack('h*', random_bytes(8)); + my $secret = scalar(dbh()->selectrow_array('select secret from secret')); + my $signed_email = sha1_hex("$problem_id-$email-$salt-$secret"); $out = <<EOF; -<p>You have successfully confirmed your update and you can now <a href="/?id=$id">view it on the site</a>.</p> +<form action="/alert" method="post"> +<p>You have successfully confirmed your update and you can now <a href="/?id=$problem_id#update_$id">view it on the site</a>.</p> +<p>You could also +<a href="/rss/$problem_id">subscribe to the RSS feed</a> of updates on this problem, +or +<input type="hidden" name="signed_email" value="$salt,$signed_email"> +<input type="hidden" name="email" value="$email"> +<input type="hidden" name="id" value="$problem_id"> +<input type="hidden" name="type" value="updates"> +<input type="submit" value="sign up"> if you wish to receive updates by email. +</p> +</form> EOF } elsif ($type eq 'problem') { dbh()->do("update problem set state='confirmed' where id=?", {}, $id); - my $pc = dbh()->selectrow_array("select postcode from problem where id=?", {}, $id); - # Ask about email alert here, and RSS feed? + my $email = dbh()->selectrow_array("select email from problem where id=?", {}, $id); + my $salt = unpack('h*', random_bytes(8)); + my $secret = scalar(dbh()->selectrow_array('select secret from secret')); + my $signed_email = sha1_hex("$id-$email-$salt-$secret"); $out = <<EOF; -<p>You have successfully confirmed your problem and you can now <a href="/?id=$id;pc=$pc">view it on the site</a>.</p> +<form action="/alert" method="post"> +<p>You have successfully confirmed your problem and you can now <a href="/?id=$id">view it on the site</a>.</p> +<p>You could also +<a href="/rss/$id">subscribe to the RSS feed</a> of updates on this problem, +or +<input type="hidden" name="signed_email" value="$salt,$signed_email"> +<input type="hidden" name="email" value="$email"> +<input type="hidden" name="id" value="$id"> +<input type="hidden" name="type" value="updates"> +<input type="submit" value="sign up"> if you wish to receive updates by email. +</p> +</form> EOF } dbh()->commit(); diff --git a/web/css.css b/web/css.css index 2b1f93e08..98b2efe57 100644 --- a/web/css.css +++ b/web/css.css @@ -254,10 +254,23 @@ ol#current img { font-weight: bolder; } -#comments div em { +#updates div em { border-bottom: dotted 1px #5e552b; } +#email_alert_box { + display:none; + position: absolute; + padding: 3px; + font-size:83%; + border:solid 1px #7399C3; + background-color: #eeeeff; + color: #000000; +} +#email_alert_box p { + margin: 0; +} + #rss_items { width:62%; float:left; diff --git a/web/i/email.png b/web/i/email.png Binary files differnew file mode 100644 index 000000000..030eafde6 --- /dev/null +++ b/web/i/email.png diff --git a/web/i/feed.gif b/web/i/feed.gif Binary files differdeleted file mode 100644 index b0e4adf1d..000000000 --- a/web/i/feed.gif +++ /dev/null diff --git a/web/i/feed.png b/web/i/feed.png Binary files differnew file mode 100644 index 000000000..1679ab05b --- /dev/null +++ b/web/i/feed.png diff --git a/web/index.cgi b/web/index.cgi index 55538ac19..545899a8c 100755 --- a/web/index.cgi +++ b/web/index.cgi @@ -6,7 +6,7 @@ # Copyright (c) 2006 UK Citizens Online Democracy. All rights reserved. # Email: matthew@mysociety.org. WWW: http://www.mysociety.org # -# $Id: index.cgi,v 1.58 2007-01-26 14:19:42 matthew Exp $ +# $Id: index.cgi,v 1.59 2007-01-26 22:48:31 matthew Exp $ # TODO # Nothing is done about the update checkboxes - not stored anywhere on anything! @@ -411,7 +411,7 @@ EOF $out .= <<EOF; </ol> <h2>Recent problems reported within 10km</h2> - <p><a href="/rss/$x,$y"><img align="right" src="/i/feed.gif" width="16" height="16" alt="RSS feed" border="0"></a></p> + <p><a href="/rss/$x,$y"><img align="right" src="/i/feed.png" width="16" height="16" title="RSS feed of recent local problems" alt="RSS feed" border="0"></a></p> <ol id="current" start="$list_start"> EOF foreach (@$current) { @@ -487,6 +487,19 @@ EOF my $back = NewURL($q, id=>undef, x=>$x_tile, y=>$y_tile); $out .= '<p style="padding-bottom: 0.5em; border-bottom: dotted 1px #999999;" align="right"><a href="' . $back . '">Back to listings</a></p>'; + $out .= '<a href="/rss/'.$input_h{id}.'"><img align="right" src="/i/feed.png" width="16" height="16" title="RSS feed" alt="RSS feed of updates to this problem" border="0"></a> '; + $out .= '<a id="email_alert" href="/alert?type=updates;id='.$input_h{id}.'"><img align="right" src="/i/email.png" width="16" height="16" title="Email alerts" alt="Email alerts of updates to this problem" border="0"></a>'; + $out .= <<EOF; +<form action="alert" method="post" id="email_alert_box"> +<p>Receive email when updates are left on this problem</p> +<label class="n" for="alert_email">Email:</label> +<input type="text" name="email" id="alert_email" value="$input_h{email}" size="30"> +<input type="hidden" name="id" value="$input_h{id}"> +<input type="hidden" name="type" value="updates"> +<input type="submit" value="Subscribe"> +</form> +EOF + # Display updates my $updates = select_all( "select id, name, extract(epoch from created) as created, text, mark_fixed, mark_open @@ -494,10 +507,9 @@ EOF order by created desc", $input{id}); if (@$updates) { $out .= '<div id="updates">'; - $out .= '<a href="/rss/'.$input_h{id}.'"><img align="right" src="/i/feed.gif" width="16" height="16" alt="RSS feed of updates to this problem" border="0"></a>'; - $out .= '<h2>Updates</h2>'; + $out .= '<h2>Updates</h2>'; foreach my $row (@$updates) { - $out .= "<div><em>Posted by $row->{name} at " . prettify_epoch($row->{created}); + $out .= "<div><a name=\"update_$row->{id}\"></a><em>Posted by $row->{name} at " . prettify_epoch($row->{created}); $out .= ', marked fixed' if ($row->{mark_fixed}); $out .= ', reopened' if ($row->{mark_open}); $out .= '</em>'; @@ -505,19 +517,6 @@ EOF } $out .= '</div>'; } - $out .= <<EOF; -<h2>Follow this problem</h2> -<ul> -<li>Receive email when updates are left on this problem. -<form action="alert" method="post"> -<label class="n" for="alert_email">Email:</label> -<input type="text" name="email" id="alert_email" value="$input_h{email}" size="30"> -<input type="hidden" name="id" value="$input_h{id}"> -<input type="hidden" name="type" value="updates"> -<input type="submit" value="Subscribe"> -</form> -</ul> -EOF $out .= '<h2>Provide an update</h2>'; if (@errors) { $out .= '<ul id="error"><li>' . join('</li><li>', @errors) . '</li></ul>'; @@ -34,6 +34,22 @@ YAHOO.util.Event.onContentReady('mapForm', function() { } }); +YAHOO.util.Event.onContentReady('email_alert', function() { + this.onclick = function() { + if (this.on) { + YAHOO.util.Dom.setStyle('email_alert_box', 'display', 'none'); + this.on = false; + } else { + var pos = YAHOO.util.Dom.getXY(this); + pos[0] -= 20; pos[1] += 20; + YAHOO.util.Dom.setStyle('email_alert_box', 'display', 'block'); + YAHOO.util.Dom.setXY('email_alert_box', pos); + this.on = true; + } + return false; + } +}); + // I love the global var tile_x = 0; var tile_y = 0; diff --git a/web/rss.cgi b/web/rss.cgi index 0fd0e8602..6c25b56fa 100755 --- a/web/rss.cgi +++ b/web/rss.cgi @@ -6,7 +6,7 @@ # Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. # Email: matthew@mysociety.org. WWW: http://www.mysociety.org # -# $Id: rss.cgi,v 1.2 2007-01-26 14:19:42 matthew Exp $ +# $Id: rss.cgi,v 1.3 2007-01-26 22:48:31 matthew Exp $ use strict; require 5.8.0; @@ -40,13 +40,13 @@ sub main { if ($type eq 'local_problems') { my $x = $q->param('x'); my $y = $q->param('y'); - my $qs = 'x='.$x.';y='.$y; + my $qs = 'x='.$x.';y='.$y; $x = ($x * 5000 / 31); $y = ($y * 5000 / 31); mySociety::Alert::generate_rss($type, $qs, $x, $y); } elsif ($type eq 'new_updates') { my $id = $q->param('id'); - my $qs = 'id='.$id; + my $qs = 'id='.$id; mySociety::Alert::generate_rss($type, $qs, $id); } elsif ($type eq 'new_problems') { mySociety::Alert::generate_rss($type, ''); diff --git a/web/xsl.xsl b/web/xsl.xsl index 1572193b4..85334ae18 100644 --- a/web/xsl.xsl +++ b/web/xsl.xsl @@ -36,14 +36,14 @@ href="https://secure.mysociety.org/cvstrac/dir?d=mysociety/services/TilMa">code< <h1>What is this page?</h1> <p>This is an RSS feed from the Neighbourhood Fix-It website. RSS feeds allow you to stay up to date with the latest changes and additions to the site. To subscribe to it, you will need a News Reader or other similar device. - <br/> - <a href="http://news.bbc.co.uk/1/hi/help/3223484.stm#whatisrss"><strong>Help</strong>, I don't know what a news reader is and still don't know what this is about (from the BBC).</a></p> + <br/> + <a href="http://news.bbc.co.uk/1/hi/help/3223484.stm#whatisrss"><strong>Help</strong>, I don't know what a news reader is and still don't know what this is about (from the BBC).</a></p> </div> <p>Below is the latest content available from this feed, <a href="#" class="item"><img height="16" hspace="5" vspace="0" border="0" width="16" alt="RSS News feeds" src="/i/feed.gif" title="RSS News feeds" /><xsl:value-of select="$title"/></a>.</p> - <div id="rss_items"><ul><xsl:apply-templates select="item"/></ul></div> + <div id="rss_items"><ul><xsl:apply-templates select="item"/></ul></div> <div id="rss_rhs"> <h2 style="margin:0">Subscribe to this feed</h2> <p>You can subscribe to this RSS feed in a number of ways, including the following:</p> @@ -54,11 +54,16 @@ href="https://secure.mysociety.org/cvstrac/dir?d=mysociety/services/TilMa">code< </ul> <h3>One-click subscriptions</h3> <p>If you use one of the following web-based News Readers, click on the appropriate button to subscribe to the RSS feed.</p> - <a href="http://www.bloglines.com/sub/{uri}"><img height="18" width="91" vspace="3" border="0" alt="bloglines" src="http://newsimg.bbc.co.uk/shared/bsp/xsl/rss/img/bloglines.gif" /></a><br /> - <a href="http://www.feedzilla.com/mini/default.asp?ref=bbc&url={uri}"><img height="22" width="93" vspace="3" border="0" alt="feedzilla" src="http://newsimg.bbc.co.uk/shared/bsp/xsl/rss/img/feedzilla.gif" /></a><br /> - <a href="http://add.my.yahoo.com/rss?url={uri}"><img height="17" width="91" vspace="3" border="0" alt="my yahoo" src="http://newsimg.bbc.co.uk/shared/bsp/xsl/rss/img/myyahoo.gif" /></a><br /> -<a href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url={uri}"><img height="17" width="91" vspace="3" border="0" alt="newsgator" src="http://newsimg.bbc.co.uk/shared/bsp/xsl/rss/img/newsgator.gif" /></a><br /> -<a href="http://www.live.com/?add={uri}"><img height="17" width="91" vspace="3" border="0" alt="Microsoft Live" src="http://newsimg.bbc.co.uk/shared/bsp/xsl/rss/img/windowslive.gif" /></a><br /> +<a href="http://www.bloglines.com/sub/{uri}"><img height="18" width="91" hspace="3" vspace="3" border="0" alt="bloglines" src="http://newsimg.bbc.co.uk/shared/bsp/xsl/rss/img/bloglines.gif" /></a> +<a href="http://www.feedzilla.com/mini/default.asp?ref=bbc&url={uri}"><img height="22" width="93" hspace="3" vspace="3" border="0" alt="feedzilla" src="http://newsimg.bbc.co.uk/shared/bsp/xsl/rss/img/feedzilla.gif" /></a> +<a href="http://add.my.yahoo.com/rss?url={uri}"><img height="17" width="91" hspace="3" vspace="3" border="0" alt="my yahoo" src="http://newsimg.bbc.co.uk/shared/bsp/xsl/rss/img/myyahoo.gif" /></a> +<a href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url={uri}"><img height="17" width="91" hspace="3" vspace="3" border="0" alt="newsgator" src="http://newsimg.bbc.co.uk/shared/bsp/xsl/rss/img/newsgator.gif" /></a> +<a href="http://www.live.com/?add={uri}"><img height="17" width="91" hspace="3" vspace="3" border="0" alt="Microsoft Live" src="http://newsimg.bbc.co.uk/shared/bsp/xsl/rss/img/windowslive.gif" /></a> +<a href="http://feeds.my.aol.com/add.jsp?url={uri}"><img hspace="3" src="http://o.aolcdn.com/myfeeds/html/vis/myaol_cta1.gif" alt="Add to My AOL" border="0"/></a> +<a href="http://www.rojo.com/add-subscription?resource={uri}"><img hspace="3" src="http://www.rojo.com/corporate/images/add-to-rojo.gif" alt="Subscribe in Rojo"/></a> +<a href="http://www.netvibes.com/subscribe.php?url={uri}"><img hspace="3" src="http://www.netvibes.com/img/add2netvibes.gif" alt="Add to netvibes" /></a> +<a href="http://fusion.google.com/add?feedurl={uri}"><img hspace="3" src="http://buttons.googlesyndication.com/fusion/add.gif" width="104" height="17" alt="Add to Google"/></a> +<a href="http://www.pageflakes.com/subscribe.aspx?url={uri}"><img hspace="3" src="http://www.pageflakes.com/subscribe2.gif" border="0"/></a> <ul> <li><a href="http://google.com/reader/view/feed/{uri}">Google Reader</a></li> |