Web modules are a special integration mechanism between the Melbis server platform and its Windows client, Melbis Shop. The essence of the mechanism: a full-featured Chromium-based browser is embedded inside the application, capable of opening website pages directly within the application’s windows and communicating with them in both directions — passing the context of the manager’s current work to the server and receiving results back. The developer creates a standard Melbis module, and the application opens it inside its own window.
This allows extending the application’s functionality without modifying its code: analytics, reports, management tools — all of this can be implemented in PHP and connected through settings.
Embedded modules work directly inside the application’s working
windows — “Products / Prices”, “Business / Customers”, “Business /
Orders”. To open an embedded web module in any of these windows, press
Ctrl+W — a panel with an embedded browser will appear.
The key feature: when a manager navigates through records (selects a product, opens an order, switches to a customer), the application automatically sends a POST request to the module with the current window’s context. The module always knows which object the manager is currently working with and can display relevant information — without any additional actions on their part.
There can be multiple such modules for each section — they are switched using tabs. Access rights can be set for each module: which user groups can see it.
Examples of embedded modules for the “Products / Prices” section:
Examples for the “Business / Orders” and “Business / Customers” sections:
Embedded modules are configured in the application: “Design → Modules and Options”, the “Embedded Modules” tab. Select a group (“Products”, “Customer”, “Orders”) and add a module with the following parameters:
http://my-shop.com/?mod=melbis_web_sample.melbis_web_sample). Used for access rights
verification.login and
pass_code (MD5 hash of the password) fields in POST. This
allows the module to authenticate the user and verify their
permissions.With each request to an embedded module, the application sends a POST request with the current window’s context. Below is the full set of parameters for the “Orders” section:
// $mVars in the module:
[
'get' => ['mod' => 'melbis_web_sample'],
'post' => [
'order_id' => '1',
'order_version_id' => '2',
'order_version_client_id' => '1',
'order_version_user_id' => '1',
'order_client_field_id' => '3',
'order_store_id' => '2',
'order_option_id' => '3',
'login' => 'admin',
'pass_code' => '21232f297a57a5a743894a0e4a801fc3',
]
]In the “Products” section, the store_id of the selected
product is passed. In the “Customers” section — the client identifier.
The password is passed as an MD5 hash.
For web modules, the standard demo store distribution includes the
melbis_inc_auth library. It handles all the routine code:
user authentication via login + pass_code,
storing the authentication state in a PHP session, checking access
rights to the module, and routing POST calls between module
functions.
The central function —
MELBIS_INC_AUTH_router($module, $mVars) — performs
the following steps in order:
logout in
POST).login +
pass_code from POST, or from a previously saved
session.gVars['page'] the global variables available
in all templates: authentication status (auth),
user_id, and mod — the URL of the current
module. The {PAGE:MOD} variable is used in templates as the
base URL for AJAX requests.func parameter is passed in
POST — calls the function
MODULE_NAME_func($userId, $mVars), otherwise calls
MODULE_NAME_default($userId, $mVars).Thanks to this, the entire main function of the module reduces to a single line:
function MELBIS_WEB_SAMPLE($mVars)
{
return MELBIS_INC_AUTH_router(MELBIS()->UnitName(), $mVars);
}MELBIS()->UnitName() returns the name of the current
module — it is passed to the router for rights verification and for
constructing the names of child functions.
The _default function is the entry point when the module
is first opened. It receives $mUserId (or null
if authentication failed) and the full $mVars array.
function MELBIS_WEB_SAMPLE_default($mUserId, $mVars)
{
$tpl = MELBIS()->TplCreate();
if ( $mUserId > 0 )
{
// Prepare the user rights cache for working with web_key
MELBIS_INC_AUTH_web_key_prepare($mUserId);
// Pass input variables to the template
MELBIS()->TplAssign($tpl, 'VARS', var_export($mVars, true));
// Pass order data for the back-transfer mechanism
MELBIS()->TplAssign($tpl, 'ORDER', $mVars['post']['order'] ?? '{}');
MELBIS()->TplParse($tpl, 'SCRIPTS', 'scripts');
MELBIS()->TplParse($tpl, 'CONTENT', 'page');
}
else
{
// User is not authenticated — show the login form
MELBIS()->TplParse($tpl, 'CONTENT', 'auth');
}
MELBIS()->gVars['page'] += ['title' => 'Sample Web module'];
return MELBIS()->TplFinal($tpl, 'main');
}The auth.htm template contains a call to the
melbis_web_auth module — it renders the login form. After
the form is submitted, MELBIS_INC_AUTH_router processes the
POST again, authenticates the user, and this time returns the main
page.htm template.
For interactive actions within the module, JavaScript passes the
func parameter in POST. The router will find and call the
corresponding function — provided the user is authenticated and has
access to the module.
In the template, the {PAGE:MOD} variable contains the
ready-made URL of the current module
(/?mod=melbis_web_sample) — it is convenient to use as the
endpoint for all AJAX requests:
$('#melbis_table_cataloge').bootstrapTable({
url: '{PAGE:MOD}',
method: 'post',
queryParams: function(params) {
params.func = 'get_cataloge'; // the router will call MELBIS_WEB_SAMPLE_get_cataloge
return params;
},
pagination: true,
sidePagination: 'server',
pageSize: 20,
sortName: 'absindex',
sortOrder: 'asc'
});On the PHP side, the function receives control only if the user is authenticated, executes the query, and returns JSON:
function MELBIS_WEB_SAMPLE_get_cataloge($mUserId, $mVars)
{
$limit = (int) $mVars['post']['limit'];
$offset = (int) $mVars['post']['offset'];
$sort = preg_replace('/[^a-z_]/', '', $mVars['post']['sort']);
$sort .= ($mVars['post']['order'] == 'asc') ? ' ASC' : ' DESC';
$command = "SELECT t.id, t.name, t.tlevel, COUNT(ts.id) AS amount
FROM {DBNICK}_topic t
LEFT JOIN {DBNICK}_topic_store ts ON t.id = ts.topic_id
GROUP BY t.id
ORDER BY $sort";
$data = MELBIS()->SqlSelectLimit(__LINE__, $command, $offset, $limit);
return json_encode($data);
}The same principle applies to any other module functions — searching, filtering, saving data to the database, sending notifications, and so on.
The most unique capability of embedded web modules is passing data from the browser back to the Windows application without additional HTTP requests.
This is implemented via console.log with a special key.
The application intercepts the browser’s console output and, upon
detecting the MELBIS_ORDER_UPDATE key, applies the received
data to the order being edited.
The mechanism only works in the order editing window while the order has not yet been saved. In other sections (products, customers), the web module writes changes directly to the database via standard parser SQL methods when needed.
A practical example: the module receives all the data of the open order from the application in JSON format, displays it in interactive tables, and allows the manager to edit individual fields directly in the browser.
// PHP: retrieve the order data passed by the application and send it to the template
MELBIS()->TplAssign($tpl, 'ORDER', $mVars['post']['order'] ?? '{}');// Template: parse JSON and build tables
var melbis_order = {ORDER};
function melbis_init_back() {
for (var table in melbis_order) {
var data = melbis_order[table];
var columns = [];
for (var c in data[0]) {
columns.push({field: c, title: c});
}
$('.melbis_table_order[data-table="' + table + '"]').bootstrapTable({
columns: columns,
data: data
});
}
}
// Click on a cell — edit dialog
$('.melbis_table_order').on('click-cell.bs.table', function(event, field, value, row) {
var table = event.target.dataset.table;
bootbox.prompt({
title: 'Edit value of "' + field + '"',
value: value,
callback: function(result) {
if (result != null) {
var update = {};
update[table] = [{id: row.id, [field]: result}];
// Pass the change to the application via the console
console.log('MELBIS_ORDER_UPDATE' + JSON.stringify(update));
}
}
});
});The application reads the console output, recognizes the
MELBIS_ORDER_UPDATE key, and applies the changes to the
order fields in its interface.
External modules differ from embedded ones in one fundamental way:
they are not tied to a specific application window and do not receive
the context of the current record. Instead, when opened from the
application, only the user’s authentication parameters
(login, pass_code) are passed.
Access to external modules is available through the “Business / Web Modules” section — this contains a browser with a catalog of all available modules in the left panel. In addition, any of these modules can be opened in a regular browser on any device — a tablet, smartphone, or a workstation without the application.
This makes external modules a universal tool for dashboards, analytical reports, and administrative panels — anything that does not require binding to a specific product or order. The screenshot above shows an example of a real external module with sales analytics, financial accounting, inventory analysis, and management tools built directly into the application.
Configuring external modules — “Design → Modules and Options”, the “External Web Modules” tab: the name, URL, and symbolic key of each module are set here, along with access rights by user groups.
Since external modules can also be opened from a regular browser, authentication here works in two modes automatically:
When opened from the application — Melbis Shop
passes login and pass_code in POST.
MELBIS_INC_AUTH_router authenticates the user without a
form and immediately displays the content.
When opened directly in a browser — POST does not
contain credentials, the router returns $mUserId = null,
and the _default function substitutes the authentication
form template. After a successful login, the data is saved in the PHP
session, and the form is no longer shown on subsequent requests.
The same module code works in both cases — the branching happens
entirely automatically inside the melbis_inc_auth library.
The developer only needs to correctly handle the
$mUserId == null case in the _default
function.
Windows Melbis Shop
│
│ POST: store_id / order_id / login / pass_code
▼
index.php → Run('melbis_web_sample')
│
▼
MELBIS_WEB_SAMPLE($mVars)
│
▼
MELBIS_INC_AUTH_router(...)
├─ authentication + rights verification
├─ func = 'default' → MELBIS_WEB_SAMPLE_default($userId, $mVars) → HTML
├─ func = 'get_cataloge' → MELBIS_WEB_SAMPLE_get_cataloge($userId, $mVars) → JSON
└─ func = 'get_goods' → MELBIS_WEB_SAMPLE_get_goods($userId, $mVars) → JSON
HTML page in the application's browser
│ (only for the order editing window)
▼
console.log('MELBIS_ORDER_UPDATE{"version":[...]}')
│
▼
Windows Melbis Shop — applies changes to the order