From 22746a2a0d9bead7f9539b7d0d523e7ba9003ccc Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Thu, 9 Feb 2017 07:09:55 +0100 Subject: [PATCH] docs: add documentation for gluon-web --- .../{configmode.rst => web/config-mode.rst} | 0 docs/dev/web/controller.rst | 117 ++++++++++++++ docs/dev/{ => web}/i18n.rst | 0 docs/dev/web/model.rst | 148 ++++++++++++++++++ docs/dev/web/view.rst | 55 +++++++ docs/index.rst | 14 +- 6 files changed, 332 insertions(+), 2 deletions(-) rename docs/dev/{configmode.rst => web/config-mode.rst} (100%) create mode 100644 docs/dev/web/controller.rst rename docs/dev/{ => web}/i18n.rst (100%) create mode 100644 docs/dev/web/model.rst create mode 100644 docs/dev/web/view.rst diff --git a/docs/dev/configmode.rst b/docs/dev/web/config-mode.rst similarity index 100% rename from docs/dev/configmode.rst rename to docs/dev/web/config-mode.rst diff --git a/docs/dev/web/controller.rst b/docs/dev/web/controller.rst new file mode 100644 index 00000000..cfec73d4 --- /dev/null +++ b/docs/dev/web/controller.rst @@ -0,0 +1,117 @@ +Controllers +=========== + +Controllers live in ``/lib/gluon/web/controller``. They define which pages ("routes") +exist under the ``/cgi-bin/gluon`` path, and what code is run when these pages are requested. + +Controller scripts mostly consist of calls of the `entry` function, which each define +one route: + +.. code-block:: lua + + entry({"admin"}, alias("admin", "info"), _("Advanced settings"), 10) + entry({"admin", "info"}, template("admin/info"), _("Information"), 1) + +The entry function expects 4 arguments: + + - `path`: Components of the path to define a route for. + + The above example defines routes for the paths ``admin`` and ``admin/info``. + + - `target`: Dispatcher for the route. See the following section for details. + - `title`: Page title (also used in navigation). The underscore function is used + + - `order`: Sort index in navigation (defaults to 100) + +Navigation indexes are automatically generated for each path level. Pages can be +hidden from the navigation by setting the `hidden` property of the node object +returned by `entry`: + +.. code-block:: lua + + entry({"hidden"}, alias("foo"), _("I'm hidden!")).hidden = true + + +Dispatchers +----------- + + - *alias* (*path*, ...): Redirects to a different page. The path components are + passed as individual arguments. + - *call* (*func*, ...): Runs a Lua function for custom request handling. The given + function is called with the HTTP object and the template renderer as first + two arguments, followed by all additional arguments passed to `call`. + - *template* (*view*): Renders the given view. See :doc:`view`. + - *model* (*name*): Displays and evaluates a form as defined by the given model. See the + :doc:`model` page for an explanation of gluon-web models. + + +.. _web-controller-http: + +The HTTP object +--------------- + +The HTTP object provides information about the HTTP requests and allows to add +data to the reply. Using it directly is rarely necessary when gluon-web +models and views are used. + +Useful functions: + + - *getenv* (*key*): Returns a value from the CGI environment passed by the webserver. + - *formvalue* (*key*): Returns a value passed in a query string or in the content + of a POST request. If multiple values with the same name have been passed, only + the first is returned. + - *formvaluetable* (*key*): Similar to *formvalue*, but returns a table of all + values for the given key. + - *status* (*code*, *message*): Writes the HTTP status to the reply. Has no effect + if a status has already been sent or non-header data has been written. + - *header* (*key*, *value*): Adds an HTTP header to the reply to be sent to to + the client. Has no effect when non-header data has already been written. + - *prepare_content* (*mime*): Sets the *Content-Type* header to the given MIME + type, potentially setting additional headers or modifying the MIME type to + accommodate browser quirks + - *write* (*data*, ...): Sends the given data to the client. If headers have not + been sent, it will be done before the data is written. + + +HTTP functions are called in method syntax, for example: + +.. code-block:: lua + + http:write('Output!') + + +.. _web-controller-template-renderer: + +The template renderer +--------------------- + +The template renderer allows to render templates (views). The most useful functions +are: + + - *render* (*view*, *scope*): Renders the given view, optionally passing a table + with additional variables to make available in the template. + - *render_string* (*str*, *scope*): Same as *render*, but the template is passed + directly instead of being loaded from the view directory. + +The renderer functions are called in property syntax, for example: + +.. code-block:: lua + + renderer.render('layout') + + +Differences from LuCI +--------------------- + + - Controllers must not use the *module* function to define a Lua module (*gluon-web* + will set up a proper environment for each controller itself) + - Entries are defined at top level, not inside an *index* function + - The *alias* dispatcher triggers an HTTP redirect instead of directly running + the dispatcher of the aliased route. + - The *call* dispatcher is passed a function instead of a string with a function + name. + - The *cbi* dispatcher of LuCI has been renamed to *model*. + - The HTTP POST handler support the multipart/form-data encoding only, so + ``enctype="multipart/form-data"`` must be included in all *
* HTML + elements. + - Other dispatchers like *form* are not provided. diff --git a/docs/dev/i18n.rst b/docs/dev/web/i18n.rst similarity index 100% rename from docs/dev/i18n.rst rename to docs/dev/web/i18n.rst diff --git a/docs/dev/web/model.rst b/docs/dev/web/model.rst new file mode 100644 index 00000000..6026a71c --- /dev/null +++ b/docs/dev/web/model.rst @@ -0,0 +1,148 @@ +Models +====== + +Models are defined in ``/lib/gluon/web/model``. Each model defines one or more +forms to display on a page, and how the submitted form data is handled. + +Let's start with an example: + +.. code-block:: lua + + local f = Form(translate('Hostname')) + + local s = f:section(Section) + + local o = s:option(Value, 'hostname', translate('Hostname')) + o.default = uci:get_first('system', 'system', 'hostname') + function o:write(data) + uci:set('system', uci:get_first('system', 'system'), 'hostname', data) + uci:commit('system') + end + + return f + +The toplevel element of a model is always a *Form*, but it is also possible for +a model to return multiple forms, which are displayed one below the other. + +A *Form* has one or more *Sections*, and each *Section* has different types +of options. + +All of these elements have an *id*, which is used to identify them in the HTML +form and handlers. If no ID is given, numerical IDs will be assigned automatically, +but using explicitly named elements is often advisable (and it is required if a +form does not always include the same elements, i.e., some forms, sections or +options are added conditionally). IDs are hierarchical, so in the above example, +the *Value* would get the ID ``1.1.hostname`` (value *hostname* in first section +of first form). + +Classes and methods +------------------- + + - *Form* (*title*, *description*, *id*) + + - *Form:section* (*type*, *title*, *description*, *id*) + + Creates a new section of the given type (usually *Section*). + + - *Form:write* () + + Is called after the form has beed submitted (but only if the data is valid). It + is called last (after all options' *write* methods) and is usually used + to commit changed UCI packages. + + The default implementation of *write* doesn't to anything, but it can be + overridden. + + - *Section* (usually instanciated through *Form:section*) + + - *Section:option* (*type*, *id*, *title*, *description*) + + Creates a new option of the given type. Option types: + + - *Value*: simple text entry + - *TextValue*: multiline text field + - *ListValue*: radio buttons or dropdown selection + - *DynamicList*: variable number of text entry fields + - *Flag*: checkbox + +Most option types share the same properties and methods: + + - *default*: default value + - *optional*: value may be empty + - *datatype*: one of the types described in :ref:`web-model-datatypes` + + By default (when *datatype* is *nil*), all values are accepted. + + - *state*: has one of the values *FORM_NODATA*, *FORM_VALID* and *FORM_INVALID* + when read in a form handler + + An option that has not been submitted because of its dependencies will have + the state *FORM_NODATA*, *FORM_INVALID* if the submitted value is not valid + according to the set *datatype*, and *FORM_VALID* otherwise. + + - *data*: can be read in form handlers to get the submitted value + + - *depends* (*self*, *option*, *value*): adds a dependency on another option + + The option will only be shown when the passed option has the given value. This + is mainly useful when the other value is a *Flag* or *ListValue*. + + - *depends* (*self*, *deps*): adds a dependency on multiple other options + + *deps* must be a table with options as keys and values as values. The option + will only be shown when all passed options have the corresponding values. + + Multiple alternative dependencies can be added by calling *depends* repeatedly. + + - *value* (*self*, *value*, *text*): adds a choice to a *ListValue* + + - *write* (*self*, *data*): is called with the submitted value when all form data is valid. + + Does not do anything by default, but can be overridden. + +The *default* value, the *value* argument to *depends* and the output *data* always have +the same type, which is usually a string (or *nil* for optional values). Exceptions +are: + + - *Flag* uses boolean values + - *DynamicList* uses a table of strings + +Despite its name, the *datatype* setting does not affect the returned value type, +but only defines a validator the check the submitted value with. + +For a more complete example that actually makes use of most of these features, +have a look at the model of the *gluon-web-network* package. + +.. _web-model-datatypes: + +Data types +---------- + + - *integer*: an integral number + - *uinteger*: an integral number greater than or equal to zero + - *float*: a number + - *ufloat*: a number greater than or equal to zero + - *ipaddr*: an IPv4 or IPv6 address + - *ip4addr*: an IPv4 address + - *ip6addr*: an IPv6 address + - *wpakey*: a string usable as a WPA key (either between 8 and 63 characters, or 64 hex digits) + - *range* (*min*, *max*): a number in the given range (inclusive) + - *min* (*min*): a number greater than or equal to the given minimum + - *max* (*max*): a number less than or equal to the given maximum + - *irange* (*min*, *max*): an integral number in the given range (inclusive) + - *imin* (*min*): an integral number greater than or equal to the given minimum + - *imax* (*max*): an integral number less than or equal to the given maximum + - *minlength* (*min*): a string with the given minimum length + - *maxlength* (*max*): a string with the given maximum length + +Differences from LuCI +--------------------- + + - LuCI's *SimpleForm* and *SimpleSection* are called *Form* and *Section*, respectively + - Is it not possible to add options to a *Form* directly, a *Section* must always + be created explicitly + - Many of LuCI's CBI classes have been removed, most importantly the *Map* + - The *rmempty* option attribute does not exist, use *optional* instead + - Only the described data types are supported + - Form handlers work completely differently (in particular, a *Form*'s *handle* + method should usually not be overridden in *gluon-web*) diff --git a/docs/dev/web/view.rst b/docs/dev/web/view.rst new file mode 100644 index 00000000..ae25f3c1 --- /dev/null +++ b/docs/dev/web/view.rst @@ -0,0 +1,55 @@ +Views +===== + +The template parser reads views from ``/lib/gluon/web/view``. Writing own view +should be avoided in favour of using :doc:`model` with their predefined views. + +Views are partial HTML pages, with additional template tags that allow +to embed Lua code and translation strings. The following tags are defined: + + - ``<%`` ... ``%>`` evaluates the enclosed Lua expression. + - ``<%=`` ... ``%>`` evaluates the enclosed Lua expression and prints its value. + - ``<%+`` ... ``%>`` includes another template. + - ``<%:`` ... ``%>`` translates the enclosed string using the loaded i18n catalog. + - ``<%_`` ... ``%>`` translates the enclosed string *without escaping HTML entities* + in the translation. This only makes sense when the i18n catalog contains HTML code. + - ``<%#`` ... ``%>`` is a comment. + +All of these also come in the whitespace-stripping variants ``<%-`` and ``-%>`` that +remove all whitespace before or after the tag. + +Complex combinations of HTML and Lua code are possible, for example: + +.. code-block:: text + +
+ <% if foo then %> + Content + <% end %> +
+ + +Variables and functions +----------------------- + +Many call sites define additional variables (for example, model templates can +access the model as *self* and an unique element ID as *id*), but the following +variables and functions should always be available for the embedded Lua code: + + - *renderer*: :ref:`web-controller-template-renderer` + - *http*: :ref:`web-controller-http` + - *request*: Table containing the path components of the current page + - *url* (*path*): returns the URL for the given path, which is passed as a table of path components. + - *attr* (*key*, *value*): Returns a string of the form ``key="value"`` + (with a leading space character before the key). + + *value* is converted to a string (tables are serialized as JSON) and HTML entities + are escaped. Returns an empty string when *value* is *nil* or *false*. + - *include* (*template*): Includes another template. + - *node* (*path*, ...): Returns the controller node for the given page (passed as + one argument per path component). + + Use ``node(unpack(request))`` to get the node for the current page. + - *pcdata* (*str*): Escapes HTML entities in the passed string. + - *urlencode* (*str*): Escapes the passed string for use in an URL. + - *translate* and *translatef*: see :doc:`i18n` diff --git a/docs/index.rst b/docs/index.rst index 95bdb1ef..b1d40eab 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -41,11 +41,21 @@ Developer Documentation dev/basics dev/hardware dev/upgrade - dev/configmode dev/wan - dev/i18n dev/mac_addresses +gluon-web Reference +^^^^^^^^^^^^^^^^^^^ + +.. toctree:: + :maxdepth: 1 + + dev/web/controller + dev/web/model + dev/web/view + dev/web/i18n + dev/web/config-mode + Packages --------