gluon-web-model: add dependency support for sections

- Add a dependency array to section template
- Lift dependency handling from AbstractValue to Node
- Split resolve_depends() into a function handling dependencies of a
  node itself (resolve_node_depends()), which now is used for all nodes,
  and the existing recursive descent through the node tree
- When any node's dependencies are not satisfied, all descendents are
  reset recursively, meaning that an option in a section that is hidden
  will always be ignored, as if the option's own dependencies were
  unsatisfied

Fixes: #1349
This commit is contained in:
Matthias Schiffer 2020-05-09 01:12:51 +02:00 committed by Andreas Ziegler
parent 35950b44b7
commit 11a3b56617
2 changed files with 63 additions and 47 deletions

View File

@ -1,4 +1,4 @@
<fieldset class="gluon-section"> <fieldset class="gluon-section" id="<%=id%>" data-index="<%=self.index%>"<%= attr("data-depends", self:deplist()) %>>
<% if self.title and #self.title > 0 then -%> <% if self.title and #self.title > 0 then -%>
<legend><%|self.title%></legend> <legend><%|self.title%></legend>
<%- end %> <%- end %>

View File

@ -47,6 +47,7 @@ M.Node = Node
function Node:__init__(name, title, description) function Node:__init__(name, title, description)
self.children = {} self.children = {}
self.deps = {}
self.title = title or "" self.title = title or ""
self.description = description or "" self.description = description or ""
self.name = name self.name = name
@ -71,6 +72,12 @@ function Node:id()
return prefix.."."..self:id_suffix() return prefix.."."..self:id_suffix()
end end
function Node:reset()
for _, child in ipairs(self.children) do
child:reset()
end
end
function Node:parse(http) function Node:parse(http)
for _, child in ipairs(self.children) do for _, child in ipairs(self.children) do
child:parse(http) child:parse(http)
@ -80,8 +87,8 @@ end
function Node:render(renderer, scope) function Node:render(renderer, scope)
if self.template then if self.template then
local env = setmetatable({ local env = setmetatable({
self = self, self = self,
id = self:id(), id = self:id(),
scope = scope, scope = scope,
}, {__index = scope}) }, {__index = scope})
renderer.render(self.template, env, self.package) renderer.render(self.template, env, self.package)
@ -94,14 +101,63 @@ function Node:render_children(renderer, scope)
end end
end end
function Node:depends(field, value)
local deps
if instanceof(field, Node) then
deps = { [field] = value }
else
deps = field
end
table.insert(self.deps, deps)
end
function Node:deplist(deplist)
local deps = {}
for _, d in ipairs(deplist or self.deps) do
local a = {}
for k, v in pairs(d) do
a[k:id()] = v
end
table.insert(deps, a)
end
if next(deps) then
return deps
end
end
function Node:resolve_depends() function Node:resolve_depends()
local updated = false local updated = self:resolve_node_depends()
for _, node in ipairs(self.children) do for _, node in ipairs(self.children) do
updated = updated or node:resolve_depends() updated = updated or node:resolve_depends()
end end
return updated return updated
end end
function Node:resolve_node_depends()
if #self.deps == 0 then
return false
end
for _, d in ipairs(self.deps) do
local valid = true
for k, v in pairs(d) do
if k.state ~= M.FORM_VALID or k.data ~= v then
valid = false
break
end
end
if valid then return false end
end
self:reset()
return true
end
function Node:handle() function Node:handle()
for _, node in ipairs(self.children) do for _, node in ipairs(self.children) do
node:handle() node:handle()
@ -123,7 +179,6 @@ M.AbstractValue = AbstractValue
function AbstractValue:__init__(...) function AbstractValue:__init__(...)
Node.__init__(self, ...) Node.__init__(self, ...)
self.deps = {}
self.default = nil self.default = nil
self.size = nil self.size = nil
@ -134,33 +189,6 @@ function AbstractValue:__init__(...)
self.state = M.FORM_NODATA self.state = M.FORM_NODATA
end end
function AbstractValue:depends(field, value)
local deps
if instanceof(field, Node) then
deps = { [field] = value }
else
deps = field
end
table.insert(self.deps, deps)
end
function AbstractValue:deplist(deplist)
local deps = {}
for _, d in ipairs(deplist or self.deps) do
local a = {}
for k, v in pairs(d) do
a[k:id()] = v
end
table.insert(deps, a)
end
if next(deps) then
return deps
end
end
function AbstractValue:defaultvalue() function AbstractValue:defaultvalue()
return self.default return self.default
end end
@ -214,24 +242,12 @@ function AbstractValue:parse(http)
self.state = M.FORM_VALID self.state = M.FORM_VALID
end end
function AbstractValue:resolve_depends() function AbstractValue:resolve_node_depends()
if self.state == M.FORM_NODATA or #self.deps == 0 then if self.state == M.FORM_NODATA then
return false return false
end end
for _, d in ipairs(self.deps) do return Node.resolve_node_depends(self)
local valid = true
for k, v in pairs(d) do
if k.state ~= M.FORM_VALID or k.data ~= v then
valid = false
break
end
end
if valid then return false end
end
self:reset()
return true
end end
function AbstractValue:validate() function AbstractValue:validate()