// 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., 51 Franklin Street, Fifth Floor, Boston, MA
// 02110-1301, USA.
// http://www.uvm.edu/~ashawley/php/txt2rss.html
// $Id: txt2rss.php,v 1.3 2007/01/10 19:12:12 ashawley Exp ashawley $
/** RssLib
* Some auxillary RSS 1.0 functions.
*/
class RssLib
{
var $date_format = "Y-m-d\\Th:i:sO";
function setDateFormat($fmt)
{
$this->date_format;
} // end func setDateFormat
function formatDate($date)
{
return $this->w3cDateTimeFormat(date($this->date_format, $date));
} // end func formatDate
// http://www.w3.org/TR/NOTE-datetime
// Profile of ISO 8601
// http://www.iso.ch/iso/en/prods-services/popstds/datesandtime.html
function w3cDateTimeFormat($date)
{
return preg_replace('/\d\d$/', ':\0', $date);
} // end func w3cDateTimeFormat
} // end class RssLib
/** TextFileToRssItem
* Takes a text file RSS item data and can give the corresponding XML.
*/
class TextFileToRssItem extends RssLib
{
var $rss_lines = array('url' => "http://",
'title' => "No Title",
'description' => "No description",
'author' => "No Authors");
function getArray($file)
{
if (!file_exists($file)) {
return new Error("File does not exist", $file);
}
if (!is_readable($file)) {
return new Error("File is not readable", $file);
}
$date = filemtime($file);
$txt_file = new TextLineFile($this->rss_lines);
$data = $txt_file->parseFile($file);
if (Main::instance_of($data, 'Error')) {
return $data;
}
$data['file'] = $file;
$data['date'] = $this->formatDate($date);
return $data;
} // end func getArray
function getItem($file)
{
$id = $this->getArray($file);
if (Main::instance_of($id, 'Error')) {
return $id;
}
$xml = ''
. "\n" . ' '
. "\n" . ' ' . htmlspecialchars($id['title']) . ''
. "\n" . ' ' . htmlspecialchars($id['url']) . ''
. "\n" . ' ' . htmlspecialchars($id['description'])
. ''
. "\n" . ' ' . htmlspecialchars($id['author'])
. ''
. "\n" . ' ' . htmlspecialchars($id['date'])
. ''
. "\n" . '';
return array($id['url'] => $xml);
} // end func getItem
} // end class TextFileToRssItem
/** TextFilesToRss
* Convert a text file (rss.txt) and text files provided with
* addFile(), and create a complete RSS 1.0 XML file.
* See Main() below for example.
*/
class TextFilesToRss extends TextFileToRssItem
{
// See TextFileToRssItem#rss_line_item
var $rss_meta_lines = array('update_period' => "daily",
'update_frequency' => "1");
var $update_base = "1901-01-01T00:00+00:00";
var $output_file = "rss.xml";
var $meta_file = "rss.txt";
var $template_file = "template.xml";
var $files = array();
var $template = array(); // Lines of template file.
function TextFilesToRss($file)
{
$author_at = array_search('author', array_keys($this->rss_lines));
$this->rss_lines = array_splice($this->rss_lines, 0, $author_at);
$this->rss_meta_lines = array_merge($this->rss_lines,
$this->rss_meta_lines);
$this->setOutputFile($file);
} // end func TextFileToRss
function setOutputFile($file)
{
$this->output_file = $file;
} // end func setOutputFile
function setTemplate($file)
{
$this->template_file = $file;
} // end func setTemplate
function setMetaFile($file)
{
$this->meta_file = $file;
} // end func setMetaFile
function addFile($file)
{
$this->files[] = $file;
} // end func addFile
function getFiles()
{
return $this->files;
} // end func getFiles
function getXml()
{
$meta = $this->getMetaData();
if (Main::instance_of($meta, 'Error')) {
return $meta;
}
$rss = $meta;
$items = array();
foreach ($this->getFiles() as $f) {
$item = $this->processRssItemFile($f);
if (Main::instance_of($item, 'Error')) {
return $item;
}
list($url, $xml) = each($item);
if (isset($items[$url])) {
return new Error("Duplicate RSS item",
"$url in $f");
}
$items[$url] = $xml;
}
$rss['item'] = $items;
$list_items = array_keys($items);
$rss['li'] = array();
foreach ($list_items as $li) {
$rss['li'][] = '';
}
$rss['file'] = $this->output_file;
$rss['update_base'] = $this->update_base;
return $this->mergeWithTemplateFile($rss);
} // end func getXml
function processRssItemFile($file)
{
$txt2item = new TextFileToRssItem;
$item = $txt2item->getItem($file);
return $item;
} // end func processRssItemFile
function getMetaData()
{
$this->rss_lines = $this->rss_meta_lines;
$meta = $this->getArray($this->meta_file);
if (Main::instance_of($meta, 'Error')) {
return $meta;
}
return $meta;
} // end func getMetaData
function mergeWithTemplateFile($rss)
{
if (!file_exists($this->template_file)) {
return new Error("Template file not found",
$this->template_file);
}
if (!is_readable($this->template_file)) {
return new Error("Template file not readable",
$this->template_file);
}
$this->template = file($this->template_file);
$prefixes = array();
$prefixes = $this->templateFileTagPrefixes();
if (isset($rss['li'])) {
$prefix = isset($prefixes['li']) ? $prefixes['li'] : "";
$rss['li'] = join("\n$prefix", $rss['li']) . "\n";
}
if (isset($rss['item'])) {
$prefix = isset($prefixes['item']) ? $prefixes['item'] : "";
$rss['item'] = join("\n", $rss['item']);
$rss['item'] = str_replace("\n", "\n$prefix", $rss['item'])
. "\n";
}
$tags_2_re = create_function('$t', 'return "/%%".$t."%%\n?/";');
$tag_res = array_map($tags_2_re, array_keys($rss));
$xml = "";
foreach ($this->template as $line) {
$xml .= preg_replace($tag_res, array_values($rss), $line);
}
return $xml . "\n";
} // end func mergeWithTemplateFile
// Retrieve the prefixes for each tag.
// This work is only done for getting the indentation right.
// But it's worth it!
function templateFileTagPrefixes()
{
foreach ($this->template as $line) {
preg_match_all("/^(.*)%%[^%]*?%%/", $line, &$p);
while (isset($p[1]) && list($k, $prefix) = each($p[1])) {
preg_match_all("/%%([^%]*?)%%/", $line, &$m);
while (isset($m[1]) && list($k, $tag) = each($m[1])) {
$prefixes[$tag] = $prefix;
}
}
}
return $prefixes;
} // end func templateFileTagPrefixes
} // end class TextFilesToRss
/** TextLineFile
* Each line is presumed the value of an array.
* Provide an array of keys to be given each value.
*/
class TextLineFile
{
var $lines;
var $lines_key;
var $n_fields;
function TextLineFile($key = null)
{
$this->setLineKey($key);
} // end func TextLineFile
function setLineKey($key = null)
{
if (isset($key)) {
$this->lines_key = $key;
$n_fields = count($this->lines_key);
}
} // end func setLineKey
function nFields()
{
return $this->n_fields;
} // end func nFields
function parseFile($file)
{
$this->TextLineFile();
$lines = file($file);
if (count($lines) <= $this->nFields()) {
return new Error("Incomplete text file", $file);
}
$lines = array_map('chop', $lines);
return Main::array_combine(array_keys($this->lines_key),
$lines);
} // end func parseFile
} // end class TextLineFile
/** Error
* A simple error class.
*/
class Error {
var $message;
var $problem;
function Error($msg, $prob)
{
$this->message = $msg;
$this->problem = $prob;
} // end func Error
function text()
{
return $this->message . ": " . $this->problem . "\n";
} // end func text
} // end class Error
/** Main
* The main routine, and some functions needed for PHP 4.0 compatability.
*/
class Main
{
function main($glob, $out)
{
$rss = new TextFilesToRss($out);
$rss->setMetaFile("rss.txt");
$rss->setTemplate("rss.xml");
$files = array_reverse(glob($glob));
foreach ($files as $file) {
$rss->addFile($file);
}
$str = $rss->getXml();
if (Main::instance_of($str, 'Error')) {
if (strpos(($http = $_SERVER['SERVER_PROTOCOL']), 'HTTP') !== false) {
header("$http 500 Internal Server Error");
}
header("Content-type: text/plain");
print $str->text();
} else {
header("Content-type: text/xml; charset=utf-8");
print $str;
}
} // end func main
function instance_of($a, $b)
{
if (phpversion() >= 5) {
return $a instanceof $b;
} // else
return (is_object($a) && is_string($b)
&& get_class($a) === strtolower($b))
|| (is_object($b) && is_string($a)
&& get_class($b) === strtolower($a));
} // end func instance_of
function array_combine($keys, $values)
{
$n = array(); // New array.
foreach ($keys as $k) {
list($vkey, $v) = each($values);
$n[$k] = $v;
}
return $n;
} // end func array_combine
} // end class Main
$main = new Main("event*.txt", "events.xml");
?>