openkb
Version:
openKB is an Open Source Nodejs Markdown based knowledge base/FAQ/Wiki app with powerful lunr search
1,248 lines (1,107 loc) • 60.4 kB
JavaScript
var express = require('express');
var path = require('path');
var router = express.Router();
var fs = require('fs');
var getSlug = require('speakingurl');
var common = require('./common');
var _ = require('lodash');
var mime = require('mime-types');
var config = common.read_config();
var appDir = path.dirname(require.main.filename);
// The homepage of the site
router.get('/', common.restrict, function (req, res, next){
var db = req.app.db;
common.config_expose(req.app);
var featuredCount = config.settings.featured_articles_count ? config.settings.featured_articles_count : 4;
// set the template dir
common.setTemplateDir('user', req);
// get sortBy from config, set to 'kb_viewcount' if nothing found
var sortByField = typeof config.settings.sort_by.field !== 'undefined' ? config.settings.sort_by.field : 'kb_viewcount';
var sortByOrder = typeof config.settings.sort_by.order !== 'undefined' ? config.settings.sort_by.order : -1;
var sortBy = {};
sortBy[sortByField] = sortByOrder;
// get the top results based on sort order
common.dbQuery(db.kb, {kb_published: 'true'}, sortBy, config.settings.num_top_results, function (err, top_results){
common.dbQuery(db.kb, {kb_published: 'true', kb_featured: 'true'}, sortBy, featuredCount, function (err, featured_results){
res.render('index', {
title: 'openKB',
user_page: true,
homepage: true,
top_results: top_results,
featured_results: featured_results,
session: req.session,
message: common.clear_session_value(req.session, 'message'),
message_type: common.clear_session_value(req.session, 'message_type'),
config: config,
current_url: req.protocol + '://' + req.get('host') + req.app_context,
fullUrl: req.protocol + '://' + req.get('host') + req.originalUrl,
helpers: req.handlebars,
show_footer: 'show_footer'
});
});
});
});
router.post('/protected/action', function (req, res){
var db = req.app.db;
// get article
db.kb.findOne({kb_published: 'true', _id: common.getId(req.body.kb_id)}, function (err, result){
// check password
if(req.body.password === result.kb_password){
// password correct. Allow viewing the article this time
req.session.pw_validated = 'true';
res.redirect(req.header('Referer'));
}else{
// password incorrect
req.session.pw_validated = null;
res.render('error', {message: 'Password incorrect. Please try again.', helpers: req.handlebars, config: config});
}
});
});
router.post('/search_api', function (req, res){
var index = common.getIndex();
res.status(200).json(index);
});
// vote on articles
router.post('/vote', function (req, res){
var db = req.app.db;
// if voting allowed
if(config.settings.allow_voting === true){
// check if voted
db.votes.findOne({$and: [{doc_id: req.body.doc_id}, {session_id: req.sessionID}]}, function (err, result){
// if not voted
if(!result){
var vote = req.body.vote_type === 'upvote' ? 1 : -1;
// update kb vote
db.kb.update({_id: common.getId(req.body.doc_id)}, {$inc: {kb_votes: vote}}, function (err, numReplaced){
// insert session id into table to stop muli-voters
db.votes.insert({doc_id: req.body.doc_id, session_id: req.sessionID}, function (err, newDoc){
res.writeHead(200, {'Content-Type': 'application/text'});
res.end('Vote successful');
});
});
}else{
// User has already voted
res.writeHead(404, {'Content-Type': 'application/text'});
res.end('User already voted');
}
});
}else{
// Voting not allowed
res.writeHead(404, {'Content-Type': 'application/text'});
res.end('Voting now allowed');
}
});
// Render a version of the article to logged in users
router.get('/' + config.settings.route_name + '/:id/version', common.restrict, function (req, res){
var db = req.app.db;
common.config_expose(req.app);
var classy = require('../public/javascripts/markdown-it-classy');
var markdownit = req.markdownit;
markdownit.use(classy);
// check for logged in user
if(!req.session.user){
res.render('error', {message: '404 - Page not found', helpers: req.handlebars, config: config});
return;
}
// get sortBy from config, set to 'kb_viewcount' if nothing found
var sortByField = typeof config.settings.sort_by.field !== 'undefined' ? config.settings.sort_by.field : 'kb_viewcount';
var sortByOrder = typeof config.settings.sort_by.order !== 'undefined' ? config.settings.sort_by.order : -1;
var sortBy = {};
sortBy[sortByField] = sortByOrder;
var featuredCount = config.settings.featured_articles_count ? config.settings.featured_articles_count : 4;
db.kb.findOne({_id: common.getId(req.params.id)}, function (err, result){
// show the view
common.dbQuery(db.kb, {kb_published: 'true', kb_versioned_doc: {$eq: true}}, sortBy, featuredCount, function (err, featured_results){
res.render('kb', {
title: result.kb_title,
result: result,
user_page: true,
kb_body: markdownit.render(result.kb_body),
featured_results: featured_results,
config: config,
session: req.session,
current_url: req.protocol + '://' + req.get('host') + req.app_context,
fullUrl: req.protocol + '://' + req.get('host') + req.originalUrl,
message: common.clear_session_value(req.session, 'message'),
message_type: common.clear_session_value(req.session, 'message_type'),
helpers: req.handlebars,
show_footer: 'show_footer'
});
});
});
});
router.get('/' + config.settings.route_name + '/:id', common.restrict, function (req, res){
var db = req.app.db;
common.config_expose(req.app);
var classy = require('../public/javascripts/markdown-it-classy');
var markdownit = req.markdownit;
markdownit.use(classy);
var featuredCount = config.settings.featured_articles_count ? config.settings.featured_articles_count : 4;
// set the template dir
common.setTemplateDir('user', req);
// get sortBy from config, set to 'kb_viewcount' if nothing found
var sortByField = typeof config.settings.sort_by.field !== 'undefined' ? config.settings.sort_by.field : 'kb_viewcount';
var sortByOrder = typeof config.settings.sort_by.order !== 'undefined' ? config.settings.sort_by.order : -1;
var sortBy = {};
sortBy[sortByField] = sortByOrder;
db.kb.findOne({$or: [{_id: common.getId(req.params.id)}, {kb_permalink: req.params.id}], kb_versioned_doc: {$ne: true}}, function (err, result){
// render 404 if page is not published
if(result == null || result.kb_published === 'false'){
res.render('error', {message: '404 - Page not found', helpers: req.handlebars, config: config});
}else{
// check if has a password
if(result.kb_password){
if(result.kb_password !== ''){
if(req.session.pw_validated === 'false' || req.session.pw_validated === undefined || req.session.pw_validated == null){
res.render('protected_kb', {
title: 'Protected Article',
result: result,
config: config,
session: req.session,
helpers: req.handlebars
});
return;
}
}
}
// add to old view count
var old_viewcount = result.kb_viewcount;
if(old_viewcount == null){
old_viewcount = 0;
}
var new_viewcount = old_viewcount;
// increment if the user is logged in and if settings say so
if(req.session.user && config.settings.update_view_count_logged_in){
new_viewcount = old_viewcount + 1;
}
// increment if the user is a guest and not logged in
if(!req.session.user){
new_viewcount = old_viewcount + 1;
}
// update kb_viewcount
db.kb.update({$or: [{_id: common.getId(req.params.id)}, {kb_permalink: req.params.id}]},
{
$set: {kb_viewcount: new_viewcount}
}, {multi: false}, function (err, numReplaced){
// clear session auth and render page
req.session.pw_validated = null;
// show the view
common.dbQuery(db.kb, {kb_published: 'true'}, sortBy, featuredCount, function (err, featured_results){
res.render('kb', {
title: result.kb_title,
result: result,
user_page: true,
kb_body: markdownit.render(result.kb_body),
featured_results: featured_results,
config: config,
session: req.session,
current_url: req.protocol + '://' + req.get('host') + req.app_context,
fullUrl: req.protocol + '://' + req.get('host') + req.originalUrl,
message: common.clear_session_value(req.session, 'message'),
message_type: common.clear_session_value(req.session, 'message_type'),
helpers: req.handlebars,
show_footer: 'show_footer'
});
});
});
}
});
});
// render the settings page
router.get('/settings', common.restrict, function (req, res){
var junk = require('junk');
// only allow admin
if(req.session.is_admin !== 'true'){
res.render('error', {message: 'Access denied', helpers: req.handlebars, config: config});
return;
}
// path to themes
var themePath = path.join(__dirname, '../public/themes');
fs.readdir(themePath, function (err, files){
res.render('settings', {
title: 'Settings',
session: req.session,
themes: files.filter(junk.not),
locale: Object.keys(req.i18n.locales),
message: common.clear_session_value(req.session, 'message'),
message_type: common.clear_session_value(req.session, 'message_type'),
config: config,
helpers: req.handlebars
});
});
});
// update the settings
router.post('/update_settings', common.restrict, function (req, res){
// only allow admin
if(req.session.is_admin !== 'true'){
res.render('error', {message: 'Access denied', helpers: req.handlebars, config: config});
return;
}
// get the new settings
var settings = req.body;
// possible boolean type values
var booleanArray = [true, 'true', false, 'false'];
// loop settings, update config
for(var key in settings){
if(Object.prototype.hasOwnProperty.call(settings, key)){
// if true/false, convert to boolean - TODO: Figure a better way of doing this?
var settingValue = settings[key];
if(booleanArray.indexOf(settingValue) > -1){
settingValue = (settingValue === 'true');
}
config.settings[key] = settingValue;
}
}
// write settings to file
var dir = path.join(__dirname, '..', 'config');
if(!fs.existsSync(dir)){
fs.mkdirSync(dir);
}
fs.writeFileSync(path.join(dir, 'config.json'), JSON.stringify(config, null, 4), 'utf8');
if(config.settings.locale){
req.i18n.setLocale(config.settings.locale);
res.cookie('locale', config.settings.locale);
req.i18n.setLocaleFromCookie();
}
// set notification
req.session.message = req.i18n.__('Settings successfully updated.');
req.session.message_type = 'success';
// redirect back
res.redirect(req.app_context + '/settings');
});
// resets the view count of a given article ID
router.get('/' + config.settings.route_name + '/resetviewCount/:id', common.restrict, function (req, res){
var db = req.app.db;
db.kb.update({_id: common.getId(req.params.id)}, {$set: {kb_viewcount: 0}}, {multi: false}, function (err, numReplaced){
if(err){
req.session.message = req.i18n.__('View count could not be reset. Try again.');
req.session.message_type = 'danger';
}else{
req.session.message = req.i18n.__('View count successfully reset to zero.');
req.session.message_type = 'success';
}
// redirect to new doc
res.redirect(req.app_context + '/edit/' + req.params.id);
});
});
// resets the vote count of a given article ID
router.get('/' + config.settings.route_name + '/resetvoteCount/:id', common.restrict, function (req, res){
var db = req.app.db;
db.kb.update({_id: common.getId(req.params.id)}, {$set: {kb_votes: 0}}, {multi: false}, function (err, numReplaced){
if(err){
req.session.message = req.i18n.__('Vote count could not be reset. Try again.');
req.session.message_type = 'danger';
}else{
req.session.message = req.i18n.__('Vote count successfully reset to zero.');
req.session.message_type = 'success';
}
// redirect to new doc
res.redirect(req.app_context + '/edit/' + req.params.id);
});
});
// render the editor
router.get('/edit/:id', common.restrict, function (req, res){
var db = req.app.db;
common.config_expose(req.app);
db.kb.findOne({_id: common.getId(req.params.id), kb_versioned_doc: {$ne: true}}, function (err, result){
if(!result){
res.render('error', {message: '404 - Page not found', helpers: req.handlebars, config: config});
return;
}
common.dbQuery(db.kb, {kb_parent_id: req.params.id}, {kb_last_updated: -1}, 20, function (err, versions){
res.render('edit', {
title: 'Edit article',
result: result,
versions: versions,
session: req.session,
message: common.clear_session_value(req.session, 'message'),
message_type: common.clear_session_value(req.session, 'message_type'),
config: config,
editor: true,
helpers: req.handlebars
});
});
});
});
// insert new KB form action
router.post('/insert_kb', common.restrict, function (req, res){
var db = req.app.db;
var doc = {
kb_permalink: req.body.frm_kb_permalink,
kb_title: req.body.frm_kb_title,
kb_body: req.body.frm_kb_body,
kb_published: req.body.frm_kb_published,
kb_keywords: req.body.frm_kb_keywords,
kb_published_date: new Date(),
kb_last_updated: new Date(),
kb_last_update_user: req.session.users_name + ' - ' + req.session.user,
kb_author: req.session.users_name,
kb_author_email: req.session.user
};
db.kb.count({'kb_permalink': req.body.frm_kb_permalink}, function (err, kb){
if(kb > 0 && req.body.frm_kb_permalink !== ''){
// permalink exits
req.session.message = req.i18n.__('Permalink already exists. Pick a new one.');
req.session.message_type = 'danger';
// keep the current stuff
req.session.kb_title = req.body.frm_kb_title;
req.session.kb_body = req.body.frm_kb_body;
req.session.kb_keywords = req.body.frm_kb_keywords;
req.session.kb_permalink = req.body.frm_kb_permalink;
// redirect to insert
res.redirect(req.app_context + '/insert');
}else{
db.kb.insert(doc, function (err, newDoc){
if(err){
console.error('Error inserting document: ' + err);
// keep the current stuff
req.session.kb_title = req.body.frm_kb_title;
req.session.kb_body = req.body.frm_kb_body;
req.session.kb_keywords = req.body.frm_kb_keywords;
req.session.kb_permalink = req.body.frm_kb_permalink;
req.session.message = req.i18n.__('Error') + ': ' + err;
req.session.message_type = 'danger';
// redirect to insert
res.redirect(req.app_context + '/insert');
}else{
// setup keywords
var keywords = '';
if(req.body.frm_kb_keywords !== undefined){
keywords = req.body.frm_kb_keywords.toString().replace(/,/g, ' ');
}
// get the new ID
var newId = newDoc._id;
if(config.settings.database.type !== 'embedded'){
newId = newDoc.insertedIds;
}
// add to store
var href = req.body.frm_kb_permalink !== '' ? req.body.frm_kb_permalink : newId;
common.updateStore(newId, {t: req.body.frm_kb_title, p: href});
// add to lunr index
common.buildIndex(db, function(){
req.session.message = req.i18n.__('New article successfully created');
req.session.message_type = 'success';
// redirect to new doc
res.redirect(req.app_context + '/edit/' + newId);
});
}
});
}
});
});
// Update an existing KB article form action
router.get('/suggest', common.suggest_allowed, function (req, res){
// set the template dir
common.setTemplateDir('admin', req);
res.render('suggest', {
title: 'Suggest article',
config: config,
editor: true,
is_admin: req.session.is_admin,
helpers: req.handlebars,
message: common.clear_session_value(req.session, 'message'),
message_type: common.clear_session_value(req.session, 'message_type'),
session: req.session
});
});
// Update an existing KB article form action
router.post('/insert_suggest', common.suggest_allowed, function (req, res){
var db = req.app.db;
// if empty, remove the comma and just have a blank string
var keywords = req.body.frm_kb_keywords;
if(common.safe_trim(keywords) === ','){
keywords = '';
}
var doc = {
kb_title: req.body.frm_kb_title + ' (SUGGESTION)',
kb_body: req.body.frm_kb_body,
kb_published: 'false',
kb_keywords: keywords,
kb_published_date: new Date(),
kb_last_updated: new Date()
};
db.kb.insert(doc, function (err, newDoc){
if(err){
console.error('Error inserting suggestion: ' + err);
req.session.message = req.i18n.__('Suggestion failed. Please contact admin.');
req.session.message_type = 'danger';
res.redirect(req.app_context + '/');
}else{
// update store
var href = newId;
common.updateStore(newId, {t: req.body.frm_kb_title, p: href})
// rebuild index
common.buildIndex(db, function(){
// redirect to new doc
req.session.message = req.i18n.__('Suggestion successfully processed');
req.session.message_type = 'success';
res.redirect(req.app_context + '/');
});
}
});
});
// Update an existing KB article form action
router.post('/save_kb', common.restrict, function (req, res){
var db = req.app.db;
var kb_featured = req.body.frm_kb_featured === 'on' ? 'true' : 'false';
// if empty, remove the comma and just have a blank string
var keywords = req.body.frm_kb_keywords;
if(common.safe_trim(keywords) === ','){
keywords = '';
}
db.kb.count({'kb_permalink': req.body.frm_kb_permalink, $not: {_id: common.getId(req.body.frm_kb_id)}, kb_versioned_doc: {$ne: true}}, function (err, kb){
if(kb > 0 && req.body.frm_kb_permalink !== ''){
// permalink exits
req.session.message = req.i18n.__('Permalink already exists. Pick a new one.');
req.session.message_type = 'danger';
// keep the current stuff
req.session.kb_title = req.body.frm_kb_title;
req.session.kb_body = req.body.frm_kb_body;
req.session.kb_keywords = req.body.frm_kb_keywords;
req.session.kb_permalink = req.body.frm_kb_permalink;
req.session.kb_featured = kb_featured;
req.session.kb_seo_title = req.body.frm_kb_seo_title;
req.session.kb_seo_description = req.body.frm_kb_seo_description;
req.session.b_edit_reason = req.body.frm_kb_edit_reason;
// redirect to insert
res.redirect(req.app_context + '/edit/' + req.body.frm_kb_id);
}else{
db.kb.findOne({_id: common.getId(req.body.frm_kb_id)}, function (err, article){
// update author if not set
var author = article.kb_author ? article.kb_author : req.session.users_name;
var author_email = article.kb_author_email ? article.kb_author_email : req.session.user;
// set published date to now if none exists
var published_date;
if(article.kb_published_date == null || article.kb_published_date === undefined){
published_date = new Date();
}else{
published_date = article.kb_published_date;
}
// update our old doc
db.kb.update({_id: common.getId(req.body.frm_kb_id)}, {
$set: {
kb_title: req.body.frm_kb_title,
kb_body: req.body.frm_kb_body,
kb_published: req.body.frm_kb_published,
kb_keywords: keywords,
kb_last_updated: new Date(),
kb_last_update_user: req.session.users_name + ' - ' + req.session.user,
kb_author: author,
kb_author_email: author_email,
kb_published_date: published_date,
kb_password: req.body.frm_kb_password,
kb_permalink: req.body.frm_kb_permalink,
kb_featured: kb_featured,
kb_seo_title: req.body.frm_kb_seo_title,
kb_seo_description: req.body.frm_kb_seo_description
}
}, {}, function (err, numReplaced){
if(err){
console.error('Failed to save KB: ' + err);
req.session.message = req.i18n.__('Failed to save. Please try again');
req.session.message_type = 'danger';
res.redirect(req.app_context + '/edit/' + req.body.frm_kb_id);
}else{
// setup keywords
var keywords = '';
if(req.body.frm_kb_keywords !== undefined){
keywords = req.body.frm_kb_keywords.toString().replace(/,/g, ' ');
}
// update store
var href = req.body.frm_kb_permalink !== '' ? req.body.frm_kb_permalink : req.body.frm_kb_id;
common.updateStore(req.body.frm_kb_id, {t: req.body.frm_kb_title, p: href});
// rebuild the index
common.buildIndex(db, function(){
var article_versioning = config.settings.article_versioning ? config.settings.article_versioning : false;
// if versions turned on, insert a doc to track versioning
if(article_versioning === true){
// version doc
var version_doc = {
kb_title: req.body.frm_kb_title,
kb_parent_id: req.body.frm_kb_id,
kb_versioned_doc: true,
kb_edit_reason: req.body.frm_kb_edit_reason,
kb_body: req.body.frm_kb_body,
kb_published: false,
kb_keywords: keywords,
kb_last_updated: new Date(),
kb_last_update_user: req.session.users_name + ' - ' + req.session.user,
kb_author: author,
kb_author_email: author_email,
kb_published_date: published_date,
kb_password: req.body.frm_kb_password,
kb_permalink: req.body.frm_kb_permalink,
kb_featured: kb_featured,
kb_seo_title: req.body.frm_kb_seo_title,
kb_seo_description: req.body.frm_kb_seo_description
};
// insert a doc to track versioning
db.kb.insert(version_doc, function (err, version_doc){
req.session.message = req.i18n.__('Successfully saved');
req.session.message_type = 'success';
res.redirect(req.app_context + '/edit/' + req.body.frm_kb_id);
});
}else{
req.session.message = req.i18n.__('Successfully saved');
req.session.message_type = 'success';
res.redirect(req.app_context + '/edit/' + req.body.frm_kb_id);
}
});
}
});
});
}
});
});
// logout
router.get('/logout', function (req, res){
req.session.user = null;
req.session.users_name = null;
req.session.is_admin = null;
req.session.pw_validated = null;
req.session.message = null;
req.session.message_type = null;
res.redirect(req.app_context + '/');
});
// users
router.get('/users', common.restrict, function (req, res){
// only allow admin
if(req.session.is_admin !== 'true'){
res.render('error', {message: 'Access denied', helpers: req.handlebars, config: config});
return;
}
var db = req.app.db;
common.dbQuery(db.users, {}, null, null, function (err, users){
res.render('users', {
title: 'Users',
users: users,
config: config,
is_admin: req.session.is_admin,
helpers: req.handlebars,
session: req.session,
message: common.clear_session_value(req.session, 'message'),
message_type: common.clear_session_value(req.session, 'message_type')
});
});
});
// users
router.get('/user/edit/:id', common.restrict, function (req, res){
var db = req.app.db;
db.users.findOne({_id: common.getId(req.params.id)}, function (err, user){
// if the user we want to edit is not the current logged in user and the current user is not
// an admin we render an access denied message
if(user.user_email !== req.session.user && req.session.is_admin === 'false'){
req.session.message = req.i18n.__('Access denied');
req.session.message_type = 'danger';
res.redirect(req.app_context + '/Users/');
return;
}
res.render('user_edit', {
title: 'User edit',
user: user,
session: req.session,
message: common.clear_session_value(req.session, 'message'),
message_type: common.clear_session_value(req.session, 'message_type'),
helpers: req.handlebars,
config: config
});
});
});
// users
router.get('/users/new', common.restrict, function (req, res){
// only allow admin
if(req.session.is_admin !== 'true'){
res.render('error', {message: 'Access denied', helpers: req.handlebars, config: config});
return;
}
res.render('user_new', {
title: 'User - New',
session: req.session,
message: common.clear_session_value(req.session, 'message'),
message_type: common.clear_session_value(req.session, 'message_type'),
config: config,
helpers: req.handlebars
});
});
// kb list
router.get('/articles', common.restrict, function (req, res){
var db = req.app.db;
common.dbQuery(db.kb, {kb_versioned_doc: {$ne: true}}, {kb_published_date: -1}, 10, function (err, articles){
res.render('articles', {
title: 'Articles',
articles: articles,
session: req.session,
message: common.clear_session_value(req.session, 'message'),
message_type: common.clear_session_value(req.session, 'message_type'),
config: config,
helpers: req.handlebars
});
});
});
router.get('/articles/all', common.restrict, function (req, res){
var db = req.app.db;
common.dbQuery(db.kb, {kb_versioned_doc: {$ne: true}}, {kb_published_date: -1}, null, function (err, articles){
res.render('articles', {
title: 'Articles',
articles: articles,
session: req.session,
message: common.clear_session_value(req.session, 'message'),
message_type: common.clear_session_value(req.session, 'message_type'),
config: config,
helpers: req.handlebars
});
});
});
router.get('/articles/:tag', function (req, res){
var db = req.app.db;
var lunr_index = common.getIndex().index;
// we strip the ID's from the lunr index search
var lunr_id_array = [];
lunr_index.search(req.params.tag).forEach(function (id){
lunr_id_array.push(id.ref);
});
// we search on the lunr indexes
common.dbQuery(db.kb, {_id: {$in: lunr_id_array}}, {kb_published_date: -1}, null, function (err, results){
res.render('articles', {
title: 'Articles',
results: results,
session: req.session,
message: common.clear_session_value(req.session, 'message'),
message_type: common.clear_session_value(req.session, 'message_type'),
search_term: req.params.tag,
config: config,
helpers: req.handlebars
});
});
});
// update the published state based on an ajax call from the frontend
router.post('/published_state', common.restrict, function (req, res){
var db = req.app.db;
db.kb.update({_id: common.getId(req.body.id)}, {$set: {kb_published: req.body.state}}, {multi: false}, function (err, numReplaced){
if(err){
console.error('Failed to update the published state: ' + err);
res.writeHead(400, {'Content-Type': 'application/text'});
res.end('Published state not updated');
}else{
res.writeHead(200, {'Content-Type': 'application/text'});
res.end('Published state updated');
}
});
});
// insert a user
router.post('/user_insert', common.restrict, function (req, res){
var db = req.app.db;
var bcrypt = req.bcrypt;
var url = require('url');
// set the account to admin if using the setup form. Eg: First user account
var url_parts = url.parse(req.header('Referer'));
// check if account being setup from the /setup route.
// probably not the most elegent code but does the job.
var is_admin = 'false';
if(typeof config.settings.app_context !== 'undefined' && config.settings.app_context !== ''){
if(url_parts.path === '/' + config.settings.app_context + '/setup'){
is_admin = 'true';
}
}else if(url_parts.path === '/setup'){
is_admin = 'true';
}
// sets up the document
var doc = {
users_name: req.body.users_name,
user_email: req.body.user_email,
user_password: bcrypt.hashSync(req.body.user_password),
is_admin: is_admin
};
// check for existing user
db.users.findOne({'user_email': req.body.user_email}, function (err, user){
if(user){
// user already exists with that email address
console.error('Failed to insert user, possibly already exists: ' + err);
req.session.message = req.i18n.__('A user with that email address already exists');
req.session.message_type = 'danger';
res.redirect(req.app_context + '/users/new');
}else{
// email is ok to be used.
db.users.insert(doc, function (err, doc){
// show the view
if(err){
console.error('Failed to insert user: ' + err);
req.session.message = req.i18n.__('User exists');
req.session.message_type = 'danger';
res.redirect(req.app_context + '/user/edit/' + doc._id);
}else{
req.session.message = req.i18n.__('User account inserted');
req.session.message_type = 'success';
// if from setup we add user to session and redirect to login.
// Otherwise we show users screen
if(url_parts.path === '/setup'){
req.session.user = req.body.user_email;
res.redirect(req.app_context + '/login');
}else{
res.redirect(req.app_context + '/Users');
}
}
});
}
});
});
// update a user
router.post('/user_update', common.restrict, function (req, res){
var db = req.app.db;
var bcrypt = req.bcrypt;
var is_admin = req.body.user_admin === 'on' ? 'true' : 'false';
// get the user we want to update
db.users.findOne({_id: common.getId(req.body.user_id)}, function (err, user){
// if the user we want to edit is not the current logged in user and the current user is not
// an admin we render an access denied message
if(user.user_email !== req.session.user && req.session.is_admin === 'false'){
req.session.message = req.i18n.__('Access denied');
req.session.message_type = 'danger';
res.redirect(req.app_context + '/Users/');
return;
}
// if editing your own account, retain admin true/false
if(user.user_email === req.session.user){
is_admin = user.is_admin;
}
// create the update doc
var update_doc = {};
update_doc.is_admin = is_admin;
update_doc.users_name = req.body.users_name;
if(req.body.user_password){
update_doc.user_password = bcrypt.hashSync(req.body.user_password);
}
db.users.update({_id: common.getId(req.body.user_id)},
{
$set: update_doc
}, {multi: false}, function (err, numReplaced){
if(err){
console.error('Failed updating user: ' + err);
req.session.message = req.i18n.__('Failed to update user');
req.session.message_type = 'danger';
res.redirect(req.app_context + '/user/edit/' + req.body.user_id);
}else{
// show the view
req.session.message = req.i18n.__('User account updated.');
req.session.message_type = 'success';
res.redirect(req.app_context + '/user/edit/' + req.body.user_id);
}
});
});
});
// login form
router.get('/login', function (req, res){
var db = req.app.db;
// set the template
common.setTemplateDir('admin', req);
db.users.count({}, function (err, user_count){
// we check for a user. If one exists, redirect to login form otherwise setup
if(user_count > 0){
// set needs_setup to false as a user exists
req.session.needs_setup = false;
res.render('login', {
title: 'Login',
referring_url: req.header('Referer'),
config: config,
message: common.clear_session_value(req.session, 'message'),
message_type: common.clear_session_value(req.session, 'message_type'),
show_footer: 'show_footer',
helpers: req.handlebars
});
}else{
// if there are no users set the "needs_setup" session
req.session.needs_setup = true;
res.redirect(req.app_context + '/setup');
}
});
});
// setup form is shown when there are no users setup in the DB
router.get('/setup', function (req, res){
var db = req.app.db;
db.users.count({}, function (err, user_count){
// dont allow the user to "re-setup" if a user exists.
// set needs_setup to false as a user exists
req.session.needs_setup = false;
if(user_count === 0){
res.render('setup', {
title: 'Setup',
config: config,
message: common.clear_session_value(req.session, 'message'),
message_type: common.clear_session_value(req.session, 'message_type'),
show_footer: 'show_footer',
helpers: req.handlebars
});
}else{
res.redirect(req.app_context + '/login');
}
});
});
// Loops files on the disk, checks for their existance in any KB articles and removes non used files.
router.get('/file_cleanup', common.restrict, function (req, res){
var db = req.app.db;
var path = require('path');
var fs = require('fs');
var walk = require('walk');
var walkPath = path.join(appDir, 'public', 'uploads', 'inline_files');
var walker = walk.walk(walkPath, {followLinks: false});
// only allow admin
if(req.session.is_admin !== 'true'){
res.render('error', {message: 'Access denied', helpers: req.handlebars, config: config});
return;
}
walker.on('file', function (root, stat, next){
var file_name = path.resolve(root, stat.name);
// find posts with the file in question
common.dbQuery(db.kb, {'kb_body': new RegExp(stat.name)}, null, null, function (err, posts){
// if the images doesn't exists in any posts then we remove it
if(posts.length === 0){
fs.unlinkSync(file_name);
}
next();
});
});
walker.on('end', function (){
req.session.message = req.i18n.__('All unused files have been removed');
req.session.message_type = 'success';
res.redirect(req.app_context + req.header('Referer'));
});
});
// login the user and check the password
router.post('/login_action', function (req, res){
var db = req.app.db;
var bcrypt = req.bcrypt;
var url = require('url');
db.users.findOne({user_email: req.body.email}, function (err, user){
// check if user exists with that email
if(user === undefined || user === null){
req.session.message = req.i18n.__('A user with that email does not exist.');
req.session.message_type = 'danger';
res.redirect(req.app_context + '/login');
}else{
// we have a user under that email so we compare the password
if(bcrypt.compareSync(req.body.password, user.user_password) === true){
req.session.user = req.body.email;
req.session.users_name = user.users_name;
req.session.user_id = user._id.toString();
req.session.is_admin = user.is_admin;
if(req.body.frm_referring_url === undefined || req.body.frm_referring_url === ''){
res.redirect(req.app_context + '/');
}else{
var url_parts = url.parse(req.body.frm_referring_url, true);
if(url_parts.pathname !== '/setup' && url_parts.pathname !== req.app_context + '/login'){
res.redirect(req.body.frm_referring_url);
}else{
res.redirect(req.app_context + '/');
}
}
}else{
// password is not correct
req.session.message = req.i18n.__('Access denied. Check password and try again.');
req.session.message_type = 'danger';
res.redirect(req.app_context + '/login');
}
}
});
});
// delete user
router.get('/user/delete/:id', common.restrict, function (req, res){
// only allow admin
if(req.session.is_admin !== 'true'){
res.render('error', {message: 'Access denied', helpers: req.handlebars, config: config});
return;
}
var db = req.app.db;
// remove the article
if(req.session.is_admin === 'true'){
db.users.remove({_id: common.getId(req.params.id)}, {}, function (err, numRemoved){
req.session.message = req.i18n.__('User deleted.');
req.session.message_type = 'success';
res.redirect(req.app_context + '/users');
});
}else{
req.session.message = req.i18n.__('Access denied.');
req.session.message_type = 'danger';
res.redirect(req.app_context + '/users');
}
});
// delete article
router.get('/delete/:id', common.restrict, function (req, res){
var db = req.app.db;
// remove the article
db.kb.remove({_id: common.getId(req.params.id)}, {}, function (err, numRemoved){
// setup keywords
var keywords = '';
if(req.body.frm_kb_keywords !== undefined){
keywords = req.body.frm_kb_keywords.toString().replace(/,/g, ' ');
}
// create lunr doc
var lunr_doc = {
id: req.params.id
};
// remove from store
common.removeStore(req.params.id);
// remove from index
common.buildIndex(db, function(){
// redirect home
req.session.message = req.i18n.__('Article successfully deleted');
req.session.message_type = 'success';
res.redirect(req.app_context + '/articles');
})
});
});
var multer_upload = require('multer');
var inline_upload = multer_upload({dest: path.join(appDir, 'public', 'uploads', 'inline_files')});
router.post('/file/upload_file', common.restrict, inline_upload.single('file'), function (req, res, next){
var fs = require('fs');
if(req.file){
// check for upload select
var upload_dir = path.join(appDir, 'public', 'uploads', 'inline_files');
var relative_upload_dir = req.app_context + '/uploads/inline_files';
var file = req.file;
var source = fs.createReadStream(file.path);
var dest = fs.createWriteStream(path.join(upload_dir, file.originalname));
// save the new file
source.pipe(dest);
source.on('end', function (){ });
// delete the temp file.
fs.unlink(file.path, function (err){ });
// uploaded
res.writeHead(200, {'Content-Type': 'application/json'});
res.end(JSON.stringify({'filename': relative_upload_dir + '/' + file.originalname}));
return;
}
res.writeHead(500, {'Content-Type': 'application/json'});
res.end(JSON.stringify({'filename': 'fail'}, null, 3));
return;
});
router.post('/file/new_dir', common.restrict, function (req, res, next){
var mkdirp = require('mkdirp');
// if new directory exists
if(req.body.custom_dir){
mkdirp(path.join(appDir, 'public', 'uploads', req.body.custom_dir), function (err){
if(err){
console.error('Directory creation error: ' + err);
req.session.message = req.i18n.__('Directory creation error. Please try again');
req.session.message_type = 'danger';
res.redirect(req.app_context + '/files');
}else{
req.session.message = req.i18n.__('Directory successfully created');
req.session.message_type = 'success';
res.redirect(req.app_context + '/files');
}
});
}else{
req.session.message = req.i18n.__('Please enter a directory name');
req.session.message_type = 'danger';
res.redirect(req.app_context + '/files');
}
});
// upload the file
var multer = require('multer');
var upload = multer({dest: path.join(appDir, 'public', 'uploads')});
router.post('/file/upload', common.restrict, upload.single('upload_file'), function (req, res, next){
var fs = require('fs');
if(req.file){
// check for upload select
var upload_dir = path.join(appDir, 'public', 'uploads');
if(req.body.directory !== '/uploads'){
upload_dir = path.join(appDir, 'public/', req.body.directory);
}
var file = req.file;
var source = fs.createReadStream(file.path);
var dest = fs.createWriteStream(path.join(upload_dir, file.originalname.replace(/ /g, '_')));
// save the new file
source.pipe(dest);
source.on('end', function (){ });
// delete the temp file.
fs.unlink(file.path, function (err){ });
req.session.message = req.i18n.__('File uploaded successfully');
req.session.message_type = 'success';
res.redirect(req.app_context + '/files');
}else{
req.session.message = req.i18n.__('File upload error. Please select a file.');
req.session.message_type = 'danger';
res.redirect(req.app_context + '/files');
}
});
// delete a file via ajax request
router.post('/file/delete', common.restrict, function (req, res){
var fs = require('fs');
// only allow admin
if(req.session.is_admin !== 'true'){
res.writeHead(400, {'Content-Type': 'application/text'});
res.end('Access denied');
return;
}
req.session.message = null;
req.session.message_type = null;
fs.unlink('public/' + req.body.img, function (err){
if(err){
console.error('File delete error: ' + err);
res.writeHead(400, {'Content-Type': 'application/text'});
res.end('Failed to delete file: ' + err);
}else{
res.writeHead(200, {'Content-Type': 'application/text'});
res.end('File deleted successfully');
}
});
});
router.get('/files', common.restrict, function (req, res){
var glob = require('glob');
var fs = require('fs');
// only allow admin
if(req.session.is_admin !== 'true'){
res.render('error', {message: 'Access denied', helpers: req.handlebars, config: config});
return;
}
// loop files in /public/uploads/
glob('public/uploads/**', {nosort: true}, function (er, files){
// sort array
files.sort();
// declare the array of objects
var file_list = [];
var dir_list = [];
// loop these files
for(var i = 0; i < files.length; i++){
if(fs.existsSync(files[i])){
if(fs.lstatSync(files[i]).isDirectory() === false){
// declare the file object and set its values
var file = {
id: i,
path: files[i].substring(6)
};
// push the file object into the array
file_list.push(file);
}else{
var dir = {
id: i,
path: files[i].substring(6)
};
// push the dir object into the array
dir_list.push(dir);
}
}
}
// render the files route
res.render('files', {
title: 'Files',
files: file_list,
dirs: dir_list,
session: req.session,
config: config,
message: common.clear_session_value(req.session, 'message'),
message_type: common.clear_session_value(req.session, 'message_type'),
helpers: req.handlebars
});
});
});
// insert form
router.get('/insert', common.restrict, function (req, res){
res.render('insert', {
title: 'Insert new',
session: req.session,
kb_title: common.clear_session_value(req.session, 'kb_title'),
kb_body: common.clear_session_value(req.session, 'kb_body'),
kb_keywords: common.clear_session_value(req.session, 'kb_keywords'),
kb_permalink: common.clear_session_value(req.session, 'kb_permalink'),
message: common