summaryrefslogtreecommitdiffhomepage
path: root/applications/luci-app-radicale/luasrc
diff options
context:
space:
mode:
authorChristian Schoenebeck <christian.schoenebeck@gmail.com>2016-02-07 09:30:29 +0100
committerChristian Schoenebeck <christian.schoenebeck@gmail.com>2016-02-07 09:30:29 +0100
commit026ac8d033f4c0a65b0c8d121d1a2a86b2bd1ee1 (patch)
tree767f1d7e3a4614b35ace2a9377ae63139172a887 /applications/luci-app-radicale/luasrc
parent26d009aff924f8253533ae6a303f61fe0d35d2f4 (diff)
luci-app-radicale: bump to version 1.1.0
- support Radicale > v1.1 - modified version detection - adaption to new function version_compare() in ipkg.lua - adaption to fixed Flag.parse() in cbi.lua - adaption to new property map.tabbed in cbi.lua using map template with extensions - change optional values to non optional - add support new option "system.boot_delay" Signed-off-by: Christian Schoenebeck <christian.schoenebeck@gmail.com>
Diffstat (limited to 'applications/luci-app-radicale/luasrc')
-rw-r--r--applications/luci-app-radicale/luasrc/controller/radicale.lua298
-rw-r--r--applications/luci-app-radicale/luasrc/model/cbi/radicale.lua306
-rw-r--r--applications/luci-app-radicale/luasrc/view/radicale/btn_startstop.htm6
-rw-r--r--applications/luci-app-radicale/luasrc/view/radicale/ro_value.htm35
-rw-r--r--applications/luci-app-radicale/luasrc/view/radicale/tabmap_nsections.htm49
5 files changed, 375 insertions, 319 deletions
diff --git a/applications/luci-app-radicale/luasrc/controller/radicale.lua b/applications/luci-app-radicale/luasrc/controller/radicale.lua
index 10ec1fe54..0be433a48 100644
--- a/applications/luci-app-radicale/luasrc/controller/radicale.lua
+++ b/applications/luci-app-radicale/luasrc/controller/radicale.lua
@@ -1,15 +1,23 @@
--- Copyright 2014 Christian Schoenebeck <christian dot schoenebeck at gmail dot com>
+-- Copyright 2014-2016 Christian Schoenebeck <christian dot schoenebeck at gmail dot com>
-- Licensed under the Apache License, Version 2.0
module("luci.controller.radicale", package.seeall)
-local NX = require("nixio")
-local NXFS = require("nixio.fs")
-local DISP = require "luci.dispatcher"
-local HTTP = require("luci.http")
+local NX = require("nixio")
+local NXFS = require("nixio.fs")
+local DISP = require("luci.dispatcher")
+local HTTP = require("luci.http")
local I18N = require("luci.i18n") -- not globally avalible here
-local UTIL = require("luci.util")
-local SYS = require("luci.sys")
+local IPKG = require("luci.model.ipkg")
+local UTIL = require("luci.util")
+local SYS = require("luci.sys")
+
+local srv_name = "radicale"
+local srv_ver_min = "1.1" -- minimum version of service required
+local srv_ver_cmd = [[/usr/bin/radicale --version]]
+local app_name = "luci-app-radicale"
+local app_title = I18N.translate("Radicale CalDAV/CardDAV Server")
+local app_version = "1.1.0-1"
function index()
entry( {"admin", "services", "radicale"}, alias("admin", "services", "radicale", "edit"), _("CalDAV/CardDAV"), 58)
@@ -19,6 +27,75 @@ function index()
entry( {"admin", "services", "radicale", "status"}, call("_status") ).leaf = true
end
+-- Application / Service specific information functions
+function app_description()
+ return I18N.translate("The Radicale Project is a complete CalDAV (calendar) and CardDAV (contact) server solution.") .. [[<br />]]
+ .. I18N.translate("Calendars and address books are available for both local and remote access, possibly limited through authentication policies.") .. [[<br />]]
+ .. I18N.translate("They can be viewed and edited by calendar and contact clients on mobile phones or computers.")
+end
+function app_title_main()
+ return [[<a href="javascript:alert(']]
+ .. I18N.translate("Version Information")
+ .. [[\n\n]] .. app_name
+ .. [[\n\t]] .. I18N.translate("Version") .. [[:\t]] .. app_version
+ .. [[\n\n]] .. srv_name .. [[ ]] .. I18N.translate("required") .. [[:]]
+ .. [[\n\t]] .. I18N.translate("Version") .. [[:\t]]
+ .. srv_ver_min .. [[ ]] .. I18N.translate("or higher")
+ .. [[\n\n]] .. srv_name .. [[ ]] .. I18N.translate("installed") .. [[:]]
+ .. [[\n\t]] .. I18N.translate("Version") .. [[:\t]]
+ .. (service_version() or I18N.translate("NOT installed"))
+ .. [[\n\n]]
+ .. [[')">]]
+ .. I18N.translate(app_title)
+ .. [[</a>]]
+end
+function app_title_back()
+ return [[<a href="]]
+ .. DISP.build_url("admin", "services", "radicale")
+ .. [[">]]
+ .. I18N.translate(app_title)
+ .. [[</a>]]
+end
+function app_err_value()
+ if not service_version() then
+ return [[<h3><strong><br /><font color="red">&nbsp;&nbsp;&nbsp;&nbsp;]]
+ .. I18N.translate("Software package '%s' is not installed." % srv_name)
+ .. [[</font><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;]]
+ .. I18N.translate("required") .. [[: ]] .. srv_name .. [[ ]] .. srv_ver_min
+ .. [[<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;]]
+ .. [[<a href="]] .. DISP.build_url("admin", "system", "packages") ..[[">]]
+ .. I18N.translate("Please install current version !")
+ .. [[</a><br />&nbsp;</strong></h3>]]
+ else
+ return [[<h3><strong><br /><font color="red">&nbsp;&nbsp;&nbsp;&nbsp;]]
+ .. I18N.translate("Software package '%s' is outdated." % srv_name)
+ .. [[</font><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;]]
+ .. I18N.translate("installed") .. [[: ]] .. srv_name .. [[ ]] .. service_version()
+ .. [[<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;]]
+ .. I18N.translate("required") .. [[: ]] .. srv_name .. [[ ]] .. srv_ver_min
+ .. [[<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;]]
+ .. [[<a href="]] .. DISP.build_url("admin", "system", "packages") ..[[">]]
+ .. I18N.translate("Please update to current version !")
+ .. [[</a><br />&nbsp;</strong></h3>]]
+ end
+end
+
+function service_version()
+ local ver = nil
+ IPKG.list_installed(srv_name, function(n, v, d)
+ if v and (#v > 0) then ver = v end
+ end
+ )
+ if not ver or (#ver == 0) then
+ ver = UTIL.exec(srv_ver_cmd)
+ if #ver == 0 then ver = nil end
+ end
+ return ver
+end
+function service_ok()
+ return IPKG.compare_versions((service_version() or "0"), ">=", srv_ver_min)
+end
+
-- called by XHR.get from detail_logview.htm
function _logread()
-- read application settings
@@ -60,138 +137,103 @@ function _status()
HTTP.write(tostring(pid)) -- HTTP needs string not number
end
--- Application / Service specific information functions ########################
-function luci_app_name()
- return "luci-app-radicale"
-end
-
-function service_name()
- return "radicale"
-end
-function service_required()
- return "0.10-1"
-end
-function service_installed()
- local v = ipkg_ver_installed("radicale-py2")
- if not v or #v == 0 then v = ipkg_ver_installed("radicale-py3") end
- if not v or #v == 0 then v = "0" end
- return v
-end
-function service_ok()
- return ipkg_ver_compare(service_installed(),">=",service_required())
-end
-
-function app_title_main()
- return [[<a href="javascript:alert(']]
- .. I18N.translate("Version Information")
- .. [[\n\n]] .. luci_app_name()
- .. [[\n\t]] .. I18N.translate("Version") .. [[:\t]]
- .. (ipkg_ver_installed(luci_app_name()) == ""
- and I18N.translate("NOT installed")
- or ipkg_ver_installed(luci_app_name()) )
- .. [[\n\n]] .. service_name() .. [[ ]] .. I18N.translate("required") .. [[:]]
- .. [[\n\t]] .. I18N.translate("Version") .. [[:\t]]
- .. service_required() .. [[ ]] .. I18N.translate("or higher")
- .. [[\n\n]] .. service_name() .. [[ ]] .. I18N.translate("installed") .. [[:]]
- .. [[\n\t]] .. I18N.translate("Version") .. [[:\t]]
- .. (service_installed() == "0"
- and I18N.translate("NOT installed")
- or service_installed())
- .. [[\n\n]]
- .. [[')">]]
- .. I18N.translate("Radicale CalDAV/CardDAV Server")
- .. [[</a>]]
-end
-function app_title_back()
- return [[<a href="]]
- .. DISP.build_url("admin", "services", "radicale")
- .. [[">]]
- .. I18N.translate("Radicale CalDAV/CardDAV Server")
- .. [[</a>]]
-end
-function app_description()
- return I18N.translate("The Radicale Project is a complete CalDAV (calendar) and CardDAV (contact) server solution.") .. [[<br />]]
- .. I18N.translate("Calendars and address books are available for both local and remote access, possibly limited through authentication policies.") .. [[<br />]]
- .. I18N.translate("They can be viewed and edited by calendar and contact clients on mobile phones or computers.")
-end
-
--- other multiused functions ###################################################
-
--return pid of running process
function get_pid()
return tonumber(SYS.exec([[ps | grep "[p]ython.*[r]adicale" 2>/dev/null | awk '{print $1}']])) or 0
end
--- compare versions using "<=" "<" ">" ">=" "=" "<<" ">>"
-function ipkg_ver_compare(ver1, comp, ver2)
- if not ver1 or not ver2
- or not comp or not (#comp > 0) then return nil end
- -- correct compare string
- if comp == "<>" or comp == "><" or comp == "!=" or comp == "~=" then comp = "~="
- elseif comp == "<=" or comp == "<" or comp == "=<" then comp = "<="
- elseif comp == ">=" or comp == ">" or comp == "=>" then comp = ">="
- elseif comp == "=" or comp == "==" then comp = "=="
- elseif comp == "<<" then comp = "<"
- elseif comp == ">>" then comp = ">"
- else return nil end
-
- local av1 = UTIL.split(ver1, "[%.%-]", nil, true)
- local av2 = UTIL.split(ver2, "[%.%-]", nil, true)
-
- for i = 1, math.max(table.getn(av1),table.getn(av2)), 1 do
- local s1 = av1[i] or ""
- local s2 = av2[i] or ""
-
- -- first "not equal" found return true
- if comp == "~=" and (s1 ~= s2) then return true end
- -- first "lower" found return true
- if (comp == "<" or comp == "<=") and (s1 < s2) then return true end
- -- first "greater" found return true
- if (comp == ">" or comp == ">=") and (s1 > s2) then return true end
- -- not equal then return false
- if (s1 ~= s2) then return false end
+-- replacement of build-in parse of "Value"
+-- modified AbstractValue.parse(self, section, novld) from cbi.lua
+-- validate is called if rmempty/optional true or false
+-- before write check if forcewrite, value eq default, and more
+function value_parse(self, section, novld)
+ local fvalue = self:formvalue(section)
+ local fexist = ( fvalue and (#fvalue > 0) ) -- not "nil" and "not empty"
+ local cvalue = self:cfgvalue(section)
+ local rm_opt = ( self.rmempty or self.optional )
+ local eq_cfg -- flag: equal cfgvalue
+
+ -- If favlue and cvalue are both tables and have the same content
+ -- make them identical
+ if type(fvalue) == "table" and type(cvalue) == "table" then
+ eq_cfg = (#fvalue == #cvalue)
+ if eq_cfg then
+ for i=1, #fvalue do
+ if cvalue[i] ~= fvalue[i] then
+ eq_cfg = false
+ end
+ end
+ end
+ if eq_cfg then
+ fvalue = cvalue
+ end
end
- -- all equal and not compare greater or lower then true
- return not (comp == "<" or comp == ">")
-end
+ -- removed parameter "section" from function call because used/accepted nowhere
+ -- also removed call to function "transfer"
+ local vvalue, errtxt = self:validate(fvalue)
--- read version information for given package if installed
-function ipkg_ver_installed(pkg)
- local version = ""
- local control = io.open("/usr/lib/opkg/info/%s.control" % pkg, "r")
- if control then
- local ln
- repeat
- ln = control:read("*l")
- if ln and ln:match("^Version: ") then
- version = ln:gsub("^Version: ", "")
- break
- end
- until not ln
- control:close()
+ -- error handling; validate return "nil"
+ if not vvalue then
+ if novld then -- and "novld" set
+ return -- then exit without raising an error
+ end
+
+ if fexist then -- and there is a formvalue
+ self:add_error(section, "invalid", errtxt)
+ return -- so data are invalid
+ elseif not rm_opt then -- and empty formvalue but NOT (rmempty or optional) set
+ self:add_error(section, "missing", errtxt)
+ return -- so data is missing
+ elseif errtxt then
+ self:add_error(section, "invalid", errtxt)
+ return
+ end
+-- error ("\n option: " .. self.option ..
+-- "\n fvalue: " .. tostring(fvalue) ..
+-- "\n fexist: " .. tostring(fexist) ..
+-- "\n cvalue: " .. tostring(cvalue) ..
+-- "\n vvalue: " .. tostring(vvalue) ..
+-- "\n vexist: " .. tostring(vexist) ..
+-- "\n rm_opt: " .. tostring(rm_opt) ..
+-- "\n eq_cfg: " .. tostring(eq_cfg) ..
+-- "\n eq_def: " .. tostring(eq_def) ..
+-- "\n novld : " .. tostring(novld) ..
+-- "\n errtxt: " .. tostring(errtxt) )
end
- return version
-end
--- replacement of build-in Flag.parse of cbi.lua
--- modified to mark section as changed if value changes
--- current parse did not do this, but it is done AbstaractValue.parse()
-function flag_parse(self, section)
- local fexists = self.map:formvalue(
- luci.cbi.FEXIST_PREFIX .. self.config .. "." .. section .. "." .. self.option)
-
- if fexists then
- local fvalue = self:formvalue(section) and self.enabled or self.disabled
- local cvalue = self:cfgvalue(section)
- if fvalue ~= self.default or (not self.optional and not self.rmempty) then
- self:write(section, fvalue)
- else
- self:remove(section)
+ -- lets continue with value returned from validate
+ eq_cfg = ( vvalue == cvalue ) -- update equal_config flag
+ local vexist = ( vvalue and (#vvalue > 0) ) and true or false -- not "nil" and "not empty"
+ local eq_def = ( vvalue == self.default ) -- equal_default flag
+
+ -- (rmempty or optional) and (no data or equal_default)
+ if rm_opt and (not vexist or eq_def) then
+ if self:remove(section) then -- remove data from UCI
+ self.section.changed = true -- and push events
end
- if (fvalue ~= cvalue) then self.section.changed = true end
- else
- self:remove(section)
+ return
+ end
+
+ -- not forcewrite and no changes, so nothing to write
+ if not self.forcewrite and eq_cfg then
+ return
+ end
+
+ -- we should have a valid value here
+ assert (vvalue, "\n option: " .. self.option ..
+ "\n fvalue: " .. tostring(fvalue) ..
+ "\n fexist: " .. tostring(fexist) ..
+ "\n cvalue: " .. tostring(cvalue) ..
+ "\n vvalue: " .. tostring(vvalue) ..
+ "\n vexist: " .. tostring(vexist) ..
+ "\n rm_opt: " .. tostring(rm_opt) ..
+ "\n eq_cfg: " .. tostring(eq_cfg) ..
+ "\n eq_def: " .. tostring(eq_def) ..
+ "\n errtxt: " .. tostring(errtxt) )
+
+ -- write data to UCI; raise event only on changes
+ if self:write(section, vvalue) and not eq_cfg then
self.section.changed = true
end
end
diff --git a/applications/luci-app-radicale/luasrc/model/cbi/radicale.lua b/applications/luci-app-radicale/luasrc/model/cbi/radicale.lua
index 8abb68869..c610478bc 100644
--- a/applications/luci-app-radicale/luasrc/model/cbi/radicale.lua
+++ b/applications/luci-app-radicale/luasrc/model/cbi/radicale.lua
@@ -1,14 +1,43 @@
--- Copyright 2015 Christian Schoenebeck <christian dot schoenebeck at gmail dot com>
+-- Copyright 2015-2016 Christian Schoenebeck <christian dot schoenebeck at gmail dot com>
-- Licensed under the Apache License, Version 2.0
-local NXFS = require("nixio.fs")
-local DISP = require("luci.dispatcher")
-local DTYP = require("luci.cbi.datatypes")
-local HTTP = require("luci.http")
-local UTIL = require("luci.util")
-local UCI = require("luci.model.uci")
-local SYS = require("luci.sys")
-local TOOLS = require("luci.controller.radicale") -- this application's controller and multiused functions
+local NXFS = require("nixio.fs")
+local DISP = require("luci.dispatcher")
+local DTYP = require("luci.cbi.datatypes")
+local HTTP = require("luci.http")
+local UTIL = require("luci.util")
+local UCI = require("luci.model.uci")
+local SYS = require("luci.sys")
+local WADM = require("luci.tools.webadmin")
+local CTRL = require("luci.controller.radicale") -- this application's controller and multiused functions
+
+-- #################################################################################################
+-- Error handling if not installed or wrong version -- #########################
+if not CTRL.service_ok() then
+ local f = SimpleForm("__sf")
+ f.title = CTRL.app_title_main()
+ f.description = CTRL.app_description()
+ f.embedded = true
+ f.submit = false
+ f.reset = false
+
+ local s = f:section(SimpleSection)
+ s.title = [[<font color="red">]] .. [[<strong>]]
+ .. translate("Software update required")
+ .. [[</strong>]] .. [[</font>]]
+
+ local v = s:option(DummyValue, "_dv")
+ v.rawhtml = true
+ v.value = CTRL.app_err_value
+
+ return f
+end
+
+-- #################################################################################################
+-- Error handling if no config, create an empty one -- #########################
+if not NXFS.access("/etc/config/radicale") then
+ NXFS.writefile("/etc/config/radicale", "")
+end
-- #################################################################################################
-- takeover arguments if any -- ################################################
@@ -19,8 +48,8 @@ if arg[1] then
-- SimpleForm ------------------------------------------------
local ft = SimpleForm("_text")
- ft.title = TOOLS.app_title_back()
- ft.description = TOOLS.app_description()
+ ft.title = CTRL.app_title_back()
+ ft.description = CTRL.app_description()
ft.redirect = DISP.build_url("admin", "services", "radicale") .. "#cbi-radicale-" .. argument
if argument == "logger" then
ft.reset = false
@@ -95,54 +124,12 @@ if arg[1] then
end
--- #################################################################################################
--- Error handling if not installed or wrong version -- #########################
-if not TOOLS.service_ok() then
- local f = SimpleForm("_no_config")
- f.title = TOOLS.app_title_main()
- f.description = TOOLS.app_description()
- f.submit = false
- f.reset = false
-
- local s = f:section(SimpleSection)
-
- local v = s:option(DummyValue, "_update_needed")
- v.rawhtml = true
- if TOOLS.service_installed() == "0" then
- v.value = [[<h3><strong><br /><font color="red">&nbsp;&nbsp;&nbsp;&nbsp;]]
- .. translate("Software package '" .. TOOLS.service_name() .. "' is not installed.")
- .. [[</font><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;]]
- .. translate("required") .. [[: ]] .. TOOLS.service_name() .. [[ ]] .. TOOLS.service_required()
- .. [[<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;]]
- .. [[<a href="]] .. DISP.build_url("admin", "system", "packages") ..[[">]]
- .. translate("Please install current version !")
- .. [[</a><br />&nbsp;</strong></h3>]]
- else
- v.value = [[<h3><strong><br /><font color="red">&nbsp;&nbsp;&nbsp;&nbsp;]]
- .. translate("Software package '" .. TOOLS.service_name() .. "' is outdated.")
- .. [[</font><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;]]
- .. translate("installed") .. [[: ]] .. TOOLS.service_name() .. [[ ]] .. TOOLS.service_installed()
- .. [[<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;]]
- .. translate("required") .. [[: ]] .. TOOLS.service_name() .. [[ ]] .. TOOLS.service_required()
- .. [[<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;]]
- .. [[<a href="]] .. DISP.build_url("admin", "system", "packages") ..[[">]]
- .. translate("Please update to current version !")
- .. [[</a><br />&nbsp;</strong></h3>]]
- end
-
- return f
-end
-
--- #################################################################################################
--- Error handling if no config, create an empty one -- #########################
-if not NXFS.access("/etc/config/radicale") then
- NXFS.writefile("/etc/config/radicale", "")
-end
-
-- cbi-map -- ##################################################################
local m = Map("radicale")
-m.title = TOOLS.app_title_main()
-m.description = TOOLS.app_description()
+m.title = CTRL.app_title_main()
+m.description = CTRL.app_description()
+m.template = "radicale/tabmap_nsections"
+m.tabbed = true
function m.commit_handler(self)
if self.changed then -- changes ?
os.execute("/etc/init.d/radicale reload &") -- reload configuration
@@ -150,11 +137,14 @@ function m.commit_handler(self)
end
-- cbi-section "System" -- #####################################################
-local sys = m:section( NamedSection, "_system" )
+local sys = m:section( NamedSection, "system", "system" )
sys.title = translate("System")
sys.description = nil
function sys.cfgvalue(self, section)
- return "_dummysection"
+ if not self.map:get(section) then -- section might not exist
+ self.map:set(section, nil, self.sectiontype)
+ end
+ return self.map:get(section)
end
-- start/stop button -----------------------------------------------------------
@@ -165,7 +155,7 @@ btn.rmempty = true
btn.title = translate("Start / Stop")
btn.description = translate("Start/Stop Radicale server")
function btn.cfgvalue(self, section)
- local pid = TOOLS.get_pid(true)
+ local pid = CTRL.get_pid(true)
if pid > 0 then
btn.inputtitle = "PID: " .. pid
btn.inputstyle = "reset"
@@ -183,18 +173,39 @@ local ena = sys:option(Flag, "_enabled")
ena.title = translate("Auto-start")
ena.description = translate("Enable/Disable auto-start of Radicale on system start-up and interface events")
ena.orientation = "horizontal" -- put description under the checkbox
-ena.rmempty = false -- we need write
+ena.rmempty = false -- force write() function
function ena.cfgvalue(self, section)
- return (SYS.init.enabled("radicale")) and "1" or "0"
+ return (SYS.init.enabled("radicale")) and self.enabled or self.disabled
end
function ena.write(self, section, value)
- if value == "1" then
+ if value == self.enabled then
return SYS.init.enable("radicale")
else
return SYS.init.disable("radicale")
end
end
+-- boot_delay ------------------------------------------------------------------
+local bd = sys:option(Value, "boot_delay")
+bd.title = translate("Boot delay")
+bd.description = translate("Delay (in seconds) during system boot before Radicale start")
+ .. [[<br />]]
+ .. translate("During delay ifup-events are not monitored !")
+bd.default = "10"
+function bd.parse(self, section, novld)
+ CTRL.value_parse(self, section, novld)
+end
+function bd.validate(self, value)
+ local val = tonumber(value)
+ if not val then
+ return nil, self.title .. ": " .. translate("Value is not a number")
+ elseif val < 0 or val > 300 then
+ return nil, self.title .. ": " .. translate("Value not between 0 and 300")
+ end
+ return value
+end
+
+
-- cbi-section "Server" -- #####################################################
local srv = m:section( NamedSection, "server", "setting" )
srv.title = translate("Server")
@@ -215,15 +226,17 @@ sh.description = translate("'Hostname:Port' or 'IPv4:Port' or '[IPv6]:Port' Radi
.. [[</strong>]]
sh.placeholder = "0.0.0.0:5232"
sh.rmempty = true
+function sh.parse(self, section, novld)
+ CTRL.value_parse(self, section, novld)
+end
-- realm -----------------------------------------------------------------------
local alm = srv:option( Value, "realm" )
alm.title = translate("Logon message")
alm.description = translate("Message displayed in the client when a password is needed.")
alm.default = "Radicale - Password Required"
-alm.rmempty = false
-function alm.parse(self, section)
- AbstractValue.parse(self, section, "true") -- otherwise unspecific validate error
+function alm.parse(self, section, novld)
+ CTRL.value_parse(self, section, novld)
end
function alm.validate(self, value)
if value then
@@ -232,22 +245,11 @@ function alm.validate(self, value)
return self.default
end
end
-function alm.write(self, section, value)
- if value ~= self.default then
- return self.map:set(section, self.option, value)
- else
- return self.map:del(section, self.option)
- end
-end
-- ssl -------------------------------------------------------------------------
local ssl = srv:option( Flag, "ssl" )
ssl.title = translate("Enable HTTPS")
ssl.description = nil
-ssl.rmempty = false
-function ssl.parse(self, section)
- TOOLS.flag_parse(self, section)
-end
function ssl.write(self, section, value)
if value == "0" then -- delete all if not https enabled
self.map:del(section, "protocol") -- protocol
@@ -273,18 +275,18 @@ prt:value ("PROTOCOL_SSLv3", "SSL v3")
prt:value ("PROTOCOL_TLSv1", "TLS v1")
prt:value ("PROTOCOL_TLSv1_1", "TLS v1.1")
prt:value ("PROTOCOL_TLSv1_2", "TLS v1.2")
+function prt.parse(self, section, novld)
+ CTRL.value_parse(self, section, novld)
+end
-- certificate -----------------------------------------------------------------
local crt = srv:option( Value, "certificate" )
crt.title = translate("Certificate file")
crt.description = translate("Full path and file name of certificate")
crt.placeholder = "/etc/radicale/ssl/server.crt"
-crt.rmempty = false -- force validate/write
crt:depends ("ssl", "1")
-function crt.parse(self, section)
- local _ssl = ssl:formvalue(section) or "0"
- local novld = (_ssl == "0")
- AbstractValue.parse(self, section, novld) -- otherwise unspecific validate error
+function crt.parse(self, section, novld)
+ CTRL.value_parse(self, section, novld)
end
function crt.validate(self, value)
local _ssl = ssl:formvalue(srv.section) or "0"
@@ -295,17 +297,10 @@ function crt.validate(self, value)
if DTYP.file(value) then
return value
else
- return nil, self.title .. " - " .. translate("File not found !")
+ return nil, self.title .. ": " .. translate("File not found !")
end
else
- return nil, self.title .. " - " .. translate("Path/File required !")
- end
-end
-function crt.write(self, section, value)
- if not value or #value == 0 then
- return self.map:del(section, self.option)
- else
- return self.map:set(section, self.option, value)
+ return nil, self.title .. ": " .. translate("Path/File required !")
end
end
@@ -314,12 +309,9 @@ local key = srv:option( Value, "key" )
key.title = translate("Private key file")
key.description = translate("Full path and file name of private key")
key.placeholder = "/etc/radicale/ssl/server.key"
-key.rmempty = false -- force validate/write
key:depends ("ssl", "1")
-function key.parse(self, section)
- local _ssl = ssl:formvalue(section) or "0"
- local novld = (_ssl == "0")
- AbstractValue.parse(self, section, novld) -- otherwise unspecific validate error
+function key.parse(self, section, novld)
+ CTRL.value_parse(self, section, novld)
end
function key.validate(self, value)
local _ssl = ssl:formvalue(srv.section) or "0"
@@ -330,17 +322,10 @@ function key.validate(self, value)
if DTYP.file(value) then
return value
else
- return nil, self.title .. " - " .. translate("File not found !")
+ return nil, self.title .. ": " .. translate("File not found !")
end
else
- return nil, self.title .. " - " .. translate("Path/File required !")
- end
-end
-function key.write(self, section, value)
- if not value or #value == 0 then
- return self.map:del(section, self.option)
- else
- return self.map:set(section, self.option, value)
+ return nil, self.title .. ": " .. translate("Path/File required !")
end
end
@@ -377,6 +362,9 @@ aty:value ("htpasswd", translate("htpasswd file"))
--aty:value ("HTTP", "HTTP") -- The HTTP authentication module relies on the requests module
--aty:value ("remote_user", "remote_user")
--aty:value ("custom", translate("custom"))
+function aty.parse(self, section, novld)
+ CTRL.value_parse(self, section, novld)
+end
function aty.write(self, section, value)
if value ~= "htpasswd" then
self.map:del(section, "htpasswd_encryption")
@@ -403,9 +391,12 @@ hte:value ("crypt", translate("crypt"))
hte:value ("plain", translate("plain"))
hte:value ("sha1", translate("SHA-1"))
hte:value ("ssha", translate("salted SHA-1"))
+function hte.parse(self, section, novld)
+ CTRL.value_parse(self, section, novld)
+end
-- htpasswd_file (dummy) -------------------------------------------------------
-local htf = aut:option( DummyValue, "_htf" )
+local htf = aut:option( Value, "_htf" )
htf.title = translate("htpasswd file")
htf.description = [[<strong>]]
.. translate("Read only!")
@@ -416,9 +407,6 @@ htf.description = [[<strong>]]
.. [[">]]
.. translate("To edit the file follow this link!")
.. [[</a>]]
-htf.keylist = {} -- required by template
-htf.vallist = {} -- required by template
-htf.template = "radicale/ro_value"
htf.readonly = true
htf:depends ("type", "htpasswd")
function htf.cfgvalue()
@@ -448,6 +436,9 @@ rty:value ("owner_only", translate("Full access for Owner only") )
rty:value ("owner_write", translate("Owner allow write, authenticated users allow read") )
rty:value ("from_file", translate("Rights are based on a regexp-based file") )
--rty:value ("custom", "Custom handler")
+function rty.parse(self, section, novld)
+ CTRL.value_parse(self, section, novld)
+end
function rty.write(self, section, value)
if value ~= "custom" then
self.map:del(section, "custom_handler")
@@ -460,7 +451,7 @@ function rty.write(self, section, value)
end
-- from_file (dummy) -----------------------------------------------------------
-local rtf = rig:option( DummyValue, "_rtf" )
+local rtf = rig:option( Value, "_rtf" )
rtf.title = translate("RegExp file")
rtf.description = [[<strong>]]
.. translate("Read only!")
@@ -471,9 +462,6 @@ rtf.description = [[<strong>]]
.. [[">]]
.. translate("To edit the file follow this link!")
.. [[</a>]]
-rtf.keylist = {} -- required by template
-rtf.vallist = {} -- required by template
-rtf.template = "radicale/ro_value"
rtf.readonly = true
rtf:depends ("type", "from_file")
function rtf.cfgvalue()
@@ -501,6 +489,9 @@ sty:value ("filesystem", translate("File-system"))
--sty:value ("multifilesystem", translate("") )
--sty:value ("database", translate("Database") )
--sty:value ("custom", translate("Custom") )
+function sty.parse(self, section, novld)
+ CTRL.value_parse(self, section, novld)
+end
function sty.write(self, section, value)
if value ~= "filesystem" then
self.map:del(section, "filesystem_folder")
@@ -516,13 +507,10 @@ end
local sfi = sto:option( Value, "filesystem_folder" )
sfi.title = translate("Directory")
sfi.description = nil
-sfi.default = "/srv/radicale"
-sfi.rmempty = false -- force validate/write
+sfi.placeholder = "/srv/radicale"
sfi:depends ("type", "filesystem")
-function sfi.parse(self, section)
- local _typ = sty:formvalue(sto.section) or ""
- local novld = (_typ ~= "filesystem")
- AbstractValue.parse(self, section, novld) -- otherwise unspecific validate error
+function sfi.parse(self, section, novld)
+ CTRL.value_parse(self, section, novld)
end
function sfi.validate(self, value)
local _typ = sty:formvalue(sto.section) or ""
@@ -533,10 +521,10 @@ function sfi.validate(self, value)
if DTYP.directory(value) then
return value
else
- return nil, self.title .. " - " .. translate("Directory not exists/found !")
+ return nil, self.title .. ": " .. translate("Directory not exists/found !")
end
else
- return nil, self.title .. " - " .. translate("Directory required !")
+ return nil, self.title .. ": " .. translate("Directory required !")
end
end
@@ -562,6 +550,9 @@ lco:value ("INFO", translate("Info") )
lco:value ("WARNING", translate("Warning") )
lco:value ("ERROR", translate("Error") )
lco:value ("CRITICAL", translate("Critical") )
+function lco.parse(self, section, novld)
+ CTRL.value_parse(self, section, novld)
+end
function lco.write(self, section, value)
if value ~= self.default then
return self.map:set(section, self.option, value)
@@ -581,6 +572,9 @@ lsl:value ("INFO", translate("Info") )
lsl:value ("WARNING", translate("Warning") )
lsl:value ("ERROR", translate("Error") )
lsl:value ("CRITICAL", translate("Critical") )
+function lsl.parse(self, section, novld)
+ CTRL.value_parse(self, section, novld)
+end
function lsl.write(self, section, value)
if value ~= self.default then
return self.map:set(section, self.option, value)
@@ -600,6 +594,9 @@ lfi:value ("INFO", translate("Info") )
lfi:value ("WARNING", translate("Warning") )
lfi:value ("ERROR", translate("Error") )
lfi:value ("CRITICAL", translate("Critical") )
+function lfi.parse(self, section, novld)
+ CTRL.value_parse(self, section, novld)
+end
function lfi.write(self, section, value)
if value ~= self.default then
return self.map:set(section, self.option, value)
@@ -618,12 +615,14 @@ lfp.description = translate("Directory where the rotating log-files are stored")
.. translate("To view latest log file follow this link!")
.. [[</a>]]
lfp.default = "/var/log/radicale"
-function lfp.write(self, section, value)
- if value ~= self.default then
- return self.map:set(section, self.option, value)
- else
- return self.map:del(section, self.option)
+function lfp.parse(self, section, novld)
+ CTRL.value_parse(self, section, novld)
+end
+function lfp.validate(self, value)
+ if not value or (#value < 1) or (value:find("/") ~= 1) then
+ return nil, self.title .. ": " .. translate("no valid path given!")
end
+ return value
end
-- file_maxbytes ---------------------------------------------------------------
@@ -634,23 +633,18 @@ lmb.description = translate("Maximum size of each rotation log-file.")
.. translate("Setting this parameter to '0' will disable rotation of log-file.")
.. [[</strong>]]
lmb.default = "8196"
-lmb.rmempty = false
+function lmb.parse(self, section, novld)
+ CTRL.value_parse(self, section, novld)
+end
function lmb.validate(self, value)
if value then -- otherwise errors in datatype check
if DTYP.uinteger(value) then
return value
else
- return nil, self.title .. " - " .. translate("Value is not an Integer >= 0 !")
+ return nil, self.title .. ": " .. translate("Value is not an Integer >= 0 !")
end
else
- return nil, self.title .. " - " .. translate("Value required ! Integer >= 0 !")
- end
-end
-function lmb.write(self, section, value)
- if value ~= self.default then
- return self.map:set(section, self.option, value)
- else
- return self.map:del(section, self.option)
+ return nil, self.title .. ": " .. translate("Value required ! Integer >= 0 !")
end
end
@@ -662,23 +656,18 @@ lbc.description = translate("Number of backup files of log to create.")
.. translate("Setting this parameter to '0' will disable rotation of log-file.")
.. [[</strong>]]
lbc.default = "1"
-lbc.rmempty = false
+function lbc.parse(self, section, novld)
+ CTRL.value_parse(self, section, novld)
+end
function lbc.validate(self, value)
if value then -- otherwise errors in datatype check
if DTYP.uinteger(value) then
return value
else
- return nil, self.title .. " - " .. translate("Value is not an Integer >= 0 !")
+ return nil, self.title .. ": " .. translate("Value is not an Integer >= 0 !")
end
else
- return nil, self.title .. " - " .. translate("Value required ! Integer >= 0 !")
- end
-end
-function lbc.write(self, section, value)
- if value ~= self.default then
- return self.map:set(section, self.option, value)
- else
- return self.map:del(section, self.option)
+ return nil, self.title .. ": " .. translate("Value required ! Integer >= 0 !")
end
end
@@ -699,14 +688,18 @@ local enr = enc:option( Value, "request" )
enr.title = translate("Response Encoding")
enr.description = translate("Encoding for responding requests.")
enr.default = "utf-8"
-enr.optional = true
+function enr.parse(self, section, novld)
+ CTRL.value_parse(self, section, novld)
+end
-- stock -----------------------------------------------------------------------
local ens = enc:option( Value, "stock" )
ens.title = translate("Storage Encoding")
ens.description = translate("Encoding for storing local collections.")
ens.default = "utf-8"
-ens.optional = true
+function ens.parse(self, section, novld)
+ CTRL.value_parse(self, section, novld)
+end
-- cbi-section "Headers" -- ####################################################
local hea = m:section( NamedSection, "headers", "setting" )
@@ -724,25 +717,32 @@ end
local heo = hea:option( DynamicList, "Access_Control_Allow_Origin" )
heo.title = translate("Access-Control-Allow-Origin")
heo.description = nil
-heo.default = "*"
-heo.optional = true
+function heo.parse(self, section, novld)
+ CTRL.value_parse(self, section, novld)
+end
-- Access_Control_Allow_Methods ------------------------------------------------
local hem = hea:option( DynamicList, "Access_Control_Allow_Methods" )
hem.title = translate("Access-Control-Allow-Methods")
hem.description = nil
-hem.optional = true
+function hem.parse(self, section, novld)
+ CTRL.value_parse(self, section, novld)
+end
-- Access_Control_Allow_Headers ------------------------------------------------
local heh = hea:option( DynamicList, "Access_Control_Allow_Headers" )
heh.title = translate("Access-Control-Allow-Headers")
heh.description = nil
-heh.optional = true
+function heh.parse(self, section, novld)
+ CTRL.value_parse(self, section, novld)
+end
-- Access_Control_Expose_Headers -----------------------------------------------
local hee = hea:option( DynamicList, "Access_Control_Expose_Headers" )
hee.title = translate("Access-Control-Expose-Headers")
hee.description = nil
-hee.optional = true
+function hee.parse(self, section, novld)
+ CTRL.value_parse(self, section, novld)
+end
return m
diff --git a/applications/luci-app-radicale/luasrc/view/radicale/btn_startstop.htm b/applications/luci-app-radicale/luasrc/view/radicale/btn_startstop.htm
index dbf4dddbc..d9ef82214 100644
--- a/applications/luci-app-radicale/luasrc/view/radicale/btn_startstop.htm
+++ b/applications/luci-app-radicale/luasrc/view/radicale/btn_startstop.htm
@@ -4,7 +4,7 @@
// show XHR.poll/XHR.get response on button
function _data2elements(x) {
- var btn = document.getElementById("cbid.radicale._system._startstop");
+ var btn = document.getElementById("cbid.radicale.<%=section%>._startstop");
if ( ! btn ) { return; } // security check
if (x.responseText == "0") {
btn.value = "<%:Start%>";
@@ -21,12 +21,12 @@
function onclick_startstop(id) {
// do start/stop
var btnXHR = new XHR();
- btnXHR.post('<%=url('admin/services/radicale/startstop')%>', { token: '<%=token%>' },
+ btnXHR.post('<%=url([[admin/services/radicale/startstop]])%>', { token: '<%=token%>' },
function(x) { _data2elements(x); }
);
}
- XHR.poll(5, '<%=url('admin/services/radicale/status')%>', null,
+ XHR.poll(5, '<%=url([[admin/services/radicale/status]])%>', null,
function(x, data) { _data2elements(x); }
);
diff --git a/applications/luci-app-radicale/luasrc/view/radicale/ro_value.htm b/applications/luci-app-radicale/luasrc/view/radicale/ro_value.htm
deleted file mode 100644
index 6e05206aa..000000000
--- a/applications/luci-app-radicale/luasrc/view/radicale/ro_value.htm
+++ /dev/null
@@ -1,35 +0,0 @@
-<%+cbi/valueheader%>
- <input type="<%=self.password and 'password" class="cbi-input-password' or 'text" class="cbi-input-text' %>" onchange="cbi_d_update(this.id)"<%=
- attr("name", cbid) .. attr("id", cbid) .. attr("value", self:cfgvalue(section) or self.default) ..
- ifattr(self.size, "size") .. ifattr(self.placeholder, "placeholder") .. ifattr(self.readonly, "readonly")
- %> />
- <% if self.password then %><img src="<%=resource%>/cbi/reload.gif" style="vertical-align:middle" title="<%:Reveal/hide password%>" onclick="var e = document.getElementById('<%=cbid%>'); e.type = (e.type=='password') ? 'text' : 'password';" /><% end %>
- <% if #self.keylist > 0 or self.datatype then -%>
- <script type="text/javascript">//<![CDATA[
- <% if #self.keylist > 0 then -%>
- cbi_combobox_init('<%=cbid%>', {
- <%-
- for i, k in ipairs(self.keylist) do
- -%>
- <%-=string.format("%q", k) .. ":" .. string.format("%q", self.vallist[i])-%>
- <%-if i<#self.keylist then-%>,<%-end-%>
- <%-
- end
- -%>
- }, '<%- if not self.rmempty and not self.optional then -%>
- <%-: -- Please choose -- -%>
- <%- elseif self.placeholder then -%>
- <%-= pcdata(self.placeholder) -%>
- <%- end -%>', '
- <%- if self.combobox_manual then -%>
- <%-=self.combobox_manual-%>
- <%- else -%>
- <%-: -- custom -- -%>
- <%- end -%>');
- <%- end %>
- <% if self.datatype then -%>
- cbi_validate_field('<%=cbid%>', <%=tostring((self.optional or self.rmempty) == true)%>, '<%=self.datatype:gsub("'", "\\'")%>');
- <%- end %>
- //]]></script>
- <% end -%>
-<%+cbi/valuefooter%>
diff --git a/applications/luci-app-radicale/luasrc/view/radicale/tabmap_nsections.htm b/applications/luci-app-radicale/luasrc/view/radicale/tabmap_nsections.htm
new file mode 100644
index 000000000..45fe60cc8
--- /dev/null
+++ b/applications/luci-app-radicale/luasrc/view/radicale/tabmap_nsections.htm
@@ -0,0 +1,49 @@
+<%- if firstmap and messages then local msg; for _, msg in ipairs(messages) do -%>
+ <div class="errorbox"><%=pcdata(msg)%></div>
+<%- end end -%>
+
+<%-+cbi/apply_xhr-%>
+
+<div class="cbi-map" id="cbi-<%=self.config%>">
+ <% if self.title and #self.title > 0 then %><h2 name="content"><%=self.title%></h2><% end %>
+ <% if self.description and #self.description > 0 then %><div class="cbi-map-descr"><%=self.description%></div><% end %>
+ <%- if firstmap and applymap then cbi_apply_xhr(self.config, parsechain, redirect) end -%>
+
+ <% if self.tabbed then %>
+ <ul class="cbi-tabmenu map">
+ <%- self.selected_tab = luci.http.formvalue("tab.m-" .. self.config) %>
+ <% for i, section in ipairs(self.children) do %>
+ <%- if not self.selected_tab then self.selected_tab = section.sectiontype end %>
+ <li id="tab.m-<%=self.config%>.<%=section.section or section.sectiontype%>" class="cbi-tab<%=(section.sectiontype == self.selected_tab) and '' or '-disabled'%>">
+ <a onclick="this.blur(); return cbi_t_switch('m-<%=self.config%>', '<%=section.section or section.sectiontype%>')" href="<%=REQUEST_URI%>?tab.m-<%=self.config%>=<%=section.section or section.sectiontype%>"><%=section.title or section.section or section.sectiontype %></a>
+ <% if section.sectiontype == self.selected_tab then %><input type="hidden" id="tab.m-<%=self.config%>" name="tab.m-<%=self.config%>" value="<%=section.section or section.sectiontype%>" /><% end %>
+ </li>
+ <% end %>
+ </ul>
+ <br />
+ <% for i, section in ipairs(self.children) do %>
+ <div class="cbi-tabcontainer" id="container.m-<%=self.config%>.<%=section.section or section.sectiontype%>"<% if section.sectiontype ~= self.selected_tab then %> style="display:none"<% end %>>
+ <% section:render() %>
+ </div>
+ <script type="text/javascript">cbi_t_add('m-<%=self.config%>', '<%=section.section or section.sectiontype%>')</script>
+ <% end %>
+
+ <% else %>
+ <%- self:render_children() %>
+ <% end %>
+
+ <% if not self.save then -%>
+ <div class="cbi-section-error">
+ <% for _, section in ipairs(self.children) do %>
+ <% if section.error and section.error[section.section] then -%>
+ <ul><li>
+ <%:One or more missing/invalid fields on tab%>:&nbsp;<%=section.title or section.section or section.sectiontype%>
+ </li></ul>
+ <%- end %>
+ <% end %>
+ </div>
+ <%- end %>
+
+ <br />
+
+</div>