* CBI update

* Added some configuration pages
* Introduced contact site
* Introduced luci UCI config file
This commit is contained in:
Steven Barth 2008-03-26 20:55:14 +00:00
parent 5f9910566d
commit 68d142e792
30 changed files with 345 additions and 129 deletions

View file

@ -1,14 +1,14 @@
LUAC = luac LUAC = luac
LUAC_OPTIONS = -s LUAC_OPTIONS = -s
FILES = ffluci/config.lua FILES =
CFILES = ffluci/util.lua ffluci/http.lua \ CFILES = ffluci/util.lua ffluci/http.lua ffluci/fs.lua \
ffluci/fs.lua ffluci/i18n.lua ffluci/model/uci.lua \ ffluci/model/uci.lua ffluci/config.lua ffluci/i18n.lua \
ffluci/template.lua ffluci/cbi.lua ffluci/dispatcher.lua \ ffluci/template.lua ffluci/cbi.lua ffluci/dispatcher.lua \
ffluci/menu.lua ffluci/init.lua ffluci/sys.lua ffluci/menu.lua ffluci/init.lua ffluci/sys.lua
DIRECTORIES = dist/ffluci/model dist/ffluci/controller dist/ffluci/i18n dist/ffluci/view DIRECTORIES = dist/ffluci/model/cbi dist/ffluci/controller dist/ffluci/i18n dist/ffluci/view
INFILES = $(CFILES:%=src/%) INFILES = $(CFILES:%=src/%)
OUTFILE = ffluci/init.lua OUTFILE = ffluci/init.lua
@ -28,6 +28,7 @@ dist:
cp src/ffluci/controller/* dist/ffluci/controller/ -R cp src/ffluci/controller/* dist/ffluci/controller/ -R
cp src/ffluci/i18n/* dist/ffluci/i18n/ cp src/ffluci/i18n/* dist/ffluci/i18n/
cp src/ffluci/view/* dist/ffluci/view/ -R cp src/ffluci/view/* dist/ffluci/view/ -R
cp src/ffluci/model/cbi/* dist/ffluci/model/cbi/ -R
examples: examples:
cp examples/* dist/ -R cp examples/* dist/ -R

9
README
View file

@ -1,6 +1,6 @@
FFLuCI - Freifunk Lua Configuration Interface FFLuCI - Freifunk Lua Configuration Interface
This is a leightweight MVC-Webframework for small embedded device. This is a leightweight MVC-Webframework for small embedded devices.
It uses the the Lua programming language and relies on Haserl. It uses the the Lua programming language and relies on Haserl.
It consists of several parts: It consists of several parts:
@ -25,6 +25,13 @@ Template engine
> See src/ffluci/template.lua for details > See src/ffluci/template.lua for details
> See src/view/ for examples > See src/view/ for examples
Configuration Bind Interface (CBI)
Generates and validates XHTML-Forms out of an UCI model description
Makes it very easy to create webinterface pages that manipulate UCI files
> See src/ffluci/cbi.lua
i18n Translation support i18n Translation support

20
contrib/ffluci.uci Normal file
View file

@ -0,0 +1,20 @@
config core main
option lang de
option mediaurlbase /ffluci/media
config public contact
option nickname -
option name -
option mail -
option phone -
option location -
option geo -
option note -
config event uci_oncommit
option network "/etc/init.d/network restart"
option wireless "/etc/init.d/network restart"
option olsrd "/etc/init.d/olsrd restart"
option dhcp "/etc/init.d/dhcp restart"

View file

@ -9,6 +9,7 @@ h1 {
margin: 0%; margin: 0%;
font-size: 1.4em; font-size: 1.4em;
font-weight: bold; font-weight: bold;
margin-bottom: 0.5em;
} }
h2 { h2 {
@ -17,6 +18,10 @@ h2 {
font-weight: bold; font-weight: bold;
} }
h3 {
margin: 0%;
}
#header { #header {
padding: 0.2em; padding: 0.2em;
height: 4.5em; height: 4.5em;
@ -156,6 +161,19 @@ h2 {
display: none; display: none;
} }
.inline {
display: inline;
}
code {
display: block;
background: #f7f7f7;
border: 1px solid #d7d7d7;
margin: 1em 1.75em;
padding: 1em;
overflow: auto;
white-space: pre;
}
.cbi-section { .cbi-section {
margin-top: 1em; margin-top: 1em;
@ -166,24 +184,22 @@ h2 {
} }
.cbi-value-title { .cbi-value-title {
font-weight: bold;
line-height: 1.75em; line-height: 1.75em;
} }
.cbi-value-field { .cbi-value-field {
text-align: right; margin-left: 10em;
vertical-align: center; text-align: center;
line-height: 1.75em; line-height: 1.75em;
} }
.cbi-value-description { .cbi-value-field input, .cbi-value-field select, .cbi-optionals select, .cbi-optionals input {
clear: both; font-size: 0.8em;
font-style: italic;
font-size: 0.8em;
} }
.cbi-value { .cbi-value-description {
margin-bottom: 1em; font-style: italic;
font-size: 0.8em;
} }
.cbi-form-separator { .cbi-form-separator {
@ -191,9 +207,14 @@ h2 {
} }
.cbi-section-node { .cbi-section-node {
margin-top: 1em; display: block;
border: none; background: #f7f7f7;
background-color: #eeeeee; border: 1px solid #d7d7d7;
overflow: auto;
}
.cbi-section-node h3 {
margin-bottom: 0.5em;
} }
.cbi-error { .cbi-error {
@ -205,11 +226,6 @@ h2 {
margin-top: 2em; margin-top: 2em;
} }
.cbi-optionals select {
height: 1.5em;
width: 20em;
}
.cbi-optionals option { .cbi-optionals option {
font-size: 0.8em; font-size: 0.8em;
} }

View file

@ -0,0 +1,3 @@
.contact th {
text-align: left;
}

View file

@ -39,7 +39,9 @@ define Package/ffluci/install
$(CP) $(PKG_BUILD_DIR)/contrib/media $(1)/www/ffluci/ -R $(CP) $(PKG_BUILD_DIR)/contrib/media $(1)/www/ffluci/ -R
$(INSTALL_BIN) $(PKG_BUILD_DIR)/contrib/ffluci $(1)/www/cgi-bin $(INSTALL_BIN) $(PKG_BUILD_DIR)/contrib/ffluci $(1)/www/cgi-bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/contrib/index.cgi $(1)/www/cgi-bin $(INSTALL_BIN) $(PKG_BUILD_DIR)/contrib/index.cgi $(1)/www/cgi-bin
$(CP) $(PKG_BUILD_DIR)/contrib/ffluci.uci $(1)/etc/config/luci
$(CP) -a ./ipkg/ffluci.postinst $(1)/CONTROL/postinst $(CP) -a ./ipkg/ffluci.postinst $(1)/CONTROL/postinst
$(CP) -a ./ipkg/conffiles $(1)/CONTROL/conffiles
endef endef
$(eval $(call BuildPackage,ffluci)) $(eval $(call BuildPackage,ffluci))

View file

@ -0,0 +1 @@
/etc/config/luci

View file

@ -168,7 +168,9 @@ end
-- UCI get (cached) -- UCI get (cached)
function Map.get(self, section, option) function Map.get(self, section, option)
if option and self.ucidata[section] then if not section then
return self.ucidata
elseif option and self.ucidata[section] then
return self.ucidata[section][option] return self.ucidata[section][option]
else else
return self.ucidata[section] return self.ucidata[section]
@ -188,8 +190,8 @@ function AbstractSection.__init__(self, map, sectiontype, ...)
self.config = map.config self.config = map.config
self.optionals = {} self.optionals = {}
self.addremove = true
self.optional = true self.optional = true
self.addremove = false
self.dynamic = false self.dynamic = false
end end
@ -210,14 +212,16 @@ function AbstractSection.parse_optionals(self, section)
return return
end end
self.optionals[section] = {}
local field = ffluci.http.formvalue("cbi.opt."..self.config.."."..section) local field = ffluci.http.formvalue("cbi.opt."..self.config.."."..section)
for k,v in ipairs(self.children) do for k,v in ipairs(self.children) do
if v.optional and not v:ucivalue(section) then if v.optional and not v:cfgvalue(section) then
if field == v.option then if field == v.option then
self.map:set(section, field, v.default) self.map:set(section, field, v.default)
field = nil field = nil
else else
table.insert(self.optionals, v) table.insert(self.optionals[section], v)
end end
end end
end end
@ -239,7 +243,7 @@ function AbstractSection.parse_dynamic(self, section)
return return
end end
local arr = ffluci.util.clone(self:ucivalue(section)) local arr = ffluci.util.clone(self:cfgvalue(section))
local form = ffluci.http.formvalue("cbid."..self.config.."."..section) local form = ffluci.http.formvalue("cbid."..self.config.."."..section)
if type(form) == "table" then if type(form) == "table" then
for k,v in pairs(form) do for k,v in pairs(form) do
@ -263,10 +267,20 @@ function AbstractSection.parse_dynamic(self, section)
end end
-- Returns the section's UCI table -- Returns the section's UCI table
function AbstractSection.ucivalue(self, section) function AbstractSection.cfgvalue(self, section)
return self.map:get(section) return self.map:get(section)
end end
-- Removes the section
function AbstractSection.remove(self, section)
return self.map:del(section)
end
-- Creates the section
function AbstractSection.create(self, section)
return self.map:set(section, nil, self.sectiontype)
end
--[[ --[[
@ -282,42 +296,33 @@ function NamedSection.__init__(self, map, section, ...)
self.addremove = false self.addremove = false
end end
function NamedSection.parse(self) function NamedSection.parse(self)
local active = self:ucivalue(self.section) local s = self.section
local active = self:cfgvalue(s)
if self.addremove then if self.addremove then
local path = self.config.."."..self.section local path = self.config.."."..s
if active then -- Remove the section if active then -- Remove the section
if ffluci.http.formvalue("cbi.rns."..path) and self:remove() then if ffluci.http.formvalue("cbi.rns."..path) and self:remove(s) then
return return
end end
else -- Create and apply default values else -- Create and apply default values
if ffluci.http.formvalue("cbi.cns."..path) and self:create() then if ffluci.http.formvalue("cbi.cns."..path) and self:create(s) then
for k,v in pairs(self.children) do for k,v in pairs(self.children) do
v:write(self.section, v.default) v:write(s, v.default)
end end
end end
end end
end end
if active then if active then
AbstractSection.parse_dynamic(self, self.section) AbstractSection.parse_dynamic(self, s)
Node.parse(self, self.section) Node.parse(self, s)
AbstractSection.parse_optionals(self, self.section) AbstractSection.parse_optionals(self, s)
end end
end end
-- Removes the section
function NamedSection.remove(self)
return self.map:del(self.section)
end
-- Creates the section
function NamedSection.create(self)
return self.map:set(self.section, nil, self.sectiontype)
end
--[[ --[[
TypedSection - A (set of) configuration section(s) defined by the type TypedSection - A (set of) configuration section(s) defined by the type
@ -385,18 +390,13 @@ function TypedSection.parse(self)
end end
end end
for k, v in pairs(self:ucisections()) do for k, v in pairs(self:cfgsections()) do
AbstractSection.parse_dynamic(self, k) AbstractSection.parse_dynamic(self, k)
Node.parse(self, k) Node.parse(self, k)
AbstractSection.parse_optionals(self, k) AbstractSection.parse_optionals(self, k)
end end
end end
-- Remove a section
function TypedSection.remove(self, name)
return self.map:del(name)
end
-- Render the children -- Render the children
function TypedSection.render_children(self, section) function TypedSection.render_children(self, section)
for k, node in ipairs(self.children) do for k, node in ipairs(self.children) do
@ -405,9 +405,9 @@ function TypedSection.render_children(self, section)
end end
-- Return all matching UCI sections for this TypedSection -- Return all matching UCI sections for this TypedSection
function TypedSection.ucisections(self) function TypedSection.cfgsections(self)
local sections = {} local sections = {}
for k, v in pairs(self.map.ucidata) do for k, v in pairs(self.map:get()) do
if v[".type"] == self.sectiontype then if v[".type"] == self.sectiontype then
if ffluci.util.validate(k, self.scope) then if ffluci.util.validate(k, self.scope) then
sections[k] = v sections[k] = v
@ -440,7 +440,7 @@ function AbstractValue.__init__(self, map, option, ...)
self.valid = nil self.valid = nil
self.depends = nil self.depends = nil
self.default = nil self.default = " "
self.size = nil self.size = nil
self.optional = false self.optional = false
end end
@ -463,7 +463,7 @@ function AbstractValue.parse(self, section)
if not fvalue then if not fvalue then
self.tag_invalid[section] = true self.tag_invalid[section] = true
end end
if fvalue and not (fvalue == self:ucivalue(section)) then if fvalue and not (fvalue == self:cfgvalue(section)) then
self:write(section, fvalue) self:write(section, fvalue)
end end
elseif ffluci.http.formvalue("cbi.submit") then -- Unset the UCI or error elseif ffluci.http.formvalue("cbi.submit") then -- Unset the UCI or error
@ -477,13 +477,13 @@ end
-- Render if this value exists or if it is mandatory -- Render if this value exists or if it is mandatory
function AbstractValue.render(self, section) function AbstractValue.render(self, section)
if not self.optional or self:ucivalue(section) then if not self.optional or self:cfgvalue(section) then
ffluci.template.render(self.template, {self=self, section=section}) ffluci.template.render(self.template, {self=self, section=section})
end end
end end
-- Return the UCI value of this object -- Return the UCI value of this object
function AbstractValue.ucivalue(self, section) function AbstractValue.cfgvalue(self, section)
return self.map:get(section, self.option) return self.map:get(section, self.option)
end end
@ -559,7 +559,7 @@ function Flag.parse(self, section)
end end
if fvalue == self.enabled or (not self.optional and not self.rmempty) then if fvalue == self.enabled or (not self.optional and not self.rmempty) then
if not(fvalue == self:ucivalue(section)) then if not(fvalue == self:cfgvalue(section)) then
self:write(section, fvalue) self:write(section, fvalue)
end end
else else
@ -625,7 +625,7 @@ function MultiValue.add_value(self, key, val)
end end
function MultiValue.valuelist(self, section) function MultiValue.valuelist(self, section)
local val = self:ucivalue(section) local val = self:cfgvalue(section)
if not(type(val) == "string") then if not(type(val) == "string") then
return {} return {}

View file

@ -2,10 +2,8 @@
FFLuCI - Configuration FFLuCI - Configuration
Description: Description:
Some FFLuCI configuration values Some FFLuCI configuration values read from uci file "luci"
ToDo:
Port over to UCI
FileId: FileId:
$Id$ $Id$
@ -28,10 +26,22 @@ limitations under the License.
]]-- ]]--
module("ffluci.config", package.seeall) module("ffluci.config", package.seeall)
require("ffluci.model.uci")
require("ffluci.util")
-- Warning! This is only for fallback and compatibility purporses! --
main = {}
-- This is where stylesheets and images go -- This is where stylesheets and images go
mediaurlbase = "/ffluci/media" main.mediaurlbase = "/ffluci/media"
-- Does anybody think about browser autodetect here? -- Does anybody think about browser autodetect here?
-- Too bad busybox doesn't populate HTTP_ACCEPT_LANGUAGE -- Too bad busybox doesn't populate HTTP_ACCEPT_LANGUAGE
lang = "de" main.lang = "de"
-- Now overwrite with UCI values
local ucidata = ffluci.model.uci.show("luci")
if ucidata and ucidata.luci then
ffluci.util.update(ffluci.config, ucidata.luci)
end

View file

@ -0,0 +1,9 @@
module(..., package.seeall)
menu = {
descr = "Übersicht",
order = 10,
entries = {
{action = "contact", descr = "Kontakt"}
}
}

View file

@ -0,0 +1,10 @@
module(..., package.seeall)
menu = {
descr = "Netzwerk",
order = 20,
entries = {
{action = "vlan", descr = "VLAN"},
{action = "ifaces", descr = "Schnittstellen"}
}
}

View file

@ -0,0 +1,57 @@
module("ffluci.controller.admin.uci", package.seeall)
-- This function has a higher priority than the admin_uci/apply template
function action_apply()
local changes = ffluci.model.uci.changes()
local output = ""
if changes then
local apply = {}
-- Collect files to be applied
for i, line in ipairs(ffluci.util.split(changes)) do
local r = line:match("^[^.]+")
if r then
apply[r] = true
end
end
-- Commit changes
ffluci.model.uci.commit()
-- Search for post-commit commands
if ffluci.config.uci_oncommit then
for k, v in pairs(apply) do
local cmd = ffluci.config.uci_oncommit[k]
if cmd then
output = output .. ffluci.util.exec(cmd)
end
end
end
end
ffluci.template.render("admin_uci/apply", {changes=changes, output=output})
end
function action_revert()
local changes = ffluci.model.uci.changes()
if changes then
local revert = {}
-- Collect files to be reverted
for i, line in ipairs(ffluci.util.split(changes)) do
local r = line:match("^[^.]+")
if r then
revert[r] = true
end
end
-- Revert them
for k, v in pairs(revert) do
ffluci.model.uci.revert(k)
end
end
ffluci.template.render("admin_uci/revert", {changes=changes})
end

View file

@ -1 +1,9 @@
module(..., package.seeall) module(..., package.seeall)
menu = {
descr = "Übersicht",
order = 10,
entries = {
{action = "contact", descr = "Kontakt"}
}
}

View file

@ -52,7 +52,7 @@ end
-- Same as load but autocompletes the filename with .LANG from config.lang -- Same as load but autocompletes the filename with .LANG from config.lang
function loadc(file) function loadc(file)
return load(file .. "." .. ffluci.config.lang) return load(file .. "." .. ffluci.config.main.lang)
end end
-- Returns the i18n-value defined by "key" or if there is no such: "default" -- Returns the i18n-value defined by "key" or if there is no such: "default"

View file

@ -0,0 +1,15 @@
m = Map("luci", "Kontakt", [[Diese Daten sind auf der öffentlichen Kontaktseite
sichtbar. Alle Felder sind natürlich freiwillig. Du kannst soviel oder so wenig
über dich angeben, wie du möchtest.]])
c = m:section(NamedSection, "contact")
c:option(Value, "nickname", "Pseudonym")
c:option(Value, "name", "Name")
c:option(Value, "mail", "E-Mail")
c:option(Value, "phone", "Telefon")
c:option(Value, "location", "Standort")
c:option(Value, "geo", "Koordinaten", "Bitte als Breite;Länge angeben")
c:option(Value, "note", "Notiz")
return m

View file

@ -61,11 +61,11 @@ end
-- Wrapper for "uci changes" -- Wrapper for "uci changes"
function Session.changes(self, config) function Session.changes(self, config)
return self:_uci3("changes " .. _path(config)) return self:_uci("changes " .. _path(config))
end end
function change(...) function changes(...)
return default:change(...) return default:changes(...)
end end
@ -105,7 +105,7 @@ function Session.revert(self, config)
end end
function revert(...) function revert(...)
return self:revert(...) return default:revert(...)
end end

View file

@ -52,9 +52,9 @@ compiler_enable_bytecode = false
-- Define the namespace for template modules -- Define the namespace for template modules
viewns = { viewns = {
translate = ffluci.i18n.translate, translate = ffluci.i18n.translate,
config = ffluci.model.uci.get, config = function(...) return ffluci.model.uci.get(...) or "" end,
controller = os.getenv("SCRIPT_NAME"), controller = os.getenv("SCRIPT_NAME"),
media = ffluci.config.mediaurlbase, media = ffluci.config.main.mediaurlbase,
write = io.write, write = io.write,
include = function(name) Template(name):render(getfenv(2)) end, include = function(name) Template(name):render(getfenv(2)) end,
} }
@ -108,7 +108,7 @@ function compile(template)
elseif p == "~" then elseif p == "~" then
re = sanitize(v):gsub("~(.-)%.(.-)%.(.+)", r_uci) re = sanitize(v):gsub("~(.-)%.(.-)%.(.+)", r_uci)
elseif p == "=" then elseif p == "=" then
re = r_pexec:format(string.sub(v, 2)) re = r_pexec:format(v:sub(2))
else else
re = r_exec:format(v) re = r_exec:format(v)
end end
@ -120,6 +120,10 @@ function compile(template)
template = string.dump(tf) template = string.dump(tf)
end end
c = c or 1
ffluci.fs.writefile("/tmp/"..tostring(c), template)
c = c+1
return template return template
end end

View file

@ -152,9 +152,7 @@ end
-- Resets the scope of f doing a shallow copy of its scope into a new table -- Resets the scope of f doing a shallow copy of its scope into a new table
function resfenv(f) function resfenv(f)
local scope = getfenv(f) setfenv(f, clone(getfenv(f)))
setfenv(f, {})
updfenv(f, scope)
end end
@ -166,31 +164,41 @@ end
-- Splits a string into an array (Taken from lua-users.org) -- Splits a string into an array (Taken from lua-users.org)
function split(str, pat) function split(str, pat)
local t = {} pat = pat or "\n"
local fpat = "(.-)" .. pat
local last_end = 1 local t = {}
local s, e, cap = str:find(fpat, 1) local fpat = "(.-)" .. pat
while s do local last_end = 1
if s ~= 1 or cap ~= "" then local s, e, cap = str:find(fpat, 1)
table.insert(t,cap)
end while s do
last_end = e+1 if s ~= 1 or cap ~= "" then
s, e, cap = str:find(fpat, last_end) table.insert(t,cap)
end end
if last_end <= #str then last_end = e+1
cap = str:sub(last_end) s, e, cap = str:find(fpat, last_end)
table.insert(t, cap) end
end
return t if last_end <= #str then
cap = str:sub(last_end)
table.insert(t, cap)
end
return t
end
-- Updates given table with new values
function update(t, updates)
for k, v in pairs(updates) do
t[k] = v
end
end end
-- Updates the scope of f with "extscope" -- Updates the scope of f with "extscope"
function updfenv(f, extscope) function updfenv(f, extscope)
local scope = getfenv(f) update(getfenv(f), extscope)
for k, v in pairs(extscope) do
scope[k] = v
end
end end

View file

@ -0,0 +1,6 @@
<%+header%>
<h1><%:config Konfiguration%></h1>
<p><%:uci_applied Die folgenden Änderungen wurden übernommen:%></p>
<code><%=(changes or "-")%>
<%=output%></code>
<%+footer%>

View file

@ -0,0 +1,11 @@
<%+header%>
<h1><%:config Konfiguration%></h1>
<h2><%:changes Änderungen%></h2>
<code><%=(ffluci.model.uci.changes() or "-")%></code>
<form class="inline" method="get" action="<%=controller%>/admin/uci/apply">
<input type="submit" value="<%:apply Anwenden%>" />
</form>
<form class="inline" method="get" action="<%=controller%>/admin/uci/revert">
<input type="submit" value="<%:revert Verwerfen%>" />
</form>
<%+footer%>

View file

@ -0,0 +1,5 @@
<%+header%>
<h1><%:config Konfiguration%></h1>
<p><%:uci_reverted Die folgenden Änderungen wurden verworfen:%></p>
<code><%=(changes or "-")%></code>
<%+footer%>

View file

@ -1,5 +1,4 @@
<hr class="cbi-form-separator" /> <input type="submit" value="<%:save Speichern%>" />
<input type="submit" value="<%:cbi_save Speichern%>" /> <input type="reset" value="<%:reset Zurücksetzen%>" />
<input type="reset" value="<%:cbi_reset Zurücksetzen%>" />
</form> </form>
<%+footer%> <%+footer%>

View file

@ -4,7 +4,7 @@
<div class="cbi-value-description"><%=self.description%></div> <div class="cbi-value-description"><%=self.description%></div>
</div> </div>
<div class="cbi-value-field"> <div class="cbi-value-field">
<input type="checkbox" name="cbid.<%=self.config.."."..section.."."..self.option%>"<% if self:ucivalue(section) == self.enabled then %> checked="checked"<% end %> value="1" /> <input type="checkbox" name="cbid.<%=self.config.."."..section.."."..self.option%>"<% if self:cfgvalue(section) == self.enabled then %> checked="checked"<% end %> value="1" />
</div> </div>
<div class="clear"></div> <div class="clear"></div>
</div> </div>

View file

@ -1,20 +1,18 @@
<div class="cbi-value"> <div class="cbi-value">
<div class="left"> <div class="cbi-value-title left"><%=self.title%></div>
<div class="cbi-value-title"><%=self.title%></div> <div class="cbi-value-description right"><%=self.description%></div>
<div class="cbi-value-description"><%=self.description%></div>
</div>
<div class="cbi-value-field"> <div class="cbi-value-field">
<% if self.widget == "select" then %> <% if self.widget == "select" then %>
<select name="cbid.<%=self.config.."."..section.."."..self.option%>"<% if self.size then %> size="<%=self.size%>"<% end %>> <select name="cbid.<%=self.config.."."..section.."."..self.option%>"<% if self.size then %> size="<%=self.size%>"<% end %>>
<%for i, key in pairs(self.keylist) do%> <%for i, key in pairs(self.keylist) do%>
<option<% if self:ucivalue(section) == key then %> selected="selected"<% end %> value="<%=key%>"><%=self.vallist[i]%></option> <option<% if self:cfgvalue(section) == key then %> selected="selected"<% end %> value="<%=key%>"><%=self.vallist[i]%></option>
<% end %> <% end %>
</select> </select>
<% elseif self.widget == "radio" then <% elseif self.widget == "radio" then
local c = 0; local c = 0;
for i, key in pairs(self.keylist) do for i, key in pairs(self.keylist) do
c = c + 1%> c = c + 1%>
<%=self.vallist[i]%><input type="radio" name="cbid.<%=self.config.."."..section.."."..self.option%>"<% if self:ucivalue(section) == key then %> checked="checked"<% end %> value="<%=key%>" /> <%=self.vallist[i]%><input type="radio" name="cbid.<%=self.config.."."..section.."."..self.option%>"<% if self:cfgvalue(section) == key then %> checked="checked"<% end %> value="<%=key%>" />
<% if c == self.size then c = 0 %><br /> <% if c == self.size then c = 0 %><br />
<% end end %> <% end end %>
<% end %> <% end %>

View file

@ -1,17 +1,17 @@
<% if self:ucivalue(self.section) then %> <% if self:cfgvalue(self.section) then %>
<div class="cbi-section" id="cbi-<%=self.config%>-<%=self.section%>"> <div class="cbi-section" id="cbi-<%=self.config%>-<%=self.section%>">
<h2><%=self.title%></h2> <h2><%=self.title%></h2>
<div class="cbi-section-descr"><%=self.description%></div> <div class="cbi-section-descr"><%=self.description%></div>
<fieldset class="cbi-section-node"> <fieldset class="cbi-section-node">
<% self:render_children(self.section) %> <% self:render_children(self.section) %>
<% if #self.optionals > 0 or self.dynamic then %> <% if #self.optionals[self.section] > 0 or self.dynamic then %>
<div class="cbi-optionals"> <div class="cbi-optionals">
<% if self.dynamic then %> <% if self.dynamic then %>
<input type="text" name="cbi.opt.<%=self.config%>.<%=self.section%>" /> <input type="text" name="cbi.opt.<%=self.config%>.<%=self.section%>" />
<% else %> <% else %>
<select name="cbi.opt.<%=self.config%>.<%=self.section%>"> <select name="cbi.opt.<%=self.config%>.<%=self.section%>">
<option><%:cbi_selopt *** Zusätzliche Felder ***%></option> <option><%:cbi_selopt *** Zusätzliche Parameter ***%></option>
<% for key, val in pairs(self.optionals) do %> <% for key, val in pairs(self.optionals[self.section]) do %>
<option value="<%=val.option%>"><%=val.title%></option> <option value="<%=val.option%>"><%=val.title%></option>
<% end %> <% end %>
</select> </select>

View file

@ -1,18 +1,18 @@
<div class="cbi-section" id="cbi-<%=self.config%>-<%=self.sectiontype%>"> <div class="cbi-section" id="cbi-<%=self.config%>-<%=self.sectiontype%>">
<h2><%=self.title%></h2> <h2><%=self.title%></h2>
<div class="cbi-section-descr"><%=self.description%></div> <div class="cbi-section-descr"><%=self.description%></div>
<% for k, v in pairs(self:ucisections()) do%> <% for k, v in pairs(self:cfgsections()) do%>
<fieldset class="cbi-section-node" id="cbi-<%=self.config%>-<%=k%>"> <fieldset class="cbi-section-node" id="cbi-<%=self.config%>-<%=k%>">
<% if not self.anonymous then %><legend><%=k%></legend><% end %> <% if not self.anonymous then %><h3><%=k%></h3><% end %>
<% self:render_children(k) %> <% self:render_children(k) %>
<% if #self.optionals > 0 or self.dynamic then %> <% if #self.optionals[k] > 0 or self.dynamic then %>
<div class="cbi-optionals"> <div class="cbi-optionals">
<% if self.dynamic then %> <% if self.dynamic then %>
<input type="text" name="cbi.opt.<%=self.config%>.<%=k%>" /> <input type="text" name="cbi.opt.<%=self.config%>.<%=k%>" />
<% else %> <% else %>
<select name="cbi.opt.<%=self.config%>.<%=k%>"> <select name="cbi.opt.<%=self.config%>.<%=k%>">
<option><%:cbi_selopt *** Zusätzliche Felder ***%></option> <option><%:cbi_selopt *** Zusätzliche Parameter ***%></option>
<% for key, val in pairs(self.optionals) do %> <% for key, val in pairs(self.optionals[k]) do %>
<option value="<%=val.option%>"><%=val.title%></option> <option value="<%=val.option%>"><%=val.title%></option>
<% end %> <% end %>
</select> </select>

View file

@ -1,10 +1,8 @@
<div class="cbi-value"> <div class="cbi-value">
<div class="left"> <div class="cbi-value-title left"><%=self.title%></div>
<div class="cbi-value-title"><%=self.title%></div> <div class="cbi-value-description right"><%=self.description%></div>
<div class="cbi-value-description"><%=self.description%></div>
</div>
<div class="cbi-value-field"> <div class="cbi-value-field">
<input type="text" <% if self.size then %>size="<%=self.size%>" <% end %><% if self.maxlength then %>maxlength="<%=self.maxlength%>" <% end %>name="cbid.<%=self.config.."."..section.."."..self.option%>" value="<%=(self:ucivalue(section) or "")%>" /> <input type="text" <% if self.size then %>size="<%=self.size%>" <% end %><% if self.maxlength then %>maxlength="<%=self.maxlength%>" <% end %>name="cbid.<%=self.config.."."..section.."."..self.option%>" value="<%=(self:cfgvalue(section) or "")%>" />
</div> </div>
<div class="clear"></div> <div class="clear"></div>
<% if self.tag_invalid[section] then %><div class="cbi-error"><%:cbi_invalid Fehler: Ungültige Eingabe%></div><% end %> <% if self.tag_invalid[section] then %><div class="cbi-error"><%:cbi_invalid Fehler: Ungültige Eingabe%></div><% end %>

View file

@ -2,6 +2,6 @@
<div class="clear"></div> <div class="clear"></div>
</div></div> </div></div>
<div class="separator magenta bold">FFLuCI 0.1 - Freifunk Lua Configuration Interface</div> <div class="separator magenta bold">FFLuCI 0.2 - Freifunk Lua Configuration Interface</div>
</body> </body>
</html> </html>

View file

@ -11,7 +11,6 @@ require("ffluci.http").htmlheader()
<head> <head>
<link rel="stylesheet" type="text/css" href="<%=media%>/cascade.css" /> <link rel="stylesheet" type="text/css" href="<%=media%>/cascade.css" />
<link rel="stylesheet" type="text/css" href="<%=media%>/css/<%=req.category%>_<%=req.module%>.css" /> <link rel="stylesheet" type="text/css" href="<%=media%>/css/<%=req.category%>_<%=req.module%>.css" />
<link rel="stylesheet" type="text/css" href="<%=media%>/css/<%=req.category%>_<%=req.module%>/<%=req.action%>.css" />
<title>FFLuCI</title> <title>FFLuCI</title>
</head> </head>
<body> <body>
@ -49,12 +48,29 @@ require("ffluci.http").htmlheader()
<div class="sidebar right"> <div class="sidebar right">
<div><%:webif Weboberfläche%> <div><%:webif Weboberfläche%>
<ul> <ul>
<li<% if "public" == req.category then %> class="yellowtext"<% end %>><a href="<%=controller%>/public"><%:public Public%></a></li> <li<% if "public" == req.category then %> class="yellowtext"<% end %>><a href="<%=controller%>/public"><%:public Öffentlich%></a></li>
<li<% if "admin" == req.category then %> class="yellowtext"<% end %>><a href="<%=controller%>/admin"><%:admin Admin%></a></li> <li<% if "admin" == req.category then %> class="yellowtext"<% end %>><a href="<%=controller%>/admin"><%:admin Verwaltung%></a></li>
</ul>
</div>
<%
if "admin" == req.category then
require("ffluci.model.uci")
local ucic = ffluci.model.uci.changes()
if ucic then
ucic = #ffluci.util.split(ucic)
end
%>
<div><%:config Konfiguration%>
<ul>
<% if ucic then %>
<li><a href="<%=controller%>/admin/uci/changes"><%:changes Änderungen:%> <%=ucic%></a></li>
<li><a href="<%=controller%>/admin/uci/apply"><%:apply Anwenden%></a></li>
<li><a href="<%=controller%>/admin/uci/revert"><%:revert Verwerfen%></a></li>
<% else %>
<li><%:changes Änderungen: %> 0</li>
<% end %>
</ul> </ul>
</div> </div>
<% if "admin" == req.category then %>
<div>Konfiguration<ul><li>x Änderungen</li><li>Anwenden</li><li>Zurücksetzen</li></ul></div>
<% end %> <% end %>
</div> </div>
<div id="content"> <div id="content">

View file

@ -0,0 +1,12 @@
<%+header%>
<h1><%:contact Kontakt%></h1>
<table class="contact">
<tr><th><%:nickname Pseudonym%>:</th><td><%~luci.contact.nickname%></td></tr>
<tr><th><%:name Name%>:</th><td><%~luci.contact.name%></td></tr>
<tr><th><%:mail E-Mail%>:</th><td><%~luci.contact.mail%></td></tr>
<tr><th><%:phone Telefon%>:</th><td><%~luci.contact.phone%></td></tr>
<tr><th><%:location Standort%>:</th><td><%~luci.contact.location%></td></tr>
<tr><th><%:geocoord Geokoordinaten%>:</th><td><%~luci.contact.geo%></td></tr>
<tr><th><%:note Notiz%>:</th><td><%~luci.contact.note%></td></tr>
</table>
<%+footer%>