[fix, plugin] Evernote exporter only writes one documents clippings when txt export used, rest is ignored (#5774)

fixes #3690
reviewable/pr5795/r1
Mustafa Ali Mutlu 4 years ago committed by Frans de Jonge
parent 23d848acf4
commit 2736661bfc

@ -6,7 +6,6 @@ local NetworkMgr = require("ui/network/manager")
local DataStorage = require("datastorage") local DataStorage = require("datastorage")
local DocSettings = require("docsettings") local DocSettings = require("docsettings")
local UIManager = require("ui/uimanager") local UIManager = require("ui/uimanager")
local ConfirmBox = require("ui/widget/confirmbox")
local Screen = require("device").screen local Screen = require("device").screen
local util = require("ffi/util") local util = require("ffi/util")
local Device = require("device") local Device = require("device")
@ -309,13 +308,9 @@ For more information, please visit https://github.com/koreader/koreader/wiki/Eve
text = _("Purge history records"), text = _("Purge history records"),
callback = function() callback = function()
self.config:purge() self.config:purge()
UIManager:show(ConfirmBox:new{ UIManager:show(InfoMessage:new{
text = _("History records have been purged.\nAll notes will be exported again next time.\nWould you like to remove the existing KOReaderClipping.txt file to avoid duplication?\nRecords will be appended to KOReaderClipping.txt instead of being overwritten."), text = _("History records have been purged.\nAll notes will be exported again next time.\n"),
ok_text = _("Remove file"), timeout = 2,
ok_callback = function()
os.remove(self.text_clipping_file)
end,
cancel_text = _("Keep file"),
}) })
end end
} }
@ -484,6 +479,23 @@ function EvernoteExporter:updateMyClippings(clippings, new_clippings)
return clippings return clippings
end end
--[[--
Parses highlights and calls exporter functions.
Entry point for exporting highlights. User interface calls this function.
Parses current document and documents from history, passes them to exportClippings().
Highlight: Highlighted text or image in document, stored in "highlights" table in
documents sidecar file. Parser uses this table. If highlight._._.text field is empty parser uses
highlight._._.pboxes field to get an image instead.
Bookmarks: Data in bookmark explorer. Stored in "bookmarks" table of documents sidecar file. Every
field in bookmarks._ has "text" and "notes" fields When user edits a highlight or "renames" bookmark,
text field is created or updated. Parser looks to bookmarks._.text field for edited notes. bookmarks._.notes isn't used for exporting operations.
https://github.com/koreader/koreader/blob/605f6026bbf37856ee54741b8a0697337ca50039/plugins/evernote.koplugin/clip.lua#L229
Clippings: Parsed form of highlights, stored in clipboard/evernote.sdr/metadata.sdr.lua
for all documents. Used only for exporting bookmarks. Internal highlight or bookmark functions
does not usew this table.
Booknotes: Every table in clippings table. clippings = {"title" = booknotes}
--]]
function EvernoteExporter:exportAllNotes() function EvernoteExporter:exportAllNotes()
-- Flush highlights of current document. -- Flush highlights of current document.
if not self:isDocless() then if not self:isDocless() then
@ -518,6 +530,7 @@ function EvernoteExporter:exportClippings(clippings)
elseif self.html_export then elseif self.html_export then
exported_stamp= "html" exported_stamp= "html"
elseif self.txt_export then elseif self.txt_export then
os.remove(self.text_clipping_file)
exported_stamp = "txt" exported_stamp = "txt"
elseif self.joplin_export then elseif self.joplin_export then
exported_stamp = "joplin" exported_stamp = "joplin"
@ -544,7 +557,8 @@ function EvernoteExporter:exportClippings(clippings)
end end
-- check if booknotes are exported in this notebook -- check if booknotes are exported in this notebook
-- so that booknotes will still be exported after switching user account -- so that booknotes will still be exported after switching user account
if booknotes.exported[exported_stamp] ~= true then --Don't respect exported_stamp on txt export since it isn't possible to delete(update) prior clippings.
if booknotes.exported[exported_stamp] ~= true or self.txt_export then
local ok, err local ok, err
if self.html_export then if self.html_export then
ok, err = pcall(self.exportBooknotesToHTML, self, title, booknotes) ok, err = pcall(self.exportBooknotesToHTML, self, title, booknotes)
@ -639,7 +653,6 @@ end
function EvernoteExporter:exportBooknotesToTXT(title, booknotes) function EvernoteExporter:exportBooknotesToTXT(title, booknotes)
-- Use wide_space to avoid crengine to treat it specially. -- Use wide_space to avoid crengine to treat it specially.
local wide_space = "\227\128\128" 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") local file = io.open(self.text_clipping_file, "a")
if file then if file then
file:write(title .. "\n" .. wide_space .. "\n") file:write(title .. "\n" .. wide_space .. "\n")
@ -648,19 +661,16 @@ function EvernoteExporter:exportBooknotesToTXT(title, booknotes)
file:write(wide_space .. chapter.title .. "\n" .. wide_space .. "\n") file:write(wide_space .. chapter.title .. "\n" .. wide_space .. "\n")
end end
for _ignore2, clipping in ipairs(chapter) do for _ignore2, clipping in ipairs(chapter) do
-- If this clipping has already been exported, we ignore it. file:write(wide_space .. wide_space ..
if clipping.time >= file_modification then T(_("-- Page: %1, added on %2\n"),
file:write(wide_space .. wide_space .. clipping.page, os.date("%c", clipping.time)))
T(_("-- Page: %1, added on %2\n"), if clipping.text then
clipping.page, os.date("%c", clipping.time))) file:write(clipping.text)
if clipping.text then end
file:write(clipping.text) if clipping.image then
end file:write(_("<An image>"))
if clipping.image then
file:write(_("<An image>"))
end
file:write("\n-=-=-=-=-=-\n")
end end
file:write("\n-=-=-=-=-=-\n")
end end
end end

@ -0,0 +1,102 @@
describe("Evernote plugin module", function()
local readerui, match
local sample_clippings, sample_epub
local DocumentRegistry
setup(function()
require("commonrequire")
match = require("luassert.match")
local ReaderUI = require("apps/reader/readerui")
DocumentRegistry = require("document/documentregistry")
sample_epub = "spec/front/unit/data/juliet.epub"
readerui = ReaderUI:new{
document = DocumentRegistry:openDocument(sample_epub),
}
sample_clippings = {
["Title1"] = {
[1] = {
[1] = {
["page"] = 6,
["time"] = 1578946897,
["sort"] = "highlight",
["text"] = "Some important stuff 1"
}
},
[2] = {
[1] = {
["page"] = 13,
["time"] = 1578946903,
["sort"] = "highlight",
["text"] = "Some important stuff 2"
}
},
["file"] = "path/to/title1",
["exported"] = {
["txt"] = true,
["html"] = true,
},
["title"] = "Title1"
},
["Title2"] = {
[1] = {
[1] = {
["page"] = 233,
["time"] = 1578946918,
["sort"] = "highlight",
["text"] = "Some important stuff 3"
}
},
[2] = {
[1] = {
["page"] = 237,
["time"] = 1578947501,
["sort"] = "highlight",
["text"] = "",
["image"] = {
["hash"] = "cb7b40a63afc89f0aa452f2b655877e6",
["png"] = "Binary Encoding of image"
},
}
},
["file"] = "path/to/title2",
["exported"] = {
},
["title"] = "Title2"
},
}
end)
it("should write clippings to txt file", function ()
local file_mock = mock( {
write = function() return end,
close = function() return end
})
local old_io = _G.io
_G.io = mock({
open = function(file, mode)
if file == readerui.evernote.text_clipping_file then
return file_mock
else
return old_io.open(file, mode)
end
end
})
readerui.evernote:exportBooknotesToTXT("Title1", sample_clippings.Title1)
assert.spy(io.open).was.called()
assert.spy(file_mock.write).was.called_with(match.is_ref(file_mock), "Some important stuff 1")
_G.io = old_io
end)
it("should not export booknotes with exported_stamp", function()
readerui.evernote.html_export = true
stub(readerui.evernote, "exportBooknotesToHTML")
readerui.evernote:exportClippings(sample_clippings)
assert.stub(readerui.evernote.exportBooknotesToHTML).was_called_with(match.is_truthy(), "Title2", match.is_truthy())
assert.stub(readerui.evernote.exportBooknotesToHTML).was_not_called_with(match.is_truthy(), "Title1", match.is_truthy())
end)
end)
Loading…
Cancel
Save