*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/**
* site: http://danamlund.dk/bmsrr.html
*/
/**
* This is a self-hosted rss-reader. It reads a google reader
* subscriptions.xml file, and generate a html file of the news
* stories in the RSS or Atom feeds from subscriptions.xml. You can
* click a news story to clear all stories older than the clicked
* item. Updating fetches stories newer than the most recently clicked
* clear-older-than story.
*
* This script generates 2 files: bmsrr_data.txt and bmsrr.html. The
* script needs to have write permission to these two files.
*
* This is a simple reader, it cannot save stories. This means you
* cannot "star" specific stories, and only stories avilable in the
* feed at the time of generation are added (most feeds remove older
* stories).
*/
error_reporting(0);
ini_set("display_errors", "0");
set_time_limit(60 * 5);
date_default_timezone_set('Europe/Copenhagen');
$bmsrr_conf =
array("google reader subscriptions.xml" => "subscriptions.xml",
"data file" => "./bmsrr_data.txt",
"item sorter" => "itemSorterOldestFirst",
"item changer" => "doNotChangeItem",
"output file" => "bmsrr.html",
"output header" =>
'
BMSRR
Showing {{itemsAmount}} items after {{showItemsAfter}}
update
',
"output footer" =>
'
',
"output item" =>
'
From {{itemFeedTitle}} on {{itemPubDate}}
(clear older than this)
{{itemDescription}}
'
);
function doNotChangeItem($item) {
return $item;
}
makeRssReader($bmsrr_conf);
//// config that uses lazyload.js
// needs (in same dir as bmsrr.php)
// 1. jquery.js @ http://jquery.com/
// 2. jquery.lazyload.js @ https://github.com/jquery/plugins.jquery.com
// 3. grey.gif @ http://danamlund.dk/files/grey.gif
// To install comment out "makeRssReader($bmsrr_conf);" and uncomment
// "makeRssReader($bmsrr_lazyload_conf);"
$bmsrr_lazyload_conf =
array("google reader subscriptions.xml" => "subscriptions.xml",
"data file" => "./bmsrr_data.txt",
"item sorter" => "itemSorterOldestFirst",
"item changer" => "changeItemLazyLoad",
"output file" => "bmsrr.html",
"output header" =>
'
Basic RSS reader
Showing {{itemsAmount}} items after {{showItemsAfter}}
update
',
"output footer" =>
'
',
"output item" =>
'
From {{itemFeedTitle}} on {{itemPubDate}}
(clear older than this)
{{itemDescription}}
'
);
// makeRssReader($bmsrr_lazyload_conf);
function itemSorterOldestFirst($a, $b) {
return $a["pubDate"] - $b["pubDate"];
}
function changeItemLazyLoad($item) {
$dom = new DOMDocument("1.0", "UTF0-8");
$dom->loadHTML('' . $item["description"]);
foreach ($dom->getElementsByTagName("img") as $img) {
$dataOriginal = $dom->createAttribute("data-original");
$img->appendChild($dataOriginal);
$img->setAttribute("data-original", $img->getAttribute("src"));
$img->setAttribute("src", "grey.gif");
}
$item["description"] = $dom->saveHTML($dom);
$item["description"] =
preg_replace('~<(?:!DOCTYPE|/?(?:html|head|body))[^>]*>\s*~i', '',
$item["description"]);
return $item;
}
// Code from down here
function makeRssReader($conf) {
printf("\r\n");
$fp = fopen($conf["output file"], "w");
if ($fp == null) {
pln("Couldn't write to '%s'", $conf["output file"]);
} else {
if (flock($fp, LOCK_EX)) {
if (isset($_REQUEST["clearolder"])) {
$clearOlderThan = intval($_REQUEST["clearolder"]);
$out = file_put_contents($conf["data file"],
serialize($clearOlderThan));
if ($out === false) {
pln("Could not write to '%s'", $conf["data file"]);
}
}
if (!file_exists($conf["data file"])) {
$showItemsAfter = 0;
} else {
$showItemsAfter = unserialize(file_get_contents($conf["data file"]));
}
$feedUrls = getSubscriptions($conf["google reader subscriptions.xml"]);
if ($feedUrls == null) {
pln("Couldn't read '%s'", $conf["google reader subscriptions.xml"]);
} else {
$validItems = array();
foreach ($feedUrls as $feedUrl) {
$items = getRss($feedUrl);
if($items === null) {
pln("Problem fetching '%s'", $feedUrl);
continue;
}
$newItemsFromFeed = 0;
foreach ($items as $item) {
if ($item["pubDate"] > $showItemsAfter) {
$validItems[] = call_user_func($conf["item changer"], $item);
$newItemsFromFeed++;
}
}
pln("Fetched %d new items from %s", $newItemsFromFeed, $feedUrl);
flush();
}
usort($validItems, $conf["item sorter"]);
fwrite($fp, templateText($conf["output header"],
array("showItemsAfter"
=> dateString($showItemsAfter),
"itemsAmount" => count($validItems),
"updateUrl" => $_SERVER["PHP_SELF"])));
foreach ($validItems as $item) {
$clearOlderThanUrl =
$_SERVER["PHP_SELF"]."?clearolder=".$item["pubDate"];
fwrite($fp,
templateText($conf["output item"],
array("itemLink" => $item["link"],
"itemTitle" => $item["title"],
"itemFeedTitle" => $item["feedTitle"],
"itemPubDate" => dateString($item["pubDate"]),
"itemDescription" => $item["description"],
"clearOlderThanUrl" => $clearOlderThanUrl)));
}
fwrite($fp, $conf["output footer"]);
}
flock($fp, LOCK_UN);
} else {
pln("Could not get file lock");
}
fclose($fp);
printf("
\r\n");
pln('rss reader', $conf["output file"]);
}
printf("\r\n");
}
function dateString($unixtime) {
return date(DATE_RFC850, $unixtime);
}
function pln() {
$args = func_get_args();
$string = call_user_func_array('sprintf', $args);
printf("%s
\r\n", $string);
}
function templateText($html, $replaces) {
$from = array();
$to = array();
foreach ($replaces as $key => $value) {
$from[] = "{{" . $key . "}}";
$to[] = $value;
}
return str_replace($from, $to, $html);
}
function getSubscriptions($filename) {
$subscriptionsXml = simplexml_load_file($filename);
if ($subscriptionsXml == null) {
return null;
}
$rssUrls = array();
foreach ($subscriptionsXml->body->outline as $subscription) {
$rssUrls[] = (string) $subscription["xmlUrl"];
}
return $rssUrls;
}
function getRss($url) {
$rssXml = simplexml_load_file($url);
if (!$rssXml) {
return null;
}
$items = array();
if ($rssXml->entry) {
// atom
foreach ($rssXml->entry as $entry) {
$description = trim((string) $entry->content
? $entry->content : $entry->summary);
$items[] = array("feedTitle" => trim((string)$rssXml->title),
"title" => trim((string) $entry->title),
"description" => $description,
"link" => trim((string) $entry->link["href"]),
"guid" => trim((string) $entry->id),
"pubDate" => strtotime(trim((string) $entry->updated)));
}
} else if ($rssXml->channel->item) {
// rss
foreach ($rssXml->channel->item as $item) {
$items[] = array("feedTitle" => trim((string) $rssXml->channel->title),
"title" => trim((string) $item->title),
"description" => trim((string) $item->description),
"link" => trim((string) $item->link),
"guid" => trim((string) $item->guid),
"pubDate" => strtotime(trim((string) $item->pubDate)));
}
} else {
return null;
}
return $items;
}