Merge pull request #416 from fabio70mi/patch-1
[project/luci.git] / documentation / ModulesHowTo.md
1 *Note:* If you plan to integrate your module into LuCI, you should read the [wiki:Documentation/Modules Module Reference] before.
2
3 This tutorial describes how to write your own modules for the LuCI WebUI.
4 For this tutorial we refer to your LuCI installation direcotry as *lucidir_' (/usr/lib/lua/luci if you are working with an installed version) and assume your LuCI installation is reachable through your webserver via '_/cgi-bin/luci*.
5
6 If you are working with the development environment replace *lucidir_' with '''''/path/to/your/luci/checkout''/applications/myapplication/luasrc''' (this is a default empty module you can use for your experiments) and your LuCI installation can probably be reached via http://localhost:8080/luci/ after you ran '_make runhttpd*.
7
8
9
10 # Show me the way (The dispatching process)
11 To write a module you need to understand the basics of the dispatching process in LuCI.
12 LuCI uses a dispatching tree that will be built by executing the index-Function of every available controller.
13 The CGI-environment variable *PATH_INFO* will be used as the path in this dispatching tree, e.g.: /cgi-bin/luci/foo/bar/baz
14 will be resolved to foo.bar.baz
15
16 To register a function in the dispatching tree, you can use the *entry*-function of _luci.dispatcher_. entry takes 4 arguments (2 are optional):
17         
18         entry(path, target, title=nil, order=nil)
19         
20
21 * *path* is a table that describes the position in the dispatching tree: For example a path of {"foo", "bar", "baz"} would insert your node in foo.bar.baz.
22 * *target* describes the action that will be taken when a user requests the node. There are several predefined ones of which the 3 most important (call, template, cbi) are described later on on this page
23 * *title* defines the title that will be visible to the user in the menu (optional)
24 * *order* is a number with which nodes on the same level will be sorted in the menu (optional)
25
26 You can assign more attributes by manipulating the node table returned by the entry-function. A few example attributes:
27
28 * *i18n* defines which translation file should be automatically loaded when the page gets requested
29 * *dependent* protects plugins to be called out of their context if a parent node is missing
30 * *leaf* stops parsing the request at this node and goes no further in the dispatching tree
31 * *sysauth* requires the user to authenticate with a given system user account
32
33
34 # It's all about names (Naming and the module file)
35 Now that you know the basics about dispatching, we can start writing modules. But before you have to choose the category and name of your new digital child.
36
37 We assume you want to create a new application "myapp" with a module "mymodule".
38
39 So you have to create a new subdirectory *_lucidir''/controller/myapp''' with a file '_mymodule.lua* with the following content:
40         
41         module("luci.controller.myapp.mymodule", package.seeall)
42         
43         function index()
44         
45         end
46         
47
48 The first line is required for Lua to correctly identify the module and create its scope.
49 The index-Function will be used to register actions in the dispatching tree.
50
51
52
53 # Teaching your new child (Actions)
54 So it is there and has a name but it has no actions.
55
56 We assume you want to reuse your module myapp.mymodule that you begun in the last step.
57
58
59 ## Actions
60 Reopen *_lucidir_/controller/myapp/mymodule.lua* and just add a function to it so that its content looks like this example:
61
62         
63         module("luci.controller.myapp.mymodule", package.seeall)
64         
65         function index()
66             entry({"click", "here", "now"}, call("action_tryme"), "Click here", 10).dependent=false
67         end
68          
69         function action_tryme()
70             luci.http.prepare_content("text/plain")
71             luci.http.write("Haha, rebooting now...")
72             luci.sys.reboot()
73         end
74         
75
76 And now type */cgi-bin/luci/click/here/now_' ('_[http://localhost:8080/luci/click/here/now]* if you are using the development environment) in your browser.
77
78 You see these action functions simple have to be added to a dispatching entry.
79
80 As you might or might not know: CGI specification requires you to send a Content-Type header before you can send your content. You will find several shortcuts (like the one used above) as well as redirecting functions in the module *luci.http*
81
82 ## Views
83 If you only want to show the user a text or some interesting familiy photos it may be enough to use a HTML-template. These templates can also include some Lua code but be aware that writing whole office suites by only using these templates might be called "dirty" by other developers.
84
85 Now let's create a little template *_lucidir_/view/myapp-mymodule/helloworld.htm* with the content:
86
87         
88         <%+header%>
89         <h1><%:Hello World%></h1> 
90         <%+footer%>
91         
92
93
94 and add the following line to the index-Function of your module file.
95         
96         entry({"my", "new", "template"}, template("myapp-mymodule/helloworld"), "Hello world", 20).dependent=false
97         
98
99 Now type */cgi-bin/luci/my/new/template_' ('_[http://localhost:8080/luci/my/new/template]* if you are using the development environment) in your browser.
100
101 You may notice those fancy <% %>-Tags, these are [wiki:Documentation/Templates|template markups] used by the LuCI template processor.
102 It is always good to include header and footer at the beginning and end of a template as those create the default design and menu.
103
104 ## CBI models
105 The CBI is one of the uber coolest features of LuCI. It creates a formular based user interface and saves its contents to a specific UCI config file. You only have to describe the structure of the configuration file in a CBI model file and Luci does the rest of the work. This includes generating, parsing and validating a XHTML form and reading and writing the UCI file.
106
107 So let's be serious at least for this paragraph and create a real pratical example *_lucidir_/model/cbi/myapp-mymodule/netifaces.lua* with the following contents:
108
109         
110         m = Map("network", "Network") -- We want to edit the uci config file /etc/config/network
111         
112         s = m:section(TypedSection, "interface", "Interfaces") -- Especially the "interface"-sections
113         s.addremove = true -- Allow the user to create and remove the interfaces
114         function s:filter(value)
115            return value ~= "loopback" and value -- Don't touch loopback
116         end 
117         s:depends("proto", "static") -- Only show those with "static"
118         s:depends("proto", "dhcp") -- or "dhcp" as protocol and leave PPPoE and PPTP alone
119         
120         p = s:option(ListValue, "proto", "Protocol") -- Creates an element list (select box)
121         p:value("static", "static") -- Key and value pairs
122         p:value("dhcp", "DHCP")
123         p.default = "static"
124         
125         s:option(Value, "ifname", "interface", "the physical interface to be used") -- This will give a simple textbox
126         
127         s:option(Value, "ipaddr", translate("ip", "IP Address")) -- Ja, das ist eine i18n-Funktion ;-)
128         
129         s:option(Value, "netmask", "Netmask"):depends("proto", "static") -- You may remember this "depends" function from above
130         
131         mtu = s:option(Value, "mtu", "MTU")
132         mtu.optional = true -- This one is very optional
133         
134         dns = s:option(Value, "dns", "DNS-Server")
135         dns:depends("proto", "static")
136         dns.optional = true
137         function dns:validate(value) -- Now, that's nifty, eh?
138             return value:match("[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+") -- Returns nil if it doesn't match otherwise returns match
139         end
140         
141         gw = s:option(Value, "gateway", "Gateway")
142         gw:depends("proto", "static")
143         gw.rmempty = true -- Remove entry if it is empty
144         
145         return m -- Returns the map
146         
147
148 and of course don't forget to add something like this to your module's index-Function.
149         
150         entry({"admin", "network", "interfaces"}, cbi("myapp-mymodule/netifaces"), "Network interfaces", 30).dependent=false
151         
152
153 There are many more features, see [wiki:Documentation/CBI the CBI reference] and the modules shipped with LuCI.