mirror of
https://github.com/leanote/desktop-app.git
synced 2025-10-19 01:54:02 +00:00
export leanote
This commit is contained in:
502
public/plugins/export_leanote/plugin.js
Normal file
502
public/plugins/export_leanote/plugin.js
Normal file
@@ -0,0 +1,502 @@
|
||||
/**
|
||||
* 导出Leanote插件
|
||||
* @author life life@leanote.com
|
||||
* 导出的格式为json, 后缀名为 .leanote
|
||||
|
||||
{
|
||||
exportDate: '2015-10-12 12:00:00',
|
||||
app: 'leanote.desktop.app.mac',
|
||||
appVersion: '1.0',
|
||||
notes: [
|
||||
{
|
||||
title: 'life',
|
||||
content: 'laldfadf', // 图片, 附件链接为 leanote://api/file/getImage?fileId=xxxx, leanote://api/file/getAttach?fileId=3232323
|
||||
tags: [1,2,3],
|
||||
isMarkdown: true,
|
||||
author: 'leanote', // 作者, 没用
|
||||
createdTime: '2015-10-12 12:00:00',
|
||||
updatedTime: '2015-10-12 12:00:00',
|
||||
files: {
|
||||
// fileId =>
|
||||
'32323': {base64: '', md5: '', type: 'png', 'isAttach': false, createdTime: '2031-12-31 12:12:32'}
|
||||
'32323': {base64: '', md5: '', type: 'png', 'isAttach': false, createdTime: '2031-12-31 12:12:32'}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
*
|
||||
*/
|
||||
define(function() {
|
||||
var async = require('async');
|
||||
|
||||
//===========
|
||||
// start
|
||||
|
||||
var exportLeanote = {
|
||||
langs: {
|
||||
'en-us': {
|
||||
'export': 'Export Leanote',
|
||||
'Exporting': 'Exporting',
|
||||
'Exporting: ': 'Exporting: ',
|
||||
'exportSuccess': 'Leanote saved successful!',
|
||||
'exportFailure': 'Leanote saved failure!',
|
||||
'notExists': 'Please sync your note to ther server firslty.'
|
||||
},
|
||||
'zh-cn': {
|
||||
'export': '导出Leanote',
|
||||
'Exporting': '正在导出',
|
||||
'Exporting: ': '正在导出: ',
|
||||
'exportSuccess': 'Leanote导出成功!',
|
||||
'exportFailure': 'Leanote导出失败!'
|
||||
},
|
||||
'zh-hk': {
|
||||
'export': '導出Leanote',
|
||||
'Exporting': '正在導出',
|
||||
'Exporting: ': '正在導出: ',
|
||||
'exportSuccess': 'Leanote導出成功!',
|
||||
'exportFailure': 'Leanote導出失敗!'
|
||||
}
|
||||
},
|
||||
|
||||
_inited: false,
|
||||
init: function() {
|
||||
var me = this;
|
||||
me._inited = true;
|
||||
},
|
||||
|
||||
replaceAll: function(src, pattern, to) {
|
||||
if(!src) {
|
||||
return src;
|
||||
}
|
||||
while(true) {
|
||||
var oldSrc = src;
|
||||
src = src.replace(pattern, to);
|
||||
if(oldSrc === src) {
|
||||
return src;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
fixFilename: function(filename) {
|
||||
var reg = new RegExp("/|#|\\$|!|\\^|\\*|'| |\"|%|&|\\(|\\)|\\+|\\,|/|:|;|<|>|=|\\?|@|\\||\\\\", 'g');
|
||||
filename = filename.replace(reg, "-");
|
||||
// 防止出现两个连续的-
|
||||
while(filename.indexOf('--') != -1) {
|
||||
filename = this.replaceAll(filename, '--', '-');
|
||||
}
|
||||
// 最后一个-
|
||||
filename = filename.replace(/\-$/, '');
|
||||
return filename;
|
||||
},
|
||||
|
||||
getLeanoteTime: function(t) {
|
||||
// 20151026T033928Z
|
||||
// 2015 10 26 T 03 39 28 Z
|
||||
if (!t) {
|
||||
t = new Date();
|
||||
}
|
||||
return t.format("yyyy-MM-dd hh:mm:ss");
|
||||
},
|
||||
|
||||
render: function(note, callback) {
|
||||
var me = this;
|
||||
var appVersion = Api.getCurVersion() || {version: 'unknown'};
|
||||
var info = {
|
||||
exportDate: me.getLeanoteTime(),
|
||||
app: 'leanote.desktop.app.' + process.platform,
|
||||
appVersion: appVersion.version,
|
||||
notes: []
|
||||
}
|
||||
me.fixFiles(note, function (content, files) {
|
||||
content = $('<div>' + content + '</div>').html();
|
||||
var noteInfo = {
|
||||
title: note.Title,
|
||||
content: content,
|
||||
tags: note.Tags,
|
||||
author: Api.userService.email || Api.userService.username || '',
|
||||
isMarkdown: note.IsMarkdown,
|
||||
createdTime: me.getLeanoteTime(note.CreatedTime),
|
||||
updatedTime: me.getLeanoteTime(note.UpdatedTime),
|
||||
files: files || []
|
||||
};
|
||||
info.notes.push(noteInfo);
|
||||
callback(JSON.stringify(info, null, 2));
|
||||
/*
|
||||
enml.ENMLOfHTML(content, function(err, ENML) {
|
||||
if (err) {
|
||||
info.content = content;
|
||||
}
|
||||
else {
|
||||
info.content = ENML;
|
||||
}
|
||||
|
||||
if (note.IsMarkdown) {
|
||||
info.content = '<pre>' + info.content + '</pre>';
|
||||
}
|
||||
|
||||
callback(me.renderTpl(tpl, info, keys));
|
||||
});
|
||||
*/
|
||||
});
|
||||
},
|
||||
|
||||
findAllImages: function (content) {
|
||||
var reg = new RegExp('<img([^>]*?)src=["\']?' + Api.evtService.localUrl + '/api/file/getImage\\?fileId=([0-9a-zA-Z]{24})["\']?(.*?)>', 'g');
|
||||
var matches = reg.exec(content);
|
||||
// 先找到所有的
|
||||
var allMatchs = [];
|
||||
while(matches) {
|
||||
var all = matches[0];
|
||||
var pre = matches[1]; // img与src之间
|
||||
var fileId = matches[2];
|
||||
var back = matches[3]; // src与>之间
|
||||
allMatchs.push({
|
||||
fileId: fileId,
|
||||
pre: pre,
|
||||
back: back,
|
||||
all: all
|
||||
});
|
||||
// 下一个
|
||||
matches = reg.exec(content);
|
||||
}
|
||||
|
||||
return allMatchs;
|
||||
},
|
||||
|
||||
findAllAttachs: function (content) {
|
||||
var reg = new RegExp('<a([^>]*?)href=["\']?' + Api.evtService.localUrl + '/api/file/getAttach\\?fileId=([0-9a-zA-Z]{24})["\']?(.*?)>([^<]*)</a>', 'g');
|
||||
var matches = reg.exec(content);
|
||||
|
||||
// 先找到所有的
|
||||
var allMatchs = [];
|
||||
while(matches) {
|
||||
var all = matches[0];
|
||||
var pre = matches[1]; // a 与href之间
|
||||
var fileId = matches[2];
|
||||
var back = matches[3] // href与>之间
|
||||
var title = matches[4];
|
||||
|
||||
allMatchs.push({
|
||||
fileId: fileId,
|
||||
title: title,
|
||||
pre: pre,
|
||||
back: back,
|
||||
isAttach: true,
|
||||
all: all
|
||||
});
|
||||
// 下一个
|
||||
matches = reg.exec(content);
|
||||
}
|
||||
return allMatchs;
|
||||
},
|
||||
|
||||
fixFiles: function (note, callback) {
|
||||
var me = this;
|
||||
|
||||
var content = note.Content;
|
||||
|
||||
var allImages = me.findAllImages(content) || [];
|
||||
var allAttachs = me.findAllAttachs(content) || [];
|
||||
|
||||
var allMatchs = allImages.concat(allAttachs);
|
||||
|
||||
if (allMatchs.length == 0) {
|
||||
callback(content, []);
|
||||
return;
|
||||
}
|
||||
|
||||
var files = {}; // fileId => {}
|
||||
|
||||
function replaceContent () {
|
||||
for (var i = 0; i < allMatchs.length; ++i) {
|
||||
var eachMatch = allMatchs[i];
|
||||
var fileInfo = files[eachMatch.fileId];
|
||||
|
||||
var link;
|
||||
if (!fileInfo) {
|
||||
link = '';
|
||||
}
|
||||
else {
|
||||
if (!eachMatch.isAttach) {
|
||||
var href = 'leanote://api/file/getImage?fileId=' + eachMatch.fileId;
|
||||
link = '<img ' + eachMatch.pre + 'src="' + href + '"' + eachMatch.back + '>';
|
||||
}
|
||||
else {
|
||||
var href = 'leanote://api/file/getAttach?fileId=' + eachMatch.fileId;
|
||||
link = '<a ' + eachMatch.pre + 'href="' + href + '"' + eachMatch.back + '>' + eachMatch.title + '</a>';
|
||||
}
|
||||
}
|
||||
|
||||
content = content.replace(eachMatch.all, link);
|
||||
}
|
||||
}
|
||||
|
||||
// 附件
|
||||
var attachs = note.Attachs || [];
|
||||
for (var i = 0; i < attachs.length; ++i) {
|
||||
var attach = attachs[i];
|
||||
var base64AndMd5 = Api.fileService.getFileBase64AndMd5(attach.Path);
|
||||
if (base64AndMd5) {
|
||||
files[attach.FileId] = {
|
||||
base64: base64AndMd5.base64,
|
||||
md5: base64AndMd5.md5,
|
||||
type: attach.Type,
|
||||
title: attach.Title,
|
||||
createdTime: me.getLeanoteTime(attach.UpdatedTime || attach.CreatedTime),
|
||||
isAttach: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 得到图片资源
|
||||
var fileIdFixed = {};
|
||||
async.eachSeries(allImages, function(eachMatch, cb) {
|
||||
var fileId = eachMatch.fileId;
|
||||
if (fileIdFixed[fileId]) {
|
||||
cb();
|
||||
return;
|
||||
}
|
||||
|
||||
Api.fileService.getImageInfo(fileId, function(err, doc) {
|
||||
fileIdFixed[fileId] = true;
|
||||
if(doc) {
|
||||
var base64AndMd5 = Api.fileService.getFileBase64AndMd5(doc.Path);
|
||||
if (base64AndMd5) {
|
||||
files[doc.FileId] = {
|
||||
base64: base64AndMd5.base64,
|
||||
md5: base64AndMd5.md5,
|
||||
type: doc.Type,
|
||||
title: doc.Title,
|
||||
createdTime: me.getLeanoteTime(doc.UpdatedTime || doc.CreatedTime),
|
||||
}
|
||||
}
|
||||
cb();
|
||||
}
|
||||
else {
|
||||
cb();
|
||||
}
|
||||
});
|
||||
|
||||
}, function () {
|
||||
replaceContent();
|
||||
callback(content, files);
|
||||
});
|
||||
},
|
||||
|
||||
//--------------
|
||||
|
||||
// 得到可用的文件名, 避免冲突
|
||||
getExportedFilePath: function(pathInfo, n, cb) {
|
||||
var me = this;
|
||||
if(n > 1) {
|
||||
pathInfo.nameNotExt = pathInfo.nameNotExtRaw + '-' + n;
|
||||
}
|
||||
var absPath = pathInfo.getFullPath();
|
||||
|
||||
// Api.nodeFs.existsSync(absPath) 总是返回false, 不知道什么原因
|
||||
// 在控制台上是可以的
|
||||
Api.nodeFs.exists(absPath, function(exists) {
|
||||
if(!exists) {
|
||||
cb(absPath);
|
||||
}
|
||||
else {
|
||||
me.getExportedFilePath(pathInfo, n+1, cb);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
getTargetPath: function(callback) {
|
||||
// showSaveDialog 不支持property选择文件夹
|
||||
Api.gui.dialog.showOpenDialog(Api.gui.getCurrentWindow(),
|
||||
{
|
||||
defaultPath: Api.gui.app.getPath('userDesktop') + '/',
|
||||
properties: ['openDirectory']
|
||||
},
|
||||
function(targetPath) {
|
||||
callback(targetPath);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
loadingIsClosed: false,
|
||||
|
||||
exportLeanoteForNotebook: function (notebookId) {
|
||||
var me = this;
|
||||
if (!notebookId) {
|
||||
return;
|
||||
}
|
||||
me.getTargetPath(function(targetPath) {
|
||||
if (!targetPath) {
|
||||
return;
|
||||
}
|
||||
|
||||
me.loadingIsClosed = false;
|
||||
Api.loading.show(Api.getMsg('plugin.export_leanote.Exporting'),
|
||||
{
|
||||
hasProgress: true,
|
||||
isLarge: true,
|
||||
onClose: function () {
|
||||
me.loadingIsClosed = true;
|
||||
setTimeout(function() {
|
||||
me.hideLoading();
|
||||
});
|
||||
}});
|
||||
Api.loading.setProgress(1);
|
||||
|
||||
Api.noteService.getNotes(notebookId, function(notes) {
|
||||
if (!notes) {
|
||||
me.hideLoading();
|
||||
return;
|
||||
}
|
||||
|
||||
var total = notes.length;
|
||||
var i = 0;
|
||||
async.eachSeries(notes, function(note, cb) {
|
||||
if (me.loadingIsClosed) {
|
||||
cb();
|
||||
me.hideLoading();
|
||||
return;
|
||||
}
|
||||
i++;
|
||||
Api.loading.setProgress(100 * i / total);
|
||||
me._exportLeanote(note, targetPath, function() {
|
||||
cb();
|
||||
}, i, total);
|
||||
}, function() {
|
||||
me.hideLoading();
|
||||
Notify.show({title: 'Info', body: getMsg('plugin.export_leanote.exportSuccess')});
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
hideLoading: function () {
|
||||
setTimeout(function () {
|
||||
Api.loading.hide();
|
||||
}, 1000);
|
||||
},
|
||||
|
||||
exportLeanote: function (noteIds) {
|
||||
var me = this;
|
||||
if (!noteIds || noteIds.length == 0) {
|
||||
return;
|
||||
}
|
||||
me.getTargetPath(function(targetPath) {
|
||||
if (!targetPath) {
|
||||
return;
|
||||
}
|
||||
|
||||
me.loadingIsClosed = false;
|
||||
Api.loading.show(Api.getMsg('plugin.export_leanote.Exporting'),
|
||||
{
|
||||
hasProgress: true,
|
||||
isLarge: true,
|
||||
onClose: function () {
|
||||
me.loadingIsClosed = true;
|
||||
setTimeout(function() {
|
||||
me.hideLoading();
|
||||
});
|
||||
}});
|
||||
Api.loading.setProgress(1);
|
||||
|
||||
var i = 0;
|
||||
var total = noteIds.length;
|
||||
|
||||
async.eachSeries(noteIds, function(noteId, cb) {
|
||||
if (me.loadingIsClosed) {
|
||||
cb();
|
||||
return;
|
||||
}
|
||||
|
||||
i++;
|
||||
Api.loading.setProgress(100 * i / total);
|
||||
Api.noteService.getNote(noteId, function(note) {
|
||||
me._exportLeanote(note, targetPath, function() {
|
||||
cb();
|
||||
}, i, total);
|
||||
});
|
||||
|
||||
}, function () {
|
||||
me.hideLoading();
|
||||
Notify.show({title: 'Info', body: getMsg('plugin.export_leanote.exportSuccess')});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_exportLeanote: function(note, path, callback, i, total) {
|
||||
var me = this;
|
||||
if(!note) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (me.loadingIsClosed) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
Api.loading.setMsg(Api.getMsg('plugin.export_leanote.Exporting: ') + (note.Title || getMsg('Untitled')));
|
||||
Api.loading.setProgressRate(i + '/' + total);
|
||||
}, 100);
|
||||
|
||||
var name = note.Title ? note.Title + '.leanote' : getMsg('Untitled') + '.leanote';
|
||||
name = me.fixFilename(name);
|
||||
|
||||
var targetPath = path + '/' + name;
|
||||
|
||||
// 将路径和名字区分开
|
||||
var pathInfo = Api.commonService.splitFile(targetPath);
|
||||
pathInfo.nameNotExt = me.fixFilename(pathInfo.nameNotExt); // 重新修正一次
|
||||
var nameNotExt = pathInfo.nameNotExt;
|
||||
pathInfo.nameNotExtRaw = pathInfo.nameNotExt;
|
||||
|
||||
// 得到可用文件的绝对路径
|
||||
me.getExportedFilePath(pathInfo, 1, function(absLeanoteFilePath) {
|
||||
me.render(note, function (content) {
|
||||
Api.commonService.writeFile(absLeanoteFilePath, content);
|
||||
callback();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// 打开前要执行的
|
||||
onOpen: function() {
|
||||
var me = this;
|
||||
var gui = Api.gui;
|
||||
|
||||
var menu = {
|
||||
label: Api.getMsg('plugin.export_leanote.export'),
|
||||
enabled: function(noteIds) {
|
||||
return true;
|
||||
},
|
||||
click: (function() {
|
||||
return function(noteIds) {
|
||||
me.exportLeanote(noteIds);
|
||||
}
|
||||
})()
|
||||
};
|
||||
Api.addExportMenu(menu);
|
||||
|
||||
Api.addExportMenuForNotebook({
|
||||
label: Api.getMsg('plugin.export_leanote.export'),
|
||||
enabled: function(notebookId) {
|
||||
return true;
|
||||
},
|
||||
click: (function() {
|
||||
return function(notebookId) {
|
||||
me.exportLeanoteForNotebook(notebookId);
|
||||
}
|
||||
})()
|
||||
});
|
||||
},
|
||||
// 打开后
|
||||
onOpenAfter: function() {
|
||||
},
|
||||
// 关闭时需要运行的
|
||||
onClose: function() {
|
||||
}
|
||||
};
|
||||
|
||||
return exportLeanote;
|
||||
});
|
Reference in New Issue
Block a user