Files
desktop-app/node_modules/note.js
2015-02-01 21:30:28 +08:00

889 lines
24 KiB
JavaScript

var async = require('async');
var db = require('db');
var fs = require('fs');
var File = require('file');
var Evt = require('evt');
var User = require('user');
var Notebook = require('notebook');
var Tag = require('tag');
// var Api = require('api');
var Server = require('server');
var Common = require('common');
var Notes = db.notes;
var Api = null; // require('api')
function log(o) {
console.log(o);
}
// 笔记服务
var Note = {
/*
type NoteOrContent struct {
NotebookId string
NoteId string
UserId string
Title string
Desc string
ImgSrc string
Tags []string
Content string
Abstract string
IsNew bool
IsMarkdown bool
FromUserId string // 为共享而新建
IsBlog bool // 是否是blog, 更新note不需要修改, 添加note时才有可能用到, 此时需要判断notebook是否设为Blog
}
*/
// 更新笔记
updateNoteOrContent: function(noteOrContent, callback) {
var userId = User.getCurActiveUserId();
noteOrContent['UserId'] = userId;
log('update');
var date = new Date();
noteOrContent.UpdatedTime = date;
noteOrContent['IsDirty'] = true; // 已修改
// 新建笔记, IsNew还是保存着
if(noteOrContent.IsNew) {
noteOrContent.CreatedTime = date;
noteOrContent['IsTrash'] = false;
delete noteOrContent['IsNew'];
noteOrContent['LocalIsNew'] = true;
Notes.insert(noteOrContent, function (err, newDoc) { // Callback is optional
if(err) {
console.log(err);
callback && callback(false);
} else {
// 为什么又设置成true, 因为js的对象是共享的, callback用到了noteOrContent.IsNew来做判断
noteOrContent['IsNew'] = true;
callback && callback(newDoc);
// 重新统计笔记本的笔记数量
Notebook.reCountNotebookNumberNotes(noteOrContent.NotebookId);
// 标签
if(noteOrContent.Tags && noteOrContent.Tags.length > 0) {
Tag.addTags(noteOrContent.Tags);
}
}
});
// 更新笔记
} else {
var updateFields = ['Desc', 'ImgSrc', 'Title', 'Tags', 'Content'];
var updates = {};
var needUpdate = false;
for(var i in updateFields) {
var field = updateFields[i];
if(field in noteOrContent) {
updates[field] = noteOrContent[field];
needUpdate = true;
}
}
if('Content' in updateFields) {
// 内容更新了, 因为内容是以后才从远程获取的, 获取内容后改变ContentIsDirty=false
noteOrContent['ContentIsDirty'] = true;
}
if(needUpdate) {
updates['IsDirty'] = true;
updates.UpdatedTime = date;
// Set an existing field's value
Notes.update({NoteId: noteOrContent.NoteId}, { $set: updates }, {}, function (err, numReplaced) {
if(err) {
log(err);
callback && callback(false);
} else {
callback && callback(noteOrContent);
// 标签
if(noteOrContent.Tags && noteOrContent.Tags.length > 0) {
Tag.addTags(noteOrContent.Tags);
}
}
});
}
}
},
// 获取笔记列表
getNotes: function(notebookId, callback) {
var me = this;
me._getNotes(notebookId, false, callback);
},
// 获取trash笔记
getTrashNotes: function(callback) {
var me = this;
me._getNotes('', true, callback);
},
_getNotes: function(notebookId, isTrash, callback) {
var userId = User.getCurActiveUserId();
var query = {
UserId: userId,
IsTrash: false,
};
if(notebookId) {
query['NotebookId'] = notebookId;
}
if(isTrash) {
query['IsTrash'] = true;
}
Notes.find(query).sort({'UpdatedTime': -1}).exec(function(err, notes) {
if(err) {
log(err);
return callback && callback(false);
}
return callback && callback(notes);
});
},
deleteNote: function(noteId, callback) {
Notes.update({NoteId: noteId}, {$set: {IsTrash: true, IsDirty: true}}, function(err, n) {
if(err || !n) {
callback(false);
} else {
callback(true);
}
});
},
deleteTrash: function(noteId, callback) {
Notes.update({NoteId: noteId}, {$set: {IsDirty: true, LocalIsDelete: true}}, function(err, n) {
if(err || !n) {
callback(false);
} else {
callback(true);
}
});
},
// 得到笔记
getNote: function(noteId, callback) {
var me = this;
Notes.findOne({NoteId: noteId}, function(err, doc) {
if(err || !doc) {
log('不存在');
callback && callback(false);
} else {
callback && callback(doc);
}
});
},
// 服务器上的数据到本地
fixNoteContent: function(content) {
if(!content) {
return content;
}
var reg = new RegExp(Evt.leanoteUrl + '/api/file/getImage', 'g');
content = content.replace(reg, Server.localUrl + '/api/file/getImage');
var reg2 = new RegExp(Evt.leanoteUrl + '/api/file/getAttach', 'g');
content = content.replace(reg, Evt.localUrl + '/api/file/getAttach');
var reg3 = new RegExp(Evt.leanoteUrl + '/api/file/getAllAttach', 'g');
content = content.replace(reg, Evt.localUrl + '/api/file/getAllAttach');
return content;
},
// 将本地的url改下, 发送数据到服务器上
fixNoteContentForSend: function(content) {
if(!content) {
return content;
}
console.log(Evt.localUrl + '/api/file/getImage');
var reg = new RegExp(Evt.localUrl + '/api/file/getImage', 'g');
content = content.replace(reg, Evt.leanoteUrl + '/api/file/getImage');
var reg2 = new RegExp(Evt.localUrl + '/api/file/getAttach', 'g');
content = content.replace(reg, Evt.leanoteUrl + '/api/file/getAttach');
var reg3 = new RegExp(Evt.localUrl + '/api/file/getAllAttach', 'g');
content = content.replace(reg, Evt.leanoteUrl + '/api/file/getAllAttach');
return content;
},
// 远程修改本地内容
updateNoteContentForce: function(noteId, content, callback) {
var me = this;
content = me.fixNoteContent(content);
Notes.update({NoteId: noteId}, { $set: {Content: content, InitSync: false, IsContentDirty: false} }, {}, function (err, numReplaced) {
if(err) {
log(err);
callback && callback(false);
} else {
callback && callback(content);
}
});
},
/*
// 同步内容
updateNoteContentForce: function(noteId, content, callback) {
// 将笔记内容中
Notes.update({NoteId: noteId}, { $set: {Content: content, InitSync: false} }, {}, function (err, numReplaced) {
if(err) {
log(err);
callback && callback(false);
} else {
callback && callback(content);
}
});
},
*/
// 得到笔记内容
// noteId是本地Id
getNoteContent: function(noteId, callback) {
var me = this;
log('getNoteContent------')
me.getNote(noteId, function(note) {
if(!Common.isOk(note)) {
log('not ok');
log(note);
callback && callback(false);
} else {
// 如果笔记是刚同步过来的, 那么内容要重新获取
if(note.InitSync) {
log('need load from server');
if(!Api) {
Api = require('api')
}
// 远程获取
me.getServerNoteIdByNoteId(noteId, function(serverNoteId) {
if(!serverNoteId) {
return callback && callback(false);
}
Api.getNoteContent(serverNoteId, function(noteContent) {
// 同步到本地
if(Common.isOk(noteContent)) {
me.updateNoteContentForce(noteId, noteContent.Content, function(content) {
noteContent.Content = content;
noteContent.NoteId = noteId;
callback && callback(noteContent);
});
} else {
callback && callback(false);
}
});
});
} else {
log('not need');
callback && callback(note);
}
}
});
},
//----------------
// 同步
//----------------
getNoteByServerNoteId: function(noteId, callback) {
var me = this;
Notes.findOne({ServerNoteId: noteId}, function(err, doc) {
if(err || !doc) {
log('getNoteByServerNoteId 不存在' + noteId);
callback && callback(false);
} else {
callback && callback(doc);
}
});
},
getNoteIdByServerNoteId: function(noteId, callback) {
var me = this;
Notes.findOne({ServerNoteId: noteId}, function(err, doc) {
if(err || !doc) {
log('getNoteIdByServerNoteId 不存在' + noteId);
callback && callback(false);
} else {
callback && callback(doc.NoteId);
}
});
},
getServerNoteIdByNoteId: function(noteId, callback) {
var me = this;
Notes.findOne({NoteId: noteId}, function(err, doc) {
if(err || !doc) {
log('getServerNoteIdByNoteId 不存在');
callback && callback(false);
} else {
callback && callback(doc.ServerNoteId);
}
});
},
// 强制删除
// TODO 是否真的删除 ?
// 有可能服务器上删除了是误删 ?
deleteNoteForce: function(noteId, callback) {
var me = this;
Notes.remove({ServerNoteId: noteId}, function(err, n) {
if(err) {
callback && callback(false);
} else {
callback && callback(true);
}
});
},
// 添加笔记本, note object
// note是服务器传过来的, 需要处理下fix
// NoteId, ServerNoteId, NotebookId(本地的)
addNoteForce: function(note, callback) {
note.InitSync = true; // 刚同步完, 表示content, images, attach没有同步
note.IsDirty = false;
note.ServerNoteId = note.NoteId;
note.NoteId = Common.objectId();
// 附件操作
var files = note.Files || [];
var attachs = [];
for(var i in files) {
var file = files[i];
if(file.IsAttach) { // LocalFileId, FileId
file.ServerFileId = file.FileId;
file.FileId = file.ServerFileId; // 弄成一样的, 只是没有Path
attachs.push(file);
}
}
note.Attachs = attachs;
delete note['Files'];
Notebook.getNotebookIdByServerNotebookId(note.NotebookId, function(localNotebookId) {
note.NotebookId = localNotebookId;
Notes.insert(note, function (err, newDoc) { // Callback is optional
if(err) {
console.log(err);
callback && callback(false);
} else {
callback && callback(newDoc);
}
});
});
},
// 删除附件
deleteAttachs: function(attachs) {
var me = this;
var fileBasePath = User.getCurUserAttachsPath();
if(!attachs) {
return;
}
for(var i in attachs) {
var path = attachs[i].Path;
if(path && path.indexOf(fileBasePath) > 0) {
try {
fs.unlink(path);
} catch(e) {
console.log(e);
}
}
}
},
// sync <- 时
// 更新笔记, 合并之, 内容要重新获取
// note是服务器传过来的, 需要处理下fix
// note.NoteId是服务器的
updateNoteForce: function(note, callback) {
var me = this;
note.IsDirty = false;
note.InitSync = true;
note.LocalIsNew = false;
// 附件处理
var files = note.Files || [];
var attachsMap = [];
for(var i in files) {
var file = files[i];
if(file.IsAttach) { // LocalFileId, FileId
// 对于服务器上的, 只有FileId会传过来, 此时要与之前的做对比
file.ServerFileId = file.FileId;
delete file['FileId'];
attachsMap[file.ServerFileId] = file;
}
}
// 之前也是有attachs的, 得到之前的attachs, 进行个merge
me.getNoteByServerNoteId(note.NoteId, function(everNote) {
if(!everNote) {
return;
}
var everAttachs = everNote.Attachs;
var everAttachsMap = {};
// var needAdds = [];
// 得到要删除的
var needDeletes = [];
for(var i in everAttachs) {
var everAttach = everAttachs[i];
everAttachsMap[everAttach.ServerFileId] = everAttach;
if(!attachsMap[everAttach.ServerFileId]) {
needDeletes.push(everAttach);
}
}
console.log('everAttachs');
console.log(everAttachs);
console.log('attachsMap')
console.log(attachsMap);
// 通过FileId删除文件
me.deleteAttachs(needDeletes);
// 得到要添加的,所有的
// 新添加的没有Path
var allAttachs = [];
for(var serverFileId in attachsMap) {
if(!everAttachsMap[serverFileId]) {
// needAdds.push(attachMap[serverFileId]);
attachsMap[serverFileId].FileId = serverFileId; // 生成一个Id(一样的), 但是没有Path
allAttachs.push(attachsMap[serverFileId]);
} else {
allAttachs.push(everAttachsMap[serverFileId]);
}
}
note.Attachs = allAttachs;
note.ServerNoteId = note.NoteId;
note.NoteId = everNote.NoteId;
delete note['Files'];
// console.log('evernote');
// console.log(everNote);
// 得到本地笔记本Id
Notebook.getNotebookIdByServerNotebookId(note.NotebookId, function(localNotebookId) {
note['NotebookId'] = localNotebookId;
console.log("updateNoteForce 后的")
console.log(note);
Notes.update({NoteId: note.NoteId}, {$set: note}, {}, function (err, cnt) { // Callback is optional
console.log('re:');
console.log(err);
console.log(cnt);
if(err) {
console.log(err);
callback && callback(false);
} else {
log('强制更新...');
callback && callback(note);
}
});
});
});
},
// addNote, updateNote后的操作
// 添加修改ServerNoteId; 更新修改usn
updateNoteForceForSendChange: function(note, isAdd, callback) {
var me = this;
note.IsDirty = false;
note.InitSync = false;
note.LocalIsNew = false;
// note.UserId = User.getCurActiveUserId();
//
console.log("updateNoteForceForSendChange");
console.log(note);
// 如果是添加的, 因为不会传内容
// if(isAdd) {
delete note['Content'];
// }
console.log('server data from::::');
console.log(note.NoteId);
console.log(note.Files);
// 修改Imags的LocalFileId <=> FileId的映射
File.updateImageForce(note.Files);
// 修改attach, 建立LocalFileId <=> FileId的映射
var files = note.Files || [];
var filesMap = {}; // LocalFileId => ServerFileId
for(var i in files) {
var file = files[i];
if(file.IsAttach) { // LocalFileId, FileId
filesMap[file.LocalFileId] = file.FileId;
}
}
// 之前也是有attachs的, 得到之前的attachs, 进行个merge
me.getNote(note.NoteId, function(everNote) {
if(!everNote) {
console.log('我靠, 没有?' + note.NoteId);
return;
}
var everAttachs = everNote.Attachs || [];
for(var i in everAttachs) {
var everAttach = everAttachs[i];
if(filesMap[everAttach.FileId]) {
everAttach.ServerFileId = filesMap[everAttach.FileId];
everAttach.IsDirty = false; // 不为dirty了, 记得在sync后也改为false
}
}
note.Attachs = everAttachs;
console.log('fix after');
console.log(everAttachs);
delete note['Files'];
Notes.update({NoteId: note.NoteId}, {$set: note}, function(err, n) {
if(err || !n) {
log('updateNoteForceForSendChange err');
log(err);
return callback && callback(false);
}
return callback && callback(true);
});
});
},
// 服务器上的数据
// 为冲突更新, note已有有NoteId, ServerNoteId, 但NotebookId是服务器端的
updateNoteForceForConflict: function(note, callback) {
var me = this;
note.NoteId = note.ServerNoteId;
me.updateNoteForce(note, callback);
return;
note.IsDirty = false;
note.InitSync = true;
note.LocalIsNew = false;
// 文件操作
Notebook.getNotebookIdByServerNotebookId(note.NotebookId, function(localNotebookId) {
note['NotebookId'] = localNotebookId;
Notes.update({NoteId: note.NoteId}, {$set: note}, {}, function (err, cnt) { // Callback is optional
if(err) {
console.log(err);
callback && callback(false);
} else {
log('强制更新...');
callback && callback(note);
}
});
});
},
// 将本地冲突的笔记复制一份
// serverNoteId
// TODO 附件也要复制一份
copyNoteForConfict: function(noteId, callback) {
var me = this;
me.getNote(noteId, function(note) {
if(!note) {
callback(false);
return;
}
// 新Id
delete note['_id'];
note.NoteId = Common.objectId();
note.ConflictNoteId = noteId; // 与noteId有冲突
note.ConflictTime = new Date(); // 发生冲突时间
note.ConflictFixed = false; // 冲突未解决
note.IsDirty = true;
note.LocalIsNew = true; // 新增加的
note.InitSync = false; // 都是本地的, 相当于新建的笔记
// 只复制有path的
var attachs = note.Attachs || [];
var newAttachs = [];
console.log('不会吧.............')
console.log(attachs);
async.eachSeries(attachs, function(attach, cb) {
if(!attach.Path) {
return cb();
}
// 新路径
var filePathAttr = Common.splitFile(attach.Path);
filePathAttr.nameNotExt += '_cp_' + attach.FileId; // 另一个
var newPath = filePathAttr.getFullPath();
console.log('复制文件');
console.log(attach);
// 复制之
// try {
Common.copyFile(attach.Path, newPath, function(ret) {
if(ret) {
attach.FileId = Common.objectId();
attach.IsDirty = true;
attach.Path = newPath;
delete attach['ServerFileId'];
newAttachs.push(attach);
}
cb();
});
/*
} catch(e) {
cb();
}
*/
}, function() {
note.Attachs = newAttachs;
console.log('conflict 复制后的');
console.log(note.Attachs);
Notes.insert(note, function(err, newNote) {
if(err) {
callback(false);
} else {
callback(newNote);
}
});
});
});
},
// 处理冲突
// notes是服务器的数据, 与本地的有冲突
// 1) 将本地的note复制一份
// 2) 服务器替换之前
fixConflicts: function(noteSyncInfo, noteWeb, callback) {
var me = this;
// 处理冲突
if(!noteWeb) {
return callback && callback();
}
var conflictNotes = noteSyncInfo.conflicts;
log('fix note conflicts');
log(conflictNotes);
// 这里为什么要同步? 因为fixConflicts后要进行send changes, 这些有冲突的不能发送changes
if(conflictNotes) {
async.eachSeries(conflictNotes, function(note, cb) { // note是服务器上最新的, note.NoteId, ServerNoteId已转换
var noteId = note.NoteId; // 本地noteId
// 复制一份, 本地的复制一份, 然后服务器上的替换本地的
me.copyNoteForConfict(noteId, function(newNote) {
if(newNote) {
// 更新之前的
me.updateNoteForceForConflict(note, function(note2) {
if(note2) {
// 前端来处理, 全量sync时不用前端一个个处理
noteWeb && noteWeb.fixSyncConflict(note2, newNote);
}
cb();
});
} else {
cb();
}
});
}, function() {
// 最后调用
callback && callback();
});
} else {
callback && callback();
}
// 发送改变的冲突
// 复制一份
// [待测]
var changeConflicts = noteSyncInfo.changeConflicts;
console.log('changeConflicts');
console.log(changeConflicts);
for(var i in changeConflicts) {
var note = changeConflicts[i]; // note是本地的note
// 复制一份
me.copyNoteForConfict(note.NoteId, function(newNote) {
if(newNote) {
// 更新之前的, 要先从服务器上得到服务版的
// 这里的note是本地的, 所以将服务器上的覆盖它
if(!Api) {
Api = require('api');
}
Api.getNote(note.ServerNoteId, function(serverNote) {
serverNote.ServerNoteId = serverNote.NoteId;
serverNote.NoteId = note.NoteId;
me.updateNoteForceForConflict(serverNote, function(note2) {
if(!note2) {
// 前端来处理, 全量sync时不用前端一个个处理
noteWeb && noteWeb.fixSyncConflict(note2, newNote);
}
});
});
} else {
}
});
}
// 服务器没有, 但是是发送更新的, 所以需要作为添加以后再send changes
if(noteSyncInfo.changeNeedAdds) {
var needAddNotes = noteSyncInfo.changeNeedAdds;
for(var i in needAddNotes) {
log('need add ');
var note = needAddNotes[i];
me.setIsNew(note.NoteId);
}
}
// 处理添加的
var addNotes = noteSyncInfo.adds;
log('has add...');
log(addNotes);
noteWeb.addSync(addNotes);
log('has updates...');
log(noteSyncInfo);
log(noteSyncInfo.updates);
// 处理更新的
noteWeb.updateSync(noteSyncInfo.updates);
// 处理删除的
noteWeb.deleteSync(noteSyncInfo.deletes);
},
// 得到所有文件要传的基本信息和传送的数据
getFilesPostInfo: function(files, callback) {
var needPostFilesAttr = [];
var needTransferFiles = {};
if(!files || files.length == 0) {
return callback(needPostFilesAttr, needTransferFiles);
}
async.eachSeries(files, function(file, cb) {
// var file = files[i];
var needFile = {
FileId: file.ServerFileId,
LocalFileId: file.FileId,
Type: file.Type,
HasBody: false,
IsAttach: file.IsAttach,
};
// console.log(file);
// 要传数据的
if(file.IsDirty) {
fs.exists(file.Path, function(isExists) {
needTransferFiles[file.FileId] = {
file: file.Path,
content_type: 'application/' + file.Type // TODO
}
if(file.Title) {
needTransferFiles[file.FileId].filename = file.Title;
}
needFile.HasBody = true;
needPostFilesAttr.push(needFile);
return cb();
});
} else {
needPostFilesAttr.push(needFile);
return cb();
}
}, function() {
callback(needPostFilesAttr, needTransferFiles);
});
},
// 获得用户修改的笔记
getDirtyNotes: function(callback) {
var me = this;
Notes.find({UserId: User.getCurActiveUserId(), IsDirty: true}, function(err, notes) {
if(err) {
log(err);
return callback && callback(false);
} else {
// 每一个笔记得到图片, 附件信息和数据
async.eachSeries(notes, function(note, cb) {
me.getNoteFiles(note, function(files) {
note.Content = me.fixNoteContentForSend(note.Content);
// note.Files = files || [];
me.getFilesPostInfo(files, function(attrs, fileDatas) {
note.Files = attrs;
note.FileDatas = fileDatas;
cb();
});
});
}, function() {
callback(notes);
});
}
});
},
// 得到笔记的文件
getNoteFiles: function(note, callback) {
var noteId = note.NoteId;
var content = note.Content;
// 1. 先得到附件
var attachs = note.Attachs || [];
for(var i in attachs) {
var attach = attachs[i];
attach.IsAttach = true;
}
// 1. 先得到图片
// 得到图片信息, 通过内容
// http://localhost:8002/api/file/getImage?fileId=xxxxxx, 得到fileId, 查询数据库, 得到图片
// console.log(content);
// console.log(Evt.localUrl + '/api/file/getImage?fileId=([0-9a-zA-Z]{24})');
var reg = new RegExp(Evt.localUrl + "/api/file/getImage\\?fileId=([0-9a-zA-Z]{24})", 'g');
var fileIds = [];
// var fileIdsMap = {}; // 防止多个
while((result = reg.exec(content)) != null) {
// result = [所有, 子表达式1, 子表达式2]
if(result && result.length > 1) {
// console.log(result);
var fileId = result[1];
fileIds.push(fileId);
}
}
var files = []; // {localFileId: "must", fileId: "", hasBody: true, filename: "xx.png"}
if(fileIds.length > 0) {
// 得到所有的图片
File.getAllImages(fileIds, function(images) {
// attach与图片结合
if(images) {
attachs = attachs.concat(images);
}
callback(attachs);
});
} else {
callback(attachs);
}
},
// 在send delete笔记时成功
setNotDirty: function(noteId) {
Notes.update({NoteId: noteId}, {$set:{IsDirty: false}})
},
// 在send delete笔记时有冲突, 设为不删除
setNotDirtyNotDelete: function(noteId) {
Notes.update({NoteId: noteId}, {$set:{IsDirty: false, LocalIsDelete: false}})
},
setIsNew: function(noteId) {
Notes.update({NoteId: noteId}, {$set:{LocalIsNew: true, IsDirty: true}})
},
//----------------------------------
// Attach
// 有部分操作放在File中了,
// 也有attach表, 但只作添加/删除附件用
//
// 更新笔记的附件
// web只要一个添加了, 删除的, 全部更新
updateAttach: function(noteId, attachs) {
var me = this;
console.log('updateAttach');
console.log(attachs);
Notes.update({NoteId: noteId}, {$set: {Attachs: attachs, IsDirty: true}});
// File, 删除修改了的
File.deleteNotExistsAttach(noteId, attachs);
},
};
module.exports = Note;