Code snippets for extending the capabilities of your ZnetDK for Mobile Starter App
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.fc-toolbar-title
class is for optimization purpose on mobile small screens.calOptions
JS variable defines the FullCalendar standard options applied to the calendar (see JS Calendar documentation).fetchEvents()
JS function requests through the AJAX API the meetings to display on the calendar.afterviewdisplay
events instantiates the calendar when the view is displayed for the first time.znetdkMobile.header.autoHideOnScroll()
method.
<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>
meetingctrl.php
The controller meetingctrl.php
must be installed into the INSTALL_DIR/applications/default/app/controller/
folder.
action_fetch()
method is called by the meetings.php
view through the AJAX API (see the fetchEvents()
JS function in the view).start
and end
POST parameters sent in the AJAX requests indicate the begin and end date of the week to display on the calendar.\Response
object).
<?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;
}
}
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.
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');
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.
static public function initAppMenuItems() {
// ... Here, some menu items...
\MenuManager::addMenuItem(NULL, 'meetings', 'My meetings', 'fa-calendar');
// ... Here, other menu items...
}
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.loadChartData()
JS function requests through the AJAX API the data of the bar chart.showChart()
JS function instantiates the bar chart on first call and updates its data if it is already instantiated (see ChartJS documentation).afterviewdisplay
events is triggered each time the view mybarchart.php
is displayed.
<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>
barchartctrl.php
The controller barchartctrl.php
must be installed into the INSTALL_DIR/applications/default/app/controller/
folder.
action_data()
method is called by the mybarchart.php
view through the AJAX API (see the loadChartData()
JS function in the view).\Response
object).
<?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;
}
}
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.
define('CFG_APP_JS', 'https://cdn.jsdelivr.net/npm/chart.js@2.9.4/dist/Chart.min.js');
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.
static public function initAppMenuItems() {
// ... Here, some menu items...
\MenuManager::addMenuItem(NULL, 'mybarchart', 'My bar chart', 'fa-bar-chart');
// ... Here, other menu items...
}
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.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()
).stopScan()
JS function (in turn by a call to Quagga.stop()
).Quagga.onProcessed()
method registers a callback function that draws frames around possible barcodes on the video-stream.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.
<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>
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.
define('CFG_APP_JS', 'https://cdnjs.cloudflare.com/ajax/libs/quagga/0.12.1/quagga.min.js');
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.
static public function initAppMenuItems() {
// ... Here, some menu items...
\MenuManager::addMenuItem(NULL, 'mybarcodeview', 'EAN-13 barcode', 'fa-barcode');
// ... Here, other menu items...
}
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.onRender()
is a JS function called before displaying a suggestion of country in the autocomplete field.
<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>
autocompletectrl.php
The controller autocompletectrl.php
must be installed into the INSTALL_DIR/applications/default/app/controller/
folder.
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.query
POST parameter and read through a ZnetDK \Request
object.\Response
object.
<?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($countriesAsJson, TRUE);
$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;
}
}
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.
static public function initAppMenuItems() {
// ... Here, some menu items...
\MenuManager::addMenuItem(NULL, 'autocompleteview', 'Autocomplete', 'fa-keyboard-o');
// ... Here, other menu items...
}
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.
textarea
field named my_content
.data-zdk-submit
attribute) and load (see data-zdk-load
attribute) the data typed in the textarea
field.textarea
element, the CKEditor component is instantiated by calling the ClassicEditor.create() API method.Show contentbutton, the view named
frontoffice
is displayed using the znetdkMobile.content.displayView() API method.
<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>
backofficectrl.php
The controller backofficectrl.php
must be installed into the INSTALL_DIR/applications/default/app/controller/
folder.
action_get()
method is called by both the backoffice
and frontoffice
views. It returns the text in HTML format typed in the editor.
\UserSession::getCustomValue()
method. Otherwise, a default text is returned.
\DAO
API.action_submit
method is called by backoffice
view to store in session the formated text typed in the HTML editor.
\UserSession::setCustomValue()
method.
\DAO
API.
<?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::$contentSessionKey, htmlentities($_REQUEST['my_content']));
$response = new \Response();
$response->setSuccessMessage(NULL, 'Content saved');
return $response;
}
}
frontoffice.php
The view frontoffice.php
must be installed into the INSTALL_DIR/applications/default/app/view/
folder.
frontoffice
view is retrieved through the get
controller's action by calling the \AppController::doAction()
method.editcontent
is displayed on the view to display the backoffice
view in order to edit the HTML content.
znetdkMobile.action.addCustomButton()
method.
znetdkMobile.action.registerView()
displays the action button and implements the click event handler.
znetdkMobile.content.displayView()
method is called to display the backoffice
view.
<?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>
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.
define('CFG_APP_JS', 'https://cdn.ckeditor.com/ckeditor5/25.0.0/classic/ckeditor.js');
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.
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...
}
dataformview.php
The view dataformview.php
must be installed into the INSTALL_DIR/applications/default/app/view/
folder.
id="my-data-form"
is declared with the data-zdk-submit
attribute to specify the controller and the action to call on form submit.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.
type="number"
attribute.zdk-required
ZnetDK CSS class.
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.data-zdk-submit
attribute, the ZnetDK method znetdkMobile.form.make()
is called in JavaScript.
app\controller\FormValidCtrl::action_submit()
PHP method described in next chapter.
<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>
formvalidctrl.php
The controller formvalidctrl.php
must be installed into the INSTALL_DIR/applications/default/app/controller/
folder.
app\controller\FormValidCtrl
class is derived from the \AppController
ZnetDK class to be identified as an application controller.action_submit()
method, a controller's action, is called on submit of the input form.
\app\validator\FormValidator()
custom Validator
class described in next chapter.\Response::setFailedMessage()
method.\Response::setSuccessMessage()
method.
<?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;
}
}
formvalidator.php
The validator formvalidator.php
must be installed into the INSTALL_DIR/applications/default/app/validator/
folder.
FormValidator
custom class is derived from the \Validator
ZnetDK class, specialized in data validation.initVariables()
must be implemented to indicate as an array, the names of the the POST parameters whose values the validator must check.check_
are responsible for validating the value of a particular input field.
check_title()
method checks the value of the input field named title
.$value
variable is correct, then the method returns TRUE
.
Validator::setErrorMessage()
method and the value FALSE
is returned.check_max_number()
method, the value of the input field named min_number
is read by calling the Validator::getValue()
method.
<?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) > 0 && !is_numeric($value)) {
$this->setErrorMessage('Min. number is not a number.');
return FALSE;
}
return TRUE;
}
protected function check_max_number($value) {
if (strlen($value) > 0 && !is_numeric($value)) {
$this->setErrorMessage('Max. number is not a number.');
return FALSE;
}
if (strlen($value) > 0 && $this->getValue('min_number') >= $value) {
$this->setErrorMessage('Max. number is not higher than the min. number');
return FALSE;
}
return TRUE;
}
}
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.
static public function initAppMenuItems() {
// ... Here, some menu items...
\MenuManager::addMenuItem(NULL, 'dataformview', 'Data form', 'fa-check-circle-o');
// ... Here, other menu items...
}
statboxes.php
The view statboxes.php
must be installed into the INSTALL_DIR/applications/default/app/view/
folder.
get
action implemented in the \app\controller\StatCtrl
controller (see implementation in Step 2), via the doAction()
method.get
action are inserted in the corresponding <h3>
tags when the view is converted to HTML on the server-side, before being downloaded.zdk-viewreload
CSS class is added to any HTML element of the view, in this case to the div.statboxes
element.w3-row-padding
and w3-quarter
W3.CSS classes.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).<script>
tag in response to the clicks on the Updatebutton, to request in AJAX the last statistics and display them in the view, thus avoiding fully reloading the view.
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.
znetdkMobile.messages.showSnackbar()
method.
<?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.
.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);
}
statctrl.php
The controller statctrl.php
must be installed into the INSTALL_DIR/applications/default/app/controller/
folder.
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.\Convert::toMoney()
method is called to convert the float values to currency amounts according to the locale configured for the application.
<?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;
}
}
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.
define('LC_LOCALE_ALL', serialize(array('fr_FR.utf8')));
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.
define('CFG_APPLICATION_CSS', serialize([
'https://www.w3schools.com/lib/w3-colors-flat.css',
'applications/default/public/css/statboxes.css'
]));
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.
static public function initAppMenuItems() {
// ... Here, some menu items...
\MenuManager::addMenuItem(NULL, 'statboxes', 'Dashboard', 'fa-dashboard');
// ... Here, other menu items...
}
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.
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.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).
znetdkMobile.ajax.getParamsFromAjaxURL()
method.
<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>
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.
<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>
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.
<h2>Content</h2>
<p>Example of text content translated into the application display language.</p>
<h2>Contenido</h2>
<p>Ejemplo de contenido de texto traducido al idioma de visualización de la aplicación.</p>
<h2>Contente</h2>
<p>Exemplo de conteúdo de texto traduzido para o idioma de exibição do aplicativo.</p>
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.
<?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', '© 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');
<?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', '© 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');
<?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', '© 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');
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.
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...
}
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.
define('CFG_MULTI_LANG_ENABLED', TRUE);
To add the PHPMailer API (version 6.5.1 at the time of writing this article) to your Starter Application:
INSTALL_DIR/applications/default/app/
directory.INSTALL_DIR/applications/default/app/PHPMailer-master/
directory to INSTALL_DIR/applications/default/app/phpmailer/
.
src/
subdirectory must exist into the new INSTALL_DIR/applications/default/app/phpmailer/
directory.
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.
require
PHP statement. The use
statement eliminates the need to specify the namespace as prefix when instantiating a new PHPMailer
object.app\controller\SendMailCtrl
class is derived from the \AppController
ZnetDK class to be identified as an application controller.action_send()
method, a controller's action, is called on submit of the input form implemented in step 3.\Request
object.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).\Response::setFailedMessage()
method.\Response::setSuccessMessage()
method.
<?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;
}
}
sendmailview.php
The view sendmailview.php
must be installed into the INSTALL_DIR/applications/default/app/view/
folder.
id="sendmail-form"
is declared with the data-zdk-submit
attribute to specify the controller and the action to call on form submit.data-zdk-submit
attribute, the ZnetDK method znetdkMobile.form.make()
is called in JavaScript.
app\controller\SendMailCtrl::action_send()
PHP method described in the previous chapter.
<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>
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.
static public function initAppMenuItems() {
// ... Here, some menu items...
\MenuManager::addMenuItem(NULL, 'sendmailview', 'Send mail', 'fa-paper-plane-o');
// ... Here, other menu items...
}
openstreetmap.php
The view openstreetmap.php
must be installed into the INSTALL_DIR/applications/default/app/view/
folder.
id='location-button'
when clicked, zooms into the user's current location.<DIV>
element with id='map'
is the container that displays the map.afterviewdisplay
events loads the map when the view is displayed for the first time.https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png
url.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.onLocationFound()
function), a marker is displayed on the map and a circle is drawn to indicate how accurately the user has been located.ajax()
function.
<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>
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.
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');
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.
static public function initAppMenuItems() {
// ... Here, some menu items...
\MenuManager::addMenuItem(NULL, 'openstreetmap', 'OpenStreetMap', 'fa-globe');
// ... Here, other menu items...
}
uploadview.php
The view uploadview.php
must be installed into the INSTALL_DIR/applications/default/app/view/
folder.
About de HTML code:
<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).<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
<div class="selected">
element. The <p class="no-selection>
paragraph is displayed when no file is selected.About the JavaScript code:
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.click
event is triggered on the input of type file to show the file selection dialog.update
events issued by the input of type file when the file selection dialog is closed.
this
object within the event handler that has the files
property which is of type FileList
.
<div class="selected">
element and styled for display.
<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>
uploadctrl.php
The PHP script uploadctrl.php
processes the data transmited on form submit.
$_FILES
PHP superglobal, in particular for the POST parameter named files
.\Request
object for the POST parameter named file_tag
.\Response::setFailedMessage()
method.\Response::setSuccessMessage()
method.
<?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']) === 0
|| $_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;
}
}
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.
static public function initAppMenuItems() {
// ... Here, some menu items...
\MenuManager::addMenuItem(NULL, 'uploadview', 'Upload files', 'fa-upload');
// ... Here, other menu items...
}
citiesview.php
The view citiesview.php
must be installed into the INSTALL_DIR/applications/default/app/view/
folder.
<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)<select>
element is declared with the HTML multiple
property to display the element as a list allowing multiple selection.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.
<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>
citiesctrl.php
The controller citiesctrl.php
must be installed into the INSTALL_DIR/applications/default/app/controller/
folder.
CitiesCtrl::action_submit()
method) through the POST parameter named cities[]
.$request->cities
property of the ZnetDK \Request
object is used to read the selection of cities as an array.
<?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;
}
}
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.
static public function initAppMenuItems() {
// ... Here, some menu items...
\MenuManager::addMenuItem(NULL, 'citiesview', 'Multiple Selection', 'fa-list-alt');
// ... Here, other menu items...
}