Added private permission and clean up codes, solved potential race condition in realtime.js

This commit is contained in:
Wu Cheng-Han 2016-01-17 09:51:27 -06:00
parent 473212676a
commit 49c7dded45
6 changed files with 297 additions and 199 deletions

View file

@ -12,7 +12,7 @@ var db = require("./db.js");
var logger = require("./logger.js"); var logger = require("./logger.js");
//permission types //permission types
permissionTypes = ["freely", "editable", "locked"]; permissionTypes = ["freely", "editable", "locked", "private"];
// create a note model // create a note model
var model = mongoose.model('note', { 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({ var note = new model({
id: id, id: id,
permission: permission, permission: permission,

View file

@ -43,6 +43,7 @@ EditorSocketIOServer.prototype.addClient = function (socket) {
socket.on('operation', function (revision, operation, selection) { socket.on('operation', function (revision, operation, selection) {
operation = LZString.decompressFromUTF16(operation); operation = LZString.decompressFromUTF16(operation);
operation = JSON.parse(operation); operation = JSON.parse(operation);
socket.origin = 'operation';
self.mayWrite(socket, function (mayWrite) { self.mayWrite(socket, function (mayWrite) {
if (!mayWrite) { if (!mayWrite) {
console.log("User doesn't have the right to edit."); console.log("User doesn't have the right to edit.");
@ -59,6 +60,7 @@ EditorSocketIOServer.prototype.addClient = function (socket) {
self.onGetOperations(socket, base, head); self.onGetOperations(socket, base, head);
}); });
socket.on('selection', function (obj) { socket.on('selection', function (obj) {
socket.origin = 'selection';
self.mayWrite(socket, function (mayWrite) { self.mayWrite(socket, function (mayWrite) {
if (!mayWrite) { if (!mayWrite) {
console.log("User doesn't have the right to edit."); console.log("User doesn't have the right to edit.");
@ -104,6 +106,7 @@ EditorSocketIOServer.prototype.onOperation = function (socket, revision, operati
'operation', clientId, revision, 'operation', clientId, revision,
wrappedPrime.wrapped.toJSON(), wrappedPrime.meta wrappedPrime.wrapped.toJSON(), wrappedPrime.meta
); );
//set document is dirty
this.isDirty = true; this.isDirty = true;
} catch (exc) { } catch (exc) {
logger.error(exc); logger.error(exc);

View file

@ -90,10 +90,25 @@ var updater = setInterval(function () {
if (note.server.isDirty) { if (note.server.isDirty) {
if (config.debug) if (config.debug)
logger.info("updater found dirty note: " + key); logger.info("updater found dirty note: " + key);
updaterUpdateMongo(note, function(err, result) {
if (err) return callback(err, null);
updaterUpdatePostgres(note, function(err, result) {
if (err) return callback(err, null);
callback(null, null);
});
});
} else {
callback(null, null);
}
}, function (err) {
if (err) return logger.error('updater error', err);
});
}, 1000);
function updaterUpdateMongo(note, callback) {
Note.findNote(note.id, function (err, _note) { Note.findNote(note.id, function (err, _note) {
if (err || !_note) return callback(err, null); if (err || !_note) return callback(err, null);
//mongo update if (note.lastchangeuser) {
if (note.lastchangeuser && _note.lastchangeuser != note.lastchangeuser) { if (_note.lastchangeuser != note.lastchangeuser) {
var lastchangeuser = note.lastchangeuser; var lastchangeuser = note.lastchangeuser;
var lastchangeuserprofile = null; var lastchangeuserprofile = null;
User.findUser(lastchangeuser, function (err, user) { User.findUser(lastchangeuser, function (err, user) {
@ -105,41 +120,40 @@ var updater = setInterval(function () {
name: profile.displayName || profile.username, name: profile.displayName || profile.username,
photo: User.parsePhotoByProfile(profile) photo: User.parsePhotoByProfile(profile)
} }
note.lastchangeuser = lastchangeuser; _note.lastchangeuser = lastchangeuser;
note.lastchangeuserprofile = lastchangeuserprofile; note.lastchangeuserprofile = lastchangeuserprofile;
Note.updateLastChangeUser(_note, lastchangeuser, function (err, result) { Note.updateLastChangeUser(_note, lastchangeuser, function (err, result) {
if (err) return callback(err, null); if (err) return callback(err, null);
callback(null, null);
}); });
} }
} }
}); });
}
} else { } else {
note.lastchangeuser = null; _note.lastchangeuser = null;
note.lastchangeuserprofile = null; note.lastchangeuserprofile = null;
Note.updateLastChangeUser(_note, null, function (err, result) { Note.updateLastChangeUser(_note, null, function (err, result) {
if (err) return callback(err, null); if (err) return callback(err, null);
callback(null, null);
}); });
} }
});
}
function updaterUpdatePostgres(note, callback) {
//postgres update //postgres update
var body = note.server.document; var body = note.server.document;
var title = Note.getNoteTitle(body); var title = Note.getNoteTitle(body);
title = LZString.compressToBase64(title); title = LZString.compressToBase64(title);
body = LZString.compressToBase64(body); body = LZString.compressToBase64(body);
db.saveToDB(key, title, body, function (err, result) { db.saveToDB(note.id, title, body, function (err, result) {
if (err) return callback(err, null); if (err) return callback(err, null);
note.server.isDirty = false; note.server.isDirty = false;
note.updatetime = Date.now(); note.updatetime = Date.now();
emitCheck(note); emitCheck(note);
callback(null, null); callback(null, null);
}); });
});
} else {
callback(null, null);
} }
}, function (err) {
if (err) return logger.error('updater error', err);
});
}, 1000);
//clean when user not in any rooms or user not in connected list //clean when user not in any rooms or user not in connected list
var cleaner = setInterval(function () { var cleaner = setInterval(function () {
async.each(Object.keys(users), function (key, callback) { async.each(Object.keys(users), function (key, callback) {
@ -310,6 +324,19 @@ var disconnectSocketQueue = [];
function finishConnection(socket, note, user) { function finishConnection(socket, note, user) {
if (!socket || !note || !user) return; 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.users[socket.id] = user;
note.socks.push(socket); note.socks.push(socket);
note.server.addClient(socket); note.server.addClient(socket);
@ -363,13 +390,9 @@ function startConnection(socket) {
var owner = data.rows[0].owner; var owner = data.rows[0].owner;
var ownerprofile = null; var ownerprofile = null;
var permission = "freely";
if (owner && owner != "null") {
permission = "editable";
}
//find or new note //find or new note
Note.findOrNewNote(notename, permission, function (err, note) { Note.findOrNewNote(notename, owner, function (err, note) {
if (err) { if (err) {
responseError(res, "404", "Not Found", "oops."); responseError(res, "404", "Not Found", "oops.");
clearSocketQueue(connectionSocketQueue, socket); clearSocketQueue(connectionSocketQueue, socket);
@ -400,6 +423,8 @@ function startConnection(socket) {
server: server server: server
}; };
async.parallel([
function getlastchangeuser(callback) {
if (lastchangeuser) { if (lastchangeuser) {
//find last change user profile if lastchangeuser exists //find last change user profile if lastchangeuser exists
User.findUser(lastchangeuser, function (err, user) { User.findUser(lastchangeuser, function (err, user) {
@ -413,9 +438,13 @@ function startConnection(socket) {
notes[notename].lastchangeuserprofile = lastchangeuserprofile; notes[notename].lastchangeuserprofile = lastchangeuserprofile;
} }
} }
callback(null, null);
}); });
} else {
callback(null, null);
} }
},
function getowner(callback) {
if (owner && owner != "null") { if (owner && owner != "null") {
//find owner profile if owner exists //find owner profile if owner exists
User.findUser(owner, function (err, user) { User.findUser(owner, function (err, user) {
@ -429,11 +458,16 @@ function startConnection(socket) {
notes[notename].ownerprofile = ownerprofile; notes[notename].ownerprofile = ownerprofile;
} }
} }
finishConnection(socket, notes[notename], users[socket.id]); callback(null, null);
}); });
} else { } else {
finishConnection(socket, notes[notename], users[socket.id]); callback(null, null);
} }
}
], function(err, results){
if (err) return;
finishConnection(socket, notes[notename], users[socket.id]);
});
}); });
}); });
} else { } else {
@ -545,14 +579,14 @@ function ifMayEdit(socket, callback) {
if (!socket.request.user || !socket.request.user.logged_in) if (!socket.request.user || !socket.request.user.logged_in)
mayEdit = false; mayEdit = false;
break; break;
case "locked": case "locked": case "private":
//only owner can change //only owner can change
if (note.owner != socket.request.user._id) if (note.owner != socket.request.user._id)
mayEdit = false; mayEdit = false;
break; break;
} }
//if user may edit and this note have owner (not anonymous usage) //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 //save for the last change user id
if (socket.request.user && socket.request.user.logged_in) { if (socket.request.user && socket.request.user.logged_in) {
note.lastchangeuser = socket.request.user._id; note.lastchangeuser = socket.request.user._id;
@ -652,12 +686,22 @@ function connection(socket) {
permission: permission permission: permission
}; };
realtime.io.to(note.id).emit('permission', out); realtime.io.to(note.id).emit('permission', out);
/*
for (var i = 0, l = note.socks.length; i < l; i++) { for (var i = 0, l = note.socks.length; i < l; i++) {
var sock = note.socks[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);
}
}
}
}
}); });
}); });
} }

View file

@ -90,21 +90,9 @@ function showIndex(req, res, next) {
} }
function responseHackMD(res, noteId) { 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) { db.readFromDB(noteId, function (err, data) {
if (err) { if (err) {
responseError(res, "404", "Not Found", "oops."); return response.errorNotFound(res);
return;
} }
var body = LZString.decompressFromBase64(data.rows[0].content); var body = LZString.decompressFromBase64(data.rows[0].content);
var meta = null; var meta = null;
@ -144,15 +132,19 @@ function newNote(req, res, next) {
body = LZString.compressToBase64(body); body = LZString.compressToBase64(body);
var owner = null; var owner = null;
if (req.isAuthenticated()) { if (req.isAuthenticated()) {
owner = req.session.passport.user; owner = req.user._id;
} }
db.newToDB(newId, owner, body, function (err, result) { db.newToDB(newId, owner, body, function (err, result) {
if (err) { if (err) {
responseError(res, "500", "Internal Error", "wtf."); return response.errorInternalError(res);
return; }
Note.newNote(newId, owner, function(err, result) {
if (err) {
return response.errorInternalError(res);
} }
res.redirect("/" + LZString.compressToBase64(newId)); res.redirect("/" + LZString.compressToBase64(newId));
}); });
});
} }
function showFeatures(req, res, next) { function showFeatures(req, res, next) {
@ -162,8 +154,7 @@ function showFeatures(req, res, next) {
body = LZString.compressToBase64(body); body = LZString.compressToBase64(body);
db.newToDB(config.featuresnotename, null, body, function (err, result) { db.newToDB(config.featuresnotename, null, body, function (err, result) {
if (err) { if (err) {
responseError(res, "500", "Internal Error", "wtf."); return response.errorInternalError(res);
return;
} }
responseHackMD(res, config.featuresnotename); responseHackMD(res, config.featuresnotename);
}); });
@ -175,11 +166,32 @@ function showFeatures(req, res, next) {
function showNote(req, res, next) { function showNote(req, res, next) {
var noteId = req.params.noteId; var noteId = req.params.noteId;
if (noteId != config.featuresnotename) {
if (!Note.checkNoteIdValid(noteId)) { if (!Note.checkNoteIdValid(noteId)) {
responseError(res, "404", "Not Found", "oops."); return response.errorNotFound(res);
return; }
noteId = LZString.decompressFromBase64(noteId);
if (!noteId) {
return response.errorNotFound(res);
}
}
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); responseHackMD(res, noteId);
});
});
} }
function showPublishNote(req, res, next) { function showPublishNote(req, res, next) {
@ -187,30 +199,33 @@ function showPublishNote(req, res, next) {
if (shortId.isValid(shortid)) { if (shortId.isValid(shortid)) {
Note.findNote(shortid, function (err, note) { Note.findNote(shortid, function (err, note) {
if (err || !note) { if (err || !note) {
responseError(res, "404", "Not Found", "oops."); return response.errorNotFound(res);
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);
} }
//increase note viewcount //increase note viewcount
Note.increaseViewCount(note, function (err, note) { Note.increaseViewCount(note, function (err, note) {
if (err || !note) { if (err || !note) {
responseError(res, "404", "Not Found", "oops."); return response.errorNotFound(res);
return;
} }
db.readFromDB(note.id, function (err, data) { var body = LZString.decompressFromBase64(notedata.content);
if (err) {
responseError(res, "404", "Not Found", "oops.");
return;
}
var body = LZString.decompressFromBase64(data.rows[0].content);
var meta = null; var meta = null;
try { try {
meta = metaMarked(body).meta; meta = metaMarked(body).meta;
} catch(err) { } catch(err) {
//na //na
} }
var updatetime = data.rows[0].update_time; var updatetime = notedata.update_time;
var text = S(body).escapeHTML().s; var text = S(body).escapeHTML().s;
var title = data.rows[0].title; var title = notedata.title;
var decodedTitle = LZString.decompressFromBase64(title); var decodedTitle = LZString.decompressFromBase64(title);
if (decodedTitle) title = decodedTitle; if (decodedTitle) title = decodedTitle;
title = Note.generateWebTitle(title); title = Note.generateWebTitle(title);
@ -247,7 +262,7 @@ function showPublishNote(req, res, next) {
}); });
}); });
} else { } else {
responseError(res, "404", "Not Found", "oops."); return response.errorNotFound(res);
} }
} }
@ -271,18 +286,12 @@ function renderPublish(data, res) {
function actionPublish(req, res, noteId) { function actionPublish(req, res, noteId) {
db.readFromDB(noteId, function (err, data) { db.readFromDB(noteId, function (err, data) {
if (err) { if (err) {
responseError(res, "404", "Not Found", "oops."); return response.errorNotFound(res);
return;
} }
var owner = data.rows[0].owner; var owner = data.rows[0].owner;
var permission = "freely"; Note.findOrNewNote(noteId, owner, function (err, note) {
if (owner && owner != "null") {
permission = "editable";
}
Note.findOrNewNote(noteId, permission, function (err, note) {
if (err) { if (err) {
responseError(res, "404", "Not Found", "oops."); return response.errorNotFound(res);
return;
} }
res.redirect("/s/" + note.shortid); res.redirect("/s/" + note.shortid);
}); });
@ -292,18 +301,12 @@ function actionPublish(req, res, noteId) {
function actionSlide(req, res, noteId) { function actionSlide(req, res, noteId) {
db.readFromDB(noteId, function (err, data) { db.readFromDB(noteId, function (err, data) {
if (err) { if (err) {
responseError(res, "404", "Not Found", "oops."); return response.errorNotFound(res);
return;
} }
var owner = data.rows[0].owner; var owner = data.rows[0].owner;
var permission = "freely"; Note.findOrNewNote(noteId, owner, function (err, note) {
if (owner && owner != "null") {
permission = "editable";
}
Note.findOrNewNote(noteId, permission, function (err, note) {
if (err) { if (err) {
responseError(res, "404", "Not Found", "oops."); return response.errorNotFound(res);
return;
} }
res.redirect("/p/" + note.shortid); res.redirect("/p/" + note.shortid);
}); });
@ -313,8 +316,7 @@ function actionSlide(req, res, noteId) {
function actionDownload(req, res, noteId) { function actionDownload(req, res, noteId) {
db.readFromDB(noteId, function (err, data) { db.readFromDB(noteId, function (err, data) {
if (err) { if (err) {
responseError(res, "404", "Not Found", "oops."); return response.errorNotFound(res);
return;
} }
var body = LZString.decompressFromBase64(data.rows[0].content); var body = LZString.decompressFromBase64(data.rows[0].content);
var title = Note.getNoteTitle(body); var title = Note.getNoteTitle(body);
@ -331,8 +333,7 @@ function actionDownload(req, res, noteId) {
function actionPDF(req, res, noteId) { function actionPDF(req, res, noteId) {
db.readFromDB(noteId, function (err, data) { db.readFromDB(noteId, function (err, data) {
if (err) { if (err) {
responseError(res, "404", "Not Found", "oops."); return response.errorNotFound(res);
return;
} }
var body = LZString.decompressFromBase64(data.rows[0].content); var body = LZString.decompressFromBase64(data.rows[0].content);
try { try {
@ -365,15 +366,27 @@ function noteActions(req, res, next) {
var noteId = req.params.noteId; var noteId = req.params.noteId;
if (noteId != config.featuresnotename) { if (noteId != config.featuresnotename) {
if (!Note.checkNoteIdValid(noteId)) { if (!Note.checkNoteIdValid(noteId)) {
responseError(res, "404", "Not Found", "oops."); return response.errorNotFound(res);
return;
} }
noteId = LZString.decompressFromBase64(noteId); noteId = LZString.decompressFromBase64(noteId);
if (!noteId) { if (!noteId) {
responseError(res, "404", "Not Found", "oops."); return response.errorNotFound(res);
return;
} }
} }
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; var action = req.params.action;
switch (action) { switch (action) {
case "publish": case "publish":
@ -396,27 +409,39 @@ function noteActions(req, res, next) {
res.redirect('/' + noteId); res.redirect('/' + noteId);
break; break;
} }
});
});
} }
function publishNoteActions(req, res, next) { function publishNoteActions(req, res, next) {
var action = req.params.action;
switch (action) {
case "edit":
var shortid = req.params.shortid; var shortid = req.params.shortid;
if (shortId.isValid(shortid)) { if (shortId.isValid(shortid)) {
Note.findNote(shortid, function (err, note) { Note.findNote(shortid, function (err, note) {
if (err || !note) { if (err || !note) {
responseError(res, "404", "Not Found", "oops."); return response.errorNotFound(res);
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);
}
var action = req.params.action;
switch (action) {
case "edit":
if (note.id != config.featuresnotename) if (note.id != config.featuresnotename)
res.redirect('/' + LZString.compressToBase64(note.id)); res.redirect('/' + LZString.compressToBase64(note.id));
else else
res.redirect('/' + note.id); res.redirect('/' + note.id);
});
}
break; break;
} }
});
});
}
} }
function showPublishSlide(req, res, next) { function showPublishSlide(req, res, next) {
@ -424,27 +449,30 @@ function showPublishSlide(req, res, next) {
if (shortId.isValid(shortid)) { if (shortId.isValid(shortid)) {
Note.findNote(shortid, function (err, note) { Note.findNote(shortid, function (err, note) {
if (err || !note) { if (err || !note) {
responseError(res, "404", "Not Found", "oops."); return response.errorNotFound(res);
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);
} }
//increase note viewcount //increase note viewcount
Note.increaseViewCount(note, function (err, note) { Note.increaseViewCount(note, function (err, note) {
if (err || !note) { if (err || !note) {
responseError(res, "404", "Not Found", "oops."); return response.errorNotFound(res);
return;
} }
db.readFromDB(note.id, function (err, data) { var body = LZString.decompressFromBase64(notedata.content);
if (err) {
responseError(res, "404", "Not Found", "oops.");
return;
}
var body = LZString.decompressFromBase64(data.rows[0].content);
try { try {
body = metaMarked(body).markdown; body = metaMarked(body).markdown;
} catch(err) { } catch(err) {
//na //na
} }
var title = data.rows[0].title; var title = notedata.title;
var decodedTitle = LZString.decompressFromBase64(title); var decodedTitle = LZString.decompressFromBase64(title);
if (decodedTitle) title = decodedTitle; if (decodedTitle) title = decodedTitle;
title = Note.generateWebTitle(title); title = Note.generateWebTitle(title);
@ -454,7 +482,7 @@ function showPublishSlide(req, res, next) {
}); });
}); });
} else { } else {
responseError(res, "404", "Not Found", "oops."); return response.errorNotFound(res);
} }
} }

View file

@ -327,7 +327,8 @@ var ui = {
label: $(".ui-permission-label"), label: $(".ui-permission-label"),
freely: $(".ui-permission-freely"), freely: $(".ui-permission-freely"),
editable: $(".ui-permission-editable"), editable: $(".ui-permission-editable"),
locked: $(".ui-permission-locked") locked: $(".ui-permission-locked"),
private: $(".ui-permission-private")
} }
}, },
toc: { toc: {
@ -1067,6 +1068,10 @@ ui.infobar.permission.editable.click(function () {
ui.infobar.permission.locked.click(function () { ui.infobar.permission.locked.click(function () {
emitPermission("locked"); emitPermission("locked");
}); });
//private
ui.infobar.permission.private.click(function () {
emitPermission("private");
});
function emitPermission(_permission) { function emitPermission(_permission) {
if (_permission != permission) { if (_permission != permission) {
@ -1094,6 +1099,10 @@ function updatePermission(newPermission) {
label = '<i class="fa fa-lock"></i> Locked'; label = '<i class="fa fa-lock"></i> Locked';
title = "Only owner can edit"; title = "Only owner can edit";
break; break;
case "private":
label = '<i class="fa fa-hand-stop-o"></i> Private';
title = "Only owner can view & edit";
break;
} }
if (personalInfo.userid == owner) { if (personalInfo.userid == owner) {
label += ' <i class="fa fa-caret-down"></i>'; label += ' <i class="fa fa-caret-down"></i>';
@ -1118,6 +1127,7 @@ function havePermission() {
} }
break; break;
case "locked": case "locked":
case "private":
if (personalInfo.userid != owner) { if (personalInfo.userid != owner) {
bool = false; bool = false;
} else { } else {
@ -1145,7 +1155,14 @@ socket.emit = function () {
}; };
socket.on('info', function (data) { socket.on('info', function (data) {
console.error(data); console.error(data);
switch (data.code) {
case 404:
location.href = "./404"; location.href = "./404";
break;
case 403:
location.href = "./403";
break;
}
}); });
socket.on('error', function (data) { socket.on('error', function (data) {
console.error(data); console.error(data);
@ -1755,6 +1772,7 @@ editor.on('beforeChange', function (cm, change) {
$('.signin-modal').modal('show'); $('.signin-modal').modal('show');
break; break;
case "locked": case "locked":
case "private":
$('.locked-modal').modal('show'); $('.locked-modal').modal('show');
break; break;
} }

View file

@ -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-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-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-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> </ul>
</span> </span>
</small> </small>