diff options
author | Christian Schoenebeck <christian.schoenebeck@gmail.com> | 2016-02-07 09:30:29 +0100 |
---|---|---|
committer | Christian Schoenebeck <christian.schoenebeck@gmail.com> | 2016-02-07 09:30:29 +0100 |
commit | 026ac8d033f4c0a65b0c8d121d1a2a86b2bd1ee1 (patch) | |
tree | 767f1d7e3a4614b35ace2a9377ae63139172a887 /applications/luci-app-radicale/luasrc | |
parent | 26d009aff924f8253533ae6a303f61fe0d35d2f4 (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')
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"> ]] + .. 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 @@ -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"> ]] - .. 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 @@ -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%>: <%=section.title or section.section or section.sectiontype%> + </li></ul> + <%- end %> + <% end %> + </div> + <%- end %> + + <br /> + +</div> |