2014-04-23 14:19:29 +00:00
local InputContainer = require ( " ui/widget/container/inputcontainer " )
local LoginDialog = require ( " ui/widget/logindialog " )
local InfoMessage = require ( " ui/widget/infomessage " )
2016-06-26 00:53:08 +00:00
local NetworkMgr = require ( " ui/network/manager " )
2015-10-03 06:18:47 +00:00
local DataStorage = require ( " datastorage " )
local DocSettings = require ( " docsettings " )
2014-04-23 14:19:29 +00:00
local UIManager = require ( " ui/uimanager " )
2017-02-27 07:41:06 +00:00
local ConfirmBox = require ( " ui/widget/confirmbox " )
2014-10-30 18:42:18 +00:00
local Screen = require ( " device " ) . screen
2016-07-05 14:15:52 +00:00
local util = require ( " ffi/util " )
local Device = require ( " device " )
2014-04-23 14:19:29 +00:00
local DEBUG = require ( " dbg " )
2014-11-28 21:38:54 +00:00
local T = require ( " ffi/util " ) . template
2014-04-23 14:19:29 +00:00
local _ = require ( " gettext " )
2019-08-24 21:06:06 +00:00
local N_ = _.ngettext
2014-04-23 14:19:29 +00:00
local slt2 = require ( ' slt2 ' )
local MyClipping = require ( " clip " )
2017-01-21 09:32:42 +00:00
local realpath = require ( " ffi/util " ) . realpath
2014-04-23 14:19:29 +00:00
local EvernoteExporter = InputContainer : new {
2014-10-09 04:26:52 +00:00
name = " evernote " ,
2014-04-23 14:19:29 +00:00
login_title = _ ( " Login to Evernote " ) ,
2016-04-15 13:04:41 +00:00
notebook_name = _ ( " KOReader Notes " ) ,
2014-05-01 10:38:43 +00:00
evernote_domain = nil ,
2014-10-16 10:25:02 +00:00
notemarks = _ ( " Note: " ) ,
2015-10-03 06:18:47 +00:00
clipping_dir = DataStorage : getDataDir ( ) .. " /clipboard " ,
2014-04-23 14:19:29 +00:00
2016-11-01 07:31:24 +00:00
evernote_token = nil ,
notebook_guid = nil ,
2014-04-23 14:19:29 +00:00
}
function EvernoteExporter : init ( )
2017-02-27 07:41:06 +00:00
self.text_clipping_file = self.clipping_dir .. " /KOReaderClipping.txt "
2014-04-23 14:19:29 +00:00
local settings = G_reader_settings : readSetting ( " evernote " ) or { }
2014-05-01 10:38:43 +00:00
self.evernote_domain = settings.domain
2014-04-23 14:19:29 +00:00
self.evernote_username = settings.username or " "
self.evernote_token = settings.token
self.notebook_guid = settings.notebook
2014-10-16 10:25:02 +00:00
self.html_export = settings.html_export or false
2017-01-21 09:32:42 +00:00
if self.html_export then
self.txt_export = false
else
self.txt_export = settings.txt_export or false
end
2014-04-23 14:19:29 +00:00
self.parser = MyClipping : new {
my_clippings = " /mnt/us/documents/My Clippings.txt " ,
history_dir = " ./history " ,
}
self.template = slt2.loadfile ( self.path .. " /note.tpl " )
2016-07-05 14:15:52 +00:00
self : migrateClippings ( )
self.config = DocSettings : open ( util.joinPath ( self.clipping_dir , " evernote.sdr " ) )
2017-02-27 07:41:06 +00:00
self.ui . menu : registerToMainMenu ( self )
2016-07-05 14:15:52 +00:00
end
2017-01-09 08:32:10 +00:00
function EvernoteExporter : isDocless ( )
return self.ui == nil or self.ui . document == nil or self.view == nil
end
function EvernoteExporter : readyToExport ( )
2017-01-21 09:32:42 +00:00
return self.evernote_token ~= nil or self.html_export ~= false or self.txt_export ~= false
2017-01-09 08:32:10 +00:00
end
2016-07-05 14:15:52 +00:00
function EvernoteExporter : migrateClippings ( )
local old_dir = util.joinPath ( util.realpath ( util.joinPath ( self.path , " .. " ) ) ,
" evernote.sdr " )
if lfs.attributes ( old_dir , " mode " ) == " directory " then
local mv_bin = Device : isAndroid ( ) and " /system/bin/mv " or " /bin/mv "
return util.execute ( mv_bin , old_dir , self.clipping_dir ) == 0
end
2014-04-23 14:19:29 +00:00
end
2017-03-04 13:46:38 +00:00
function EvernoteExporter : addToMainMenu ( menu_items )
menu_items.evernote = {
2014-08-09 14:15:44 +00:00
text = _ ( " Evernote " ) ,
2014-04-23 14:19:29 +00:00
sub_item_table = {
{
text_func = function ( )
2016-11-01 07:31:24 +00:00
local domain
2014-05-15 09:53:56 +00:00
if self.evernote_domain == " sandbox " then
2014-08-09 11:56:49 +00:00
domain = " Sandbox "
2014-05-15 09:53:56 +00:00
elseif self.evernote_domain == " yinxiang " then
2014-08-09 11:56:49 +00:00
domain = " Yinxiang "
2014-05-15 09:53:56 +00:00
else
2014-08-09 11:56:49 +00:00
domain = " Evernote "
2014-05-15 09:53:56 +00:00
end
2014-05-01 10:38:43 +00:00
return self.evernote_token and ( _ ( " Logout " ) .. " " .. domain )
or _ ( " Login " )
end ,
callback_func = function ( )
return self.evernote_token and function ( ) self : logout ( ) end
or nil
end ,
sub_item_table_func = function ( )
return not self.evernote_token and {
{
2014-07-17 13:06:01 +00:00
text = " Evernote " ,
2014-05-01 10:38:43 +00:00
callback = function ( )
self.evernote_domain = nil
self : login ( )
end
} ,
{
2014-07-17 13:06:01 +00:00
text = " 印象笔记 " ,
2014-05-01 10:38:43 +00:00
callback = function ( )
self.evernote_domain = " yinxiang "
self : login ( )
end
}
} or nil
2014-04-23 14:19:29 +00:00
end ,
} ,
{
text = _ ( " Export all notes in this book " ) ,
2014-05-01 10:38:43 +00:00
enabled_func = function ( )
2017-02-27 07:41:06 +00:00
return not self : isDocless ( ) and self : readyToExport ( ) and not self.txt_export
2014-05-01 10:38:43 +00:00
end ,
2014-04-23 14:19:29 +00:00
callback = function ( )
UIManager : scheduleIn ( 0.5 , function ( )
self : exportCurrentNotes ( self.view )
end )
UIManager : show ( InfoMessage : new {
2016-04-15 13:04:41 +00:00
text = _ ( " Exporting may take several seconds… " ) ,
2014-05-01 10:38:43 +00:00
timeout = 1 ,
2014-04-23 14:19:29 +00:00
} )
end
} ,
{
text = _ ( " Export all notes in your library " ) ,
2014-05-01 10:38:43 +00:00
enabled_func = function ( )
2017-01-09 08:32:10 +00:00
return self : readyToExport ( )
2014-05-01 10:38:43 +00:00
end ,
2014-04-23 14:19:29 +00:00
callback = function ( )
UIManager : scheduleIn ( 0.5 , function ( )
self : exportAllNotes ( )
end )
UIManager : show ( InfoMessage : new {
2016-04-15 13:04:41 +00:00
text = _ ( " Exporting may take several minutes… " ) ,
2014-05-01 10:38:43 +00:00
timeout = 1 ,
2014-04-23 14:19:29 +00:00
} )
end
} ,
2014-10-16 10:25:02 +00:00
{
text = _ ( " Export to local HTML files " ) ,
checked_func = function ( ) return self.html_export end ,
callback = function ( )
self.html_export = not self.html_export
2017-01-21 09:32:42 +00:00
if self.html_export then self.txt_export = false end
self : saveSettings ( )
end
} ,
{
text = _ ( " Export to local clipping text file " ) ,
checked_func = function ( ) return self.txt_export end ,
callback = function ( )
self.txt_export = not self.txt_export
if self.txt_export then self.html_export = false end
2017-01-16 13:50:36 +00:00
self : saveSettings ( )
2014-10-16 10:25:02 +00:00
end
} ,
2017-01-21 09:32:42 +00:00
{
text = _ ( " Purge history records " ) ,
callback = function ( )
self.config : purge ( )
2017-02-27 07:41:06 +00:00
UIManager : show ( ConfirmBox : new {
text = _ ( " History records have been purged. \n All notes will be exported again next time. \n Would you like to remove the existing KOReaderClipping.txt file to avoid duplication? \n Records will be appended to KOReaderClipping.txt instead of being overwritten. " ) ,
2019-08-24 07:25:38 +00:00
ok_text = _ ( " Remove file " ) ,
2017-02-27 07:41:06 +00:00
ok_callback = function ( )
os.remove ( self.text_clipping_file )
end ,
2019-08-24 07:25:38 +00:00
cancel_text = _ ( " Keep file " ) ,
2017-01-21 09:32:42 +00:00
} )
end
}
2014-04-23 14:19:29 +00:00
}
2017-02-28 21:46:32 +00:00
}
2014-04-23 14:19:29 +00:00
end
function EvernoteExporter : login ( )
2016-11-28 00:40:47 +00:00
if not NetworkMgr : isOnline ( ) then
2015-02-01 07:16:27 +00:00
NetworkMgr : promptWifiOn ( )
end
2014-04-23 14:19:29 +00:00
self.login_dialog = LoginDialog : new {
title = self.login_title ,
username = self.evernote_username or " " ,
buttons = {
{
{
text = _ ( " Cancel " ) ,
enabled = true ,
callback = function ( )
self : closeDialog ( )
end ,
} ,
{
text = _ ( " Login " ) ,
enabled = true ,
callback = function ( )
local username , password = self : getCredential ( )
self : closeDialog ( )
UIManager : scheduleIn ( 0.5 , function ( )
self : doLogin ( username , password )
end )
2014-05-01 10:38:43 +00:00
UIManager : show ( InfoMessage : new {
2016-04-15 13:04:41 +00:00
text = _ ( " Logging in. Please wait… " ) ,
2014-05-01 10:38:43 +00:00
timeout = 1 ,
} )
2014-04-23 14:19:29 +00:00
end ,
} ,
} ,
} ,
width = Screen : getWidth ( ) * 0.8 ,
height = Screen : getHeight ( ) * 0.4 ,
}
UIManager : show ( self.login_dialog )
2018-03-30 10:46:36 +00:00
self.login_dialog : onShowKeyboard ( )
2014-04-23 14:19:29 +00:00
end
function EvernoteExporter : closeDialog ( )
self.login_dialog : onClose ( )
UIManager : close ( self.login_dialog )
end
function EvernoteExporter : getCredential ( )
return self.login_dialog : getCredential ( )
end
function EvernoteExporter : doLogin ( username , password )
2014-05-01 10:38:43 +00:00
local EvernoteOAuth = require ( " EvernoteOAuth " )
local EvernoteClient = require ( " EvernoteClient " )
2014-04-23 14:19:29 +00:00
local oauth = EvernoteOAuth : new {
domain = self.evernote_domain ,
username = username ,
password = password ,
}
self.evernote_username = username
local ok , token = pcall ( oauth.getToken , oauth )
2014-07-17 13:00:45 +00:00
-- prompt users to turn on Wifi if network is unreachable
2015-02-01 07:16:27 +00:00
if not ok and token then
2014-04-23 14:19:29 +00:00
UIManager : show ( InfoMessage : new {
2014-11-19 16:33:45 +00:00
text = _ ( " An error occurred while logging in: " ) .. " \n " .. token ,
2014-04-23 14:19:29 +00:00
} )
return
end
local client = EvernoteClient : new {
domain = self.evernote_domain ,
authToken = token ,
}
2016-11-01 07:31:24 +00:00
local guid
ok , guid = pcall ( self.getExportNotebook , self , client )
2014-07-17 13:00:45 +00:00
if not ok and guid and guid : find ( " Transport not open " ) then
NetworkMgr : promptWifiOn ( )
return
elseif not ok and guid then
2014-04-23 14:19:29 +00:00
UIManager : show ( InfoMessage : new {
2014-11-19 16:33:45 +00:00
text = _ ( " An error occurred while logging in: " ) .. " \n " .. guid ,
2014-04-23 14:19:29 +00:00
} )
2014-07-17 13:00:45 +00:00
elseif ok and guid then
2014-04-23 14:19:29 +00:00
self.evernote_token = token
self.notebook_guid = guid
UIManager : show ( InfoMessage : new {
2016-04-16 10:21:49 +00:00
text = _ ( " Logged in to Evernote. " ) ,
2014-04-23 14:19:29 +00:00
} )
end
2017-01-16 13:50:36 +00:00
self : saveSettings ( )
2014-04-23 14:19:29 +00:00
end
function EvernoteExporter : logout ( )
self.evernote_token = nil
self.notebook_guid = nil
2014-05-01 10:38:43 +00:00
self.evernote_domain = nil
2017-01-16 13:50:36 +00:00
self : saveSettings ( )
2014-04-23 14:19:29 +00:00
end
2017-01-16 13:50:36 +00:00
function EvernoteExporter : saveSettings ( )
2014-04-23 14:19:29 +00:00
local settings = {
2014-05-01 10:38:43 +00:00
domain = self.evernote_domain ,
2014-04-23 14:19:29 +00:00
username = self.evernote_username ,
token = self.evernote_token ,
notebook = self.notebook_guid ,
2014-10-16 10:25:02 +00:00
html_export = self.html_export ,
2017-01-21 09:32:42 +00:00
txt_export = self.txt_export ,
2014-04-23 14:19:29 +00:00
}
G_reader_settings : saveSetting ( " evernote " , settings )
end
function EvernoteExporter : getExportNotebook ( client )
local name = self.notebook_name
return client : findNotebookByTitle ( name ) or client : createNotebook ( name ) . guid
end
function EvernoteExporter : exportCurrentNotes ( view )
local clippings = self.parser : parseCurrentDoc ( view )
2014-10-16 10:25:02 +00:00
self : exportClippings ( clippings )
2014-04-23 14:19:29 +00:00
end
2014-05-12 13:19:17 +00:00
function EvernoteExporter : updateHistoryClippings ( clippings , new_clippings )
-- update clippings from history clippings
2014-05-12 10:07:20 +00:00
for title , booknotes in pairs ( new_clippings ) do
for chapter_index , chapternotes in ipairs ( booknotes ) do
for note_index , note in ipairs ( chapternotes ) do
if clippings [ title ] == nil or clippings [ title ] [ chapter_index ] == nil
or clippings [ title ] [ chapter_index ] [ note_index ] == nil
or clippings [ title ] [ chapter_index ] [ note_index ] . page ~= note.page
or clippings [ title ] [ chapter_index ] [ note_index ] . time ~= note.time
or clippings [ title ] [ chapter_index ] [ note_index ] . text ~= note.text
or clippings [ title ] [ chapter_index ] [ note_index ] . note ~= note.note then
2014-05-12 13:19:17 +00:00
DEBUG ( " found new notes in history " , booknotes.title )
2014-05-12 10:07:20 +00:00
clippings [ title ] = booknotes
end
end
end
end
return clippings
end
2014-05-12 13:19:17 +00:00
function EvernoteExporter : updateMyClippings ( clippings , new_clippings )
-- only new titles or new notes in My clippings are updated to clippings
-- since appending is the only way to modify notes in My Clippings
for title , booknotes in pairs ( new_clippings ) do
if clippings [ title ] == nil or # clippings [ title ] < # booknotes then
DEBUG ( " found new notes in MyClipping " , booknotes.title )
clippings [ title ] = booknotes
end
end
return clippings
end
2014-04-23 14:19:29 +00:00
function EvernoteExporter : exportAllNotes ( )
2017-02-27 07:41:06 +00:00
-- Flush highlights of current document.
if not self : isDocless ( ) then
self.ui : saveSettings ( )
end
2014-05-12 10:07:20 +00:00
local clippings = self.config : readSetting ( " clippings " ) or { }
2014-05-12 13:19:17 +00:00
clippings = self : updateHistoryClippings ( clippings , self.parser : parseHistory ( ) )
clippings = self : updateMyClippings ( clippings , self.parser : parseMyClippings ( ) )
2014-04-23 14:19:29 +00:00
-- remove blank entries
for title , booknotes in pairs ( clippings ) do
-- chapter number is zero
if # booknotes == 0 then
clippings [ title ] = nil
end
end
--DEBUG("clippings", clippings)
2014-10-16 10:25:02 +00:00
self : exportClippings ( clippings )
2014-05-12 10:07:20 +00:00
self.config : saveSetting ( " clippings " , clippings )
self.config : flush ( )
2014-04-23 14:19:29 +00:00
end
2014-10-16 10:25:02 +00:00
function EvernoteExporter : exportClippings ( clippings )
local client = nil
2017-01-21 09:32:42 +00:00
local exported_stamp
if not self.html_export and not self.txt_export then
2014-10-16 10:25:02 +00:00
client = require ( " EvernoteClient " ) : new {
domain = self.evernote_domain ,
authToken = self.evernote_token ,
}
exported_stamp = self.notebook_guid
2017-01-21 09:32:42 +00:00
elseif self.html_export then
exported_stamp = " html "
elseif self.txt_export then
exported_stamp = " txt "
else
assert ( " an exported_stamp is expected for a new export type " )
2014-10-16 10:25:02 +00:00
end
2014-04-23 14:19:29 +00:00
local export_count , error_count = 0 , 0
local export_title , error_title
for title , booknotes in pairs ( clippings ) do
2014-05-15 09:27:05 +00:00
if type ( booknotes.exported ) ~= " table " then
booknotes.exported = { }
end
-- check if booknotes are exported in this notebook
-- so that booknotes will still be exported after switching user account
2014-10-16 10:25:02 +00:00
if booknotes.exported [ exported_stamp ] ~= true then
local ok , err
if self.html_export then
2017-01-21 09:32:42 +00:00
ok , err = pcall ( self.exportBooknotesToHTML , self , title , booknotes )
elseif self.txt_export then
ok , err = pcall ( self.exportBooknotesToTXT , self , title , booknotes )
2014-10-16 10:25:02 +00:00
else
2017-01-21 09:32:42 +00:00
ok , err = pcall ( self.exportBooknotesToEvernote , self , client , title , booknotes )
2014-10-16 10:25:02 +00:00
end
2014-05-12 10:07:20 +00:00
-- error reporting
2014-07-17 13:00:45 +00:00
if not ok and err and err : find ( " Transport not open " ) then
NetworkMgr : promptWifiOn ( )
return
elseif not ok and err then
2014-05-12 10:07:20 +00:00
DEBUG ( " Error occurs when exporting book: " , title , err )
error_count = error_count + 1
error_title = title
2014-07-17 13:00:45 +00:00
elseif ok then
2014-05-12 10:07:20 +00:00
DEBUG ( " Exported notes in book: " , title )
export_count = export_count + 1
export_title = title
2014-10-16 10:25:02 +00:00
booknotes.exported [ exported_stamp ] = true
2014-05-12 10:07:20 +00:00
end
2014-04-23 14:19:29 +00:00
end
end
2014-11-19 16:33:45 +00:00
local msg = " Nothing was exported. "
2014-04-23 14:19:29 +00:00
local all_count = export_count + error_count
if export_count > 0 and error_count == 0 then
if all_count == 1 then
2014-11-28 21:38:54 +00:00
msg = T (
2019-08-24 21:06:06 +00:00
N_ ( " Exported notes from the book: \n %1 " ,
" Exported notes from the book: \n %1 \n and %2 others. " ,
all_count - 1 ) ,
2014-11-28 21:38:54 +00:00
export_title ,
all_count - 1
)
2014-04-23 14:19:29 +00:00
end
elseif error_count > 0 then
if all_count == 1 then
2014-11-28 21:38:54 +00:00
msg = T (
2019-08-24 21:06:06 +00:00
N_ ( " An error occurred while trying to export notes from the book: \n %1 " ,
" Multiple errors occurred while trying to export notes from the book: \n %1 \n and %2 others. " ,
error_count - 1 ) ,
2014-11-28 21:38:54 +00:00
error_title ,
error_count - 1
)
2014-04-23 14:19:29 +00:00
end
end
2017-02-27 07:41:06 +00:00
if ( self.html_export or self.txt_export ) and export_count > 0 then
2017-01-21 09:32:42 +00:00
msg = msg .. T ( _ ( " \n Notes can be found in %1/. " ) , realpath ( self.clipping_dir ) )
end
2014-04-23 14:19:29 +00:00
UIManager : show ( InfoMessage : new { text = msg } )
end
2014-10-16 10:25:02 +00:00
function EvernoteExporter : exportBooknotesToEvernote ( client , title , booknotes )
2014-04-23 14:19:29 +00:00
local content = slt2.render ( self.template , {
booknotes = booknotes ,
2014-10-16 10:25:02 +00:00
notemarks = self.notemarks ,
2014-04-23 14:19:29 +00:00
} )
--DEBUG("content", content)
local note_guid = client : findNoteByTitle ( title , self.notebook_guid )
2014-05-15 08:10:45 +00:00
local resources = { }
for _ , chapter in ipairs ( booknotes ) do
for _ , clipping in ipairs ( chapter ) do
if clipping.image then
table.insert ( resources , {
image = clipping.image
} )
-- nullify clipping image after passing it to evernote client
clipping.image = nil
end
end
end
2014-04-23 14:19:29 +00:00
if not note_guid then
2014-05-15 08:10:45 +00:00
client : createNote ( title , content , resources , { } , self.notebook_guid )
2014-04-23 14:19:29 +00:00
else
2014-05-15 08:10:45 +00:00
client : updateNote ( note_guid , title , content , resources , { } , self.notebook_guid )
2014-04-23 14:19:29 +00:00
end
end
2014-10-16 10:25:02 +00:00
function EvernoteExporter : exportBooknotesToHTML ( title , booknotes )
local content = slt2.render ( self.template , {
booknotes = booknotes ,
notemarks = self.notemarks ,
} )
--DEBUG("content", content)
local html = io.open ( self.clipping_dir .. " / " .. title .. " .html " , " w " )
if html then
html : write ( content )
html : close ( )
end
end
2017-01-21 09:32:42 +00:00
function EvernoteExporter : exportBooknotesToTXT ( title , booknotes )
2017-02-27 07:41:06 +00:00
-- Use wide_space to avoid crengine to treat it specially.
local wide_space = " \227 \128 \128 "
local file_modification = lfs.attributes ( self.text_clipping_file , " modification " ) or 0
local file = io.open ( self.text_clipping_file , " a " )
2017-01-21 09:32:42 +00:00
if file then
2017-02-27 07:41:06 +00:00
file : write ( title .. " \n " .. wide_space .. " \n " )
2017-01-21 09:32:42 +00:00
for _ignore1 , chapter in ipairs ( booknotes ) do
if chapter.title then
2017-02-27 07:41:06 +00:00
file : write ( wide_space .. chapter.title .. " \n " .. wide_space .. " \n " )
2017-01-21 09:32:42 +00:00
end
for _ignore2 , clipping in ipairs ( chapter ) do
2017-02-27 07:41:06 +00:00
-- If this clipping has already been exported, we ignore it.
if clipping.time >= file_modification then
file : write ( wide_space .. wide_space ..
T ( _ ( " -- Page: %1, added on %2 \n " ) ,
clipping.page , os.date ( " %c " , clipping.time ) ) )
if clipping.text then
file : write ( clipping.text )
end
if clipping.image then
file : write ( _ ( " <An image> " ) )
end
file : write ( " \n -=-=-=-=-=- \n " )
2017-01-21 09:32:42 +00:00
end
end
end
file : write ( " \n " )
file : close ( )
end
end
2014-04-23 14:19:29 +00:00
return EvernoteExporter