3 var Mustache = require("lib/mustache");
4 var List = require("vendor/couchapp/lib/list");
5 var path = require("vendor/couchapp/lib/path").init(req);
6 var Atom = require("vendor/couchapp/lib/atom");
9 if ( 'within' in req.query ) {
10 within = Number(req.query['within']);
11 log("within: " + within);
13 within = 60 * 60 * 3; // within three hours;
15 var ts = (new Date()).getTime()/1000 - within; // convert milliseconds to seconds
17 var indexPath = path.list('nodelist','node-status',{descending:true, limit:10});
18 var feedPath = path.list('nodelist','node-status',{descending:true, limit:10, format:"atom"});
19 var commentsFeed = path.list('comments','comments',{descending:true, limit:10, format:"atom"});
22 var path_parts = req.path;
31 function check_field(query, field, def) {
33 if ( field in query ) {
35 log(field + ": " + ret);
40 function tokenize(sf) {
45 var f_le = /<=|%3C=|%3c=/;
46 var f_ge = />=|%3E=|%3e=/;
49 if ( sf.match(f_eq) ) {
50 fields = sf.split("==");
51 ret = [fields[0], fields[1], EQ];
52 } else if ( sf.match(f_ne) ) {
53 fields = sf.split("!=");
54 ret = [fields[0], fields[1], NE] ;
55 } else if ( sf.match(f_le) ) {
56 fields = sf.split("<=");
57 ret = [fields[0], Number(fields[1]), LE] ;
58 } else if ( sf.match(f_ge) ) {
59 fields = sf.split(">=");
60 ret = [fields[0], Number(fields[1]), GE] ;
61 } else if ( sf.match(f_match) ) {
62 fields = sf.split("=~");
63 ret = [fields[0], new RegExp(fields[1]), MATCH] ;
69 function pf(x) { return 0; };
70 function parse_filter (sfilter) {
71 var f_and_or = /AND|%%|&&|\|\|/;
74 var f_le = /<=|%3C=|%3c=/;
75 var f_ge = />=|%3E=|%3e=/;
81 log("sfilter: " + sfilter);
82 if ( sfilter.match(f_and_or) )
84 sfilter_list = sfilter.split(f_and_or);
85 log("sfilter_list: " + sfilter_list);
87 sfilter_list = [ sfilter ] ;
90 for ( var i=0; i < sfilter_list.length ; i++ )
96 if ( sf.match(f_eq) ) {
97 var fields = sf.split("==");
98 eq[fields[0]] = [fields[1], EQ] ;
99 } else if ( sf.match(f_ne) ) {
100 var fields = sf.split("!=");
101 eq[fields[0]] = [fields[1], NE] ;
102 } else if ( sf.match(f_le) ) {
103 var fields = sf.split("<=");
104 eq[fields[0]] = [Number(fields[1]), LE] ;
105 } else if ( sf.match(f_ge) ) {
106 var fields = sf.split(">=");
107 eq[fields[0]] = [Number(fields[1]), GE] ;
108 } else if ( sf.match(f_match) ) {
109 var fields = sf.split("=~");
110 eq[fields[0]] = [new RegExp(fields[1]), MATCH] ;
114 //log("kv_list: " + JSON.stringify(kv_list));
115 //pfilter = [ kv_list, AND ];
120 function filter_matches (row, pfilter) {
122 if ( pfilter != null ) {
123 for ( var i=0; i < pfilter.length ; i++ )
125 //log("starting filter_matches");
127 for ( var prop in pfilter[i] ) {
128 f = pfilter[i][prop];
129 //for ( var p in row ) {
130 // log("row[" + p + "] == " + row[p]);
133 //log("filter:" + f);
134 //log("row[prop]: " + row[prop]);
135 //log("typeof(row[prop]): " + typeof(row[prop]));
138 result &= f[0] == row[prop];
139 } else if ( f[1] == NE ) {
140 result &= f[0] != row[prop];
141 } else if ( f[1] == LE && prop in row ) {
142 result &= Number(row[prop]) <= f[0];
143 } else if ( f[1] == GE && prop in row ) {
144 result &= Number(row[prop]) >= f[0];
145 } else if ( f[1] == MATCH && prop in row ) {
146 if ( typeof(row[prop]) == "object" ) {
148 for (var i; i < row[prop].length ; i++ ) {
149 if ( f[0].test(row[prop][i]) ) {
156 result &= f[0].test(row[prop]);
161 //log("result: " + result);
164 //log("filter:" + JSON.stringify(pfilter) + " result: " + result);
168 // The provides function serves the format the client requests.
169 // The first matching format is sent, so reordering functions changes
170 // thier priority. In this case HTML is the preferred format, so it comes first.
171 provides("text", function() {
172 var row, first_row = true;
173 var skip_header = false;
174 var skip_props = {'_id' : "", '_rev' : "", '_deleted_conflicts' : '',}; // 'hardware' : '', };
176 //send(JSON.stringify(req)+'\n');
177 var more_props = true;
182 var rpmpattern = null;
183 var prop_start = [ "hostname", 'ts' ];
184 var prop_list = {'hostname' : '', 'ts' : ''};
186 var show = check_field(req.query, 'show', null);
187 if ( show != null ) { show = Number(show); }
193 if ( 'filter' in req.query ) {
194 pfilter = parse_filter(req.query['filter']);
198 if ( 'rpmpattern' in req.query ) {
199 rpmpattern = req.query['rpmpattern'];
200 log("found rpmpattern");
205 if ( 'prop_list' in req.query || 'fields' in req.query ) {
206 if ( 'prop_list' in req.query ) {
207 prop_start = req.query['prop_list'].split(',');
209 prop_start = req.query['fields'].split(',');
211 prop_list = {'hostname' : '', 'ts' : ''};
212 for ( var i=0; i < prop_start.length ; i++) {
213 prop_list[prop_start[i]] = "";
217 if ( 'skip_header' in req.query ) {
221 while (row = getRow()) {
222 if ( row.key[0] < ts ) { continue; }
223 if ( show != null && count >= show ) { break; }
225 if ( first_row == true )
228 // get a list of all properties
229 for (var prop in row.value ) {
230 if ( ! (prop in prop_list) && more_props ) {
231 prop_start.push(prop);
232 prop_list[prop] = "";
235 if ( ! skip_header ) {
236 // create a header for all the data to follow
237 for (var i=0 ; i < prop_start.length ; i++) {
238 prop = prop_start[i];
239 if ( ! (prop in skip_props) ) {
240 header += prop + ',';
243 send(header.substring(0,header.length-1) + '\n');
249 if ( filter_matches(row.value, pfilter) )
251 //send(header.substring(0,-1) + '\n');
252 // Send the data that matches the filter when present
253 for (var i=0 ; i < prop_start.length ; i++) {
254 prop = prop_start[i];
255 if ( ! (prop in skip_props) )
257 if ( (prop == 'rpm_versions') && (rpmpattern != null) && ('rpm_versions' in prop_list) ) {
258 if ( typeof(row.value['rpm_versions']) == "object" ) {
259 for (var i; i < row.value['rpm_versions'].length ; i++ ) {
260 var rpm = row.value['rpm_versions'][i].match(rpmpattern);
261 if ( rpm ) { break; }
265 rowstring += rpm.join(' ') + ',';
271 log(typeof(row.value['rpm_versions']));
275 if ( typeof(row.value[prop]) == "object" )
277 rowstring += row.value[prop].join(" ") + ',';
279 rowstring += row.value[prop] + ',';
284 send(rowstring.substring(0,rowstring.length-1) + '\n');
291 provides("3mtl", function() {
293 // render the html head using a template
297 blogName : ddoc.blog.title,
299 commentsFeed : commentsFeed
303 design : req.path[2],
305 newPostPath : path.show("edit"),
306 assets : path.asset(),
307 nodes : List.withRows(function(row) {
308 var node = row.value;
311 name : node.hostname,
313 date : node.date_created,
314 link : path.list('node','node-page', {startkey : [row.id]}),
320 return path.older(key);
323 "10" : path.limit(10),
324 "25" : path.limit(25)
326 return Mustache.to_html(ddoc.templates.nodelist, stash, ddoc.templates.partials, List.send);
329 // if the client requests an atom feed and not html,
330 // we run this function to generate the feed.
331 provides("atom", function() {
332 var path = require("vendor/couchapp/lib/path").init(req);
333 var markdown = require("vendor/couchapp/lib/markdown");
334 var textile = require("vendor/textile/textile");
336 // we load the first row to find the most recent change date
339 // generate the feed header
340 var feedHeader = Atom.header({
341 updated : (row ? new Date(row.value.created_at) : new Date()),
342 title : ddoc.blog.title,
343 feed_id : path.absolute(indexPath),
344 feed_link : path.absolute(feedPath),
347 // send the header to the client
350 // loop over all rows
353 if (row.value.format == "markdown") {
354 var html = markdown.encode(row.value.body);
355 } else if (row.value.format == "textile") {
356 var html = textile.encode(row.value.body);
358 var html = Mustache.escape(row.value.html);
360 // generate the entry for this row
361 var feedEntry = Atom.entry({
362 entry_id : path.absolute('/'+encodeURIComponent(req.info.db_name)+'/'+encodeURIComponent(row.id)),
363 title : row.value.title,
365 updated : new Date(row.value.created_at),
366 author : row.value.author,
367 alternate : path.absolute(path.show('post', row.id))
369 // send the entry to client
371 } while (row = getRow());
374 // close the loop after all rows are rendered