summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/note.js8
-rwxr-xr-xlib/ot/editor-socketio-server.js3
-rw-r--r--lib/realtime.js204
-rw-r--r--lib/response.js258
-rw-r--r--public/js/index.js22
-rw-r--r--public/views/body.ejs1
6 files changed, 297 insertions, 199 deletions
diff --git a/lib/note.js b/lib/note.js
index 671e5383..cd3816f9 100644
--- a/lib/note.js
+++ b/lib/note.js
@@ -12,7 +12,7 @@ var db = require("./db.js");
var logger = require("./logger.js");
//permission types
-permissionTypes = ["freely", "editable", "locked"];
+permissionTypes = ["freely", "editable", "locked", "private"];
// create a note model
var model = mongoose.model('note', {
@@ -126,7 +126,11 @@ function findNote(id, callback) {
});
}
-function newNote(id, permission, callback) {
+function newNote(id, owner, callback) {
+ var permission = "freely";
+ if (owner && owner != "null") {
+ permission = "editable";
+ }
var note = new model({
id: id,
permission: permission,
diff --git a/lib/ot/editor-socketio-server.js b/lib/ot/editor-socketio-server.js
index 1b2529be..9e4ddf96 100755
--- a/lib/ot/editor-socketio-server.js
+++ b/lib/ot/editor-socketio-server.js
@@ -43,6 +43,7 @@ EditorSocketIOServer.prototype.addClient = function (socket) {
socket.on('operation', function (revision, operation, selection) {
operation = LZString.decompressFromUTF16(operation);
operation = JSON.parse(operation);
+ socket.origin = 'operation';
self.mayWrite(socket, function (mayWrite) {
if (!mayWrite) {
console.log("User doesn't have the right to edit.");
@@ -59,6 +60,7 @@ EditorSocketIOServer.prototype.addClient = function (socket) {
self.onGetOperations(socket, base, head);
});
socket.on('selection', function (obj) {
+ socket.origin = 'selection';
self.mayWrite(socket, function (mayWrite) {
if (!mayWrite) {
console.log("User doesn't have the right to edit.");
@@ -104,6 +106,7 @@ EditorSocketIOServer.prototype.onOperation = function (socket, revision, operati
'operation', clientId, revision,
wrappedPrime.wrapped.toJSON(), wrappedPrime.meta
);
+ //set document is dirty
this.isDirty = true;
} catch (exc) {
logger.error(exc);
diff --git a/lib/realtime.js b/lib/realtime.js
index 484ef12e..d0db6072 100644
--- a/lib/realtime.js
+++ b/lib/realtime.js
@@ -90,46 +90,10 @@ var updater = setInterval(function () {
if (note.server.isDirty) {
if (config.debug)
logger.info("updater found dirty note: " + key);
- 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) {
+ updaterUpdateMongo(note, function(err, result) {
+ if (err) return callback(err, null);
+ updaterUpdatePostgres(note, function(err, result) {
if (err) return callback(err, null);
- note.server.isDirty = false;
- note.updatetime = Date.now();
- emitCheck(note);
callback(null, null);
});
});
@@ -140,6 +104,56 @@ var updater = setInterval(function () {
if (err) return logger.error('updater error', err);
});
}, 1000);
+function updaterUpdateMongo(note, callback) {
+ Note.findNote(note.id, function (err, _note) {
+ if (err || !_note) return callback(err, null);
+ if (note.lastchangeuser) {
+ if (_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);
+ callback(null, null);
+ });
+ }
+ }
+ });
+ }
+ } else {
+ _note.lastchangeuser = null;
+ note.lastchangeuserprofile = null;
+ Note.updateLastChangeUser(_note, null, function (err, result) {
+ if (err) return callback(err, null);
+ callback(null, null);
+ });
+ }
+ });
+}
+function updaterUpdatePostgres(note, callback) {
+ //postgres update
+ var body = note.server.document;
+ var title = Note.getNoteTitle(body);
+ title = LZString.compressToBase64(title);
+ body = LZString.compressToBase64(body);
+ db.saveToDB(note.id, title, body, function (err, result) {
+ if (err) return callback(err, null);
+ note.server.isDirty = false;
+ note.updatetime = Date.now();
+ emitCheck(note);
+ callback(null, null);
+ });
+}
//clean when user not in any rooms or user not in connected list
var cleaner = setInterval(function () {
async.each(Object.keys(users), function (key, callback) {
@@ -310,6 +324,19 @@ var disconnectSocketQueue = [];
function finishConnection(socket, note, user) {
if (!socket || !note || !user) return;
+ //check view permission
+ if (note.permission == 'private') {
+ if (socket.request.user && socket.request.user.logged_in && socket.request.user._id == note.owner) {
+ //na
+ } else {
+ socket.emit('info', {
+ code: 403
+ });
+ clearSocketQueue(connectionSocketQueue, socket);
+ isConnectionBusy = false;
+ return socket.disconnect(true);
+ }
+ }
note.users[socket.id] = user;
note.socks.push(socket);
note.server.addClient(socket);
@@ -363,13 +390,9 @@ function startConnection(socket) {
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) {
+ Note.findOrNewNote(notename, owner, function (err, note) {
if (err) {
responseError(res, "404", "Not Found", "oops.");
clearSocketQueue(connectionSocketQueue, socket);
@@ -399,41 +422,52 @@ function startConnection(socket) {
updatetime: moment(updatetime).valueOf(),
server: server
};
-
- 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)
+
+ async.parallel([
+ function getlastchangeuser(callback) {
+ 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;
+ }
}
- notes[notename].lastchangeuserprofile = lastchangeuserprofile;
- }
+ callback(null, null);
+ });
+ } else {
+ callback(null, null);
}
- });
- }
-
- 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)
+ },
+ function getowner(callback) {
+ 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;
+ }
}
- notes[notename].ownerprofile = ownerprofile;
- }
+ callback(null, null);
+ });
+ } else {
+ callback(null, null);
}
- finishConnection(socket, notes[notename], users[socket.id]);
- });
- } else {
+ }
+ ], function(err, results){
+ if (err) return;
finishConnection(socket, notes[notename], users[socket.id]);
- }
+ });
});
});
} else {
@@ -545,14 +579,14 @@ function ifMayEdit(socket, callback) {
if (!socket.request.user || !socket.request.user.logged_in)
mayEdit = false;
break;
- case "locked":
+ case "locked": case "private":
//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") {
+ if (socket.origin == 'operation' && 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;
@@ -652,12 +686,22 @@ function connection(socket) {
permission: permission
};
realtime.io.to(note.id).emit('permission', out);
- /*
for (var i = 0, l = note.socks.length; i < l; i++) {
var sock = note.socks[i];
- sock.emit('permission', out);
- };
- */
+ if (typeof sock !== 'undefined' && sock) {
+ //check view permission
+ if (permission == 'private') {
+ if (sock.request.user && sock.request.user.logged_in && sock.request.user._id == note.owner) {
+ //na
+ } else {
+ sock.emit('info', {
+ code: 403
+ });
+ return sock.disconnect(true);
+ }
+ }
+ }
+ }
});
});
}
diff --git a/lib/response.js b/lib/response.js
index a30df470..e27411c8 100644
--- a/lib/response.js
+++ b/lib/response.js
@@ -90,21 +90,9 @@ function showIndex(req, res, next) {
}
function responseHackMD(res, noteId) {
- if (noteId != config.featuresnotename) {
- if (!Note.checkNoteIdValid(noteId)) {
- responseError(res, "404", "Not Found", "oops.");
- return;
- }
- noteId = LZString.decompressFromBase64(noteId);
- if (!noteId) {
- responseError(res, "404", "Not Found", "oops.");
- return;
- }
- }
db.readFromDB(noteId, function (err, data) {
if (err) {
- responseError(res, "404", "Not Found", "oops.");
- return;
+ return response.errorNotFound(res);
}
var body = LZString.decompressFromBase64(data.rows[0].content);
var meta = null;
@@ -144,14 +132,18 @@ function newNote(req, res, next) {
body = LZString.compressToBase64(body);
var owner = null;
if (req.isAuthenticated()) {
- owner = req.session.passport.user;
+ owner = req.user._id;
}
db.newToDB(newId, owner, body, function (err, result) {
if (err) {
- responseError(res, "500", "Internal Error", "wtf.");
- return;
+ return response.errorInternalError(res);
}
- res.redirect("/" + LZString.compressToBase64(newId));
+ Note.newNote(newId, owner, function(err, result) {
+ if (err) {
+ return response.errorInternalError(res);
+ }
+ res.redirect("/" + LZString.compressToBase64(newId));
+ });
});
}
@@ -162,8 +154,7 @@ function showFeatures(req, res, next) {
body = LZString.compressToBase64(body);
db.newToDB(config.featuresnotename, null, body, function (err, result) {
if (err) {
- responseError(res, "500", "Internal Error", "wtf.");
- return;
+ return response.errorInternalError(res);
}
responseHackMD(res, config.featuresnotename);
});
@@ -175,11 +166,32 @@ function showFeatures(req, res, next) {
function showNote(req, res, next) {
var noteId = req.params.noteId;
- if (!Note.checkNoteIdValid(noteId)) {
- responseError(res, "404", "Not Found", "oops.");
- return;
+ if (noteId != config.featuresnotename) {
+ if (!Note.checkNoteIdValid(noteId)) {
+ return response.errorNotFound(res);
+ }
+ noteId = LZString.decompressFromBase64(noteId);
+ if (!noteId) {
+ return response.errorNotFound(res);
+ }
}
- responseHackMD(res, noteId);
+ Note.findNote(noteId, function (err, note) {
+ if (err || !note) {
+ return response.errorNotFound(res);
+ }
+ db.readFromDB(note.id, function (err, data) {
+ if (err) {
+ return response.errorNotFound(res);
+ }
+ var notedata = data.rows[0];
+ //check view permission
+ if (note.permission == 'private') {
+ if (!req.isAuthenticated() || notedata.owner != req.user._id)
+ return response.errorForbidden(res);
+ }
+ responseHackMD(res, noteId);
+ });
+ });
}
function showPublishNote(req, res, next) {
@@ -187,30 +199,33 @@ function showPublishNote(req, res, next) {
if (shortId.isValid(shortid)) {
Note.findNote(shortid, function (err, note) {
if (err || !note) {
- responseError(res, "404", "Not Found", "oops.");
- return;
+ return response.errorNotFound(res);
}
- //increase note viewcount
- Note.increaseViewCount(note, function (err, note) {
- if (err || !note) {
- responseError(res, "404", "Not Found", "oops.");
- return;
+ db.readFromDB(note.id, function (err, data) {
+ if (err) {
+ return response.errorNotFound(res);
+ }
+ var notedata = data.rows[0];
+ //check view permission
+ if (note.permission == 'private') {
+ if (!req.isAuthenticated() || notedata.owner != req.user._id)
+ return response.errorForbidden(res);
}
- db.readFromDB(note.id, function (err, data) {
- if (err) {
- responseError(res, "404", "Not Found", "oops.");
- return;
+ //increase note viewcount
+ Note.increaseViewCount(note, function (err, note) {
+ if (err || !note) {
+ return response.errorNotFound(res);
}
- var body = LZString.decompressFromBase64(data.rows[0].content);
+ var body = LZString.decompressFromBase64(notedata.content);
var meta = null;
try {
meta = metaMarked(body).meta;
} catch(err) {
//na
}
- var updatetime = data.rows[0].update_time;
+ var updatetime = notedata.update_time;
var text = S(body).escapeHTML().s;
- var title = data.rows[0].title;
+ var title = notedata.title;
var decodedTitle = LZString.decompressFromBase64(title);
if (decodedTitle) title = decodedTitle;
title = Note.generateWebTitle(title);
@@ -247,7 +262,7 @@ function showPublishNote(req, res, next) {
});
});
} else {
- responseError(res, "404", "Not Found", "oops.");
+ return response.errorNotFound(res);
}
}
@@ -271,18 +286,12 @@ function renderPublish(data, res) {
function actionPublish(req, res, noteId) {
db.readFromDB(noteId, function (err, data) {
if (err) {
- responseError(res, "404", "Not Found", "oops.");
- return;
+ return response.errorNotFound(res);
}
var owner = data.rows[0].owner;
- var permission = "freely";
- if (owner && owner != "null") {
- permission = "editable";
- }
- Note.findOrNewNote(noteId, permission, function (err, note) {
+ Note.findOrNewNote(noteId, owner, function (err, note) {
if (err) {
- responseError(res, "404", "Not Found", "oops.");
- return;
+ return response.errorNotFound(res);
}
res.redirect("/s/" + note.shortid);
});
@@ -292,18 +301,12 @@ function actionPublish(req, res, noteId) {
function actionSlide(req, res, noteId) {
db.readFromDB(noteId, function (err, data) {
if (err) {
- responseError(res, "404", "Not Found", "oops.");
- return;
+ return response.errorNotFound(res);
}
var owner = data.rows[0].owner;
- var permission = "freely";
- if (owner && owner != "null") {
- permission = "editable";
- }
- Note.findOrNewNote(noteId, permission, function (err, note) {
+ Note.findOrNewNote(noteId, owner, function (err, note) {
if (err) {
- responseError(res, "404", "Not Found", "oops.");
- return;
+ return response.errorNotFound(res);
}
res.redirect("/p/" + note.shortid);
});
@@ -313,8 +316,7 @@ function actionSlide(req, res, noteId) {
function actionDownload(req, res, noteId) {
db.readFromDB(noteId, function (err, data) {
if (err) {
- responseError(res, "404", "Not Found", "oops.");
- return;
+ return response.errorNotFound(res);
}
var body = LZString.decompressFromBase64(data.rows[0].content);
var title = Note.getNoteTitle(body);
@@ -331,8 +333,7 @@ function actionDownload(req, res, noteId) {
function actionPDF(req, res, noteId) {
db.readFromDB(noteId, function (err, data) {
if (err) {
- responseError(res, "404", "Not Found", "oops.");
- return;
+ return response.errorNotFound(res);
}
var body = LZString.decompressFromBase64(data.rows[0].content);
try {
@@ -365,57 +366,81 @@ function noteActions(req, res, next) {
var noteId = req.params.noteId;
if (noteId != config.featuresnotename) {
if (!Note.checkNoteIdValid(noteId)) {
- responseError(res, "404", "Not Found", "oops.");
- return;
+ return response.errorNotFound(res);
}
noteId = LZString.decompressFromBase64(noteId);
if (!noteId) {
- responseError(res, "404", "Not Found", "oops.");
- return;
+ return response.errorNotFound(res);
}
}
- 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;
- }
+ Note.findNote(noteId, function (err, note) {
+ if (err || !note) {
+ return response.errorNotFound(res);
+ }
+ db.readFromDB(note.id, function (err, data) {
+ if (err) {
+ return response.errorNotFound(res);
+ }
+ var notedata = data.rows[0];
+ //check view permission
+ if (note.permission == 'private') {
+ if (!req.isAuthenticated() || notedata.owner != req.user._id)
+ return response.errorForbidden(res);
+ }
+ 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;
+ }
+ });
+ });
}
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;
+ var shortid = req.params.shortid;
+ if (shortId.isValid(shortid)) {
+ Note.findNote(shortid, function (err, note) {
+ if (err || !note) {
+ return response.errorNotFound(res);
+ }
+ db.readFromDB(note.id, function (err, data) {
+ if (err) {
+ return response.errorNotFound(res);
+ }
+ var notedata = data.rows[0];
+ //check view permission
+ if (note.permission == 'private') {
+ if (!req.isAuthenticated() || notedata.owner != req.user._id)
+ return response.errorForbidden(res);
+ }
+ var action = req.params.action;
+ switch (action) {
+ case "edit":
+ if (note.id != config.featuresnotename)
+ res.redirect('/' + LZString.compressToBase64(note.id));
+ else
+ res.redirect('/' + note.id);
+ break;
}
- if (note.id != config.featuresnotename)
- res.redirect('/' + LZString.compressToBase64(note.id));
- else
- res.redirect('/' + note.id);
});
- }
- break;
+ });
}
}
@@ -424,27 +449,30 @@ function showPublishSlide(req, res, next) {
if (shortId.isValid(shortid)) {
Note.findNote(shortid, function (err, note) {
if (err || !note) {
- responseError(res, "404", "Not Found", "oops.");
- return;
+ return response.errorNotFound(res);
}
- //increase note viewcount
- Note.increaseViewCount(note, function (err, note) {
- if (err || !note) {
- responseError(res, "404", "Not Found", "oops.");
- return;
+ db.readFromDB(note.id, function (err, data) {
+ if (err) {
+ return response.errorNotFound(res);
+ }
+ var notedata = data.rows[0];
+ //check view permission
+ if (note.permission == 'private') {
+ if (!req.isAuthenticated() || notedata.owner != req.user._id)
+ return response.errorForbidden(res);
}
- db.readFromDB(note.id, function (err, data) {
- if (err) {
- responseError(res, "404", "Not Found", "oops.");
- return;
+ //increase note viewcount
+ Note.increaseViewCount(note, function (err, note) {
+ if (err || !note) {
+ return response.errorNotFound(res);
}
- var body = LZString.decompressFromBase64(data.rows[0].content);
+ var body = LZString.decompressFromBase64(notedata.content);
try {
body = metaMarked(body).markdown;
} catch(err) {
//na
}
- var title = data.rows[0].title;
+ var title = notedata.title;
var decodedTitle = LZString.decompressFromBase64(title);
if (decodedTitle) title = decodedTitle;
title = Note.generateWebTitle(title);
@@ -454,7 +482,7 @@ function showPublishSlide(req, res, next) {
});
});
} else {
- responseError(res, "404", "Not Found", "oops.");
+ return response.errorNotFound(res);
}
}
diff --git a/public/js/index.js b/public/js/index.js
index 1dfadc9a..86771fde 100644
--- a/public/js/index.js
+++ b/public/js/index.js
@@ -327,7 +327,8 @@ var ui = {
label: $(".ui-permission-label"),
freely: $(".ui-permission-freely"),
editable: $(".ui-permission-editable"),
- locked: $(".ui-permission-locked")
+ locked: $(".ui-permission-locked"),
+ private: $(".ui-permission-private")
}
},
toc: {
@@ -1067,6 +1068,10 @@ ui.infobar.permission.editable.click(function () {
ui.infobar.permission.locked.click(function () {
emitPermission("locked");
});
+//private
+ui.infobar.permission.private.click(function () {
+ emitPermission("private");
+});
function emitPermission(_permission) {
if (_permission != permission) {
@@ -1094,6 +1099,10 @@ function updatePermission(newPermission) {
label = '<i class="fa fa-lock"></i> Locked';
title = "Only owner can edit";
break;
+ case "private":
+ label = '<i class="fa fa-hand-stop-o"></i> Private';
+ title = "Only owner can view & edit";
+ break;
}
if (personalInfo.userid == owner) {
label += ' <i class="fa fa-caret-down"></i>';
@@ -1118,6 +1127,7 @@ function havePermission() {
}
break;
case "locked":
+ case "private":
if (personalInfo.userid != owner) {
bool = false;
} else {
@@ -1145,7 +1155,14 @@ socket.emit = function () {
};
socket.on('info', function (data) {
console.error(data);
- location.href = "./404";
+ switch (data.code) {
+ case 404:
+ location.href = "./404";
+ break;
+ case 403:
+ location.href = "./403";
+ break;
+ }
});
socket.on('error', function (data) {
console.error(data);
@@ -1755,6 +1772,7 @@ editor.on('beforeChange', function (cm, change) {
$('.signin-modal').modal('show');
break;
case "locked":
+ case "private":
$('.locked-modal').modal('show');
break;
}
diff --git a/public/views/body.ejs b/public/views/body.ejs
index b3a49db8..fa7436e7 100644
--- a/public/views/body.ejs
+++ b/public/views/body.ejs
@@ -18,6 +18,7 @@
<li class="ui-permission-freely"><a><i class="fa fa-leaf fa-fw"></i> Freely - Anyone can edit</a></li>
<li class="ui-permission-editable"><a><i class="fa fa-shield fa-fw"></i> Editable - Signed people can edit</a></li>
<li class="ui-permission-locked"><a><i class="fa fa-lock fa-fw"></i> Locked - Only owner can edit</a></li>
+ <li class="ui-permission-private"><a><i class="fa fa-hand-stop-o fa-fw"></i> Private - Only owner can view &amp; edit</a></li>
</ul>
</span>
</small>