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(parameter, value, type) {
41 return { 'param' : parameter, 'value' : value, 'type' : type };
43 function token_get(sf) {
48 var f_le = /<=|%3C=|%3c=/;
49 var f_ge = />=|%3E=|%3e=/;
52 if ( sf.match(f_eq) ) {
53 fields = sf.split("==");
54 ret = [fields[0], fields[1], EQ];
55 } else if ( sf.match(f_ne) ) {
56 fields = sf.split("!=");
57 ret = [fields[0], fields[1], NE] ;
58 } else if ( sf.match(f_le) ) {
59 fields = sf.split("<=");
60 ret = [fields[0], Number(fields[1]), LE] ;
61 } else if ( sf.match(f_ge) ) {
62 fields = sf.split(">=");
63 ret = [fields[0], Number(fields[1]), GE] ;
64 } else if ( sf.match(f_match) ) {
65 fields = sf.split("=~");
66 ret = [fields[0], new RegExp(fields[1]), MATCH] ;
70 function token_conjunction_get(sf) {
71 var f_and = /AND|%%|&&/;
78 log("sfilter: " + sf);
79 if ( sf.match(f_and) ) {
80 fields = sf.split(f_and);
83 } else if ( sf.match(f_or) ) {
84 fields = sf.split(f_or);
92 for ( var i=0; i < fields.length ; i++)
94 l.push(token_get(fields[i]));
96 return {'list' : l, 'type' : type};
99 function token_test(token, row)
104 var result_minor = true;
107 result_minor &= val == row[prop];
108 } else if ( op == NE ) {
109 result_minor &= val != row[prop];
110 } else if ( op == LE && prop in row ) {
111 result_minor &= Number(row[prop]) <= val;
112 } else if ( op == GE && prop in row ) {
113 result_minor &= Number(row[prop]) >= val;
114 } else if ( op == MATCH && prop in row ) {
115 if ( typeof(row[prop]) == "object" ) {
117 for (var i; i < row[prop].length ; i++ ) {
118 if ( val.test(row[prop][i]) ) {
123 result_minor &= found;
125 result_minor &= val.test(row[prop]);
128 result_minor = false;
133 function parse_filter (sfilter) {
135 log("sfilter: " + sfilter);
136 var ret = token_conjunction_get(sfilter);
140 function filter_matches (row, pfilter) {
141 var result_minor = true;
143 if ( pfilter != null ) {
145 // set defaults based on conjunction type
146 if ( pfilter['type'] == AND ) {
148 } else if ( pfilter['type'] == OR ) {
152 for ( var i=0; i < pfilter['list'].length ; i++ )
154 tok = pfilter['list'][i];
155 result_minor = token_test(tok, row);
157 if (pfilter['type'] == AND ) {
158 result &= result_minor;
159 } else if ( pfilter['type'] == OR ) {
160 result |= result_minor;
163 //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