Add documentation converted from old Trac wiki pages
authorJo-Philipp Wich <jow@openwrt.org>
Tue, 16 Jun 2015 08:11:03 +0000 (10:11 +0200)
committerJo-Philipp Wich <jow@openwrt.org>
Tue, 16 Jun 2015 08:11:45 +0000 (10:11 +0200)
Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
documentation/CBI.md [new file with mode: 0644]
documentation/JsonRpcHowTo.md [new file with mode: 0644]
documentation/LAR.md [new file with mode: 0644]
documentation/LMO.md [new file with mode: 0644]
documentation/LuCI-0.10.md [new file with mode: 0644]
documentation/Modules.md [new file with mode: 0644]
documentation/ModulesHowTo.md [new file with mode: 0644]
documentation/SubmitPatchesHowTo:.md [new file with mode: 0644]
documentation/Templates.md [new file with mode: 0644]
documentation/ThemesHowTo.md [new file with mode: 0644]
documentation/i18n.md [new file with mode: 0644]

diff --git a/documentation/CBI.md b/documentation/CBI.md
new file mode 100644 (file)
index 0000000..0b47c22
--- /dev/null
@@ -0,0 +1,248 @@
+CBI models are Lua files describing the structure of an UCI config file and the resulting HTML form to be evaluated by the CBI parser.
+All CBI model files must return an object of type *luci.cbi.Map*. For a commented example of a CBI model, see the [[Documentation/ModulesHowTo#CBImodels|Writing Modules tutorial]].
+
+The scope of a CBI model file is automatically extended by the contents of the module *luci.cbi_' and the '_translate* function from luci.i18n
+
+This Reference covers *the basics* of the CBI system.
+
+
+
+# class Map (_config'', ''title'', ''description_)
+This is the root object of the model.
+* *config*: configuration name to be mapped, see uci documentation and the files in /etc/config
+* *title*: title shown in the UI
+* *description*: description shown in the UI
+
+## :section (_sectionclass_, ...)
+Creates a new section
+* *sectionclass*: a class object of the section
+* _additional parameters passed to the constructor of the section class_
+
+----
+
+# class NamedSection (_name'', ''type'', ''title'', ''description_)
+An object describing an UCI section selected by the name.
+Use [[#A.3Asection.28.27.27sectionclass.27.27.2C....29|Map:section(NamedSection, _name'', ''type'', ''title'', ''description_)]] to instantiate.
+* *name*: section name
+* *type*: section type
+* *title*: The title shown in the UI
+* *description*: description shown in the UI
+
+## .addremove = false
+Allows the user to remove and recreate the configuration section
+
+## .dynamic = false
+Marks this section as dynamic. Dynamic sections can contain an undefinded number of completely userdefined options.
+
+## .optional = true
+Parse optional options
+
+
+## :option (_optionclass_, ...)
+Creates a new option
+* *optionclass*: a class object of the section
+* _additional parameters passed to the constructor of the option class_
+
+----
+
+# class TypedSection (_type'', ''title'', ''description_)
+An object describing a group of UCI sections selected by their type.
+Use [[#A.3Asection.28.27.27sectionclass.27.27.2C....29|Map:section(TypedSection, _type'', ''title'', ''description_)]] to instantiate.
+* *type*: section type
+* *title*: The title shown in the UI
+* *description*: description shown in the UI
+
+## .addremove = false
+Allows the user to remove and recreate the configuration section
+
+## .dynamic = false
+Marks this section as dynamic. Dynamic sections can contain an undefinded number of completely userdefined options.
+
+## .optional = true
+Parse optional options
+
+## .anonymous = false
+Do not show section names
+
+
+## :depends (_key'', ''value_)
+Only select those sections where the option _key'' == ''value_<br />
+If you call this function several times the dependencies will be linked with *or*
+
+## .filter (_self'', ''section_) [abstract]
+You can override this function to filter certain sections that will not be parsed.
+The filter function will be called for every section that should be parsed and returns *nil* for sections that should be filtered. For all other sections it should return the section name as given in the second parameter.
+
+## :option (_optionclass_, ...)
+Creates a new option
+ _optionclass_: a class object of the section
+ additional parameters passed to the constructor of the option class
+
+----
+
+# class Value (_option'', ''title'', ''description_)
+An object describing an option in a section of a UCI File. Creates a standard text field in the formular.
+Use [[#A.3Aoption.28.27.27optionclass.27.27.2C....29|NamedSection:option(Value, _option'', ''title'', ''description'')]] or [[#A.3Aoption.28.27.27optionclass.27.27.2C....29-1|TypedSection:option(Value, ''option'', ''title'', ''description_)]] to instantiate.
+* *option*: section name
+* *title*: The title shown in the UI
+* *description*: description shown in the UI
+
+## .default = nil
+The default value
+
+## .maxlength = nil
+The maximum length of the value
+
+## .optional = false
+Marks this option as optional, implies .rmempty = true
+
+## .rmempty = true
+Removes this option from the configuration file when the user enters an empty value
+
+## .size = nil
+The size of the form field
+
+## :value (_key'', ''value'' = ''key_)
+Convert this text field into a combobox if possible and add a selection option.
+
+
+## :depends (_key'', ''value_)
+Only show this option field if another option _key'' is set to ''value_ in the same section.<br />
+If you call this function several times the dependencies will be linked with *or*
+
+----
+
+# class ListValue (_option'', ''title'', ''description_)
+An object describing an option in a section of a UCI File. Creates a list box in the formular.
+Use [[#A.3Aoption.28.27.27optionclass.27.27.2C....29|NamedSection:option(Value, _option'', ''title'', ''description'')]] or [[#A.3Aoption.28.27.27optionclass.27.27.2C....29-1|TypedSection:option(Value, ''option'', ''title'', ''description_)]] to instantiate.
+* *option*: section name
+* *title*: The title shown in the UI
+* *description*: description shown in the UI
+
+
+## .default = nil
+The default value
+
+## .optional = false
+Marks this option as optional, implies .rmempty = true
+
+## .rmempty = true
+Removes this option from the configuration file when the user enters an empty value
+
+## .size = nil
+The size of the form field
+
+## .widget = "select"
+selects the form widget to be used
+
+
+## :depends (_key'', ''value_)
+Only show this option field if another option _key'' is set to ''value_ in the same section.<br />
+If you call this function several times the dependencies will be linked with *or*
+
+## :value (_key'', ''value'' = ''key_)
+Adds an entry to the selection list
+
+----
+
+# class Flag (_option'', ''title'', ''description_)
+An object describing an option with two possible values in a section of a UCI File. Creates a checkbox field in the formular.
+Use [[#A.3Aoption.28.27.27optionclass.27.27.2C....29|NamedSection:option(Value, _option'', ''title'', ''description'')]] or [[#A.3Aoption.28.27.27optionclass.27.27.2C....29-1|TypedSection:option(Value, ''option'', ''title'', ''description_)]] to instantiate.
+* *option*: section name
+* *title*: The title shown in the UI
+* *description*: description shown in the UI
+
+## .default = nil
+The default value
+
+## .disabled = 0
+the value that shoudl be set if the checkbox is unchecked
+
+## .enabled = 1
+the value that should be set if the checkbox is checked
+
+## .optional = false
+Marks this option as optional, implies .rmempty = true
+
+## .rmempty = true
+Removes this option from the configuration file when the user enters an empty value
+
+## .size = nil
+The size of the form field
+
+
+## :depends (_key'', ''value_)
+Only show this option field if another option _key'' is set to ''value_ in the same section.<br />
+If you call this function several times the dependencies will be linked with *or*
+
+----
+
+# class MultiValue (_option'', ''title'', ''description_)
+An object describing an option in a section of a UCI File. Creates several checkboxed as form fields.
+Use [[#A.3Aoption.28.27.27optionclass.27.27.2C....29|NamedSection:option(Value, _option'', ''title'', ''description'')]] or [[#A.3Aoption.28.27.27optionclass.27.27.2C....29-1|TypedSection:option(Value, ''option'', ''title'', ''description_)]] to instantiate.
+* *option*: section name
+* *title*: The title shown in the UI
+* *description*: description shown in the UI
+
+
+## .default = nil
+The default value
+
+## .delimiter = " "
+The string which will be used to delimit the values
+
+## .optional = false
+Marks this option as optional, implies .rmempty = true
+
+## .rmempty = true
+Removes this option from the configuration file when the user enters an empty value
+
+## .size = nil
+The size of the form field
+
+## .widget = "checkbox"
+selects the form widget to be used
+
+
+## :depends (_key'', ''value_)
+Only show this option field if another option _key'' is set to ''value_ in the same section.<br />
+If you call this function several times the dependencies will be linked with *or*
+
+## :value (_key'', ''value'' = ''key_)
+Adds an entry to the checkbox list
+
+----
+
+# class DummyValue (_option'', ''title'', ''description_)
+An object describing an option in a section of a UCI File. Creates a readonly field in the form.
+Use [[#A.3Aoption.28.27.27optionclass.27.27.2C....29|NamedSection:option(Value, _option'', ''title'', ''description'')]] or [[#A.3Aoption.28.27.27optionclass.27.27.2C....29-1|TypedSection:option(Value, ''option'', ''title'', ''description_)]] to instantiate.
+* *option*: section name
+* *title*: The title shown in the UI
+* *description*: description shown in the UI
+
+
+
+## :depends (_key'', ''value_)
+Only show this option field if another option _key'' is set to ''value_ in the same section.<br />
+If you call this function several times the dependencies will be linked with *or*
+
+----
+
+
+# class TextValue (_option'', ''title'', ''description_)
+An object describing a multi-line textbox in a section in a non-UCI form.
+
+----
+
+# class Button (_option'', ''title'', ''description_)
+An object describing a Button in a section in a non-UCI form.
+
+----
+
+# class StaticList (_option'', ''title'', ''description_)
+Similar to the MultiValue, but stores selected Values into a UCI list instead of a space-separated string.
+
+----
+
+# class DynamicList (_option'', ''title'', ''description_)
+A list of user-defined values.
diff --git a/documentation/JsonRpcHowTo.md b/documentation/JsonRpcHowTo.md
new file mode 100644 (file)
index 0000000..76d61f8
--- /dev/null
@@ -0,0 +1,66 @@
+LuCI provides some of its libraries to external applications through a JSON-RPC API.
+This Howto shows how to use it and provides information about available functions.
+
+
+# Basics
+LuCI comes with an efficient JSON De-/Encoder together with a JSON-RPC-Server which implements the *JSON-RPC 1.0_' and 2.0 (partly) specifications. The LuCI JSON-RPC server offers several independent APIs. Therefore you have to use '_different URLs for every exported library*.
+Assuming your LuCI-Installation can be reached through */cgi-bin/luci_' any exported library can be reached via '''/cgi-bin/luci/rpc/''LIBRARY_*.
+
+
+# Authentication
+Most exported libraries will require a valid authentication to be called with. If you get an *HTTP 403 Forbidden_' status code you are probably missing a valid authentication token. To get such a token you have to call the function '''login''' of the RPC-Library '''auth'''. Following our example from above this login function would be provided at '_/cgi-bin/luci/rpc/auth*. The function accepts 2 parameters: username and password (of a valid user account on the host system) and returns an authentication token.
+
+If you want to call any exported library which requires an authentication token you have to *append it as an URL parameter _auth''''' to the RPC-Server URL. So instead of calling '''/cgi-bin/luci/rpc/''LIBRARY''''' you have to call '''/cgi-bin/luci/rpc/''LIBRARY''?auth=''TOKEN_*.
+
+If your JSON-RPC client is Cookie-aware (like most browsers are) you will receive the authentication token also with a session cookie and probably don't have to append it to the RPC-Server URL.
+
+
+# Exported Libraries
+## uci
+The UCI-Library */rpc/uci* offers functionality to interact with the Universal Configuration Interface.
+*Exported Functions:*
+* [(string) add(config, type)](http://luci.subsignal.org/api/luci/modules/luci.model.uci.html#Cursor.add)
+* [(integer) apply(config)](http://luci.subsignal.org/api/luci/modules/luci.model.uci.html#Cursor.apply)
+* [http://luci.subsignal.org/api/luci/modules/luci.model.uci.html#Cursor.changes (object) changes([config])]
+* [(boolean) commit(config)](http://luci.subsignal.org/api/luci/modules/luci.model.uci.html#Cursor.commit)
+* [http://luci.subsignal.org/api/luci/modules/luci.model.uci.html#Cursor.delete (boolean) delete(config, section[, option])]
+* [http://luci.subsignal.org/api/luci/modules/luci.model.uci.html#Cursor.delete_all (boolean) delete_all(config[, type])]
+* [http://luci.subsignal.org/api/luci/modules/luci.model.uci.html#Cursor.foreach (array) foreach(config[, type])]
+* [http://luci.subsignal.org/api/luci/modules/luci.model.uci.html#Cursor.get (mixed) get(config, section[, option])]
+* [http://luci.subsignal.org/api/luci/modules/luci.model.uci.html#Cursor.get_all (object) get_all(config[, section])]
+* [http://luci.subsignal.org/api/luci/modules/luci.model.uci.html#Cursor.get (mixed) get_state(config, section[, option])]
+* [(boolean) revert(config)](http://luci.subsignal.org/api/luci/modules/luci.model.uci.html#Cursor.revert)
+* [(name) section(config, type, name, values)](http://luci.subsignal.org/api/luci/modules/luci.model.uci.html#Cursor.section)
+* [(boolean) set(config, section, option, value)](http://luci.subsignal.org/api/luci/modules/luci.model.uci.html#Cursor.set)
+* [(boolean) tset(config, section, values)](http://luci.subsignal.org/api/luci/modules/luci.model.uci.html#Cursor.tset)
+
+## uvl
+The UVL-Library */rpc/uvl* offers functionality to validate UCI files and get schemes describing UCI files.
+*Exported Functions:*
+* [(array) get_scheme(scheme)](http://luci.subsignal.org/api/luci/modules/luci.uvl.html#UVL.get_scheme)
+* [(array) validate(config, section, option)](http://luci.subsignal.org/api/luci/modules/luci.uvl.html#UVL.validate)
+* [(array) validate_config(config)](http://luci.subsignal.org/api/luci/modules/luci.uvl.html#UVL.validate_config)
+* [(array) validate_section(config, section)](http://luci.subsignal.org/api/luci/modules/luci.uvl.html#UVL.validate_section)
+* [(array) validate(config, section, option)](http://luci.subsignal.org/api/luci/modules/luci.uvl.html#UVL.validate_option)
+
+## fs
+The Filesystem library */rpc/fs* offers functionality to interact with the filesystem on the host machine.
+*Exported Functions:*
+
+* [Complete luci.fs library](http://luci.subsignal.org/api/luci/modules/luci.fs.html)
+*Note:* All functions are exported as they are except for _readfile'' which encodes its return value in base64 and ''writefile'' which only accepts base64 encoded data as second argument. Note that both functions will only be available when the ''luasocket_ packet is installed on the hostsystem.
+
+## sys
+The System library */rpc/sys* offers functionality to interact with the operating system on the host machine.
+*Exported Functions:*
+* [Complete luci.sys library](http://luci.subsignal.org/api/luci/modules/luci.sys.html)
+* [Complete luci.sys.group library](http://luci.subsignal.org/api/luci/modules/luci.sys.group.html) with prefix *group.*
+* [Complete luci.sys.net library](http://luci.subsignal.org/api/luci/modules/luci.sys.net.html) with prefix *net.*
+* [Complete luci.sys.process library](http://luci.subsignal.org/api/luci/modules/luci.sys.process.html) with prefix *process.*
+* [Complete luci.sys.user library](http://luci.subsignal.org/api/luci/modules/luci.sys.user.html) with prefix *user.*
+* [Complete luci.sys.wifi library](http://luci.subsignal.org/api/luci/modules/luci.sys.wifi.html) with prefix *wifi.*
+
+## ipkg
+The IPKG library */rpc/ipkg* offers functionality to interact with the package manager (IPKG or OPKG) on the host machine.
+*Exported Functions:*
+* [Complete luci.model.ipkg library](http://luci.subsignal.org/api/luci/modules/luci.model.ipkg.html)
diff --git a/documentation/LAR.md b/documentation/LAR.md
new file mode 100644 (file)
index 0000000..f44d8dc
--- /dev/null
@@ -0,0 +1,87 @@
+LAR is a simple archive format to pack multiple lua source files and arbitary other resources into a single file.
+
+
+# Format Specification
+
+A LAR archive file is divided into two parts: the payload and the index lookup table.
+All segments of the archive are 4 Byte aligned to ease reading and processing of the format.
+All integers are stored in network byte order, so an implementation has to use htonl() and htons() to properly read them.
+
+Schema:
+       
+       <payload:
+         <member:
+           <N*4 bytes: path of file #1>
+           <N*4 bytes: data of file #1>
+         >
+         
+         <member:
+           <N*4 bytes: path of file #2>
+           <N*4 bytes: data of file #2>
+         >
+       
+         ...
+       
+         <member:
+           <N*4 bytes: path of file #N>
+           <N*4 bytes: data of file #N>
+         >
+       >
+       
+       <index table:
+         <entry:
+           <uint32: offset for path of file #1> <uint32: length for path of file #1>
+           <uint32: offset for data of file #1> <uint32: length for data of file #1>
+           <uint16: type of file #1> <uint16: flags of file #1>
+         >
+       
+         <entry:
+           <uint32: offset for path of file #2> <uint32: length for path of file #2>
+           <uint32: offset for data of file #2> <uint32: length for data of file #2>
+           <uint16: type of file #2> <uint16: flags of file #2>
+         >
+       
+         ...
+       
+         <entry:
+           <uint32: offset for path of file #N> <uint32: length for path of file #N>
+           <uint32: offset for data of file #N> <uint32: length for data of file #N>
+           <uint16: type of file #N> <uint16: flags of file #N>
+         >
+       >
+       
+       <uint32: offset for begin of index table>
+       
+
+
+# Processing
+
+In order to process an LAR archive, an implementation would have to do the following steps:
+
+## Read Index
+
+1. Locate and open the archive file
+1. Seek to end of file - 4 bytes
+1. Read 32bit index offset and swap from network to native byte order
+1. Seek to index offset, calculate index length: filesize - index offset - 4
+1. Initialize a linked list for index table entries
+1. Read each index entry until the index length is reached, read and byteswap 4 * 32bit int and 2 * 16bit int
+1. Seek to begin of file
+
+## Read Member
+
+1. Read the archive index
+1. Iterate through the linked index list, perform the following steps for each entry
+1. Seek to the specified file path offset
+1. Read as much bytes as specified in the file path length into a buffer
+1. Compare the contents of the buffer against the path of the searched member
+1. If buffer and searched path are equal, seek to the specified file data offset
+1. Read data until the file data length is reached, return
+1. Select the next index table entry and repeat from step 3, if there is no next entry then return
+
+# Reference implementation
+
+A reference implementation can be found here:
+http://luci.subsignal.org/trac/browser/luci/trunk/contrib/lar
+
+The lar.pl script is a simple packer for LAR archives and cli.c provides a utility to list and dump packed LAR archives.
diff --git a/documentation/LMO.md b/documentation/LMO.md
new file mode 100644 (file)
index 0000000..961a45b
--- /dev/null
@@ -0,0 +1,144 @@
+LMO is a simple binary format to pack language strings into a more efficient form. Although it's suitable to store any kind of key-value table, it's only used for the LuCI *.po based translation system at the moment. The abbreviation "LMO" stands for "Lua Machine Objects" in the style of the GNU gettext *.mo format.
+
+
+# Format Specification
+
+A LMO file is divided into two parts: the payload and the index lookup table.
+All segments of the file are 4 Byte aligned to ease reading and processing of the format.
+Only unsigned 32bit integers are used and stored in network byte order, so an implementation has to use htonl() to properly read them.
+
+Schema:
+       
+       <file:
+         <payload:
+           <entry #1: 4 byte aligned data>
+       
+           <entry #2: 4 byte aligned data>
+       
+           ...
+       
+           <entry #N: 4 byte aligned data>
+         >
+       
+         <index table:
+           <entry #1:
+             <uint32_t: hash of the first key>
+             <uint32_t: hash of the first value>
+             <uint32_t: file offset of the first value>
+             <uint32_t: length of the first value>
+           >
+       
+           <entry #2:
+             <uint32_t: hash of the second key>
+             <uint32_t: hash of the second value>
+             <uint32_t: file offset of the second value>
+             <uint32_t: length of the second value>
+           >
+       
+           ...
+       
+           <entry #N:
+             <uint32_t: hash of the Nth key>
+             <uint32_t: hash of the Nth value>
+             <uint32_t: file offset of the Nth value>
+             <uint32_t: length of the Nth value>
+           >
+         >
+       
+         <uint32_t: offset of the begin of index table>
+       >
+       
+
+
+# Processing
+
+In order to process a LMO file, an implementation would have to do the following steps:
+
+## Read Index
+
+1. Locate and open the archive file
+1. Seek to end of file - 4 bytes (sizeof(uint32_t))
+1. Read 32bit index offset and swap from network to native byte order
+1. Seek to index offset, calculate index length: filesize - index offset - 4
+1. Initialize a linked list for index table entries
+1. Read each index entry until the index length is reached, read and byteswap 4 * uint32_t for each step
+1. Seek to begin of file
+
+## Read Entry
+
+1. Calculate the unsigned 32bit hash of the entries key value (see "Hash Function" section below)
+1. Obtain the archive index
+1. Iterate through the linked index list, perform the following steps for each entry:
+  1. Compare the entry hash value with the calculated hash from step 1
+  2. If the hash values are equal proceed with step 4
+  3. Select the next entry and repeat from step 3.1
+1. Seek to the file offset specified in the selected entry
+1. Read as much bytes as specified in the entry length into a buffer
+1. Return the buffer value
+
+# Hash Function
+
+The current LuCI-LMO implementation uses the "Super Fast Hash" function which was kindly put in the public domain by it's original author. See http://www.azillionmonkeys.com/qed/hash.html for details. Below is the C-Implementation of this function:
+
+       
+       #if (defined(__GNUC__) && defined(__i386__))
+       #define sfh_get16(d) (*((const uint16_t *) (d)))
+       #else
+       #define sfh_get16(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\
+                                                  +(uint32_t)(((const uint8_t *)(d))[0]) )
+       #endif
+       
+       uint32_t sfh_hash(const char * data, int len)
+       {
+               uint32_t hash = len, tmp;
+               int rem;
+       
+               if (len <= NULL) return 0;
+       
+               rem = len & 3;
+               len >>= 2;
+       
+               /* Main loop */
+               for (;len > 0; len--) {
+                       hash  += sfh_get16(data);
+                       tmp    = (sfh_get16(data+2) << 11) ^ hash;
+                       hash   = (hash << 16) ^ tmp;
+                       data  += 2*sizeof(uint16_t);
+                       hash  += hash >> 11;
+               }
+       
+               /* Handle end cases */
+               switch (rem) {
+                       case 3: hash += sfh_get16(data);
+                               hash ^= hash << 16;
+                               hash ^= data[sizeof(uint16_t)] << 18;
+                               hash += hash >> 11;
+                               break;
+                       case 2: hash += sfh_get16(data);
+                               hash ^= hash << 11;
+                               hash += hash >> 17;
+                               break;
+                       case 1: hash += *data;
+                               hash ^= hash << 10;
+                               hash += hash >> 1;
+               }
+       
+               /* Force "avalanching" of final 127 bits */
+               hash ^= hash << 3;
+               hash += hash >> 5;
+               hash ^= hash << 4;
+               hash += hash >> 17;
+               hash ^= hash << 25;
+               hash += hash >> 6;
+       
+               return hash;
+       }
+       
+
+# Reference Implementation
+
+A reference implementation can be found here:
+http://luci.subsignal.org/trac/browser/luci/trunk/libs/lmo/src
+
+The lmo_po2lmo.c executable implements a *.po to *.lmo conversation utility and lmo_lookup.c is a simple *.lmo test utility.
+Lua bindings for lmo are defined in lmo_lualib.c and associated headers.
diff --git a/documentation/LuCI-0.10.md b/documentation/LuCI-0.10.md
new file mode 100644 (file)
index 0000000..5db9895
--- /dev/null
@@ -0,0 +1,202 @@
+[[PageOutline(2-5, Table of Contents, floated)]]
+
+
+This document describes new features and incompatibilities to LuCI 0.9.x.
+It is targeted at module authors developing external addons to LuCI.
+
+# I18N Changes
+
+## API
+
+The call conventions for the i18n api changed, there is no dedicated translation
+key anymore and the english text is used for lookup instead. This was done to
+ease the maintenance of language files.
+
+Code that uses _translate()'' or ''i18n()_ must be changed as follows:
+
+       
+       -- old style:
+       translate("some_text", "Some Text")
+       translatef("some_format_text", "Some formatted Text: %d", 123)
+       
+       -- new style:
+       translate("Some Text")
+       translatef("Some formatted Text: %d", 123)
+       
+
+Likewise for templates:
+
+       
+       <!-- old style: -->
+       <%:some_text Some Text%>
+       
+       <!-- new style: -->
+       <%:Some Text%>
+       
+
+If code must support both LuCI 0.9.x and 0.10.x versions, it is suggested to write the calls as follows:
+       
+       translate("Some Text", "Some Text")
+       
+
+An alternative is wrapping translate() calls into a helper function:
+       
+       function tr(key, alt)
+           return translate(key) or translate(alt) or alt
+       end
+       
+
+... which is used as follows:
+       
+       tr("some_key", "Some Text")
+       
+
+## Translation File Format
+
+Translation catalogs are now maintained in *.po format files. During build those get translated
+into [*.lmo archives](http://luci.subsignal.org/trac/wiki/Documentation/LMO).
+
+LuCI ships a [utility script](http://luci.subsignal.org/trac/browser/luci/branches/luci-0.10/build/i18n-lua2po.pl)
+in the build/ directory to convert old Lua translation files to the *.po format. The generated *.po files should
+be placed in the appropriate subdirectories within the top po/ file in the LuCI source tree.
+
+### Components built within the LuCI tree
+
+If components using translations are built along with the LuCI tree, the newly added *.po file are automatically
+compiled into *.lmo archives during the build process. In order to bundle the appropriate *.lmo files into the
+corresponding *.ipk packages, component Makefiles must include a "PO" variable specifying the files to include.
+
+Given a module _applications/example/'' which uses ''po/en/example.po'' and ''po/en/example-extra.po_,
+the _applications/example/Makefile_ must be changed as follows:
+
+       
+       PO = example example-extra
+       
+       include ../../build/config.mk
+       include ../../build/module.mk
+       
+
+### Standalone components
+
+Authors who externally package LuCI components must prepare required *.lmo archives themselves.
+To convert existing Lua based message catalogs to the *.po format, the build/i18n-lua2po.pl helper script can be used.
+In order to convert *.po files into *.lmo files, the standalone "po2lmo" utility must be compiled as follows:
+
+       
+       $ svn co http://svn.luci.subsignal.org/luci/branches/luci-0.10/libs/lmo
+       $ cd lmo/
+       $ make
+       $ ./src/po2lmo translations.po translations.lmo
+       
+
+Note that at the time of writing, the utility program needs Lua headers installed on the system in order to compile properly.
+
+# CBI
+
+## Datatypes
+
+The server side UVL validation has been dropped to reduce space requirements on the target.
+Instead it is possible to define datatypes for CBI widgets now:
+
+       
+       opt = section:option(Value, "optname", "Title Text")
+       opt.datatype = "ip4addr"
+       
+
+User provided data is validated once on the frontend via JavaScript and on the server side prior to saving it.
+A list of possible datatypes can be found in the [luci.cbi.datatypes](http://luci.subsignal.org/trac/browser/luci/branches/luci-0.10/libs/web/luasrc/cbi/datatypes.lua#L26) class.
+
+## Validation
+
+Server-sided validator function can now return custom error messages to provide better feedback on invalid input.
+
+       
+       opt = section:option(Value, "optname", "Title Text")
+       
+       function opt.validate(self, value, section)
+           if input_is_valid(value) then
+               return value
+           else
+               return nil, "The value is invalid because ..."
+           end
+       end
+       
+
+## Tabs
+
+It is now possible to break up CBI sections into multiple tabs to better organize longer forms.
+The TypedSection and NamedSection classes gained two new functions to define tabs, _tab()'' and ''taboption()_.
+
+       
+       sct = map:section(TypedSection, "name", "type", "Title Text")
+       
+       sct:tab("general", "General Tab Title", "General Tab Description")
+       sct:tab("advanced", "Advanced Tab Title", "Advanced Tab Description")
+       
+       opt = sct:taboption("general", Value, "optname", "Title Text")
+       ...
+       
+
+The _tab()_ function is declares a new tab and takes up to three arguments:
+  * Internal name of the tab, must be unique within the section
+  * Title text of the tab
+  * Optional description text for the tab
+
+The _taboption()'' function wraps ''option()_ and assigns the option object to the given tab.
+It takes up to five arguments:
+
+  * Name of the tab to assign the option to
+  * Option type, e.g. Value or DynamicList
+  * Option name
+  * Title text of the option
+  * Optional description text of the option
+
+If tabs are used within a particular section, the _option()_ function must not be used,
+doing so results in undefined behaviour.
+
+## Hooks
+
+The CBI gained support for _hooks_ which can be used to trigger additional actions during the
+life-cycle of a map:
+
+       
+       map = Map("config", "Title Text")
+       
+       function map.on_commit(self)
+           -- do something if the UCI configuration got committed
+       end
+       
+
+The following hooks are defined:
+
+|| on_cancel || The user pressed cancel within a multi-step Delegator or a SimpleForm instance || 
+|| on_init || The CBI is about to render the Map object ||
+|| on_parse || The CBI is about to read received HTTP form values ||
+|| on_save, on_before_save || The CBI is about to save modified UCI configuration files ||
+|| on_after_save || Modified UCI configuration files just got saved
+|| on_before_commit || The CBI is about to commit the changes ||
+|| on_commit, on_after_commit, on_before_apply || Modified configurations got committed and the CBI is about to restart associated services ||
+|| on_apply, on_after_apply || All changes where completely applied (only works on Map instances with the apply_on_parse attribute set) ||
+
+## Sortable Tables
+
+TypedSection instances which use the "cbi/tblsection" template may now use a new attribute _sortable_ to allow the user to reorder table rows.
+
+       
+       sct = map:section(TypedSection, "name", "type", "Title Text")
+       sct.template = "cbi/tblsection"
+       sct.sortable = true
+       
+       ...
+       
+
+# JavaScript
+
+The LuCI 0.10 branch introduced a new JavaScript file _xhr.js_ which provides support routines for XMLHttpRequest operations.
+Each theme must include this file in the <head> area of the document for forms to work correctly.
+
+It should be included like this:
+
+       
+       <script type="text/javascript" src="<%=resource%>/xhr.js"></script>
+       
\ No newline at end of file
diff --git a/documentation/Modules.md b/documentation/Modules.md
new file mode 100644 (file)
index 0000000..2897df9
--- /dev/null
@@ -0,0 +1,94 @@
+# Categories
+
+The LuCI modules are divided into several category directories, namely:
+* applications (Single applications or plugins for other modules or applications)
+* i18n (Translation files)
+* libs (Independent libraries)
+* modules (Collections of applications)
+* themes (Frontend themes)
+
+Each module goes into a subdirectory of any of this category-directories.
+
+# Module directory
+The contents of a module directory are as follows:
+
+## Makefile
+This is the module's makefile. If the module just contains Lua sourcecode or resources then the following Makefile should suffice.
+       
+       include ../../build/config.mk
+       include ../../build/module.mk
+       
+
+If you have C(++) code in your module your Makefile should at least contain the following things.
+       
+       include ../../build/config.mk
+       include ../../build/gccconfig.mk
+       include ../../build/module.mk
+       
+       compile:
+           # Commands to compile and link your C-code
+           # and to install them under the dist/ hierarchy
+       
+       clean: luaclean
+           # Commands to clean your compiled objects
+       
+
+## src
+The *src* directory is reserved for C sourcecode.
+
+## luasrc
+*luasrc* contains all Lua sourcecode files. These will automatically be stripped or compiled depending on the Make target and are installed in the LuCI installation directory.
+
+## lua
+*lua* is equivalent to _luasrc_ but containing Lua files will be installed in the Lua document root.
+
+## htdocs
+All files under *htdocs* will be copied to the document root of the target webserver.
+
+## root
+All directories and files under *root* will be copied to the installation target as they are.
+
+## dist
+*dist* is reserved for the builder to create a working installation tree that will represent the filesystem on the target machine.
+*DO NOT* put any files there as they will get deleted.
+
+## ipkg
+*ipkg* contains IPKG package control files, like _preinst'', ''posinst'', ''prerm'', ''postrm''. ''conffiles_.
+See IPKG documentation for details.
+
+
+# OpenWRT feed integration
+If you want to add your module to the LuCI OpenWRT feed you have to add several sections to the contrib/package/luci/Makefile.
+
+For a Web UI applications this is:
+
+A package description:
+       
+       define Package/luci-app-YOURMODULE
+         $(call Package/luci/webtemplate)
+         DEPENDS+=+some-package +some-other-package
+         TITLE:=SHORT DESCRIPTION OF YOURMODULE
+       endef
+       
+       
+
+A package installation target:
+       
+       define Package/luci-app-YOURMODULE/install
+               $(call Package/luci/install/template,$(1),applications/YOURMODULE)
+       endef
+       
+
+A module build instruction:
+       
+       ifneq ($(CONFIG_PACKAGE_luci-app-YOURMODULE),)
+               PKG_SELECTED_MODULES+=applications/YOURMODULE
+       endif
+       
+
+
+A build package call:
+       
+       $(eval $(call BuildPackage,luci-app-YOURMODULE))
+       
diff --git a/documentation/ModulesHowTo.md b/documentation/ModulesHowTo.md
new file mode 100644 (file)
index 0000000..3f70b78
--- /dev/null
@@ -0,0 +1,153 @@
+*Note:* If you plan to integrate your module into LuCI, you should read the [wiki:Documentation/Modules Module Reference] before.
+
+This tutorial describes how to write your own modules for the LuCI WebUI.
+For this tutorial we refer to your LuCI installation direcotry as *lucidir_' (/usr/lib/lua/luci if you are working with an installed version) and assume your LuCI installation is reachable through your webserver via '_/cgi-bin/luci*.
+
+If you are working with the development environment replace *lucidir_' with '''''/path/to/your/luci/checkout''/applications/myapplication/luasrc''' (this is a default empty module you can use for your experiments) and your LuCI installation can probably be reached via http://localhost:8080/luci/ after you ran '_make runhttpd*.
+
+
+
+# Show me the way (The dispatching process)
+To write a module you need to understand the basics of the dispatching process in LuCI.
+LuCI uses a dispatching tree that will be built by executing the index-Function of every available controller.
+The CGI-environment variable *PATH_INFO* will be used as the path in this dispatching tree, e.g.: /cgi-bin/luci/foo/bar/baz
+will be resolved to foo.bar.baz
+
+To register a function in the dispatching tree, you can use the *entry*-function of _luci.dispatcher_. entry takes 4 arguments (2 are optional):
+       
+       entry(path, target, title=nil, order=nil)
+       
+
+* *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.
+* *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 on this page
+* *title* defines the title that will be visible to the user in the menu (optional)
+* *order* is a number with which nodes on the same level will be sorted in the menu (optional)
+
+You can assign more attributes by manipulating the node table returned by the entry-function. A few example attributes:
+
+* *i18n* defines which translation file should be automatically loaded when the page gets requested
+* *dependent* protects plugins to be called out of their context if a parent node is missing
+* *leaf* stops parsing the request at this node and goes no further in the dispatching tree
+* *sysauth* requires the user to authenticate with a given system user account
+
+
+# It's all about names (Naming and the module file)
+Now that you know the basics about dispatching, we can start writing modules. But before you have to choose the category and name of your new digital child.
+
+We assume you want to create a new application "myapp" with a module "mymodule".
+
+So you have to create a new subdirectory *_lucidir''/controller/myapp''' with a file '_mymodule.lua* with the following content:
+       
+       module("luci.controller.myapp.mymodule", package.seeall)
+       
+       function index()
+       
+       end
+       
+
+The first line is required for Lua to correctly identify the module and create its scope.
+The index-Function will be used to register actions in the dispatching tree.
+
+
+
+# Teaching your new child (Actions)
+So it is there and has a name but it has no actions.
+
+We assume you want to reuse your module myapp.mymodule that you begun in the last step.
+
+
+## Actions
+Reopen *_lucidir_/controller/myapp/mymodule.lua* and just add a function to it so that its content looks like this example:
+
+       
+       module("luci.controller.myapp.mymodule", package.seeall)
+       
+       function index()
+           entry({"click", "here", "now"}, call("action_tryme"), "Click here", 10).dependent=false
+       end
+        
+       function action_tryme()
+           luci.http.prepare_content("text/plain")
+           luci.http.write("Haha, rebooting now...")
+           luci.sys.reboot()
+       end
+       
+
+And now type */cgi-bin/luci/click/here/now_' ('_[http://localhost:8080/luci/click/here/now]* if you are using the development environment) in your browser.
+
+You see these action functions simple have to be added to a dispatching entry.
+
+As you might or might 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*
+
+## Views
+If you only want to show the user a text or some interesting familiy photos it may be enough to use a HTML-template. These templates can also include some Lua code but be aware that writing whole office suites by only using these templates might be called "dirty" by other developers.
+
+Now let's create a little template *_lucidir_/view/myapp-mymodule/helloworld.htm* with the content:
+
+       
+       <%+header%>
+       <h1><%:Hello World%></h1> 
+       <%+footer%>
+       
+
+
+and add the following line to the index-Function of your module file.
+       
+       entry({"my", "new", "template"}, template("myapp-mymodule/helloworld"), "Hello world", 20).dependent=false
+       
+
+Now type */cgi-bin/luci/my/new/template_' ('_[http://localhost:8080/luci/my/new/template]* if you are using the development environment) in your browser.
+
+You may notice those fancy <% %>-Tags, these are [wiki:Documentation/Templates|template markups] used by the LuCI template processor.
+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.
+
+## CBI models
+The CBI is one of the uber coolest features of LuCI. It creates a formular based user interface and saves its contents to a specific UCI config file. You only have to describe the structure of the configuration file in a CBI model file and Luci does the rest of the work. This includes generating, parsing and validating a XHTML form and reading and writing the UCI file.
+
+So let's be serious at least for this paragraph and create a real pratical example *_lucidir_/model/cbi/myapp-mymodule/netifaces.lua* with the following contents:
+
+       
+       m = Map("network", "Network") -- We want to edit the uci config file /etc/config/network
+       
+       s = m:section(TypedSection, "interface", "Interfaces") -- Especially the "interface"-sections
+       s.addremove = true -- Allow the user to create and remove the interfaces
+       function s:filter(value)
+          return value ~= "loopback" and value -- Don't touch loopback
+       end 
+       s:depends("proto", "static") -- Only show those with "static"
+       s:depends("proto", "dhcp") -- or "dhcp" as protocol and leave PPPoE and PPTP alone
+       
+       p = s:option(ListValue, "proto", "Protocol") -- Creates an element list (select box)
+       p:value("static", "static") -- Key and value pairs
+       p:value("dhcp", "DHCP")
+       p.default = "static"
+       
+       s:option(Value, "ifname", "interface", "the physical interface to be used") -- This will give a simple textbox
+       
+       s:option(Value, "ipaddr", translate("ip", "IP Address")) -- Ja, das ist eine i18n-Funktion ;-)
+       
+       s:option(Value, "netmask", "Netmask"):depends("proto", "static") -- You may remember this "depends" function from above
+       
+       mtu = s:option(Value, "mtu", "MTU")
+       mtu.optional = true -- This one is very optional
+       
+       dns = s:option(Value, "dns", "DNS-Server")
+       dns:depends("proto", "static")
+       dns.optional = true
+       function dns:validate(value) -- Now, that's nifty, eh?
+           return value:match("[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+") -- Returns nil if it doesn't match otherwise returns match
+       end
+       
+       gw = s:option(Value, "gateway", "Gateway")
+       gw:depends("proto", "static")
+       gw.rmempty = true -- Remove entry if it is empty
+       
+       return m -- Returns the map
+       
+
+and of course don't forget to add something like this to your module's index-Function.
+       
+       entry({"admin", "network", "interfaces"}, cbi("myapp-mymodule/netifaces"), "Network interfaces", 30).dependent=false
+       
+
+There are many more features, see [wiki:Documentation/CBI the CBI reference] and the modules shipped with LuCI.
diff --git a/documentation/SubmitPatchesHowTo:.md b/documentation/SubmitPatchesHowTo:.md
new file mode 100644 (file)
index 0000000..cdd1552
--- /dev/null
@@ -0,0 +1,33 @@
+# Checkout svn
+       
+       svn co http://svn.luci.subsignal.org/luci/trunk
+       
+and change to that directory:
+       
+       cd trunk
+       
+# Make your changes
+
+Edit the files you want to change. If you add some new files you need to add them to the svn tree:
+       
+       svn add <dir/files>
+       
+Where <dir/files> are the directories/files you have added. Its possible to specify multiple files/directories here.
+
+# Use svn diff to generate a patch with your changes
+
+To check if your changes look ok first do:
+       
+       svn diff <dir/files>
+       
+and check the output. Again you can specify multiple dirs/directories here.
+
+If everything looks like expected save the patch:
+       
+       svn diff <dir/files> > ./mypatch.patch
+       
+
+# Submit patches
+
+Use the [Ticket system](http://luci.subsignal.org/trac/newticket) to submit your patch.
+
diff --git a/documentation/Templates.md b/documentation/Templates.md
new file mode 100644 (file)
index 0000000..6affd7f
--- /dev/null
@@ -0,0 +1,66 @@
+LuCI has a simple regex based template processor which parses HTML-files to Lua functions and allows to store precompiled template files.
+The simplest form of a template is just an ordinary HTML-file. It will be printed out to the user as is.
+
+In LuCI every template is an object with an own scope. It can therefore be instanciated and each instance can has a different scope. As every template processor. LuCI supports several special markups. Those are enclosed in *<% %>-Tags*.
+
+By adding a *-_' right after the opening '''<%''' every whitespace before the markup will be stripped. Adding a '''-''' right before the closing '_%>* will equivalently strip every whitespace behind the markup.
+
+<<BR>>
+
+
+# Builtin functions and markups
+## Including Lua code
+*Markup:*
+       
+       <% code %>
+       
+
+
+## Writing variables and function values
+*Syntax:*
+       
+       <% write (value) %>
+       
+
+*Short-Markup:*
+       
+       <%=value%>
+       
+
+## Including templates
+*Syntax:*
+       
+       <% include (templatename) %>
+       
+
+*Short-Markup:*
+       
+       <%+templatename%>
+       
+
+
+## Translating
+*Syntax:*
+       
+       <%= translate("Text to translate") %>
+       
+
+
+*Short-Markup:*
+       
+       <%:Text to translate%>
+       
+
+
+## Commenting
+*Markup:*
+       
+       <%# comment %>
+       
+
+# Builtin constants
+||*Name_'||'_Value*||
+||REQUEST_URI||The current URL (without server part)||
+||controller||Path to the Luci main dispatcher||
+||resource||Path to the resource directory||
+||media||Path to the active theme directory||
diff --git a/documentation/ThemesHowTo.md b/documentation/ThemesHowTo.md
new file mode 100644 (file)
index 0000000..46b95fc
--- /dev/null
@@ -0,0 +1,76 @@
+# HowTo: Create Themes
+*Note:* You should read the [wiki:Documentation/Modules Module Reference] and the [wiki:Documentation/Templates Template Reference] before.
+
+We assume you want to call your new theme _mytheme_. Make sure you replace this by your module name everytime this is mentionend in this Howto.
+
+
+
+# Creating the structure
+At first create a new theme directory *themes/_mytheme_*.
+
+Create a _Makefile_ inside your theme directory with the following content:
+       
+       include ../../build/config.mk
+       include ../../build/module.mk
+       
+
+Create the following directory structure inside your theme directory.
+* ipkg
+* htdocs
+  * luci-static
+   * _mytheme_
+* luasrc
+  * view
+   * themes
+    * _mytheme_
+* root
+  * etc
+   * uci-defaults
+
+
+
+# Designing
+Create two LuCI HTML-Templates named _header.htm'' and ''footer.htm'' under *luasrc/view/themes/''mytheme_*.
+The _header.htm'' will be included at the beginning of each rendered page and the ''footer.htm_ at the end.
+So your _header.htm'' will probably contain a DOCTYPE description, headers, the menu and layout of the page and the ''footer.htm_ will close all remaining open tags and may add a footer bar but hey that's your choice you are the designer ;-).
+
+Just make sure your _header.htm_ *begins* with the following lines:
+       
+       <%
+       require("luci.http").prepare_content("text/html")
+       -%>
+       
+
+This makes sure your content will be sent to the client with the right content type. Of course you can adapt _text/html_ to your needs.
+
+
+Put any stylesheets, Javascripts, images, ... into *htdocs/luci-static/_mytheme_*.
+You should refer to this directory in your header and footer templates as: _<%=media%>''. That means for a stylesheet *htdocs/luci-static/''mytheme_/cascade.css* you would write:
+       
+       <link rel="stylesheet" type="text/css" href="<%=media%>/cascade.css" />
+       
+
+
+
+# Making the theme selectable
+If you are done with your work there are two last steps to do.
+To make your theme OpenWRT-capable and selectable on the settings page you should now create a file *root/etc/uci-defaults/luci-theme-_mytheme_* with the following contents:
+       
+       #!/bin/sh
+       uci batch <<-EOF
+               set luci.themes.MyTheme=/luci-static/mytheme
+               commit luci
+       EOF
+       
+
+and another file *ipkg/postinst* with the following content:
+       
+       #!/bin/sh
+       [ -n "${IPKG_INSTROOT}" ] || {
+               ( . /etc/uci-defaults/luci-theme-mytheme ) &&        rm -f /etc/uci-defaults/luci-theme-mytheme
+       }
+       
+
+This is some OpenWRT magic to correctly register the template with LuCI when it gets installed.
+
+That's all. Now send your theme to the LuCI developers to get it into the development repository - if you like.
diff --git a/documentation/i18n.md b/documentation/i18n.md
new file mode 100644 (file)
index 0000000..fdacb08
--- /dev/null
@@ -0,0 +1,17 @@
+# General
+Translations are saved in the folder po/. You find the reference in po/templates/<package>.pot. The actual translation files can be found at po/<lang>/<package>.po .
+
+In order to use the commands below you need to have the _gettext'' utilities (''msgcat'', ''msgfmt'', ''msgmerge_) installed on your system.
+
+# Rebuild po files
+If you want to rebuild the translations after you made changes to a package this is an easy way:
+
+       
+       ./build/i18n-scan.pl applications/[package] > po/templates/[application].pot
+       ./build/i18n-update.pl po [application].po
+       
+*Note:* Some packages share translation files, in this case you need to scan through all their folders. The first command from above should then be:
+
+       
+       ./build/i18n-scan.pl applications/[package-1] applications/[package-2] applications/[package-n] > po/templates/[application].pot
+