});
cbi_combobox(id, values, def, man);
}
+
+function cbi_filebrowser(id, url, defpath) {
+ var field = document.getElementById(id);
+ var browser = window.open(
+ url + ( field.value || defpath || '' ) + '?field=' + id,
+ "luci_filebrowser", "width=300,height=400,left=100,top=200,scrollbars=yes"
+ );
+
+ browser.focus();
+}
require("luci.util")
require("luci.http")
require("luci.uvl")
+require("luci.fs")
local uci = require("luci.model.uci")
local class = luci.util.class
FORM_NODATA = 0
FORM_VALID = 1
FORM_INVALID = -1
+FORM_CHANGED = 2
AUTO = true
require("luci.config")
require("luci.util")
+ local upldir = "/lib/uci/upload/"
local cbidir = luci.util.libpath() .. "/model/cbi/"
local func, err = loadfile(cbimap) or loadfile(cbidir..cbimap..".lua")
assert(func, err)
return rawget(tbl, key) or _M[key] or _G[key]
end}))
- local maps = {func()}
+ local maps = { func() }
+ local uploads = { }
+ local has_upload = false
for i, map in ipairs(maps) do
if not instanceof(map, Node) then
return nil
else
map:prepare()
+ if map.upload_fields then
+ has_upload = true
+ for _, field in ipairs(map.upload_fields) do
+ uploads[
+ field.config .. '.' ..
+ field.section.sectiontype .. '.' ..
+ field.option
+ ] = true
+ end
+ end
end
end
+ if has_upload then
+ local uci = luci.model.uci.cursor()
+ local prm = luci.http.context.request.message.params
+ local fd, cbid
+
+ luci.http.setfilehandler(
+ function( field, chunk, eof )
+ if not field then return end
+ if field.name and not cbid then
+ local c, s, o = field.name:gmatch(
+ "cbid%.([^%.]+)%.([^%.]+)%.([^%.]+)"
+ )()
+
+ if c and s and o then
+ local t = uci:get( c, s )
+ if t and uploads[c.."."..t.."."..o] then
+ local path = upldir .. field.name
+ fd = io.open(path, "w")
+ if fd then
+ cbid = field.name
+ prm[cbid] = path
+ end
+ end
+ end
+ end
+
+ if field.name == cbid and fd then
+ fd:write(chunk)
+ end
+
+ if eof and fd then
+ fd:close()
+ fd = nil
+ cbid = nil
+ end
+ end
+ )
+ end
+
return maps
end
end
end
+function Map.submitstate(self)
+ return luci.http.formvalue("cbi.submit")
+end
-- Chain foreign config
function Map.chain(self, config)
for i, config in ipairs(self.parsechain) do
self.uci:unload(config)
end
+ if type(self.commit_handler) == "function" then
+ self:commit_handler(self:submitstate())
+ end
+ end
+
+ if self:submitstate() then
+ if self.save then
+ return self.changed and FORM_CHANGED or FORM_VALID
+ else
+ return FORM_INVALID
+ end
+ else
+ return FORM_NODATA
end
end
end
local state =
- not luci.http.formvalue("cbi.submit") and 0
- or valid and 1
- or -1
+ not self:submitstate() and FORM_NODATA
+ or valid and FORM_VALID
+ or FORM_INVALID
self.dorender = not self.handle or self:handle(state, self.data) ~= false
+ return state
end
function SimpleForm.render(self, ...)
end
end
+function SimpleForm.submitstate(self)
+ return luci.http.formvalue("cbi.submit")
+end
+
function SimpleForm.section(self, class, ...)
if instanceof(class, AbstractSection) then
local obj = class(self, ...)
function datasource.get(self, section, option)
return data[section] and data[section][option]
end
+
+ function datasource.submitstate(self)
+ return luci.http.formvalue("cbi.submit")
+ end
function datasource.del(...)
return true
function Table.parse(self)
for i, k in ipairs(self:cfgsections()) do
- if luci.http.formvalue("cbi.submit") then
+ if self.map:submitstate() then
Node.parse(self, k)
end
end
if active then
AbstractSection.parse_dynamic(self, s)
- if luci.http.formvalue("cbi.submit") then
+ if self.map:submitstate() then
Node.parse(self, s)
if not novld and not self.override_scheme and self.map.scheme then
local co
for i, k in ipairs(self:cfgsections()) do
AbstractSection.parse_dynamic(self, k)
- if luci.http.formvalue("cbi.submit") then
+ if self.map:submitstate() then
Node.parse(self, k)
if not novld and not self.override_scheme and self.map.scheme then
self.inputstyle = nil
self.rmempty = true
end
+
+
+FileUpload = class(AbstractValue)
+
+function FileUpload.__init__(self, ...)
+ AbstractValue.__init__(self, ...)
+ self.template = "cbi/upload"
+ if not self.map.upload_fields then
+ self.map.upload_fields = { self }
+ else
+ self.map.upload_fields[#self.map.upload_fields+1] = self
+ end
+end
+
+function FileUpload.formcreated(self, section)
+ return AbstractValue.formcreated(self, section) or
+ luci.http.formvalue("cbi.rlf."..section.."."..self.option) or
+ luci.http.formvalue("cbi.rlf."..section.."."..self.option..".x")
+end
+
+function FileUpload.cfgvalue(self, section)
+ local val = AbstractValue.cfgvalue(self, section)
+ if val and luci.fs.access(val) then
+ return val
+ end
+ return nil
+end
+
+function FileUpload.formvalue(self, section)
+ local val = AbstractValue.formvalue(self, section)
+ if val then
+ if not luci.http.formvalue("cbi.rlf."..section.."."..self.option) and
+ not luci.http.formvalue("cbi.rlf."..section.."."..self.option..".x")
+ then
+ return val
+ end
+ luci.fs.unlink(val)
+ self.value = nil
+ end
+ return nil
+end
+
+function FileUpload.remove(self, section)
+ local val = AbstractValue.formvalue(self, section)
+ if val and luci.fs.access(val) then luci.fs.unlink(val) end
+ return AbstractValue.remove(self, section)
+end
+
+
+FileBrowser = class(AbstractValue)
+
+function FileBrowser.__init__(self, ...)
+ AbstractValue.__init__(self, ...)
+ self.template = "cbi/browser"
+end
-%>
<%+cbi/valueheader%>
<% if self:cfgvalue(section) ~= false then %>
- <input class="cbi-input-<% self.inputstyle or "button" %>" type="submit"<%= attr("name", cbid) .. attr("id", cbid) .. attr("value", self.title)%> />
+ <input class="cbi-input-<%=self.inputstyle or "button" %>" type="submit"<%= attr("name", cbid) .. attr("id", cbid) .. attr("value", self.title)%> />
<% else %>
-
<% end %>
-%>
- <div>
+ <div class="cbi-page-actions">
<input class="cbi-button cbi-button-apply" type="submit" name="cbi.apply" value="<%:saveapply%>" />
<input class="cbi-button cbi-button-save" type="submit" value="<%:save%>" />
<input class="cbi-button cbi-button-reset" type="reset" value="<%:reset%>" />
-%>
<%+header%>
-<form method="post" action="<%=luci.http.getenv("REQUEST_URI")%>">
+<form method="post" action="<%=luci.http.getenv("REQUEST_URI")%>" enctype="multipart/form-data">
<div>
<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
<input type="hidden" name="cbi.submit" value="1" />
<br />
</fieldset>
<% elseif self.addremove then %>
+ <% if self.template_addremove then include(self.template_addremove) else -%>
<fieldset class="cbi-section" id="cbi-<%=self.config%>-<%=self.section%>">
<% if self.title and #self.title > 0 then -%>
<legend><%=self.title%></legend>
<div class="cbi-section-descr"><%=self.description%></div>
<input type="submit" class="cbi-button-add" name="cbi.cns.<%=self.config%>.<%=self.section%>" value="<%:cbi_add%>" />
</fieldset>
+ <%- end %>
<% end %>
<!-- /nsection -->
<%- end %>
<% if self.addremove then -%>
+ <% if self.template_addremove then include(self.template_addremove) else -%>
<div class="cbi-section-create">
<% if self.anonymous then -%>
<input type="submit" class="cbi-button cbi-button-add" name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>" value="<%:cbi_add%>" />
<%- end %>
<%- end %>
</div>
+ <%- end %>
<%- end %>
</fieldset>
local coroutine = require "coroutine"
local getmetatable, setmetatable = getmetatable, setmetatable
-local getfenv, setfenv = getfenv, setfenv
local rawget, rawset, unpack = rawget, rawset, unpack
local tostring, type, assert = tostring, type, assert
local ipairs, pairs, loadstring = ipairs, pairs, loadstring
return enc
end
---- (Internal function)
+-- (Internal function)
-- Initialize given parameter and coerce string into table when the parameter
-- already exists.
-- @param tbl Table where parameter should be created
end
end
---- (Internal function)
+-- (Internal function)
-- Append given data to given parameter, either by extending the string value
-- or by appending it to the last string in the parameter's value table.
-- @param tbl Table containing the previously initialized parameter value
end
end
---- (Internal function)
+-- (Internal function)
-- Finish the value of given parameter, either by transforming the string value
-- or - in the case of multi value parameters - the last element in the
-- associated values table.
-- Populate common environment variables
msg.env = {
CONTENT_LENGTH = msg.headers['Content-Length'];
- CONTENT_TYPE = msg.headers['Content-Type'];
+ CONTENT_TYPE = msg.headers['Content-Type'] or msg.headers['Content-type'];
REQUEST_METHOD = msg.request_method:upper();
REQUEST_URI = msg.request_uri;
SCRIPT_NAME = msg.request_uri:gsub("?.+$","");
SCRIPT_FILENAME = ""; -- XXX implement me
- SERVER_PROTOCOL = "HTTP/" .. string.format("%.1f", msg.http_version)
+ SERVER_PROTOCOL = "HTTP/" .. string.format("%.1f", msg.http_version);
+ QUERY_STRING = msg.request_uri:match("?")
+ and msg.request_uri:gsub("^.+?","") or ""
}
-- Populate HTTP_* environment variables
-- Is it application/x-www-form-urlencoded ?
elseif msg.env.REQUEST_METHOD == "POST" and msg.env.CONTENT_TYPE and
- msg.env.CONTENT_TYPE == "application/x-www-form-urlencoded"
+ msg.env.CONTENT_TYPE:match("^application/x%-www%-form%-urlencoded")
then
return urldecode_message_body( src, msg, filecb )
return true
end
--- 14.27 / If-Range
+--- 14.27 / If-Range
-- The If-Range header is currently not implemented due to the lack of general
-- byte range stuff in luci.http.protocol . This function will always return
-- false, 412 to indicate a failed precondition.
return false, 412
end
--- 14.28 / If-Unmodified-Since
+--- 14.28 / If-Unmodified-Since
-- Test whether the given message object contains an "If-Unmodified-Since"
-- header and compare it against the given stat object.
-- @param req HTTP request message object
touch $@
boa-compile: $(BOA_DIR)/.configured
- $(MAKE) -C $(BOA_DIR)/src CC=$(CC) CFLAGS="$(CFLAGS)"
+ $(MAKE) -C $(BOA_DIR)/src CC=$(CC) CFLAGS="$(CFLAGS) -DINET6 -DACCEPT_ON -DWHEN_DOES_THIS_APPLY"
%.o: %.c
$(COMPILE) $(LUA_CFLAGS) -I$(BOA_DIR)/src $(FPIC) -c -o $@ $<
env.REMOTE_PORT = context.remote_port
env.SERVER_ADDR = context.server_addr
env.HTTP_COOKIE = context.cookie
+ env.HTTP_ACCEPT = context.http_accept
env.SCRIPT_NAME = env.REQUEST_URI:sub(1, #env.REQUEST_URI - #env.PATH_INFO)
luci.sgi.webuci.run(env, vars)
option datatype 'string'
option required false
+# Variable default value (schema.@variable.default)
+config variable
+ option name 'default'
+ option title 'Default value of this variable'
+ option section 'schema.variable'
+ option type 'variable'
+ option datatype 'string'
+ option required false
+
# Variable validators (schema.@variable.validator)
config variable
option name 'validator'
function authenticator.htmlauth(validator, accs, default)
local user = luci.http.formvalue("username")
local pass = luci.http.formvalue("password")
-
+
if user and validator(user, pass) then
return user
end
-
+
require("luci.i18n")
require("luci.template")
context.path = {}
luci.template.render("sysauth", {duser=default, fuser=user})
return false
-
+
end
--- Dispatch an HTTP request.
if not stat then
error500(err)
end
-
+
luci.http.close()
--context._disable_memtrace()
--context._disable_memtrace = require "luci.debug".trap_memtrace()
local ctx = context
ctx.path = request
-
+
require "luci.i18n".setlanguage(require "luci.config".main.lang)
-
+
local c = ctx.tree
local stat
if not c then
c = createtree()
end
-
+
local track = {}
local args = {}
context.args = args
end
util.update(track, c)
-
+
if c.leaf then
break
end
if track.i18n then
require("luci.i18n").loadc(track.i18n)
end
-
+
-- Init template engine
- if not track.notemplate then
+ if (c and c.index) or not track.notemplate then
local tpl = require("luci.template")
- local media = luci.config.main.mediaurlbase
+ local media = track.mediaurlbase or luci.config.main.mediaurlbase
if not pcall(tpl.Template, "themes/%s/header" % fs.basename(media)) then
media = nil
for name, theme in pairs(luci.config.themes) do
viewns.striptags = util.striptags
viewns.controller = luci.http.getenv("SCRIPT_NAME")
viewns.media = media
+ viewns.theme = fs.basename(media)
viewns.resource = luci.config.main.resourcebase
viewns.REQUEST_URI = (luci.http.getenv("SCRIPT_NAME") or "") .. (luci.http.getenv("PATH_INFO") or "")
end
-
+
track.dependent = (track.dependent ~= false)
assert(not track.dependent or not track.auto, "Access Violation")
-
+
if track.sysauth then
local sauth = require "luci.sauth"
-
+
local authen = type(track.sysauth_authenticator) == "function"
and track.sysauth_authenticator
or authenticator[track.sysauth_authenticator]
-
+
local def = (type(track.sysauth) == "string") and track.sysauth
local accs = def and {track.sysauth} or track.sysauth
local sess = ctx.authsession or luci.http.getcookie("sysauth")
sess = sess and sess:match("^[A-F0-9]+$")
local user = sauth.read(sess)
-
+
if not util.contains(accs, user) then
if authen then
local user, sess = authen(luci.sys.user.checkpasswd, accs, def)
luci.sys.process.setuser(track.setuser)
end
+ if c and (c.index or type(c.target) == "function") then
+ ctx.dispatched = c
+ ctx.requested = ctx.requested or ctx.dispatched
+ end
+
+ if c and c.index then
+ local tpl = require "luci.template"
+
+ if util.copcall(tpl.render, "indexer", {}) then
+ return true
+ end
+ end
+
if c and type(c.target) == "function" then
- context.dispatched = c
-
util.copcall(function()
local oldenv = getfenv(c.target)
local module = require(c.module)
local env = setmetatable({}, {__index=
-
+
function(tbl, key)
- return rawget(tbl, key) or module[key] or oldenv[key]
+ return rawget(tbl, key) or module[key] or oldenv[key]
end})
setfenv(c.target, env)
end)
-
+
c.target(unpack(args))
else
error404()
function createindex()
local path = luci.util.libpath() .. "/controller/"
local suff = ".lua"
-
+
if luci.util.copcall(require, "luci.fastindex") then
createindex_fastindex(path, suff)
else
-- @param suffix Controller file suffix
function createindex_fastindex(path, suffix)
index = {}
-
+
if not fi then
fi = luci.fastindex.new("index")
fi.add(path .. "*" .. suffix)
fi.add(path .. "*/*" .. suffix)
end
fi.scan()
-
+
for k, v in pairs(fi.indexes) do
index[v[2]] = v[1]
end
index = loadfile(indexcache)()
return index
- end
+ end
end
-
+
index = {}
local controllers = util.combine(
local module = "luci.controller." .. c:sub(#path+1, #c-#suffix):gsub("/", ".")
local mod = require(module)
local idx = mod.index
-
+
if type(idx) == "function" then
index[module] = idx
end
end
-
+
if indexcache then
fs.writefile(indexcache, util.get_bytecode(index))
fs.chmod(indexcache, "a-rwx,u+rw")
if not index then
createindex()
end
-
+
local ctx = context
local tree = {nodes={}}
-
+
ctx.treecache = setmetatable({}, {__mode="v"})
ctx.tree = tree
-
+
-- Load default translation
require "luci.i18n".loadc("default")
-
+
local scope = setmetatable({}, {__index = luci.dispatcher})
for k, v in pairs(index) do
setfenv(v, scope)
v()
end
-
+
return tree
end
local obj = node(unpack(path))
obj.nodes = nil
obj.module = nil
-
+
obj.title = title
obj.order = order
setmetatable(obj, {__index = _create_node(clone)})
-
+
return obj
end
--- Create a new dispatching node and define common parameters.
-- @param path Virtual path
--- @param target Target function to call when dispatched.
+-- @param target Target function to call when dispatched.
-- @param title Destination node title
-- @param order Destination node order value (optional)
-- @return Dispatching tree node
function entry(path, target, title, order)
local c = node(unpack(path))
-
+
c.target = target
c.title = title
c.order = order
if #path == 0 then
return context.tree
end
-
+
cache = cache or context.treecache
local name = table.concat(path, ".")
local c = cache[name]
-
+
if not c then
local last = table.remove(path)
c = _create_node(path, cache)
-
+
local new = {nodes={}, auto=true}
c.nodes[last] = new
cache[name] = new
-
+
return new
else
return c
function rewrite(n, ...)
local req = arg
return function()
- for i=1,n do
+ for i=1,n do
table.remove(context.path, 1)
end
-
+
for i,r in ipairs(req) do
table.insert(context.path, i, r)
end
-
+
dispatch()
end
end
--- Create a function-call dispatching target.
--- @param name Target function of local controller
+-- @param name Target function of local controller
-- @param ... Additional parameters passed to the function
function call(name, ...)
local argv = {...}
- return function() return getfenv()[name](unpack(argv)) end
+ return function(...)
+ if #argv > 0 then
+ return getfenv()[name](unpack(argv), ...)
+ else
+ return getfenv()[name](...)
+ end
+ end
end
--- Create a template render dispatching target.
return function(...)
require("luci.cbi")
require("luci.template")
+ local http = require "luci.http"
maps = luci.cbi.load(model, ...)
+ local state = nil
+
for i, res in ipairs(maps) do
- res:parse()
+ local cstate = res:parse()
+ if not state or cstate < state then
+ state = cstate
+ end
end
+ http.header("X-CBI-State", state or 0)
luci.template.render("cbi/header")
for i, res in ipairs(maps) do
res:render()
return function(...)
require("luci.cbi")
require("luci.template")
+ local http = require "luci.http"
maps = luci.cbi.load(model, ...)
+ local state = nil
+
for i, res in ipairs(maps) do
- res:parse()
+ local cstate = res:parse()
+ if not state or cstate < state then
+ state = cstate
+ end
end
+ http.header("X-CBI-State", state or 0)
luci.template.render("header")
for i, res in ipairs(maps) do
res:render()
--- Set the mime type of following content data.
-- @param mime Mimetype of following content
function prepare_content(mime)
- if mime == "application/xhtml+xml" then
- if not getenv("HTTP_ACCEPT") or
- not getenv("HTTP_ACCEPT"):find("application/xhtml+xml", nil, true) then
- mime = "text/html; charset=UTF-8"
+ if not context.headers or not context.headers["content-type"] then
+ if mime == "application/xhtml+xml" then
+ if not getenv("HTTP_ACCEPT") or
+ not getenv("HTTP_ACCEPT"):find("application/xhtml+xml", nil, true) then
+ mime = "text/html; charset=UTF-8"
+ end
+ header("Vary", "Accept")
end
- header("Vary", "Accept")
+ header("Content-Type", mime)
end
- header("Content-Type", mime)
end
--- Get the RAW HTTP input source
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
+You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
+ http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
-- @param force Force reload even if already loaded (optional)
-- @return Success status
function load(file, lang, force)
- lang = lang or ""
+ lang = lang and lang:gsub("_", "-") or ""
if force or not loaded[lang] or not loaded[lang][file] then
local f = loadfile(i18ndir .. file .. "." .. lang .. ".lua")
if f then
--- Set the context default translation language.
-- @param lang Two-letter language code
function setlanguage(lang)
- context.lang = lang
+ context.lang = lang:gsub("_", "-")
end
--- Return the translated value for a specific translation key.
-- @return Translated and formatted string
function translatef(key, default, ...)
return translate(key, default):format(...)
-end
\ No newline at end of file
+end