ZnetDK 4 Mobile

 Install the App?

Code Snippets

Code snippets for extending the capabilities of your ZnetDK for Mobile Starter App

Displaying meetings with FullCalendar

DESCRIPTION Integrating the popular Javascript Calendar FullCalendar into the Starter Web Application.

PREREQUISITE The ZnetDK for Mobile Starter Application is installed and configured (go to the Get Started page and follow the given procedure).

STEPS Only four steps are required:
  • Step 1: Coding the view in charge of displaying the calendar,
  • Step 2: Coding the controller that supplies the events to display on the calendar,
  • Step 3: Loading the CSS and JS scripts of FullCalendar from CDN hosting,
  • Step 4: Adding a new item to the App's navigation menu.

DEMONSTRATION See live demo....

Step 1 - Coding the view meetings.php

The view meetings.php must be installed into the INSTALL_DIR/applications/default/app/view/ folder.

  • <div id="my-calendar"/> is the HTML container of the calendar.
  • The extra style applied to the fc-toolbar-title class is for optimization purpose on mobile small screens.
  • The calOptions JS variable defines the FullCalendar standard options applied to the calendar (see JS Calendar documentation).
  • The fetchEvents() JS function requests through the AJAX API the meetings to display on the calendar.
  • The jQuery event handler on ZnetDK for Mobile afterviewdisplay events instantiates the calendar when the view is displayed for the first time.
  • The App's header is hidden automatically when the view content is scrolled by calling the znetdkMobile.header.autoHideOnScroll() method.

View meetings.php

<div id="my-calendar" class="w3-stretch w3-margin-top"></div>
<style>
#my-calendar .fc-toolbar-title {
    font-size: 18px;
}
</style>
<script>
console.log('meetings.php');
var myCalendar = null,
    calOptions = {
        initialView: 'timeGridWeek',
        weekends: false,
        slotMinTime: '06:00:00',
        slotMaxTime: '22:00:00',
        height: 'auto',
        stickyHeaderDates: true,
        events: fetchEvents
    };

function fetchEvents(fetchInfo, successCallback) {
    znetdkMobile.ajax.request({
        controller: 'meetingctrl',
        action: 'fetch',
        data: {start:fetchInfo.startStr, end:fetchInfo.endStr},
        callback: function (response) {
            successCallback(response);
            // Footer position is adjusted
            znetdkMobile.footer.adjustPosition();
        }
    });
}

/* Once the view is displayed ... */
$('body').on('afterviewdisplay', function (event, viewId) {
    if (viewId === 'meetings') {
        if (myCalendar === null) { // Calendar is not yet instantiated
            myCalendar = new FullCalendar.Calendar($('#my-calendar').get(0), calOptions);
            myCalendar.render();
            // The footer position is adjusted
            znetdkMobile.footer.adjustPosition();
        } else { // Calendar already exists as object
            // The events are refreshed
            myCalendar.refetchEvents();
        }
        znetdkMobile.header.autoHideOnScroll(true);
    } else {
        znetdkMobile.header.autoHideOnScroll(false);
    }
});

</script>

Step 2 - Coding the controller meetingctrl.php

The controller meetingctrl.php must be installed into the INSTALL_DIR/applications/default/app/controller/ folder.

  • The action_fetch() method is called by the meetings.php view through the AJAX API (see the fetchEvents() JS function in the view).
  • The start and end POST parameters sent in the AJAX requests indicate the begin and end date of the week to display on the calendar.
  • Two meeting events are returned by the controller's action in JSON format (the PHP array is automatically tansformed in JSON by the ZnetDK \Response object).

Controller meetingctrl.php

<?php
namespace app\controller;

class 
MeetingCtrl extends \AppController {
    static protected function 
action_fetch() {
        
$request = new \Request();
        
$start = new \DateTime($request->start);
        
$end = new \DateTime($request->end);
        
$events = [];
        
$events[] = [
            
'title' => 'All Day Event',
            
'start' => $start->format('Y-m-d'),
            
'end' => $end->format('Y-m-d')
        ];
        
$events[] = [
            
'title' => 'Meeting',
            
'start' => $start->format('Y-m-d') . ' 10:00',
            
'end' => $start->format('Y-m-d') . ' 12:00'
        
];

        
$response = new \Response();
        
$response->setResponse($events);
        return 
$response;
    }
}

Step 3 - Loading the CSS and JS FullCalendar scripts

The FullCalendar CSS and JavaScript libraries are automatically loaded by specifying into the config.php of the Web App, the matching url on the CDN hosting (see Settings | App extra CSS and JS libraries).

The config.php script is located into the INSTALL_DIR/applications/default/app/ folder.

The two lines below can be inserted anywhere in the config.php script.

Script config.php

define('CFG_APP_JS', 'https://cdn.jsdelivr.net/npm/fullcalendar@5.5.0/main.min.js');
define('CFG_APPLICATION_CSS', 'https://cdn.jsdelivr.net/npm/fullcalendar@5.5.0/main.min.css');

Step 4 - Adding a "Meetings" item to the navigation menu

Finally, to give users access to the meetings.php calendar view, a menu item definition is added into the menu.php script of the Web App (see Get Started | New menu items for more information).

The menu.php script is located into the INSTALL_DIR/applications/default/app/ folder.

Script menu.php

static public function initAppMenuItems() {
    
    // ... Here, some menu items...
    
    \MenuManager::addMenuItem(NULL, 'meetings', 'My meetings', 'fa-calendar');
    
    // ... Here, other menu items...
    
}

Displaying a bar chart with ChartJS

DESCRIPTION Integrating ChartJS into the Starter Web Application and display of bar chart.

PREREQUISITE The ZnetDK for Mobile Starter Application is installed and configured (go to the Get Started page and follow the given procedure).

STEPS Only four steps are required:
  • Step 1: Coding the view in charge of displaying the bar chart,
  • Step 2: Coding the controller that supplies data to the bar chart,
  • Step 3: Loading the JS script of ChartJS from CDN hosting,
  • Step 4: Adding a new item to the App's navigation menu.

DEMONSTRATION See live demo....

Step 1 - Coding the view mybarchart.php

The view mybarchart.php must be installed into the INSTALL_DIR/applications/default/app/view/ folder.

  • <canvas id="my-bar-chart"/> is the HTML container of the bar chart.
  • The loadChartData() JS function requests through the AJAX API the data of the bar chart.
  • The showChart() JS function instantiates the bar chart on first call and updates its data if it is already instantiated (see ChartJS documentation).
  • The jQuery event handler on ZnetDK for Mobile afterviewdisplay events is triggered each time the view mybarchart.php is displayed.

View mybarchart.php

<div class="w3-padding-64">
    <canvas id="my-bar-chart" width="400" height="400"></canvas>
</div>
<script>
console.log('mybarchart.php');
var ctx = document.getElementById('my-bar-chart').getContext('2d'),
    myChart = null;

/* Once the view is displayed ... */
$('body').on('afterviewdisplay', function (event, viewId) {
    if (viewId === 'mybarchart') {
        loadChartData();
    }
});

function loadChartData() {
    znetdkMobile.ajax.request({
        controller: 'barchartctrl',
        action: 'data',
        callback: function (response) {
            showChart(response);
            // Footer position is adjusted
            znetdkMobile.footer.adjustPosition();
        }
    });
}

function showChart(chartData) {
    if (myChart === null) { // First display
        myChart = new Chart(ctx, {
            type: 'bar',
            data: chartData,
            options: {
                scales: {
                    yAxes: [{
                        ticks: {
                            beginAtZero: true
                        }
                    }]
                }
            }
        });
    } else {
        myChart.data = chartData;
        myChart.update();
    }
}
</script>

Step 2 - Coding the controller barchartctrl.php

The controller barchartctrl.php must be installed into the INSTALL_DIR/applications/default/app/controller/ folder.

  • The action_data() method is called by the mybarchart.php view through the AJAX API (see the loadChartData() JS function in the view).
  • Data to display by the chart data are returned by the controller's action in JSON format (the PHP array is automatically tansformed in JSON by the ZnetDK \Response object).

Controller barchartctrl.php

<?php
namespace app\controller;

class 
BarChartCtrl extends \AppController {
    static protected function 
action_data() {
        
$data = [
            
labels => ["January","February","March","April","May","June","July"],
            
datasets => [[
                
label => 'My Dataset',
                
data => [65,59,80,81,56,55,40],
                
fill => false,
                
backgroundColor => ["rgba(255, 99, 132, 0.2)""rgba(255, 159, 64, 0.2)",
                    
"rgba(255, 205, 86, 0.2)""rgba(75, 192, 192, 0.2)",
                    
"rgba(54, 162, 235, 0.2)","rgba(153, 102, 255, 0.2)",
                    
"rgba(201, 203, 207, 0.2)"],
                
borderColor => ["rgb(255, 99, 132)""rgb(255, 159, 64)""rgb(255, 205, 86)",
                    
"rgb(75, 192, 192)","rgb(54, 162, 235)",
                    
"rgb(153, 102, 255)","rgb(201, 203, 207)"],
                
borderWidth => 1
            
]]
        ];
        
$response = new \Response();
        
$response->setResponse($data);
        return 
$response;
    }
}

Step 3 - Loading the ChartJS library

The ChartJS library is automatically loaded by specifying into the config.php of the Web App, the matching url on the CDN hosting (see Settings | App extra CSS and JS libraries).

The config.php script is located into the INSTALL_DIR/applications/default/app/ folder.

The line below can be inserted anywhere in the config.php script.

Script config.php

define('CFG_APP_JS', 'https://cdn.jsdelivr.net/npm/chart.js@2.9.4/dist/Chart.min.js');

Step 4 - Adding "My bar chart" item to the navigation menu

Finally, to give users access to the mybarchart.php bar chart view, a menu item definition is added into the menu.php script of the Web App (see Get Started | New menu items for more information).

The menu.php script is located into the INSTALL_DIR/applications/default/app/ folder.

Script menu.php

static public function initAppMenuItems() {
    
    // ... Here, some menu items...
    
    \MenuManager::addMenuItem(NULL, 'mybarchart', 'My bar chart', 'fa-bar-chart');
    
    // ... Here, other menu items...
    
}

Reading barcodes with QuaggaJS

DESCRIPTION Decoding EAN-13 barcode with QuaggaJS and the Starter Web Application.

PREREQUISITE The ZnetDK for Mobile Starter Application is installed and configured (go to the Get Started page and follow the given procedure).

STEPS Only three steps are required:
  • Step 1: Coding the view in charge of decoding barcodes,
  • Step 2: Loading the JS script of QuaggaJS from CDN hosting,
  • Step 3: Adding a new item to the App's navigation menu.

DEMONSTRATION See live demo....

Step 1 - Coding the view mybarcodeview.php

The view mybarcodeview.php must be installed into the INSTALL_DIR/applications/default/app/view/ folder.

  • <form id="barcode-form"/> is the HTML form that embeds the Start and Stop buttons of the camera and that displays the barcode number once decoded.
  • <div id="live-camera"/> is the HTML container of the camera stream.
  • By clicking on the Start button, the Quagga.init() function initializes the Quagga library for the configuration set through the liveStreamConfig variable and then requests for camera access. Next, if no error is detected, the video-stream is started by a call to the startScan() JS function (that finally calls Quagga.start()).
  • By clicking on the Stop button, the camera is disconnected through a call to the stopScan() JS function (in turn by a call to Quagga.stop()).
  • The Quagga.onProcessed() method registers a callback function that draws frames around possible barcodes on the video-stream.
  • Finally, the Quagga.onDetected() method registers the function called once a barcode has been decoded. The barcode number is displayed in the Decoded barcode field and the camera-stream is stopped.

View mybarcodeview.php

<form id="barcode-form" class="w3-padding-16">
    <label>Decoded barcode</label><br>
    <input class="w3-input w3-border" type="search" placeholder="Click on Start for scanning a barcode..." readonly><br>
    <button class="start w3-btn w3-green" type="button"><i class="fa fa-play"></i> Start</button>
    <button class="stop w3-btn w3-red w3-hide" type="button"><i class="fa fa-stop"></i> Stop</button>
</form>
<div id="live-camera" class="w3-hide">
</div>
<style>
    #live-camera {
        position: relative;
        width: 100%;
        height: auto;
        overflow: hidden;
        text-align: center;}
    #live-camera > canvas,
    #live-camera > video {
        max-width: 100%;
        width: 100%;
    }
    #live-camera > canvas.drawing,
    #live-camera > canvas.drawingBuffer {
        position: absolute;
        left: 0; top: 0;
    }
</style>
<script>
    console.log('mybarcodeview.php');
    var liveStreamConfig = {
        inputStream: {
            type: "LiveStream",
            target: '#live-camera',
            constraints: {
                width: {min: 640},
                height: {min: 480},
                aspectRatio: {min: 1, max: 100},
                facingMode: "environment" // or "user" for the front camera
            }
        },
        locator: {
            patchSize: "medium",
            halfSample: true
        },
        numOfWorkers: (navigator.hardwareConcurrency ? navigator.hardwareConcurrency : 4),
        decoder: {
            "readers": [
                {"format": "ean_reader", "config": {}}
            ]
        },
        locate: true
    };
    // The fallback to the file API requires a different inputStream option.
    // The rest is the same
    var fileConfig = $.extend(
            {},
            liveStreamConfig,
            {
                inputStream: {
                    size: 800
                }
            }
    );
    // Start the live stream scanner when the start button is clicked on
    $('#barcode-form button.start').on('click', function () {
        Quagga.init(
                liveStreamConfig,
                function (err) {
                    if (err) {
                        znetdkMobile.messages.add('error', err.name, err.message);
                        stopScan();
                        return;
                    }
                    startScan();
                }
        );
    });
    
    $('#barcode-form button.stop').on('click', function () {
        stopScan();
    });

    // Make sure, QuaggaJS draws frames an lines around possible
    // barcodes on the live stream
    Quagga.onProcessed(function (result) {
        var drawingCtx = Quagga.canvas.ctx.overlay,
                drawingCanvas = Quagga.canvas.dom.overlay;

        if (result) {
            if (result.boxes) {
                drawingCtx.clearRect(0, 0, parseInt(drawingCanvas.getAttribute("width")),
                    parseInt(drawingCanvas.getAttribute("height")));
                result.boxes.filter(function (box) {
                    return box !== result.box;
                }).forEach(function (box) {
                    Quagga.ImageDebug.drawPath(box, {x: 0, y: 1}, drawingCtx, {color: "green", lineWidth: 2});
                });
            }

            if (result.box) {
                Quagga.ImageDebug.drawPath(result.box, {x: 0, y: 1}, drawingCtx, {color: "#00F", lineWidth: 2});
            }

            if (result.codeResult && result.codeResult.code) {
                Quagga.ImageDebug.drawPath(result.line, {x: 'x', y: 'y'}, drawingCtx, {color: 'red', lineWidth: 3});
            }
        }
    });

    // Once a barcode had been read successfully, stop quagga
    Quagga.onDetected(function (result) {
        if (result.codeResult.code) {
            $('#barcode-form input').val(result.codeResult.code);
            $('#barcode-form input').addClass('w3-pale-green');
            stopScan();
            znetdkMobile.messages.add('info', 'Information', 'Barcode decoded successfully.');
        }
    });
    
    function startScan() {
        $('#barcode-form input').removeClass('w3-pale-green');
        $('#barcode-form input').val('');
        Quagga.start();
        $('#live-camera').removeClass('w3-hide');
        $('#barcode-form button.start').addClass('w3-hide');
        $('#barcode-form button.stop').removeClass('w3-hide');
        znetdkMobile.messages.showSnackbar('Scan in progress...');
    }
    
    function stopScan() {
        Quagga.stop();
        $('#live-camera').addClass('w3-hide');
        $('#barcode-form button.stop').addClass('w3-hide');
        $('#barcode-form button.start').removeClass('w3-hide');
        znetdkMobile.messages.showSnackbar('Scan terminated.');
    }
    
</script>

Step 2 - Loading the QuaggaJS library

The QuaggaJS library is automatically loaded by specifying into the config.php of the Web App, the matching url on the CDN hosting (see Settings | App extra CSS and JS libraries).

The config.php script is located into the INSTALL_DIR/applications/default/app/ folder.

The line below can be inserted anywhere in the config.php script.

Script config.php

define('CFG_APP_JS', 'https://cdnjs.cloudflare.com/ajax/libs/quagga/0.12.1/quagga.min.js');

Step 3 - Adding "EAN-13 barcode" item to the navigation menu

Finally, to give users access to the mybarcodeview.php bar chart view, a menu item definition is added into the menu.php script of the Web App (see Get Started | New menu items for more information).

The menu.php script is located into the INSTALL_DIR/applications/default/app/ folder.

Script menu.php

static public function initAppMenuItems() {
    
    // ... Here, some menu items...
    
    \MenuManager::addMenuItem(NULL, 'mybarcodeview', 'EAN-13 barcode', 'fa-barcode');
    
    // ... Here, other menu items...
    
}

Adding an Autocomplete field to a form

DESCRIPTION Example of the Starter application showing a form with an autocomplete field to select a country.

PREREQUISITE The ZnetDK for Mobile Starter Application is installed and configured (go to the Get Started page and follow the given procedure).

STEPS Only three steps are required:
  • Step 1: Coding the view in charge of displaying the form with the autocomplete field,
  • Step 2: Coding the controller that supplies data to the autocomplete field,
  • Step 3: Adding a new item to the App's navigation menu.

DEMONSTRATION See live demo....

Step 1 - Coding the view autocompleteview.php

The view autocompleteview.php must be installed into the INSTALL_DIR/applications/default/app/view/ folder.

  • <input id="my-autocomplete"> is the HTML input element used as autocomplete field.
  • znetdkMobile.autocomplete.make() is the JS method that changes the input element to an autocomplete field and connect it to the remote PHP controller's action developed in next step 2.
  • onSelect() is a JS function called when a country is selected in the autocomplete field.
    This function displays a snackbar message indicating the name of the selected country.
  • onRender() is a JS function called before displaying a suggestion of country in the autocomplete field.
    This function customizes the display of the suggestion by showing the country code in bold on the left and the country label on the right in italics.

View autocompleteview.php

<form id="barcode-form" class="w3-padding-16">
    <label>Country</label><br>
    <input id="my-autocomplete" class="w3-input w3-border" type="search" placeholder="Type a country name..."><br>
</form>
<script>
    console.log('autocompleteview.php');
    znetdkMobile.autocomplete.make('#my-autocomplete', {
        controller: 'autocompletectrl',
        action: 'suggestions'
    }, onSelect, onRender);

    function onSelect(item) {
        znetdkMobile.messages.showSnackbar('Country <b>' + item.label + '</b> selected');
    }
    function onRender(item) {
        return '<b>' + item.value + '</b> - <i>' + item.label + '</i>';
    }
</script>

Step 2 - Coding the controller autocompletectrl.php

The controller autocompletectrl.php must be installed into the INSTALL_DIR/applications/default/app/controller/ folder.

  • The AutocompleteCtrl::action_suggestions() method is the controller's action called by the autocompleteview.php to get the suggestions matching the letters entered in the autocomplete field.
  • The countries are read in JSON format from the http://country.io/names.json URL and are converted in PHP array.
  • The letters entered in the autocomplete field are passed to the controller's action through the query POST parameter and read through a ZnetDK \Request object.
  • The suggestions of countries matching the letters entered in the autocomplete field are returned to the view in JSON format through the ZnetDK \Response object.

Controller autocompletectrl.php

<?php
namespace app\controller;

class 
AutocompleteCtrl extends \AppController {
    static protected function 
action_suggestions() {
        
$response = new \Response();
        
$countriesAsJson file_get_contents("http://country.io/names.json");
        if (
$countriesAsJson === FALSE) {
            
$response->setCriticalError(NULL'Unable to read the countries');
            return 
$response;
        }
        
$allCountries json_decode($countriesAsJsonTRUE);
        
$request = new \Request();
        
$suggestions = [];
        if (
is_array($allCountries)) {
            foreach(
$allCountries as $key => $label) {
                if (
stripos($label$request->query) !== FALSE) {
                    
$suggestions[] = ['label' => $label'value' => $key];
                }
            }
        }
        
$response->setResponse($suggestions);
        return 
$response;
    }
}

Step 3 - Adding "Autocomplete" item to the navigation menu

Finally, to give users access to the autocompleteview.php view, a menu item definition is added into the menu.php script of the Web App (see Get Started | New menu items for more information).

The menu.php script is located into the INSTALL_DIR/applications/default/app/ folder.

Script menu.php

static public function initAppMenuItems() {
    
    // ... Here, some menu items...
    
    \MenuManager::addMenuItem(NULL, 'autocompleteview', 'Autocomplete', 'fa-keyboard-o');
    
    // ... Here, other menu items...
    
}

Editing HTML content with CKEditor

DESCRIPTION Here is an example of application that shows how to edit HTML content with the popular Javascript Editor CKEditor in version 5.

PREREQUISITE The ZnetDK for Mobile Starter Application is installed and configured (go to the Get Started page and follow the given procedure).

STEPS Five steps are required:
  • Step 1: Coding the view used to edit HTML content in Back Office,
  • Step 2: Coding the controller that stores and supplies the HTML content,
  • Step 3: Coding the view that displays the HTML content in Front Office,
  • Step 4: Loading the CKEditor JS library from CDN hosting,
  • Step 5: Adding a new item to the App's navigation menu.

DEMONSTRATION See live demo....

Step 1 - Coding the view backoffice.php

The view backoffice.php must be installed into the INSTALL_DIR/applications/default/app/view/ folder.
It embeds the editor used to edit HTML content.

  • The HTML element used as editor is a textarea field named my_content.
  • This editor is declared within a ZnetDK form that allows both to store (see data-zdk-submit attribute) and load (see data-zdk-load attribute) the data typed in the textarea field.
  • The form data is loaded via a call to the JS znetdkMobile.form.load() API method.
  • After the data is loaded into the textarea element, the CKEditor component is instantiated by calling the ClassicEditor.create() API method.
  • The position of the app footer is adjusted whenever text is changed in the HTML editor with the help of the znetdkMobile.footer.adjustPosition() API method.
  • By clicking on the Show content button, the view named frontoffice is displayed using the znetdkMobile.content.displayView() API method.

View backoffice.php

<div class="w3-content w3-margin-top">
    <form id="my-content-form" data-zdk-load="backofficectrl:get" data-zdk-submit="backofficectrl:save">
        <textarea name="my_content"></textarea>
        <button class="w3-button w3-green w3-margin-top" type="submit"><i class="fa fa-save"></i> Save</button>
        <button class="w3-button w3-theme-action w3-margin-top show" type="button"><i class="fa fa-eye"></i> Show content</button>
    </form>
</div>
<script>
console.log('backoffice.php');
// The ZnetDK form is instantiated and form content loaded
var myForm = znetdkMobile.form.make('#my-content-form');
myForm.load(null, function(){
    // CKEditor is instantiated
    var ckeditor = document.querySelector('#my-content-form textarea[name=my_content]');
    ClassicEditor.create(ckeditor, {
        toolbar: ['heading', 'bold', 'italic', 'undo', 'redo']    
    }).then( newEditor => {
        newEditor.model.document.on('change:data', () => {
            // App's footer is adjusted
            znetdkMobile.footer.adjustPosition();
        });
    })
    .catch( error => {
        console.error( error );
    });    
});
// On click events of the Show content button
$('#my-content-form button.show').on('click', function(){
    znetdkMobile.content.displayView('frontoffice');
});
</script>

Step 2 - Coding the controller backofficectrl.php

The controller backofficectrl.php must be installed into the INSTALL_DIR/applications/default/app/controller/ folder.

  • The action_get() method is called by both the backoffice and frontoffice views. It returns the text in HTML format typed in the editor.
    The data is read from the user's session if it exists through the \UserSession::getCustomValue() method. Otherwise, a default text is returned.
    In a real world application, the data would instead be read from a database via the \DAO API.
  • The action_submit method is called by backoffice view to store in session the formated text typed in the HTML editor.
    The data is stored in the user's session by calling the \UserSession::setCustomValue() method.
    As said before, a better solution would be to store the HTML text in a database via the \DAO API.

Controller backofficectrl.php

<?php
namespace app\controller;

class 
BackOfficeCtrl extends \AppController {

    static private 
$allowedTags '<strong><i><br><p><h2><h3><h4>';
    static private 
$contentSessionKey 'z4mdemo-editor-content';

    static protected function 
action_get() {
        
$response = new \Response();
        
$myContent '<p>Type here <strong>the content</strong> to publish in <em>Front office</em>...</p>';
        
$contentInSession = \UserSession::getCustomValue(self::$contentSessionKey);
        if (!
is_null($contentInSession)) {
            
$myContent strip_tags(html_entity_decode($contentInSession), self::$allowedTags);
        }
        
$response->my_content $myContent;
        return 
$response;
    }

    static protected function 
action_save() {
        
$request = new \Request();
        \
UserSession::setCustomValue(self::$contentSessionKeyhtmlentities($_REQUEST['my_content']));
        
$response = new \Response();
        
$response->setSuccessMessage(NULL'Content saved');
        return 
$response;
    }

}

Step 3 - Coding the view frontoffice.php

The view frontoffice.php must be installed into the INSTALL_DIR/applications/default/app/view/ folder.

View frontoffice.php

<?php $contentObj = \app\controller\BackOfficeCtrl::doAction('get'); ?>
<div class="zdk-viewreload"><?php echo $contentObj->my_content; ?></div>
<script>
znetdkMobile.action.addCustomButton('editcontent', 'fa-pencil', 'w3-blue');
znetdkMobile.action.registerView('frontoffice', {
    editcontent: {
        isVisible: true,
        callback: function () {
            znetdkMobile.content.displayView('backoffice');
        }
    }
});
</script>

Step 4 - Loading the CKEditor JS library

The CKEditor library is automatically loaded by specifying into the config.php of the Web App, the matching url on the CDN hosting (see Settings | App extra CSS and JS libraries).

The config.php script is located into the INSTALL_DIR/applications/default/app/ folder.

The line below can be inserted anywhere in the config.php script.

Script config.php

define('CFG_APP_JS', 'https://cdn.ckeditor.com/ckeditor5/25.0.0/classic/ckeditor.js');

Step 5 - Adding "Edit content" and "Show content" items to the navigation menu

Finally, to give users access to the backoffice.php and frontoffice.php views, the menu's items are declared into the menu.php script of the Web App (see Get Started | New menu items for more information).

The menu.php script is located into the INSTALL_DIR/applications/default/app/ folder.

Script menu.php

static public function initAppMenuItems() {
    
    // ... Here, some menu items...
    
    \MenuManager::addMenuItem(NULL, 'backoffice', 'Edit content', 'fa-code');
    \MenuManager::addMenuItem(NULL, 'frontoffice', 'Show content', 'fa-eye');
    
    // ... Here, other menu items...
    
}

Server-side form data validation

DESCRIPTION This application shows how to validate on the server-side the data entered in an input form using the Data Validation API.
For illustration purposes, all the fields in the input form are defined with type="text" and without the required attribute for the mandatory fields.

PREREQUISITE The ZnetDK for Mobile Starter Application is installed and configured (go to the Get Started page and follow the given procedure).

STEPS Only four steps are required:
  • Step 1: Coding the view that displays the input form,
  • Step 2: Coding the controller that processes the form data,
  • Step 3: Coding the validator in charge of checking the data entered by the user,
  • Step 4: Adding a new item to the App's navigation menu.

DEMONSTRATION See live demo....

Step 1 - Coding the view dataformview.php

The view dataformview.php must be installed into the INSTALL_DIR/applications/default/app/view/ folder.

  • The input form id="my-data-form" is declared with the data-zdk-submit attribute to specify the controller and the action to call on form submit.
  • All the input fields are declared voluntarily with the type="text" attribute, even for the fields used to input the minimal and maximal numeric values because we want the data to be validated on the server-side.
    Obviously, in a real world input form, the two numeric fields would be preferably declared with the type="number" attribute.
  • A red asterisk is added to the Title label via the zdk-required ZnetDK CSS class.
    On the other hand, no required attribute is voluntarily defined for the corresponding input field name="title" as we want to check in PHP on the server-side, that a value is indeed entered.
  • Finally, to connect the input form to the remote PHP controller action specified via the the data-zdk-submit attribute, the ZnetDK method znetdkMobile.form.make() is called in JavaScript.
    So, when the submit button is clicked, the data entered in the form are sent in AJAX through POST parameters to the app\controller\FormValidCtrl::action_submit() PHP method described in next chapter.

View dataformview.php

<form id="my-data-form" class="w3-padding-16" data-zdk-submit="formvalidctrl:submit">
    <label class="zdk-required"><b>Title</b></label><br>
    <input class="w3-input w3-border" type="text" name="title" placeholder="This field is mandatory"><br>
    <label><b>Min. number</b></label><br>
    <input class="w3-input w3-border" type="text" name="min_number" placeholder="Must be the lower value"><br>
    <label><b>Max. number</b></label><br>
    <input class="w3-input w3-border" type="text" name="max_number" placeholder="Must be the higher value"><br>
    <label><b>Other value</b></label><br>
    <input class="w3-input w3-border" type="text" name="other" placeholder="This field is optional"><br>
    <button class="w3-button w3-block w3-green w3-section w3-padding" type="submit"><i class="fa fa-check"></i> Submit</button>
</form>
<script>
    console.log('dataformview.php');
    znetdkMobile.form.make('#my-data-form');
</script>

Step 2 - Coding the controller formvalidctrl.php

The controller formvalidctrl.php must be installed into the INSTALL_DIR/applications/default/app/controller/ folder.

  • The custom app\controller\FormValidCtrl class is derived from the \AppController ZnetDK class to be identified as an application controller.
  • The action_submit() method, a controller's action, is called on submit of the input form.
    The form data is validated with the help of the \app\validator\FormValidator() custom Validator class described in next chapter.
  • If data validation failed, an error message is returned in the response of the HTTP request by calling the \Response::setFailedMessage() method.
  • Otherwise if data validation succeeded, then a success message is returned by calling the \Response::setSuccessMessage() method.

Controller formvalidctrl.php

<?php
namespace app\controller;

class 
FormValidCtrl extends \AppController {
    static protected function 
action_submit() {
        
$response = new \Response();
        
$validator = new \app\validator\FormValidator();
        
$validator->setCheckingMissingValues();
        if (!
$validator->validate()) {
            
$response->setFailedMessage(NULL$validator->getErrorMessage(),
                
$validator->getErrorVariable());
            return 
$response;
        }
        
$response->setSuccessMessage(NULL'Form data checked.');
        return 
$response;
    }
}

Step 3 - Coding the validator formvalidator.php

The validator formvalidator.php must be installed into the INSTALL_DIR/applications/default/app/validator/ folder.

  • The FormValidator custom class is derived from the \Validator ZnetDK class, specialized in data validation.
  • The initVariables() must be implemented to indicate as an array, the names of the the POST parameters whose values the validator must check.
  • The three other methods declared in the class with their name prefixed by check_ are responsible for validating the value of a particular input field.
    For example, the check_title() method checks the value of the input field named title.
  • If the entry field value passed in parameter of the method through the $value variable is correct, then the method returns TRUE.
    Otherwise, if the value is incorrect, the error message to be displayed to the end user is set by calling the Validator::setErrorMessage() method and the value FALSE is returned.
  • As shown in the check_max_number() method, the value of the input field named min_number is read by calling the Validator::getValue() method.

Validator formvalidator.php

<?php
namespace app\validator;

class 
FormValidator extends \Validator {
    protected function 
initVariables() {
        return array(
'title','min_number','max_number');
    }
    protected function 
check_title($value) {
        if (
strlen($value) === 0) {
            
$this->setErrorMessage('Title is missing.');
            return 
FALSE;
        }
        return 
TRUE;
    }
    protected function 
check_min_number($value) {
        if (
strlen($value) > && !is_numeric($value)) {
            
$this->setErrorMessage('Min. number is not a number.');
            return 
FALSE;
        }
        return 
TRUE;
    }
    protected function 
check_max_number($value) {
        if (
strlen($value) > && !is_numeric($value)) {
            
$this->setErrorMessage('Max. number is not a number.');
            return 
FALSE;
        }
        if (
strlen($value) > && $this->getValue('min_number') >= $value) {
            
$this->setErrorMessage('Max. number is not higher than the min. number');
            return 
FALSE;
        }
        return 
TRUE;
    }
}

Step 4 - Adding "Data form" item to the navigation menu

Finally, to give users access to the dataformview.php view, the menu's item named Data form is declared into the menu.php script of the Web App (see Get Started | New menu items for more information).

The menu.php script is located into the INSTALL_DIR/applications/default/app/ folder.

Script menu.php

static public function initAppMenuItems() {
    
    // ... Here, some menu items...
    
    \MenuManager::addMenuItem(NULL, 'dataformview', 'Data form', 'fa-check-circle-o');
    
    // ... Here, other menu items...
    
}

Displaying Dashboard boxes

DESCRIPTION This Web App illustrates the display of statistics boxes like the ones we can find on popular dashboard templates.

The statistics displayed on the boxes are retrieved from a remote PHP controller action.
They are refreshed both by using an AJAX request (Refresh button) and by reloading the entire view.

Finally, the amounts are displayed in euros by applying the French locale installed on the web hosting.


PREREQUISITE The ZnetDK for Mobile Starter Application is installed and configured (go to the Get Started page and follow the given procedure).

STEPS Only four steps are required:
  • Step 1: Coding the view in charge of displaying the statistics boxes,
  • Step 2: Coding the controller that supplies the data to display on the boxes,
  • Step 3: Setting the locale to French in order to format the amounts in currency,
  • Step 4: Loading two extra style sheets to beautify the display of the statistics boxes,
  • Step 5: Adding a new item to the App's navigation menu.

DEMONSTRATION See live demo....

Step 1 - Coding the view statboxes.php

The view statboxes.php must be installed into the INSTALL_DIR/applications/default/app/view/ folder.

  • In order to retrieve the statistics to display in the view, the code starts by calling directly in PHP on the server-side, the get action implemented in the \app\controller\StatCtrl controller (see implementation in Step 2), via the doAction() method.
  • Next, the statistics returned by the get action are inserted in the corresponding <h3> tags when the view is converted to HTML on the server-side, before being downloaded.
  • In order to force the view to be reloaded each time the corresponding menu item is clicked, the zdk-viewreload CSS class is added to any HTML element of the view, in this case to the div.statboxes element.
  • The responsiveness of the statistics boxes is ensured by the w3-row-padding and w3-quarter W3.CSS classes.
  • Extra Flat UI colors (w3-flat-alizarin, w3-flat-belize-hole, ...) are used to color the statistics boxes. Theses colors are declared by loading the w3-colors-flat.css library (see step 4).
  • Finally, an event handler is implemented within the <script> tag in response to the clicks on the Update button, to request in AJAX the last statistics and display them in the view, thus avoiding fully reloading the view.
    The znetdkMobile.ajax.request() is the method used to invoke remotely the same controller action than the one initially called on the server-side, to fill the view with the statistics.
    Once the statistics updated in the DOM, a snackbar message is displayed by calling the znetdkMobile.messages.showSnackbar() method.

View statboxes.php

<?php
$response = \app\controller\StatCtrl::doAction('get');
?>
<div class="statboxes zdk-viewreload w3-row-padding w3-margin-top w3-stretch">
    <div class="w3-quarter w3-margin-bottom">
        <div class="w3-container w3-flat-alizarin w3-padding-16">
            <div class="w3-left"><i class="fa fa-tasks w3-xxxlarge"></i></div>
            <div class="w3-right">
                <h3 class="tasks"><?php echo $response->tasks; ?></h3>
            </div>
            <div class="w3-clear"></div>
            <h4>Tasks</h4>
        </div>
    </div>
    <div class="w3-quarter w3-margin-bottom">
        <div class="w3-container w3-flat-belize-hole w3-padding-16">
            <div class="w3-left"><i class="fa fa-bank w3-xxxlarge"></i></div>
            <div class="w3-right">
                <h3 class="balance"><?php echo $response->balance; ?></h3>
            </div>
            <div class="w3-clear"></div>
            <h4>Balance</h4>
        </div>
    </div>
    <div class="w3-quarter w3-margin-bottom">
        <div class="w3-container w3-flat-emerald w3-padding-16">
            <div class="w3-left"><i class="fa fa-credit-card w3-xxxlarge"></i></div>
            <div class="w3-right">
                <h3 class="expenses"><?php echo $response->expenses; ?></h3>
            </div>
            <div class="w3-clear"></div>
            <h4>Expenses</h4>
        </div>
    </div>
    <div class="w3-quarter w3-margin-bottom">
        <div class="w3-container w3-flat-carrot w3-text-white w3-padding-16">
            <div class="w3-left"><i class="fa fa-handshake-o w3-xxxlarge"></i></div>
            <div class="w3-right">
                <h3 class="sales-orders"><?php echo $response->salesOrders; ?></h3>
            </div>
            <div class="w3-clear"></div>
            <h4>Sales orders</h4>
        </div>
    </div>
</div>
<button id="btn-refresh-stats" class="w3-button w3-theme-action"><i class="fa fa-refresh"></i> Update</button>
<script>
    $('#btn-refresh-stats').on('click', function(){
        znetdkMobile.ajax.request({
            controller: 'statctrl',
            action: 'get',
            callback: function (response) {
                $('.statboxes h3.tasks').text(response.tasks);
                $('.statboxes h3.balance').text(response.balance);
                $('.statboxes h3.expenses').text(response.expenses);
                $('.statboxes h3.sales-orders').text(response.salesOrders);
                znetdkMobile.messages.showSnackbar('Statistics refreshed.');
            }
        });
    });
</script>

This view requires extra styles that are coded in a dedicated style sheet named statboxes.css, placed into the INSTALL_DIR/applications/default/public/css/ folder and finally loaded in the main page of the application via the declaration made in Step 4.

Style sheet statboxes.css

.statboxes .fa {
    color: rgba(255,255,255,.6);
}
.statboxes h4 {
    border-left: 6px solid;
    margin-left: -16px;
    padding-left: 12px;
    color: rgba(255,255,255,.6);
}

Step 2 - Coding the controller statctrl.php

The controller statctrl.php must be installed into the INSTALL_DIR/applications/default/app/controller/ folder.

  • The get action (method named action_get() in the class derived from \AppController) is called by the statboxes.php view to obtain the statistics to display.
  • The \Convert::toMoney() method is called to convert the float values to currency amounts according to the locale configured for the application.

Controller statctrl.php

<?php
namespace app\controller;
class 
StatCtrl extends \AppController {
    static protected function 
action_get() {
        
$response = new \Response();
        
$response->tasks rand(0,99).'%';
        
$response->balance = \Convert::toMoney(rand(0,9999)+(rand(0,99)/100));
        
$response->expenses = \Convert::toMoney(rand(0,9999)+(rand(0,99)/100));
        
$response->salesOrders rand(0,200);
        return 
$response;
    }
}

Step 3 - Setting the locale to French

To set the locale to French (in my case named fr_FR.utf8), just edit the INSTALL_DIR/applications/default/app/lang/locale.php script and declare the LC_LOCALE_ALL PHP constant as shown below.

Script locale.php

define('LC_LOCALE_ALL', serialize(array('fr_FR.utf8')));

Step 4 - Loading two extra CSS libraries

Two extra style sheets are loaded in the main page of the application by declaring the CFG_APPLICATION_CSS parameter into the config.php script located into the INSTALL_DIR/applications/default/app/ folder.

The line below can be inserted anywhere in the config.php script.

Script config.php

define('CFG_APPLICATION_CSS', serialize([
    'https://www.w3schools.com/lib/w3-colors-flat.css',
    'applications/default/public/css/statboxes.css'
]));

Step 5 - Adding "Dashboard" item to the navigation menu

Finally, to give users access to the statboxes.php view, the menu's item named Dashboard is declared into the menu.php script of the Web App (see Get Started | New menu items for more information).

The menu.php script is located into the INSTALL_DIR/applications/default/app/ folder.

Script menu.php

static public function initAppMenuItems() {
    
    // ... Here, some menu items...
    
    \MenuManager::addMenuItem(NULL, 'statboxes', 'Dashboard', 'fa-dashboard');
    
    // ... Here, other menu items...
    
}

Multilingual Application

DESCRIPTION

Here is an example of a Web application whose display is translated into English, Spanish and Portuguese.

The source code can be adapted to translate the application into languages other than those offered.


PREREQUISITE The ZnetDK for Mobile Starter Application is installed and configured (go to the Get Started page and follow the given procedure).

STEPS Only 6 steps are required:
  • Step 1: Coding of the display language selection screen,
  • Step 2: Coding a multilingual data form,
  • Step 3: Displaying a multilingual content,
  • Step 4: Translating labels into three languages,
  • Step 5: Adding new items to the App's navigation menu,
  • Step 6: Configuring multilingual display.

DEMONSTRATION See live demo....

Step 1 - Coding the view lang.php

The view lang.php allows to select another language than the one currently set to display the application (English by default, see CFG_DEFAULT_LANGUAGE parameter).
This view must be installed into the INSTALL_DIR/applications/default/app/view/ folder.

  • The labels displayed for each button are defined via a PHP constant (for example MYLOCALE_BUTTON_LANG_ENGLISH) that contains the button text translated into the current display language. The PHP constants in charge of translating the labels of the application are defined in step 4.
  • When a button is clicked, the application is reloaded (via the locale.assign() JavaScript method) with the addition of the lang parameter of type GET to the URL and whose value corresponds to the code of the selected language (for example pt if Portuguese was clicked).
    The URL of the Web App is obtained by a call to the znetdkMobile.ajax.getParamsFromAjaxURL() method.

View lang.php

<div id="myapp-select-lang" class="w3-panel w3-stretch">
    <p>
        <button lang="en" class="w3-button w3-block w3-theme-d3">
            <i class="fa fa-fw fa-lg"></i>
            <b><?php echo MYLOCALE_BUTTON_LANG_ENGLISH; ?></b>
        </button>
    </p>
    <p>
        <button lang="es" class="w3-button w3-block w3-theme-d3">
            <i class="fa fa-fw fa-lg"></i>
            <b><?php echo MYLOCALE_BUTTON_LANG_SPANISH; ?></b>
        </button>
    </p>
    <p>
        <button lang="pt" class="w3-button w3-block w3-theme-d3">
            <i class="fa fa-fw fa-lg"></i>
            <b><?php echo MYLOCALE_BUTTON_LANG_PORTUGUESE; ?></b>
        </button>
    </p>
</div>
<script>
    console.log('View "lang.php"');
    /* Current display language is checked */
    var currentLang = $('html').attr('lang'),
        selectedButton = $('#myapp-select-lang button[lang=' + currentLang + ']');
    selectedButton.prop('disabled', true);
    selectedButton.find('i.fa').addClass('fa-check');

    /* Click event of a button */
    $('#myapp-select-lang button').on('click', function(){
        var newLang = $(this).attr('lang'),
            url = znetdkMobile.ajax.getParamsFromAjaxURL().url;
        znetdkMobile.browser.events.detachBeforeUnload();
        location.assign(url + '?lang=' + newLang);
    });
</script>

Step 2 - Coding the view form.php

The view form.php illustrates the display of the same data form in several languages thanks to the PHP constants defined in step 4.
This view must be installed into the INSTALL_DIR/applications/default/app/view/ folder.

View form.php

<form class="w3-panel w3-stretch">
    <label><b><?php echo MYLOCALE_FORM_LABEL_MY_VALUE; ?></b></label>
    <input class="w3-input w3-border" placeholder="<?php echo MYLOCALE_FORM_PLACEHOLDER_MY_VALUE; ?>">
    <button class="w3-button w3-block w3-section w3-green"
        type="button" onclick="znetdkMobile.messages.showSnackbar('<?php echo MYLOCALE_FORM_BUTTON_SUBMIT; ?>')">
        <i class="fa fa-check fa-lg"> </i><?php echo MYLOCALE_FORM_BUTTON_SUBMIT; ?>
    </button>
</form>

Step 3 - Coding the views content_[LANG].php

Here are the three views content_en.php, content_es.php and content_pt.php which display the same text translated in the three languages supported by the application.
These views illustrate another way to translate a view in several languages.
They must be installed into the INSTALL_DIR/applications/default/app/view/ folder.

View content_en.php

<h2>Content</h2>
<p>Example of text content translated into the application display language.</p>

View content_es.php

<h2>Contenido</h2>
<p>Ejemplo de contenido de texto traducido al idioma de visualización de la aplicación.</p>

View content_pt.php

<h2>Contente</h2>
<p>Exemplo de conteúdo de texto traduzido para o idioma de exibição do aplicativo.</p>

Step 4 - Translating the labels in locale_[LANG].php

The three scripts locale_en.php, locale_en.php and locale_en.php are created from the INSTALL_DIR/applications/default/app/lang/locale.php script supplied with the Starter Application.
Each script contains the definition of PHP constants which translate the labels of the application into a specific language.
These scripts must be installed into the INSTALL_DIR/applications/default/app/lang/ folder.

View locale_en.php

<?php
/**
* ZnetDK, Starter Web Application for rapid & easy development
* See official website http://www.znetdk.fr
* ------------------------------------------------------------
* Custom english labels of your application
* YOU CAN FREELY CUSTOMIZE THE CONTENT OF THIS FILE
*/

/* General labels */
define('LC_PAGE_TITLE''ZnetDK 4 Mobile');

/* Heading labels */
define('LC_HEAD_TITLE''Starter App');

/* Heading images */
//define('LC_HEAD_IMG_LOGO', ZNETDK_APP_URI.'images/logo.png');

/* Footer labels */
define('LC_FOOTER_LEFT''Version 1.0');
define('LC_FOOTER_CENTER''Developed with ZnetDK');
define('LC_FOOTER_RIGHT''&copy; 2021');

/* Custom labels */
define('MYLOCALE_MENU_HOME''Home');
define('MYLOCALE_MENU_LANG''Language');
define('MYLOCALE_MENU_FORM''Form');
define('MYLOCALE_MENU_CONTENT''Content');

define('MYLOCALE_BUTTON_LANG_ENGLISH''English');
define('MYLOCALE_BUTTON_LANG_SPANISH''Spanish');
define('MYLOCALE_BUTTON_LANG_PORTUGUESE''Portuguese');

define('MYLOCALE_FORM_LABEL_MY_VALUE''My value');
define('MYLOCALE_FORM_PLACEHOLDER_MY_VALUE''Enter a value...');
define('MYLOCALE_FORM_BUTTON_SUBMIT''Submit');

View locale_es.php

<?php
/**
* ZnetDK, Starter Web Application for rapid & easy development
* See official website http://www.znetdk.fr
* ------------------------------------------------------------
* Custom english labels of your application
* YOU CAN FREELY CUSTOMIZE THE CONTENT OF THIS FILE
*/

/* General labels */
define('LC_PAGE_TITLE''ZnetDK 4 Mobile');

/* Heading labels */
define('LC_HEAD_TITLE''Aplicación de inicio');

/* Heading images */
//define('LC_HEAD_IMG_LOGO', ZNETDK_APP_URI.'images/logo.png');

/* Footer labels */
define('LC_FOOTER_LEFT''Versión 1.0');
define('LC_FOOTER_CENTER''Desarrollado with ZnetDK');
define('LC_FOOTER_RIGHT''&copy; 2021');

/* Custom labels */
define('MYLOCALE_MENU_HOME''PĆ”gina de inicio');
define('MYLOCALE_MENU_LANG''Idioma');
define('MYLOCALE_MENU_FORM''Formulario');
define('MYLOCALE_MENU_CONTENT''Contenido');

define('MYLOCALE_BUTTON_LANG_ENGLISH''InglƩs');
define('MYLOCALE_BUTTON_LANG_SPANISH''EspaƱol');
define('MYLOCALE_BUTTON_LANG_PORTUGUESE''PortuguƩs');

define('MYLOCALE_FORM_LABEL_MY_VALUE''Mi valor');
define('MYLOCALE_FORM_PLACEHOLDER_MY_VALUE''Ingrese un valor...');
define('MYLOCALE_FORM_BUTTON_SUBMIT''Enviar');

View locale_pt.php

<?php
/**
* ZnetDK, Starter Web Application for rapid & easy development
* See official website http://www.znetdk.fr
* ------------------------------------------------------------
* Custom english labels of your application
* YOU CAN FREELY CUSTOMIZE THE CONTENT OF THIS FILE
*/

/* General labels */
define('LC_PAGE_TITLE''ZnetDK 4 Mobile');

/* Heading labels */
define('LC_HEAD_TITLE''Aplicativo inicial');

/* Heading images */
//define('LC_HEAD_IMG_LOGO', ZNETDK_APP_URI.'images/logo.png');

/* Footer labels */
define('LC_FOOTER_LEFT''VersĆ£o 1.0');
define('LC_FOOTER_CENTER''Desenvolvido with ZnetDK');
define('LC_FOOTER_RIGHT''&copy; 2021');

/* Custom labels */
define('MYLOCALE_MENU_HOME''Pagina inicial');
define('MYLOCALE_MENU_LANG''LĆ­ngua');
define('MYLOCALE_MENU_FORM''Forma');
define('MYLOCALE_MENU_CONTENT''Contente');

define('MYLOCALE_BUTTON_LANG_ENGLISH''InglĆŖs');
define('MYLOCALE_BUTTON_LANG_SPANISH''Espanhol');
define('MYLOCALE_BUTTON_LANG_PORTUGUESE''PortuguĆŖs');

define('MYLOCALE_FORM_LABEL_MY_VALUE''Meu valor');
define('MYLOCALE_FORM_PLACEHOLDER_MY_VALUE''Insira um valor...');
define('MYLOCALE_FORM_BUTTON_SUBMIT''Enviar');

Step 5 - Link the views to the navigation menu

Finally, to give users access to the views of the application, menu items are declared into the menu.php script of the Web App (see Get Started | New menu items for more information).

The menu.php script is located into the INSTALL_DIR/applications/default/app/ folder.

Script menu.php

static public function initAppMenuItems() {
    
    // ... Here, some menu items...
    
    \MenuManager::addMenuItem(NULL, '_home', MYLOCALE_MENU_HOME, 'fa-home');
    \MenuManager::addMenuItem('_home', 'lang', MYLOCALE_MENU_LANG, 'fa-language');
    \MenuManager::addMenuItem('_home', 'form', MYLOCALE_MENU_FORM, 'fa-keyboard-o');
    \MenuManager::addMenuItem('_home', 'content', MYLOCALE_MENU_CONTENT, 'fa-newspaper-o');
    
    // ... Here, other menu items...
    
}

Step 6 - Activate multilingual display in config.php

Multilingual display is enabled by declaring the CFG_MULTI_LANG_ENABLED parameter into the config.php script located into the INSTALL_DIR/applications/default/app/ folder.

The line below can be inserted anywhere in the config.php script.

Script config.php

define('CFG_MULTI_LANG_ENABLED', TRUE);

Sending email with PHPMailer

DESCRIPTION The application described in this section allows you to send an email using the PHPMailer API.

PREREQUISITE The ZnetDK for Mobile Starter Application is installed and configured (go to the Get Started page and follow the given procedure).

STEPS Only four steps are required:
  • Step 1: Installing PHPMailer,
  • Step 2: Coding the controller that sends an email,
  • Step 3: Coding the view that displays an email entry form,
  • Step 4: Adding a new item to the App's navigation menu.

DEMONSTRATION See live demo....

Step 1 - Installing PHPMailer

To add the PHPMailer API (version 6.5.1 at the time of writing this article) to your Starter Application:

  • Download PHPMailer as a ZIP file from the official github page,
  • Unzip the downloaded ZIP file content into the INSTALL_DIR/applications/default/app/ directory.
  • Rename the new INSTALL_DIR/applications/default/app/PHPMailer-master/ directory to INSTALL_DIR/applications/default/app/phpmailer/.
    The src/ subdirectory must exist into the new INSTALL_DIR/applications/default/app/phpmailer/ directory.

Step 2 - Coding the controller sendmailctrl.php

The PHP script sendmailctrl.php is responsible for sending by email the message entered in the HTML form implemented in the next step. It is created into the INSTALL_DIR/applications/default/app/controller/ directory.

  • The PHPMailer classes are loaded manually through the require PHP statement. The use statement eliminates the need to specify the namespace as prefix when instantiating a new PHPMailer object.
  • The custom app\controller\SendMailCtrl class is derived from the \AppController ZnetDK class to be identified as an application controller.
  • The action_send() method, a controller's action, is called on submit of the input form implemented in step 3.
  • The form values sent in HTTP through POST parameters are read using a ZnetDK \Request object.
  • To simplify the code shown below, the PHPMailer send() method uses the PHP's mail() function to send emails. However, it is strongly recommended to favor the use of the SMTP protocol to send emails (see PHPMailer documentation for SMTP implementation).
  • If an error occurs while sending an email, an error message is returned in the response of the HTTP request by calling the \Response::setFailedMessage() method.
  • Otherwise if the email is sent without error, a success message is returned by calling the \Response::setSuccessMessage() method.

Controller sendmailctrl.php

<?php
namespace app\controller;

use 
PHPMailer\PHPMailer\PHPMailer;

require 
'app/phpmailer/src/Exception.php';
require 
'app/phpmailer/src/PHPMailer.php';
require 
'app/phpmailer/src/SMTP.php';

class 
SendMailCtrl extends \AppController {

    static protected function 
action_send() {
        
$request = new \Request();
        
$mail = new PHPMailer();
        
$mail->setFrom($request->from);
        
$mail->addAddress($request->to);
        
$mail->Subject $request->subject;
        
$mail->Body $request->body;
        
$mail->AltBody $request->body;
        
$response = new \Response();
        if (!
$mail->send()) {
            
$response->setFailedMessage(NULL'Mailer Error: ' $mail->ErrorInfo);
        } else {
            
$response->setSuccessMessage(NULL'Email sent successfully.');
        }
        return 
$response;
    }

}

Step 3 - Coding the view sendmailview.php

The view sendmailview.php must be installed into the INSTALL_DIR/applications/default/app/view/ folder.

  • The input form id="sendmail-form" is declared with the data-zdk-submit attribute to specify the controller and the action to call on form submit.
  • To connect the input form to the remote PHP controller action specified via the the data-zdk-submit attribute, the ZnetDK method znetdkMobile.form.make() is called in JavaScript.
    So, when the submit button is clicked, the data entered in the form are sent in AJAX through POST parameters to the app\controller\SendMailCtrl::action_send() PHP method described in the previous chapter.

View sendmailview.php

<form id="sendmail-form" class="w3-container" data-zdk-submit="sendmailctrl:send">
    <div class="w3-section">
        <label class="zdk-required"><b>From</b></label>
        <input class="w3-input w3-border w3-margin-bottom" type="email" name="from" placeholder="Sender email" required>
        <label class="zdk-required"><b>Recipient</b></label>
        <input class="w3-input w3-border w3-margin-bottom" type="email" name="to" placeholder="Recipient email" required>
        <label class="zdk-required"><b>Subject</b></label>
        <input class="w3-input w3-border w3-margin-bottom" type="text" name="subject" placeholder="Subject of the message" required>
        <label class="zdk-required"><b>Body</b></label>
        <textarea class="w3-input w3-border" name="body" rows="4" placeholder="Body of the message"></textarea>
        <button class="w3-button w3-block w3-green w3-section w3-padding" type="submit">Submit</button>
    </div>
</form>
<!-- Input form initialization -->
<script>
    var myForm = znetdkMobile.form.make('#sendmail-form');
</script>

Step 4 - Adding a "Send mail" item to the navigation menu

Finally, to give users access to the sendmailview.php view, a menu item definition is added into the menu.php script of the Web App (see Get Started | New menu items for more information).

The menu.php script is located into the INSTALL_DIR/applications/default/app/ folder.

Script menu.php

static public function initAppMenuItems() {
    
    // ... Here, some menu items...
    
    \MenuManager::addMenuItem(NULL, 'sendmailview', 'Send mail', 'fa-paper-plane-o');
    
    // ... Here, other menu items...
    
}

Showing your geolocation with OpenStreetMap

DESCRIPTION This application shows you how to display an OpenStreetMap map through the Leaflet API.

A world map is displayed first. When clicking the View my location button, the map is zoomed to show a marker corresponding to the user's current location. A popup windows indicates the GPS coordinates and the postal address.
A call to the Nominatim reverse geocoding web service makes it possible to find out the postal address corresponding to the latitude and longitude of the user's position.


PREREQUISITE The ZnetDK for Mobile Starter Application is installed and configured (go to the Get Started page and follow the given procedure).

STEPS Only three steps are required:
  • Step 1: Coding the view in charge of displaying the map,
  • Step 2: Loading Leaflet's CSS stylesheet and the JS library from CDN hosting,
  • Step 3: Adding a new item to the App's navigation menu.

DEMONSTRATION See live demo....

Step 1 - Coding the view openstreetmap.php

The view openstreetmap.php must be installed into the INSTALL_DIR/applications/default/app/view/ folder.

  • The button with id='location-button' when clicked, zooms into the user's current location.
  • The <DIV> element with id='map' is the container that displays the map.
  • The jQuery event handler on the ZnetDK for Mobile afterviewdisplay events loads the map when the view is displayed for the first time.
  • The OpenStreetMap standard tile layer is loaded from the https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png url.
  • The Leaflet API locate() method, based on the HTML5 JS Geolocation API, allows you to retrieve the user's GPS coordinates, provided that the user accepts to be geolocated via the functionality of the internet browser.
  • On geolocation success (see onLocationFound() function), a marker is displayed on the map and a circle is drawn to indicate how accurately the user has been located.
  • In order to retrieve the postal address from the user's GPS coordinates (reverse geocoding), the Nominatim web service is called through the jQuery ajax() function.
  • Finally, a popup attached to the user's location marker displays their GPS coordinates in latitude and longitude as well as the corresponding postal address.

View openstreetmap.php

<div class="w3-stretch w3-margin-bottom w3-margin-top">
  <button id="location-button" class="w3-button w3-theme-action"><i class="fa fa-map-marker"></i> See my location</button>
</div>
<div id='map' class="w3-stretch"></div>
<style>
  #map { height: 65vh; }
  #zdk-side-nav-menu { z-index: 1004; } /* To display the side navigation menu over the map */
</style>
<script>
  console.log('<openstreetmap> view - Inline JS Code');
  var map = null, locationMarker = null, locationCircle = null;
  $('body').on('afterviewdisplay', function (event, viewId) { /* Once the view is displayed ... */
    if (viewId === 'openstreetmap' && map === null) {
      map = L.map('map', {zoomControl: false}).fitWorld();
      L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        maxZoom: 19,
        attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
      }).addTo(map);
      map.setZoom(2);
      map.on('locationfound', onLocationFound);
      map.on('locationerror', onLocationError);
    }
  });
  $('#location-button').on('click', function(){ /* On button click... */
    if (map !== null) {
      map.locate({setView: true, maxZoom: 17});
      $(this).prop('disabled', true);
    }
  });
  function onLocationFound(e) { /* When the user's location is found... */
    var radius = e.accuracy / 2,
      popupMsg = 'You are within <b>' + Math.round(radius) + ' meters</b> from this point.<br>'
        + 'Position: lat=<b>' + e.latlng.lat + '</b>, long=<b>' + e.latlng.lng + '</b>.<br>';
    locationMarker = L.marker(e.latlng).addTo(map).bindPopup(popupMsg);
    locationCircle = L.circle(e.latlng, radius).addTo(map);
    // Reverse geocoding to get postal address
    reverseGeocode(e.latlng.lat, e.latlng.lng, function(postalAddress){
      popupMsg += 'Address: <b>' + postalAddress + '</b>';
      locationMarker.unbindPopup().bindPopup(popupMsg).openPopup();
    });
  }
  function onLocationError(e) { /* In case of location error */
    console.error('Location error: ' + e.message);
  }
  function reverseGeocode(latitude, longitude, callback) { /* Reverse geocoding */
    $.ajax({
      type: 'POST',
      url: 'https://nominatim.openstreetmap.org/reverse?format=geojson&lat='
          + latitude + '&lon=' + longitude,
      dataType: 'json',
      success: function (response) {
        callback(response.features[0].properties.display_name);
      }
    }).fail(function(jqXHR, textStatus) {
      console.error('Request failed: ' + textStatus);
    });
  }
</script>

Step 2 - Loading the CSS and JS Leaflet libraries

The Leaflet CSS and JavaScript libraries are automatically loaded by specifying into the config.php of the Web App, the matching url on the CDN hosting (see Settings | App extra CSS and JS libraries).

The config.php script is located into the INSTALL_DIR/applications/default/app/ folder.

The two lines below can be inserted anywhere in the config.php script.

Script config.php

define('CFG_APP_JS', 'https://unpkg.com/leaflet@1.7.1/dist/leaflet.js');
define('CFG_APPLICATION_CSS', 'https://unpkg.com/leaflet@1.7.1/dist/leaflet.css');

Step 3 - Adding an "OpenStreetMap" item to the navigation menu

Finally, to give users access to the openstreetmap.php view, a menu item definition is added into the menu.php script of the Web App (see Get Started | New menu items for more information).

The menu.php script is located into the INSTALL_DIR/applications/default/app/ folder.

Script menu.php

static public function initAppMenuItems() {
    
    // ... Here, some menu items...
    
    \MenuManager::addMenuItem(NULL, 'openstreetmap', 'OpenStreetMap', 'fa-globe');
    
    // ... Here, other menu items...
    
}

Uploading files from your Application

DESCRIPTION This application demonstrates how to upload files from your ZnetDK for Mobile App.

The file upload mechanism is based on the <input> element declared with type="file" and the multiple property. An event handler is attached to the update events to display the selected files through the FileList Web API


PREREQUISITE The ZnetDK for Mobile Starter Application is installed and configured (go to the Get Started page and follow the given procedure).

STEPS Only three steps are required:
  • Step 1: Coding of the view that displays the form for selecting files to upload,
  • Step 2: Coding of the controller that processes the loaded files,
  • Step 3: Adding a new item to the App's navigation menu.

DEMONSTRATION See live demo....

Step 1 - Coding the view uploadview.php

The view uploadview.php must be installed into the INSTALL_DIR/applications/default/app/view/ folder.

About de HTML code:

  • The <form> element is declared with data-zdk-submit="uploadctrl:upload" to connect it to the uploadctrl controller and the upload action (see step 2 for detail on the controller action).
  • Within the form, the <input type="file"> element is named files[] (please note the square brackets) and the multiple property is set to allow multiple file selection. This input element is hidden by setting the CSS style to opacity:0
  • The file selection dialog is displayed when clicking the Select files... button.
  • Selected files are displayed into the <div class="selected"> element. The <p class="no-selection> paragraph is displayed when no file is selected.
  • A File tag entry field is also added to the form in order to associate in option, a tag to the file selection.
  • User submits the form by clicking the Upload button.

About the JavaScript code:

  • Calling the znetdkMobile.form.make() method enables data form transfer in AJAX to the specified controller action specified through the data-zdk-submit attribute. The submit callback function is implemented to customize the display of the response returned by the controller action.
  • When the Selected files... button is clicked, a click event is triggered on the input of type file to show the file selection dialog.
  • A event handler is declared for the update events issued by the input of type file when the file selection dialog is closed.
    The file selection is obtained via the this object within the event handler that has the files property which is of type FileList.
    If no file is selected, the message No file selected is displayed.
    Otherwise, the selected file names are added into the <div class="selected"> element and styled for display.

View uploadview.php

<form id="znetdkm-upload-demo" class="w3-theme-light" novalidate data-zdk-submit="uploadctrl:upload">
    <div class="w3-section">
        <label class="zdk-required"><b>Files to upload</b></label>
        <input class="upload w3-hide" type="file" name="files[]" multiple required style="opacity:0;position:absolute">
        <button class="upload w3-button w3-block w3-theme-action w3-margin-bottom" type="button"><i class="fa fa-folder-open-o fa-lg"></i> Select files...</button>
        <div class="w3-section">
            <p class="no-selection w3-text-red"><i class="fa fa-exclamation-circle"></i> No file selected.</p>
            <div class="selected"></div>
        </div>
        <label><b>File Tag</b></label>
        <input class="w3-input w3-border" type="text" name="file_tag" placeholder="Picture, Music, PDF document, ...">
        <button class="w3-button w3-block w3-green w3-section w3-padding" type="submit"><i class="fa fa-upload fa-lg"></i> Upload</button>
    </div>
</form>
<script>
    let uploadForm = znetdkMobile.form.make('#znetdkm-upload-demo', function(response) {
        if (response.success) {
            znetdkMobile.messages.removeAll();
            znetdkMobile.messages.add('info', response.summary, response.msg, false);
            return false;
        }
    });
    $('#znetdkm-upload-demo button.upload').on('click', function(){
        var fileInput = $('#znetdkm-upload-demo input.upload');
        fileInput.trigger('click');
        fileInput.off('change').one('change', function(){
            const selectedFiles = this.files,
                noSelectionEl = $('#znetdkm-upload-demo .no-selection'),
                selectionContainer = $('#znetdkm-upload-demo .selected');
            if (selectedFiles.length === 0) {
                noSelectionEl.removeClass('w3-hide');
                selectionContainer.empty();
                selectionContainer.addClass('w3-hide');
                return false;
            }
            noSelectionEl.addClass('w3-hide');
            selectionContainer.removeClass('w3-hide');
            for (let i = 0; i < selectedFiles.length; i++) {
                currentFile = selectedFiles.item(i);
                selectionContainer.append('<span class="w3-tag w3-round-large w3-theme-l3 w3-margin-right"><i class="fa fa-file-o"></i> ' + currentFile.name + '</span>');
            }
            // Form error message is hidden
            uploadForm.hideError();
        });
    });
</script>

Step 2 - Coding the controller uploadctrl.php

The PHP script uploadctrl.php processes the data transmited on form submit.

  • The uploaded files are read from the $_FILES PHP superglobal, in particular for the POST parameter named files.
  • The file tag is read from the \Request object for the POST parameter named file_tag.
  • If no file is selected, an error message is set as response of the controller action by calling the \Response::setFailedMessage() method.
  • On the other hand, if files are selected, their names and the entered file tag are returned in a message by calling the \Response::setSuccessMessage() method.

Controller uploadctrl.php

<?php
namespace app\controller;

class 
UploadCtrl extends \AppController {

    static protected function 
action_upload() {
        
$response = new \Response();        
        if (!
is_array($_FILES['files'])
                || !
is_array($_FILES['files']['name'])
                || 
count($_FILES['files']['name']) === 
                
|| $_FILES['files']['name'][0] === '') {
            
$response->setFailedMessage(NULL'Please, select a file.');
            return 
$response;
        }
        
$request = new \Request();
        
$fileCount count($_FILES['files']['name']);
        
$fileTag $request->file_tag === NULL '<i>no tag</i>' "<b>{$request->file_tag}</b>";
        
$filenames implode(', '$_FILES['files']['name']);
        
$response->setSuccessMessage('Upload'"Count of uploaded files: <b>$fileCount</b><br>"
            
"Uploaded files: <i>$filenames</i><br>"
            
"Choosen files tag : $fileTag<br>");
        return 
$response;
    }

}

Step 3 - Adding an "Upload files" item to the navigation menu

Finally, to give users access to the uploadview.php view, a menu item definition is added into the menu.php script of the Web App (see Get Started | New menu items for more information).

The menu.php script is located into the INSTALL_DIR/applications/default/app/ folder.

Script menu.php

static public function initAppMenuItems() {
    
    // ... Here, some menu items...
    
    \MenuManager::addMenuItem(NULL, 'uploadview', 'Upload files', 'fa-upload');
    
    // ... Here, other menu items...
    
}

Multiple selection in a web form

DESCRIPTION This application shows how to develop a form with a <select> element allowing multiple selection.

To allow multiple selection, the <select> element must be declared with the multiple property.


PREREQUISITE The ZnetDK for Mobile Starter Application is installed and configured (go to the Get Started page and follow the given procedure).

STEPS Only three steps are required:
  • Step 1: Coding of the view that displays the form for selecting multiple cities,
  • Step 2: Coding of the controller that processes the selected cities on form submit,
  • Step 3: Adding a new item to the App's navigation menu.

DEMONSTRATION See live demo....

Step 1 - Coding the view citiesview.php

The view citiesview.php must be installed into the INSTALL_DIR/applications/default/app/view/ folder.

  • The <form> element is declared with the data-zdk-submit ZnetDK property that specifies the citiesctrl controller and the submit action to call on form submit (see controller description in step 2)
  • The <select> element is declared with the HTML multiple property to display the element as a list allowing multiple selection.
    Pay attention in particular to the name cities[] given to this element. The square brackets are required to indicate in PHP that the POST parameter contains multiple values that have to be converted in array.

View uploadview.php

<form data-zdk-submit="citiesctrl:submit">
    <div class="w3-section">
        <label class="zdk-required"><b>Select multiple cities</b></label>
        <select class="w3-select w3-border" name="cities[]" size="12" multiple required>
            <optgroup label="EspaƱa">
                <option value="Madrid">Madrid</option>
                <option value="Barcelona">Barcelona</option>
                <option value="Sevilla">Sevilla</option>
            </optgroup>
            <optgroup label="France">
                <option value="Paris">Paris</option>
                <option value="Marseille">Marseille</option>
                <option value="Lyon">Lyon</option>
            </optgroup>
            <optgroup label="Italia">
                <option value="Roma">Roma</option>
                <option value="Torino">Torino</option>
                <option value="Milano">Milano</option>
            </optgroup>
        </select>
        <div class="w3-panel w3-pale-yellow">
            <p>
                <i class="fa fa-info-circle fa-lg"></i>
                <i>On desktop computer, hold the <b>Ctrl</b>, <b>Command</b> or <b>Shift</b> key before clicking several cities.</i>
            </p>
        </div>
        <button class="w3-button w3-block w3-green w3-section w3-padding" type="submit"><i class="fa fa-check fa-lg"></i> Submit</button>
    </div>
</form>

Step 2 - Coding the controller citiesctrl.php

The controller citiesctrl.php must be installed into the INSTALL_DIR/applications/default/app/controller/ folder.

  • The selected cities once submitted in the view are transmitted to the PHP controller's action (CitiesCtrl::action_submit() method) through the POST parameter named cities[].
  • The $request->cities property of the ZnetDK \Request object is used to read the selection of cities as an array.

Controller citiesctrl.php

<?php
namespace app\controller;
class 
CitiesCtrl extends \AppController {

    static protected function 
action_submit() {
        
$request = new \Request();
        
$citiesAsText implode(', '$request->cities);
        
$response = new \Response();
        
$response->setSuccessMessage('Selected cities'$citiesAsText);
        return 
$response;
    }

}

Step 3 - Adding an "Multiple Selection" item to the navigation menu

Finally, to give users access to the citiesview.php view, a menu item definition is added into the menu.php script of the Web App (see Get Started | New menu items for more information).

The menu.php script is located into the INSTALL_DIR/applications/default/app/ folder.

Script menu.php

static public function initAppMenuItems() {
    
    // ... Here, some menu items...
    
    \MenuManager::addMenuItem(NULL, 'citiesview', 'Multiple Selection', 'fa-list-alt');
    
    // ... Here, other menu items...
    
}