--- 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)
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"> ]]
+ .. I18N.translate("Software package '%s' is not installed." % srv_name)
+ .. [[</font><br /><br /> ]]
+ .. I18N.translate("required") .. [[: ]] .. srv_name .. [[ ]] .. srv_ver_min
+ .. [[<br /><br /> ]]
+ .. [[<a href="]] .. DISP.build_url("admin", "system", "packages") ..[[">]]
+ .. I18N.translate("Please install current version !")
+ .. [[</a><br /> </strong></h3>]]
+ else
+ return [[<h3><strong><br /><font color="red"> ]]
+ .. I18N.translate("Software package '%s' is outdated." % srv_name)
+ .. [[</font><br /><br /> ]]
+ .. I18N.translate("installed") .. [[: ]] .. srv_name .. [[ ]] .. service_version()
+ .. [[<br /> ]]
+ .. I18N.translate("required") .. [[: ]] .. srv_name .. [[ ]] .. srv_ver_min
+ .. [[<br /><br /> ]]
+ .. [[<a href="]] .. DISP.build_url("admin", "system", "packages") ..[[">]]
+ .. I18N.translate("Please update to current version !")
+ .. [[</a><br /> </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
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
--- 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 -- ################################################
-- 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
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"> ]]
- .. translate("Software package '" .. TOOLS.service_name() .. "' is not installed.")
- .. [[</font><br /><br /> ]]
- .. translate("required") .. [[: ]] .. TOOLS.service_name() .. [[ ]] .. TOOLS.service_required()
- .. [[<br /><br /> ]]
- .. [[<a href="]] .. DISP.build_url("admin", "system", "packages") ..[[">]]
- .. translate("Please install current version !")
- .. [[</a><br /> </strong></h3>]]
- else
- v.value = [[<h3><strong><br /><font color="red"> ]]
- .. translate("Software package '" .. TOOLS.service_name() .. "' is outdated.")
- .. [[</font><br /><br /> ]]
- .. translate("installed") .. [[: ]] .. TOOLS.service_name() .. [[ ]] .. TOOLS.service_installed()
- .. [[<br /> ]]
- .. translate("required") .. [[: ]] .. TOOLS.service_name() .. [[ ]] .. TOOLS.service_required()
- .. [[<br /><br /> ]]
- .. [[<a href="]] .. DISP.build_url("admin", "system", "packages") ..[[">]]
- .. translate("Please update to current version !")
- .. [[</a><br /> </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
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 -----------------------------------------------------------
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"
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")
.. [[</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
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
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"
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
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"
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
--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")
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!")
.. [[">]]
.. 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()
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")
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!")
.. [[">]]
.. 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()
--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")
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 ""
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
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)
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)
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)
.. 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 ---------------------------------------------------------------
.. 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
.. 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
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" )
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