2019-08-23 17:53:53 +00:00
--[[--
@ module koplugin.wallabag
] ]
2020-01-04 00:18:51 +00:00
local BD = require ( " ui/bidi " )
2018-10-16 18:49:44 +00:00
local DataStorage = require ( " datastorage " )
2020-10-12 03:06:04 +00:00
local Dispatcher = require ( " dispatcher " )
2019-03-05 12:00:49 +00:00
local DocSettings = require ( " docsettings " )
2021-01-17 08:22:48 +00:00
local DocumentRegistry = require ( " document/documentregistry " )
2019-03-05 12:00:49 +00:00
local Event = require ( " ui/event " )
2018-10-16 18:49:44 +00:00
local FFIUtil = require ( " ffi/util " )
local FileManager = require ( " apps/filemanager/filemanager " )
local InfoMessage = require ( " ui/widget/infomessage " )
local InputDialog = require ( " ui/widget/inputdialog " )
local JSON = require ( " json " )
local LuaSettings = require ( " frontend/luasettings " )
2020-04-15 10:50:13 +00:00
local Math = require ( " optmath " )
2020-06-12 20:40:48 +00:00
local MultiConfirmBox = require ( " ui/widget/multiconfirmbox " )
2018-10-16 18:49:44 +00:00
local MultiInputDialog = require ( " ui/widget/multiinputdialog " )
local NetworkMgr = require ( " ui/network/manager " )
2020-04-15 10:50:13 +00:00
local ReadHistory = require ( " readhistory " )
2018-10-16 18:49:44 +00:00
local UIManager = require ( " ui/uimanager " )
local WidgetContainer = require ( " ui/widget/container/widgetcontainer " )
local filemanagerutil = require ( " apps/filemanager/filemanagerutil " )
local http = require ( " socket.http " )
2022-09-27 23:10:50 +00:00
local lfs = require ( " libs/libkoreader-lfs " )
2018-10-16 18:49:44 +00:00
local logger = require ( " logger " )
local ltn12 = require ( " ltn12 " )
local socket = require ( " socket " )
2021-03-15 00:25:10 +00:00
local socketutil = require ( " socketutil " )
2018-10-16 18:49:44 +00:00
local util = require ( " util " )
local _ = require ( " gettext " )
local T = FFIUtil.template
-- constants
2019-05-14 17:10:41 +00:00
local article_id_prefix = " [w-id_ "
2018-10-16 18:49:44 +00:00
local article_id_postfix = " ] "
2018-11-19 11:30:17 +00:00
local failed , skipped , downloaded = 1 , 2 , 3
2018-10-16 18:49:44 +00:00
Clarify our OOP semantics across the codebase (#9586)
Basically:
* Use `extend` for class definitions
* Use `new` for object instantiations
That includes some minor code cleanups along the way:
* Updated `Widget`'s docs to make the semantics clearer.
* Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283)
* Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass).
* Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events.
* Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier.
* Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references.
* ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak).
* Terminal: Make sure the shell is killed on plugin teardown.
* InputText: Fix Home/End/Del physical keys to behave sensibly.
* InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...).
* OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of.
* ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed!
* Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
local Wallabag = WidgetContainer : extend {
2018-10-16 18:49:44 +00:00
name = " wallabag " ,
}
2020-10-12 03:06:04 +00:00
function Wallabag : onDispatcherRegisterActions ( )
2021-09-10 20:11:24 +00:00
Dispatcher : registerAction ( " wallabag_download " , { category = " none " , event = " SynchronizeWallabag " , title = _ ( " Wallabag retrieval " ) , general = true , } )
2020-10-12 03:06:04 +00:00
end
2018-10-16 18:49:44 +00:00
function Wallabag : init ( )
self.token_expiry = 0
-- default values so that user doesn't have to explicitely set them
self.is_delete_finished = true
self.is_delete_read = false
self.is_auto_delete = false
self.is_sync_remote_delete = false
2022-04-19 15:39:47 +00:00
self.is_archiving_deleted = true
2022-01-10 20:38:45 +00:00
self.send_review_as_tags = false
2018-10-16 18:49:44 +00:00
self.filter_tag = " "
2019-10-13 06:48:37 +00:00
self.ignore_tags = " "
2022-04-18 00:15:01 +00:00
self.auto_tags = " "
2019-07-28 08:53:32 +00:00
self.articles_per_sync = 30
2018-10-16 18:49:44 +00:00
2020-10-12 03:06:04 +00:00
self : onDispatcherRegisterActions ( )
2018-10-16 18:49:44 +00:00
self.ui . menu : registerToMainMenu ( self )
self.wb_settings = self.readSettings ( )
self.server_url = self.wb_settings . data.wallabag . server_url
self.client_id = self.wb_settings . data.wallabag . client_id
self.client_secret = self.wb_settings . data.wallabag . client_secret
self.username = self.wb_settings . data.wallabag . username
self.password = self.wb_settings . data.wallabag . password
self.directory = self.wb_settings . data.wallabag . directory
2018-11-19 11:30:17 +00:00
if self.wb_settings . data.wallabag . is_delete_finished ~= nil then
self.is_delete_finished = self.wb_settings . data.wallabag . is_delete_finished
end
2022-01-10 20:38:45 +00:00
if self.wb_settings . data.wallabag . send_review_as_tags ~= nil then
self.send_review_as_tags = self.wb_settings . data.wallabag . send_review_as_tags
end
2018-11-19 11:30:17 +00:00
if self.wb_settings . data.wallabag . is_delete_read ~= nil then
self.is_delete_read = self.wb_settings . data.wallabag . is_delete_read
end
if self.wb_settings . data.wallabag . is_auto_delete ~= nil then
self.is_auto_delete = self.wb_settings . data.wallabag . is_auto_delete
end
if self.wb_settings . data.wallabag . is_sync_remote_delete ~= nil then
self.is_sync_remote_delete = self.wb_settings . data.wallabag . is_sync_remote_delete
end
2019-07-28 11:59:29 +00:00
if self.wb_settings . data.wallabag . is_archiving_deleted ~= nil then
self.is_archiving_deleted = self.wb_settings . data.wallabag . is_archiving_deleted
end
2018-11-19 11:30:17 +00:00
if self.wb_settings . data.wallabag . filter_tag then
self.filter_tag = self.wb_settings . data.wallabag . filter_tag
end
2019-10-13 06:48:37 +00:00
if self.wb_settings . data.wallabag . ignore_tags then
self.ignore_tags = self.wb_settings . data.wallabag . ignore_tags
end
2022-04-18 00:15:01 +00:00
if self.wb_settings . data.wallabag . auto_tags then
self.auto_tags = self.wb_settings . data.wallabag . auto_tags
end
2019-07-28 08:53:32 +00:00
if self.wb_settings . data.wallabag . articles_per_sync ~= nil then
self.articles_per_sync = self.wb_settings . data.wallabag . articles_per_sync
end
2020-04-15 10:50:13 +00:00
self.remove_finished_from_history = self.wb_settings . data.wallabag . remove_finished_from_history or false
2020-05-24 13:56:48 +00:00
self.download_queue = self.wb_settings . data.wallabag . download_queue or { }
2018-10-16 18:49:44 +00:00
-- workaround for dateparser only available if newsdownloader is active
self.is_dateparser_available = false
self.is_dateparser_checked = false
-- workaround for dateparser, only once
-- the parser is in newsdownloader.koplugin, check if it is available
if not self.is_dateparser_checked then
local res
res , self.dateparser = pcall ( require , " lib.dateparser " )
if res then self.is_dateparser_available = true end
self.is_dateparser_checked = true
end
2022-12-11 15:52:40 +00:00
if self.ui and self.ui . link then
self.ui . link : addToExternalLinkDialog ( " 25_wallabag " , function ( this , link_url )
return {
text = _ ( " Add to Wallabag " ) ,
callback = function ( )
UIManager : close ( this.external_link_dialog )
this.ui : handleEvent ( Event : new ( " AddWallabagArticle " , link_url ) )
end ,
}
end )
end
2018-10-16 18:49:44 +00:00
end
function Wallabag : addToMainMenu ( menu_items )
menu_items.wallabag = {
text = _ ( " Wallabag " ) ,
sub_item_table = {
{
text = _ ( " Retrieve new articles from server " ) ,
callback = function ( )
2019-03-05 12:00:49 +00:00
self.ui : handleEvent ( Event : new ( " SynchronizeWallabag " ) )
2018-10-16 18:49:44 +00:00
end ,
} ,
{
text = _ ( " Delete finished articles remotely " ) ,
callback = function ( )
Various Wi-Fi QoL improvements (#6424)
* Revamped most actions that require an internet connection to a new/fixed backend that allows forwarding the initial action and running it automatically once connected. (i.e., it'll allow you to set "Action when Wi-Fi is off" to "turn_on", and whatch stuff connect and do what you wanted automatically without having to re-click anywhere instead of showing you a Wi-Fi prompt and then not doing anything without any other feedback).
* Speaking of, fixed the "turn_on" beforeWifi action to, well, actually work. It's no longer marked as experimental.
* Consistently use "Wi-Fi" everywhere.
* On Kobo/Cervantes/Sony, implemented a "Kill Wi-Fi connection when inactive" system that will automatically disconnect from Wi-Fi after sustained *network* inactivity (i.e., you can keep reading, it'll eventually turn off on its own). This should be smart and flexible enough not to murder Wi-Fi while you need it, while still not keeping it uselessly on and murdering your battery.
(i.e., enable that + turn Wi-Fi on when off and enjoy never having to bother about Wi-Fi ever again).
* Made sending `NetworkConnected` / `NetworkDisconnected` events consistent (they were only being sent... sometimes, which made relying on 'em somewhat problematic).
* restoreWifiAsync is now only run when really needed (i.e., we no longer stomp on an existing working connection just for the hell of it).
* We no longer attempt to kill a bogus non-existent Wi-Fi connection when going to suspend, we only do it when it's actually needed.
* Every method of enabling Wi-Fi will now properly tear down Wi-Fi on failure, instead of leaving it in an undefined state.
* Fixed an issue in the fancy crash screen on Kobo/reMarkable that could sometime lead to the log excerpt being missing.
* Worked-around a number of sneaky issues related to low-level Wi-Fi/DHCP/DNS handling on Kobo (see the lengthy comments [below](https://github.com/koreader/koreader/pull/6424#issuecomment-663881059) for details). Fix #6421
Incidentally, this should also fix the inconsistencies experienced re: Wi-Fi behavior in Nickel when toggling between KOReader and Nickel (use NM/KFMon, and run a current FW for best results).
* For developers, this involves various cleanups around NetworkMgr and NetworkListener. Documentation is in-line, above the concerned functions.
2020-07-27 01:39:06 +00:00
local connect_callback = function ( )
local num_deleted = self : processLocalFiles ( " manual " )
UIManager : show ( InfoMessage : new {
text = T ( _ ( " Articles processed. \n Deleted: %1 " ) , num_deleted )
} )
self : refreshCurrentDirIfNeeded ( )
2018-10-16 18:49:44 +00:00
end
Various Wi-Fi QoL improvements (#6424)
* Revamped most actions that require an internet connection to a new/fixed backend that allows forwarding the initial action and running it automatically once connected. (i.e., it'll allow you to set "Action when Wi-Fi is off" to "turn_on", and whatch stuff connect and do what you wanted automatically without having to re-click anywhere instead of showing you a Wi-Fi prompt and then not doing anything without any other feedback).
* Speaking of, fixed the "turn_on" beforeWifi action to, well, actually work. It's no longer marked as experimental.
* Consistently use "Wi-Fi" everywhere.
* On Kobo/Cervantes/Sony, implemented a "Kill Wi-Fi connection when inactive" system that will automatically disconnect from Wi-Fi after sustained *network* inactivity (i.e., you can keep reading, it'll eventually turn off on its own). This should be smart and flexible enough not to murder Wi-Fi while you need it, while still not keeping it uselessly on and murdering your battery.
(i.e., enable that + turn Wi-Fi on when off and enjoy never having to bother about Wi-Fi ever again).
* Made sending `NetworkConnected` / `NetworkDisconnected` events consistent (they were only being sent... sometimes, which made relying on 'em somewhat problematic).
* restoreWifiAsync is now only run when really needed (i.e., we no longer stomp on an existing working connection just for the hell of it).
* We no longer attempt to kill a bogus non-existent Wi-Fi connection when going to suspend, we only do it when it's actually needed.
* Every method of enabling Wi-Fi will now properly tear down Wi-Fi on failure, instead of leaving it in an undefined state.
* Fixed an issue in the fancy crash screen on Kobo/reMarkable that could sometime lead to the log excerpt being missing.
* Worked-around a number of sneaky issues related to low-level Wi-Fi/DHCP/DNS handling on Kobo (see the lengthy comments [below](https://github.com/koreader/koreader/pull/6424#issuecomment-663881059) for details). Fix #6421
Incidentally, this should also fix the inconsistencies experienced re: Wi-Fi behavior in Nickel when toggling between KOReader and Nickel (use NM/KFMon, and run a current FW for best results).
* For developers, this involves various cleanups around NetworkMgr and NetworkListener. Documentation is in-line, above the concerned functions.
2020-07-27 01:39:06 +00:00
NetworkMgr : runWhenOnline ( connect_callback )
2018-10-16 18:49:44 +00:00
end ,
enabled_func = function ( )
return self.is_delete_finished or self.is_delete_read
end ,
} ,
{
text = _ ( " Go to download folder " ) ,
callback = function ( )
2019-06-09 13:40:21 +00:00
if self.ui . document then
self.ui : onClose ( )
end
2018-10-16 18:49:44 +00:00
if FileManager.instance then
2019-10-02 20:55:19 +00:00
FileManager.instance : reinit ( self.directory )
2018-10-16 18:49:44 +00:00
else
2019-10-02 20:55:19 +00:00
FileManager : showFiles ( self.directory )
2018-10-16 18:49:44 +00:00
end
end ,
} ,
{
2018-12-17 19:41:46 +00:00
text = _ ( " Settings " ) ,
2018-10-16 18:49:44 +00:00
callback_func = function ( )
return nil
end ,
2020-06-28 09:34:22 +00:00
separator = true ,
2018-10-16 18:49:44 +00:00
sub_item_table = {
{
text = _ ( " Configure Wallabag server " ) ,
keep_menu_open = true ,
callback = function ( )
self : editServerSettings ( )
end ,
} ,
2019-07-28 08:53:32 +00:00
{
text = _ ( " Configure Wallabag client " ) ,
keep_menu_open = true ,
callback = function ( )
self : editClientSettings ( )
end ,
} ,
2018-10-16 18:49:44 +00:00
{
text_func = function ( )
local path
if not self.directory or self.directory == " " then
2018-12-17 19:41:46 +00:00
path = _ ( " Not set " )
2018-10-16 18:49:44 +00:00
else
path = filemanagerutil.abbreviate ( self.directory )
end
2023-02-20 19:13:14 +00:00
return T ( _ ( " Set download folder: %1 " ) , BD.dirpath ( path ) )
2018-10-16 18:49:44 +00:00
end ,
keep_menu_open = true ,
callback = function ( touchmenu_instance )
self : setDownloadDirectory ( touchmenu_instance )
end ,
2020-06-28 09:34:22 +00:00
separator = true ,
2018-10-16 18:49:44 +00:00
} ,
{
text_func = function ( )
local filter
if not self.filter_tag or self.filter_tag == " " then
2018-12-17 19:41:46 +00:00
filter = _ ( " All articles " )
2018-10-16 18:49:44 +00:00
else
filter = self.filter_tag
end
2023-02-20 19:13:14 +00:00
return T ( _ ( " Filter articles by tag: %1 " ) , filter )
2018-10-16 18:49:44 +00:00
end ,
keep_menu_open = true ,
callback = function ( touchmenu_instance )
self : setFilterTag ( touchmenu_instance )
end ,
} ,
2019-10-13 06:48:37 +00:00
{
text_func = function ( )
if not self.ignore_tags or self.ignore_tags == " " then
return _ ( " Ignore tags " )
end
return T ( _ ( " Ignore tags (%1) " ) , self.ignore_tags )
end ,
keep_menu_open = true ,
callback = function ( touchmenu_instance )
2022-04-18 00:15:01 +00:00
self : setTagsDialog ( touchmenu_instance ,
_ ( " Tags to ignore " ) ,
_ ( " Enter a comma-separated list of tags to ignore. " ) ,
self.ignore_tags ,
function ( tags )
self.ignore_tags = tags
end
)
2019-10-13 06:48:37 +00:00
end ,
} ,
2022-04-17 19:06:50 +00:00
{
text_func = function ( )
if not self.auto_tags or self.auto_tags == " " then
return _ ( " Automatic tags " )
end
return T ( _ ( " Automatic tags (%1) " ) , self.auto_tags )
end ,
keep_menu_open = true ,
callback = function ( touchmenu_instance )
2022-04-18 00:15:01 +00:00
self : setTagsDialog ( touchmenu_instance ,
_ ( " Tags to automatically add " ) ,
_ ( " Enter a comma-separated list of tags to automatically add to new articles. " ) ,
self.auto_tags ,
function ( tags )
self.auto_tags = tags
end
)
2022-04-17 19:06:50 +00:00
end ,
separator = true ,
} ,
2018-10-16 18:49:44 +00:00
{
2020-06-28 09:34:22 +00:00
text = _ ( " Article deletion " ) ,
separator = true ,
sub_item_table = {
{
text = _ ( " Remotely delete finished articles " ) ,
checked_func = function ( ) return self.is_delete_finished end ,
callback = function ( )
self.is_delete_finished = not self.is_delete_finished
self : saveSettings ( )
end ,
} ,
{
text = _ ( " Remotely delete 100% read articles " ) ,
checked_func = function ( ) return self.is_delete_read end ,
callback = function ( )
self.is_delete_read = not self.is_delete_read
self : saveSettings ( )
end ,
separator = true ,
} ,
{
2023-02-04 07:45:16 +00:00
text = _ ( " Mark as finished instead of deleting " ) ,
2020-06-28 09:34:22 +00:00
checked_func = function ( ) return self.is_archiving_deleted end ,
callback = function ( )
self.is_archiving_deleted = not self.is_archiving_deleted
self : saveSettings ( )
end ,
separator = true ,
} ,
{
text = _ ( " Process deletions when downloading " ) ,
checked_func = function ( ) return self.is_auto_delete end ,
callback = function ( )
self.is_auto_delete = not self.is_auto_delete
self : saveSettings ( )
end ,
} ,
{
text = _ ( " Synchronize remotely deleted files " ) ,
checked_func = function ( ) return self.is_sync_remote_delete end ,
callback = function ( )
self.is_sync_remote_delete = not self.is_sync_remote_delete
self : saveSettings ( )
end ,
} ,
} ,
2018-10-16 18:49:44 +00:00
} ,
2022-01-10 20:38:45 +00:00
{
text = _ ( " Send review as tags " ) ,
help_text = _ ( " This allow you to write tags in the review field, separated by commas, which can then be sent to Wallabag. " ) ,
keep_menu_open = true ,
checked_func = function ( )
return self.send_review_as_tags or false
end ,
callback = function ( )
self.send_review_as_tags = not self.send_review_as_tags
self : saveSettings ( )
end ,
} ,
2019-07-28 11:59:29 +00:00
{
2020-06-28 09:34:22 +00:00
text = _ ( " Remove finished articles from history " ) ,
keep_menu_open = true ,
checked_func = function ( )
return self.remove_finished_from_history or false
2018-10-16 18:49:44 +00:00
end ,
callback = function ( )
2020-06-28 09:34:22 +00:00
self.remove_finished_from_history = not self.remove_finished_from_history
2018-10-16 18:49:44 +00:00
self : saveSettings ( )
end ,
} ,
2020-04-15 10:50:13 +00:00
{
2020-06-28 09:34:22 +00:00
text = _ ( " Remove 100% read articles from history " ) ,
2020-04-15 10:50:13 +00:00
keep_menu_open = true ,
checked_func = function ( )
2020-06-28 09:34:22 +00:00
return self.remove_read_from_history or false
2020-04-15 10:50:13 +00:00
end ,
callback = function ( )
2020-06-28 09:34:22 +00:00
self.remove_read_from_history = not self.remove_read_from_history
2020-04-15 10:50:13 +00:00
self : saveSettings ( )
end ,
2020-06-28 09:34:22 +00:00
separator = true ,
2020-04-15 10:50:13 +00:00
} ,
2018-10-16 18:49:44 +00:00
{
text = _ ( " Help " ) ,
keep_menu_open = true ,
callback = function ( )
UIManager : show ( InfoMessage : new {
text = _ ( [ [ Download directory : use a directory that is exclusively used by the Wallabag plugin . Existing files in this directory risk being deleted .
Articles marked as finished or 100 % read can be deleted from the server . Those articles can also be deleted automatically when downloading new articles if the ' Process deletions during download ' option is enabled .
2019-03-05 12:00:49 +00:00
The ' Synchronize remotely deleted files ' option will remove local files that no longer exist on the server . ] ] )
2018-10-16 18:49:44 +00:00
} )
end ,
}
}
} ,
{
text = _ ( " Info " ) ,
keep_menu_open = true ,
callback = function ( )
UIManager : show ( InfoMessage : new {
2019-03-05 12:00:49 +00:00
text = T ( _ ( [ [ Wallabag is an open source read - it - later service . This plugin synchronizes with a Wallabag server .
2018-10-16 18:49:44 +00:00
More details : https : // wallabag.org
2023-02-20 19:13:14 +00:00
Downloads to folder : % 1 ] ] ) , BD.dirpath ( filemanagerutil.abbreviate ( self.directory ) ) )
2018-10-16 18:49:44 +00:00
} )
end ,
} ,
} ,
}
end
function Wallabag : getBearerToken ( )
-- Check if the configuration is complete
local function isempty ( s )
return s == nil or s == " "
end
2020-06-12 20:40:48 +00:00
local server_empty = isempty ( self.server_url ) or isempty ( self.username ) or isempty ( self.password ) or isempty ( self.client_id ) or isempty ( self.client_secret )
local directory_empty = isempty ( self.directory )
if server_empty or directory_empty then
UIManager : show ( MultiConfirmBox : new {
text = _ ( " Please configure the server settings and set a download folder. " ) ,
choice1_text_func = function ( )
if server_empty then
return _ ( " Server (★) " )
else
return _ ( " Server " )
end
end ,
choice1_callback = function ( ) self : editServerSettings ( ) end ,
choice2_text_func = function ( )
if directory_empty then
return _ ( " Folder (★) " )
else
return _ ( " Folder " )
end
end ,
choice2_callback = function ( ) self : setDownloadDirectory ( ) end ,
2018-10-16 18:49:44 +00:00
} )
return false
end
-- Check if the download directory is valid
local dir_mode = lfs.attributes ( self.directory , " mode " )
if dir_mode ~= " directory " then
UIManager : show ( InfoMessage : new {
text = _ ( " The download directory is not valid. \n Please configure it in the settings. " )
} )
return false
end
2019-10-02 20:55:19 +00:00
if string.sub ( self.directory , - 1 ) ~= " / " then
2018-10-16 18:49:44 +00:00
self.directory = self.directory .. " / "
end
local now = os.time ( )
if self.token_expiry - now > 300 then
-- token still valid for a while, no need to renew
return true
end
local login_url = " /oauth/v2/token "
2019-01-05 19:28:28 +00:00
local body = {
grant_type = " password " ,
client_id = self.client_id ,
client_secret = self.client_secret ,
username = self.username ,
password = self.password
}
local bodyJSON = JSON.encode ( body )
2018-10-16 18:49:44 +00:00
local headers = {
[ " Content-type " ] = " application/json " ,
[ " Accept " ] = " application/json, */* " ,
2019-01-05 19:28:28 +00:00
[ " Content-Length " ] = tostring ( # bodyJSON ) ,
2021-03-15 00:25:10 +00:00
}
2019-10-02 20:55:19 +00:00
local result = self : callAPI ( " POST " , login_url , headers , bodyJSON , " " )
2018-10-16 18:49:44 +00:00
if result then
self.access_token = result.access_token
self.token_expiry = now + result.expires_in
return true
else
UIManager : show ( InfoMessage : new {
text = _ ( " Could not login to Wallabag server. " ) , } )
return false
end
end
2019-10-13 06:48:37 +00:00
--- Get a JSON formatted list of articles from the server.
-- The list should have self.article_per_sync item, or less if an error occured.
-- If filter_tag is set, only articles containing this tag are queried.
-- If ignore_tags is defined, articles containing either of the tags are skipped.
2018-10-16 18:49:44 +00:00
function Wallabag : getArticleList ( )
2018-11-19 11:30:17 +00:00
local filtering = " "
if self.filter_tag ~= " " then
filtering = " &tags= " .. self.filter_tag
end
2019-10-13 06:48:37 +00:00
local article_list = { }
local page = 1
-- query the server for articles until we hit our target number
while # article_list < self.articles_per_sync do
-- get the JSON containing the article list
local articles_url = " /api/entries.json?archive=0 "
.. " &page= " .. page
.. " &perPage= " .. self.articles_per_sync
.. filtering
2022-04-15 17:25:58 +00:00
local articles_json , err , code = self : callAPI ( " GET " , articles_url , nil , " " , " " , true )
2019-10-13 06:48:37 +00:00
2022-04-15 17:25:58 +00:00
if err == " http_error " and code == 404 then
2019-10-13 06:48:37 +00:00
-- we may have hit the last page, there are no more articles
logger.dbg ( " Wallabag: couldn't get page # " , page )
break -- exit while loop
2022-09-16 22:08:00 +00:00
elseif err or articles_json == nil then
2022-04-15 17:25:58 +00:00
-- another error has occured. Don't proceed with downloading
-- or deleting articles
logger.warn ( " Wallabag: download of page # " , page , " failed with " , err , code )
UIManager : show ( InfoMessage : new {
text = _ ( " Requesting article list failed. " ) , } )
return
2019-10-13 06:48:37 +00:00
end
-- We're only interested in the actual articles in the JSON
-- build an array of those so it's easier to manipulate later
local new_article_list = { }
for _ , article in ipairs ( articles_json._embedded . items ) do
table.insert ( new_article_list , article )
end
-- Apply the filters
new_article_list = self : filterIgnoredTags ( new_article_list )
-- Append the filtered list to the final article list
2022-09-16 22:08:00 +00:00
for _ , article in ipairs ( new_article_list ) do
2019-10-13 06:48:37 +00:00
if # article_list == self.articles_per_sync then
logger.dbg ( " Wallabag: hit the article target " , self.articles_per_sync )
break
end
table.insert ( article_list , article )
end
page = page + 1
end
return article_list
end
--- Remove all the articles from the list containing one of the ignored tags.
-- article_list: array containing a json formatted list of articles
-- returns: same array, but without any articles that contain an ignored tag.
function Wallabag : filterIgnoredTags ( article_list )
-- decode all tags to ignore
local ignoring = { }
if self.ignore_tags ~= " " then
for tag in util.gsplit ( self.ignore_tags , " [,]+ " , false ) do
ignoring [ tag ] = true
end
end
-- rebuild a list without the ignored articles
local filtered_list = { }
for _ , article in ipairs ( article_list ) do
local skip_article = false
for _ , tag in ipairs ( article.tags ) do
if ignoring [ tag.label ] then
skip_article = true
logger.dbg ( " Wallabag: ignoring tag " , tag.label , " in article " ,
article.id , " : " , article.title )
break -- no need to look for other tags
end
end
if not skip_article then
table.insert ( filtered_list , article )
end
end
return filtered_list
2018-10-16 18:49:44 +00:00
end
2019-08-23 17:53:53 +00:00
--- Download Wallabag article.
-- @string article
-- @treturn int 1 failed, 2 skipped, 3 downloaded
2018-10-16 18:49:44 +00:00
function Wallabag : download ( article )
local skip_article = false
2019-06-10 15:06:13 +00:00
local title = util.getSafeFilename ( article.title , self.directory , 230 , 0 )
2019-09-29 13:10:31 +00:00
local file_ext = " .epub "
local item_url = " /api/entries/ " .. article.id .. " /export.epub "
2021-01-17 08:22:48 +00:00
-- If the article links to a supported file, we will download it directly.
-- All webpages are HTML. Ignore them since we want the Wallabag EPUB instead!
if article.mimetype ~= " text/html " then
if DocumentRegistry : hasProvider ( nil , article.mimetype ) then
file_ext = " . " .. DocumentRegistry : mimeToExt ( article.mimetype )
item_url = article.url
-- A function represents `null` in our JSON.decode, because `nil` would just disappear.
-- In that case, fall back to the file extension.
elseif type ( article.mimetype ) == " function " and DocumentRegistry : hasProvider ( article.url ) then
file_ext = " "
item_url = article.url
end
2019-09-29 13:10:31 +00:00
end
local local_path = self.directory .. article_id_prefix .. article.id .. article_id_postfix .. title .. file_ext
2018-11-19 11:30:17 +00:00
logger.dbg ( " Wallabag: DOWNLOAD: id: " , article.id )
logger.dbg ( " Wallabag: DOWNLOAD: title: " , article.title )
logger.dbg ( " Wallabag: DOWNLOAD: filename: " , local_path )
2018-10-16 18:49:44 +00:00
local attr = lfs.attributes ( local_path )
if attr then
-- File already exists, skip it. Preferably only skip if the date of local file is newer than server's.
-- newsdownloader.koplugin has a date parser but it is available only if the plugin is activated.
2019-08-23 17:53:53 +00:00
--- @todo find a better solution
2018-10-16 18:49:44 +00:00
if self.is_dateparser_available then
local server_date = self.dateparser . parse ( article.updated_at )
if server_date < attr.modification then
skip_article = true
2018-11-19 11:30:17 +00:00
logger.dbg ( " Wallabag: skipping file (date checked): " , local_path )
2018-10-16 18:49:44 +00:00
end
else
skip_article = true
2018-11-19 11:30:17 +00:00
logger.dbg ( " Wallabag: skipping file: " , local_path )
2018-10-16 18:49:44 +00:00
end
end
if skip_article == false then
2019-10-02 20:55:19 +00:00
if self : callAPI ( " GET " , item_url , nil , " " , local_path ) then
2018-11-19 11:30:17 +00:00
return downloaded
else
return failed
end
2018-10-16 18:49:44 +00:00
end
2018-11-19 11:30:17 +00:00
return skipped
2018-10-16 18:49:44 +00:00
end
-- method: (mandatory) GET, POST, DELETE, PATCH, etc...
2019-09-29 13:10:31 +00:00
-- apiurl: (mandatory) API call excluding the server path, or full URL to a file
2018-10-16 18:49:44 +00:00
-- headers: defaults to auth if given nil value, provide all headers necessary if in use
-- body: empty string if not needed
-- filepath: downloads the file if provided, returns JSON otherwise
2022-04-15 17:25:58 +00:00
-- @treturn result or (nil, "network_error") or (nil, "json_error")
-- or (nil, "http_error", code)
2019-09-29 13:10:31 +00:00
---- @todo separate call to internal API from the download on external server
2020-06-10 19:54:57 +00:00
function Wallabag : callAPI ( method , apiurl , headers , body , filepath , quiet )
2021-03-15 00:25:10 +00:00
local sink = { }
local request = { }
2019-09-29 13:10:31 +00:00
-- Is it an API call, or a regular file direct download?
if apiurl : sub ( 1 , 1 ) == " / " then
-- API call to our server, has the form "/random/api/call"
2019-10-02 20:55:19 +00:00
request.url = self.server_url .. apiurl
2019-09-29 13:10:31 +00:00
if headers == nil then
2021-03-15 00:25:10 +00:00
headers = {
[ " Authorization " ] = " Bearer " .. self.access_token ,
}
2019-09-29 13:10:31 +00:00
end
else
-- regular url link to a foreign server
local file_url = apiurl
2019-10-02 20:55:19 +00:00
request.url = file_url
2019-09-29 13:10:31 +00:00
if headers == nil then
2021-03-15 00:25:10 +00:00
-- no need for a token here
headers = { }
2019-09-29 13:10:31 +00:00
end
end
2019-10-02 20:55:19 +00:00
request.method = method
2018-10-16 18:49:44 +00:00
if filepath ~= " " then
2019-10-02 20:55:19 +00:00
request.sink = ltn12.sink . file ( io.open ( filepath , " w " ) )
2021-05-13 08:15:53 +00:00
socketutil : set_timeout ( socketutil.FILE_BLOCK_TIMEOUT , socketutil.FILE_TOTAL_TIMEOUT )
2018-10-16 18:49:44 +00:00
else
2019-10-02 20:55:19 +00:00
request.sink = ltn12.sink . table ( sink )
2021-05-13 08:15:53 +00:00
socketutil : set_timeout ( socketutil.LARGE_BLOCK_TIMEOUT , socketutil.LARGE_TOTAL_TIMEOUT )
2018-10-16 18:49:44 +00:00
end
2019-10-02 20:55:19 +00:00
request.headers = headers
2018-10-16 18:49:44 +00:00
if body ~= " " then
2019-10-02 20:55:19 +00:00
request.source = ltn12.source . string ( body )
2018-10-16 18:49:44 +00:00
end
2019-10-02 20:55:19 +00:00
logger.dbg ( " Wallabag: URL " , request.url )
2018-11-19 11:30:17 +00:00
logger.dbg ( " Wallabag: method " , method )
2018-10-16 18:49:44 +00:00
2022-09-16 22:08:00 +00:00
local code , resp_headers , status = socket.skip ( 1 , http.request ( request ) )
2021-03-15 00:25:10 +00:00
socketutil : reset_timeout ( )
2018-10-16 18:49:44 +00:00
-- raise error message when network is unavailable
if resp_headers == nil then
2022-09-16 22:08:00 +00:00
logger.dbg ( " Wallabag: Server error: " , status or code )
2022-04-15 17:25:58 +00:00
return nil , " network_error "
2018-10-16 18:49:44 +00:00
end
if code == 200 then
if filepath ~= " " then
2018-11-19 11:30:17 +00:00
logger.dbg ( " Wallabag: file downloaded to " , filepath )
2018-10-16 18:49:44 +00:00
return true
else
local content = table.concat ( sink )
if content ~= " " and string.sub ( content , 1 , 1 ) == " { " then
local ok , result = pcall ( JSON.decode , content )
if ok and result then
-- Only enable this log when needed, the output can be large
2018-11-19 11:30:17 +00:00
--logger.dbg("Wallabag: result ", result)
2018-10-16 18:49:44 +00:00
return result
else
UIManager : show ( InfoMessage : new {
text = _ ( " Server response is not valid. " ) , } )
end
else
UIManager : show ( InfoMessage : new {
text = _ ( " Server response is not valid. " ) , } )
end
2022-04-15 17:25:58 +00:00
return nil , " json_error "
2018-10-16 18:49:44 +00:00
end
else
if filepath ~= " " then
2018-11-19 11:30:17 +00:00
local entry_mode = lfs.attributes ( filepath , " mode " )
if entry_mode == " file " then
os.remove ( filepath )
2022-09-16 22:08:00 +00:00
logger.dbg ( " Wallabag: Removed failed download: " , filepath )
2018-11-19 11:30:17 +00:00
end
2020-06-10 19:54:57 +00:00
elseif not quiet then
2018-11-19 11:30:17 +00:00
UIManager : show ( InfoMessage : new {
text = _ ( " Communication with server failed. " ) , } )
2018-10-16 18:49:44 +00:00
end
2022-09-16 22:08:00 +00:00
logger.dbg ( " Wallabag: Request failed: " , status or code )
logger.dbg ( " Wallabag: Response headers: " , resp_headers )
2022-04-15 17:25:58 +00:00
return nil , " http_error " , code
2018-10-16 18:49:44 +00:00
end
end
2019-03-05 12:00:49 +00:00
function Wallabag : synchronize ( )
2018-10-16 18:49:44 +00:00
local info = InfoMessage : new { text = _ ( " Connecting… " ) }
UIManager : show ( info )
UIManager : forceRePaint ( )
UIManager : close ( info )
if self : getBearerToken ( ) == false then
return false
end
2020-05-24 13:56:48 +00:00
if self.download_queue and next ( self.download_queue ) ~= nil then
info = InfoMessage : new { text = _ ( " Adding articles from queue… " ) }
UIManager : show ( info )
UIManager : forceRePaint ( )
for _ , articleUrl in ipairs ( self.download_queue ) do
self : addArticle ( articleUrl )
end
self.download_queue = { }
self : saveSettings ( )
UIManager : close ( info )
end
2018-10-16 18:49:44 +00:00
local deleted_count = self : processLocalFiles ( )
info = InfoMessage : new { text = _ ( " Getting article list… " ) }
UIManager : show ( info )
UIManager : forceRePaint ( )
UIManager : close ( info )
local remote_article_ids = { }
local downloaded_count = 0
2018-11-19 11:30:17 +00:00
local failed_count = 0
2018-10-16 18:49:44 +00:00
if self.access_token ~= " " then
local articles = self : getArticleList ( )
2018-11-19 11:30:17 +00:00
if articles then
2022-09-16 22:08:00 +00:00
logger.dbg ( " Wallabag: number of articles: " , # articles )
2018-11-19 11:30:17 +00:00
info = InfoMessage : new { text = _ ( " Downloading articles… " ) }
UIManager : show ( info )
UIManager : forceRePaint ( )
UIManager : close ( info )
2019-10-13 06:48:37 +00:00
for _ , article in ipairs ( articles ) do
2018-11-19 11:30:17 +00:00
logger.dbg ( " Wallabag: processing article ID: " , article.id )
2019-10-02 20:55:19 +00:00
remote_article_ids [ tostring ( article.id ) ] = true
2018-11-19 11:30:17 +00:00
local res = self : download ( article )
if res == downloaded then
downloaded_count = downloaded_count + 1
elseif res == failed then
failed_count = failed_count + 1
end
2018-10-16 18:49:44 +00:00
end
2019-03-05 12:00:49 +00:00
-- synchronize remote deletions
2019-10-02 20:55:19 +00:00
deleted_count = deleted_count + self : processRemoteDeletes ( remote_article_ids )
2018-10-16 18:49:44 +00:00
2018-11-19 11:30:17 +00:00
local msg
if failed_count ~= 0 then
msg = _ ( " Processing finished. \n \n Articles downloaded: %1 \n Deleted: %2 \n Failed: %3 " )
2019-10-02 20:55:19 +00:00
info = InfoMessage : new { text = T ( msg , downloaded_count , deleted_count , failed_count ) }
2018-11-19 11:30:17 +00:00
else
msg = _ ( " Processing finished. \n \n Articles downloaded: %1 \n Deleted: %2 " )
2019-10-02 20:55:19 +00:00
info = InfoMessage : new { text = T ( msg , downloaded_count , deleted_count ) }
2018-11-19 11:30:17 +00:00
end
UIManager : show ( info )
end -- articles
end -- access_token
2018-10-16 18:49:44 +00:00
end
2019-10-02 20:55:19 +00:00
function Wallabag : processRemoteDeletes ( remote_article_ids )
2018-10-16 18:49:44 +00:00
if not self.is_sync_remote_delete then
2018-11-19 11:30:17 +00:00
logger.dbg ( " Wallabag: Processing of remote file deletions disabled. " )
return 0
2018-10-16 18:49:44 +00:00
end
2018-11-19 11:30:17 +00:00
logger.dbg ( " Wallabag: articles IDs from server: " , remote_article_ids )
2018-10-16 18:49:44 +00:00
2020-07-24 08:46:55 +00:00
local info = InfoMessage : new { text = _ ( " Synchronizing remote deletions… " ) }
2018-10-16 18:49:44 +00:00
UIManager : show ( info )
UIManager : forceRePaint ( )
UIManager : close ( info )
local deleted_count = 0
for entry in lfs.dir ( self.directory ) do
if entry ~= " . " and entry ~= " .. " then
local entry_path = self.directory .. " / " .. entry
2019-10-02 20:55:19 +00:00
local id = self : getArticleID ( entry_path )
2018-10-16 18:49:44 +00:00
if not remote_article_ids [ id ] then
2019-10-02 20:55:19 +00:00
logger.dbg ( " Wallabag: Deleting local file (deleted on server): " , entry_path )
self : deleteLocalArticle ( entry_path )
2018-10-16 18:49:44 +00:00
deleted_count = deleted_count + 1
end
end
end -- for entry
return deleted_count
end
2019-10-02 20:55:19 +00:00
function Wallabag : processLocalFiles ( mode )
2018-10-16 18:49:44 +00:00
if mode then
if self.is_auto_delete == false and mode ~= " manual " then
2018-11-19 11:30:17 +00:00
logger.dbg ( " Wallabag: Automatic processing of local files disabled. " )
2018-10-16 18:49:44 +00:00
return 0 , 0
end
end
if self : getBearerToken ( ) == false then
return 0 , 0
end
local num_deleted = 0
if self.is_delete_finished or self.is_delete_read then
local info = InfoMessage : new { text = _ ( " Processing local files… " ) }
UIManager : show ( info )
UIManager : forceRePaint ( )
UIManager : close ( info )
for entry in lfs.dir ( self.directory ) do
if entry ~= " . " and entry ~= " .. " then
local entry_path = self.directory .. " / " .. entry
if DocSettings : hasSidecarFile ( entry_path ) then
2022-01-10 20:38:45 +00:00
if self.send_review_as_tags then
self : addTags ( entry_path )
end
2023-02-20 19:13:14 +00:00
local doc_settings = DocSettings : open ( entry_path )
local summary = doc_settings : readSetting ( " summary " )
local status = summary and summary.status
local percent_finished = doc_settings : readSetting ( " percent_finished " )
2018-10-16 18:49:44 +00:00
if status == " complete " or status == " abandoned " then
if self.is_delete_finished then
2019-10-02 20:55:19 +00:00
self : removeArticle ( entry_path )
2018-10-16 18:49:44 +00:00
num_deleted = num_deleted + 1
end
elseif percent_finished == 1 then -- 100% read
if self.is_delete_read then
2019-10-02 20:55:19 +00:00
self : removeArticle ( entry_path )
2018-10-16 18:49:44 +00:00
num_deleted = num_deleted + 1
end
end
end -- has sidecar
end -- not . and ..
end -- for entry
end -- flag checks
return num_deleted
end
2019-03-29 17:10:44 +00:00
function Wallabag : addArticle ( article_url )
logger.dbg ( " Wallabag: adding article " , article_url )
if not article_url or self : getBearerToken ( ) == false then
return false
end
local body = {
url = article_url ,
2022-04-18 00:15:01 +00:00
tags = self.auto_tags ,
2019-03-29 17:10:44 +00:00
}
local body_JSON = JSON.encode ( body )
local headers = {
[ " Content-type " ] = " application/json " ,
[ " Accept " ] = " application/json, */* " ,
[ " Content-Length " ] = tostring ( # body_JSON ) ,
[ " Authorization " ] = " Bearer " .. self.access_token ,
}
2019-03-31 17:19:07 +00:00
return self : callAPI ( " POST " , " /api/entries.json " , headers , body_JSON , " " )
2019-03-29 17:10:44 +00:00
end
2022-01-10 20:38:45 +00:00
function Wallabag : addTags ( path )
logger.dbg ( " Wallabag: managing tags for article " , path )
local id = self : getArticleID ( path )
if id then
2023-02-20 19:13:14 +00:00
local doc_settings = DocSettings : open ( path )
local summary = doc_settings : readSetting ( " summary " )
local tags = summary and summary.note
if tags and tags ~= " " then
logger.dbg ( " Wallabag: sending tags " , tags , " for " , path )
2022-01-10 20:38:45 +00:00
2023-02-20 19:13:14 +00:00
local body = {
tags = tags ,
}
2022-01-10 20:38:45 +00:00
2023-02-20 19:13:14 +00:00
local bodyJSON = JSON.encode ( body )
2022-01-10 20:38:45 +00:00
2023-02-20 19:13:14 +00:00
local headers = {
[ " Content-type " ] = " application/json " ,
[ " Accept " ] = " application/json, */* " ,
[ " Content-Length " ] = tostring ( # bodyJSON ) ,
[ " Authorization " ] = " Bearer " .. self.access_token ,
}
2022-01-10 20:38:45 +00:00
2023-02-20 19:13:14 +00:00
self : callAPI ( " POST " , " /api/entries/ " .. id .. " /tags.json " , headers , bodyJSON , " " )
else
logger.dbg ( " Wallabag: no tags to send for " , path )
end
2022-01-10 20:38:45 +00:00
end
end
2019-10-02 20:55:19 +00:00
function Wallabag : removeArticle ( path )
logger.dbg ( " Wallabag: removing article " , path )
local id = self : getArticleID ( path )
2018-10-16 18:49:44 +00:00
if id then
2019-07-28 11:59:29 +00:00
if self.is_archiving_deleted then
local body = {
archive = 1
}
local bodyJSON = JSON.encode ( body )
local headers = {
[ " Content-type " ] = " application/json " ,
[ " Accept " ] = " application/json, */* " ,
[ " Content-Length " ] = tostring ( # bodyJSON ) ,
[ " Authorization " ] = " Bearer " .. self.access_token ,
}
2019-10-02 20:55:19 +00:00
self : callAPI ( " PATCH " , " /api/entries/ " .. id .. " .json " , headers , bodyJSON , " " )
2019-07-28 11:59:29 +00:00
else
2019-10-02 20:55:19 +00:00
self : callAPI ( " DELETE " , " /api/entries/ " .. id .. " .json " , nil , " " , " " )
2019-07-28 11:59:29 +00:00
end
2019-10-02 20:55:19 +00:00
self : deleteLocalArticle ( path )
2018-10-16 18:49:44 +00:00
end
end
2019-10-02 20:55:19 +00:00
function Wallabag : deleteLocalArticle ( path )
2023-02-20 19:13:14 +00:00
if lfs.attributes ( path , " mode " ) == " file " then
FileManager : deleteFile ( path , true )
2018-10-16 18:49:44 +00:00
end
end
2019-10-02 20:55:19 +00:00
function Wallabag : getArticleID ( path )
2018-10-16 18:49:44 +00:00
-- extract the Wallabag ID from the file name
local offset = self.directory : len ( ) + 2 -- skip / and advance to the next char
2019-05-14 17:10:41 +00:00
local prefix_len = article_id_prefix : len ( )
2019-10-02 20:55:19 +00:00
if path : sub ( offset , offset + prefix_len - 1 ) ~= article_id_prefix then
logger.warn ( " Wallabag: getArticleID: no match! " , path : sub ( offset , offset + prefix_len - 1 ) )
2018-10-16 18:49:44 +00:00
return
end
2019-10-02 20:55:19 +00:00
local endpos = path : find ( article_id_postfix , offset + prefix_len )
2018-10-16 18:49:44 +00:00
if endpos == nil then
2019-10-02 20:55:19 +00:00
logger.warn ( " Wallabag: getArticleID: no match! " )
2018-10-16 18:49:44 +00:00
return
end
2019-10-02 20:55:19 +00:00
local id = path : sub ( offset + prefix_len , endpos - 1 )
2018-10-16 18:49:44 +00:00
return id
end
function Wallabag : refreshCurrentDirIfNeeded ( )
if FileManager.instance then
2019-10-02 20:55:19 +00:00
FileManager.instance : onRefresh ( )
2018-10-16 18:49:44 +00:00
end
end
function Wallabag : setFilterTag ( touchmenu_instance )
self.tag_dialog = InputDialog : new {
title = _ ( " Set a single tag to filter articles on " ) ,
input = self.filter_tag ,
buttons = {
{
{
text = _ ( " Cancel " ) ,
2022-03-04 20:20:00 +00:00
id = " close " ,
2018-10-16 18:49:44 +00:00
callback = function ( )
UIManager : close ( self.tag_dialog )
end ,
} ,
{
text = _ ( " OK " ) ,
is_enter_default = true ,
callback = function ( )
self.filter_tag = self.tag_dialog : getInputText ( )
self : saveSettings ( )
touchmenu_instance : updateItems ( )
UIManager : close ( self.tag_dialog )
end ,
}
}
} ,
}
UIManager : show ( self.tag_dialog )
self.tag_dialog : onShowKeyboard ( )
end
2022-04-18 00:15:01 +00:00
function Wallabag : setTagsDialog ( touchmenu_instance , title , description , value , callback )
self.tags_dialog = InputDialog : new {
title = title ,
description = description ,
input = value ,
2022-04-17 19:06:50 +00:00
buttons = {
{
{
text = _ ( " Cancel " ) ,
id = " close " ,
callback = function ( )
2022-04-18 00:15:01 +00:00
UIManager : close ( self.tags_dialog )
2022-04-17 19:06:50 +00:00
end ,
} ,
{
text = _ ( " Set tags " ) ,
is_enter_default = true ,
callback = function ( )
2022-04-18 00:15:01 +00:00
callback ( self.tags_dialog : getInputText ( ) )
2022-04-17 19:06:50 +00:00
self : saveSettings ( )
touchmenu_instance : updateItems ( )
2022-04-18 00:15:01 +00:00
UIManager : close ( self.tags_dialog )
2022-04-17 19:06:50 +00:00
end ,
}
}
} ,
}
2022-04-18 00:15:01 +00:00
UIManager : show ( self.tags_dialog )
self.tags_dialog : onShowKeyboard ( )
2022-04-17 19:06:50 +00:00
end
2018-10-16 18:49:44 +00:00
function Wallabag : editServerSettings ( )
2019-08-22 15:11:47 +00:00
local text_info = T ( _ ( [ [
Enter the details of your Wallabag server and account .
2018-10-16 18:49:44 +00:00
Client ID and client secret are long strings so you might prefer to save the empty settings and edit the config file directly in your installation directory :
% 1 / wallabag.lua
2020-01-04 00:18:51 +00:00
Restart KOReader after editing the config file . ] ] ) , BD.dirpath ( DataStorage : getSettingsDir ( ) ) )
2018-10-16 18:49:44 +00:00
self.settings_dialog = MultiInputDialog : new {
title = _ ( " Wallabag settings " ) ,
fields = {
{
text = self.server_url ,
--description = T(_("Server URL:")),
hint = _ ( " Server URL " )
} ,
{
text = self.client_id ,
--description = T(_("Client ID and secret")),
hint = _ ( " Client ID " )
} ,
{
text = self.client_secret ,
hint = _ ( " Client secret " )
} ,
{
text = self.username ,
2018-10-18 04:35:37 +00:00
--description = T(_("Username and password")),
2018-10-16 18:49:44 +00:00
hint = _ ( " Username " )
} ,
{
text = self.password ,
text_type = " password " ,
hint = _ ( " Password " )
} ,
} ,
buttons = {
{
{
text = _ ( " Cancel " ) ,
2022-03-04 20:20:00 +00:00
id = " close " ,
2018-10-16 18:49:44 +00:00
callback = function ( )
UIManager : close ( self.settings_dialog )
end
} ,
{
text = _ ( " Info " ) ,
callback = function ( )
UIManager : show ( InfoMessage : new { text = text_info } )
end
} ,
{
text = _ ( " Apply " ) ,
callback = function ( )
2022-10-06 15:11:47 +00:00
local myfields = self.settings_dialog : getFields ( )
2023-07-27 07:29:29 +00:00
self.server_url = myfields [ 1 ] : gsub ( " /*$ " , " " ) -- remove all trailing "/" slashes
2019-07-28 08:53:32 +00:00
self.client_id = myfields [ 2 ]
self.client_secret = myfields [ 3 ]
self.username = myfields [ 4 ]
self.password = myfields [ 5 ]
self : saveSettings ( )
2018-10-16 18:49:44 +00:00
UIManager : close ( self.settings_dialog )
end
} ,
} ,
} ,
}
UIManager : show ( self.settings_dialog )
self.settings_dialog : onShowKeyboard ( )
end
2019-07-28 08:53:32 +00:00
function Wallabag : editClientSettings ( )
self.client_settings_dialog = MultiInputDialog : new {
title = _ ( " Wallabag client settings " ) ,
fields = {
{
text = self.articles_per_sync ,
description = _ ( " Number of articles " ) ,
input_type = " number " ,
hint = _ ( " Number of articles to download per sync " )
} ,
} ,
buttons = {
{
{
text = _ ( " Cancel " ) ,
2022-03-04 20:20:00 +00:00
id = " close " ,
2019-07-28 08:53:32 +00:00
callback = function ( )
UIManager : close ( self.client_settings_dialog )
end
} ,
{
text = _ ( " Apply " ) ,
callback = function ( )
2022-10-06 15:11:47 +00:00
local myfields = self.client_settings_dialog : getFields ( )
2019-07-28 08:53:32 +00:00
self.articles_per_sync = math.max ( 1 , tonumber ( myfields [ 1 ] ) or self.articles_per_sync )
self : saveSettings ( myfields )
UIManager : close ( self.client_settings_dialog )
end
} ,
} ,
} ,
}
UIManager : show ( self.client_settings_dialog )
self.client_settings_dialog : onShowKeyboard ( )
end
2018-10-16 18:49:44 +00:00
function Wallabag : setDownloadDirectory ( touchmenu_instance )
require ( " ui/downloadmgr " ) : new {
2022-03-21 17:47:22 +00:00
onConfirm = function ( path )
logger.dbg ( " Wallabag: set download directory to: " , path )
self.directory = path
self : saveSettings ( )
if touchmenu_instance then
touchmenu_instance : updateItems ( )
end
end ,
2018-10-16 18:49:44 +00:00
} : chooseDir ( )
end
2019-10-02 20:55:19 +00:00
function Wallabag : saveSettings ( )
2018-10-16 18:49:44 +00:00
local tempsettings = {
server_url = self.server_url ,
client_id = self.client_id ,
client_secret = self.client_secret ,
username = self.username ,
password = self.password ,
directory = self.directory ,
filter_tag = self.filter_tag ,
2019-10-13 06:48:37 +00:00
ignore_tags = self.ignore_tags ,
2022-04-18 00:15:01 +00:00
auto_tags = self.auto_tags ,
2018-10-16 18:49:44 +00:00
is_delete_finished = self.is_delete_finished ,
is_delete_read = self.is_delete_read ,
2019-07-28 11:59:29 +00:00
is_archiving_deleted = self.is_archiving_deleted ,
2018-10-16 18:49:44 +00:00
is_auto_delete = self.is_auto_delete ,
2019-07-28 08:53:32 +00:00
is_sync_remote_delete = self.is_sync_remote_delete ,
2020-04-15 10:50:13 +00:00
articles_per_sync = self.articles_per_sync ,
2022-01-10 20:38:45 +00:00
send_review_as_tags = self.send_review_as_tags ,
2020-04-15 10:50:13 +00:00
remove_finished_from_history = self.remove_finished_from_history ,
2020-06-28 09:34:22 +00:00
remove_read_from_history = self.remove_read_from_history ,
2020-05-24 13:56:48 +00:00
download_queue = self.download_queue ,
2018-10-16 18:49:44 +00:00
}
self.wb_settings : saveSetting ( " wallabag " , tempsettings )
self.wb_settings : flush ( )
end
function Wallabag : readSettings ( )
local wb_settings = LuaSettings : open ( DataStorage : getSettingsDir ( ) .. " /wallabag.lua " )
2023-02-20 19:13:14 +00:00
wb_settings : readSetting ( " wallabag " , { } )
2018-10-16 18:49:44 +00:00
return wb_settings
end
function Wallabag : saveWBSettings ( setting )
2023-02-20 19:13:14 +00:00
if not self.wb_settings then self.wb_settings = self : readSettings ( ) end
2018-10-16 18:49:44 +00:00
self.wb_settings : saveSetting ( " wallabag " , setting )
self.wb_settings : flush ( )
end
2019-03-31 17:19:07 +00:00
function Wallabag : onAddWallabagArticle ( article_url )
if not NetworkMgr : isOnline ( ) then
2020-05-24 13:56:48 +00:00
self : addToDownloadQueue ( article_url )
UIManager : show ( InfoMessage : new {
text = T ( _ ( " Article added to download queue: \n %1 " ) , BD.url ( article_url ) ) ,
timeout = 1 ,
} )
2019-03-31 17:19:07 +00:00
return
end
local wallabag_result = self : addArticle ( article_url )
if wallabag_result then
UIManager : show ( InfoMessage : new {
2020-01-04 00:18:51 +00:00
text = T ( _ ( " Article added to Wallabag: \n %1 " ) , BD.url ( article_url ) ) ,
2019-03-31 17:19:07 +00:00
} )
else
UIManager : show ( InfoMessage : new {
2020-01-04 00:18:51 +00:00
text = T ( _ ( " Error adding link to Wallabag: \n %1 " ) , BD.url ( article_url ) ) ,
2019-03-31 17:19:07 +00:00
} )
end
-- stop propagation
return true
end
2019-03-05 12:00:49 +00:00
function Wallabag : onSynchronizeWallabag ( )
Various Wi-Fi QoL improvements (#6424)
* Revamped most actions that require an internet connection to a new/fixed backend that allows forwarding the initial action and running it automatically once connected. (i.e., it'll allow you to set "Action when Wi-Fi is off" to "turn_on", and whatch stuff connect and do what you wanted automatically without having to re-click anywhere instead of showing you a Wi-Fi prompt and then not doing anything without any other feedback).
* Speaking of, fixed the "turn_on" beforeWifi action to, well, actually work. It's no longer marked as experimental.
* Consistently use "Wi-Fi" everywhere.
* On Kobo/Cervantes/Sony, implemented a "Kill Wi-Fi connection when inactive" system that will automatically disconnect from Wi-Fi after sustained *network* inactivity (i.e., you can keep reading, it'll eventually turn off on its own). This should be smart and flexible enough not to murder Wi-Fi while you need it, while still not keeping it uselessly on and murdering your battery.
(i.e., enable that + turn Wi-Fi on when off and enjoy never having to bother about Wi-Fi ever again).
* Made sending `NetworkConnected` / `NetworkDisconnected` events consistent (they were only being sent... sometimes, which made relying on 'em somewhat problematic).
* restoreWifiAsync is now only run when really needed (i.e., we no longer stomp on an existing working connection just for the hell of it).
* We no longer attempt to kill a bogus non-existent Wi-Fi connection when going to suspend, we only do it when it's actually needed.
* Every method of enabling Wi-Fi will now properly tear down Wi-Fi on failure, instead of leaving it in an undefined state.
* Fixed an issue in the fancy crash screen on Kobo/reMarkable that could sometime lead to the log excerpt being missing.
* Worked-around a number of sneaky issues related to low-level Wi-Fi/DHCP/DNS handling on Kobo (see the lengthy comments [below](https://github.com/koreader/koreader/pull/6424#issuecomment-663881059) for details). Fix #6421
Incidentally, this should also fix the inconsistencies experienced re: Wi-Fi behavior in Nickel when toggling between KOReader and Nickel (use NM/KFMon, and run a current FW for best results).
* For developers, this involves various cleanups around NetworkMgr and NetworkListener. Documentation is in-line, above the concerned functions.
2020-07-27 01:39:06 +00:00
local connect_callback = function ( )
self : synchronize ( )
self : refreshCurrentDirIfNeeded ( )
2019-03-05 12:00:49 +00:00
end
Various Wi-Fi QoL improvements (#6424)
* Revamped most actions that require an internet connection to a new/fixed backend that allows forwarding the initial action and running it automatically once connected. (i.e., it'll allow you to set "Action when Wi-Fi is off" to "turn_on", and whatch stuff connect and do what you wanted automatically without having to re-click anywhere instead of showing you a Wi-Fi prompt and then not doing anything without any other feedback).
* Speaking of, fixed the "turn_on" beforeWifi action to, well, actually work. It's no longer marked as experimental.
* Consistently use "Wi-Fi" everywhere.
* On Kobo/Cervantes/Sony, implemented a "Kill Wi-Fi connection when inactive" system that will automatically disconnect from Wi-Fi after sustained *network* inactivity (i.e., you can keep reading, it'll eventually turn off on its own). This should be smart and flexible enough not to murder Wi-Fi while you need it, while still not keeping it uselessly on and murdering your battery.
(i.e., enable that + turn Wi-Fi on when off and enjoy never having to bother about Wi-Fi ever again).
* Made sending `NetworkConnected` / `NetworkDisconnected` events consistent (they were only being sent... sometimes, which made relying on 'em somewhat problematic).
* restoreWifiAsync is now only run when really needed (i.e., we no longer stomp on an existing working connection just for the hell of it).
* We no longer attempt to kill a bogus non-existent Wi-Fi connection when going to suspend, we only do it when it's actually needed.
* Every method of enabling Wi-Fi will now properly tear down Wi-Fi on failure, instead of leaving it in an undefined state.
* Fixed an issue in the fancy crash screen on Kobo/reMarkable that could sometime lead to the log excerpt being missing.
* Worked-around a number of sneaky issues related to low-level Wi-Fi/DHCP/DNS handling on Kobo (see the lengthy comments [below](https://github.com/koreader/koreader/pull/6424#issuecomment-663881059) for details). Fix #6421
Incidentally, this should also fix the inconsistencies experienced re: Wi-Fi behavior in Nickel when toggling between KOReader and Nickel (use NM/KFMon, and run a current FW for best results).
* For developers, this involves various cleanups around NetworkMgr and NetworkListener. Documentation is in-line, above the concerned functions.
2020-07-27 01:39:06 +00:00
NetworkMgr : runWhenOnline ( connect_callback )
2019-03-31 17:19:07 +00:00
-- stop propagation
return true
2019-03-05 12:00:49 +00:00
end
2020-04-15 10:50:13 +00:00
function Wallabag : getLastPercent ( )
2023-02-20 19:13:14 +00:00
local percent = self.ui . paging and self.ui . paging : getLastPercent ( ) or self.ui . rolling : getLastPercent ( )
return Math.roundPercent ( percent )
2020-04-15 10:50:13 +00:00
end
2020-05-24 13:56:48 +00:00
function Wallabag : addToDownloadQueue ( article_url )
table.insert ( self.download_queue , article_url )
self : saveSettings ( )
end
2020-04-15 13:25:29 +00:00
function Wallabag : onCloseDocument ( )
2020-06-28 09:34:22 +00:00
if self.remove_finished_from_history or self.remove_read_from_history then
2020-04-15 10:50:13 +00:00
local document_full_path = self.ui . document.file
2023-02-20 19:13:14 +00:00
local summary = self.ui . doc_settings : readSetting ( " summary " )
local status = summary and summary.status
local is_finished = status == " complete " or status == " abandoned "
2020-06-28 09:34:22 +00:00
local is_read = self : getLastPercent ( ) == 1
if document_full_path
and self.directory
and ( ( self.remove_finished_from_history and is_finished ) or ( self.remove_read_from_history and is_read ) )
and self.directory == string.sub ( document_full_path , 1 , string.len ( self.directory ) ) then
2020-04-15 10:50:13 +00:00
ReadHistory : removeItemByPath ( document_full_path )
2020-05-19 16:21:58 +00:00
self.ui : setLastDirForFileBrowser ( self.directory )
2020-04-15 10:50:13 +00:00
end
end
end
2018-10-16 18:49:44 +00:00
return Wallabag