--- /dev/null
+function(head, req) {
+ var ddoc = this;
+ var Mustache = require("lib/mustache");
+ var List = require("vendor/couchapp/lib/list");
+ var path = require("vendor/couchapp/lib/path").init(req);
+ var Atom = require("vendor/couchapp/lib/atom");
+
+ var within = 0;
+ if ( 'within' in req.query ) {
+ within = Number(req.query['within']);
+ log("within: " + within);
+ } else {
+ within = 60 * 60 * 3; // within three hours;
+ }
+ var ts = (new Date()).getTime()/1000 - within; // convert milliseconds to seconds
+
+ var indexPath = path.list('nodelist','node-status',{descending:true, limit:10});
+ var feedPath = path.list('nodelist','node-status',{descending:true, limit:10, format:"atom"});
+ var commentsFeed = path.list('comments','comments',{descending:true, limit:10, format:"atom"});
+
+
+ var path_parts = req.path;
+ var EQ=1;
+ var NE=2;
+ var LE=3;
+ var GE=4;
+ var MATCH=5;
+ var AND=6;
+ var OR=7;
+
+ function check_field(query, field, def) {
+ var ret = def;
+ if ( field in query ) {
+ ret = query[field];
+ log(field + ": " + ret);
+ }
+ return ret;
+ }
+
+ function tokenize(sf) {
+ var ret = null;
+ var fields = null;
+ var f_eq = /==/;
+ var f_ne = /!=/;
+ var f_le = /<=|%3C=|%3c=/;
+ var f_ge = />=|%3E=|%3e=/;
+ var f_match = /=~/;
+
+ if ( sf.match(f_eq) ) {
+ fields = sf.split("==");
+ ret = [fields[0], fields[1], EQ];
+ } else if ( sf.match(f_ne) ) {
+ fields = sf.split("!=");
+ ret = [fields[0], fields[1], NE] ;
+ } else if ( sf.match(f_le) ) {
+ fields = sf.split("<=");
+ ret = [fields[0], Number(fields[1]), LE] ;
+ } else if ( sf.match(f_ge) ) {
+ fields = sf.split(">=");
+ ret = [fields[0], Number(fields[1]), GE] ;
+ } else if ( sf.match(f_match) ) {
+ fields = sf.split("=~");
+ ret = [fields[0], new RegExp(fields[1]), MATCH] ;
+ }
+ return ret;
+ }
+
+
+ function pf(x) { return 0; };
+ function parse_filter (sfilter) {
+ var f_and_or = /AND|%%|&&|\|\|/;
+ var f_eq = /==/;
+ var f_ne = /!=/;
+ var f_le = /<=|%3C=|%3c=/;
+ var f_ge = />=|%3E=|%3e=/;
+ var f_match = /=~/;
+ var eq = {};
+ var kv_list = [ ];
+ var pfilter = [ ] ;
+
+ log("sfilter: " + sfilter);
+ if ( sfilter.match(f_and_or) )
+ {
+ sfilter_list = sfilter.split(f_and_or);
+ log("sfilter_list: " + sfilter_list);
+ } else {
+ sfilter_list = [ sfilter ] ;
+ }
+
+ for ( var i=0; i < sfilter_list.length ; i++ )
+ {
+ sf = sfilter_list[i];
+ t = tokenize(sf);
+ kv_list[i] = t;
+
+ if ( sf.match(f_eq) ) {
+ var fields = sf.split("==");
+ eq[fields[0]] = [fields[1], EQ] ;
+ } else if ( sf.match(f_ne) ) {
+ var fields = sf.split("!=");
+ eq[fields[0]] = [fields[1], NE] ;
+ } else if ( sf.match(f_le) ) {
+ var fields = sf.split("<=");
+ eq[fields[0]] = [Number(fields[1]), LE] ;
+ } else if ( sf.match(f_ge) ) {
+ var fields = sf.split(">=");
+ eq[fields[0]] = [Number(fields[1]), GE] ;
+ } else if ( sf.match(f_match) ) {
+ var fields = sf.split("=~");
+ eq[fields[0]] = [new RegExp(fields[1]), MATCH] ;
+ }
+ }
+ pfilter = [ eq ];
+ //log("kv_list: " + JSON.stringify(kv_list));
+ //pfilter = [ kv_list, AND ];
+
+ return pfilter;
+ }
+
+ function filter_matches (row, pfilter) {
+ var result = true;
+ if ( pfilter != null ) {
+ for ( var i=0; i < pfilter.length ; i++ )
+ {
+ //log("starting filter_matches");
+
+ for ( var prop in pfilter[i] ) {
+ f = pfilter[i][prop];
+ //for ( var p in row ) {
+ // log("row[" + p + "] == " + row[p]);
+ //}
+
+ //log("filter:" + f);
+ //log("row[prop]: " + row[prop]);
+ //log("typeof(row[prop]): " + typeof(row[prop]));
+ if ( f[1] == EQ )
+ {
+ result &= f[0] == row[prop];
+ } else if ( f[1] == NE ) {
+ result &= f[0] != row[prop];
+ } else if ( f[1] == LE && prop in row ) {
+ result &= Number(row[prop]) <= f[0];
+ } else if ( f[1] == GE && prop in row ) {
+ result &= Number(row[prop]) >= f[0];
+ } else if ( f[1] == MATCH && prop in row ) {
+ if ( typeof(row[prop]) == "object" ) {
+ var found = false;
+ for (var i; i < row[prop].length ; i++ ) {
+ if ( f[0].test(row[prop][i]) ) {
+ found = true;
+ break;
+ }
+ }
+ result &= found;
+ } else {
+ result &= f[0].test(row[prop]);
+ }
+ } else {
+ result = false;
+ }
+ //log("result: " + result);
+ }
+ }
+ //log("filter:" + JSON.stringify(pfilter) + " result: " + result);
+ }
+ return result;
+ }
+ // The provides function serves the format the client requests.
+ // The first matching format is sent, so reordering functions changes
+ // thier priority. In this case HTML is the preferred format, so it comes first.
+ provides("text", function() {
+ var row, first_row = true;
+ var skip_header = false;
+ var skip_props = {'_id' : "", '_rev' : "", '_deleted_conflicts' : '',}; // 'hardware' : '', };
+
+ //send(JSON.stringify(req)+'\n');
+ var more_props = true;
+ var header;
+ var count=0;
+ var rowstring;
+ var pfilter = null;
+ var rpmpattern = null;
+ var prop_start = [ "hostname", 'ts' ];
+ var prop_list = {'hostname' : '', 'ts' : ''};
+
+ var show = check_field(req.query, 'show', null);
+ if ( show != null ) { show = Number(show); }
+
+ log("req: ");
+ log(req);
+ log(head);
+
+ if ( 'filter' in req.query ) {
+ pfilter = parse_filter(req.query['filter']);
+ log(pfilter);
+ }
+
+ if ( 'rpmpattern' in req.query ) {
+ rpmpattern = req.query['rpmpattern'];
+ log("found rpmpattern");
+ log(rpmpattern);
+ }
+
+
+ if ( 'prop_list' in req.query || 'fields' in req.query ) {
+ if ( 'prop_list' in req.query ) {
+ prop_start = req.query['prop_list'].split(',');
+ } else {
+ prop_start = req.query['fields'].split(',');
+ }
+ prop_list = {'hostname' : '', 'ts' : ''};
+ for ( var i=0; i < prop_start.length ; i++) {
+ prop_list[prop_start[i]] = "";
+ }
+ more_props = false;
+ }
+ if ( 'skip_header' in req.query ) {
+ skip_header = true;
+ }
+
+ while (row = getRow()) {
+ if ( row.key[0] < ts ) { continue; }
+ if ( show != null && count >= show ) { break; }
+
+ if ( first_row == true )
+ {
+ header = "";
+ // get a list of all properties
+ for (var prop in row.value ) {
+ if ( ! (prop in prop_list) && more_props ) {
+ prop_start.push(prop);
+ prop_list[prop] = "";
+ }
+ }
+ if ( ! skip_header ) {
+ // create a header for all the data to follow
+ for (var i=0 ; i < prop_start.length ; i++) {
+ prop = prop_start[i];
+ if ( ! (prop in skip_props) ) {
+ header += prop + ',';
+ }
+ }
+ send(header.substring(0,header.length-1) + '\n');
+ }
+ first_row = false;
+ }
+ rowstring = "";
+
+ if ( filter_matches(row.value, pfilter) )
+ {
+ //send(header.substring(0,-1) + '\n');
+ // Send the data that matches the filter when present
+ for (var i=0 ; i < prop_start.length ; i++) {
+ prop = prop_start[i];
+ if ( ! (prop in skip_props) )
+ {
+ if ( (prop == 'rpm_versions') && (rpmpattern != null) && ('rpm_versions' in prop_list) ) {
+ if ( typeof(row.value['rpm_versions']) == "object" ) {
+ for (var i; i < row.value['rpm_versions'].length ; i++ ) {
+ var rpm = row.value['rpm_versions'][i].match(rpmpattern);
+ if ( rpm ) { break; }
+ }
+ log(rpm);
+ if ( rpm ) {
+ rowstring += rpm.join(' ') + ',';
+ } else {
+ rowstring += ',';
+ }
+ } else {
+ log("not a string");
+ log(typeof(row.value['rpm_versions']));
+ rowstring += ',';
+ }
+ } else {
+ if ( typeof(row.value[prop]) == "object" )
+ {
+ rowstring += row.value[prop].join(" ") + ',';
+ } else {
+ rowstring += row.value[prop] + ',';
+ }
+ }
+ }
+ }
+ send(rowstring.substring(0,rowstring.length-1) + '\n');
+ count += 1;
+ }
+ }
+ // tail
+ return '\n';
+ });
+ provides("3mtl", function() {
+ var key = "";
+ // render the html head using a template
+ var stash = {
+ header : {
+ index : indexPath,
+ blogName : ddoc.blog.title,
+ feedPath : feedPath,
+ commentsFeed : commentsFeed
+ },
+ scripts : {},
+ db : req.path[0],
+ design : req.path[2],
+ feedPath : feedPath,
+ newPostPath : path.show("edit"),
+ assets : path.asset(),
+ nodes : List.withRows(function(row) {
+ var node = row.value;
+ key = row.key;
+ return {
+ name : node.hostname,
+ site : node.site,
+ date : node.date_created,
+ link : path.list('node','node-page', {startkey : [row.id]}),
+ has_slices : false,
+ slices : [],
+ };
+ }),
+ older : function() {
+ return path.older(key);
+ },
+ "5" : path.limit(5),
+ "10" : path.limit(10),
+ "25" : path.limit(25)
+ };
+ return Mustache.to_html(ddoc.templates.nodelist, stash, ddoc.templates.partials, List.send);
+ });
+
+ // if the client requests an atom feed and not html,
+ // we run this function to generate the feed.
+ provides("atom", function() {
+ var path = require("vendor/couchapp/lib/path").init(req);
+ var markdown = require("vendor/couchapp/lib/markdown");
+ var textile = require("vendor/textile/textile");
+
+ // we load the first row to find the most recent change date
+ var row = getRow();
+
+ // generate the feed header
+ var feedHeader = Atom.header({
+ updated : (row ? new Date(row.value.created_at) : new Date()),
+ title : ddoc.blog.title,
+ feed_id : path.absolute(indexPath),
+ feed_link : path.absolute(feedPath),
+ });
+
+ // send the header to the client
+ send(feedHeader);
+
+ // loop over all rows
+ if (row) {
+ do {
+ if (row.value.format == "markdown") {
+ var html = markdown.encode(row.value.body);
+ } else if (row.value.format == "textile") {
+ var html = textile.encode(row.value.body);
+ } else {
+ var html = Mustache.escape(row.value.html);
+ }
+ // generate the entry for this row
+ var feedEntry = Atom.entry({
+ entry_id : path.absolute('/'+encodeURIComponent(req.info.db_name)+'/'+encodeURIComponent(row.id)),
+ title : row.value.title,
+ content : html,
+ updated : new Date(row.value.created_at),
+ author : row.value.author,
+ alternate : path.absolute(path.show('post', row.id))
+ });
+ // send the entry to client
+ send(feedEntry);
+ } while (row = getRow());
+ }
+
+ // close the loop after all rows are rendered
+ return "</feed>";
+ });
+};