deploy: 68d8c0517a7f4ee095603b1185a31059cc007f0f
[project/luci.git] / ModulesHowTo.md
1 # HowTo: Write Modules
2
3 See [online wiki](https://github.com/openwrt/luci/wiki/ModulesHowTo) for latest version.
4
5 **Note:** If you plan to integrate your module into LuCI, you should read the [Module Reference](./Modules.md) in advance.
6
7 This tutorial describes how to write your own modules for the LuCI WebUI.
8 For this tutorial we refer to your LuCI installation directory as `lucidir` (`/usr/lib/lua/luci` on your OpenWRT device) and assume your LuCI installation is reachable through your webserver via `http://192.168.1.1/cgi-bin/luci`.
9
10 The recommended way to set up development environment:
11
12 Install OpenWRT on your router/device (You could use a QEMU or VirtualBox image instead)
13
14 Install SSHFS on your host
15
16 Mount your routers' root (/) someplace on your development host (eg. /mnt/router)
17
18 Then open /mnt/router/(lucidir) in your favorite development studio
19
20 Extra: Add configurations to your dev studio which will delete the luci cache (detailed below) and then open a browser window to your routers' configuration page in order to see your module/application.
21
22
23 When testing, if you have edited index files, be sure to remove the folder `/tmp/luci-modulecache/*` and the file(s) `/tmp/luci-indexcache*`, then refresh the LUCI page to see your edits.
24
25 ## Show me the way (The dispatching process)
26 To write a module you need to understand the basics of the dispatching process in LuCI.
27 LuCI uses a dispatching tree that will be built by executing the index-Function of every available controller.
28 The CGI-environment variable `PATH_INFO` will be used as the path in this dispatching tree, e.g.: `/cgi-bin/luci/foo/bar/baz`
29 will be resolved to `foo.bar.baz`
30
31 To register a function in the dispatching tree, you can use the `entry`-function of `luci.dispatcher`. It takes 4 arguments (2 are optional):
32 ```lua
33 entry(path, target, title=nil, order=nil)
34 ```
35
36 * `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`.
37 * `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 this page
38 * `title` defines the title that will be visible to the user in the menu (optional)
39 * `order` is a number with which nodes on the same level will be sorted in the menu (optional)
40
41 You can assign more attributes by manipulating the node table returned by the entry-function. A few example attributes:
42
43 * `i18n` defines which translation file should be automatically loaded when the page gets requested
44 * `dependent` protects plugins to be called out of their context if a parent node is missing
45 * `leaf` stops parsing the request at this node and goes no further in the dispatching tree
46 * `sysauth` requires the user to authenticate with a given system user account
47
48
49 # It's all about names (Naming and the module file)
50 Now that you know the basics about dispatching, we can start writing modules. Now, choose the category and name of your new digital child.
51
52 Let's assume you want to create a new application `myapp` with a module `mymodule`.
53
54 So you have to create a new sub-directory `lucidir/controller/myapp` with a file `mymodule.lua` with the following content:
55 ```lua
56 module("luci.controller.myapp.mymodule", package.seeall)
57
58 function index()
59
60 end
61 ```
62
63 The first line is required for Lua to correctly identify the module and create its scope.
64 The `index`-Function will be used to register actions in the dispatching tree.
65
66
67
68 ## Teaching your new child (Actions)
69 So it has a name, but no actions.
70
71 We assume you want to reuse your module myapp.mymodule that you began in the last step.
72
73
74 ### Actions
75 Reopen `lucidir/controller/myapp/mymodule.lua` and just add a function to it with:
76 ```lua
77 module("luci.controller.myapp.mymodule", package.seeall)
78
79 function index()
80 entry({"click", "here", "now"}, call("action_tryme"), "Click here", 10).dependent=false
81 end
82
83 function action_tryme()
84 luci.http.prepare_content("text/plain")
85 luci.http.write("Haha, rebooting now...")
86 luci.sys.reboot()
87 end
88 ```
89
90 And now visit the path `/cgi-bin/luci/click/here/now` (`http://192.168.1.1/luci/click/here/now` if you are using the development environment) in your browser.
91
92 These action functions simply have to be added to a dispatching entry.
93
94 As you may or may 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`
95
96 ### Views
97 If you only want to show the user text or some interesting family photos, it may be enough to use an HTML-template.
98 These templates can also include some Lua code but be aware that writing whole office-suites by only using these templates might be considered "dirty" by other developers.
99
100 Now let's create a little template `lucidir/view/myapp-mymodule/helloworld.htm` with the content:
101
102 ```html
103 <%+header%>
104 <h1><%:Hello World%></h1>
105 <%+footer%>
106 ```
107
108
109 and add the following line to the `index`-Function of your module file.
110 ```lua
111 entry({"my", "new", "template"}, template("myapp-mymodule/helloworld"), "Hello world", 20).dependent=false
112 ```
113
114 Now visit the path `/cgi-bin/luci/my/new/template` (`http://192.168.1.1/luci/my/new/template`) in your browser.
115
116 You may notice those special `<% %>`-Tags, these are [template markups](./Templates.md) used by the LuCI template processor.
117 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.
118
119 ### CBI models
120 The CBI is one of the coolest features of LuCI.
121 It creates a formulae based user interface and saves its contents to a specific UCI config file.
122 You only have to describe the structure of the configuration file in a CBI model file and Luci does the rest of the work.
123 This includes generating, parsing and validating an XHTML form and reading and writing the UCI file.
124
125 So let's be serious at least for this paragraph and create a practical example `lucidir/model/cbi/myapp-mymodule/netifaces.lua` with the following contents:
126
127 ```lua
128 m = Map("network", "Network") -- We want to edit the uci config file /etc/config/network
129
130 s = m:section(TypedSection, "interface", "Interfaces") -- Especially the "interface"-sections
131 s.addremove = true -- Allow the user to create and remove the interfaces
132 function s:filter(value)
133 return value ~= "loopback" and value -- Don't touch loopback
134 end
135 s:depends("proto", "static") -- Only show those with "static"
136 s:depends("proto", "dhcp") -- or "dhcp" as protocol and leave PPPoE and PPTP alone
137
138 p = s:option(ListValue, "proto", "Protocol") -- Creates an element list (select box)
139 p:value("static", "static") -- Key and value pairs
140 p:value("dhcp", "DHCP")
141 p.default = "static"
142
143 s:option(Value, "ifname", "interface", "the physical interface to be used") -- This will give a simple textbox
144
145 s:option(Value, "ipaddr", translate("ip", "IP Address")) -- Yes, this is an i18n function ;-)
146
147 s:option(Value, "netmask", "Netmask"):depends("proto", "static") -- You may remember this "depends" function from above
148
149 mtu = s:option(Value, "mtu", "MTU")
150 mtu.optional = true -- This one is very optional
151
152 dns = s:option(Value, "dns", "DNS-Server")
153 dns:depends("proto", "static")
154 dns.optional = true
155 function dns:validate(value) -- Now, that's nifty, eh?
156 return value:match("[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+") -- Returns nil if it doesn't match otherwise returns match
157 end
158
159 gw = s:option(Value, "gateway", "Gateway")
160 gw:depends("proto", "static")
161 gw.rmempty = true -- Remove entry if it is empty
162
163 return m -- Returns the map
164 ```
165
166 and of course remember to add something like this to your module's `index`-Function.
167 ```lua
168 entry({"admin", "network", "interfaces"}, cbi("myapp-mymodule/netifaces"), "Network interfaces", 30).dependent=false
169 ```
170
171 There are many more features. See [the CBI reference](./CBI.md) and the modules shipped with LuCI.