760 lines
24 KiB
JavaScript
760 lines
24 KiB
JavaScript
#!/usr/bin/env node
|
|
var http = require('http');
|
|
var fs = require('fs');
|
|
var m_path = require('path');
|
|
var util = require('util');
|
|
/* qwiki
|
|
Node.js based Implementation of the proposed qwiki document
|
|
*/
|
|
/*********************************
|
|
* QCore *
|
|
*********************************/
|
|
var QCore = function() {
|
|
var acts = {};
|
|
this.acts = acts;
|
|
self = this;
|
|
this.mimetypes = {};
|
|
this.index = null;
|
|
this.index_parts = [];
|
|
this.last_part = { start: 0, end: 0 };
|
|
|
|
this.etags = {}; // etags cache, obj: uri { etag: '...', timestamp: }
|
|
|
|
this.res_cache = {}; // resource cache ('/view.png' => '03xc345');
|
|
this.rules = {};
|
|
|
|
this.http = http.createServer(function(req, res) {
|
|
var last = req.url.lastIndexOf("/");
|
|
var act = req.url.substr(last+1);
|
|
if (acts[act]) {
|
|
req.url = req.url.substr(0, last); // rewrite the url without the act
|
|
} else {
|
|
act = "";
|
|
}
|
|
req.post = {};
|
|
if (req.method == 'POST') {
|
|
var chunks = [];
|
|
var chunks_len = 0;
|
|
req.on('data', function(chunk) {
|
|
chunks.push(chunk);
|
|
chunks_len += chunk.length;
|
|
});
|
|
req.on('end', function(){
|
|
var data = Buffer.concat(chunks, chunks_len);
|
|
var parts = data.toString().split('&');
|
|
for (var i = 0; i < parts.length; i++) {
|
|
split = parts[i].split('=');
|
|
key = split[0];
|
|
value = split[1];
|
|
if (typeof value !== 'undefined') {
|
|
req.post[key] = decodeURIComponent(value.replace(/[+]/g, function(c) { return ' '; }));
|
|
} else {
|
|
req.post[key] = '';
|
|
}
|
|
}
|
|
for (var i = 0, keys = Object.keys(acts[act]); i < keys.length; i++) {
|
|
acts[act][i](req, res);
|
|
}
|
|
});
|
|
} else {
|
|
for (var i = 0, keys = Object.keys(acts[act]); i < keys.length; i++) {
|
|
acts[act][i](req, res);
|
|
}
|
|
}
|
|
});
|
|
|
|
this.act = function(act, callback) {
|
|
if (typeof acts[act] === 'undefined') {
|
|
acts[act] = [];
|
|
}
|
|
acts[act].push(callback);
|
|
};
|
|
this.rule = function(zone, replace, callback) {
|
|
if (typeof this.rules[zone] === 'undefined') {
|
|
this.rules[zone] = {};
|
|
}
|
|
this.rules[zone][replace] = callback;
|
|
};
|
|
var fs_timeout; // timer to bypass multiple fs.watch triggering due to unstable API
|
|
this.loadIndex = function(event, filename) {
|
|
if (fs_timeout) return;
|
|
fs_timeout = setTimeout(function() { fs_timeout = null; }, 100);
|
|
var reg = /@@[a-z]+@@/ig;
|
|
fs.readFile('index.html', function(err, data) {
|
|
if (err) throw err;
|
|
self.index = data;
|
|
var search;
|
|
self.index_parts = [];
|
|
while ((search = reg.exec(data)) !== null) {
|
|
self.index_parts.push({ replace: search[0], start: search.index, end: reg.lastIndex });
|
|
}
|
|
});
|
|
};
|
|
fs.watch('index.html', this.loadIndex);
|
|
};
|
|
QCore.prototype.defaults = {};
|
|
QCore.prototype.listen = function(port) {
|
|
this.loadIndex();
|
|
this.loadWikiIndex();
|
|
try {
|
|
this.http.listen(port);
|
|
} catch(err) {
|
|
console.log('uhoh, could not listen on port '+port);
|
|
console.log(err);
|
|
}
|
|
};
|
|
QCore.prototype.setDefault = function(key, value) {
|
|
this.defaults[key] = value;
|
|
};
|
|
QCore.prototype.getDefault = function(key) {
|
|
if (key in this.defaults) {
|
|
return this.defaults[key];
|
|
}
|
|
return '';
|
|
};
|
|
QCore.prototype.addMIMEtype = function(ext, mimetype) {
|
|
if (typeof this.mimetypes[ext] === 'undefined') this.mimetypes[ext] = [];
|
|
this.mimetypes[ext].push(mimetype);
|
|
};
|
|
QCore.prototype.getMIMEtype = function(ext) {
|
|
if (this.mimetypes[ext]) return this.mimetypes[ext][0];
|
|
return '';
|
|
};
|
|
QCore.prototype.loadWikiIndex = function() {
|
|
try {
|
|
this.wiki_index = JSON.parse(fs.readFileSync('index.json'));
|
|
} catch (e) {
|
|
this.wiki_index = { pages: {} };
|
|
console.log(e);
|
|
}
|
|
var wiki_index = this.wiki_index;
|
|
console.log('loaded indices for ' + Object.keys(this.wiki_index.pages).length + ' pages');
|
|
readFiles('wiki', function(err, file, is_dir) {
|
|
var wiki_file = file.replace('wiki/', '');
|
|
if (is_dir) {
|
|
if (wiki_file in wiki_index.pages) {
|
|
if (wiki_index.pages[wiki_file].dir != true) wiki_index.pages[wiki_file].dir = true;
|
|
} else {
|
|
wiki_index.pages[wiki_file] = { dir: true };
|
|
}
|
|
} else {
|
|
if (getExt(file) == 'qwk') {
|
|
if (wiki_file in wiki_index.pages) {
|
|
} else {
|
|
var name = wiki_file.slice(0, -4);
|
|
wiki_index.pages[name] = { format: 'md' }; // TODO: this.config.default_type
|
|
}
|
|
} else {
|
|
if (wiki_file in wiki_index.pages) {
|
|
} else {
|
|
wiki_index.pages[wiki_file] = { format: 'file' };
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
QCore.prototype.parsePage = function(zone, fallback_zone, req, res) {
|
|
var qparse = new QParse();
|
|
qparse.indices = this.index_parts.slice(); // clone it TODO: just make an indices (number-only) array
|
|
|
|
var context = this;
|
|
var cb = function(qparse, callback) {
|
|
res.write(context.index.slice(qparse.offset, qparse.current.start));
|
|
if (typeof context.rules[zone] === 'undefined') { zone = fallback_zone; }
|
|
if (typeof context.rules[zone] === 'undefined') { zone = ''; }
|
|
if (typeof context.rules[zone] !== 'undefined') {
|
|
if (typeof context.rules[zone][qparse.current.replace] !== 'undefined') {
|
|
context.rules[zone][qparse.current.replace](req, res, qparse, callback);
|
|
} else {
|
|
// fallback action
|
|
if (typeof context.rules[fallback_zone][qparse.current.replace] !== 'undefined') {
|
|
context.rules[fallback_zone][qparse.current.replace](req, res, qparse, callback);
|
|
// default action
|
|
} else if (typeof context.rules[''][qparse.current.replace] !== 'undefined') {
|
|
context.rules[''][qparse.current.replace](req, res, qparse, callback);
|
|
} else {
|
|
callback();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
var series = function(qparse) {
|
|
if (qparse.current) {
|
|
cb(qparse, function(next) {
|
|
if (qparse.next) {
|
|
res.write(context.index.slice(qparse.current.end, qparse.next.start));
|
|
qparse.offset = qparse.next.end;
|
|
qparse.current = qparse.indices.shift();
|
|
qparse.next = qparse.indices[0];
|
|
series(qparse);
|
|
} else {
|
|
res.write(context.index.slice(qparse.current.end));
|
|
qparse.current = qparse.indices.shift();
|
|
qparse.next = qparse.indices[0];
|
|
series(qparse);
|
|
}
|
|
});
|
|
} else {
|
|
res.end();
|
|
}
|
|
};
|
|
qparse.current = qparse.indices.shift();
|
|
qparse.next = qparse.indices[0];
|
|
series(qparse);
|
|
};
|
|
/*********************************
|
|
* QParse *
|
|
*********************************/
|
|
var QParse = function() {};
|
|
QParse.prototype.indices = {};
|
|
QParse.prototype.offset = 0;
|
|
QParse.prototype.current = null;
|
|
QParse.prototype.next = null;
|
|
/*********************************
|
|
* QWiki *
|
|
*********************************/
|
|
var QWiki = function() {
|
|
QCore.call(this);
|
|
try {
|
|
var stats = fs.statSync('cache');
|
|
if (!stats.isDirectory()) {
|
|
console.log('error, "cache" is not a directory');
|
|
}
|
|
} catch (err) {
|
|
if (err.code == 'ENOENT') {
|
|
fs.mkdirSync('cache');
|
|
console.log('Created "cache/" -- this is where processed wiki pages are stored as HTML');
|
|
} else {
|
|
console.log('error while loading cache');
|
|
}
|
|
}
|
|
}; util.inherits(QWiki, QCore);
|
|
QWiki.prototype.formats = {
|
|
raw: {
|
|
name: 'raw',
|
|
fullname: 'Raw',
|
|
convert: function(source) {
|
|
return source;
|
|
},
|
|
},
|
|
html: {
|
|
name: 'html',
|
|
fullname: 'HTML',
|
|
convert: function(source) {
|
|
return source;
|
|
}
|
|
}
|
|
};
|
|
QWiki.prototype.addFormat = function(name, fullname, convertor_cb, pre_cb, post_cb) {
|
|
this.formats[name] = {
|
|
'name': name,
|
|
'fullname': fullname,
|
|
'convert': convertor_cb,
|
|
'pre': pre_cb,
|
|
'post': post_cb
|
|
};
|
|
};
|
|
QWiki.prototype.convertAndSave = function(path, name, source, cb) {
|
|
if (typeof cb === 'undefined') cb = function() {};
|
|
if (!(name in this.formats)) {
|
|
name = 'raw';
|
|
}
|
|
console.log('converting to ' + name + ' with path ' + path);
|
|
var converted = this.formats[name].convert(source);
|
|
r_mkdir(m_path.dirname('cache'+path+'.html'), 0777, function() {
|
|
fs.writeFile('cache'+path+'.html', converted, function(err) {
|
|
if (err) {
|
|
console.log('error writing "cache'+ path + '.html"');
|
|
} else {
|
|
console.log('wrote "cache' + path + '.html"');
|
|
}
|
|
cb();
|
|
});
|
|
});
|
|
};
|
|
QWiki.prototype.deleteCache = function(wiki_page, cb) {
|
|
fs.unlink('cache/'+wiki_page+'.html', cb);
|
|
};
|
|
QWiki.prototype.createCache = function(wiki_page, source, cb) {
|
|
if (typeof cb === 'undefined') cb = function() {};
|
|
var format = this.defaults.format;
|
|
if (typeof this.wiki_index.pages[wiki_page] !== 'undefined') {
|
|
format = this.wiki_index.pages[wiki_page].format;
|
|
}
|
|
if (!(format in this.formats)) {
|
|
format = 'raw';
|
|
}
|
|
console.log('createCache: caching ' + wiki_page + ' as ' + format);
|
|
|
|
var converted = this.formats[format].convert(source);
|
|
|
|
var path = 'cache/' + wiki_page + '.html';
|
|
|
|
r_mkdir(m_path.dirname(path), 0777, function() {
|
|
fs.writeFile(path, converted, function(err) {
|
|
if (err) {
|
|
console.log('createCache: error writing "' + path + '"');
|
|
} else {
|
|
console.log('createCache: created "' + path + '"');
|
|
}
|
|
cb(err);
|
|
});
|
|
});
|
|
};
|
|
QWiki.prototype.deletePage = function(wiki_page, cb) {
|
|
fs.unlink('wiki/'+wiki_page+'.qwk', cb);
|
|
};
|
|
QWiki.prototype.savePage = function(wiki_page, content, cb) {
|
|
var path = 'wiki/' + wiki_page + '.qwk';
|
|
r_mkdir(m_path.dirname(path), 0777, function() {
|
|
console.log('savePage: writing ' + path);
|
|
qwiki.deleteCache(wiki_page, function() {
|
|
fs.writeFile(path, content, function(err) {
|
|
console.log('savePage: wrote ' + path);
|
|
cb(err);
|
|
});
|
|
});
|
|
});
|
|
};
|
|
|
|
/*********************************
|
|
* qwiki instance *
|
|
*********************************/
|
|
var qwiki = new QWiki();
|
|
|
|
qwiki.addMIMEtype('png', 'image/png');
|
|
qwiki.addMIMEtype('svg', 'image/svg+xml');
|
|
qwiki.addMIMEtype('css', 'text/css');
|
|
|
|
var m_markdownit = require('markdown-it')({
|
|
typographer: true
|
|
});
|
|
m_markdownit.use(require('markdown-it-anchor'));
|
|
m_markdownit.use(require('markdown-it-table-of-contents'), {includeLevel: "2-8"});
|
|
m_markdownit.use(require('markdown-it-deflist'));
|
|
m_markdownit.use(require('markdown-it-header-sections'));
|
|
m_markdownit.use(require('markdown-it-sup'));
|
|
qwiki.addFormat('md', 'Markdown', function(source) {
|
|
// source is a Buffer, but render seems to need a String
|
|
return m_markdownit.render(source.toString());
|
|
});
|
|
|
|
qwiki.setDefault('format', 'md');
|
|
|
|
// **** DEFAULT
|
|
qwiki.rule('', '@@CONTENT@@', function(req, res, instance, next) {
|
|
var area = req.url;
|
|
if (area == '') {
|
|
area = 'front';
|
|
}
|
|
|
|
// instance.pos, instance.offset, instance.indices
|
|
var cache_path = 'cache/'+area+'.html';
|
|
readFile(res, cache_path, function(type, err) {
|
|
if (type == 'FNF') {
|
|
// cache does not exist - is there a wiki source?
|
|
var wiki_path = 'wiki/'+area+'.qwk';
|
|
fs.stat(wiki_path, function(err, stats) {
|
|
// TODO: stats.isFile()
|
|
if (err && err.code == 'ENOENT') {
|
|
// the wiki entry does not exist
|
|
res.write(area + ' does not exist yet');
|
|
next();
|
|
} else if (err) {
|
|
// error while statting wiki entry
|
|
res.write(area + ': ' + err.code);
|
|
next();
|
|
} else {
|
|
// wiki entry exists!
|
|
fs.readFile(wiki_path, function(err, data) {
|
|
if (err) {
|
|
res.write(area + ': ' + err.code);
|
|
next();
|
|
} else {
|
|
qwiki.convertAndSave(area, (req.url in qwiki.wiki_index.pages ? qwiki.wiki_index.pages[req.url].format : qwiki.getDefault('format')), data, function() {
|
|
readFile(res, wiki_path, function(type, err) {
|
|
if (type == 'FNF') {
|
|
res.write('error while creating cache');
|
|
next();
|
|
} else if (err) {
|
|
res.write(area + ': ' + err.code);
|
|
next();
|
|
} else {
|
|
next();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
});
|
|
}
|
|
});
|
|
} else if (err) {
|
|
res.write(area + ': ' + err.code);
|
|
next();
|
|
} else {
|
|
next();
|
|
}
|
|
});
|
|
});
|
|
qwiki.rule('', '@@TITLE@@', function(req, res, instance, next) {
|
|
res.write('qwiki ' + req.url);
|
|
next();
|
|
});
|
|
qwiki.rule('', '@@PAGE@@', function(req, res, instance, next) {
|
|
res.write(req.url);
|
|
next();
|
|
});
|
|
qwiki.rule('', '@@CONTROLS@@', function(req, res, instance, next) {
|
|
res.write('<li><a href="'+req.url+'/edit"><img src="/edit.png">Edit</a></li><li><a href="'+req.url+'/new"><img src="/new.png">New Page</a></li>');
|
|
next();
|
|
});
|
|
qwiki.rule('', '@@CRUMBS@@', function(req, res, instance, next) {
|
|
var parts = req.url.split('/');
|
|
var path = '';
|
|
parts[0] = '>';
|
|
for (var i = 0; i < parts.length; i++) {
|
|
path += (i == 0 ? '/' : parts[i]);
|
|
res.write('<li><a href="'+path+'">'+parts[i]+'</a></li>');
|
|
path += (i == 0 ? '' : '/');
|
|
}
|
|
next();
|
|
});
|
|
qwiki.rule('', '@@FOOTER@@', function(req, res, instance, next) {
|
|
res.write('<a href="http://kettek.net/qwiki">qwiki</a> Copyright 2015 <a href="http://kettek.net">kts of kettek</a>');
|
|
next();
|
|
});
|
|
qwiki.act('', function(req, res) {
|
|
var mimetype = qwiki.getMIMEtype(getExt(req.url));
|
|
if (mimetype != '') { // write file on disk directly
|
|
var path = 'wiki/'+req.url;
|
|
fs.stat(path, function(err, stat) {
|
|
if (err == null) {
|
|
// TODO: etags should actually be based on binary data(CRC32, etc.), not last modified
|
|
// TODO: files should not be stat()`d each call, but should be cached and updated when the file updates (how? browser-based upload interface?)
|
|
var mtime = stat.mtime.getTime();
|
|
if (!(path in qwiki.etags) || qwiki.etags[path] != mtime) {
|
|
qwiki.etags[path] = mtime;
|
|
}
|
|
if ('if-none-match' in req.headers && req.headers['if-none-match'] == qwiki.etags[path]) {
|
|
res.writeHead(304, "Not Modified");
|
|
res.end();
|
|
} else {
|
|
res.writeHead(200, "OK", {
|
|
"Content-Type": mimetype,
|
|
"Content-Length": stat.size,
|
|
"ETag": mtime
|
|
});
|
|
var rs = fs.createReadStream(path);
|
|
rs.on('data', function(chunk) {
|
|
res.write(chunk);
|
|
});
|
|
rs.on('end', function() {
|
|
res.end();
|
|
});
|
|
}
|
|
} else if (err.code == 'ENOENT') {
|
|
res.writeHead(404);
|
|
res.end();
|
|
} else {
|
|
console.log(err);
|
|
res.write(err);
|
|
res.end();
|
|
}
|
|
});
|
|
} else { // no mimetype/ext, it must be a wiki entry
|
|
res.writeHead(200, "OK", {
|
|
"Content-Type": "text/html",
|
|
});
|
|
qwiki.parsePage('', '', req, res);
|
|
}
|
|
});
|
|
// **** INDEX
|
|
qwiki.rule('index', '@@CONTENT@@', function(req, res, instance, next) {
|
|
var path = 'wiki/'+req.url;
|
|
console.log('reading path' + path);
|
|
fs.readdir(path, function(err, files) {
|
|
for (var file in files) {
|
|
if (getExt(files[file]) == 'qwk') {
|
|
name = files[file].slice(0, -4);
|
|
res.write('<li><a href="'+req.url+'/'+name+'">'+name+'</a></li>');
|
|
} else {
|
|
res.write('<li><a href="'+req.url+'/'+files[file]+'">'+files[file]+'</a></li>');
|
|
}
|
|
}
|
|
next();
|
|
});
|
|
});
|
|
qwiki.act('index', function(req, res) {
|
|
res.writeHead(200, "OK", {
|
|
"Content-Type": "text/html",
|
|
});
|
|
qwiki.parsePage('index', '', req, res);
|
|
});
|
|
// **** UPLOAD
|
|
qwiki.act('upload', function(req, res) {
|
|
res.writeHead(200, "OK", {
|
|
"Content-Type": "text/html",
|
|
});
|
|
qwiki.parsePage('upload', '', req, res);
|
|
});
|
|
// **** VIEW
|
|
qwiki.act('view', function(req, res) {
|
|
if (req.url == '') {
|
|
req.url = 'index';
|
|
}
|
|
var path = 'wiki/'+req.url+'.qwk';
|
|
|
|
readFile(res, path, function(type, err) {
|
|
if (type == 'FNF') {
|
|
} else if (err) {
|
|
res.write(err);
|
|
}
|
|
res.end();
|
|
});
|
|
});
|
|
// **** NEW
|
|
qwiki.rule('new', '@@CONTENT@@', function(req, res, instance, next) {
|
|
var area = req.url;
|
|
if (area == '') {
|
|
area = 'new_page';
|
|
} else {
|
|
area = req.url.substr(1) + '/new_page';
|
|
}
|
|
var path = 'wiki/'+req.url+'.qwk';
|
|
res.write('<form action="" method="POST"><div class="edit"><div><label for="page"><span>This is the page name that corresponds to the wiki URL, e.g., my_page => kettek.net/qwiki/my_page</span>Page</label> <input type="text" name="page" value="'+area+'"></div>');
|
|
res.write('</div><div class="prompt"><input type="submit" name="submit" value="edit"></div></form>');
|
|
next();
|
|
});
|
|
qwiki.act('new', function(req, res) {
|
|
// handle POST
|
|
if ('submit' in req.post && req.post['submit'] == 'edit') {
|
|
if ('page' in req.post) {
|
|
res.writeHead(302, {'Location': '/'+req.post['page']+'/edit'});
|
|
res.end();
|
|
return;
|
|
}
|
|
}
|
|
res.writeHead(200, "OK", {
|
|
"Content-Type": "text/html",
|
|
});
|
|
qwiki.parsePage('new', 'edit', req, res);
|
|
});
|
|
// **** EDIT
|
|
qwiki.rule('edit', '@@CONTENT@@', function(req, res, instance, next) {
|
|
var area = (req.url == '' ? 'front' : req.url.substr(1));
|
|
var path = 'wiki/'+area+'.qwk';
|
|
res.write('<form action="" method="POST"><div class="edit"><div><label for="page"><span>This is the page name that corresponds to the wiki URL, e.g., my_page => kettek.net/qwiki/my_page</span>Page</label> <input type="text" name="page" value="'+area+'"></div>');
|
|
res.write('<div><label for="format"><span>This is the data format of content source, such as HTML or raw.</span>Format</label> <select name="format">');
|
|
for (var format in qwiki.formats) {
|
|
var page_format = (typeof qwiki.wiki_index.pages[area] !== 'undefined' ? qwiki.wiki_index.pages[area].format : qwiki.getDefault('format'));
|
|
res.write('<option ' + (qwiki.formats[format].name == page_format ? 'selected ' : '') + 'value="'+qwiki.formats[format].name+'">'+qwiki.formats[format].fullname+'</option>');
|
|
}
|
|
res.write('</select></div>');
|
|
res.write('</div><div id="edit_content"><label for="content">Content<span>This is the content of the wiki page</span></label> <textarea name="content">');
|
|
readFile(res, path, function(type, err) {
|
|
if (type == 'FNF') {
|
|
} else if (err) {
|
|
res.write(err);
|
|
}
|
|
res.write('</textarea></div><div class="prompt"><input type="submit" name="submit" value="cancel"><input type="submit" name="submit" value="save"></div></form>');
|
|
next();
|
|
});
|
|
});
|
|
qwiki.rule('edit', '@@CONTROLS@@', function(req, res, instance, next) {
|
|
res.write('<li><a href="'+(req.url == '' ? '/' : req.url)+'"><img src="/view.png">View</a></li><li><a href="'+req.url+'/revisions"><img src="/revisions.png">Revisions</a></li><li><a href="'+req.url+'/delete"><img src="/delete.png">Delete</a></li>');
|
|
next();
|
|
});
|
|
qwiki.act('edit', function(req, res) {
|
|
var area = (req.url == '' ? 'front' : req.url.substr(1));
|
|
// handle POST
|
|
if ('submit' in req.post && req.post['submit'] == 'save') {
|
|
var old_area = area;
|
|
if ('page' in req.post) {
|
|
area = req.post['page'];
|
|
}
|
|
|
|
if (typeof qwiki.wiki_index.pages[area] == 'undefined') {
|
|
qwiki.wiki_index.pages[area] = {
|
|
format: qwiki.getDefault('format')
|
|
};
|
|
}
|
|
// get our format
|
|
if ('format' in req.post) {
|
|
qwiki.wiki_index.pages[area].format = req.post['format'];
|
|
}
|
|
//
|
|
console.log(old_area + ' vs ' + area);
|
|
if (old_area != area) {
|
|
fs.rename('wiki/'+old_area+'.qwk', 'wiki/'+area+'.qwk', function() {
|
|
qwiki.deleteCache(old_area, function() {
|
|
if ('content' in req.post) {
|
|
qwiki.savePage(area, req.post['content'], function(err) {
|
|
if (err) {
|
|
res.write(err.code);
|
|
res.end();
|
|
} else {
|
|
qwiki.createCache(area, req.post['content'], function(err) {
|
|
if (err) {
|
|
res.write(err.code);
|
|
res.end();
|
|
} else {
|
|
res.writeHead(302, {'Location': '/'+area+'/edit'});
|
|
res.end();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
});
|
|
});
|
|
} else {
|
|
if ('content' in req.post) {
|
|
qwiki.savePage(area, req.post['content'], function(err) {
|
|
if (err) {
|
|
res.write(err.code);
|
|
res.end();
|
|
} else {
|
|
qwiki.createCache(area, req.post['content'], function(err) {
|
|
if (err) {
|
|
res.write(err.code);
|
|
res.end();
|
|
} else {
|
|
res.writeHead(302, {'Location': '/'+area+'/edit'});
|
|
res.end();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
res.writeHead(200, "OK", {
|
|
"Content-Type": "text/html",
|
|
});
|
|
qwiki.parsePage('edit', '', req, res);
|
|
});
|
|
// **** DELETE
|
|
qwiki.rule('delete', '@@CONTENT@@', function(req, res, instance, next) {
|
|
res.write('<form action="" method="POST"> Deleting this page will also delete all page revisions! Are you sure you wish to do this?<div class="prompt"><input type="submit" name="submit" value="Yes"><input type="submit" name="submit" value="No"></div></form>');
|
|
next()
|
|
});
|
|
qwiki.act('delete', function(req, res) {
|
|
var area = (req.url == '' ? 'front' : req.url.substr(1));
|
|
// handle POST
|
|
if ('submit' in req.post) {
|
|
if (req.post['submit'] == 'Yes') {
|
|
qwiki.deletePage(area, function() {
|
|
qwiki.deleteCache(area, function() {
|
|
console.log('redirecting to '+req.url);
|
|
res.writeHead(302, {'Location': req.url});
|
|
res.end();
|
|
});
|
|
});
|
|
return;
|
|
} else {
|
|
res.writeHead(302, {'Location': req.url+'/edit'});
|
|
res.end();
|
|
}
|
|
}
|
|
qwiki.parsePage('delete', 'edit', req, res);
|
|
});
|
|
// **** REVISIONS
|
|
qwiki.rule('revisions', '@@CONTENT@@', function(req, res, instance, next) {
|
|
var path = 'wiki/'+req.url+'.qwk';
|
|
res.write('revisions for ' + req.url.substr(1));
|
|
next()
|
|
});
|
|
qwiki.act('revisions', function(req, res) {
|
|
console.log("revisions action");
|
|
qwiki.parsePage('revisions', 'edit', req, res);
|
|
});
|
|
// **** SEARCH
|
|
qwiki.rule('search', '@@CONTENT@@', function(req, res, instance, next) {
|
|
if ('submit' in req.post) {
|
|
if ('search' in req.post) {
|
|
res.write('Results for: ' + req.post['search']);
|
|
}
|
|
}
|
|
next();
|
|
});
|
|
qwiki.act('search', function(req, res) {
|
|
res.writeHead(200, "OK", {
|
|
"Content-Type": "text/html",
|
|
});
|
|
qwiki.parsePage('search', '', req, res);
|
|
});
|
|
|
|
var readFile = function(stream, path, cb) {
|
|
fs.exists(path, function(exists) {
|
|
if (exists) {
|
|
var rs = fs.createReadStream(path);
|
|
rs.on('data', function(chunk) {
|
|
stream.write(chunk);
|
|
});
|
|
rs.on('end', function() {
|
|
cb('EOF');
|
|
});
|
|
rs.on('error', function(err) {
|
|
cb('ERR', err);
|
|
});
|
|
} else {
|
|
cb('FNF'); // file not found
|
|
}
|
|
});
|
|
};
|
|
|
|
var readFiles = function(dir, callback) {
|
|
fs.readdir(dir, function(err, files) {
|
|
files.forEach(function(file) {
|
|
var stat_file = dir+'/'+file
|
|
fs.stat(stat_file, function(err, stats) {
|
|
if (stats.isDirectory()) {
|
|
readFiles(stat_file, callback);
|
|
callback(err, stat_file, true);
|
|
} else if (stats.isFile()) {
|
|
callback(err, stat_file, false);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
};
|
|
|
|
var getExt = function(file) {
|
|
var last = file.lastIndexOf(".");
|
|
var ext = file.substr(last+1);
|
|
return ext;
|
|
};
|
|
|
|
var r_mkdir = function(dir, mode, cb) {
|
|
console.log('creating '+dir);
|
|
fs.mkdir(dir, mode, function(err) {
|
|
if (err && err.code == 'ENOENT') {
|
|
r_mkdir(m_path.dirname(dir), mode, r_mkdir.bind(this, dir, mode, cb));
|
|
} else if (typeof cb !== 'undefined') {
|
|
cb(err);
|
|
}
|
|
});
|
|
};
|
|
|
|
var port = 8080;
|
|
process.argv.forEach(function (val, index, array) {
|
|
var parts = val.split('=');
|
|
var key = '';
|
|
var value = '';
|
|
if (parts[0].substr(0, 2) == '--') {
|
|
key = parts[0].substr(2);
|
|
value = parts[1];
|
|
}
|
|
if (key == 'port') {
|
|
if (value == '') {
|
|
console.log(key + ' needs a value');
|
|
} else {
|
|
port = Number(value);
|
|
}
|
|
}
|
|
});
|
|
|
|
qwiki.listen(port);
|