Skip to content
Closed
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions examples/simple.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
var RSS = require('../lib/rss');

/* let's create an rss feed */
var feed = new RSS({
title: 'title',
description: 'description',
feed_url: 'http://example.com/rss.xml',
site_url: 'http://example.com',
image_url: 'http://example.com/icon.png',
docs: 'http://example.com/rss/docs.html',
managingEditor: 'Dylan Greene',
webMaster: 'Dylan Greene',
copyright: '2013 Dylan Greene',
language: 'en',
categories: ['Category 1','Category 2','Category 3'],
pubDate: 'May 20, 2012 04:00:00 GMT',
ttl: '60',
no_cdata_fields: ['title', 'category'],
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example of specifying a field for no CDATA wrapping

customNamespaces: {
'itunes': 'http://www.itunes.com/dtds/podcast-1.0.dtd'
},
custom_elements: [
{'itunes:subtitle': 'A show about everything'},
{'itunes:author': 'John Doe'},
{'itunes:summary': 'All About Everything is a show about everything. Each week we dive into any subject known to man and talk about it as much as we can. Look for our podcast in the Podcasts app or in the iTunes Store'},
{'itunes:owner': [
{'itunes:name': 'John Doe'},
{'itunes:email': '[email protected]'}
]},
{'itunes:image': {
_attr: {
href: 'http://example.com/podcasts/everything/AllAboutEverything.jpg'
}
}},
{'itunes:category': [
{_attr: {
text: 'Technology'
}},
{'itunes:category': {
_attr: {
text: 'Gadgets'
}
}}
]}
]
});

/* loop over data and add to feed */
feed.item({
title: 'item title & fun',
description: 'use this for the content. It can include html.',
url: 'http://example.com/article4?this&that', // link to the item
guid: '1123', // optional - defaults to url
categories: ['Category 1','Category 2','Category 3','Category 4'], // optional - array of item categories
author: 'Guest Author', // optional - defaults to feed author property
date: 'May 27, 2012', // any format that js Date can parse.
lat: 33.417974, //optional latitude field for GeoRSS
long: -111.933231, //optional longitude field for GeoRSS
enclosure: {url:'https://www.google.com/images/srpr/logo11w.png'},
// enclosure: {file:'path-to-file'}, // optional enclosure
custom_elements: [
{'itunes:author': 'John Doe'},
{'itunes:subtitle': 'A short primer on table spices'},
{'itunes:image': {
_attr: {
href: 'http://example.com/podcasts/everything/AllAboutEverything/Episode1.jpg'
}
}},
{'itunes:duration': '7:04'}
]
});

// cache the xml to send to clients
var xml = feed.xml("\t");

console.log(xml);
81 changes: 62 additions & 19 deletions lib/rss.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
'use strict';

var xml = require('xml'),
mime = require('mime'),
fs = require('fs');
var xml = require('xml'),
mime = require('mime'),
fs = require('fs');

function ifTruePush(bool, array, data) {
if (bool) {
Expand All @@ -21,11 +21,27 @@ function ifTruePushArray(bool, array, dataArray) {
}

function generateXML (data){
// Field names that should not be CDATA wrapped
var no_cdata_fields = data.no_cdata_fields || [];

// Handle formatting of CDATA-able output
function output(field_name, value) {
if (!value) {
return;
}
var ret_value = {};
if (no_cdata_fields.indexOf(field_name) !== -1) {
ret_value[field_name] = value;
} else {
ret_value[field_name] = { _cdata: value }; // CDATA
}
return ret_value;
}

var channel = [];
channel.push({ title: { _cdata: data.title } });
channel.push({ description: { _cdata: data.description || data.title } });
channel.push({ link: data.site_url || 'http://github.com/dylang/node-rss' });
channel.push( output('title', data.title) );
channel.push( output('description', (data.description || data.title)) );
channel.push({ link: data.site_url || 'http://github.com/dylang/node-rss' });
// image_url set?
if (data.image_url) {
channel.push({ image: [ {url: data.image_url}, {title: data.title}, {link: data.site_url} ] });
Expand All @@ -34,37 +50,35 @@ function generateXML (data){
channel.push({ lastBuildDate: new Date().toGMTString() });

ifTruePush(data.feed_url, channel, { 'atom:link': { _attr: { href: data.feed_url, rel: 'self', type: 'application/rss+xml' } } });
ifTruePush(data.author, channel, { 'author': { _cdata: data.author } });
ifTruePush(data.author, channel, output('author', data.author));
ifTruePush(data.pubDate, channel, { 'pubDate': new Date(data.pubDate).toGMTString() });
ifTruePush(data.copyright, channel, { 'copyright': { _cdata: data.copyright } });
ifTruePush(data.language, channel, { 'language': { _cdata: data.language } });
ifTruePush(data.managingEditor, channel, { 'managingEditor': { _cdata: data.managingEditor } });
ifTruePush(data.webMaster, channel, { 'webMaster': { _cdata: data.webMaster } });
ifTruePush(data.copyright, channel, output('copyright', data.copyright) );
ifTruePush(data.language, channel, output('language', data.language) );
ifTruePush(data.managingEditor, channel, output('managingEditor', data.managingEditor) );
ifTruePush(data.webMaster, channel, output('webMaster', data.webMaster) );
ifTruePush(data.docs, channel, { 'docs': data.docs });
ifTruePush(data.ttl, channel, { 'ttl': data.ttl });
ifTruePush(data.hub, channel, { 'atom:link': { _attr: { href: data.hub, rel: 'hub' } } });

if (data.categories) {
data.categories.forEach(function(category) {
ifTruePush(category, channel, { category: { _cdata: category } });
ifTruePush(category, channel, output('category', category));
});
}

ifTruePushArray(data.custom_elements, channel, data.custom_elements);

data.items.forEach(function(item) {
var item_values = [
{ title: { _cdata: item.title } }
];
ifTruePush(item.description, item_values, { description: { _cdata: item.description } });
var item_values = [ output('title', item.title) ];
ifTruePush(item.description, item_values, output('description', item.description));
ifTruePush(item.url, item_values, { link: item.url });
ifTruePush(item.link || item.guid || item.title, item_values, { guid: [ { _attr: { isPermaLink: !item.guid && !!item.url } }, item.guid || item.url || item.title ] });

item.categories.forEach(function(category) {
ifTruePush(category, item_values, { category: { _cdata: category } });
ifTruePush(category, item_values, output('category', category));
});

ifTruePush(item.author || data.author, item_values, { 'dc:creator': { _cdata: item.author || data.author } });
ifTruePush(item.author || data.author, item_values, output('dc:creator', (item.author || data.author)) );
ifTruePush(item.date, item_values, { pubDate: new Date(item.date).toGMTString() });

//Set GeoRSS to true if lat and long are set
Expand Down Expand Up @@ -150,7 +164,8 @@ function RSS (options, items) {
this.geoRSS = options.geoRSS || false;
this.custom_namespaces = options.custom_namespaces || {};
this.custom_elements = options.custom_elements || [];
this.items = items || [];
this.no_cdata_fields = options.no_cdata_fields || [];
this.items = []; // placeholder, see below for passed in items

this.item = function (options) {
options = options || {};
Expand All @@ -172,10 +187,38 @@ function RSS (options, items) {
return this;
};

// replace items
this.replace_items = function(items) {
items = items || [];
if (items && items.length > 0) {
this.items = [];
return this.concat_items(items);
} else {
return this;
}
};

// Concat new items to this.items
this.concat_items = function (items) {
var self = this;
items = items || [];
if (items && items.length > 0) {
items.forEach(function(item){
self.item(item);
});
}
return this;
};

this.xml = function(indent) {
return '<?xml version="1.0" encoding="UTF-8"?>\n' +
xml(generateXML(this), indent);
};

// handle passed in items on obj creation using item()
if (items) {
return this.replace_items(items);
}
}

module.exports = RSS;
92 changes: 70 additions & 22 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,6 @@

> RSS feed generator. Add RSS feeds to any project. Supports enclosures and GeoRSS.








### Usage
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The readme file is generated.

grunt pre-publish to re-generate it.

The files it uses are in /templates/readme.


#### Create a new feed
Expand Down Expand Up @@ -38,18 +31,13 @@ var feed = new RSS(feedOptions);
* `hub` _optional_ **PubSubHubbub hub url** Where is the PubSubHub hub located.
* `custom_namespaces` _optional_ **object** Put additional namespaces in <rss> element (without 'xmlns:' prefix)
* `custom_elements` _optional_ **array** Put additional elements in the feed (node-xml syntax)
* `no_cdata_fields` _optional_ **array** Field names that shouldn't be wrapped with CDATA tag. The data will be escaped for XML. Default is to wrap with CDATA. You should only use this to work around problematic XML clients.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make this change in templates/readme/usage.md


#### Add items to a feed

An item can be used for a blog entry, project update, log entry, etc. Your RSS feed
can have any number of items. Most feeds use 20 or fewer items.

```js
feed.item(itemOptions);
```

##### itemOptions

* `title` **string** Title of this particular item.
* `description` **string** Content for the item. Can contain html but link and image urls must be absolute path including hostname.
* `url` **url string** Url to the item. This could be a blog entry.
Expand All @@ -67,7 +55,20 @@ feed.item(itemOptions);
* `long` _optional_ **number** The longitude coordinate of the item.
* `custom_elements` _optional_ **array** Put additional elements in the item (node-xml syntax)

##### Feed XML
##### Add single item
```js
feed.item(itemOptions);
```
##### Concatenate an array of items
```js
feed.concat_items(arrayOfItemOptions);
```
##### Replace items with a new array of items
```js
feed.replace_items(arrayOfItemOptions);
```

#### Feed XML

```js
var xml = feed.xml(indent);
Expand All @@ -80,11 +81,12 @@ This returns the XML as a string.


### Example Usage
(examples/simple.js)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make these changes in templates/readme/examples.md.


```js
var RSS = require('rss');
var RSS = require('../lib/rss');

/* lets create an rss feed */
/* let's create an rss feed */
var feed = new RSS({
title: 'title',
description: 'description',
Expand All @@ -99,10 +101,11 @@ var feed = new RSS({
categories: ['Category 1','Category 2','Category 3'],
pubDate: 'May 20, 2012 04:00:00 GMT',
ttl: '60',
customNamespaces: {
no_cdata_fields: ['title', 'category'],
custom_namespaces: {
'itunes': 'http://www.itunes.com/dtds/podcast-1.0.dtd'
},
custom: [
custom_elements: [
{'itunes:subtitle': 'A show about everything'},
{'itunes:author': 'John Doe'},
{'itunes:summary': 'All About Everything is a show about everything. Each week we dive into any subject known to man and talk about it as much as we can. Look for our podcast in the Podcasts app or in the iTunes Store'},
Expand All @@ -128,7 +131,6 @@ var feed = new RSS({
]
});

/* loop over data and add to feed */
feed.item({
title: 'item title',
description: 'use this for the content. It can include html.',
Expand All @@ -139,8 +141,9 @@ feed.item({
date: 'May 27, 2012', // any format that js Date can parse.
lat: 33.417974, //optional latitude field for GeoRSS
long: -111.933231, //optional longitude field for GeoRSS
enclosure: {url:'...', file:'path-to-file'}, // optional enclosure
custom: [
enclosure: {url:'https://www.google.com/images/srpr/logo11w.png'},
// enclosure: {file:'path-to-file'}, // optional enclosure
custom_elements: [
{'itunes:author': 'John Doe'},
{'itunes:subtitle': 'A short primer on table spices'},
{'itunes:image': {
Expand All @@ -153,7 +156,52 @@ feed.item({
});

// cache the xml to send to clients
var xml = feed.xml();
var xml = feed.xml("\t");
console.log(xml);
```
#### XML Output:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#">
<channel>
<title>title</title>
<description><![CDATA[description]]></description>
<link>http://example.com</link>
<image>
<url>http://example.com/icon.png</url>
<title>title</title>
<link>http://example.com</link>
</image>
<generator>RSS for Node</generator>
<lastBuildDate>Sat, 13 Dec 2014 01:44:19 GMT</lastBuildDate>
<atom:link href="http://example.com/rss.xml" rel="self" type="application/rss+xml"/>
<pubDate>Sun, 20 May 2012 04:00:00 GMT</pubDate>
<copyright><![CDATA[2013 Dylan Greene]]></copyright>
<language><![CDATA[en]]></language>
<managingEditor><![CDATA[Dylan Greene]]></managingEditor>
<webMaster><![CDATA[Dylan Greene]]></webMaster>
<docs>http://example.com/rss/docs.html</docs>
<ttl>60</ttl>
<category>Category 1</category>
<category>Category 2</category>
<category>Category 3</category>
<item>
<title>item title</title>
<description><![CDATA[use this for the content. It can include html.]]></description>
<link>http://example.com/article4?this&amp;that</link>
<guid isPermaLink="false">1123</guid>
<category>Category 1</category>
<category>Category 2</category>
<category>Category 3</category>
<category>Category 4</category>
<dc:creator><![CDATA[Guest Author]]></dc:creator>
<pubDate>Sun, 27 May 2012 07:00:00 GMT</pubDate>
<geo:lat>33.417974</geo:lat>
<geo:long>-111.933231</geo:long>
<enclosure url="https://www.google.com/images/srpr/logo11w.png" length="0" type="image/png"/>
</item>
</channel>
</rss>
```


Expand Down
Loading