Web Forms: Client Server Communication (ajax-generic.php)

The ajax-generic.php interface is a communication helper to transfer config file data from LoxBerry (“server”) to your web interface (“client”) and back.

This feature is available from LoxBerry 3.0. Set your LB Minimum version in your plugin.cfg.

ajax-generic.php is the backend to request and submit form data to your specific config file. Config files created and readed by ajax-generic.php are json files. It is thought to be used as ajax request to fill your web form with data, and to submit form data to your config file.

ajax-generic.php supports:

  • Request a json config file and transmit it to the client as json.
  • Write data to a config file.
  • Read and write the full config file, or a specified section of the json
  • Submit form data as POST in the Form Post method, or as plain json data
  • Supports abbreviations for your config file path (LBHOME, LBPCONFIG,…) - this is helpful, as both browser and server script don’t know what folder should be used, but the server knows the base directories.
  • Settings are merged (default) or overwritten (option replace).

Abstract

Reading data from config file using the Fetch API

var formdata;
fetch('/admin/system/ajax/ajax-generic.php?file=LBPCONFIG/myplugin/config.json&section=Main&read')
    .then( response => response.json() )
    .then( data => formdata = data );

Requests the section Main of config file /opt/loxberry/config/plugins/myplugin/config.json. The data are written to the formdata variable.

LBPCONFIG is an automatic placeholder for /opt/loxberry/config/plugins (see list below).

Writing form data to config file using the Fetch API

var formData = new FormData(document.querySelector('form'));
 
const requestOptions = {
  method: "POST",
  headers: { "Content-Type" : "application/json" },
  body: JSON.stringify( formData )
};
fetch('/admin/system/ajax/ajax-generic.php?file=LBPCONFIG/myplugin/myplugin.json&section=Main&write', requestOptions)

formData is set to represent alls form data of the first form of the page.

With requestOptions the request is build for the fetch call. This example sends the form data in json syntax, therefore the request body is set to be a json by the JSON.stringify(formData) call.

The request is sent to ajax-generic.php, with file is the myplugin.json and section Main, with option write.

The response is a json with the newly created data and 200 OK, or 500 ERROR on any error.

Replace a section in the config file

var formData = new FormData(document.querySelector('form'));
 
const requestOptions = {
  method: "POST",
  headers: { "Content-Type" : "application/json" },
  body: JSON.stringify( formData )
};
fetch('/admin/system/ajax/ajax-generic.php?file=LBPCONFIG/myplugin/myplugin.json&section=Main&replace', requestOptions)

The preparation of the data is equal to the section before.

In this example, the section Main is sent with option replace.

The replace option will completely replace the full section with the new submitted data (instead of the write option that merges settings). Replace has a special meaning on arrays. As arrays usually would be merged, you won’t be able to delete array elements. With the replace parameter, you can completely replace a section instead of merging.

The response is a json with the newly created data and 200 OK, or 500 ERROR on any error.

Feature details

Always use the following url to call the interface:

/admin/system/ajax/ajax-generic.php

Add the following parameters

Parameter MandatoryUsage Example
file=<filename> YES Give the full path and name of your config file. Also relative paths are allowed. Use fixed abbreviations instead of /opt/loxberry in the path (see below)

Only files in the CONFIG folders are accepted.
file=LBPCONFIG/myplugin/config.json
section=<Sectionname>Optional If no section is given, reading and writing apply to the full config file.

If a section is given, data are read and written from this base section of the json. Only the first level is possible.

Be aware to read and write with or without section, but not mixed (that will scramble your config file).
section=Main
read (Default) If read is given (it’s not required to send read=1, but simply read), that’s a read-only query. read
write NO If write is given, the submitted data are written to the config file, optional to the given section. Write will respond with a json of the new data (HTTP 200 OK), or, on error, with a json with an error string (HTTP 500 INTERNAL SERVER ERROR).

write is mutually exclusive to read.
write
replace NO If replace is given, the submitted data will replace the full section instead of merging. This is important with arrays, as you otherwise wouldn’t be able to remove elements from arrays. Without a section, the full file will be replaced by the submitted data.

Replace implicitely enables write.
replace

Filename folder abbreviations

It might be difficult to know the server-side path of your script on the client, and you should avoid to hard-code the /opt/loxberry path. Therefore in the file= query string, the following shortcuts are available. The string is replaced on the server before processing of the request.

String Default folder Description
$lbhomedir, LBHOMEDIR /opt/loxberry Home directory of LoxBerry
LBPCONFIG /opt/loxberry/config/plugins Base config directory for all plugins
$lbsconfigdir, LBSCONFIG/opt/loxberry/config/system System configuration directory of LoxBerry
LEGACY /opt/loxberry/webfrontend/legacyThe users legacy folder to place custom scripts in.

Be aware that the $ possibly needs to be encoded, depending on your JavaScript library.

Examples

/admin/system/ajax/ajax-generic.php?file=LBPCONFIG/alexa2lox/alexa.json&section=Base&read
Reads the section Base of alexa2lox/alexa.json

/admin/system/ajax/ajax-generic.php?file=LBPCONFIG/alexa2lox/alexa.json&section=Base&write
Writes the section Base of alexa2lox/alexa.json

/admin/system/ajax/ajax-generic.php?file=LBPCONFIG/alexa2lox/alexa.json&read
Reads the full alexa2lox/alexa.json

/admin/system/ajax/ajax-generic.php?file=LBPCONFIG/alexa2lox/alexa.json&write
Writes the full alexa2lox/alexa.json

Updates of general.json

ajax-generic detects an update to the general.json file, and automatically triggers the auto-generation of the legacy general.cfg (https://loxwiki.atlassian.net/wiki/spaces/LOXBERRY/pages/1246070304/Perl+Modul+LoxBerry+System+General#PerlModulLoxBerry%3A%3ASystem%3A%3AGeneral-Recreationofthelegacygeneral.cfgfromotherlanguages ).

Usage examples

Usage in a simple form

Write data

This is a working html to write the form fields to your test.json config in LoxBerry’s legacy folder.

<html>
<body>
    <h2>HTML Form</h2>
    <form id="myForm">
        <label for="fname">First name:</label><br>
        <input type="text" id="fname" name="fname" value="John"><br>
        <label for="lname">Last name:</label><br>
        <input type="text" id="lname" name="lname" value="Doe"><br><br>
        <input type="submit"> 
    </form> 
 
<script>
    myForm.onsubmit = async (e) => {
        e.preventDefault();
        let response = await fetch('/admin/system/ajax/ajax-generic.php?file=LEGACY/test.json&write', {
            method: 'POST',
            body: new FormData(myForm)
        });
 
        let result = await response.json();
 
        alert("Returned: " + result.fname + " " + result.lname);
    };
</script>
 
</body>
</html>

Usage with Vue3

Example read code to fill the $data.Mqtt object

The getData() method is called in the data() function. To prevent JavaScript errors, the received data are evaluated and set to default values if required.

getData() {
    console.log("Called getData");
    fetch('/admin/system/ajax/ajax-generic.php?file=$lbsconfigdir/general.json&section=Mqtt&read')
        .then( response => response.json() )
        .then( data => { 
            if( !data?.Brokerhost ) data.Brokerhost = 'localhost';
            if( !data?.Brokerport ) data.Brokerport = '1883';
            if( !data?.Brokeruser ) data.Brokeruser = 'loxberry';
            if( !data?.Brokerpass ) data.Brokerpass = '';
            if( !data?.Websocketport ) data.Websocketport = '9001';
            if( !data?.Uselocalbroker ) data.Uselocalbroker = 'true';
            if ( data.Uselocalbroker == 1 ) data.Uselocalbroker = 'true';
            this.Mqtt = data } );
}

Example write code of the $data.Mqtt object

The saveApply() method is called by the v-on:click binding of the Save button.

saveApply() {
    console.log("Called Save and Apply");
 
    const requestOptions = {
        method: "POST",
        headers: { "Content-Type" : "application/json" },
        body: JSON.stringify( this.$data.Mqtt )
    };
    var self=this;
    fetch('/admin/system/ajax/ajax-generic.php?file=$lbsconfigdir/general.json&section=Mqtt&write', requestOptions)
    .then( function(response) {
        console.log(response);
        if( response.ok != true ) {
            self.data_save_error = true;
        }
        else {
            self.data_save_error = false;
            self.data_saved = true;
            self.data_changed = false;
        }
    });
}

The callback of the fetch evalutes response.ok to show ok or error messages in the ui. There is the trick var self=this; before of the fetch, as in the callback this changes the context to the response, therefore we safe this in self to use it inside of the callback.

Testing or using ajax-generic.php from commandline

ajax-generic.php allows to be executed from commandline. Input data can be echo'ed with a pipe to ajax-generic.php. The GET Parameters can be added as parameter string (see example). Take notice of escaping the & symbol by \& in Bash.

echo '{ "Main": { "resetAfterSend" : true } }' |  php /opt/loxberry/webfrontend/htmlauth/system/ajax/ajax-generic.php file=LBSCONFIG/test.json\&write

Log output is printed to STDERR. The response of the call always is the new config file as json, that is echoed to STDOUT.