diff options
Diffstat (limited to 'applications/luci-app-radicale/luasrc/controller/radicale.lua')
-rw-r--r-- | applications/luci-app-radicale/luasrc/controller/radicale.lua | 298 |
1 files changed, 170 insertions, 128 deletions
diff --git a/applications/luci-app-radicale/luasrc/controller/radicale.lua b/applications/luci-app-radicale/luasrc/controller/radicale.lua index 10ec1fe545..0be433a48d 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 |