diff --git a/applications/luci-app-example/BUILDING.md b/applications/luci-app-example/BUILDING.md index 3ed65e3a0dba..4a48841f31df 100644 --- a/applications/luci-app-example/BUILDING.md +++ b/applications/luci-app-example/BUILDING.md @@ -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` diff --git a/applications/luci-app-example/README.md b/applications/luci-app-example/README.md index f12a62b4644b..938309cc32f6 100644 --- a/applications/luci-app-example/README.md +++ b/applications/luci-app-example/README.md @@ -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 -`js-beautify -t -a -j -w 110 -r ` -# 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. diff --git a/applications/luci-app-example/YAML.md b/applications/luci-app-example/YAML.md index 36f5ba8e6cc2..a97048a4d2de 100644 --- a/applications/luci-app-example/YAML.md +++ b/applications/luci-app-example/YAML.md @@ -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: diff --git a/applications/luci-app-example/structure.md b/applications/luci-app-example/structure.md index e03847f4246c..6d3b67e820ee 100644 --- a/applications/luci-app-example/structure.md +++ b/applications/luci-app-example/structure.md @@ -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 diff --git a/modules/luci-base/htdocs/luci-static/resources/form.js b/modules/luci-base/htdocs/luci-static/resources/form.js index 66ba0302087f..b1e7d2c39358 100644 --- a/modules/luci-base/htdocs/luci-static/resources/form.js +++ b/modules/luci-base/htdocs/luci-static/resources/form.js @@ -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 */ diff --git a/modules/luci-base/htdocs/luci-static/resources/fs.js b/modules/luci-base/htdocs/luci-static/resources/fs.js index b64af04e56f8..cf64cfdf62e7 100644 --- a/modules/luci-base/htdocs/luci-static/resources/fs.js +++ b/modules/luci-base/htdocs/luci-static/resources/fs.js @@ -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.