* luci/libs/cbi:
authorJo-Philipp Wich <jow@openwrt.org>
Tue, 23 Sep 2008 00:10:51 +0000 (00:10 +0000)
committerJo-Philipp Wich <jow@openwrt.org>
Tue, 23 Sep 2008 00:10:51 +0000 (00:10 +0000)
- implement file upload fields
- change default enctype in forms to multipart/form-data
- add upload template
* luci/libs/web:
- dispatch file uploads to cbi form fields
* luci/i18n:
- add german and english translations for file upload fields

i18n/english/luasrc/i18n/cbi.en.lua
i18n/english/luasrc/i18n/cbi.en.xml
i18n/german/luasrc/i18n/cbi.de.lua
i18n/german/luasrc/i18n/cbi.de.xml
libs/cbi/luasrc/cbi.lua
libs/cbi/luasrc/view/cbi/header.htm
libs/cbi/luasrc/view/cbi/upload.htm [new file with mode: 0644]
libs/web/luasrc/dispatcher.lua

index 42f93d4ccb7f86f689da1cfcf32774f7a1b7f78e..14b184b9ed1535e3cf0886980720fc54220c80ff 100644 (file)
@@ -1,5 +1,6 @@
 cbi_add = 'Add entry'
 cbi_del = 'Remove entry'
+cbi_replace = 'Replace entry'
 cbi_invalid = 'Invalid input value'
 cbi_invalid_section = '<strong>Validation failed:</strong> Please check any input fields for mistakes.'
 cbi_missing = 'This field is mandatory'
@@ -12,3 +13,4 @@ cbi_manual = '-- custom --'
 cbi_select = '-- Please choose --'
 cbi_gorel = 'Go to relevant configuration page'
 cbi_applying = 'Applying changes'
+cbi_upload = 'Uploaded File'
index 4766ad155f30a6b663956c510e7bb7f9dc12f9e9..c4843b1e72c546c0e631d61af55a4272a935eaf8 100644 (file)
@@ -4,6 +4,7 @@
 
 <i18n:msg xml:id="cbi_add">Add entry</i18n:msg>
 <i18n:msg xml:id="cbi_del">Remove entry</i18n:msg>
+<i18n:msg xml:id="cbi_replace">Replace entry</i18n:msg>
 <i18n:msg xml:id="cbi_invalid">Invalid input value</i18n:msg>
 <i18n:msg xml:id="cbi_invalid_section"><strong>Validation failed:</strong> Please check any input fields for mistakes.</i18n:msg>
 <i18n:msg xml:id="cbi_missing">This field is mandatory</i18n:msg>
@@ -16,5 +17,6 @@
 <i18n:msg xml:id="cbi_select">-- Please choose --</i18n:msg>
 <i18n:msg xml:id="cbi_gorel">Go to relevant configuration page</i18n:msg>
 <i18n:msg xml:id="cbi_applying">Applying changes</i18n:msg>
+<i18n:msg xml:id="cbi_upload">Uploaded File</i18n:msg>
 
 </i18n:msgs>
index 9e690753799d684fc21fab4d40c47cc9cf30cb83..499805c6a973a22de2879393fd0cc1ea752ab83d 100644 (file)
@@ -1,5 +1,6 @@
 cbi_add = 'Eintrag hinzufügen'
 cbi_del = 'Eintrag entfernen'
+cbi_replace = 'Eintrag ersetzen'
 cbi_invalid = 'Ungültige Eingabe'
 cbi_invalid_section = '<strong>Validierung fehlgeschlagen:</strong> Bitte die Eingabefelder auf Fehler überprüfen.'
 cbi_missing = 'Dieses Feld muss ausgefüllt werden'
@@ -11,3 +12,4 @@ cbi_manual = '-- benutzerdefiniert --'
 cbi_select = '-- Bitte auswählen --'
 cbi_gorel = 'Gehe zu relevanter Konfigurationsseite'
 cbi_applying = 'Änderungen werden angewandt'
+cbi_upload = 'hochgeladene Datei'
index c989f8d8db292876dd58a3f27d28f27c3854ed34..ab442aeaf7cb0ca75b3282e7842125447e85bfe8 100644 (file)
@@ -4,6 +4,7 @@
 
 <i18n:msg xml:id="cbi_add">Eintrag hinzufügen</i18n:msg>
 <i18n:msg xml:id="cbi_del">Eintrag entfernen</i18n:msg>
+<i18n:msg xml:id="cbi_replace">Eintrag ersetzen</i18n:msg>
 <i18n:msg xml:id="cbi_invalid">Ungültige Eingabe</i18n:msg>
 <i18n:msg xml:id="cbi_invalid_section"><strong>Validierung fehlgeschlagen:</strong> Bitte die Eingabefelder auf Fehler überprüfen.</i18n:msg>
 <i18n:msg xml:id="cbi_missing">Dieses Feld muss ausgefüllt werden</i18n:msg>
@@ -15,5 +16,6 @@
 <i18n:msg xml:id="cbi_select">-- Bitte auswählen --</i18n:msg>
 <i18n:msg xml:id="cbi_gorel">Gehe zu relevanter Konfigurationsseite</i18n:msg>
 <i18n:msg xml:id="cbi_applying">Änderungen werden angewandt</i18n:msg>
+<i18n:msg xml:id="cbi_upload">hochgeladene Datei</i18n:msg>
 
 </i18n:msgs>
index 97c453cf2f6fc27f20bef8d7d78a831ed5c131e2..8cde0a1777df4ff3be878321868c2ee104c8722e 100644 (file)
@@ -30,6 +30,7 @@ require("luci.template")
 require("luci.util")
 require("luci.http")
 require("luci.uvl")
+require("luci.fs")
 
 local uci        = require("luci.model.uci")
 local class      = luci.util.class
@@ -1328,3 +1329,44 @@ function Button.__init__(self, ...)
        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.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
index ce5d66d77a513322b70b27e34679d16d4691d0c2..3f60baf52cd50605dd532706990b2bfef50afe1b 100644 (file)
@@ -14,7 +14,7 @@ $Id$
 -%>
 
 <%+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" />
diff --git a/libs/cbi/luasrc/view/cbi/upload.htm b/libs/cbi/luasrc/view/cbi/upload.htm
new file mode 100644 (file)
index 0000000..7e0ab1b
--- /dev/null
@@ -0,0 +1,28 @@
+<%#
+LuCI - Lua Configuration Interface
+Copyright 2008 Steven Barth <steven@midlink.org>
+Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
+
+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
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+$Id$
+
+-%>
+
+<%
+       local t = require("luci.tools.webadmin")
+       local v = self:cfgvalue(section)
+-%>
+<%+cbi/valueheader%>
+       <% if v then %>
+               <%:cbi_upload Uploaded File%> (<%=t.byte_format(luci.fs.stat(v).size or 0)%>)
+               <input type="hidden"<%= attr("value", v) .. attr("name", cbid) .. attr("id", cbid) %> />
+               <input type="image" value="<%:cbi_replace%>" name="cbi.rlf.<%=section .. "." .. self.option%>" alt="<%:cbi_replace%>" title="<%:cbi_replace%>" src="<%=resource%>/cbi/reload.gif" />
+       <% else %>
+               <input type="file"<%= attr("name", cbid) .. attr("id", cbid) %> />
+       <% end %>
+<%+cbi/valuefooter%>
index 8e8e19063b8e1149890c3ff608a0ac5f0e44c34f..0665ce42b6baf99e1f01a86433a96ce89ed1d180 100644 (file)
@@ -82,17 +82,17 @@ end
 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.
@@ -110,7 +110,7 @@ function httpdispatch(request)
        if not stat then
                error500(err)
        end
-       
+
        luci.http.close()
 
        --context._disable_memtrace()
@@ -122,15 +122,15 @@ function dispatch(request)
        --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
@@ -144,7 +144,7 @@ function dispatch(request)
                end
 
                util.update(track, c)
-               
+
                if c.leaf then
                        break
                end
@@ -159,7 +159,7 @@ function dispatch(request)
        if track.i18n then
                require("luci.i18n").loadc(track.i18n)
        end
-       
+
        -- Init template engine
        if not track.notemplate then
                local tpl = require("luci.template")
@@ -174,23 +174,23 @@ function dispatch(request)
                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)
@@ -221,19 +221,19 @@ function dispatch(request)
 
        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()
@@ -244,7 +244,7 @@ end
 function createindex()
        local path = luci.util.libpath() .. "/controller/"
        local suff = ".lua"
-       
+
        if luci.util.copcall(require, "luci.fastindex") then
                createindex_fastindex(path, suff)
        else
@@ -257,14 +257,14 @@ end
 -- @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
@@ -286,9 +286,9 @@ function createindex_plain(path, suffix)
 
                        index = loadfile(indexcache)()
                        return index
-               end             
+               end
        end
-       
+
        index = {}
 
        local controllers = util.combine(
@@ -300,12 +300,12 @@ function createindex_plain(path, suffix)
                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")
@@ -318,16 +318,16 @@ function createtree()
        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
@@ -335,7 +335,7 @@ function createtree()
                setfenv(v, scope)
                v()
        end
-       
+
        return tree
 end
 
@@ -349,24 +349,24 @@ function assign(path, clone, title, order)
        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
@@ -392,19 +392,19 @@ function _create_node(path, cache)
        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
@@ -428,20 +428,20 @@ end
 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 = {...}
@@ -466,6 +466,63 @@ function cbi(model)
 
                maps = luci.cbi.load(model, ...)
 
+               local uploads    = { }
+               local has_upload = false
+
+               for _, map in ipairs(maps) do
+                       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
+
+               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 = "/lib/uci/upload/"..field.name
+                                                               fd = io.open(path, "w")
+                                                               if fd then
+                                                                       cbid = field.name
+                                                                       prm[cbid] = path
+                                                       --      else
+                                                       --              io.stderr:write("E: " .. err .. "\n")
+                                                               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
+
                for i, res in ipairs(maps) do
                        res:parse()
                end