summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/note.js21
-rw-r--r--lib/realtime.js168
-rw-r--r--lib/response.js182
-rw-r--r--lib/user.js34
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);