diff options
author | Wu Cheng-Han | 2016-01-12 08:01:42 -0600 |
---|---|---|
committer | Wu Cheng-Han | 2016-01-12 08:01:42 -0600 |
commit | 2ecec3b59aabe2fd6156338cd4cbab7672d4f9b1 (patch) | |
tree | a27846db2e2e8d0ef1480893a8ad2b69dac36ad0 /lib | |
parent | 1672df3dceebe885ded02530799a78d5a07be1d7 (diff) |
Support show last change user with profile and support YAML config inside the note with robots, lang, dir, breaks options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/note.js | 21 | ||||
-rw-r--r-- | lib/realtime.js | 168 | ||||
-rw-r--r-- | lib/response.js | 182 | ||||
-rw-r--r-- | lib/user.js | 34 |
4 files changed, 275 insertions, 130 deletions
diff --git a/lib/note.js b/lib/note.js index dc384b7f..671e5383 100644 --- a/lib/note.js +++ b/lib/note.js @@ -26,6 +26,10 @@ var model = mongoose.model('note', { type: String, enum: permissionTypes }, + lastchangeuser: { + type: Schema.Types.ObjectId, + ref: 'user' + }, viewcount: { type: Number, default: 0 @@ -45,7 +49,8 @@ var note = { getNoteTitle: getNoteTitle, generateWebTitle: generateWebTitle, increaseViewCount: increaseViewCount, - updatePermission: updatePermission + updatePermission: updatePermission, + updateLastChangeUser: updateLastChangeUser }; function checkNoteIdValid(noteId) { @@ -198,4 +203,18 @@ function updatePermission(note, permission, callback) { }); } +function updateLastChangeUser(note, lastchangeuser, callback) { + note.lastchangeuser = lastchangeuser; + note.updated = Date.now(); + note.save(function (err) { + if (err) { + logger.error('update note lastchangeuser failed: ' + err); + callback(err, null); + } else { + logger.info("update note lastchangeuser success: " + note.id); + callback(null, note); + }; + }); +} + module.exports = note;
\ No newline at end of file diff --git a/lib/realtime.js b/lib/realtime.js index a9c541cf..484ef12e 100644 --- a/lib/realtime.js +++ b/lib/realtime.js @@ -9,7 +9,6 @@ var shortId = require('shortid'); var randomcolor = require("randomcolor"); var Chance = require('chance'), chance = new Chance(); -var md5 = require("blueimp-md5").md5; var moment = require('moment'); //core @@ -68,7 +67,9 @@ function secure(socket, next) { function emitCheck(note) { var out = { - updatetime: note.updatetime + updatetime: note.updatetime, + lastchangeuser: note.lastchangeuser, + lastchangeuserprofile: note.lastchangeuserprofile }; realtime.io.to(note.id).emit('check', out); /* @@ -89,18 +90,52 @@ var updater = setInterval(function () { if (note.server.isDirty) { if (config.debug) logger.info("updater found dirty note: " + key); - var body = note.server.document; - var title = Note.getNoteTitle(body); - title = LZString.compressToBase64(title); - body = LZString.compressToBase64(body); - db.saveToDB(key, title, body, function (err, result) { - if (err) return; - note.server.isDirty = false; - note.updatetime = Date.now(); - emitCheck(note); + Note.findNote(note.id, function (err, _note) { + if (err || !_note) return callback(err, null); + //mongo update + if (note.lastchangeuser && _note.lastchangeuser != note.lastchangeuser) { + var lastchangeuser = note.lastchangeuser; + var lastchangeuserprofile = null; + User.findUser(lastchangeuser, function (err, user) { + if (err) return callback(err, null); + if (user && user.profile) { + var profile = JSON.parse(user.profile); + if (profile) { + lastchangeuserprofile = { + name: profile.displayName || profile.username, + photo: User.parsePhotoByProfile(profile) + } + note.lastchangeuser = lastchangeuser; + note.lastchangeuserprofile = lastchangeuserprofile; + Note.updateLastChangeUser(_note, lastchangeuser, function (err, result) { + if (err) return callback(err, null); + }); + } + } + }); + } else { + note.lastchangeuser = null; + note.lastchangeuserprofile = null; + Note.updateLastChangeUser(_note, null, function (err, result) { + if (err) return callback(err, null); + }); + } + //postgres update + var body = note.server.document; + var title = Note.getNoteTitle(body); + title = LZString.compressToBase64(title); + body = LZString.compressToBase64(body); + db.saveToDB(key, title, body, function (err, result) { + if (err) return callback(err, null); + note.server.isDirty = false; + note.updatetime = Date.now(); + emitCheck(note); + callback(null, null); + }); }); + } else { + callback(null, null); } - callback(); }, function (err) { if (err) return logger.error('updater error', err); }); @@ -121,7 +156,7 @@ var cleaner = setInterval(function () { disconnectSocketQueue.push(socket); disconnect(socket); } - callback(); + callback(null, null); }, function (err) { if (err) return logger.error('cleaner error', err); }); @@ -250,7 +285,11 @@ function emitRefresh(socket) { socket.emit('refresh', { docmaxlength: config.documentmaxlength, owner: note.owner, + ownerprofile: note.ownerprofile, + lastchangeuser: note.lastchangeuser, + lastchangeuserprofile: note.lastchangeuserprofile, permission: note.permission, + createtime: note.createtime, updatetime: note.updatetime }); } @@ -321,11 +360,15 @@ function startConnection(socket) { isConnectionBusy = false; return logger.error(err); } + var owner = data.rows[0].owner; + var ownerprofile = null; var permission = "freely"; if (owner && owner != "null") { permission = "editable"; } + + //find or new note Note.findOrNewNote(notename, permission, function (err, note) { if (err) { responseError(res, "404", "Not Found", "oops."); @@ -333,20 +376,64 @@ function startConnection(socket) { isConnectionBusy = false; return; } + var body = LZString.decompressFromBase64(data.rows[0].content); //body = LZString.compressToUTF16(body); + var createtime = data.rows[0].create_time; var updatetime = data.rows[0].update_time; var server = new ot.EditorSocketIOServer(body, [], notename, ifMayEdit); + + var lastchangeuser = note.lastchangeuser || null; + var lastchangeuserprofile = null; + notes[notename] = { id: notename, owner: owner, + ownerprofile: ownerprofile, permission: note.permission, + lastchangeuser: lastchangeuser, + lastchangeuserprofile: lastchangeuserprofile, socks: [], users: {}, + createtime: moment(createtime).valueOf(), updatetime: moment(updatetime).valueOf(), server: server }; - finishConnection(socket, notes[notename], users[socket.id]); + + if (lastchangeuser) { + //find last change user profile if lastchangeuser exists + User.findUser(lastchangeuser, function (err, user) { + if (!err && user && user.profile) { + var profile = JSON.parse(user.profile); + if (profile) { + lastchangeuserprofile = { + name: profile.displayName || profile.username, + photo: User.parsePhotoByProfile(profile) + } + notes[notename].lastchangeuserprofile = lastchangeuserprofile; + } + } + }); + } + + if (owner && owner != "null") { + //find owner profile if owner exists + User.findUser(owner, function (err, user) { + if (!err && user && user.profile) { + var profile = JSON.parse(user.profile); + if (profile) { + ownerprofile = { + name: profile.displayName || profile.username, + photo: User.parsePhotoByProfile(profile) + } + notes[notename].ownerprofile = ownerprofile; + } + } + finishConnection(socket, notes[notename], users[socket.id]); + }); + } else { + finishConnection(socket, notes[notename], users[socket.id]); + } }); }); } else { @@ -433,23 +520,7 @@ function updateUserData(socket, user) { //retrieve user data from passport if (socket.request.user && socket.request.user.logged_in) { var profile = JSON.parse(socket.request.user.profile); - var photo = null; - switch (profile.provider) { - case "facebook": - photo = 'https://graph.facebook.com/' + profile.id + '/picture'; - break; - case "twitter": - photo = profile.photos[0].value; - break; - case "github": - photo = 'https://avatars.githubusercontent.com/u/' + profile.id + '?s=48'; - break; - case "dropbox": - //no image api provided, use gravatar - photo = 'https://www.gravatar.com/avatar/' + md5(profile.emails[0].value); - break; - } - user.photo = photo; + user.photo = User.parsePhotoByProfile(profile); user.name = profile.displayName || profile.username; user.userid = socket.request.user._id; user.login = true; @@ -466,19 +537,28 @@ function ifMayEdit(socket, callback) { var note = notes[notename]; var mayEdit = true; switch (note.permission) { - case "freely": - //not blocking anyone - break; - case "editable": - //only login user can change - if (!socket.request.user || !socket.request.user.logged_in) - mayEdit = false; - break; - case "locked": - //only owner can change - if (note.owner != socket.request.user._id) - mayEdit = false; - break; + case "freely": + //not blocking anyone + break; + case "editable": + //only login user can change + if (!socket.request.user || !socket.request.user.logged_in) + mayEdit = false; + break; + case "locked": + //only owner can change + if (note.owner != socket.request.user._id) + mayEdit = false; + break; + } + //if user may edit and this note have owner (not anonymous usage) + if (mayEdit && note.owner && note.owner != "null") { + //save for the last change user id + if (socket.request.user && socket.request.user.logged_in) { + note.lastchangeuser = socket.request.user._id; + } else { + note.lastchangeuser = null; + } } callback(mayEdit); } diff --git a/lib/response.js b/lib/response.js index 6a180f2b..a30df470 100644 --- a/lib/response.js +++ b/lib/response.js @@ -8,6 +8,7 @@ var markdownpdf = require("markdown-pdf"); var LZString = require('lz-string'); var S = require('string'); var shortId = require('shortid'); +var metaMarked = require('meta-marked'); //core var config = require("../config.js"); @@ -15,6 +16,7 @@ var config = require("../config.js"); //others var db = require("./db.js"); var Note = require("./note.js"); +var User = require("./user.js"); //slides var md = require('reveal.js/plugin/markdown/markdown'); @@ -104,6 +106,13 @@ function responseHackMD(res, noteId) { responseError(res, "404", "Not Found", "oops."); return; } + var body = LZString.decompressFromBase64(data.rows[0].content); + var meta = null; + try { + meta = metaMarked(body).meta; + } catch(err) { + //na + } var title = data.rows[0].title; var decodedTitle = LZString.decompressFromBase64(title); if (decodedTitle) title = decodedTitle; @@ -116,7 +125,8 @@ function responseHackMD(res, noteId) { var compiled = ejs.compile(fs.readFileSync(template, 'utf8'), options); var html = compiled({ title: title, - useCDN: config.usecdn + useCDN: config.usecdn, + robots: (meta && meta.robots) || false //default allow robots }); var buf = html; res.writeHead(200, { @@ -192,34 +202,47 @@ function showPublishNote(req, res, next) { return; } var body = LZString.decompressFromBase64(data.rows[0].content); + var meta = null; + try { + meta = metaMarked(body).meta; + } catch(err) { + //na + } var updatetime = data.rows[0].update_time; var text = S(body).escapeHTML().s; var title = data.rows[0].title; var decodedTitle = LZString.decompressFromBase64(title); if (decodedTitle) title = decodedTitle; title = Note.generateWebTitle(title); - var template = config.prettypath; - var options = { - cache: !config.debug, - filename: template - }; - var compiled = ejs.compile(fs.readFileSync(template, 'utf8'), options); var origin = config.getserverurl(); - var html = compiled({ + var data = { title: title, viewcount: note.viewcount, updatetime: updatetime, url: origin, body: text, - useCDN: config.usecdn - }); - var buf = html; - res.writeHead(200, { - 'Content-Type': 'text/html; charset=UTF-8', - 'Cache-Control': 'private', - 'Content-Length': buf.length - }); - res.end(buf); + useCDN: config.usecdn, + lastchangeuserprofile: null, + robots: (meta && meta.robots) || false //default allow robots + }; + if (note.lastchangeuser) { + //find last change user profile if lastchangeuser exists + User.findUser(note.lastchangeuser, function (err, user) { + if (!err && user && user.profile) { + var profile = JSON.parse(user.profile); + if (profile) { + data.lastchangeuserprofile = { + name: profile.displayName || profile.username, + photo: User.parsePhotoByProfile(profile) + } + renderPublish(data, res); + } + } + }); + } else { + renderPublish(data, res); + } + }); }); }); @@ -228,6 +251,23 @@ function showPublishNote(req, res, next) { } } +function renderPublish(data, res) { + var template = config.prettypath; + var options = { + cache: !config.debug, + filename: template + }; + var compiled = ejs.compile(fs.readFileSync(template, 'utf8'), options); + var html = compiled(data); + var buf = html; + res.writeHead(200, { + 'Content-Type': 'text/html; charset=UTF-8', + 'Cache-Control': 'private', + 'Content-Length': buf.length + }); + res.end(buf); +} + function actionPublish(req, res, noteId) { db.readFromDB(noteId, function (err, data) { if (err) { @@ -269,36 +309,6 @@ function actionSlide(req, res, noteId) { }); }); } -//pretty api is deprecated -function actionPretty(req, res, noteId) { - db.readFromDB(noteId, function (err, data) { - if (err) { - responseError(res, "404", "Not Found", "oops."); - return; - } - var body = LZString.decompressFromBase64(data.rows[0].content); - var text = S(body).escapeHTML().s; - var title = data.rows[0].title; - var decodedTitle = LZString.decompressFromBase64(title); - if (decodedTitle) title = decodedTitle; - title = Note.generateWebTitle(title); - var template = config.prettypath; - var compiled = ejs.compile(fs.readFileSync(template, 'utf8')); - var origin = config.getserverurl(); - var html = compiled({ - title: title, - url: origin, - body: text - }); - var buf = html; - res.writeHead(200, { - 'Content-Type': 'text/html; charset=UTF-8', - 'Cache-Control': 'private', - 'Content-Length': buf.length - }); - res.end(buf); - }); -} function actionDownload(req, res, noteId) { db.readFromDB(noteId, function (err, data) { @@ -325,6 +335,11 @@ function actionPDF(req, res, noteId) { return; } var body = LZString.decompressFromBase64(data.rows[0].content); + try { + body = metaMarked(body).markdown; + } catch(err) { + //na + } var title = Note.getNoteTitle(body); if (!fs.existsSync(config.tmppath)) { @@ -361,46 +376,46 @@ function noteActions(req, res, next) { } var action = req.params.action; switch (action) { - case "publish": - case "pretty": //pretty deprecated - actionPublish(req, res, noteId); - break; - case "slide": - actionSlide(req, res, noteId); - break; - case "download": - actionDownload(req, res, noteId); - break; - case "pdf": - actionPDF(req, res, noteId); - break; - default: - if (noteId != config.featuresnotename) - res.redirect('/' + LZString.compressToBase64(noteId)); - else - res.redirect('/' + noteId); - break; + case "publish": + case "pretty": //pretty deprecated + actionPublish(req, res, noteId); + break; + case "slide": + actionSlide(req, res, noteId); + break; + case "download": + actionDownload(req, res, noteId); + break; + case "pdf": + actionPDF(req, res, noteId); + break; + default: + if (noteId != config.featuresnotename) + res.redirect('/' + LZString.compressToBase64(noteId)); + else + res.redirect('/' + noteId); + break; } } function publishNoteActions(req, res, next) { var action = req.params.action; switch (action) { - case "edit": - var shortid = req.params.shortid; - if (shortId.isValid(shortid)) { - Note.findNote(shortid, function (err, note) { - if (err || !note) { - responseError(res, "404", "Not Found", "oops."); - return; - } - if (note.id != config.featuresnotename) - res.redirect('/' + LZString.compressToBase64(note.id)); - else - res.redirect('/' + note.id); - }); - } - break; + case "edit": + var shortid = req.params.shortid; + if (shortId.isValid(shortid)) { + Note.findNote(shortid, function (err, note) { + if (err || !note) { + responseError(res, "404", "Not Found", "oops."); + return; + } + if (note.id != config.featuresnotename) + res.redirect('/' + LZString.compressToBase64(note.id)); + else + res.redirect('/' + note.id); + }); + } + break; } } @@ -424,6 +439,11 @@ function showPublishSlide(req, res, next) { return; } var body = LZString.decompressFromBase64(data.rows[0].content); + try { + body = metaMarked(body).markdown; + } catch(err) { + //na + } var title = data.rows[0].title; var decodedTitle = LZString.decompressFromBase64(title); if (decodedTitle) title = decodedTitle; diff --git a/lib/user.js b/lib/user.js index f89f7def..b3fae39c 100644 --- a/lib/user.js +++ b/lib/user.js @@ -1,6 +1,7 @@ //user //external modules var mongoose = require('mongoose'); +var md5 = require("blueimp-md5").md5; //core var config = require("../config.js"); @@ -20,9 +21,30 @@ var user = { findUser: findUser, newUser: newUser, findOrNewUser: findOrNewUser, - getUserCount: getUserCount + getUserCount: getUserCount, + parsePhotoByProfile: parsePhotoByProfile }; +function parsePhotoByProfile(profile) { + var photo = null; + switch (profile.provider) { + case "facebook": + photo = 'https://graph.facebook.com/' + profile.id + '/picture'; + break; + case "twitter": + photo = profile.photos[0].value; + break; + case "github": + photo = 'https://avatars.githubusercontent.com/u/' + profile.id + '?s=48'; + break; + case "dropbox": + //no image api provided, use gravatar + photo = 'https://www.gravatar.com/avatar/' + md5(profile.emails[0].value); + break; + } + return photo; +} + function getUserCount(callback) { model.count(function(err, count){ if(err) callback(err, null); @@ -31,9 +53,13 @@ function getUserCount(callback) { } function findUser(id, callback) { - model.findOne({ - id: id - }, function (err, user) { + var rule = {}; + var checkForHexRegExp = new RegExp("^[0-9a-fA-F]{24}$"); + if (checkForHexRegExp.test(id)) + rule._id = id; + else + rule.id = id; + model.findOne(rule, function (err, user) { if (err) { logger.error('find user failed: ' + err); callback(err, null); |