Merge pull request #6859 from stokito/luci-mod-example
[minor] chore JsDoc and luci-app-example
This commit is contained in:
commit
87734b841d
6 changed files with 74 additions and 55 deletions
|
@ -1,6 +1,10 @@
|
|||
# Building a LuCI package
|
||||
|
||||
Essentially, you follow the [build system](https://openwrt.org/docs/guide-developer/toolchain/use-buildsystem) instructions to fetch the OpenWrt repository, update the `feeds.conf.default` to point `luci` at a local directory, build out the full toolchain, and then follow the instructions for a [single package](https://openwrt.org/docs/guide-developer/toolchain/single.package) to build the `.opkg` file for the example app.
|
||||
Essentially, you follow the [build system](https://openwrt.org/docs/guide-developer/toolchain/use-buildsystem) instructions:
|
||||
1. Fetch the OpenWrt repository.
|
||||
2. Update the `feeds.conf.default` to point `luci` at a local directory
|
||||
3. Build out the full toolchain
|
||||
4. Then follow the instructions for a [single package](https://openwrt.org/docs/guide-developer/toolchain/single.package) to build the `.opkg` file for the example app.
|
||||
|
||||
Wiki documentation overrides this file.
|
||||
|
||||
|
@ -32,7 +36,8 @@ If you're doing a whole new application, instead of editing this one, you can us
|
|||
|
||||
## Toolchain build
|
||||
|
||||
Even though you're only building a simple JS + Lua package, you'll need the whole toolchain. Though the command says "install", nothing is actually installed outside of the working directory (`~/src/openwrt` in this case).
|
||||
Even though you're only building a simple JS + Lua package, you'll need the whole toolchain.
|
||||
Though the command says "install", nothing is actually installed outside of the working directory (`~/src/openwrt` in this case).
|
||||
|
||||
* Run `make tools/install`
|
||||
* Run `make toolchain/install`
|
||||
|
|
|
@ -1,71 +1,83 @@
|
|||
# Example app for js based Luci
|
||||
|
||||
This app is meant to be a starting point for developing new LuCI apps using the modern JavaScript client-rendered approach (versus the older Lua server-side render approach).
|
||||
This app is meant to be a starting point for developing new LuCI apps using the modern JavaScript client-rendered approach.
|
||||
Previously the LuCI used a Lua server-side render approach which id deprecated now.
|
||||
|
||||
# Installation
|
||||
## Installation
|
||||
|
||||
In all cases, you'll want to log out of the web interface and back in to force a cache refresh after installing the new package.
|
||||
|
||||
## From git
|
||||
### From git
|
||||
|
||||
To install the luci-app-example to your OpenWrt instance (assuming your OpenWRT instance is on 192.168.1.1):
|
||||
|
||||
```
|
||||
```sh
|
||||
scp -r root/* root@192.168.1.1:/
|
||||
scp -r htdocs/* root@192.168.1.1:/www/
|
||||
# execute the UCI defaults script to create the /etc/config/example
|
||||
ssh root@192.168.1.1 "sh /etc/uci-defaults/80_example"
|
||||
```
|
||||
|
||||
## From packages
|
||||
### From packages
|
||||
|
||||
Install the app on your OpenWrt installation. This can be an actual router/device, or something like a QEMU virtual machine.
|
||||
|
||||
`opkg install luci-app-example`
|
||||
|
||||
Visit the web UI for the device/virtual machine where the package was installed, log in to OpenWrt, and **Example** should be present in the navigation menu.
|
||||
Visit the web UI for the device/virtual machine where the package was installed.
|
||||
Log in to OpenWrt, and **Example** should be present in the navigation menu.
|
||||
|
||||
# Application structure
|
||||
## Application structure
|
||||
|
||||
See `structure.md` for details on how to lay out a LuCI application.
|
||||
|
||||
# Code format
|
||||
## Code format
|
||||
|
||||
The LuCI Javascript code should be indented with tabs. js-beautify/jsbeautifier can help with this; the examples in this application were formatted with
|
||||
The LuCI Javascript code should be indented with tabs.
|
||||
`js-beautify/jsbeautifier` can help with this.
|
||||
The examples in this application were formatted with:
|
||||
|
||||
`js-beautify -t -a -j -w 110 -r <filename>`
|
||||
js-beautify -t -a -j -w 110 -r <filename>
|
||||
|
||||
# Editing the code
|
||||
|
||||
## Editing the code
|
||||
|
||||
You can either do direct editing on the device/virtual machine, or use something like sshfs to have remote access from your development computer.
|
||||
|
||||
By default, the code is minified by the build process, which makes editing it non-trivial. You can either change the build process, or just copy the file content from the git repository and replace the content on disk.
|
||||
By default, the code is minified by the build process, which makes editing it non-trivial.
|
||||
You can either change the build process, or just copy the file content from the git repository and replace the content on disk.
|
||||
|
||||
Javascript code can be found on the device/virtual machine in `/www/luci-static/resources/view/example/`.
|
||||
|
||||
## [form.js](./htdocs/luci-static/resources/view/example/form.js)
|
||||
### [form.js](./htdocs/luci-static/resources/view/example/form.js)
|
||||
|
||||
This is a JS view that uses the **form.Map** approach to providing a form that can change the configuration. It relies on UCI access, and the relevant ACL declarations are in `root/usr/share/rpcd/acl.d/luci-app-example.json`.
|
||||
This is a JS view that uses the **form.Map** approach to providing a form that can change the configuration.
|
||||
It relies on UCI access, and the relevant ACL declarations are in `root/usr/share/rpcd/acl.d/luci-app-example.json`.
|
||||
|
||||
The declarations are `luci-app-example > read > uci` and `luci-app-example > write > uci`. Note that for both permissions, the node name "example" is provided as a list argument to the interface type (**uci**); this maps to `/etc/config/example`.
|
||||
The declarations are `luci-app-example > read > uci` and `luci-app-example > write > uci`.
|
||||
Note that for both permissions, the node name "example" is provided as a list argument to the interface type (**uci**); this maps to `/etc/config/example`.
|
||||
|
||||
Since form.Map and form.JSONMap create Promises, you cannot embed them inside a `E()`-built structure.
|
||||
|
||||
## [htmlview.js](./htdocs/luci-static/resources/view/example/htmlview.js)
|
||||
### [htmlview.js](./htdocs/luci-static/resources/view/example/htmlview.js)
|
||||
|
||||
This is a read-only view that uses `E()` to create DOM nodes.
|
||||
|
||||
Data is fetched via the function defined in `load()` - these loads are done as **Promises**, with the promise results stored in an array. Multiple load functions results are available in the array, and can be accessed via a single argument passed to the `render()` function.
|
||||
Data is fetched via the function defined in `load()` - these loads are done as **Promises**, with the promise results stored in an array.
|
||||
Multiple load functions results are available in the array, and can be accessed via a single argument passed to the `render()` function.
|
||||
|
||||
This code relies on the same ACL grants as form.js.
|
||||
|
||||
The signature for `E()` is `E(node_type, {node attributes}, [child nodes])`.
|
||||
|
||||
## [rpc.js](./htdocs/luci-static/resources/view/example/rpc.js)
|
||||
### [rpc.js](./htdocs/luci-static/resources/view/example/rpc.js)
|
||||
|
||||
The RPC JS page is read-only, and demonstrates using RPC calls to get data. It also demonstrates using the JSONMap form object for mapping a configuration to a form, but makes the form read-only for display purposes.
|
||||
The RPC JS page is read-only, and demonstrates using RPC calls to get data.
|
||||
It also demonstrates using the JSONMap form object for mapping a configuration to a form, but makes the form read-only for display purposes.
|
||||
|
||||
The configuration is stored in `/etc/config/example`. The file must exist and created on device boot by UCI defaults script in `/root/etc/uci-defaults/80_example`. The [developer guide](https://openwrt.org/docs/guide-developer/uci-defaults) has more details about UCI defaults.
|
||||
The configuration is stored in `/etc/config/example`.
|
||||
The file must exist and created on device boot by UCI defaults script in `/root/etc/uci-defaults/80_example`.
|
||||
The [developer guide](https://openwrt.org/docs/guide-developer/uci-defaults) has more details about UCI defaults.
|
||||
|
||||
The RPCd script is stored as `/usr/libexec/rpcd/luci.example`, and can be called via ubus.
|
||||
|
||||
|
@ -73,15 +85,17 @@ It relies on RPC access, and the relevant ACL declarations are in `root/usr/shar
|
|||
|
||||
The declaration is `luci-app-example > read > ubus > luci.example`; the list of names under this key is the list of APIs that can be called.
|
||||
|
||||
# ACLs
|
||||
## ACLs
|
||||
|
||||
A small note on ACLs. They are global for the entire web UI - the declaration of **luci-app-example** in a file called `acl.d/luci-app-example` is just a naming convention; nothing enforces that only the code in **luci-app-example** is mutating `/etc/config/example`. Once the ACL is defined to allow reads/writes to a UCI node, any code running from the web UI can make changes to that node.
|
||||
ACLs are global for the entire web UI - the declaration of **luci-app-example** in a file called `acl.d/luci-app-example` is just a naming convention.
|
||||
Nothing enforces that only the code in **luci-app-example** is mutating `/etc/config/example`.
|
||||
Once the ACL is defined to allow reads/writes to a UCI node, any code running from the web UI can make changes to that node.
|
||||
|
||||
# YAML
|
||||
## YAML
|
||||
|
||||
You may wish to work with YAML data. See [YAML.md](YAML.md) for details on how to integrate YAML read support.
|
||||
|
||||
# Translations
|
||||
## Translations
|
||||
|
||||
For a real world application (or changes to this example one that you wish to submit upstream), translations should be kept up to date.
|
||||
|
||||
|
|
|
@ -7,14 +7,14 @@ These are the changes you would need in the `usr/libexec/rpcd/luci.example` file
|
|||
|
||||
First, declare that you want YAML libraries:
|
||||
|
||||
```
|
||||
```lua
|
||||
-- If you need to process YAML, opkg install lyaml
|
||||
local lyaml = require "lyaml"
|
||||
```
|
||||
|
||||
Then, declare a function to handle the YAML data, and a helper to read the file
|
||||
|
||||
```
|
||||
```lua
|
||||
local function readfile(path)
|
||||
local s = fs.readfile(path)
|
||||
return s and (s:gsub("^%s+", ""):gsub("%s+$", ""))
|
||||
|
@ -38,7 +38,7 @@ end
|
|||
|
||||
Declare the method in the `methods` table
|
||||
|
||||
```
|
||||
```lua
|
||||
-- Converts the AGH YAML configuration into JSON for consumption by
|
||||
-- the LuCI app.
|
||||
get_yaml_file_sample = {
|
||||
|
@ -62,7 +62,7 @@ These are the changes you need in the `rpc.js` file.
|
|||
|
||||
Declare the RPC call
|
||||
|
||||
```
|
||||
```js
|
||||
var load_sample_yaml = rpc.declare({
|
||||
object: 'luci.example',
|
||||
method: 'get_yaml_file_sample'
|
||||
|
@ -71,7 +71,7 @@ var load_sample_yaml = rpc.declare({
|
|||
|
||||
Add this declaration to the `view.extend()` call
|
||||
|
||||
```
|
||||
```js
|
||||
render_sample_yaml: function(sample) {
|
||||
console.log('render_sample_yaml()');
|
||||
console.log(sample);
|
||||
|
@ -96,7 +96,7 @@ Add this declaration to the `view.extend()` call
|
|||
|
||||
Add a call to the `load` function in `view.extend()`
|
||||
|
||||
```
|
||||
```js
|
||||
load: function () {
|
||||
return Promise.all([
|
||||
load_sample_yaml(),
|
||||
|
@ -107,7 +107,7 @@ Add a call to the `load` function in `view.extend()`
|
|||
|
||||
Add this code to the `render` function in `view.extend()`
|
||||
|
||||
```
|
||||
```js
|
||||
E('div', { 'class': 'cbi-section', 'id': 'cbi-sample-yaml' }, [
|
||||
E('div', { 'class': 'left' }, [
|
||||
E('h3', _('Sample YAML via RPC')),
|
||||
|
@ -121,7 +121,7 @@ Add this code to the `render` function in `view.extend()`
|
|||
|
||||
Allow access to the new RPC API
|
||||
|
||||
```
|
||||
```json
|
||||
"read": {
|
||||
"ubus": {
|
||||
"luci.example": [
|
||||
|
@ -138,7 +138,7 @@ Set up the sample YAML file, by placing it either in `root/etc` of the developme
|
|||
in `/etc` on the target machine and call it `luci.example.yaml` to match up to the `reading_from_yaml`
|
||||
function's expectations.
|
||||
|
||||
```
|
||||
```yaml
|
||||
top_level_string: example
|
||||
top_level_int: 8080
|
||||
top_level:
|
||||
|
|
|
@ -3,31 +3,31 @@
|
|||
```
|
||||
.
|
||||
├── htdocs
|
||||
│ └── luci-static
|
||||
│ └── resources
|
||||
│ └── view
|
||||
│ └── example
|
||||
│ ├── form.js
|
||||
│ ├── htmlview.js
|
||||
│ └── rpc.js
|
||||
│ └── luci-static
|
||||
│ └── resources
|
||||
│ └── view
|
||||
│ └── example
|
||||
│ ├── form.js
|
||||
│ ├── htmlview.js
|
||||
│ └── rpc.js
|
||||
├── Makefile
|
||||
├── po
|
||||
│ ├── templates
|
||||
│ │ └── example.pot
|
||||
│ ├── templates
|
||||
│ │ └── example.pot
|
||||
├── README.md
|
||||
└── root
|
||||
├── etc
|
||||
│ ├── luci.example.yaml
|
||||
│ └── uci-defaults
|
||||
│ └── 80_example
|
||||
│ ├── luci.example.yaml
|
||||
│ └── uci-defaults
|
||||
│ └── 80_example
|
||||
└── usr
|
||||
├── libexec
|
||||
│ └── rpcd
|
||||
│ └── luci.example
|
||||
│ └── rpcd
|
||||
│ └── luci.example
|
||||
└── share
|
||||
├── luci
|
||||
│ └── menu.d
|
||||
│ └── luci-app-example.json
|
||||
│ └── menu.d
|
||||
│ └── luci-app-example.json
|
||||
└── rpcd
|
||||
└── acl.d
|
||||
└── luci-app-example.json
|
||||
|
|
|
@ -4007,7 +4007,7 @@ var CBIFlagValue = CBIValue.extend(/** @lends LuCI.form.FlagValue.prototype */ {
|
|||
* Sets the input value to use for the checkbox checked state.
|
||||
*
|
||||
* @name LuCI.form.FlagValue.prototype#enabled
|
||||
* @type number
|
||||
* @type string
|
||||
* @default 1
|
||||
*/
|
||||
|
||||
|
@ -4015,7 +4015,7 @@ var CBIFlagValue = CBIValue.extend(/** @lends LuCI.form.FlagValue.prototype */ {
|
|||
* Sets the input value to use for the checkbox unchecked state.
|
||||
*
|
||||
* @name LuCI.form.FlagValue.prototype#disabled
|
||||
* @type number
|
||||
* @type string
|
||||
* @default 0
|
||||
*/
|
||||
|
||||
|
|
|
@ -345,7 +345,7 @@ var FileSystem = baseclass.extend(/** @lends LuCI.fs.prototype */ {
|
|||
* @param {string} path
|
||||
* The file path to read.
|
||||
*
|
||||
* @param {"blob"|"text"|"blob"} [type=text]
|
||||
* @param {"blob"|"text"|"json"} [type=text]
|
||||
* The expected type of read file contents. Valid values are `text` to
|
||||
* interpret the contents as string, `json` to parse the contents as JSON
|
||||
* or `blob` to return the contents as Blob instance.
|
||||
|
@ -387,7 +387,7 @@ var FileSystem = baseclass.extend(/** @lends LuCI.fs.prototype */ {
|
|||
* @param {string[]} [params]
|
||||
* The arguments to pass to the command.
|
||||
*
|
||||
* @param {"blob"|"text"|"blob"} [type=text]
|
||||
* @param {"blob"|"text"|"json"} [type=text]
|
||||
* The expected output type of the invoked program. Valid values are
|
||||
* `text` to interpret the output as string, `json` to parse the output
|
||||
* as JSON or `blob` to return the output as Blob instance.
|
||||
|
|
Loading…
Reference in a new issue