Update:Remove rss feed dependencies add node-xml lib

This commit is contained in:
advplyr 2022-06-07 19:25:14 -05:00
parent 46fc89e247
commit 03bffb725a
9 changed files with 556 additions and 105 deletions

193
server/libs/rss/index.js Normal file
View file

@ -0,0 +1,193 @@
// node-rss
// SOURCE: https://github.com/dylang/node-rss
// LICENSE: https://creativecommons.org/licenses/by-sa/4.0/
'use strict';
var mime = require('mime-types');
var xml = require('../xml');
var fs = require('fs');
function ifTruePush(bool, array, data) {
if (bool) {
array.push(data);
}
}
function ifTruePushArray(bool, array, dataArray) {
if (!bool) {
return;
}
dataArray.forEach(function (item) {
ifTruePush(item, array, item);
});
}
function getSize(filename) {
if (typeof fs === 'undefined') {
return 0;
}
return fs.statSync(filename).size;
}
function generateXML(data) {
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' });
// image_url set?
if (data.image_url) {
channel.push({ image: [{ url: data.image_url }, { title: data.title }, { link: data.site_url }] });
}
channel.push({ generator: data.generator });
channel.push({ lastBuildDate: new Date().toUTCString() });
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.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.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 } });
});
}
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 } });
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(item.author || data.author, item_values, { 'dc:creator': { _cdata: 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
data.geoRSS = data.geoRSS || (item.lat && item.long);
ifTruePush(item.lat, item_values, { 'geo:lat': item.lat });
ifTruePush(item.long, item_values, { 'geo:long': item.long });
if (item.enclosure && item.enclosure.url) {
if (item.enclosure.file) {
item_values.push({
enclosure: {
_attr: {
url: item.enclosure.url,
length: item.enclosure.size || getSize(item.enclosure.file),
type: item.enclosure.type || mime.lookup(item.enclosure.file)
}
}
});
} else {
item_values.push({
enclosure: {
_attr: {
url: item.enclosure.url,
length: item.enclosure.size || 0,
type: item.enclosure.type || mime.lookup(item.enclosure.url)
}
}
});
}
}
ifTruePushArray(item.custom_elements, item_values, item.custom_elements);
channel.push({ item: item_values });
});
//set up the attributes for the RSS feed.
var _attr = {
'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'
};
Object.keys(data.custom_namespaces).forEach(function (name) {
_attr['xmlns:' + name] = data.custom_namespaces[name];
});
//only add namespace if GeoRSS is true
if (data.geoRSS) {
_attr['xmlns:geo'] = 'http://www.w3.org/2003/01/geo/wgs84_pos#';
}
return {
rss: [
{ _attr: _attr },
{ channel: channel }
]
};
}
function RSS(options, items) {
options = options || {};
this.title = options.title || 'Untitled RSS Feed';
this.description = options.description || '';
this.generator = options.generator || 'RSS for Node';
this.feed_url = options.feed_url;
this.site_url = options.site_url;
this.image_url = options.image_url;
this.author = options.author;
this.categories = options.categories;
this.pubDate = options.pubDate;
this.hub = options.hub;
this.docs = options.docs;
this.copyright = options.copyright;
this.language = options.language;
this.managingEditor = options.managingEditor;
this.webMaster = options.webMaster;
this.ttl = options.ttl;
//option to return feed as GeoRSS is set automatically if feed.lat/long is used
this.geoRSS = options.geoRSS || false;
this.custom_namespaces = options.custom_namespaces || {};
this.custom_elements = options.custom_elements || [];
this.items = items || [];
this.item = function (options) {
options = options || {};
var item = {
title: options.title || 'No title',
description: options.description || '',
url: options.url,
guid: options.guid,
categories: options.categories || [],
author: options.author,
date: options.date,
lat: options.lat,
long: options.long,
enclosure: options.enclosure || false,
custom_elements: options.custom_elements || []
};
this.items.push(item);
return this;
};
this.xml = function (indent) {
return '<?xml version="1.0" encoding="UTF-8"?>' +
xml(generateXML(this), indent);
};
}
module.exports = RSS;

View file

@ -0,0 +1,17 @@
var XML_CHARACTER_MAP = {
'&': '&amp;',
'"': '&quot;',
"'": '&apos;',
'<': '&lt;',
'>': '&gt;'
};
function escapeForXML(string) {
return string && string.replace
? string.replace(/([&"<>'])/g, function (str, item) {
return XML_CHARACTER_MAP[item];
})
: string;
}
module.exports = escapeForXML;

286
server/libs/xml/index.js Normal file
View file

@ -0,0 +1,286 @@
// node-xml
// SOURCE: https://github.com/dylang/node-xml
// LICENSE: https://github.com/dylang/node-xml/blob/master/LICENSE
var escapeForXML = require('./escapeForXML');
var Stream = require('stream').Stream;
var DEFAULT_INDENT = ' ';
function xml(input, options) {
if (typeof options !== 'object') {
options = {
indent: options
};
}
var stream = options.stream ? new Stream() : null,
output = "",
interrupted = false,
indent = !options.indent ? ''
: options.indent === true ? DEFAULT_INDENT
: options.indent,
instant = true;
function delay(func) {
if (!instant) {
func();
} else {
process.nextTick(func);
}
}
function append(interrupt, out) {
if (out !== undefined) {
output += out;
}
if (interrupt && !interrupted) {
stream = stream || new Stream();
interrupted = true;
}
if (interrupt && interrupted) {
var data = output;
delay(function () { stream.emit('data', data) });
output = "";
}
}
function add(value, last) {
format(append, resolve(value, indent, indent ? 1 : 0), last);
}
function end() {
if (stream) {
var data = output;
delay(function () {
stream.emit('data', data);
stream.emit('end');
stream.readable = false;
stream.emit('close');
});
}
}
function addXmlDeclaration(declaration) {
var encoding = declaration.encoding || 'UTF-8',
attr = { version: '1.0', encoding: encoding };
if (declaration.standalone) {
attr.standalone = declaration.standalone
}
add({ '?xml': { _attr: attr } });
output = output.replace('/>', '?>');
}
// disable delay delayed
delay(function () { instant = false });
if (options.declaration) {
addXmlDeclaration(options.declaration);
}
if (input && input.forEach) {
input.forEach(function (value, i) {
var last;
if (i + 1 === input.length)
last = end;
add(value, last);
});
} else {
add(input, end);
}
if (stream) {
stream.readable = true;
return stream;
}
return output;
}
function element(/*input, …*/) {
var input = Array.prototype.slice.call(arguments),
self = {
_elem: resolve(input)
};
self.push = function (input) {
if (!this.append) {
throw new Error("not assigned to a parent!");
}
var that = this;
var indent = this._elem.indent;
format(this.append, resolve(
input, indent, this._elem.icount + (indent ? 1 : 0)),
function () { that.append(true) });
};
self.close = function (input) {
if (input !== undefined) {
this.push(input);
}
if (this.end) {
this.end();
}
};
return self;
}
function create_indent(character, count) {
return (new Array(count || 0).join(character || ''))
}
function resolve(data, indent, indent_count) {
indent_count = indent_count || 0;
var indent_spaces = create_indent(indent, indent_count);
var name;
var values = data;
var interrupt = false;
if (typeof data === 'object') {
var keys = Object.keys(data);
name = keys[0];
values = data[name];
if (values && values._elem) {
values._elem.name = name;
values._elem.icount = indent_count;
values._elem.indent = indent;
values._elem.indents = indent_spaces;
values._elem.interrupt = values;
return values._elem;
}
}
var attributes = [],
content = [];
var isStringContent;
function get_attributes(obj) {
var keys = Object.keys(obj);
keys.forEach(function (key) {
attributes.push(attribute(key, obj[key]));
});
}
switch (typeof values) {
case 'object':
if (values === null) break;
if (values._attr) {
get_attributes(values._attr);
}
if (values._cdata) {
content.push(
('<![CDATA[' + values._cdata).replace(/\]\]>/g, ']]]]><![CDATA[>') + ']]>'
);
}
if (values.forEach) {
isStringContent = false;
content.push('');
values.forEach(function (value) {
if (typeof value == 'object') {
var _name = Object.keys(value)[0];
if (_name == '_attr') {
get_attributes(value._attr);
} else {
content.push(resolve(
value, indent, indent_count + 1));
}
} else {
//string
content.pop();
isStringContent = true;
content.push(escapeForXML(value));
}
});
if (!isStringContent) {
content.push('');
}
}
break;
default:
//string
content.push(escapeForXML(values));
}
return {
name: name,
interrupt: interrupt,
attributes: attributes,
content: content,
icount: indent_count,
indents: indent_spaces,
indent: indent
};
}
function format(append, elem, end) {
if (typeof elem != 'object') {
return append(false, elem);
}
var len = elem.interrupt ? 1 : elem.content.length;
function proceed() {
while (elem.content.length) {
var value = elem.content.shift();
if (value === undefined) continue;
if (interrupt(value)) return;
format(append, value);
}
append(false, (len > 1 ? elem.indents : '')
+ (elem.name ? '</' + elem.name + '>' : '')
+ (elem.indent && !end ? '\n' : ''));
if (end) {
end();
}
}
function interrupt(value) {
if (value.interrupt) {
value.interrupt.append = append;
value.interrupt.end = proceed;
value.interrupt = false;
append(true);
return true;
}
return false;
}
append(false, elem.indents
+ (elem.name ? '<' + elem.name : '')
+ (elem.attributes.length ? ' ' + elem.attributes.join(' ') : '')
+ (len ? (elem.name ? '>' : '') : (elem.name ? '/>' : ''))
+ (elem.indent && len > 1 ? '\n' : ''));
if (!len) {
return append(false, elem.indent ? '\n' : '');
}
if (!interrupt(elem)) {
proceed();
}
}
function attribute(key, value) {
return key + '=' + '"' + escapeForXML(value) + '"';
}
module.exports = xml;
module.exports.element = module.exports.Element = element;