610 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			610 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
| //response
 | |
| //external modules
 | |
| var fs = require('fs');
 | |
| var path = require('path');
 | |
| var markdownpdf = require("markdown-pdf");
 | |
| var LZString = require('lz-string');
 | |
| var S = require('string');
 | |
| var shortId = require('shortid');
 | |
| var querystring = require('querystring');
 | |
| var request = require('request');
 | |
| var moment = require('moment');
 | |
| 
 | |
| //core
 | |
| var config = require("./config.js");
 | |
| var logger = require("./logger.js");
 | |
| var models = require("./models");
 | |
| 
 | |
| //public
 | |
| var response = {
 | |
|     errorForbidden: function (res) {
 | |
|         responseError(res, "403", "Forbidden", "oh no.");
 | |
|     },
 | |
|     errorNotFound: function (res) {
 | |
|         responseError(res, "404", "Not Found", "oops.");
 | |
|     },
 | |
|     errorBadRequest: function (res) {
 | |
|         responseError(res, "400", "Bad Request", "something not right.");
 | |
|     },
 | |
|     errorInternalError: function (res) {
 | |
|         responseError(res, "500", "Internal Error", "wtf.");
 | |
|     },
 | |
|     errorServiceUnavailable: function (res) {
 | |
|         res.status(503).send("I'm busy right now, try again later.");
 | |
|     },
 | |
|     newNote: newNote,
 | |
|     showNote: showNote,
 | |
|     showPublishNote: showPublishNote,
 | |
|     showPublishSlide: showPublishSlide,
 | |
|     showIndex: showIndex,
 | |
|     noteActions: noteActions,
 | |
|     publishNoteActions: publishNoteActions,
 | |
|     publishSlideActions: publishSlideActions,
 | |
|     githubActions: githubActions,
 | |
|     gitlabActions: gitlabActions
 | |
| };
 | |
| 
 | |
| function responseError(res, code, detail, msg) {
 | |
|     res.status(code).render(config.errorpath, {
 | |
|         url: config.serverurl,
 | |
|         title: code + ' ' + detail + ' ' + msg,
 | |
|         code: code,
 | |
|         detail: detail,
 | |
|         msg: msg,
 | |
| 		useCDN: config.usecdn
 | |
|     });
 | |
| }
 | |
| 
 | |
| function showIndex(req, res, next) {
 | |
|     res.render(config.indexpath, {
 | |
|         url: config.serverurl,
 | |
|         useCDN: config.usecdn,
 | |
|         allowAnonymous: config.allowanonymous,
 | |
|         facebook: config.facebook,
 | |
|         twitter: config.twitter,
 | |
|         github: config.github,
 | |
|         gitlab: config.gitlab,
 | |
|         dropbox: config.dropbox,
 | |
|         google: config.google,
 | |
|         ldap: config.ldap,
 | |
|         email: config.email,
 | |
|         allowemailregister: config.allowemailregister,
 | |
|         signin: req.isAuthenticated(),
 | |
|         infoMessage: req.flash('info'),
 | |
|         errorMessage: req.flash('error')
 | |
|     });
 | |
| }
 | |
| 
 | |
| function responseHackMD(res, note) {
 | |
|     var body = note.content;
 | |
|     var extracted = models.Note.extractMeta(body);
 | |
|     var meta = models.Note.parseMeta(extracted.meta);
 | |
|     var title = models.Note.decodeTitle(note.title);
 | |
|     title = models.Note.generateWebTitle(meta.title || title);
 | |
|     res.set({
 | |
|         'Cache-Control': 'private', // only cache by client
 | |
|         'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
 | |
|     });
 | |
|     res.render(config.hackmdpath, {
 | |
|         url: config.serverurl,
 | |
|         title: title,
 | |
|         useCDN: config.usecdn,
 | |
|         allowAnonymous: config.allowanonymous,
 | |
|         facebook: config.facebook,
 | |
|         twitter: config.twitter,
 | |
|         github: config.github,
 | |
|         gitlab: config.gitlab,
 | |
|         dropbox: config.dropbox,
 | |
|         google: config.google,
 | |
|         ldap: config.ldap,
 | |
|         email: config.email,
 | |
|         allowemailregister: config.allowemailregister
 | |
|     });
 | |
| }
 | |
| 
 | |
| function newNote(req, res, next) {
 | |
|     var owner = null;
 | |
|     if (req.isAuthenticated()) {
 | |
|         owner = req.user.id;
 | |
|     } else if (!config.allowanonymous) {
 | |
|         return response.errorForbidden(res);
 | |
|     }
 | |
|     models.Note.create({
 | |
|         ownerId: owner,
 | |
|         alias: req.alias ? req.alias : null
 | |
|     }).then(function (note) {
 | |
|         return res.redirect(config.serverurl + "/" + LZString.compressToBase64(note.id));
 | |
|     }).catch(function (err) {
 | |
|         logger.error(err);
 | |
|         return response.errorInternalError(res);
 | |
|     });
 | |
| }
 | |
| 
 | |
| function checkViewPermission(req, note) {
 | |
|     if (note.permission == 'private') {
 | |
|         if (!req.isAuthenticated() || note.ownerId != req.user.id)
 | |
|             return false;
 | |
|         else
 | |
|             return true;
 | |
|     } else if (note.permission == 'limited' || note.permission == 'protected') {
 | |
|         if(!req.isAuthenticated())
 | |
|             return false;
 | |
|         else
 | |
|             return true;
 | |
|     } else {
 | |
|         return true;
 | |
|     }
 | |
| }
 | |
| 
 | |
| function findNote(req, res, callback, include) {
 | |
|     var noteId = req.params.noteId;
 | |
|     var id = req.params.noteId || req.params.shortid;
 | |
|     models.Note.parseNoteId(id, function (err, _id) {
 | |
|         models.Note.findOne({
 | |
|             where: {
 | |
|                 id: _id
 | |
|             },
 | |
|             include: include || null
 | |
|         }).then(function (note) {
 | |
|             if (!note) {
 | |
|                 if (config.allowfreeurl && noteId) {
 | |
|                     req.alias = noteId;
 | |
|                     return newNote(req, res);
 | |
|                 } else {
 | |
|                     return response.errorNotFound(res);
 | |
|                 }
 | |
|             }
 | |
|             if (!checkViewPermission(req, note)) {
 | |
|                 return response.errorForbidden(res);
 | |
|             } else {
 | |
|                 return callback(note);
 | |
|             }
 | |
|         }).catch(function (err) {
 | |
|             logger.error(err);
 | |
|             return response.errorInternalError(res);
 | |
|         });
 | |
|     });
 | |
| }
 | |
| 
 | |
| function showNote(req, res, next) {
 | |
|     findNote(req, res, function (note) {
 | |
|         // force to use note id
 | |
|         var noteId = req.params.noteId;
 | |
|         var id = LZString.compressToBase64(note.id);
 | |
|         if ((note.alias && noteId != note.alias) || (!note.alias && noteId != id))
 | |
|             return res.redirect(config.serverurl + "/" + (note.alias || id));
 | |
|         return responseHackMD(res, note);
 | |
|     });
 | |
| }
 | |
| 
 | |
| function showPublishNote(req, res, next) {
 | |
|     var include = [{
 | |
|         model: models.User,
 | |
|         as: "owner"
 | |
|     }, {
 | |
|         model: models.User,
 | |
|         as: "lastchangeuser"
 | |
|     }];
 | |
|     findNote(req, res, function (note) {
 | |
|         // force to use short id
 | |
|         var shortid = req.params.shortid;
 | |
|         if ((note.alias && shortid != note.alias) || (!note.alias && shortid != note.shortid))
 | |
|             return res.redirect(config.serverurl + "/s/" + (note.alias || note.shortid));
 | |
|         note.increment('viewcount').then(function (note) {
 | |
|             if (!note) {
 | |
|                 return response.errorNotFound(res);
 | |
|             }
 | |
|             var body = note.content;
 | |
|             var extracted = models.Note.extractMeta(body);
 | |
|             markdown = extracted.markdown;
 | |
|             meta = models.Note.parseMeta(extracted.meta);
 | |
|             var createtime = note.createdAt;
 | |
|             var updatetime = note.lastchangeAt;
 | |
|             var title = models.Note.decodeTitle(note.title);
 | |
|             title = models.Note.generateWebTitle(meta.title || title);
 | |
|             var origin = config.serverurl;
 | |
|             var data = {
 | |
|                 title: title,
 | |
|                 description: meta.description || (markdown ? models.Note.generateDescription(markdown) : null),
 | |
|                 viewcount: note.viewcount,
 | |
|                 createtime: createtime,
 | |
|                 updatetime: updatetime,
 | |
|                 url: origin,
 | |
|                 body: body,
 | |
|                 useCDN: config.usecdn,
 | |
|                 owner: note.owner ? note.owner.id : null,
 | |
|                 ownerprofile: note.owner ? models.User.getProfile(note.owner) : null,
 | |
|                 lastchangeuser: note.lastchangeuser ? note.lastchangeuser.id : null,
 | |
|                 lastchangeuserprofile: note.lastchangeuser ? models.User.getProfile(note.lastchangeuser) : null,
 | |
|                 robots: meta.robots || false, //default allow robots
 | |
|                 GA: meta.GA,
 | |
|                 disqus: meta.disqus
 | |
|             };
 | |
|             return renderPublish(data, res);
 | |
|         }).catch(function (err) {
 | |
|             logger.error(err);
 | |
|             return response.errorInternalError(res);
 | |
|         });
 | |
|     }, include);
 | |
| }
 | |
| 
 | |
| function renderPublish(data, res) {
 | |
|     res.set({
 | |
|         'Cache-Control': 'private' // only cache by client
 | |
|     });
 | |
|     res.render(config.prettypath, data);
 | |
| }
 | |
| 
 | |
| function actionPublish(req, res, note) {
 | |
|     res.redirect(config.serverurl + "/s/" + (note.alias || note.shortid));
 | |
| }
 | |
| 
 | |
| function actionSlide(req, res, note) {
 | |
|     res.redirect(config.serverurl + "/p/" + (note.alias || note.shortid));
 | |
| }
 | |
| 
 | |
| function actionDownload(req, res, note) {
 | |
|     var body = note.content;
 | |
|     var title = models.Note.decodeTitle(note.title);
 | |
|     var filename = title;
 | |
|     filename = encodeURIComponent(filename);
 | |
|     res.set({
 | |
|         'Access-Control-Allow-Origin': '*', //allow CORS as API
 | |
|         'Access-Control-Allow-Headers': 'Range',
 | |
|         'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range',
 | |
|         'Content-Type': 'text/markdown; charset=UTF-8',
 | |
|         'Cache-Control': 'private',
 | |
|         'Content-disposition': 'attachment; filename=' + filename + '.md',
 | |
|         'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
 | |
|     });
 | |
|     res.send(body);
 | |
| }
 | |
| 
 | |
| function actionInfo(req, res, note) {
 | |
|     var body = note.content;
 | |
|     var extracted = models.Note.extractMeta(body);
 | |
|     var markdown = extracted.markdown;
 | |
|     var meta = models.Note.parseMeta(extracted.meta);
 | |
|     var createtime = note.createdAt;
 | |
|     var updatetime = note.lastchangeAt;
 | |
|     var title = models.Note.decodeTitle(note.title);
 | |
|     var data = {
 | |
|         title: meta.title || title,
 | |
|         description: meta.description || (markdown ? models.Note.generateDescription(markdown) : null),
 | |
|         viewcount: note.viewcount,
 | |
|         createtime: createtime,
 | |
|         updatetime: updatetime
 | |
|     };
 | |
|     res.set({
 | |
|         'Access-Control-Allow-Origin': '*', //allow CORS as API
 | |
|         'Access-Control-Allow-Headers': 'Range',
 | |
|         'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range',
 | |
|         'Cache-Control': 'private', // only cache by client
 | |
|         'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
 | |
|     });
 | |
|     res.send(data);
 | |
| }
 | |
| 
 | |
| function actionPDF(req, res, note) {
 | |
|     var body = note.content;
 | |
|     var extracted = models.Note.extractMeta(body);
 | |
|     var title = models.Note.decodeTitle(note.title);
 | |
| 
 | |
|     if (!fs.existsSync(config.tmppath)) {
 | |
|         fs.mkdirSync(config.tmppath);
 | |
|     }
 | |
|     var path = config.tmppath + '/' + Date.now() + '.pdf';
 | |
|     markdownpdf().from.string(extracted.markdown).to(path, function () {
 | |
|         var stream = fs.createReadStream(path);
 | |
|         var filename = title;
 | |
|         // Be careful of special characters
 | |
|         filename = encodeURIComponent(filename);
 | |
|         // Ideally this should strip them
 | |
|         res.setHeader('Content-disposition', 'attachment; filename="' + filename + '.pdf"');
 | |
|         res.setHeader('Cache-Control', 'private');
 | |
|         res.setHeader('Content-Type', 'application/pdf; charset=UTF-8');
 | |
|         res.setHeader('X-Robots-Tag', 'noindex, nofollow'); // prevent crawling
 | |
|         stream.pipe(res);
 | |
|         fs.unlink(path);
 | |
|     });
 | |
| }
 | |
| 
 | |
| function actionGist(req, res, note) {
 | |
|     var data = {
 | |
|         client_id: config.github.clientID,
 | |
|         redirect_uri: config.serverurl + '/auth/github/callback/' + LZString.compressToBase64(note.id) + '/gist',
 | |
|         scope: "gist",
 | |
|         state: shortId.generate()
 | |
|     };
 | |
|     var query = querystring.stringify(data);
 | |
|     res.redirect("https://github.com/login/oauth/authorize?" + query);
 | |
| }
 | |
| 
 | |
| function actionRevision(req, res, note) {
 | |
|     var actionId = req.params.actionId;
 | |
|     if (actionId) {
 | |
|         var time = moment(parseInt(actionId));
 | |
|         if (time.isValid()) {
 | |
|             models.Revision.getPatchedNoteRevisionByTime(note, time, function (err, content) {
 | |
|                 if (err) {
 | |
|                     logger.error(err);
 | |
|                     return response.errorInternalError(res);
 | |
|                 }
 | |
|                 if (!content) {
 | |
|                     return response.errorNotFound(res);
 | |
|                 }
 | |
|                 res.set({
 | |
|                     'Access-Control-Allow-Origin': '*', //allow CORS as API
 | |
|                     'Access-Control-Allow-Headers': 'Range',
 | |
|                     'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range',
 | |
|                     'Cache-Control': 'private', // only cache by client
 | |
|                     'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
 | |
|                 });
 | |
|                 res.send(content);
 | |
|             });
 | |
|         } else {
 | |
|             return response.errorNotFound(res);
 | |
|         }
 | |
|     } else {
 | |
|         models.Revision.getNoteRevisions(note, function (err, data) {
 | |
|             if (err) {
 | |
|                 logger.error(err);
 | |
|                 return response.errorInternalError(res);
 | |
|             }
 | |
|             var out = {
 | |
|                 revision: data
 | |
|             };
 | |
|             res.set({
 | |
|                 'Access-Control-Allow-Origin': '*', //allow CORS as API
 | |
|                 'Access-Control-Allow-Headers': 'Range',
 | |
|                 'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range',
 | |
|                 'Cache-Control': 'private', // only cache by client
 | |
|                 'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
 | |
|             });
 | |
|             res.send(out);
 | |
|         });
 | |
|     }
 | |
| }
 | |
| 
 | |
| function noteActions(req, res, next) {
 | |
|     var noteId = req.params.noteId;
 | |
|     findNote(req, res, function (note) {
 | |
|         var action = req.params.action;
 | |
|         switch (action) {
 | |
|         case "publish":
 | |
|         case "pretty": //pretty deprecated
 | |
|             actionPublish(req, res, note);
 | |
|             break;
 | |
|         case "slide":
 | |
|             actionSlide(req, res, note);
 | |
|             break;
 | |
|         case "download":
 | |
|             actionDownload(req, res, note);
 | |
|             break;
 | |
|         case "info":
 | |
|             actionInfo(req, res, note);
 | |
|             break;
 | |
|         case "pdf":
 | |
|             actionPDF(req, res, note);
 | |
|             break;
 | |
|         case "gist":
 | |
|             actionGist(req, res, note);
 | |
|             break;
 | |
|         case "revision":
 | |
|             actionRevision(req, res, note);
 | |
|             break;
 | |
|         default:
 | |
|             return res.redirect(config.serverurl + '/' + noteId);
 | |
|             break;
 | |
|         }
 | |
|     });
 | |
| }
 | |
| 
 | |
| function publishNoteActions(req, res, next) {
 | |
|     findNote(req, res, function (note) {
 | |
|         var action = req.params.action;
 | |
|         switch (action) {
 | |
|         case "edit":
 | |
|             res.redirect(config.serverurl + '/' + (note.alias ? note.alias : LZString.compressToBase64(note.id)));
 | |
|             break;
 | |
|         default:
 | |
|             res.redirect(config.serverurl + '/s/' + note.shortid);
 | |
|             break;
 | |
|         }
 | |
|     });
 | |
| }
 | |
| 
 | |
| function publishSlideActions(req, res, next) {
 | |
|     findNote(req, res, function (note) {
 | |
|         var action = req.params.action;
 | |
|         switch (action) {
 | |
|         case "edit":
 | |
|             res.redirect(config.serverurl + '/' + (note.alias ? note.alias : LZString.compressToBase64(note.id)));
 | |
|             break;
 | |
|         default:
 | |
|             res.redirect(config.serverurl + '/p/' + note.shortid);
 | |
|             break;
 | |
|         }
 | |
|     });
 | |
| }
 | |
| 
 | |
| function githubActions(req, res, next) {
 | |
|     var noteId = req.params.noteId;
 | |
|     findNote(req, res, function (note) {
 | |
|         var action = req.params.action;
 | |
|         switch (action) {
 | |
|         case "gist":
 | |
|             githubActionGist(req, res, note);
 | |
|             break;
 | |
|         default:
 | |
|             res.redirect(config.serverurl + '/' + noteId);
 | |
|             break;
 | |
|         }
 | |
|     });
 | |
| }
 | |
| 
 | |
| function githubActionGist(req, res, note) {
 | |
|     var code = req.query.code;
 | |
|     var state = req.query.state;
 | |
|     if (!code || !state) {
 | |
|         return response.errorForbidden(res);
 | |
|     } else {
 | |
|         var data = {
 | |
|             client_id: config.github.clientID,
 | |
|             client_secret: config.github.clientSecret,
 | |
|             code: code,
 | |
|             state: state
 | |
|         }
 | |
|         var auth_url = 'https://github.com/login/oauth/access_token';
 | |
|         request({
 | |
|                 url: auth_url,
 | |
|                 method: "POST",
 | |
|                 json: data
 | |
|             }, function (error, httpResponse, body) {
 | |
|             if (!error && httpResponse.statusCode == 200) {
 | |
|                 var access_token = body.access_token;
 | |
|                 if (access_token) {
 | |
|                     var content = note.content;
 | |
|                     var title = models.Note.decodeTitle(note.title);
 | |
|                     var filename = title.replace('/', ' ') + '.md';
 | |
|                     var gist = {
 | |
|                         "files": {}
 | |
|                     };
 | |
|                     gist.files[filename] = {
 | |
|                         "content": content
 | |
|                     };
 | |
|                     var gist_url = "https://api.github.com/gists";
 | |
|                     request({
 | |
|                         url: gist_url,
 | |
|                         headers: {
 | |
|                             'User-Agent': 'HackMD',
 | |
|                             'Authorization': 'token ' + access_token
 | |
|                         },
 | |
|                         method: "POST",
 | |
|                         json: gist
 | |
|                     }, function (error, httpResponse, body) {
 | |
|                         if (!error && httpResponse.statusCode == 201) {
 | |
|                             res.setHeader('referer', '');
 | |
|                             res.redirect(body.html_url);
 | |
|                         } else {
 | |
|                             return response.errorForbidden(res);
 | |
|                         }
 | |
|                     });
 | |
|                 } else {
 | |
|                     return response.errorForbidden(res);
 | |
|                 }
 | |
|             } else {
 | |
|                 return response.errorForbidden(res);
 | |
|             }
 | |
|         })
 | |
|     }
 | |
| }
 | |
| 
 | |
| function gitlabActions(req, res, next) {
 | |
|     var noteId = req.params.noteId;
 | |
|     findNote(req, res, function (note) {
 | |
|         var action = req.params.action;
 | |
|         switch (action) {
 | |
|         case "projects":
 | |
|             gitlabActionProjects(req, res, note);
 | |
|             break;
 | |
|         default:
 | |
|             res.redirect(config.serverurl + '/' + noteId);
 | |
|             break;
 | |
|         }
 | |
|     });
 | |
| }
 | |
| 
 | |
| function gitlabActionProjects(req, res, note) {
 | |
|     if (req.isAuthenticated()) {
 | |
|         models.User.findOne({
 | |
|             where: {
 | |
|                 id: req.user.id
 | |
|             }
 | |
|         }).then(function (user) {
 | |
|             if (!user)
 | |
|                 return response.errorNotFound(res);
 | |
|             var ret = { baseURL: config.gitlab.baseURL };
 | |
|             ret.accesstoken = user.accessToken;
 | |
|             ret.profileid = user.profileid;
 | |
|             request(
 | |
|                 config.gitlab.baseURL + '/api/v3/projects?access_token=' + user.accessToken,
 | |
|                 function(error, httpResponse, body) {
 | |
|                     if (!error && httpResponse.statusCode == 200) {
 | |
|                         ret.projects = JSON.parse(body);
 | |
|                         return res.send(ret);
 | |
|                     } else {
 | |
|                         return res.send(ret);
 | |
|                     }
 | |
|                 }
 | |
|             );
 | |
|         }).catch(function (err) {
 | |
|             logger.error('gitlab action projects failed: ' + err);
 | |
|             return response.errorInternalError(res);
 | |
|         });
 | |
|     } else {
 | |
|         return response.errorForbidden(res);
 | |
|     }
 | |
| }
 | |
| 
 | |
| function showPublishSlide(req, res, next) {
 | |
|     var include = [{
 | |
|         model: models.User,
 | |
|         as: "owner"
 | |
|     }, {
 | |
|         model: models.User,
 | |
|         as: "lastchangeuser"
 | |
|     }];
 | |
|     findNote(req, res, function (note) {
 | |
|         // force to use short id
 | |
|         var shortid = req.params.shortid;
 | |
|         if ((note.alias && shortid != note.alias) || (!note.alias && shortid != note.shortid))
 | |
|             return res.redirect(config.serverurl + "/p/" + (note.alias || note.shortid));
 | |
|         note.increment('viewcount').then(function (note) {
 | |
|             if (!note) {
 | |
|                 return response.errorNotFound(res);
 | |
|             }
 | |
|             var body = note.content;
 | |
|             var extracted = models.Note.extractMeta(body);
 | |
|             markdown = extracted.markdown;
 | |
|             meta = models.Note.parseMeta(extracted.meta);
 | |
|             var createtime = note.createdAt;
 | |
|             var updatetime = note.lastchangeAt;
 | |
|             var title = models.Note.decodeTitle(note.title);
 | |
|             title = models.Note.generateWebTitle(meta.title || title);
 | |
|             var origin = config.serverurl;
 | |
|             var data = {
 | |
|                 title: title,
 | |
|                 description: meta.description || (markdown ? models.Note.generateDescription(markdown) : null),
 | |
|                 viewcount: note.viewcount,
 | |
|                 createtime: createtime,
 | |
|                 updatetime: updatetime,
 | |
|                 url: origin,
 | |
|                 body: markdown,
 | |
|                 meta: JSON.stringify(extracted.meta),
 | |
|                 useCDN: config.usecdn,
 | |
|                 owner: note.owner ? note.owner.id : null,
 | |
|                 ownerprofile: note.owner ? models.User.getProfile(note.owner) : null,
 | |
|                 lastchangeuser: note.lastchangeuser ? note.lastchangeuser.id : null,
 | |
|                 lastchangeuserprofile: note.lastchangeuser ? models.User.getProfile(note.lastchangeuser) : null,
 | |
|                 robots: meta.robots || false, //default allow robots
 | |
|                 GA: meta.GA,
 | |
|                 disqus: meta.disqus
 | |
|             };
 | |
|             return renderPublishSlide(data, res);
 | |
|         }).catch(function (err) {
 | |
|             logger.error(err);
 | |
|             return response.errorInternalError(res);
 | |
|         });
 | |
|     }, include);
 | |
| }
 | |
| 
 | |
| function renderPublishSlide(data, res) {
 | |
|     res.set({
 | |
|         'Cache-Control': 'private' // only cache by client
 | |
|     });
 | |
|     res.render(config.slidepath, data);
 | |
| }
 | |
| 
 | |
| module.exports = response;
 |