luci-app-example: improve docs formatting
Split long sentences and make lines shorter. Use syntax highlighting. Signed-off-by: Sergey Ponomarev <stokito@gmail.com>
This commit is contained in:
parent
0223b45e3e
commit
1a027696b7
3 changed files with 54 additions and 35 deletions
|
@ -1,6 +1,10 @@
|
||||||
# Building a LuCI package
|
# 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.
|
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
|
## 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 tools/install`
|
||||||
* Run `make toolchain/install`
|
* Run `make toolchain/install`
|
||||||
|
|
|
@ -1,71 +1,83 @@
|
||||||
# Example app for js based Luci
|
# 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.
|
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):
|
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 root/* root@192.168.1.1:/
|
||||||
scp -r htdocs/* root@192.168.1.1:/www/
|
scp -r htdocs/* root@192.168.1.1:/www/
|
||||||
# execute the UCI defaults script to create the /etc/config/example
|
# execute the UCI defaults script to create the /etc/config/example
|
||||||
ssh root@192.168.1.1 "sh /etc/uci-defaults/80_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.
|
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`
|
`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.
|
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.
|
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/`.
|
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.
|
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.
|
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.
|
This code relies on the same ACL grants as form.js.
|
||||||
|
|
||||||
The signature for `E()` is `E(node_type, {node attributes}, [child nodes])`.
|
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.
|
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.
|
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.
|
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.
|
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:
|
First, declare that you want YAML libraries:
|
||||||
|
|
||||||
```
|
```lua
|
||||||
-- If you need to process YAML, opkg install lyaml
|
-- If you need to process YAML, opkg install lyaml
|
||||||
local lyaml = require "lyaml"
|
local lyaml = require "lyaml"
|
||||||
```
|
```
|
||||||
|
|
||||||
Then, declare a function to handle the YAML data, and a helper to read the file
|
Then, declare a function to handle the YAML data, and a helper to read the file
|
||||||
|
|
||||||
```
|
```lua
|
||||||
local function readfile(path)
|
local function readfile(path)
|
||||||
local s = fs.readfile(path)
|
local s = fs.readfile(path)
|
||||||
return s and (s:gsub("^%s+", ""):gsub("%s+$", ""))
|
return s and (s:gsub("^%s+", ""):gsub("%s+$", ""))
|
||||||
|
@ -38,7 +38,7 @@ end
|
||||||
|
|
||||||
Declare the method in the `methods` table
|
Declare the method in the `methods` table
|
||||||
|
|
||||||
```
|
```lua
|
||||||
-- Converts the AGH YAML configuration into JSON for consumption by
|
-- Converts the AGH YAML configuration into JSON for consumption by
|
||||||
-- the LuCI app.
|
-- the LuCI app.
|
||||||
get_yaml_file_sample = {
|
get_yaml_file_sample = {
|
||||||
|
@ -62,7 +62,7 @@ These are the changes you need in the `rpc.js` file.
|
||||||
|
|
||||||
Declare the RPC call
|
Declare the RPC call
|
||||||
|
|
||||||
```
|
```js
|
||||||
var load_sample_yaml = rpc.declare({
|
var load_sample_yaml = rpc.declare({
|
||||||
object: 'luci.example',
|
object: 'luci.example',
|
||||||
method: 'get_yaml_file_sample'
|
method: 'get_yaml_file_sample'
|
||||||
|
@ -71,7 +71,7 @@ var load_sample_yaml = rpc.declare({
|
||||||
|
|
||||||
Add this declaration to the `view.extend()` call
|
Add this declaration to the `view.extend()` call
|
||||||
|
|
||||||
```
|
```js
|
||||||
render_sample_yaml: function(sample) {
|
render_sample_yaml: function(sample) {
|
||||||
console.log('render_sample_yaml()');
|
console.log('render_sample_yaml()');
|
||||||
console.log(sample);
|
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()`
|
Add a call to the `load` function in `view.extend()`
|
||||||
|
|
||||||
```
|
```js
|
||||||
load: function () {
|
load: function () {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
load_sample_yaml(),
|
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()`
|
Add this code to the `render` function in `view.extend()`
|
||||||
|
|
||||||
```
|
```js
|
||||||
E('div', { 'class': 'cbi-section', 'id': 'cbi-sample-yaml' }, [
|
E('div', { 'class': 'cbi-section', 'id': 'cbi-sample-yaml' }, [
|
||||||
E('div', { 'class': 'left' }, [
|
E('div', { 'class': 'left' }, [
|
||||||
E('h3', _('Sample YAML via RPC')),
|
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
|
Allow access to the new RPC API
|
||||||
|
|
||||||
```
|
```json
|
||||||
"read": {
|
"read": {
|
||||||
"ubus": {
|
"ubus": {
|
||||||
"luci.example": [
|
"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`
|
in `/etc` on the target machine and call it `luci.example.yaml` to match up to the `reading_from_yaml`
|
||||||
function's expectations.
|
function's expectations.
|
||||||
|
|
||||||
```
|
```yaml
|
||||||
top_level_string: example
|
top_level_string: example
|
||||||
top_level_int: 8080
|
top_level_int: 8080
|
||||||
top_level:
|
top_level:
|
||||||
|
|
Loading…
Reference in a new issue