model.lua 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. -- Copyright 2008 Steven Barth <steven@midlink.org>
  2. -- Copyright 2017 Matthias Schiffer <mschiffer@universe-factory.net>
  3. -- Licensed to the public under the Apache License 2.0.
  4. module("gluon.web.model", package.seeall)
  5. local util = require("gluon.web.util")
  6. local fs = require("nixio.fs")
  7. local datatypes = require("gluon.web.model.datatypes")
  8. local dispatcher = require("gluon.web.dispatcher")
  9. local class = util.class
  10. local instanceof = util.instanceof
  11. FORM_NODATA = 0
  12. FORM_VALID = 1
  13. FORM_INVALID = -1
  14. -- Loads a model from given file, creating an environment and returns it
  15. function load(name, renderer)
  16. local modeldir = util.libpath() .. "/model/"
  17. if not fs.access(modeldir..name..".lua") then
  18. error("Model '" .. name .. "' not found!")
  19. end
  20. local func = assert(loadfile(modeldir..name..".lua"))
  21. local env = {
  22. translate=renderer.translate,
  23. translatef=renderer.translatef,
  24. }
  25. setfenv(func, setmetatable(env, {__index =
  26. function(tbl, key)
  27. return _M[key] or _G[key]
  28. end
  29. }))
  30. local models = { func() }
  31. for k, model in ipairs(models) do
  32. if not instanceof(model, Node) then
  33. error("model definition returned an invalid model object")
  34. end
  35. model.index = k
  36. end
  37. return models
  38. end
  39. local function parse_datatype(code)
  40. local match, arg, arg2
  41. match, arg, arg2 = code:match('^([^%(]+)%(([^,]+),([^%)]+)%)$')
  42. if match then
  43. return datatypes[match], {arg, arg2}
  44. end
  45. match, arg = code:match('^([^%(]+)%(([^%)]+)%)$')
  46. if match then
  47. return datatypes[match], {arg}
  48. end
  49. return datatypes[code], {}
  50. end
  51. local function verify_datatype(dt, value)
  52. if dt then
  53. local c, args = parse_datatype(dt)
  54. assert(c, "Invalid datatype")
  55. return c(value, unpack(args))
  56. end
  57. return true
  58. end
  59. Node = class()
  60. function Node:__init__(title, description, name)
  61. self.children = {}
  62. self.title = title or ""
  63. self.description = description or ""
  64. self.name = name
  65. self.index = nil
  66. self.parent = nil
  67. end
  68. function Node:append(obj)
  69. table.insert(self.children, obj)
  70. obj.index = #self.children
  71. obj.parent = self
  72. end
  73. function Node:id_suffix()
  74. return self.name or (self.index and tostring(self.index)) or '_'
  75. end
  76. function Node:id()
  77. local prefix = self.parent and self.parent:id() or "id"
  78. return prefix.."."..self:id_suffix()
  79. end
  80. function Node:parse(http)
  81. for _, child in ipairs(self.children) do
  82. child:parse(http)
  83. end
  84. end
  85. function Node:render(renderer, scope)
  86. if self.template then
  87. local env = setmetatable({
  88. self = self,
  89. id = self:id(),
  90. scope = scope,
  91. }, {__index = scope})
  92. renderer.render(self.template, env)
  93. end
  94. end
  95. function Node:render_children(renderer, scope)
  96. for _, node in ipairs(self.children) do
  97. node:render(renderer, scope)
  98. end
  99. end
  100. function Node:resolve_depends()
  101. local updated = false
  102. for _, node in ipairs(self.children) do
  103. update = updated or node:resolve_depends()
  104. end
  105. return updated
  106. end
  107. function Node:handle()
  108. for _, node in ipairs(self.children) do
  109. node:handle()
  110. end
  111. end
  112. Template = class(Node)
  113. function Template:__init__(template)
  114. Node.__init__(self)
  115. self.template = template
  116. end
  117. Form = class(Node)
  118. function Form:__init__(...)
  119. Node.__init__(self, ...)
  120. self.template = "model/form"
  121. end
  122. function Form:submitstate(http)
  123. return http:getenv("REQUEST_METHOD") == "POST" and http:formvalue(self:id()) ~= nil
  124. end
  125. function Form:parse(http)
  126. if not self:submitstate(http) then
  127. self.state = FORM_NODATA
  128. return
  129. end
  130. Node.parse(self, http)
  131. while self:resolve_depends() do end
  132. for _, s in ipairs(self.children) do
  133. for _, v in ipairs(s.children) do
  134. if v.state == FORM_INVALID then
  135. self.state = FORM_INVALID
  136. return
  137. end
  138. end
  139. end
  140. self.state = FORM_VALID
  141. end
  142. function Form:handle()
  143. if self.state == FORM_VALID then
  144. Node.handle(self)
  145. self:write()
  146. end
  147. end
  148. function Form:write()
  149. end
  150. function Form:section(t, ...)
  151. assert(instanceof(t, Section), "class must be a descendent of Section")
  152. local obj = t(...)
  153. self:append(obj)
  154. return obj
  155. end
  156. Section = class(Node)
  157. function Section:__init__(...)
  158. Node.__init__(self, ...)
  159. self.fields = {}
  160. self.template = "model/section"
  161. end
  162. function Section:option(t, option, title, description, ...)
  163. assert(instanceof(t, AbstractValue), "class must be a descendant of AbstractValue")
  164. local obj = t(title, description, option, ...)
  165. self:append(obj)
  166. self.fields[option] = obj
  167. return obj
  168. end
  169. AbstractValue = class(Node)
  170. function AbstractValue:__init__(option, ...)
  171. Node.__init__(self, option, ...)
  172. self.deps = {}
  173. self.default = nil
  174. self.size = nil
  175. self.optional = false
  176. self.template = "model/valuewrapper"
  177. self.state = FORM_NODATA
  178. end
  179. function AbstractValue:depends(field, value)
  180. local deps
  181. if instanceof(field, Node) then
  182. deps = { [field] = value }
  183. else
  184. deps = field
  185. end
  186. table.insert(self.deps, deps)
  187. end
  188. function AbstractValue:deplist(section, deplist)
  189. local deps = {}
  190. for _, d in ipairs(deplist or self.deps) do
  191. local a = {}
  192. for k, v in pairs(d) do
  193. a[k:id()] = v
  194. end
  195. table.insert(deps, a)
  196. end
  197. if next(deps) then
  198. return deps
  199. end
  200. end
  201. function AbstractValue:defaultvalue()
  202. return self.default
  203. end
  204. function AbstractValue:formvalue(http)
  205. return http:formvalue(self:id())
  206. end
  207. function AbstractValue:cfgvalue()
  208. if self.state == FORM_NODATA then
  209. return self:defaultvalue()
  210. else
  211. return self.data
  212. end
  213. end
  214. function AbstractValue:add_error(type, msg)
  215. self.error = msg or type
  216. if type == "invalid" then
  217. self.tag_invalid = true
  218. elseif type == "missing" then
  219. self.tag_missing = true
  220. end
  221. self.state = FORM_INVALID
  222. end
  223. function AbstractValue:reset()
  224. self.error = nil
  225. self.tag_invalid = nil
  226. self.tag_missing = nil
  227. self.data = nil
  228. self.state = FORM_NODATA
  229. end
  230. function AbstractValue:parse(http)
  231. self.data = self:formvalue(http)
  232. local ok, err = self:validate()
  233. if not ok then
  234. if type(self.data) ~= "string" or #self.data > 0 then
  235. self:add_error("invalid", err)
  236. else
  237. self:add_error("missing", err)
  238. end
  239. return
  240. end
  241. self.state = FORM_VALID
  242. end
  243. function AbstractValue:resolve_depends()
  244. if self.state == FORM_NODATA or #self.deps == 0 then
  245. return false
  246. end
  247. for _, d in ipairs(self.deps) do
  248. local valid = true
  249. for k, v in pairs(d) do
  250. if k.state ~= FORM_VALID or k.data ~= v then
  251. valid = false
  252. break
  253. end
  254. end
  255. if valid then return false end
  256. end
  257. self:reset()
  258. return true
  259. end
  260. function AbstractValue:validate()
  261. if self.data and verify_datatype(self.datatype, self.data) then
  262. return true
  263. end
  264. if type(self.data) == "string" and #self.data == 0 then
  265. self.data = nil
  266. end
  267. if self.data == nil then
  268. return self.optional
  269. end
  270. return false
  271. end
  272. function AbstractValue:handle()
  273. if self.state == FORM_VALID then
  274. self:write(self.data)
  275. end
  276. end
  277. function AbstractValue:write(value)
  278. end
  279. Value = class(AbstractValue)
  280. function Value:__init__(...)
  281. AbstractValue.__init__(self, ...)
  282. self.subtemplate = "model/value"
  283. end
  284. Flag = class(AbstractValue)
  285. function Flag:__init__(...)
  286. AbstractValue.__init__(self, ...)
  287. self.subtemplate = "model/fvalue"
  288. self.default = false
  289. end
  290. function Flag:formvalue(http)
  291. return http:formvalue(self:id()) ~= nil
  292. end
  293. function Flag:validate()
  294. return true
  295. end
  296. ListValue = class(AbstractValue)
  297. function ListValue:__init__(...)
  298. AbstractValue.__init__(self, ...)
  299. self.subtemplate = "model/lvalue"
  300. self.size = 1
  301. self.widget = "select"
  302. self.keys = {}
  303. self.entry_list = {}
  304. end
  305. function ListValue:value(key, val, ...)
  306. if self.keys[key] then
  307. return
  308. end
  309. val = val or key
  310. self.keys[key] = true
  311. table.insert(self.entry_list, {
  312. key = tostring(key),
  313. value = tostring(val),
  314. deps = {...},
  315. })
  316. end
  317. function ListValue:entries()
  318. local ret = {unpack(self.entry_list)}
  319. if self:cfgvalue() == nil or self.optional then
  320. table.insert(ret, 1, {
  321. key = '',
  322. value = '',
  323. deps = {},
  324. })
  325. end
  326. return ret
  327. end
  328. function ListValue:validate()
  329. if self.keys[self.data] then
  330. return true
  331. end
  332. if type(self.data) == "string" and #self.data == 0 then
  333. self.data = nil
  334. end
  335. if self.data == nil then
  336. return self.optional
  337. end
  338. return false
  339. end
  340. DynamicList = class(AbstractValue)
  341. function DynamicList:__init__(...)
  342. AbstractValue.__init__(self, ...)
  343. self.subtemplate = "model/dynlist"
  344. end
  345. function DynamicList:defaultvalue()
  346. local value = self.default
  347. if type(value) == "table" then
  348. return value
  349. else
  350. return { value }
  351. end
  352. end
  353. function DynamicList:formvalue(http)
  354. return http:formvaluetable(self:id())
  355. end
  356. function DynamicList:validate()
  357. if self.data == nil then
  358. self.data = {}
  359. end
  360. if #self.data == 0 then
  361. return self.optional
  362. end
  363. for _, v in ipairs(self.data) do
  364. if not verify_datatype(self.datatype, v) then
  365. return false
  366. end
  367. end
  368. return true
  369. end
  370. TextValue = class(AbstractValue)
  371. function TextValue:__init__(...)
  372. AbstractValue.__init__(self, ...)
  373. self.subtemplate = "model/tvalue"
  374. end