docs: update documentation

This commit is contained in:
Martin Schröder 2015-10-13 16:51:57 +02:00
parent 868805f26c
commit f7a2e530c5
9 changed files with 205 additions and 171 deletions

View File

@ -1,45 +0,0 @@
# JUCI addons
JUCI itself is only 12 javascript files. Most of the functionality in JUCI comes from plugins.
So how do you create plugins?
JUCI makes it very easy to create addons. The easies way to add a new modules is to create a folder in the plugins directory that has this structure:
plugins/
new-module
src
pages
widgets
somemainfile.js
backend
.. backend scripts go here
Makefile
access.json
menu.json
You should keep this structure same for now because juci right now relies on plugin code to be structured this way.
# the Makefile
Even if you don't put anything in the Makefile, just will use default settings and scan src/{pages,widgets} folders for html and js files. But if you still want to have greater control of what is included then you can manually add your source files to build process by defining a section called Plugin/<yourpluginfoldername> and then alter one of these fields:
define Plugin/<somename>
CODE_LOAD:=<num> - this specifies loading order. It is sometimes important to control load order. Default is 50. Juci core has this set to 01 to load juci before any other modules.
STYLE_LOAD:=<num> - this is the same for styles
TPL_LOAD:=<num> - this is for templates (html). If you load your templates AFTER another module then you can basically override any template in any preceding module by simply creating file with the same name.
JAVASCRIPT-y:=<javascript files> - specify your js files
STYLES-y:=<css> - specify styles
TEMPLATES-y:=<html> specify html templates
endef
You can also create an install hook in case you need to install any extra files to the root when make install command is run.
define Plugin/<yourplugin>/install
PLUGIN_DIR - this variable is set by juci to current plugin directory (absolute path)
CP - set to command used for copying files
INSTALL_DIR - set to command for installing directories
$(1) is install destination
endef

View File

@ -1,35 +0,0 @@
# the $ethernet factory
This is an object for managing layer2 ethernet devices. This includes all
devices that are reported by "ip" command on linux and this includes ADSL,
VDSL, VLAN and various ethernet devices. This module does NOT manage ip level
settings though - that job should be left to the $network module.
## functionality
method: getAdapters - return: promise, callback(list:ethernet_adapter)
returns list of all ethernet adapters that are configurable on the
system. This includes adapters that are currently down or for some
reason not currently present on the system. This function will query
all available adapters through the ip command and then let all
subsystems fill in their own details for each item in the list before
returning the list. This is done by running the list through all
registered layer2 subsystems which are configurable through the gui.
object: ethernet_adapter
This is a structure returned by the above method.
.name: identified name of the adapter.
for wireless adapters this could be the ssid. For ports this would
be LAN1 or WAN etc. The name should be filled in by the subsystem
responsible for managing the adapter.
.id: ethernet device name.
.subsystem: name of the subsystem managing the adapter.
.config: uci configuration for the given adapter.
This can point to subsystem specific configuration for the adapter.

View File

@ -1,43 +0,0 @@
# JUCI file system structure
To make development more straightforward and in order to faciliate easy
searching and editing of files, juci follows a certain set of rules with
regards to file names.
* all pages must have the same filename as the URL hasbang identifying the
page.
WHY: so that you can look at url ex. #!internet-dns and know that this page
is in file internet-dns.[html|js] (or old style internet.dns.[html|js]).
* all page files must be globally unique across all plugins.
WHY: so that you can easily override pages from other plugins. Even if the
files did not have unique names, you would still need to have unique
controllers and directives across ALL plugins that are in use. Therefore this
does not in any way limit you. It is a good thing that allows you to for
instance override default page or widget in a juci theme.
* all widget files must be globally unique for all widgets across all plugins.
WHY: same as for pages. You can't have two html tags with same name in angular
anyway. You can still override files though.
* all widget filenames should have a name that reflects the name of the
directive and the controller for the widget. Keep in mind that angular
directive name is in "camelCase" while the resulting html tag is in
"camel-case" with a dash in between where a capital letter is placed in the
directive.
WHY: because it makes it very easy to navigate a tree of files and right
away go to the file where the widget is implemented. In vim this is a matter of
simply using text search function inside a NERDTree window - but even in geany
TreeViewer it is easy to visually find the file that contains html widget code.
And since names are globally unique, you will only have a file with that name
in a single place. When you then change names of your directive/controller, it
is a good idea to also change the name of the file.
* all lua script objects should have the same filename as the object on ubus.
Well actually, ubus-scriptd will take care of that.

90
docs/juci-structure.md Normal file
View File

@ -0,0 +1,90 @@
# JUCI file system structure
To make development more straightforward and in order to faciliate easy
searching and editing of files, juci follows a certain set of rules with
regards to file names.
PLUGINS
=======
The easiest way to build plugins in juci is to simply include them into the juci plugin tree and they will be built automatically (of course provided that you have selected the plugin in Makefile.local or in the make environment above that builds juci). All plugins in juci follow a standard directory structure. This allows all plugins to be built using the same makefile. You can however add your own custom makefile with custom commands that you want to execute during the build.
plugins/
|-- yourplugin-unique-name/
|-- src/
|-- pages/
|-- widgets/
|-- some_general_js_code.js
|-- Makefile
|-- access.json
THEMES
======
At the buildsystem level juci does not differentiate between plugins and themes. So the same directory structure that is used for plugins will also work for themes.
MAKEFILE
========
Even if you don't put anything in the Makefile, just will use default settings and scan src/{pages,widgets} folders for html and js files. But if you still want to have greater control of what is included then you can manually add your source files to build process by defining a section called Plugin/<yourpluginfoldername> and then alter one of these fields:
define Plugin/<somename>
CODE_LOAD:=<num> - this specifies loading order. It is sometimes important to control load order. Default is 50. Juci core has this set to 01 to load juci before any other modules.
STYLE_LOAD:=<num> - this is the same for styles
TPL_LOAD:=<num> - this is for templates (html). If you load your templates AFTER another module then you can basically override any template in any preceding module by simply creating file with the same name.
JAVASCRIPT-y:=<javascript files> - specify your js files
STYLES-y:=<css> - specify styles
TEMPLATES-y:=<html> specify html templates
endef
You can also create an install hook in case you need to install any extra files to the root when make install command is run.
define Plugin/<yourplugin>/install
PLUGIN_DIR - this variable is set by juci to current plugin directory (absolute path)
CP - set to command used for copying files
INSTALL_DIR - set to command for installing directories
$(1) is install destination
endef
RULES
=====
* all pages must have the same filename as the URL hasbang identifying the
page.
`WHY`: so that you can look at url ex. #!internet-dns and know that this page
is in file internet-dns.[html|js] (or old style internet.dns.[html|js]).
* all page files must be globally unique across all plugins.
`WHY`: so that you can easily override pages from other plugins. Even if the
files did not have unique names, you would still need to have unique
controllers and directives across ALL plugins that are in use. Therefore this
does not in any way limit you. It is a good thing that allows you to for
instance override default page or widget in a juci theme.
* all widget files must be globally unique for all widgets across all plugins.
`WHY`: same as for pages. You can't have two html tags with same name in angular
anyway. You can still override files though.
* all widget filenames should have a name that reflects the name of the
directive and the controller for the widget.
Keep in mind that angular directive name is in "camelCase" while the
resulting html tag is in "camel-case" with a dash in between where a capital
letter is placed in the directive.
`WHY`: because it makes it very easy to navigate a tree of files and right
away go to the file where the widget is implemented. In vim this is a matter of
simply using text search function inside a NERDTree window - but even in geany
TreeViewer it is easy to visually find the file that contains html widget code.
And since names are globally unique, you will only have a file with that name
in a single place. When you then change names of your directive/controller, it
is a good idea to also change the name of the file.
* all lua script objects should have the same filename as the object on ubus.
Well actually, ubus-scriptd will take care of that.

View File

@ -3,8 +3,22 @@ JUCI
JUCI WebGUI is designed for building modern and dynamic web interfaces for embedded devices.
CORE SUBSYTEMS
==============
* [$rpc](rpc.html) - JUCI rpc subsystem for making remote calls to the server
* [$uci](uci.html) - JUCI uci subsystem for managing configuration options in uci on the server
* [$ethernet](ethernet.html) - get information about low level devices
* [$network](network.html) - get information about ip network and clients connected to it
* [$wireless](wireless.html) - get information about wireless clients and wireless devices
TUTORIALS
=========
* [Creating Pages](creating-pages.html) - learn how to setup a new page in juci plugin
CORE OBJECTS
------------
============
The core system of juci is very small. In fact, it is there basically to provide a simple startup code and to bind all other parts together. JUCI is built almost entirely using angular.js and all components are tied together mostly through angular. The core codebase merely provides ways for juci to make RPC calls to the backend and manipulate configuration settings in UCI. The core is located in /juci/src/js/ folder of the source tree.
@ -53,3 +67,4 @@ JUCI language and translation system. This is really just a simple wrapper to ma
# juci/src/js/uci.js
The juci UCI subsystem. Together with juci RPC this system allows to easily handle uci data by providing a convenient getter/setter based interface to uci objects so that values can easily be set inside the browser and then sync-ed automatically to the server with only changed values being sent over the wire.

9
docs/wireless.md Normal file
View File

@ -0,0 +1,9 @@
JUCI WIRELESS SUBSYSTEM
=======================
TODO
METHODS
-------

View File

@ -0,0 +1,36 @@
CREATING PAGES AND WIDGETS
==============
A JUCI based interface is typically built from a number of pages that are accessible through a navigation menu and where each page can consist of unlimited number of widgets that build up the page. Thus pages are relatively static components that you refer to from your theme's menu.json file, while widgets are reusable components that can appear in any number of pages. Widgets are typically refered (or included) into a page by means of using the directive(1) of a widget. In JUCI all widgets should have the same filename as the name of the directive that they implement.
CREATING A PAGE
===============
Create two files in plugins/your_plugin/src/pages/ directory and give them a descriptive (and globally unique name):
|--pages/
|----yourplugin-page-main.js
|----yourplugin-page-main.html
Now you will create an angular controller and a template for your page. Pages typically directly refer to their controller in the page html using ng-controller tag. This is mainly an artifact from when this system was created.
yourplugin-page-main.js:
JUCI.app.controller("yourpluginPageMain", function($scope, $rpc, $uci){
$rpc.juci.system.info().done(function(info){
$scope.text = JSON.stringify(info);
$scope.$apply();
});
});
yourplugin-page-main.html:
<juci-layout-single-column>
<div ng-controller="yourpluginPageMain">
<pre>{{text}}</pre>
</div>
</juci-layout-single-column>
Here we create a page with single column layout (in reality layout-single-column is in fact a juci widget that you can use to setup this kind of layout) and then we add code to make a ubus call to rpc function located in /usr/lib/ubus/juci/system. The result is placed inside a scope variable called *text* and then the view is updated using $scope.$apply(); Note that you only need to do $scope.$apply() here because this is an asynchronous method call. In the main controller function you do not need to call apply() in order to make changes visible - but when result is only available at some later point in time after your code has already exited your controller function then you do need to call $apply() manually.
If you now build juci (after adding CONFIG_PACKAGE_yourplugin=y to Makefile.local) and go to page http://juci/#!/yourplugin-page-main then you should be able to see a JSON representation of the result from the ubus call to get system info.

View File

@ -1,4 +1,5 @@
# $rpc factory
JUCI $rpc SUBSYSTEM
===================
This factory provides access to the rpc subsystem on OpenWRT through
uhttpd-mod-ubus plugin. In juci, all dynamic data requests are done through
@ -6,14 +7,14 @@ json rpc. So most plugins will use this module to communicate with the backend.
The rpc module is available in two ways:
* angular $rpc factory / service
* $rpc variable in the browser window object (you can access it like that in
* angular $rpc factory / service
* $rpc variable in the browser window object (you can access it like that in
browser console)
For the most part you will be using the angular service for doing rpc calls
because most of juci plugins are angular objects.
### accessing $rpc in angular
`accessing $rpc in angular`
JUCI.app.controller("yourController", function($rpc){
$rpc.juci.ui.menu().done(function(){
@ -21,7 +22,7 @@ because most of juci plugins are angular objects.
});
});
### accessing in window
`accessing in window`
$rpc.juci.ui.menu().done(function(){
@ -42,7 +43,8 @@ access.json file and then install it with unique name into
/usr/share/rpcd/acl.d/ folder on the router. Exactly how to configure acl lists
is described in more detail in OpenWRT rpcd documentation.
## rpc object methods
METHODS
=======
$sid: function(sid) -> sid
Set or get current session id for currently active session
@ -72,18 +74,19 @@ is described in more detail in OpenWRT rpcd documentation.
in using current version of rpcd. In the future this may change to
include only the methods availabel through unauthenticated session.
## building the backend
BACKEND
=======
JUCI backend can be any kind of ubus plugin, but juci itself uses lua plugins
that are loaded by the main juci rpcd module. This allows you to write the juci
backend in any scripting language that you like (which of course needs to be
present on the router). JUCI prefers to use lua for backend scripting though.
In order for RPC calls to work, you obviously need a backend that will execute
the call on the actual device. JUCI supports writing backend in any language
that can publish objects on ubus and it also provides support for writing ubus
objects in shell scripts. So you have a very wide choice in how exactly you
will implement your backend. JUCI is for the most part now using Lua to
implement backend functions because it has proven to be dynamic enough and also
fast on actual hardware.
## communicating with the backend
Once your backend is accessible on ubus and you have configured uhttpd to allow
you access to it's ubus methods, you can use the $rpc object to access your
method just as though it would have been any javascript method.
Once your backend publishes it's rpc functions on ubus, they will automatically
become available in juci $rpc object once you reload the page.
Example:
@ -91,3 +94,7 @@ Example:
console.log(JSON.stringify(result));
});
You do however need to configure proper access rights for your ubus methods
before you can call them. This is part of the access list (acl) configuration.

View File

@ -1,42 +1,42 @@
# JUCI Ethernet Module
JUCI Ethernet Subsystem
=========
The ethernet module is responsible for managing ethernet devices (layer2) and
providing a single point of entry for any ethernet related queries. It allows
you to add plugins that also annotate existing ethernet devices and add other
devices that belong to some specific subsystem such as adsl or wireless.
Ethernet module gets it's information from "ip" command. After that it pipes
the list of devices through all subsystems that it knows about. j Each
subsystem can then either annotate existing devices - such as setting a proper
device name based on wifi ssid or port configuration - or it can also add new
devices to the list that may currently not be up (and thus not visible thorugh
the ip command).
This module is the core ethernet subsytem that ties together any other plugins that manage ethernet devices of some kind. It allows the user to have a standard interface for querying ethernet adapters. For example if you have a plugin for configuring vendor specific wireless settings then you would register that plugin as a subsystem of $ethernet and you will then be able to provide your vendor specific data as part of the list of adapters as it is returned by `getAdapters` method.
## Methods of $ethernet
This plugin gets most of it's information from the ip(8) command and so it will always return a list of all adapters that the system knows about. However, things like adapter names and proper presentation of adapters is usually specific to the vendor. For example, you could have an ethernet port that is tied to an ethernet port with label LAN2 printed on the box. In this case this information will be probably provided by some board specific configuration file where you specifically assign interface names to the actual port numbers which are printed on the cover of the box. This is where subsystems come to resque because they allow you to implement your vendor speciffic package separately and then to annotate the existing list of adapters inside your callback which you register with the $ethernet subsystem.
addSubsystem(subsys) -> void
METHODS
=======
These are methods provided by the `$ethernet` factory when you add it to your parameter list to you page or widget controller.
`addSubsystem(subsys)` -> void
adds a new subsystem to list of subsystems. subsys can implement
annotateAdapters(adapter:list) to modify the list of ethernet adapters
before it will be returned to the user.
adds a new subsystem to list of subsystems. subsys can implement
annotateAdapters(adapter:list) to modify the list of ethernet adapters
before it will be returned to the user.
getAdapters() -> promise
`getAdapters()` -> promise
returns the list of ethernet adapters currently present on the system
(both adapters that are actually present and the ones that are
configured but may not yet be up).
returns the list of ethernet adapters currently present on the system
(both adapters that are actually present and the ones that are
configured but may not yet be up).
## Methods of ethernet:subsystem
EXPECTED SUBSYSTEM INTERFACE
=======
annotateAdapters(adapter:list) -> promise
These are methods that a subsystem would provide when it registers itself with the `$ethernet` subsystem.
This method is called by the ethernet subsystem to annotate the list of
adapters before it is returned to the user. It takes the list of
adapters to be annotated as parameter and returns a promise that should
resolve once the operation has completed. The method should do what
it's name suggests - it should annotate the list by either modifying
it's existing properties in place or by adding new ones. This method
returns a promise because it is common that such an operation will look
inside other configuration files on the server before returning the
modified list - all of which usually involves asynchronous operations.
`annotateAdapters(adapter:list)` -> promise
This method is called by the ethernet subsystem to annotate the list of
adapters before it is returned to the user. It takes the list of
adapters to be annotated as parameter and returns a promise that should
resolve once the operation has completed. The method should do what
it's name suggests - it should annotate the list by either modifying
it's existing properties in place or by adding new ones. This method
returns a promise because it is common that such an operation will look
inside other configuration files on the server before returning the
modified list - all of which usually involves asynchronous operations.