# Developers

# Coding style

A list of coding style rules to be used when developing in vtenext.

<table id="bkmrk-file-encoding%3A-utf-8" style="border-collapse:collapse;width:100%;height:1513.17px;"><colgroup><col style="width:16.9235%;"></col><col style="width:83.0765%;"></col></colgroup><tbody><tr style="height:29.7969px;"><td class="align-right" style="height:29.7969px;">**File encoding:**</td><td style="height:29.7969px;">UTF-8. All files should be encoded in UTF-8</td></tr><tr style="height:29.7969px;"><td class="align-right" style="height:29.7969px;">**Newlines:**</td><td style="height:29.7969px;">Unix style, so **\\n**, not \\r\\n or \\r</td></tr><tr style="height:288.391px;"><td class="align-right" style="height:288.391px;">**Indentation size:**</td><td style="height:288.391px;">With TABS, set to 4 character. Don't mix spaces and tabs:

[![Jwzimage.png](https://usermanual.vtenext.com/uploads/images/gallery/2026-02/jwzimage.png)](https://usermanual.vtenext.com/uploads/images/gallery/2025-10/Jwzimage.png)

</td></tr><tr style="height:657.906px;"><td style="height:657.906px;">**Indentation style:**</td><td style="height:657.906px;">C Style, so:

- Opening brace ( `{` ) on the same line as its control clause (an exception can be made if the line is very long, so in this case the brace can go to a new line, for better readability)
- Closing brace ( `}` ) aligned with the opening statement
- Single space before the opening brace
- Single space before the parenthesis of a control structure (`if`, `for`, `foreach`, ...)

<table style="border-collapse:collapse;width:100.145%;"><colgroup><col style="width:50.0725%;"></col><col style="width:50.0725%;"></col></colgroup><thead><tr><td class="align-center" colspan="2">**Examples**  
</td></tr></thead><tbody><tr><td><p class="callout success">Good</p>

```php
if ($var === '234') {
    $out = 'ok';
} elseif ($var === '567') {
    $out = 'also ok';
} else {
    $out = 'maybe ok?';
}
```

</td><td><p class="callout danger">**Bad**: no space between `foreach` and `(`</p>

```
foreach($array as $var) {
    // do something
}
```

</td></tr><tr><td><p class="callout danger">**Bad**: opening brace on new line</p>

```
if ($var == 8)
{
    // do something
}
```

</td><td>  
</td></tr></tbody></table>

Please note that these style rules are not strictly observed throughout the existing code, so you may find exceptions. Try to follow them, to improve the code homogeneity, but don't be too rigid in following them when evaluating existing code.

</td></tr><tr><td class="align-right">**Line length:**</td><td>We don't have a strict maximum line length. Usually up to 200 characters is ok, more than that can be hard to read on smaller screens, so better to split the line, but this is left to the developer's choice.

Also, splitting too much, for example a 300 chars line divided in 30 lines of 10 chars is no more readable than the original line. Balance readability and conciseness of code.

</td></tr><tr style="height:447.688px;"><td class="align-right" style="height:447.688px;">**PHP tags:**</td><td style="height:447.688px;">Use standard opening tag: **`<?php`** , not the short one: `<?`

Avoid the closing tag ( `?>` ) if there's no output.

<table style="border-collapse:collapse;width:100.143%;"><colgroup><col style="width:33.381%;"></col><col style="width:33.381%;"></col><col style="width:33.381%;"></col></colgroup><thead><tr><td colspan="3">**Examples**

</td></tr></thead><tbody><tr><td><p class="callout success">**Good**: standard opening tag, no closing tag</p>

```php
<?php

$var = 'value';
// php code
 
// no closing tag here
```

</td><td><p class="callout danger">**Bad**: short opening tag, closing tag</p>

```php
<? // short tag
 
$var = 'value';
// php code
 
// unnecessary
// closing tag
?>
```

</td><td><p class="callout success">**Good**: standard opening tag, closing tag with output</p>

```php
<?php

$var = 'value';
// php code

// output after php code
?>
<html>
<!-- html code -->
</html>
```

</td></tr></tbody></table>

</td></tr><tr style="height:29.7969px;"><td class="align-right" style="height:29.7969px;">**Auto formatting:**</td><td style="height:29.7969px;">- **Core files**: Do not enable any "auto formatting on save" functionality in your IDE/editor, to avoid too many changes in existing files, which will be hard to diff.
- **Custom files**: you can reformat existing files to make them compliant with our standards.

</td></tr><tr style="height:29.7969px;"><td class="align-right" style="height:29.7969px;">**License:**</td><td style="height:29.7969px;">When creating a new file (php, js, tpl, html, scss) to be included in the standard vtenext, use the following license at the top of the file:

```php
<?php
/*************************************
* SPDX-FileCopyrightText: 2009-present Vtenext S.r.l. Società Benefit
* SPDX-License-Identifier: LicenseRef-vtenext-business-license
************************************/
```

</td></tr></tbody></table>

</body></html>

# Weak comparisons

#### **Backward Incompatible Changes**

View [https://www.php.net/manual/en/migration80.incompatible.php](https://www.php.net/manual/en/migration80.incompatible.php)

#### **Key principles**

Follow these fundamental rules when writing compatible code:

<p class="callout success">**Analyze variable types:** Always understand what types and values a variable can hold.</p>

<p class="callout success">**Use strict comparisons:** Prefer `===` and `!==` over `==` and `!=` to avoid implicit type coercion.</p>

<p class="callout success">**Declare types explicitly:** Always use type declarations for function/method parameters and return types.</p>

<p class="callout success">**Validate with strict checks:** Use strict type checks in conditional statements to prevent unexpected behavior (e.g., `in_array` with `true` as third parameter).  
Example: [https://www.php.net/manual/en/function.in-array.php#example-2](https://www.php.net/manual/en/function.in-array.php#example-2)</p>

<p class="callout success">**Use PHP constants:** Always use predefined PHP constants instead of numbers (e.g., `UPLOAD_ERR_OK` instead of `0`).</p>

<p class="callout success">**Enable strict types:** Add `declare(strict_types=1);` at the top of your PHP files ***when possible***.</p>

#### **Strings**

Use `empty()` instead of comparing with empty string.

<table id="bkmrk-%E2%9D%8C-don%27t-%E2%9C%85-do-if-%28%24a-"><tbody><tr><th>❌ DON'T</th><th>✅ DO</th></tr><tr><td>```php
if ($a == '') {
    // ...
}
```

</td><td>```php
if (empty($a)) {
    // ...
}
```

</td></tr><tr><td>```php
if ($a != '') {
    // ...
}
```

</td><td>```php
if (!empty($a)) {
    // ...
}
```

</td></tr></tbody></table>

#### **Checkbox values**

In PHP 8, comparing numeric strings with numbers produces different results.

<table id="bkmrk-%E2%9D%8C-don%27t-%E2%9C%85-do-if-%28%24a--1"><tbody><tr><th>❌ DON'T</th><th>✅ DO</th></tr><tr><td>```php
if ($a == 0) {
    // ...
} elseif ($a == 1) {
    // ...
} else {
    // ...
}
```

</td><td>```php
if (strval($a) === '0') {
    // ...
} elseif (strval($a) === '1') {
    // ...
} else {
    // ...
}
```

</td></tr></tbody></table>

# RequestHandler

#### **What is RequestHandler?**

`RH` (alias for `RequestHandler`) provides secure, centralized access to HTTP request data. It automatically sanitizes input using `vtlib_purify()` and applies type-safe filters from `F::` enum. This replaces direct access to superglobals like `$_REQUEST`, `$_GET`, `$_POST`, etc.  
Our goal with this system is to prevent vulnerabilities ex. SQL Injection, XSS, File Inclusion, ecc.

##### **How filtering works**

RequestHandler applies a two-level filtering process:

- **Whitelisted keys** (defined in `config/request.config.php`): These keys are automatically filtered using the filter specified in the configuration during initialization. The configured filter is applied first, and if you specify an additional filter when accessing the value, that filter is applied to the already-sanitized value.
- **Non-whitelisted keys**: All other keys pass through `vtlib_purify()` for HTML sanitization by default. If you specify an explicit filter (e.g., `F::int`, `F::email`), it is applied to the purified value.

```php
// ex. index.php?module=Accounts&action=CustomAction&custom=123&another=TEST

// In config: 'module' => F::mod
$module = RH::r('module');               // Uses F::mod filter from config
$customParam = RH::r('custom', F::int);  // Not in config: vtlib_purify() + F::int
$anotherParam = RH::r('another');        // Not in config: only vtlib_purify()
```

**This approach ensures:**

- Unknown parameters are still sanitized to prevent XSS attacks
- Explicit filters always provide additional type safety
- Explicitly mapping more parameters enhances input security in the application.

---

#### ✅ **DO**

```php
// ✅ Always access superglobals through RH
$module = RH::r('module', F::mod);
$id = RH::r('id', F::int);

// ✅ Always specify appropriate filters
$email = RH::r('email', F::email);
$age = RH::r('age', F::int);
$status = RH::r('status', F::enum(['active', 'inactive']));

// ✅ Check for null values when data is required
$userId = RH::r('user_id', F::int);
if ($userId === null) {
    throw new InvalidArgumentException('User ID is required');
}

// ✅ Use POST for operations that are sensitive or that modify the CRM state (e.g.: sending an email, saving a record, ...)
RH::ensureMethod('POST');
```

#### ❌ **DON'Ts**

<p class="callout danger">**Avoid these practices:** They bypass security, are redundant and can introduce vulnerabilities.</p>

- **Never access superglobals directly:** `$_REQUEST`, `$_GET`, `$_POST`, `$_COOKIE`, `$_FILES`, `$_SERVER`
- **Don't use manual sanitization:** Avoid `vtlib_purify()` calls, RH handles this centrally
- **Don't skip filters:** Calling `RH::r('age')` without a filter may return unvalidated strings
- **Don't use raw methods without reason:** Prefer `RH::r(..., F::...)` over `RH::r_raw(...)`
- **Don't ignore security:** Always validate HTTP methods for sensitive operations or action that modify the CRM state
- **Don't assume data types:** Always use appropriate filters and null checks

```php
// ❌ Direct superglobal access
$userId = $_REQUEST['user_id'];
$email = $_POST['email'];
$page = intval($_GET['page']);

// ❌ Manual sanitization
$name = vtlib_purify($_REQUEST['name']);
$description = vtlib_purify($_POST['description']);

// ❌ No filter applied
$age = RH::r('age');  // Returns string "25" instead of int 25
if ($age > 18) {      // String comparison may not work as expected
    // ...
}

// ❌ Using raw without justification
$data = RH::r_raw('data');  // Bypasses sanitization
echo $data;                 // Potential XSS vulnerability

// ❌ No null check for required data
$recordId = RH::r('record', F::int);
$record = getRecord($recordId);  // May fail if $recordId is null

// ❌ Accepting any HTTP method for sensitive operation or action that modify the CRM state
// No method check here - vulnerable to GET-based attacks
$settings = RH::r('settings', F::json);
saveSettings($settings);

// ❌ Type assumption without validation
$ids = RH::r('ids');     // Assuming it's an array
foreach ($ids as $id) {  // Fatal error if $ids is not an array
    // ...
}
```

---

#### **Main methods**

<table id="bkmrk-metodo-descrizione-r"><thead><tr><th>Method</th><th>Description</th></tr></thead><tbody><tr><td>`RH::r($key, $filter)`</td><td>Access $\_REQUEST with filter (optional)</td></tr><tr><td>`RH::g($key, $filter)`</td><td>Access $\_GET with filter (optional)</td></tr><tr><td>`RH::p($key, $filter)`</td><td>Access $\_POST with filter (optional)</td></tr><tr><td>`RH::c($key, $filter)`</td><td>Access $\_COOKIE with filter (optional)</td></tr><tr><td>`RH::s($key, $filter)`</td><td>Access $\_SERVER with filter (optional)</td></tr><tr><td>`RH::f($key)`</td><td>Access uploaded files (PSR-7)</td></tr><tr><td>`RH::r_mod()`</td><td>Module name from $\_REQUEST</td></tr><tr><td>`RH::r_action()`</td><td>Action name from $\_REQUEST</td></tr><tr><td>`RH::r_record()`</td><td>Record ID from $\_REQUEST</td></tr><tr><td>`RH::r_has($key)`</td><td>Verify key existence (sanitized)</td></tr><tr><td>`RH::r_has_raw($key)`</td><td>Verify key existence (raw)</td></tr><tr><td>`RH::r_all()`</td><td>All parameters sanitized</td></tr><tr><td>`RH::r_all_raw()`</td><td>All parameters raw (unsanitized)</td></tr><tr><td>`RH::ensureMethod($method)`</td><td>Force HTTP method (405 if different)</td></tr><tr><td>`RH::isFromMobile()`</td><td>Check if request is from mobile app</td></tr><tr><td>`RH::isFromPortal()`</td><td>Check if request is from portal</td></tr></tbody></table>

#### **Basic filters**

- **Null input always returns null** - All filters preserve null values
- **Filters for primitive types (int, float, bool)** - Convert invalid input to default values (0, 0.0, false)
- **Validation filters (email, url, ip, enum, regex, datetime)** - Return null for invalid input
- **Sanitization filters (str, html, alpha, alphanum, mod, action, etc.)** - Remove invalid characters and return cleaned string
- **Always check for null** - Use null coalescing operator `??` for required values

<table id="bkmrk-filter-use-case-exam" style="width:100%;"><thead><tr><th style="width:16%;">Filter</th><th style="width:20%;">Use case</th><th style="width:32%;">Valid input → Output</th><th style="width:32%;">Invalid input → Output</th></tr></thead><tbody><tr><td>`F::int`</td><td>IDs, numbers</td><td>`"42"` → `42`</td><td>`"abc"` → `0`  
`null` → `null`</td></tr><tr><td>`F::float`</td><td>Decimal numbers</td><td>`"3.14"` → `3.14`</td><td>`"abc"` → `0.0`  
`null` → `null`</td></tr><tr><td>`F::str`</td><td>General strings (**no HTML; default max length: 1000 characters**)</td><td>`"Hello"` → `"Hello"`</td><td>`"<b>abc</b>"` → `"abc"` (tags stripped)

`"Very long text..."` → Truncated to 1000 chars by default  
`null` → `null`

</td></tr><tr><td>`F::str(max: 50)`</td><td>Limited strings (**no HTML**)</td><td>`"Short text"` → `"Short text"`</td><td>`"Very long text..."` → Truncated to 50 chars  
`null` → `null`</td></tr><tr><td>`F::substr(max: 50)`</td><td>Substring (**keeps HTML**, may result in invalid HTML)</td><td>`"<b>text</b>"` → `"<b>text</b>"`</td><td>Long string → Truncated (HTML preserved)  
`null` → `null`</td></tr><tr><td>`F::html`</td><td>HTML strings (purified)</td><td>`"<b>bold</b>"` → `"<b>bold</b>"`</td><td>`"<script>alert(0)</script>"` → `""` (XSS removed)  
`null` → `null`</td></tr><tr><td>`F::bool`</td><td>Boolean flags</td><td>`"1"`, `"true"`, `"yes"` → `true`</td><td>`"0"`, `"false"`, `"no"` → `false`  
`null` → `null`</td></tr><tr><td>`F::email`</td><td>Email addresses</td><td>`"user@example.com"` → `"user@example.com"`</td><td>`"invalid-email"` → `null`  
`"user@"` → `null`  
`null` → `null`</td></tr><tr><td>`F::url`</td><td>URLs</td><td>`"https://example.com"` → `"https://example.com"`</td><td>`"invalid-url"` → `null`  
`"htp://exa"` → `null`  
`null` → `null`</td></tr><tr><td>`F::ip`</td><td>IP addresses</td><td>`"192.168.1.1"` → `"192.168.1.1"`</td><td>`"999.999.999.999"` → `null`  
`"invalid-ip"` → `null`  
`null` → `null`</td></tr><tr><td>`F::json`</td><td>JSON data</td><td>`'{"key":"value"}'` → `["key" => "value"]`</td><td>`'invalid json'` → `null`  
`'{broken'` → `null`  
`null` → `null`</td></tr></tbody></table>

#### **Specific filters**

<table id="bkmrk-filter-use-case-exam-1" style="width:100%;"><thead><tr><th style="width:16%;">Filter</th><th style="width:20%;">Use case</th><th style="width:32%;">Valid input → Output</th><th style="width:32%;">Invalid input → Output</th></tr></thead><tbody><tr><td>`F::mod`</td><td>Module names</td><td>`"Accounts"` → unchanged</td><td>`"Acc<script>"` → `"Accscript"` (special chars removed)  
`null` → `null`</td></tr><tr><td>`F::action`</td><td>Action names</td><td>`"Save"`, `"Detail.View"` → unchanged</td><td>`"Act!on@"` → `"Acton"` (special chars removed)  
`null` → `null`</td></tr><tr><td>`F::field`</td><td>Field names</td><td>`"firstname"` → unchanged</td><td>`"field-name!"` → `"fieldname"` (special chars removed)  
`null` → `null`</td></tr><tr><td>`F::recfields`</td><td>Record/field IDs</td><td>`"123@-456"`, `"1x2,3|4"` → unchanged</td><td>`"123abc!@#"` → `"123@"` (invalid chars removed)  
`null` → `null`</td></tr><tr><td>`F::path`</td><td>File paths (**<span style="color:rgb(224,62,45);">dots allowed</span>; use checkFileAccess to ensure secure file access**)</td><td>`"modules/Accounts"` → unchanged</td><td>`"path@with#special$chars"` → `"pathwithspecialchars"` (keeps valid chars)  
`null` → `null`</td></tr><tr><td>`F::subpath`</td><td>Sub paths (**<span style="color:rgb(224,62,45);">no dots</span>; use checkFileAccess to ensure secure file access**)</td><td>`"modules/Accounts"` → unchanged</td><td>`"subpath.with.dots"` → `"subpathwithdots"` (dots removed)  
`null` → `null`</td></tr><tr><td>`F::alpha`</td><td>Alphabetic only</td><td>`"abcABC_"` → unchanged</td><td>`"abc123!@#"` → `"abc"` (numbers/symbols removed)  
`null` → `null`</td></tr><tr><td>`F::alphanum`</td><td>Alphanumeric only</td><td>`"abcABC123_"` → unchanged</td><td>`"abc123!@#"` → `"abc123"` (symbols removed)  
`null` → `null`</td></tr><tr><td>`F::enum([...])`</td><td>Whitelist values</td><td>`"active"` (if in list) → unchanged</td><td>`"invalid"` (not in list) → `null`  
`null` → `null`</td></tr><tr><td>`F::regex('/pattern/')`</td><td>Pattern matching</td><td>`"ABC123"` (matches) → unchanged</td><td>`"invalid"` (no match) → `null`  
`null` → `null`</td></tr><tr><td>`F::datetime('Y-m-d')`</td><td>Date/time parsing</td><td>`"2026-01-08"` → unchanged</td><td>`"invalid-date"` → `null`  
`"2026-99-99"` → `null`  
`null` → `null`</td></tr><tr><td>`F::sep(',', 'intval')`</td><td>Split and transform</td><td>`"1,2,3"` → `[1, 2, 3]`</td><td>Non-string → `null`  
`null` → `null`</td></tr><tr><td>`F::jsfunc`</td><td>Safe JS functions</td><td>`"closePopup"` (if whitelisted) → unchanged</td><td>`"evilFunction"` (not whitelisted) → `null`  
`null` → `null`</td></tr></tbody></table>

#### **Advanced filters**

```php
// Enum filter - restrict to specific values
$status = RH::r('status', F::enum(['active', 'inactive', 'pending']));

// Regex filter - custom pattern validation
$code = RH::r('code', F::regex('/^[A-Z]{3}\d{3}$/'));

// DateTime filter - parse date strings
$date = RH::r('date', F::datetime('Y-m-d'));

// Separator filter - split strings into arrays
$ids = RH::r('ids', F::sep(',', 'intval')); // "1,2,3" -> [1,2,3]

// Array filter - validate nested arrays
$user = RH::r('user', F::array([
    'name' => F::str(max: 100),
    'email' => F::email,
    'age' => F::int,
]));

// Custom callable filter
$customCode = RH::r('code', function($value) {
    if (!is_string($value)) return null;
    $value = strtoupper(trim($value));
    return preg_match('/^[A-Z0-9]{6}$/', $value) ? $value : null;
});
```

---

#### **Examples**

##### **Simple form data**

```php
$name = RH::p('name', F::str(max: 100));
$email = RH::p('email', F::email);
$age = RH::p('age', F::int);

if ($name === null || $email === null) {
    throw new InvalidArgumentException('Name and email are required');
}
```

##### **List page with pagination**

```php
$page = RH::g('page', F::int) ?? 1;
$limit = RH::g('limit', F::int) ?? 20;
$sort = RH::g('sort', F::enum(['name', 'date', 'id'])) ?? 'name';
$direction = RH::g('dir', F::enum(['asc', 'desc'])) ?? 'asc';
$query = RH::g('q', F::str(max: 255));
```

##### **Complex form with nested arrays**

```php
$userData = RH::p('user', F::array([
    'personal' => F::array([
        'firstname' => F::str(max: 50),
        'lastname' => F::str(max: 50),
        'email' => F::email,
        'phone' => F::str(max: 20),
    ]),
    'preferences' => F::array([
        'language' => F::enum(['en', 'it', 'es']),
        'timezone' => F::str(max: 50),
    ]),
    'addresses' => F::array([
        'shipping' => F::array([
            'street' => F::str(max: 255),
            'city' => F::str(max: 100),
            'country' => F::str(max: 2),
        ]),
    ]),
]));
```

##### **File upload**

```php
$uploadedFile = RH::f('document');

if ($uploadedFile && $uploadedFile->getError() === UPLOAD_ERR_OK) {
    $filename = $uploadedFile->getClientFilename();
    $mimeType = $uploadedFile->getClientMediaType();
    $size = $uploadedFile->getSize();
    
    // ...
} else {
    // Handle upload error
    $error = $uploadedFile ? $uploadedFile->getError() : 'No file uploaded';
}
```

---

#### **Advanced Usage**

##### **Temporary request context**

<p class="callout danger">**Push/pop operations should be used only for testing or very specific scenarios.**</p>

```php
// Save current state and temporarily override
RH::push_r(['module' => 'TestModule', 'action' => 'TestAction']);

// Do something with temporary data
$module = RH::r_mod(); // Returns 'TestModule'

// Restore original state
RH::pop_r();
```

---

#### **Configuration**

The RequestHandler uses a configuration file to control request sanitization behavior and define safe parameters. Configuration is defined in `config/request.config.php` and can be extended via `config/request.config.override.php`.

##### `<strong>preserve_original_request</strong>`

```php
'preserve_original_request' => false,  // default
```

Controls whether all original request parameters are preserved in the `$_REQUEST` superglobal:

- **`false` (default, recommended):** Only keys listed in `safe_keys` are preserved in `$_REQUEST`. This provides maximum security by removing any unexpected parameters that could be malicious.
- **`true`:** All original parameters remain in `$_REQUEST`. Use only if you need backward compatibility with legacy code that accesses parameters not in the whitelist.

<p class="callout warning">**Security note:** When `preserve_original_request` is `false`, the `$_REQUEST` array is cleaned to contain only whitelisted keys. This prevents injection of unexpected parameters but does NOT bypass RH's access control - you should still use RH methods for all access.</p>

##### `<strong>safe_keys</strong>`

```php
'safe_keys' => [
    'module' => F::mod,
    'action' => F::action,
    'record' => F::int,
    'id' => F::int,
    'page' => F::int,
    'limit' => F::int,
    // ... add commonly used keys here
],
```

Defines a whitelist of parameter keys that are:

- **Preserved during request cleanup:** When `preserve_original_request` is `false`, only these keys remain in `$_REQUEST`
- **Pre-sanitized automatically:** Each key is associated with a filter (e.g., `F::mod`, `F::int`) that is applied during initialization

##### `<strong>safe_js_functions</strong>`

```php
'safe_js_functions' => [
    'closePopup',
    'LPOP.create',
    'parent.ActionTaskScript.addStaticRelatedRecord',
    // ... other trusted JavaScript function names
],
```

Lists JavaScript function names that are allowed to be passed as request parameters (e.g., for callbacks). This prevents XSS attacks by restricting which functions can be executed client-side.

- Used by the `F::jsfunc` filter
- Only listed functions are considered safe
- Reject any function not in the whitelist

##### **Creating custom configuration**

To add your own safe keys without modifying the core configuration, create `config/request.config.override.php`:

```php
// config/request.config.override.php
return [
    'safe_keys' => [
        // Your custom keys
        'custom_param' => F::str(max: 100),
        'my_record_id' => F::int,
    ],
    'safe_js_functions' => [
        // Your custom callback functions
        'MyApp.customCallback',
    ],
];
```

The override configuration is automatically merged with the default configuration.

<p class="callout warning">**Migration to version 26.04**  
During the upgrade to version 26.04, the system automatically scans for direct `$_REQUEST` superglobal usage. Any detected parameters are automatically added to `config/request.config.override.php` with the `F::html` filter to maintain backward compatibility and prevent breaking existing customizations.   
  
**All core code has been refactored to use RH methods exclusively** instead of direct superglobal access.</p>

# Routing system

Previously, `index.php` directly included the module file to process the action, based on the `action` and `file` request parameters.

With the new architecture, the resolution logic has been moved to the `<strong>IndexRouter</strong>` class, which handles action path resolution and processing.

#### **Action resolution**

##### **Searched paths (in order)**

When an action is requested, `IndexRouter` searches for the file in the following paths:

1. `modules/{MODULE}/{ACTION}.php`
2. `modules/{MODULE}/{ACTION}/{ACTION}.php`
3. `modules/{MODULE}/controllers/{ACTION}.php` <span style="color:rgb(45,194,107);">**(NEW)**</span>
4. `modules/VteCore/{ACTION}.php`
5. `modules/VteCore/controllers/{ACTION}.php` <span style="color:rgb(45,194,107);">**(NEW)**</span>

##### **Removal of `{MODULE}Ajax.php`**

It is no longer necessary to create a `{MODULE}Ajax.php` file in the module folder. AJAX requests can be handled directly by BaseAction classes, which automatically detect the request type and desired output format.

##### **Execution process**

After resolving and including the action file:

1. `IndexRouter` checks if the file contains a class that extends `BaseAction`
2. If found, it calls the `fromRequest()` method to create an instance of the class
3. During `fromRequest()`, class properties are automatically populated from request parameters
4. `processFromIndex()` is called, which performs validation and processing
5. The result is formatted according to the requested content-type and sent to the client

If the file included in `index.php` does not contain a `BaseAction` class, then the included code is executed and entirely managed by the file (previous behavior).

#### **Creating a new BaseAction**

##### **Basic structure**

To create a new action, create a file in:

- `modules/{MODULE}/controllers/{ACTION}.php`
- or `modules/VteCore/controllers/{ACTION}.php` (for global actions)

The class must:

- Extend `BaseAction`
- Implement the `process()` method
- Declare properties with appropriate attributes

```php
<?php

class MyAction extends BaseAction {
    
    protected function process() {
        // Your logic here
        return ['success' => true, 'data' => []];
    }
}

```

#### **Declaring properties**

Class properties can be automatically populated from the request using PHP 8 attributes:

<table id="bkmrk-attribute-source-des"><thead><tr><th>Attribute</th><th>Source</th><th>Description</th></tr></thead><tbody><tr><td>`RequestParam`</td><td>$\_REQUEST</td><td>Parameter from request (GET or POST)</td></tr><tr><td>`RequestRawParam`</td><td>$\_REQUEST</td><td>Raw parameter from request</td></tr><tr><td>`GetParam`</td><td>$\_GET</td><td>Parameter from GET</td></tr><tr><td>`GetRawParam`</td><td>$\_GET</td><td>Raw parameter from GET</td></tr><tr><td>`PostParam`</td><td>$\_POST</td><td>Parameter from POST</td></tr><tr><td>`PostRawParam`</td><td>$\_POST</td><td>Raw parameter from POST</td></tr><tr><td>`CookieParam`</td><td>$\_COOKIE</td><td>Parameter from cookie</td></tr></tbody></table>

#### **Attribute parameters**

- **name**: Parameter name in the request (default: property name)
- **filter**: Filter to apply (e.g. `F::int`, `F::str`, `F::bool`). For more details, see [Basic filters](https://usermanual.vtenext.com/books/developers/page/requesthandler#bkmrk-basic-filters).
- **required**: Whether the parameter is required (default: based on nullable type)
- **default**: Default value if the parameter is not present
- **validation**: Regex or callable to validate the value
- **description**: Parameter description (for documentation)

```php
<?php
class MyAction extends BaseAction {
    
    // Required parameter (not nullable)
    #[RequestParam()]
    protected int $record;
    
    // Optional parameter (nullable)
    #[RequestParam()]
    protected ?string $searchText;
    
    // Parameter with different name
    #[RequestParam(name: 'return_module', filter: F::mod)]
    protected ?string $returnModule;
    
    // Parameter with regex validation
    #[RequestParam(validation: '/^(true|false)$/')]
    protected ?string $isDuplicate;
    
    // Parameter with callable validation
    #[RequestParam(validation: 'is_numeric')]
    protected ?string $amount;
    
    // Parameter with default value
    #[RequestParam(default: 10)]
    protected int $limit;
    
    // Parameter from POST
    #[PostParam()]
    protected ?string $comment;
    
    // Parameter from GET
    #[GetParam()]
    protected ?int $page;
    
    protected function process() {
        // Properties are already populated here
        return [
            'record' => $this->record,
            'search' => $this->searchText,
        ];
    }
}

```

#### **Main methods**

##### **process()**

Main method that contains the action logic. Must return data that will be formatted and sent to the client.

```php
protected abstract function process();
```

##### **validate(?string &amp;$error): bool**

Called before `process()` to validate the request. If it returns `false`, execution stops and an error is sent.

```php
protected function validate(?string &$error): bool {
    if ($this->record <= 0) {
        $error = "Invalid record ID";
        return false;
    }
    return true;
}
```

##### **beforeProcess()**

Called before `process()`, after validation.

```php
protected function beforeProcess(): void {
    // Initialization, logging, etc.
}
```

##### **afterProcess(&amp;$result)**

Called after `process()`, allows modifying the result before sending.

```php
protected function afterProcess(&$result): void {
    // Modify result, logging, etc.
    $result['timestamp'] = time();
}
```

#### **Output**

The output format is automatically determined based on:

1. The class's `$outputFormat` property (if set)
2. The `format` or `_format` parameter in the request
3. The `Accept` header of the request
4. Whether the request is AJAX (default: `json`) or not (default: `html`)

##### **Supported formats**

- **json** - JSON response (automatic for AJAX)
- **html** - HTML response (automatic for standard requests)

You can force a specific format by setting the `$outputFormat` property:

```php
class MyAction extends BaseAction {
    
    protected ?string $outputFormat = 'json';
    
    protected function process() {
        return ['data' => 'Always JSON'];
    }
}
```

Or by using the `setOutputFormat()` method:

```php
class MyAction extends BaseAction {
    
    protected function beforeProcess(): void {
        $this->setOutputFormat('html');
    }
    
    protected function process() {
        return '<h1>HTML Output</h1>';
    }
}
```

##### **JSON response format**

On success:

```json
{
    "success": true,
    "result": { /* data returned by process() */ }
}
```

On error:

```json
{
    "success": false,
    "error": {
        "message": "Error message",
        "code": 400
    }
}
```

##### **Requesting JSON from client**

To request a JSON response, the client can:

- Add the parameter `?format=json` or `?_format=json` to the URL
- Set the header `Accept: application/json`
- Make an AJAX request (automatically detected)

#### **Example**

```php
<?php

use RequestParam;
use PostParam;

class SaveData extends BaseAction {
    
    // We don't specify outputFormat, it will be determined automatically
    
    #[RequestParam()]
    protected ?int $record;
    
    #[PostParam(required: true)]
    protected string $name;
    
    #[PostParam(filter: F::int, default: 1)]
    protected int $status;
    
    #[PostParam()]
    protected ?string $description;
    
    protected function validate(?string &$error): bool {
        if (strlen($this->name) < 3) {
            $error = "Name must be at least 3 characters long";
            return false;
        }
        return true;
    }
    
    protected function beforeProcess(): void {
        // Log the operation
        global $log;
        $log->info("Saving record: " . $this->name);
    }
    
    protected function process() {
        global $adb;
        
        // Save logic
        if ($this->record) {
            // Update logic
        } else {
            // Insert logic
        }
        
        // Return data
        // If the request is AJAX, it will be automatically formatted as JSON
        // Otherwise as HTML
        return [
            'record_id' => $id,
            'message' => 'Save completed'
        ];
    }
    
    protected function afterProcess(&$result): void {
        // Add timestamp to result
        $result['timestamp'] = time();
    }
}

```

#### **Differences from the previous version**

<table id="bkmrk-aspect-before-now-ac"><thead><tr><th>Aspect</th><th>Before</th><th>Now</th></tr></thead><tbody><tr><td>Action resolution</td><td>In index.php</td><td>In IndexRouter.php</td></tr><tr><td>Ajax files</td><td>`{MODULE}Ajax.php` required</td><td>No longer necessary</td></tr><tr><td>Controllers paths</td><td>Did not exist</td><td>`modules/{MODULE}/controllers/`  
`modules/VteCore/controllers/`</td></tr><tr><td>Parameter population</td><td>Manual with $\_REQUEST</td><td>Automatic with PHP attributes</td></tr><tr><td>Validation</td><td>Manual</td><td>validate() method + attribute validation</td></tr><tr><td>Output format</td><td>Manually handled</td><td>Automatic</td></tr><tr><td>Error handling</td><td>Manual</td><td>Automatic with try/catch in processFromIndex()</td></tr></tbody></table>

#### **Best Practices**

- Create new actions as BaseAction classes in `modules/{MODULE}/controllers/`
- Use attributes to declare parameters explicitly
- Always specify the appropriate filter (`F::int`, `F::str`, etc.)
- Implement the `validate()` method for business logic validations
- Use attribute validation for simple validations (regex, callable)
- Let the system automatically determine the output format (JSON/HTML)
- Force `$outputFormat` only when necessary
- Use `beforeProcess()` for initialization and `afterProcess()` for post-processing

# Database best practices

#### **Best practices (all versions)**

<p class="callout warning">**Avoid methods that do automatic html conversion**, like `query_result` and `fetchByAssoc(...)`</p>

<p class="callout warning">**Never concatenate variables (especially coming from request) into the sql string**</p>

<p class="callout success">**Always use the no\_html** versions (`query_result_no_html` or `fetchByAssoc(..., -1, false)`) or the new shortcut methods (see below)</p>

<p class="callout success">**Always use prepared statement** (`pquery`), to avoid SQL injections</p>

<p class="callout success">In `SELECT` statements, **select only the necessary columns**, avoid `*` if you don't need all the columns</p>

<p class="callout success">In `INSERT` statements, **always specify the column names** (to avoid breaking the query in case of new columns added later)</p>

#### **Shortcut methods (version ≥ 26.01)**

New utility methods have been added to easy query execution and retrieval of rows. None of these functions will do any html conversion of the result.

- `rows()`: Iterate over rows:  
      
    ```php
    $res = $adb->pquery("SELECT * FROM table WHERE column = ?", [$param]);
    foreach ($res->rows() as $row) {
      // ... use $row
    }
    ```
    
    This is equivalent of:  
    ```php
    $res = $adb->pquery("SELECT * FROM table WHERE column = ?", [$param]);
    while ($row = $adb->fetchByAssoc($res, -1, false)) {
      // ... use $row
    }
    ```
- `row($index = 0)`: Get a single row:  
      
    ```php
    $res = $adb->pquery("SELECT * FROM table WHERE column = ?", [$param]);
    $row1 = $res->row(1); // get the second row (index starts at 0)
    ```
- `allRows()`: Read all rows (note: may use a lot of memory if there are many rows):  
      
    ```php
    $res = $adb->pquery("SELECT * FROM table WHERE column = ?", [$param]);
    $allrows = $res->allRows();
    ```
- `col($column)`: Return a specific column (by index or by name) from the result set (note: may use a lot of memory if there are many rows):
    
    ```php
    $res = $adb->pquery("SELECT * FROM table WHERE column = ?", [$param]);
    
    $col = $res->col();     // first column
    $col = $res->col(2);    // 3rd column (0-based index)
    $col = $res->col('id'); // "id" column
    ```
- `allCols()`: Return all the columns from the result set (note: may use a lot of memory if there are many rows). The return value is an array, where each key is the name of the column and the value, an array of the values for that column:
    
    ```php
    $res = $adb->pquery("SELECT id, name FROM table WHERE column = ?", [$param]);
    $all = $res->allCols();
    
    /* 
    $all looks like:
    Array (
        [id] => Array
            (
                [0] => 1
                [1] => 6
            )
        [name] => Array
            (
                [0] => first value
                [1] => something else
            )
    )
    */
    ```
- `queryGetAll($sql, $params = [], $offset = 0, $limit = -1)`: Combines `pquery` and `allRows`:
    
    ```php
    // get all rows from query (no parameters)
    $rows = $adb->queryGetAll("SELECT column1, column2 FROM table");
    
    // get all rows from query (single parameter)
    $rows = $adb->queryGetAll("SELECT * FROM table WHERE column = ?", $param);
    
    // with limit, select first 5 rows
    $rows = $adb->queryGetAll("SELECT * FROM table WHERE column = ? AND id < ?", [$param, 6], 0, 5);
    ```
- `queryGetFirst($sql, $params = [])`: Get the first row returned by the query:  
      
    ```php
    // no need to specify LIMIT
    $row = $adb->queryGetFirst("SELECT column1, column2 FROM table");
    ```
- `queryGetFirstValue($sql, $params = [], $column = 0)`: Get a single value from the first row returned:  
      
    ```php
    // first column, if nothing specified
    $column1 = $adb->queryGetFirstValue("SELECT column1, column2 FROM table");
    
    // by name
    $column2 = $adb->queryGetFirstValue("SELECT column1, column2 FROM table", [], 'column2');
    ```
- `queryHasRows($sql, $params = [])`: Return true if the query returns at least one row:  
      
    ```php
    $hasRows = $adb->queryHasRows("SELECT column1 FROM table WHERE id > ?", [10]);
    ```
- `queryGetAllColumns($sql, $params = [], $offset = 0, $limit = -1)`: Combines `pquery` and `allCols`:
    
    ```php
    // get all columns from query (no parameters)
    $cols = $adb->queryGetAllColumns("SELECT column1, column2 FROM table");
    
    // get all columns from query (single parameter)
    $cols = $adb->queryGetAllColumns("SELECT * FROM table WHERE column = ?", $param);
    
    // with limit, select first 5 rows and return them as columns
    $cols = $adb->queryGetAllColumns("SELECT * FROM table WHERE column = ? AND id < ?", [$param, 6], 0, 5);
    ```
- `queryGetFirstColumn($sql, $params = [])`: Get the first column returned by the query:  
      
    ```php
    // no need to specify LIMIT
    $col1 = $adb->queryGetFirstColumn("SELECT column1, column2 FROM table");
    ```

<p class="callout info">In version 26.01, there is still a main source of queries transforming data to html: **reading records**. So all queries reading records to be displayed in ListView, DetailView, EditView, Reports... are still converting to html.   
This behaviour was kept to avoid too many breaks with existing custom code (uitypes, views, presave, ...)</p>

#### **Aliased methods (vtenext ≥ 26.01)**

Some methods in PearDatabase have now new aliases, to better remember that a html conversion can take place:

- `query_result_html` -&gt; alias of `query_result`
- `fetch_array_html` -&gt; alias of `fetch_array`
- `fetchByAssocHtml` -&gt; alias of `fetchByAssoc`, with 3rd parameter set to true
- `fetchByAssocNoHtml` -&gt; alias of `fetchByAssoc`, with 3rd parameter set to false

It is recommended not to use the html methods, but if really necessary, try to use the "html" aliases.

#### **New methods (version ≥ 26.04)**

- `pqueryTimeout(string $sql, ?array $params, int $timeout, bool $dieOnError=false)`: Executes a SELECT statement with a timeout in ms (only for MySql connections). Returns the string `TIMEOUT` if a timeout occured.  
    ```php
    $res = $adb->pqueryTimeout("SELECT * FROM table ORDER BY date_field", null, 3000);
    if ($res === 'TIMEOUT') {
      // took longer than 3s
      // alternative code to execute the query in background
      return;
    else {
      // use $res
    }
    ```

# Escaping

<span>How to properly escape html/js code in Smarty templates and PHP files</span>

# Introduction

Starting with vtenext version 26.01, all variables outputted by Smarty templates (for example `{$VARIABLE}` ) by default have all applicable characters are converted to html `&...;` notation. For instance, the string `"nice & 'smooth' à > è"` is converted to `"nice &amp; &039;smooth&039; &agrave; &gt; &egrave;"`

The conversion is done with the `htmlentities` function, without double encoding existing entities.

The reason of this change is to reduce as much as possible exposure to XSS attacks, since it was extremely difficult to track every possible variable in templates and ensure it was properly escaped.

There are of course some exceptions on the escaping and some edge cases that should be understood. The next chapter presents the rules to follow to write simple and secure code.

# Escaping rules

##### 1. Avoid html code in php

Avoid generating html code directly in php, or outputting it directly. If possible use Smarty templates. Exceptions can be made for very short snippets, see paragraph 3 for how to handle them.

<table border="1" id="bkmrk-" style="border-collapse: collapse; width: 108.929%; height: 419px;"><colgroup><col style="width: 44.4577%;"></col><col style="width: 55.5423%;"></col></colgroup><thead><tr><td class="align-center" colspan="2">**Examples**  
</td></tr></thead><tbody><tr style="height: 29.7969px;"><td style="height: 29.7969px;"><p class="callout success">**Good**</p>

```php
$name = "John & friends";
$smarty->assign("NAME", $name);
```

And in smarty:

```smarty
{* & will be converted to &amp; *}
<div>{$NAME}</div>
```

Will output:

```html
<div>John &amp; friends</div>
```

</td><td style="height: 29.7969px;"><p class="callout danger">**Bad**: html generated in php</p>

```php
$name = "John & friends";
// the "div" will be escaped
$string = "<div>{$name}</div>";
$smarty->assign("TEXT", $string);
```

 And in smarty:

```smarty
{$TEXT}
```

Will output:

```html
&lt;div&gt;John &amp; friends&lt;/div&gt;
```

<p class="callout danger">**Bad**: output via echo</p>

```php
$name = "John & friends";
$string = "<div>{$name}</div>";

// by using echo on the raw string,
// we introduce a XSS if $name comes from user input

echo $string;
```

</td></tr></tbody></table>

##### 2. Reading from database

Avoid using `query_result` and `fetchByAssoc` without the `-1, false` 2nd and 3rd parameters.

This is because the those functions already do html conversion, which is the wrong place to do it, since the result may have to be processed more before being displayed by the browser (or not displayed at all in a browser if the result is for a REST API).

<table border="1" id="bkmrk-examples-good-%C2%A0-%C2%A0-%24r" style="border-collapse: collapse; width: 108.929%; height: 419px;"><colgroup><col style="width: 50.0596%;"></col><col style="width: 50.0596%;"></col></colgroup><thead><tr><td class="align-center" colspan="2">**Examples**  
</td></tr></thead><tbody><tr style="height: 29.7969px;"><td style="height: 29.7969px;"><p class="callout success">**Good**</p>

```
$res = $adb->query("SELECT helpinfo FROM vte_field");

$first = $adb->query_result_no_html($res, 0, 'helpinfo');
```

</td><td style="height: 29.7969px;"><p class="callout danger">**Bad**: helpinfo is converted!</p>

```
$res = $adb->query("SELECT helpinfo FROM vte_field");

$first = $adb->query_result($res, 0, 'helpinfo');
```

</td></tr><tr style="height: 27.7969px;"><td style="height: 27.7969px;"><p class="callout success">**Good**</p>

```
$res = $adb->query("SELECT helpinfo FROM vte_field");

while ($row = $adb->fetchByAssoc($res, -1, false)) {
  // ...
}
```

</td><td style="height: 27.7969px;"><p class="callout danger">**Bad**: helpinfo is converted!</p>

```
$res = $adb->query("SELECT helpinfo FROM vte_field");
while ($row = $adb->fetchByAssoc($res)) {
  // ...
}
```

</td></tr></tbody></table>

##### 3. Generating html in php

Sometimes it's really inevitable to generate html code in php (legacy code, or existing developments...), so in this case, to avoid a double conversion when Smarty processes the template, we should use the `\Vtenext\Types\HtmlString` class to wrap the html string. This class is a simple wrapper around a string, that doesn't get automatically converted by Smarty. Remember, that in this case, all the html conversion **must be done** in php using the `VStr::toHtml` or `VStr::toHtmlAttr` methods.

Example:

```php
// for brevity:
use \Vtenext\Types\HtmlString;

$name = "John Connor";
$url = "index.php?module=aaa&action=".urlencode($_REQUEST['param']); // danger here: user input!

// building the link with manual conversion
$link = "<a href='".VStr::toHtmlAttr($url)."'>".VStr::toHtml($name)."</a>";

// wrapping it in HtmlString
$link = new HtmlString($link);

$smarty->assign("LINK", $link); // this will NOT be escaped!! Be sure all parameters are properly escaped!!
```

There are also 2 utility methods to build html string safely, `HtmlString::build` and `HtmlString::buildSmarty`. Let's see some examples:

```php
// with build(), every instance of ### is replaced with one of the parameters,
// and escaped for html. The resulting string is safe for raw inclusion in templates
$icon = HtmlString::build('<i class="vteicon md-sm" title="###" style="color: white;">settings</i>', [trans('LBL_AREAS_SETTINGS')]);

# even when reading from $_REQUEST, we don't have XSS here
$errStr = HtmlString::build("<font class='error'>Error: ###</font>", RH::r('error_string'));

// buildSmarty takes a smarty template in string form and does the standard conversion:
$tpl = '
  <input class="crmbutton" onclick="myFunction(\'{$module}\', {$recordid}, \'{$entityName}\')" type="button"
  value="{"LBL_LINK_ACTION"|trans}">';

$params = [
	'module' => $currentModule,
	'recordid' => intval($recordid),
	'entityName' => VStr::toJs($entityName, ''), // js stuff must be explicitely escaped
];
$html =  HtmlString::buildSmarty($tpl, $params);

```

##### 4. Assigning to Smarty

Assign the variables normally, without using decode\_html, htmlspecialchars, htmlentities or other conversion functions.

<table border="1" id="bkmrk-examples-good-%C2%A0-%C2%A0-%24s" style="border-collapse: collapse; width: 108.929%; height: 419px;"><colgroup><col style="width: 50.0596%;"></col><col style="width: 50.0596%;"></col></colgroup><thead><tr><td class="align-center" colspan="2">**Examples**  
</td></tr></thead><tbody><tr style="height: 29.7969px;"><td style="height: 29.7969px;"><p class="callout success">**Good**</p>

```sma
$string = "my nice text with < and >";
$smarty->assign("VAR", $string);

```

And in smarty:

```smarty
{* < and > will be converted *}
<div>{$VAR}</div>
```

</td><td style="height: 29.7969px;"><p class="callout danger">**Bad**: no need to convert 2 times</p>

```sma
$string = "my nice text with < and >";
$smarty->assign("VAR", htmlentities($string));

```

 And in smarty:

```smarty
{* still work the same, 
since double_encoding is false
but not a good idea anyway *}
<div>{$VAR}</div>
```

 </td></tr></tbody></table>

##### 5. Inside Smarty templates (html)

Output variables as they are, no additional escaping needed in html. For variables inside javascript code (ex: in onclick attributes) see the next paragraph.

##### 6. Inside Smarty templates (javascript)

Javacript code, inside Smarty templates, delimited by &lt;script&gt; tags, has a special handling. Inside these blocks, the default html conversion is not done, instead, the following happens:

1. If the variable to replace begins with `[` or `{`, no escaping is done
2. Otherwise, the variable is escaped with `VStr::toJs` (which does a addslashes)

For examples, if in PHP we have:

```php
$arr = [1,2,3];
$obj = ['name' => 'test'];

// no need of js escaping here
$smarty->assign("STR", "test <tag> 'quote' end");

// this is the preferred way to pass complex types, as they are!
$smarty->assign("ARR", $arr);
$smarty->assign("OBJ", $obj);

// for legacy code it's acceptable to have this
$smarty->assign("JSARR", json_encode($arr));
$smarty->assign("JSOBJ", json_encode($obj));

// AVOID to build js structures with string manipulation in PHP!!
```

then in the template we can use:

```html
<script>
  // both double quotes and single quotes can be used, they are both escaped!
  var str1 = '{$STR}'; // will become test <tag> \'quote\' end
  var str2 = "{$STR}";

  // the preferred way is to use json_encode for complex types
  var arr = {$ARR|json_encode};
  var obj = {$OBJ|json_encode};

  // but in case something is already in json form:
  var arr = {$JSARR}; // this WILL NOT be escaped!
  var obj = {$JSOBJ}; // also this one
  
</script>
```

Outside of `<script>` tags, the standard html conversion is done, so in case of javascript code in attributes (ex: onclick handlers), manual escaping is still necessary, for example:

```smarty
{* using escape in "javascript" mode *}
<span onclick="myFunction('{$PARAM|escape:"javascript"}')">Link</span>

{* using out VStr::toJs method *}
<span onclick="myFunction2('{VStr::toJs($PARAM)}')">Link2 </span>

{* for urls inside js, use "url" mode before the js encoding *}
<span onclick="location.href='index.php?mode={$MODPARAM|escape:"url"|escape:"javascript"}';">Link 3</span>
```

##### 7. Skipping the conversion

Sometimes it's necessary to override the default conversion and output the raw variable as is. This can be achieved in two ways:

1. By using `nofilter`:  
      
    ```smarty
    <span>{$RAW_VAR nofilter}</span>
    ```
2. By using the modifier `rawhtml` (will convert the string to HtmlString, thus avoiding the conversion)  
      
    ```smarty
    <span>{$RAW_VAR|rawhtml}</span>
    ```

##### 8. Special cases: labels

When using labels in templates (ex: `"LBL_SOMETHING"|trans` or `$APP.LBL_SOMETHING`), html chars are converted. Some labels, though, contains html code, and in this case they should be used with the `rawhtml` or `nofilter` modifiers.

##### 9. Special cases: {capture}

When using `{capture}` in templates, the content is saved in a variable. If that variable is then outputted, the content is escaped, which is usually unwanted since the content is html. In this case, the `rawhtml` or `nofilter` modifier must be used.

For example, if the capture block is:

```smarty
{capture assign="content"}
<div> Hello {$FRIEND}</div> {* $FRIEND will be converted to html entities *}
{/capture}
```

It should be used with:

```smarty
<h2>{$content|rawhtml}</h2>
```

or

```smarty
<h2>{$content nofilter}</h2>
```

or

```smarty
{include file="NoLoginMsg.tpl" BODY=$content|rawhtml}
```

# Cheat sheet

Quick reference if you just need to know how to escape stuff!

**Do's and Don'ts**

- Use only `query_result_no_html` and `fetchByAssoc(..., -1, false)` or `fetchByAssocNoHtml`, we don't want that pesky `to_html` function to be called
- Do not generate html strings in php, use templates or `HtmlString::build`
- Do not echo html code in PHP, use Smarty
- Do not use `VStr::toJsAttr` method
- Do not try to build js code from PHP, use .js files or `<script>` tags in templates
- Do not use `html_entity_decode`, `htmlentities`, `htmlspecialchars`, `addslashes`, it's probably not needed (unless you are working on legacy code)
- Do not use `to_html`, `from_html`, `decode_html` , these were always a bad idea

**How to's:**

<table border="1" id="bkmrk-how-do-i-handle...-." style="border-collapse: collapse; width: 100%; height: 681.453px;"><colgroup><col style="width: 9.77354%;"></col><col style="width: 18.9511%;"></col><col style="width: 71.2753%;"></col></colgroup><thead><tr style="height: 29.7969px;"><td style="height: 29.7969px;">**In**</td><td style="height: 29.7969px;">**How do I handle... ?**</td><td style="height: 29.7969px;">**... like this:**</td></tr></thead><tbody><tr style="height: 46.5938px;"><td rowspan="4" style="height: 316.312px;">Smarty, html code

</td><td style="height: 46.5938px;">standard variable</td><td style="height: 46.5938px;">`{$VARIABLE}`</td></tr><tr style="height: 46.5938px;"><td style="height: 46.5938px;">variable, but it's a HtmlString</td><td style="height: 46.5938px;">`{$VARIABLE}`</td></tr><tr style="height: 63.3906px;"><td style="height: 63.3906px;">variable, but it's a string and already html</td><td style="height: 63.3906px;">`{$VARIABLE nofilter}`

or

`{$VARIABLE|rawhtml}`

</td></tr><tr style="height: 159.734px;"><td style="height: 159.734px;">`{capture}` blocks</td><td style="height: 159.734px;">```smarty
{capture assign="capname"}
  <div>.... html code {$VARIABLE} </div>
{capture}

{$capname nofilter}
```

</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;"></td><td style="height: 29.7969px;">  
</td><td style="height: 29.7969px;">  
</td></tr><tr style="height: 63.375px;"><td rowspan="3" style="height: 122.969px;">Smarty,

inside `<script>`

  
</td><td style="height: 63.375px;">string variable</td><td style="height: 63.375px;">`var myvar = '{$VARIABLE}';`</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">object or array variable</td><td style="height: 29.7969px;">`var mylist = {$VARIABLE|json_encode};`</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">string inside url</td><td style="height: 29.7969px;">`var url = "index.php?module={$VARIABLE|escape:"url"}";`

</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">  
</td><td style="height: 29.7969px;">  
</td><td style="height: 29.7969px;">  
</td></tr><tr style="height: 63.3906px;"><td rowspan="2" style="height: 63.3906px;">Smarty,

js in attributes

</td><td style="height: 63.3906px;">string variable</td><td style="height: 63.3906px;">```smarty
{* using escape in "javascript" mode *}
<span onclick="myFunction('{$PARAM|escape:"javascript"}')">Link</span>

{* using out VStr::toJs method *}
<span onclick="myFunction2('{VStr::toJs($PARAM)}')">Link2 </span>
```

</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">string in url</td><td style="height: 29.7969px;">```smarty
<span onclick="location.href='index.php?mode={$MODPARAM|escape:"url"|escape:"javascript"};>Link 3</span>
```

</td></tr></tbody></table>

# SDK 2

This manual describes the SDK methods that allow customization of vtenext.

# Include custom php/js/css files

In order to include your custom code (that will be included in every page) you need to register the new file with this call:

```PHP
SDK::setUtil($src);
```

*$src : the php file path and the file name to be included*

To remove the customization:

```PHP
SDK::unsetUtil($src);
```

*$src : the php file path and the file name to be removed (the file will NOT be removed from disk)*

In case of css/js file you can call:

```PHP
Vtiger_Link::addLink($id, $type, 'SDKScript', $file);
```

*$id : module id that register the customization (in this case, SDK, 31)*  
*$type : can be “HEADERCSS” or “HEADERSCRIPT”*  
*$file : the php file path and the file name to be included*

<p class="callout info">**Hooks:**  
include/Webservices/Utils.php  
include/squirrelmail/src/redirect.php  
install/PopulateSeedData.php  
index.php</p>

# Javascript overrides and extensions

Some commonly used Javascript functions can be replaced or extended to change their behavior. To do this, simply create a function that has the same name as the function to be modified with the addition of "\_override" or "\_extension" and the same parameters. The behavior of the two extensions is as follows:

<table border="1" id="bkmrk-funzione_override%28%29-" style="border-collapse: collapse; width: 100%;"><tbody><tr><td style="width: 50%;">FUNCTION\_override()</td><td style="width: 50%;">If present, this function is called instead of the original one. The return value of this function is the return value.</td></tr><tr><td style="width: 50%;">FUNCTION\_extension()</td><td style="width: 50%;">If present, this function is called and if it returns **false** or a value equivalent to false, the original function ends by returning false, while if it returns **true** or an equivalent value, the execution continues in the original function.</td></tr></tbody></table>

The difference is that, in the first case the original function is completely ignored, while in the second, it is possible to decide whether to continue the standard execution or not. This is very convenient in the case of validation functions, usually very long, in which you simply want to add a control, without copying the entire function for small changes.  
The functions that support these extensions are as follows:

<table border="1" id="bkmrk-file-funzioni-includ" style="border-collapse: collapse; width: 100%;"><tbody><tr><td style="width: 50%;">**File**</td><td style="width: 50%;">**Functions**</td></tr><tr><td style="width: 50%;">include/js/general.js</td><td style="width: 50%;">doformValidation  
startCall  
getFormValidate</td></tr><tr><td style="width: 50%;">include/js/Inventory.js</td><td style="width: 50%;"><span style="font-family: DejaVu Sans, Arial, sans-serif;">settotalnoofrows</span>

<span style="font-family: DejaVu Sans, Arial, sans-serif;">deleteRow</span>

<span style="font-family: DejaVu Sans, Arial, sans-serif;">calcTotal</span>

<span style="font-family: DejaVu Sans, Arial, sans-serif;">calcProductTotal</span>

<span style="font-family: DejaVu Sans, Arial, sans-serif;">calcGrandTotal</span>

<span style="font-family: DejaVu Sans, Arial, sans-serif;">validateInventory</span>

<span style="font-family: DejaVu Sans, Arial, sans-serif;">FindDuplicate</span>

<span style="font-family: DejaVu Sans, Arial, sans-serif;">validateNewTaxType</span>

<span style="font-family: DejaVu Sans, Arial, sans-serif;">validateTaxes</span>

<span style="font-family: DejaVu Sans, Arial, sans-serif;">setDiscount</span>

<span style="font-family: DejaVu Sans, Arial, sans-serif;">callTaxCalc  
</span>

<span style="font-family: DejaVu Sans, Arial, sans-serif;">calcCurrentTax</span>

<span style="font-family: DejaVu Sans, Arial, sans-serif;">calcGroupTax</span>

<span style="font-family: DejaVu Sans, Arial, sans-serif;">calcSHTax</span>

<span style="font-family: DejaVu Sans, Arial, sans-serif;">validateProductDiscounts</span>

<span style="font-family: DejaVu Sans, Arial, sans-serif;">updatePrices</span>

<span style="font-family: DejaVu Sans, Arial, sans-serif;">updatePriceValues</span>

<span style="font-family: DejaVu Sans, Arial, sans-serif;">resetSHandAdjValues</span>

<span style="font-family: DejaVu Sans, Arial, sans-serif;">moveUpDown</span>

<span style="font-family: DejaVu Sans, Arial, sans-serif;">InventorySelectAll</span>

<span style="font-family: DejaVu Sans, Arial, sans-serif;">fnAddProductOrServiceRowNew</span>

</td></tr></tbody></table>

# Standard PHP replacement

You can replace the standard php files of the modules, such as DetailView.php, EditView.php and so on through the method:

```PHP
SDK::setFile($module, $file, $newfile);
```

*$module : the name of the module*  
*$file : the value of the "action" parameter to be compared*  
*$newfile: the new php source, without extension and without path*

The *$newfile* must be in the same folder of the module and must be specified without an extension and without a path.

<p class="callout warning">If you want to replace the ListView, you must call setFile twice, once with $file = "ListView" and once with $file = "index".</p>

To remove the customization:

```PHP
SDK::unsetFile($module, $file);
```

*$module : the name of the module*  
*$file : the value of the "action"*

<p class="callout info">**<span style="font-family: DejaVu Sans, Arial, sans-serif;">Hooks:  
</span>**<span style="font-family: DejaVu Sans, Arial, sans-serif;">include/Ajax/CommonAjax.php</span><span style="font-family: DejaVu Sans, Arial, sans-serif;">  
index.php</span></p>

# Inclusion of other files

To associate files or folders to a module, so that they are imported automatically, the following methods are available:

```PHP
SDK::setExtraSrc($module, $src);
```

*$module : the name of the module*  
*$src : the path of the file or folder to be associated*

To delete the association (but not the files themselves) use:

```PHP
SDK::unsetExtraSrc($module, $src);
```

*$module : the name of the module*  
*$src : the path of the file or folder associated*

# Custom Uitypes

You can add new types to the existing ones and manage them completely without changing other code. The procedure for creating a new one is:

1. Create a new custom field with the new type (nnn)
2. Create the files:  
    a. nnn.php in modules/SDK/examples  
    b. nnn.js in modules/SDK/examples  
    c. nnn.tpl in Smarty/templates/modules/SDK/examples  
    These files manage the behavior of the new field depending on the context (list, detail, and so on)
3. Register the new type with the class method SDK::setUitype.

In modules/SDK/examples/VTE-SDK-2.php there are various examples of field creation and uitype registration.  
In modules/SDK/doc/VTE-SDK-2.pdf under **Uitypes List** you can find the list of the main standard uitypes.

```PHP
SDK::setUitype($uitype, $src_php, $src_tpl, $src_js, $type='', $params='');
```

*$uitype : the number of the new type; it must be nnn (the name of the files)*  
*$src\_php: the path of nnn.php*  
*$src\_tpl: the path of nnn.tpl (without Smarty/templates/ at the beginning)*  
*$src\_js : the path of nnn.js*  
*$type : the type in webservice format (‘text’, ‘boolean’, and so on)*  
*$params : not used yet*

To remove the uitype:

```
SDK::unsetUitype($uitype);
```

*$uitype : it is the number of the uitype to remove (the files associated with it will not be deleted)*

<p class="callout warning">We recommend using uitype with a value greater than 2000, to avoid conflicts with the future releases of **vtenext**.</p>

The php script has several variables available, the first one is:

*$sdk\_mode : the views that can be customized for the new uitype (“insert”, “detail”, “edit”, “relatedlist”, “list”, “pdfmaker”, “report”, and so on)*

Depending on the type of $sdk\_mode, various variables can be read and modified.

**detail**  
to manage the display of the field in DetailView  
*INPUT*  
*$module : the current module name*  
*$fieldlabel: the label of the field*  
*$fieldname : the name of the field*  
*$col\_fields: (array) the values of the fields*  
*OUTPUT*  
*$label\_fld\[\] : the label translated*  
*$label\_fld\[\] : the value to be displayed*

**edit**  
to manage the display of the field in EditView  
*INPUT*  
*$module\_name : the current module name*  
*$fieldlabel : the label of the field*  
*$value : the value of the field*  
*OUTPUT*  
*$editview\_label\[\] : the label translated*  
*$fieldvalue\[\] : the value to be displayed*

**relatedlist, list, pdfmaker**  
to manage the display of the field in ListView, RelatedList and PDFMaker  
*INPUT*  
*$sdk\_value : the value of the field*  
*OUTPUT*  
*$value : the value to be displayed*

**report**  
to manage the display of the field in Report  
*INPUT*  
*$sdk\_value : the value of the field*  
*OUTPUT*  
*$fieldvalue : the value to be displayed*

<p class="callout warning">If the value to be displayed from the interface **is formatted differently** than the value saved in the database (e.g. number 1.000,25 which must be saved as 1000.25) then the following methods must also be managed to save the value in the correct format and search for it.</p>

**insert**  
to convert the value to the format to be saved in the database  
*INPUT*  
*$this-&gt;column\_fields : (array) the values of the fields*  
*$fieldname : the name of the field*  
*OUTPUT*  
*$fldvalue : the value to save in the database*

**formatvalue**  
to convert the value coming from the $\_REQUEST into the format saved in the database (used in the new management of Conditional Fields)  
*INPUT*  
*$value : the value of the field*  
*OUTPUT*  
*$value : the value converted to database format*

**querygeneratorsearch**  
to convert the value searched in the lists and filters by the user  
*INPUT AND OUTPUT*  
*$fieldname : the name of the field*  
*$operator : the comparison operator*  
*$value : the value searched*

**customviewsearch**  
to manage the conversion of the value in the popup filters for the reference fields  
*INPUT AND OUTPUT*  
*$tablename : the table of the field*  
*$fieldname : the name of the field*  
*$comparator : the comparison operator*  
*$value : the value searched*

**popupbasicsearch**  
to manage the conversion of the value in the popup search for the reference fields  
*INPUT*  
*$table\_name : the table name*  
*$column\_name : the column name*  
*$search\_string : the value searched*  
*OUTPUT*  
*$where : the condition of the query*  
*e.g. $where = "$table\_name.$column\_name = '".convertToDBFunction($search\_string)."'";*

**popupadvancedsearch**  
to manage the conversion of the value in the advanced popup search for the reference fields  
*INPUT AND OUTPUT*  
*$tab\_col : the table and the column of the field*  
*$srch\_cond : the comparison operator*  
*$srch\_val : the value searched*

**reportsearch**  
to convert the value searched in the reports by the user  
*INPUT AND OUTPUT*  
*$table : the table of the field*  
*$column : the column of the field*  
*$fieldname : the field name*  
*$comparator : the comparison operator*  
*$value : the value searched*

<p class="callout info">**Hooks**  
data/CRMEntity.php  
include/ListView/ListViewController.php  
include/utils/crmv\_utils.php  
include/utils/EditViewUtils.php  
include/utils/DetailViewUtils.php  
include/utils/ListViewUtils.php  
include/utils/SearchUtils.php  
include/QueryGenerator/QueryGenerator.php  
modules/PDFMaker/InventoryPDF.php  
modules/Reports/ReportRun.php  
modules/Users/Users.php  
modules/CustomView/Save.php  
modules/CustomView/CustomView.php  
Smarty/templates/DisplayFieldsReadonly.tpl  
Smarty/templates/DisplayFieldsHidden.tpl  
Smarty/templates/DetailViewFields.tpl  
Smarty/templates/EditViewUI.tpl  
Smarty/templates/DetailViewUI.tpl</p>

# Smarty Custom Templates

You can create your own templates, which replace the standard ones (such as EditView.tpl and so on). The new template is used if $\_REQUEST values of the page meet the requirements. The registration of a new template is done through the method:

```PHP
SDK::setSmartyTemplate($params, $src);
```

*$params : associative array with requirements (see below)*  
*$src : path of the new template*

In the $params variable you can specify a special value ("$NOTNULL$") to indicate that this parameter must exist, with any value. The unspecified parameters are ignored. If the rule to be inserted already exists or is not compatible with the existing ones (it could cause ambiguity for some $\_REQUEST), the insertion fails (and an explanatory message is saved in the log).

To remove the customization:

```PHP
SDK::unsetSmartyTemplate($params, $src = NULL);
```

*$params : associative array with requirements*  
*$src : path of the template (if NULL, it includes all file)*

To completely replace all types of views of a module you need at least 7 rules:

<table border="1" id="bkmrk-%24params-note-array%28-" style="border-collapse: collapse; width: 100%;"><tbody><tr><td style="width: 50%;">**$params**</td><td style="width: 50%;">**Notes**</td></tr><tr><td style="width: 50%;">array(‘module’=&gt;’Leads’, ‘action’=&gt;’ListView’)</td><td style="width: 50%;">ListView</td></tr><tr><td style="width: 50%;">array(‘module’=&gt;’Leads’, ‘action’=&gt;’index’)</td><td style="width: 50%;">ListView</td></tr><tr><td style="width: 50%;">array(‘module’=&gt;’Leads’, ‘action’=&gt;’DetailView’ , ‘record’=&gt;’$NOTNULL$’)</td><td style="width: 50%;">DetailView</td></tr><tr><td style="width: 50%;">array(‘module’=&gt;’Leads’, ‘action’=&gt;’EditView’ , ‘record’=&gt;’$NOTNULL$’)</td><td style="width: 50%;">EditView (with record in $\_REQUEST)</td></tr><tr><td style="width: 50%;">array(‘module’=&gt;’Leads’, ‘action’=&gt;’EditView’)</td><td style="width: 50%;">New record</td></tr><tr><td style="width: 50%;">array(‘module’=&gt;’Leads’, ‘action’=&gt;’EditView’, ‘record’=&gt;’$NOTNULL$’, "isDuplicate"=&gt;"true")</td><td style="width: 50%;">Duplicate record</td></tr><tr><td style="width: 50%;">array(‘module’=&gt;’Leads’, ‘action’=&gt;’LeadsAjax’, ‘record’=&gt;’$NOTNULL$’, 'ajxaction'=&gt;'LOADRELATEDLIST', 'header'=&gt;'Products')</td><td style="width: 50%;">The related list of leads showing the products</td></tr></tbody></table>

<p class="callout warning">If **multiple rules match**, the most specific will be used. For example, if there are 2 rules, one with "$NOTNULL$" and one with the value "Leads" and the request is "Leads", the second rule will be used.</p>

<p class="callout info">**Hooks**  
Smarty\_setup.php</p>

# Popup

Two actions are available for managing popup. You can insert a php script before the query is made to load the data. In addition, it is possible to insert another php script before the data is shown, so you can edit this data or the result when the popup is closed. In the first case, the are two methods available:

```PHP
SDK::setPopupQuery($type, $module, $param, $src, $hidden_rel_fields = '');
```

*$type : “field” or “related” to indicate a standard field or popup opened from a related list*  
*$module: the module in which the popup opens*  
*$param : the name of the field that opens the popup (must be uitype 10) in the case type = "field", otherwise the name of the connected module*  
*$src : the path of php file*  
*$hidden\_rel\_fields : associative array of fields to pass in the popup request like array($urlvalue =&gt; $jscode)*

To remove the customization:

```PHP
SDK::unsetPopupQuery($type, $module, $param, $src);
```

*Same parameters as before*

The following variables are available within the php script:  
*$query : the query that takes the values to show*  
*$sdk\_show\_all\_button : if true it shows the button to cancel the SDK restrictions and show all records*

In the second case there are the following methods:

```PHP
SDK::setPopupReturnFunction($module, $fieldname, $src);
```

*$module : the module containing the field that opens the popup*  
*$fieldname : the name of the field that opens the popup (only uitype 10)*  
*$src : the php file*

To remove the customization:

```PHP
SDK::unsetPopupReturnFunction($module, $fieldname = NULL, $src = NULL);
```

For now the only fields supported are those with uitype 10.

<p class="callout info">**Hooks**  
Popup.php  
include/utils/ListViewUtils.php  
include/ListView/SimpleListView.php</p>

**Esempio**

```PHP
<?php
SDK::setPopupQuery('field','Contacts','account_name','modules/SDK/examples/PopupQuery1.php');
SDK::setPopupQuery('related', 'Contacts', 'Products', 'modules/SDK/examples/contacts/PopupRelQuery.php');

SDK::setPopupReturnFunction('Contacts','vendor_id','modules/SDK/examples/ReturnVendorToContact.php');

// you can set a PopupQquery and a PopupReturnFunction to a field in a table field (VTENEXT 24.08)
SDK::setPopupQuery('field', 'Accounts', 'ml1_f4', 'modules/SDK/examples/Contacts/AccountQuery.php');
// here we have a table field (vcf_3) with an account field (vcf_4) and a contact field (vcf_5), I can view only accounts of rating Market Failed, Project Cancelled and Shutdown and only contacts of these accounts. 
$rel_fields = array('processmaker'=>'jQuery("#processmaker").val()','running_process'=>'jQuery("#running_process").val()');
SDK::setPopupQuery('field', 'Processes', 'vcf_3_vcf_4', 'modules/SDK/examples/Contacts/AccountQuery.php', $rel_fields);
$rel_fields['accountid'] = 'jQuery("#vcf_3_vcf_4_"+VTE.EditView.getTableFieldCurrentRow(this)).val()';
SDK::setPopupQuery('field', 'Processes', 'vcf_3_vcf_5', 'modules/SDK/examples/Contacts/ContactsQuery.php', $rel_fields);

// you can set a PopupReturnFunction to the product or other custom fields in the inventory product block (VTENEXT 26.01)
// view examples in modules/SDK/examples/VTE-SDK-2.php
?>
```

# Presave

You can enter your own script when you press the "Save" button in EditView mode. To register a script use the method:

```PHP
SDK::setPreSave($module, $src);
```

*$module : the name of the module*  
*$src : the path of php file*

To remove the customization:

```PHP
SDK::unsetPreSave($module, $src = NULL);
```

*$module : the name of the module*  
*$src : the path of php file (if NULL, includes all scripts registered for that module)*

The following variables are available within the script:

*$type : the form name (“MassEditSave”, “DetailView”, “EditView”, “createTODO”, “QcEditView”, “ConvertLead”, “createQuickTODO”, “Kanban”)*  
*$values : (array) new values*  
  
For MassEditSave, you can know record that are going to be managed calling getListViewCheck($currentModule);

The following variables can be set:

*$status : (bool) whether or not the submit should be saved*  
*$message: (string) if not empty a popup with the message is shown*  
*$confirm: (bool) if true, a Javascript popup is shown asking for confirmation to continue, showing $message. In this case, $status must not be set.*  
*$focus : (string) in case of error, the element that takes the focus (only if $status = false)*  
*$changes: (array) values to assign to the fields (only if $status = false)*

The $focus and $changes variables are only available when $status is false and $type is one of 'EditView', 'createTodo', 'QcEditView', 'ConvertLead'.

<p class="callout info">**Hooks**  
include/js/general.js  
include/js/KanbanView.js  
modules/Calendar/script.js  
modules/Calendar/wdCalendar/sample.php  
modules/Leads/Leads.js  
modules/Users/Forms.php  
modules/VteCore/KanbanAjax.php  
Smarty/templates/Header.tpl  
Smarty/templates/ComposeEmail.tpl  
Smarty/templates/Popup.tpl</p>

# Advanced query

You can modify the query executed to load the data in ListView, RelatedList and Popup mode in order to limit or extend the visibility of the data. This does not affect Administrator users, who have access to all data. In addition, the module must be set as Private.

Editing the query is done through a custom php function (see below). Only one function of this type can be used in each module. To register the function use:

```PHP
SDK::setAdvancedQuery($module, $func, $src);
```

*$module : the module in which to apply the function (if a function is already registered for the module, the new one is not inserted)*  
*$func : the name of the php function*  
*$src : the php file that contains the function*

To remove the customization:

```PHP
SDK::unsetAdvancedQuery($module);
```

*$module : the name of the module*

The $func function must be defined as follows:

```PHP
<?php

function myFunction($module) {
	// Your code ... 
}
```

*$module : the module that calls the function*

It returns a string:

*“” : (empty string) the query is unchanged*  
*? : (not empty string) this string is added to the query*

<p class="callout info">**Hooks**  
data/CRMEntity.php</p>

# Page Header

You can customize the user icon, the settings icon or the blue bars at the top of the pages of VTE to incorporate new features. To do this, simply extend the method setCustomVars of class VTEPageHeader as follows:

```PHP
SDK::setClass('VTEPageHeader', 'NewPageHeader', 'modules/SDK/src/NewPageHeader.php');
```

The file NewPageHeader.php will have this content:

```PHP
<?php
require_once('include/utils/PageHeader.php');

class NewPageHeader extends VTEPageHeader {
	
	protected function setCustomVars(&$smarty, $options = array()) {
		$overrides = array(

			// HTML code to be put right after the menu bar
			'post_menu_bar' => null,

			// HTML code right after the second bar
			'post_primary_bar' => null,

			// HTML code after the third bar
			'post_secondary_bar' => null,

			// HTML code that replace the standard user icon
			'user_icon' => null,

			// HTML code that replace the standard settings icon
			'settings_icon' => null,
		);
		// assign these values to a smarty variable
		$smarty->assign("HEADER_OVERRIDE", $overrides);
	}
}
```

# Translations

Translations can be customized for each language and module installed. To modify or insert a new translation use the method:

```PHP
SDK::setLanguageEntry($module, $langid, $label, $newlabel);
```

*$module : the module name that contains the string*  
*$langid : the code of the language (e.g. “en\_us”, “it\_it”)*  
*$label : the label (e.g. LBL\_TASK\_TITLE)*  
*$newlabel : the translation of the label*

If the label already exists for the chosen module and language, it will be replaced. As a module you can specify "APP\_STRINGS" to insert a global translation or "ALERT\_ARR" to make the translation available in JavaScript files. To load a string simultaneously in multiple languages, the method is:

```PHP
SDK::setLanguageEntries($module, $label, $strings);
```

*$module : the name of the module*  
*$label : the label*  
*$strings : associative array with the translations (e.g. array(“it\_it”=&gt;str1, …))*

To remove a translation:

```PHP
SDK::deleteLanguageEntry($module, $langid, $label = NULL);
```

*$module : the name of the module*  
*$langid : the code of the language*  
*$label : the label (if NULL, all the strings that match)*

# Fields Visibility

You can change the visibility of the various fields (value of $readonly) and other variables in the different modes (ListView, EditView, and so on) via SDK. To register a new "view" use the method:

```PHP
SDK::addView($module, $src, $mode, $success);
```

*$module : the name of the module to apply the view*  
*$src : the path of php file*  
*$mode : how the rule is applied (see below)*  
*$success: what to do after applying the rule (see below)*

The views defined for each module are applied in the order in which they are registered. When they register, they are added to the queue of views for that module. The $mode variable only makes sense if you change the *$readonly variable and it admits the following values:*

*“constrain” : forces the value of $readonly to take the new value given in the script*  
*“restrict” : changes the value of $readonly only for a more restrictive value (from 1 to 99 or 100, from 99 to 100, not vice versa)*

The $success variable instead can be:

*“continue” : after applying the view, continue with the next*  
*“stop” : if the view returns $success = true, no other rules are executed*

The following variables are available within the scripts:

*$sdk\_mode : one of “” (create), “edit”, “detail”, “popup\_query”, “list\_related\_query”, “popup”, “related”, “list”, “mass\_edit”*  
*$readonly : the readonly value for the current field (1, 99, 100)*  
*$col\_fields: values of fields, only for $sdk\_mode = “edit”, “detail” e “”*  
*$fieldname or $fieldName: the name of the current field*  
*$current\_user: the current user*

And you can set the $success variable with true or false.  
Depending on the mode (ListView, EditView, and so on) there are different ways to edit the queries and different variables available.

<table border="1" id="bkmrk-modalit%C3%A0-valore-di-" style="border-collapse: collapse; width: 100%; height: 276px;"><tbody><tr style="height: 30px;"><td style="width: 25%; height: 30px;">**Mode**</td><td style="width: 25%; height: 30px;">**Value of $sdk\_mode**</td><td style="width: 25%; height: 30px;">**Available variables**</td><td style="width: 25%; height: 30px;">**Notes**</td></tr><tr style="height: 31px;"><td style="width: 25%; height: 31px;">CreateView</td><td style="width: 25%; height: 31px;">*""*</td><td rowspan="4" style="width: 25%; height: 101px;">*$col\_fields*  
*$current\_user*  
*$fieldname*  
*$readonly*  
*$success*</td><td style="width: 25%; height: 31px;">1</td></tr><tr style="height: 30px;"><td style="width: 25%; height: 30px;">EditView</td><td style="width: 25%; height: 30px;">*"edit"*</td><td style="width: 25%; height: 30px;"> </td></tr><tr style="height: 10px;"><td style="width: 25%; height: 10px;">DetailView</td><td style="width: 25%; height: 10px;">*"detail"*</td><td style="width: 25%; height: 10px;"> </td></tr><tr style="height: 30px;"><td style="width: 25%; height: 30px;">MassEdit</td><td style="width: 25%; height: 30px;">*"mass\_edit"*</td><td style="width: 25%; height: 30px;"> </td></tr><tr style="height: 25px;"><td style="width: 25%; height: 25px;">PopupQuery</td><td style="width: 25%; height: 25px;">*"popup\_query"*</td><td style="width: 25%; height: 25px;">*$sdk\_columns*  
*$success*</td><td style="width: 25%; height: 25px;">2</td></tr><tr style="height: 30px;"><td style="width: 25%; height: 30px;">List/RelatedQuery</td><td style="width: 25%; height: 30px;">*"list\_related\_query"*</td><td style="width: 25%; height: 30px;">*$sdk\_columns*  
*$success*</td><td style="width: 25%; height: 30px;"> </td></tr><tr style="height: 30px;"><td style="width: 25%; height: 30px;">Popup</td><td style="width: 25%; height: 30px;">*"popup"*</td><td rowspan="3" style="width: 25%; height: 90px;">*$current\_user*  
*$fieldname*  
*$sdk\_columnnames*  
*$sdk\_columnvalues*  
*$readonly*  
*$success*</td><td style="width: 25%; height: 30px;">3</td></tr><tr style="height: 30px;"><td style="width: 25%; height: 30px;">Related</td><td style="width: 25%; height: 30px;">*"related"*</td><td style="width: 25%; height: 30px;">4</td></tr><tr style="height: 30px;"><td style="width: 25%; height: 30px;">List</td><td style="width: 25%; height: 30px;">*"list"*</td><td style="width: 25%; height: 30px;"> </td></tr></tbody></table>

Notes:

1. In these modes the values of the fields are in $col\_fields\[nameOfTheField\]
2. These modes are used to modify the query so that additional fields can be picked up. In $sdk\_columns variable there are the database columns to add to the query. Include then the php file “modules/SDK/AddColumnsToQueryView.php”
3. To get values from other fields, specified in the PopupQuery and ListRelatedQuery modes, write them in the $sdk\_columnnames variable, include the php file “modules/SDK/GetFieldsFromQueryView.php” and then take them from $sdk\_columnvalues
4. In the case of the related "Activity history", only the variables $recordId and $readonly are available and apply to the entire row, not to the single field.

To remove the view:

```PHP
SDK::deleteView($module, $src);
```

*$module : the name of the module*  
*$src : the path of php file*

<p class="callout info">**Hooks**  
include/ListView/ListViewController.php  
include/utils/DetailViewUtils.php  
include/utils/EditViewUtils.php  
include/utils/ListViewUtils.php  
include/QueryGenerator/QueryGenerator.php  
Popup.php</p>

# Home Blocks

New blocks can be added to the home of VTE via SDK. The blocks cannot be deleted from the interface. The method for creating a new block is:

```PHP
SDK::setHomeIframe($size, $url, $title, $userid = null, $useframe = true);
```

*$size : the horizontal size of the block (from 1 to 4)*  
*$url : the address to be shown within the block. It can also have a protocol at the beginning (e.g. http://www.mysite.com/file)*  
*$title : the label of the block (it can be translated via API)*  
*$userid : array containing the ids of the users who can see the block. If you leave null, the block is visible to all users*  
*$useframe: if true the content will be inside an &lt;iframe&gt; otherwise the file is included directly*

Users created later will see all previously registered blocks.  
Block cancellation is possible via 2 methods:

```PHP
SDK::unsetHomeIframe($stuffid);
```

*$stuffid : the id of the block*

```PHP
SDK::unsetHomeIframeByUrl($url);
```

*$url : the url of the block*

Blocks are removed for all users.

<p class="callout info">**Hooks**  
modules/Home/HomestuffAjax.php  
modules/Home/HomeWidgetBlockList.php  
modules/Home/HomeBlock.php  
modules/Home/Homestuff.js  
modules/Users/Save.php  
Smarty/templates/Home/MainHomeBlock.tpl</p>

# Custom Buttons

Buttons can be added under the main menu. To insert a new button use the following method:

```PHP
SDK::setMenuButton($type, $title, $onclick, $image='', $module='', $action='', $condition = '');
```

*$type : the type of button, it can be 'fixed' or 'contextual'; in the first case the button appears on the left and is always visible, in the second case the button is inserted on the right and will be visible only in the chosen module and for the chosen action.*  
*$title : the label of the button*  
*$onclick : the javascript code to execute. It is NOT possible to use double quotes! (“)*  
*$image : the button image. It must be specified without path and reside in themes/softed/images folder even in the smallest version (e.g. img.png e img\_min.png)*  
*$module : if type = ‘contestual’, the module in which the button is visible*  
*$action : if type = ‘contestual’, the action (request action) in which the button is visible*  
*$condition : string like FunctionName:PathPhp representing a function (in the PathPhp file) to be called before showing the button. If it returns false, the button is not shown. The function has only one parameter of type reference to an array with the information of the button.*

To remove the button use:

```PHP
SDK::unsetMenuButton($type, $id);
```

*$type : the type of the button*  
*$id : the id of the button*

<p class="callout info">**Hooks**  
Smarty/templates/Buttons\_List.tpl</p>

# Transitions Manager

You can change the selection options for the picklists managed by the transitions manager, as well as add messages to the "State manager" block to the right of the record detail. To register this functionality use the method:

```PHP
SDK::setTransition($module, $fieldname, $file, $function);
```

*$module : the name of the module to handle*  
*$fieldname : the name of the field managed by transition*  
*$file : the path of the php file that contains the function to call*  
*$function : the function to call*

To remove the customization:

```PHP
SDK::unsetTransition($module, $fieldname);
```

The function called by the transitions manager has the following format:

```PHP
<?php

function myFunction($module, $fieldname, $record, $status, $values) {
	// Your code ...
}
```

*$module : the current module*  
*$fieldname : the name of the field managed by transition*  
*$record : the current record*  
*$status : the value of the field managed by transition of the current record*  
*$values : array of admissible values for the status*

It must return null if you do not want to change the transitions manager behavior or an array with the following format:

*array(*  
*'values' =&gt; array(..) // array of admissible values for the status*  
*'message' =&gt; '' // html code to be included under the transitions manager block*  
*);*

<p class="callout info">**Hooks**  
modules/Transitions/Transitions.php  
modules/Transitions/Statusblock.php  
Smarty/templates/modules/Transitions/StatusBlock.tpl</p>

# PdfMaker Custom Functions

Custom functions can be added in the PDFMaker module. To insert one use:

```PHP
SDK::setPDFCustomFunction($label, $name, $params);
```

*$label: the label of the function (it is translated into the PDFMaker module)*  
*$name: the name of the function*  
*$params: array with the names of the function parameters*

<p class="callout warning">Registered functions must be saved in php files in modules/PDFMaker/functions/ to be used by the PDFMaker module</p>

To remove the function:

```PHP
SDK::unsetPDFCustomFunction($name);
```

*$name: the name of the function*

# Custom Folders and Reports

Custom folders can be created using the following API.

```PHP
SDK::setReportFolder($name, $description);
```

*$name : the name of the folder*  
*$description : the description of the folder*

The created folder will be on the reports page. It will be visible to all users and cannot be changed. The name and description can be translated with the translation API.

Folders created via API can be deleted with:

```PHP
SDK::unsetReportFolder($name, $delreports = true);
```

*$name : the name of the folder to be deleted*  
*$delreports : if true it also deletes all reports (created via API) in that folder (files are not deleted)*

Inside the folders you can insert customized reports with the following statement:

```PHP
SDK::setReport($name, $description, $foldername, $reportrun, $class, $jsfunction = '');
```

*$name : the name of the report (it can be translated via API)*  
*$description : the description of the report (it can be translated via API)*  
*$foldername : the name of the folder created via API where to insert the report*  
*$reportrun : the path of the php file that contains the class that generates the report*  
*$class : the name of the class that handles the report*  
*$jsfunction : the name of the javascript function to run when the "Generate Report" button is pressed*

The report created via API can be deleted with:

```PHP
SDK::unsetReport($name);
```

*$name : the name of the report to delete (files are not deleted)*

The class specified in $class must follow the following structure:

```PHP
<?php
require_once('modules/Reports/ReportRun.php');

class ReportRunAccounts extends ReportRun {
	
	var $enableExportPdf = true;
	var $enableExportXls = true;
	var $enablePrint = true;
	var $hideParamsBlock = true;
	
	function __construct($reportid) {
		$this->reports = Reports::getInstance($reportid); // crmv@172034
		$this->reportid = $reportid;
		$this->primarymodule = 'Accounts';
		$this->reporttype = '';
		$this->reportname = 'Account con sito';
		$this->reportlabel = getTranslatedString($this->reportname, 'Reports');
	}
	
	function getSDKBlock() {
		global $mod_strings;
		$sdkblock = '<p><h2>Questo report mostra le aziende con sito</h2></p>';
		// here I can also add custom inputs to filter the report or any html I need
		return $sdkblock;
	}
	
	// overridden, always hide the summary tab
	function hasSummary() {
		return false;
	}
	
	// overridden, always show the total tab
	function hasTotals() {
		return true;
	}
	
	// generate the report
	function GenerateReport($outputformat = "", $filterlist = null, $directOutput=false) {
		global $adb;
		
		// compatibility, please use set them with the proper methods
		if (!empty($outputformat)) {
			$format = "HTML";
			$tab = "MAIN";
		
			if (strpos($outputformat, 'HTML') !== false) $format = "HTML";
			if (strpos($outputformat, 'PRINT') !== false) $format = "PRINT";
			if (strpos($outputformat, 'PDF') !== false) $format = "PDF";
			if (strpos($outputformat, 'XLS') !== false) $format = "XLS";
			if (strpos($outputformat, 'JSON') !== false) $format = "JSON";
			if (strpos($outputformat, 'CV') !== false) $format = "NULL";
			
			if (strpos($outputformat, 'COUNT') !== false) $tab = "COUNT";
			if (strpos($outputformat, 'TOTAL') !== false) $tab = "TOTAL";
			if (strpos($outputformat, 'CV') !== false) $tab = "CV";
			
			$this->setOutputFormat($format, $directOutput);
			$this->setReportTab($tab);
		} else {
			$format = $this->outputFormat;
			$tab = $this->reportTab;
		}
		
		$format = $this->outputFormat;
		$direct = $this->directOutput;
		$tab = $this->reportTab;

		// prepare the output class
		$output = $this->getOutputClass();
		$output->clearAll();
		
		$return_data = array();
	
		
		if ($tab == 'COUNT' && $this->hasSummary()) {

			// no summary for this custom report
			
		} elseif ($tab == 'CV') {
		
			// no customview for this report
		
		} elseif ($tab == 'MAIN') {

			$sSQL = $this->getReportQuery($outputformat, $filterlist);

			$result = $adb->query($sSQL);
			$this->total_count = $adb->num_rows($result);
			
			$error_msg = $adb->database->ErrorMsg();
			if(!$result && $error_msg!=''){
				// Performance Optimization: If direct output is requried
				if($direct) {
					echo getTranslatedString('LBL_REPORT_GENERATION_FAILED', 'Reports') . "<br>" . $error_msg;
					$error_msg = false;
				}
				// END
				return $error_msg;
			}
			
			if($result) {
			
				$this->generateHeader($result, $output);
			
				while ($row = $adb->fetchByAssoc($result)) {
					$colcount = count($row);
					foreach ($row as $column => $value) {
						$cell = array(
							'value' => $value,
							'column' => $column,
							'class' => 'rptData',
						);
						$output->addCell($cell);
					}
					$output->endCurrentRow();
					
				}
				
				$output->countTotal = $this->total_count;
				$output->countFiltered = $this->total_count;
				
				if ($format == 'XLS') {
					$head = $output->getSimpleHeaderArray();
					$data = $output->getSimpleDataArray();
					foreach ($data as $row) {
						$return_data[] = array_combine($head, $row);
					}
				} else {
					$return_data[] = $output->output(!$direct);
					$return_data[] = $this->total_count;
					$return_data[] = $sSQL;
					$return_data[] = $colcount;
				}
				
			}

		} elseif ($tab == "TOTAL" && $this->hasTotals()) {
			
			$output->addHeader(array('column' => 'fieldname', 'label' => getTranslatedString('Totals')));
			$output->addHeader(array('column' => 'sum', 'label' => getTranslatedString('SUM')));
			$output->addHeader(array('column' => 'avg', 'label' => getTranslatedString('AVG')));
			$output->addHeader(array('column' => 'min', 'label' => getTranslatedString('MIN')));
			$output->addHeader(array('column' => 'max', 'label' => getTranslatedString('MAX')));
			
			// fixed totals
			$rows = array(
				array(
					array('column'=> 'fieldname', 'value' => 'Fatturato totale', 'class' => 'rptData'),
					array('column'=> 'sum', 'value' => 2000, 'class' => 'rptTotal'),
					array('column'=> 'avg', 'value' => null, 'class' => 'rptTotal'),	// not used
					array('column'=> 'min', 'value' => 850, 'class' => 'rptTotal'),
					array('column'=> 'max', 'value' => null, 'class' => 'rptTotal'),	// not used
				),
			);
			
			// add them to the output class
			foreach ($rows as $row) {
				foreach ($row as $cell) {
					$output->addCell($cell);
				}
				$output->endCurrentRow();
			}
			
			// format for xls or html
			if ($format == "XLS") {
				
				// change the output array to match the expected format for XLS export
				$return_data = array();
				$data = $output->getSimpleDataArray();
				$fieldName = '';
				foreach ($data as $row) {
					$nrow = array();
					foreach ($row as $key => $value) {
						if ($key == 'fieldname') {
							$fieldName = $value;
							continue;
						}
						$klabel = $fieldName.'_'.strtoupper($key);
						$nrow[$klabel] = $value;
					}
					$return_data[] = $nrow;
				}
			
			} else {
				$return_data = $output->output(!$direct);
			}

		}
		
		return $return_data;
	}
	
	// generate a fixed header for the report
	function generateHeader($result, $output, $options = array()) {
		global $adb, $table_prefix;
		
		$module = 'Accounts';
		$tabid = getTabid($module);
		$count = $adb->num_fields($result);

		for ($x=0; $x<$count; ++$x) {
			$fld = $adb->field_name($result, $x);
			
			// get the field label from the column (if possible)
			$res = $adb->pquery("SELECT fieldlabel FROM {$table_prefix}_field WHERE columnname = ? and tabid = ?", array($fld->name, $tabid));
			if ($res && $adb->num_rows($res) > 0) {
				$fieldlabel = $adb->query_result_no_html($res, 0, 'fieldlabel');
				$headerLabel =  getTranslatedString($fieldlabel, $module);
			} else {
				$headerLabel = $fld->name;
			}
			$hcell = array(
				'column' => $fld->name,
				'label' => $headerLabel,
				'orderable' => false,
				'searchable' => false,
			);
			$output->addHeader($hcell);
		}
		
	}
	
	// generate the report query
	function getReportQuery($outputformat, $filterlist) {
		global $table_prefix;
		$query = 'SELECT accountid, accountname, website, phone FROM '.$table_prefix.'_account WHERE website <> "" ';
		return $query;
	}
}

```

The javascript function specified in $jsfunction must already be declared and return a string to be added to the request.

```JavaScript
function preRunReport(id) {
	var params = "";
	var select = getObj('picklist1’);

	if (select) {
		params += "&picklist1="+select.options[select.selectedIndex].value;
	}

	var selectuser = getObj('picklist2’);
	if (selectuser) {
		params += "&picklist2="+selectuser.options[selectuser.selectedIndex].value;
	}

	return params;
}
```

<p class="callout warning">When updating a VTE to version 16.09 (or later), some features of the customized reports must be verified.</p>

<p class="callout warning">One of the changed parts is the management of **time filters**, which previously could be modified by extending the getPrimaryStdFilterHTML and getSecondaryStdFilterHTML methods. To obtain the same result, you need to extend the getStdFilterFields function which returns an array of available fields, for example:</p>

```PHP
<?php
function getStdFilterFields() {
	// See the method Reports::getStdFilterFields for the standard implementation
	
	// this example just loads standard fields for the Potential module
	$list = $this->reports->getStdFiltersFieldsListForChain(0, array('Potentials'));
	
	return $list;
}
```

When the report is generated the variable $this-&gt;stdfilters contains the time filter to be used. If the report query is completely customized, the generation of the time filter must also be managed manually, for example using a method like this:

```PHP
<?php
function addStdFilters() {
	global $current_user;
	$sql = '';
	
	if (is_array($this->stdfilters)) {
		foreach ($this->stdfilters as $flt) {
			if ($flt['fieldid'] > 0) {
				// get field informations
				$finfo = $this->getFieldInfoById($flt['fieldid']);
				$table = $finfo['tablename'];
				$qgen = QueryGenerator::getInstance($finfo['module'], $current_user);
				$operator = 'BETWEEN';
				if ($flt['value'] == 'custom') {
					$value = array($flt['startdate'], $flt['enddate']);
				} else {
					$cv = CRMEntity::getInstance('CustomView');
					$value = $cv->getDateforStdFilterBytype($flt['value']);
				}
				// adjust for timezone
				$value[0] = $this->fixDateTimeValue(
					$qgen, $finfo['fieldname'], $value[0]
				);
				$value[1] = $this→fixDateTimeValue(
					$qgen, $finfo['fieldname'], $value[1], false
				);
				// add the condition
				$sql .= ' AND '.$table.'.'.$finfo['columnname'].' '.
					$operator.' '.$value[0].' AND '.$value[1];
			}
		}
	}
	
	return $sql;
}
```

<p class="callout info">**Hooks**  
modules/Reports/Listview.php  
modules/SaveAndRun.php  
modules/Reports/Reports.php  
modules/Reports/CreatePDF.php  
modules/Reports/CreateXL.php  
modules/Reports/PrintReport.php  
Smarty/templates/ReportContents.tpl  
Smarty/templates/ReportRunContents.tpl  
Smarty/templates/ReportRun.tpl</p>

# Turbolift Counter

When changing the standard extraction criteria of a related list, for example using another method or redefining it by extending the class that contains it, the counter of the number of the linked records visible in the Turbolift (the right column with the list of modules in record detail) may no longer be valid. In this case, therefore, a method that returns the correct count must be defined through the following API.

```PHP
SDK::setTurboliftCount($relation_id, $method);
```

$relation\_id : relation id (table vte\_relatedlists)  
$method : the method name that returns the correct count

<p class="callout warning">The method must be implemented in the class that contains the relation (e.g. in the Contact relationship connected to a Company we mean the Accounts class or any extensions to it)</p>

To remove the customization:

```PHP
SDK::unsetTurboliftCount($relation_id);
```

<p class="callout info">The defined method can be the related list method (column **name** in vte\_relatedlists) or a custom method that returns an integer.</p>

# SDK Processes

# Process log

The way of viewing the logs has changed in the various **vtenext** releases and is summarized as follows.

- **vtenext 16.09**: Logs can be consulted by accessing the filesystem in the folder logs/ProcessEngine
- **vtenext 18.X**: In Settings → Business Process Manager → ProcessManager the button 'LOG' will be available once the following script has been executed:

```PHP
<?php
require_once('include/utils/VTEProperties.php');
$VP = VTEProperties::getInstance();
$VP->set('settings.process_manager.show_logs_button', 1);
```

<p class="callout info">The file 01.log is initially created. When the file exceeds the 5MB, the file 02.log will be created and so on.</p>

- **vtenext 19.10**: Logs are available in Settings → Other Settings → System Logs

# Import Processes with a script

From version 18.05 (rev. 1696) it is possible to import processes previously exported in the format vtebpmn (diagram + configuration) or bpmn (diagram only) with php scripts using the importFile method of the ProcessMakerUtils class.  
The importFile method takes in the first parameter the path of the file to be installed (.vtebpmn / .bpmn) and in the second one a Boolean value (true/false) depending on whether you want to automatically activate the process or not.  
This method is useful for installing processes at the end of the installation of a new module: just include the process file in the installation zip and execute the code in the case 'postinstall' of the method vtlib\_handler of the module class.

Example

```php
require_once('modules/Settings/ProcessMaker/ProcessMakerUtils.php');
$PMUtils = ProcessMakerUtils::getInstance();
$PMUtils->importFile('PATH_FILE/Process1.vtebpmn',true);
$PMUtils->importFile('PATH_FILE/DiagramProcess2.bpmn',false);
```

# SDK

You can add custom functions to processes, click [here](https://usermanual.vtenext.com/books/old-business-process-manager-manual/chapter/sdk) for more details.

# Portal v2

#### Theme documentation

---

[https://adminlte.io/docs/3.2/](https://adminlte.io/docs/3.2/)

[https://github.com/ColorlibHQ/AdminLTE](https://github.com/ColorlibHQ/AdminLTE)

[https://adminlte.io/themes/v3/](https://adminlte.io/themes/v3/)

#### Webservice REST

---

**Handler**: include/Webservices/CustomerPortal.php

**Headers:**  
• Authorization: Basic base64\_encode("username:accesskey")  
• Portal-Session-Id: the parameter is optional and is returned by the "portal.login". Use in subsequent calls to optimize portal performance.  
• Content-Type: application/json

<table id="bkmrk-nome-handler-metodo-" style="width:100%;"><tbody><tr><th class="align-left" style="width:9.38277%;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Name</span></span>**</th><th class="align-left" style="width:8.08637%;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Parameters</span></span>**</th><th class="align-left" style="width:15.2469%;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Description</span></span>**</th></tr><tr><td style="width:9.38277%;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">portal.info</span></span></td><td style="width:8.08637%;"> </td><td style="width:15.2469%;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to obtain information on the license and logos used by the connected vtenext installation.</span></span></td></tr><tr><td style="width:9.38277%;">portal.login</td><td style="width:8.08637%;"><div><div>**Body**</div><div>. username: string (*)</div><div>. password: string (*)</div><div>. language: string</div></div></td><td style="width:15.2469%;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to log in to the customer portal by providing the email (username) and password of the contact. The service also returns the accesskey and session id values to be used in subsequent calls as headers (Authorization and Portal-Session-Id).</span></span></td></tr><tr><td style="width:9.38277%;">portal.logout</td><td style="width:8.08637%;">**Headers**

. Authorization

</td><td style="width:15.2469%;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to logout the contact from the portal.</span></span></td></tr><tr><td style="width:9.38277%;">portal.send\_mail\_for\_password</td><td style="width:8.08637%;"><div><div>**Body**</div><div>. email: string (*)</div><div>. language: string</div></div></td><td style="width:15.2469%;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to recover the password of a contact.</span></span></td></tr><tr><td style="width:9.38277%;">portal.modules\_list</td><td style="width:8.08637%;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Headers</span></span>**

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">. Authorization</span></span>

<div><div>**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Body</span></span>**</div><div>. language: string (*)</div></div></td><td style="width:15.2469%;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to obtain a list of modules enabled for the contact's profile.</span></span></td></tr><tr><td style="width:9.38277%;">portal.get\_list</td><td style="width:8.08637%;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Headers</span></span>**

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">. Authorization</span></span>

**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Body</span></span>**

. module: string (\*)

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">. language: string</span></span>

. folderid: int  
. search: array

</td><td style="width:15.2469%;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to obtain the list of records of a module filtered by the contact profile.   
  
</span><span style="vertical-align:inherit;">The "folderid" parameter is used by the Documents module to get the documents of a specific folder.  
  
</span></span>The "search" parameter allows the paging of the list on the server-side.

Ex.

\[  
 'length' =&gt; 50, // Number of records to return  
 'start' =&gt; 0, // Paging offset  
 'search' =&gt; '', // Global search  
 'search\_columns' =&gt; \[ // Search by columns  
 \[  
 'index' =&gt; 0, // Index of the field  
 'column' =&gt; '', // Name of the field

 'search' =&gt; '', // Value of the field  
 \]

 \],  
 'ordering' =&gt; \[ // Ordering  
 \[

 'index' =&gt; 0, // Index of the field  
 'column' =&gt; '', // Name of the field

 'dir' =&gt; '', // asc/desc  
 \]

 \],  
\]

Note:  
1\. All searches are performed ONLY on the columns of the list.  
2\. Column search is performed in "LIKE" mode.  
3\. Sorting can only be done on one column.

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">  
</span></span></td></tr><tr><td style="width:9.38277%;">portal.get\_blocks</td><td style="width:8.08637%;">**Headers**

. Authorization

**Body**

. module: string (\*)

. language: string

. mode: string (edit, create, detail, list) (\*)  
. app\_data: array

</td><td style="width:15.2469%;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to obtain the list of the blocks and the related fields of a module filtered by the contact profile.  
  
The "app\_data" parameter represents the record being edited (array with field name and value) and is used by the SDK views to establish the visibility of the fields.  
This parameter will also be used in the future for managing conditional fields.  
</span></span></td></tr><tr><td style="width:9.38277%;">portal.get\_record</td><td style="width:8.08637%;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Headers</span></span>**

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">. Authorization</span></span>

**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Body</span></span>**

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">. module: string (\*)</span></span>

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">. </span><span style="vertical-align:inherit;">id: int (\*)</span></span>

</td><td style="width:15.2469%;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">If the contact has visibility permissions for the indicated record, it allows you to obtain its data.</span></span>

</td></tr><tr><td style="width:9.38277%;">portal.save\_record</td><td style="width:8.08637%;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Headers</span></span>**

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">. Authorization</span></span>

**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Body</span></span>**

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">. module: string (\*)</span></span>

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">. </span><span style="vertical-align:inherit;">id: int (\*)</span></span>

. values: encoded (\*)

</td><td style="width:15.2469%;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">If the contact has write permissions for the indicated record, it allows its saving.</span></span></td></tr><tr><td style="width:9.38277%;">portal.delete\_record</td><td style="width:8.08637%;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Headers</span></span>**

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">. Authorization</span></span>

**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Body</span></span>**

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">. module: string (\*)</span></span>

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">. </span><span style="vertical-align:inherit;">id: int (\*)</span></span>

</td><td style="width:15.2469%;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">If the contact has delete permissions for the indicated record, it allows its deletion.</span></span></td></tr><tr><td style="width:9.38277%;">portal.write\_ticket\_comment</td><td style="width:8.08637%;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Headers</span></span>**

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">. Authorization</span></span>

**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Body</span></span>**

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">. </span><span style="vertical-align:inherit;">id: int (\*)</span></span>

. comment: string (\*)

</td><td style="width:15.2469%;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to write a comment within the indicated ticket.</span></span></td></tr><tr><td style="width:9.38277%;">portal.get\_attachments</td><td style="width:8.08637%;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Headers</span></span>**

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">. Authorization</span></span>

**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Body</span></span>**

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">. </span><span style="vertical-align:inherit;">id: int (\*)</span></span>

</td><td style="width:15.2469%;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to obtain the list of documents of the specified record filtered by the visibility of the contact profile.</span></span></td></tr><tr><td style="width:9.38277%;">portal.download\_attachment</td><td style="width:8.08637%;">**Headers**

. Authorization

**Body**

. relid: int (\*)

. docid: int

</td><td style="width:15.2469%;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">If the contact has permission to view the attachment (relid), it allows its download.</span></span></td></tr><tr><td style="width:9.38277%;">portal.upload\_attachment</td><td style="width:8.08637%;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Headers</span></span>**

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">. Authorization</span></span>

**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Body</span></span>**

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">. relid: int (\*)</span></span>

. title: string (\*)  
**File**

</td><td style="width:15.2469%;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows the uploading of an attachment related to the specified record (relid). </span><span style="vertical-align:inherit;">You can indicate the name (title) of the document that will be generated.</span></span></td></tr><tr><td style="width:9.38277%;">portal.provide\_confidential\_info</td><td style="width:8.08637%;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Headers</span></span>**

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">. Authorization</span></span>

**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Body</span></span>**

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">. </span><span style="vertical-align:inherit;">id: int (\*)</span></span>

. comments: string

. data: string (\*)

. request\_commentid: int (\*)

</td><td style="width:15.2469%;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to respond to a request for confidential information. </span><span style="vertical-align:inherit;">The "id" parameter indicates the ticket id, the "comments" parameter indicates the unencrypted comment, the "data" parameter indicates the confidential response, and the "request\_commentid" parameter indicates the id of the comment to which the confidential response should be provided .</span></span></td></tr><tr><td style="width:9.38277%;">portal.get\_home\_widgets</td><td style="width:8.08637%;">**Headers**

. Authorization

**Body**

. language: string

</td><td style="width:15.2469%;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to obtain the widgets configured in the profile associated with the contact.</span></span></td></tr><tr><td style="width:9.38277%;">portal.save\_authenticate\_cookie</td><td style="width:8.08637%;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Headers</span></span>**

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">. Authorization</span></span>

**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Body</span></span>**

. contactid: int (\*)

</td><td style="width:15.2469%;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Provides an hash to be stored in a cookie to remember the contact's login.</span></span></td></tr><tr><td style="width:9.38277%;">portal.check\_authenticate\_cookie</td><td style="width:8.08637%;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Headers</span></span>**

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">. Authorization</span></span>

**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Body</span></span>**

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">. contactid: int (\*)</span></span>

. hash: string (\*)

</td><td style="width:15.2469%;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to verify the hash used to remember the contact's login.</span></span></td></tr><tr><td style="width:9.38277%;">portal.change\_password</td><td style="width:8.08637%;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Headers</span></span>**

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">. Authorization</span></span>

**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Body</span></span>**

. username: string (\*)

. old\_password: string (\*)

. password: string (\*)

. language: string

</td><td style="width:15.2469%;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to change the contact's password.</span></span></td></tr></tbody></table>

##### <span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Register a new REST webservice</span></span>

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Create a new file and execute it (e.g. plugins/script/script.php).</span></span>

```PHP
<?php
require('../../config.inc.php');
chdir($root_directory);
require_once('include/utils/utils.php');
require_once('vtlib/Vtecrm/Module.php');
$Vtiger_Utils_Log = true;
global $adb, $table_prefix;
VteSession::start();

SDK::setClass('CustomerPortalRestApi', 'CustomerPortalRestApi2', 'modules/SDK/src/CustomerPortalRestApi2.php');

$parameters = ['param1' => 'string', 'param2' => 'encoded', 'param3' => 'encoded'];
$perm = 'read'; // read, write, readwrite
SDK::setRestOperation('portal.foo', 'modules/SDK/src/CustomerPortalRestApi2.php', 'CustomerPortalRestApi2.foo', $parameters, $perm);

```

Create a new file that contains the CustomerPortalRestApi2 class <span style="vertical-align:inherit;">(e.g. modules/SDK/src/CustomerPortalRestApi2.php).</span>

```PHP
<?php

require_once('include/Webservices/CustomerPortal.php');

class CustomerPortalRestApi2 extends CustomerPortalRestApi {

	public function foo($param1, $param2, $param3) {
		$data = [1, 2, 3, 4, 5];
        // ...
		return $data;
	}

}
```

##### <span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Extend an existing REST webservice</span></span>

<span style="vertical-align:inherit;">Create a new file and execute it (e.g. plugins/script/script.php).</span>

```PHP
<?php
require('../../config.inc.php');
chdir($root_directory);
require_once('include/utils/utils.php');
require_once('vtlib/Vtecrm/Module.php');
$Vtiger_Utils_Log = true;
global $adb, $table_prefix;
VteSession::start();

SDK::setClass('CustomerPortalRestApi', 'CustomerPortalRestApi2', 'modules/SDK/src/CustomerPortalRestApi2.php');
```

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Create a new file that contains the CustomerPortalRestApi2 class​ (e.g. modules/SDK/src/CustomerPortalRestApi2.php).</span></span>

```PHP
<?php

require_once('include/Webservices/CustomerPortal.php');

class CustomerPortalRestApi2 extends CustomerPortalRestApi {

	public function get_list($module, $language, $folderid = 0, $search = []) {
		$ret = parent::get_list($module, $language, $folderid, $search);
		// your code here ...
		return $ret;
	}

}
```

#### <span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Structure of main folders/files</span></span>

---

<p class="callout info"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">The portal folder is located at: </span></span>**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">VTE\_ROOT/portal/v2</span></span>**</p>

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">The main folders/files of the new portal are:</span></span>

<table id="bkmrk-nome-cartella-descri" style="border-collapse:collapse;width:100%;height:469px;"><tbody><tr style="height:29px;"><td style="width:50%;height:29px;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Folder/file name</span></span>**</td><td style="width:50%;height:29px;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Description</span></span>**</td></tr><tr style="height:29px;"><td style="width:50%;height:29px;">app</td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Contains the files and the logic of the portal.</span></span></td></tr><tr style="height:46px;"><td style="width:50%;height:46px;">app/controllers</td><td style="width:50%;height:46px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Contains the files that manage the portal's default actions (e.g. Login, Logout, Edit, Detail, etc.).</span></span></td></tr><tr style="height:29px;"><td style="width:50%;height:29px;">app/fields</td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Contains the files that manage the portal module fields.</span></span></td></tr><tr style="height:29px;"><td style="width:50%;height:29px;">app/modules</td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Contains custom logic of some vtenext modules (e.g. Documents, Processes, HelpDesk, etc.).</span></span></td></tr><tr style="height:29px;"><td style="width:50%;height:29px;">app/PortalModule.php</td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Class that manages the actions of the portal modules (e.g. Create, Detail, Edit, etc.). </span><span style="vertical-align:inherit;">The class can be extended to change the default behaviors of a module.</span></span></td></tr><tr style="height:29px;"><td style="width:50%;height:29px;">config</td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Contains the portal configuration files.</span></span></td></tr><tr style="height:46px;"><td style="width:50%;height:46px;">public</td><td style="width:50%;height:46px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Contains the portal's public files (e.g. index.php, css, javascript, images, etc.).</span></span></td></tr><tr style="height:29px;"><td style="width:50%;height:29px;">resources</td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Contains the resources used by the portal such as translation files (lang) and templates (templates).</span></span></td></tr><tr style="height:29px;"><td style="width:50%;height:29px;">sdk</td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">The folder is used to insert new customizations for the customer.</span></span></td></tr><tr style="height:29px;"><td style="width:50%;height:29px;">storage</td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Contains temporary files (e.g. cache, logs, etc.).</span></span></td></tr><tr style="height:29px;"><td style="width:50%;height:29px;">vendor</td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Contains the external libraries used by the portal.</span></span></td></tr></tbody></table>

#### Request lifecycle

---

<div drawio-diagram="4959"><img src="https://usermanual.vtenext.com/uploads/images/drawio/2023-09/drawing-6-1694591374.png" alt="drawing-6-1694591374.png"/></div>


#### <span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Portal configuration</span></span>

---

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">The portal configuration file is located in "config/portal.config.php".</span></span>

<p class="callout danger"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">To </span></span>**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">overwrite the parameters</span></span>**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;"> the "config/sdk.config.php" file must be used otherwise, in case of updating the version of vtenext, the changes could be lost.</span></span></p>

<p class="callout danger"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">**How to move the Business Portal to another host/folder**  
Update the "portal\_url", "vte\_url" and "csrf\_secret" parameters in the "config/sdk.config.php" file.  
Also change the "default\_timezone" parameter if the host has a different timezone from the one in which the vte is located. Change the "portal.url" prop to the link pointing to the v2 folder. e.g. https://ticket.vtenext.com/v2​​</span></span></p>

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Here is the list of parameters supported by the new portal:</span></span>

<table id="bkmrk-parametro-tipo-defau" style="border-collapse:collapse;width:100%;height:1403px;"><tbody><tr style="height:29px;"><td style="width:22.6544%;height:29px;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Parameter</span></span>**</td><td style="width:10.9258%;height:29px;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Type</span></span>**</td><td style="width:18.7039%;height:29px;">**Default**</td><td style="width:47.716%;height:29px;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Description</span></span>**</td></tr><tr style="height:80px;"><td style="width:22.6544%;height:80px;">portal\_url</td><td style="width:10.9258%;height:80px;">String</td><td style="width:18.7039%;height:80px;">$PORTAL\_URL  
(config.inc.php)</td><td style="width:47.716%;height:80px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Indicates the URL of the customer portal. </span><span style="vertical-align:inherit;">If the portal folder is inside the vtenext root directory, the variable will be set to the value of the $PORTAL\_URL variable set in the config.inc.php file.</span></span></td></tr><tr style="height:96px;"><td style="width:22.6544%;height:96px;">vte\_url</td><td style="width:10.9258%;height:96px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">String</span></span></td><td style="width:18.7039%;height:96px;">$site\_URL (config.inc.php)</td><td style="width:47.716%;height:96px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Indicates the URL of vtenext and is used to obtain its data via the rest API. </span><span style="vertical-align:inherit;">If the portal folder is inside the vtenext root directory, the variable will be set to the value of the $site\_URL variable set in the config.inc.php file.</span></span></td></tr><tr style="height:63px;"><td style="width:22.6544%;height:63px;"><div><div>default_language</div></div></td><td style="width:10.9258%;height:63px;">String</td><td style="width:18.7039%;height:63px;"><div><div>it_it</div></div></td><td style="width:47.716%;height:63px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Indicates the default language used in the customer portal. </span><span style="vertical-align:inherit;">The value can be replaced with a supported language (see "languages" parameter).</span></span></td></tr><tr style="height:63px;"><td style="width:22.6544%;height:63px;"><div><div>languages</div></div></td><td style="width:10.9258%;height:63px;">Array</td><td style="width:18.7039%;height:63px;"><div><div>['en_us' =&gt; 'US English', 'it_it' =&gt; 'IT Italiano']</div></div></td><td style="width:47.716%;height:63px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Indicates the languages ​​supported in the customer portal. </span></span>  
<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">To add a new language you need to create a new file in the resources/lang folder.</span></span></td></tr><tr style="height:113px;"><td style="width:22.6544%;height:113px;"><div><div>production</div></div></td><td style="width:10.9258%;height:113px;">Bool</td><td style="width:18.7039%;height:113px;">false</td><td style="width:47.716%;height:113px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">This configuration indicates whether errors should be displayed or not. </span><span style="vertical-align:inherit;">If the environment is production, the value will be set to true to disable error display. </span><span style="vertical-align:inherit;">If the environment is development, the value will be set to false to allow errors to appear.</span></span></td></tr><tr style="height:80px;"><td style="width:22.6544%;height:80px;"><div><div>default_module</div></div></td><td style="width:10.9258%;height:80px;">String</td><td style="width:18.7039%;height:80px;"> </td><td style="width:47.716%;height:80px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Indicates the default module to load after logging into the portal. </span><span style="vertical-align:inherit;">The value can be replaced with the name of the desired module (must be enabled from profile). </span><span style="vertical-align:inherit;">If the parameter is empty, the portal home will be loaded.</span></span></td></tr><tr style="height:29px;"><td style="width:22.6544%;height:29px;"><div><div>favicon</div></div></td><td style="width:10.9258%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">String</span></span></td><td style="width:18.7039%;height:29px;"><div><div>assets/img/VTENEXT_favicon.ico</div></div></td><td style="width:47.716%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Indicates the path of the favicon. </span><span style="vertical-align:inherit;">The value is relative to the public folder.</span></span></td></tr><tr style="height:29px;"><td style="width:22.6544%;height:29px;"><div><div>login_logo</div></div></td><td style="width:10.9258%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">String</span></span></td><td style="width:18.7039%;height:29px;"><div><div>assets/img/VTENEXT_login.png</div></div></td><td style="width:47.716%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Indicates the path of the logo to load on the login page. </span><span style="vertical-align:inherit;">The value is relative to the public folder.</span></span>

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">\[UPDATE\] The logo must be loaded in vtenext settings &gt; Logos.</span></span>

</td></tr><tr style="height:29px;"><td style="width:22.6544%;height:29px;"><div><div>login_background</div></div></td><td style="width:10.9258%;height:29px;">String</td><td style="width:18.7039%;height:29px;"> </td><td style="width:47.716%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Indicates the path of the background to load on the login page. </span><span style="vertical-align:inherit;">The value is relative to the public folder.</span></span></td></tr><tr style="height:96px;"><td style="width:22.6544%;height:96px;"><div><div>header_logo_sm</div></div></td><td style="width:10.9258%;height:96px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">String</span></span></td><td style="width:18.7039%;height:96px;"><div><div>assets/img/VTENEXT_toggle.png</div></div></td><td style="width:47.716%;height:96px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Indicates the path of the icon to load in the minimized sidebar. </span><span style="vertical-align:inherit;">The value is relative to the public folder. </span></span>  
<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">\[UPDATE\] The icon must be loaded in vtenext settings &gt; Logos.</span></span></td></tr><tr style="height:80px;"><td style="width:22.6544%;height:80px;"><div><div>header_logo_lg</div></div></td><td style="width:10.9258%;height:80px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">String</span></span></td><td style="width:18.7039%;height:80px;"><div><div>assets/img/VTENEXT_header.png</div></div></td><td style="width:47.716%;height:80px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Indicates the path of the icon to load in the expanded sidebar. </span><span style="vertical-align:inherit;">The value is relative to the public folder. </span></span>  
<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">\[UPDATE\] The icon must be loaded in vtenext settings &gt; Logos.</span></span></td></tr><tr style="height:63px;"><td style="width:22.6544%;height:63px;"><div><div>helpdesk_logo</div></div></td><td style="width:10.9258%;height:63px;">String</td><td style="width:18.7039%;height:63px;"><div><div>assets/img/helpdesk.png</div></div></td><td style="width:47.716%;height:63px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Indicates the logo used to display the response given by customer support. </span><span style="vertical-align:inherit;">The value is relative to the public folder.</span></span></td></tr><tr style="height:80px;"><td style="width:22.6544%;height:80px;"><div><div>sidebar_theme</div></div></td><td style="width:10.9258%;height:80px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">String</span></span></td><td style="width:18.7039%;height:80px;"><div><div>sidebar-dark-primary</div></div></td><td style="width:47.716%;height:80px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Indicates the class of the main sidebar. </span></span>  
<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">It can have a "dark" or "light" brightness. </span></span>  
<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">It can also have a color variant, such as "primary", "success", "warning", "info", "danger".</span></span></td></tr><tr style="height:29px;"><td style="width:22.6544%;height:29px;"><div><div>enable_sidebar_search</div></div></td><td style="width:10.9258%;height:29px;">Bool</td><td style="width:18.7039%;height:29px;"><div><div>false</div></div></td><td style="width:47.716%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Enable/disable the search bar in the main sidebar.</span></span></td></tr><tr style="height:29px;"><td style="width:22.6544%;height:29px;"><div><div>csrf_secret</div></div></td><td style="width:10.9258%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">String</span></span></td><td style="width:18.7039%;height:29px;"><div><div>$csrf_secret (config.inc.php)</div></div></td><td style="width:47.716%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Indicates the secret key used to generate a csrf token. </span><span style="vertical-align:inherit;">If the portal folder is inside the vtenext root directory, the variable will be set to the value of the $csrf\_secret variable set in the config.inc.php file.</span></span></td></tr><tr style="height:29px;"><td style="width:22.6544%;height:29px;"><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">upload\_dir</span></span></div></div></td><td style="width:10.9258%;height:29px;">String</td><td style="width:18.7039%;height:29px;"> </td><td style="width:47.716%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Indicates the name of the folder used for uploading files.</span></span></td></tr><tr style="height:29px;"><td style="width:22.6544%;height:29px;"><div><div>browser_title_prefix</div></div></td><td style="width:10.9258%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">String</span></span></td><td style="width:18.7039%;height:29px;"> </td><td style="width:47.716%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Indicates the prefix label to use for the browser title. </span><span style="vertical-align:inherit;">The value can be replaced with the desired label.</span></span></td></tr><tr style="height:29px;"><td style="width:22.6544%;height:29px;"><div><div>browser_title_suffix</div></div></td><td style="width:10.9258%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">String</span></span></td><td style="width:18.7039%;height:29px;"><div><div>customer_portal</div></div></td><td style="width:47.716%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Indicates the suffix label to use for the browser title. </span><span style="vertical-align:inherit;">The value can be replaced with the desired label.</span></span></td></tr><tr style="height:29px;"><td style="width:22.6544%;height:29px;"><div><div>remember_cookie_name</div></div></td><td style="width:10.9258%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">String</span></span></td><td style="width:18.7039%;height:29px;"><div><div>portal_login_hash</div></div></td><td style="width:47.716%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Indicates the name of the cookie used to remember user authentication.</span></span></td></tr><tr style="height:29px;"><td style="width:22.6544%;height:29px;"><div><div>login_expire_time</div></div></td><td style="width:10.9258%;height:29px;">Int</td><td style="width:18.7039%;height:29px;"><div><div>2592000 (one month)</div></div></td><td style="width:47.716%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Indicates the expiration of the cookie used to remember user authentication. </span><span style="vertical-align:inherit;">The value can be replaced with the desired number of seconds.</span></span></td></tr><tr style="height:29px;"><td style="width:22.6544%;height:29px;"><div><div>default_timezone</div></div></td><td style="width:10.9258%;height:29px;">String</td><td style="width:18.7039%;height:29px;"><div><div>Europe/Rome</div></div></td><td style="width:47.716%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Indicates the default time zone used in the customer portal. </span><span style="vertical-align:inherit;">This configuration must be the same as the $default\_timezone variable set in the vtenext config.inc.php file.</span></span></td></tr><tr style="height:96px;"><td style="width:22.6544%;height:96px;"><div><div><div><div>module_icons</div></div></div></div></td><td style="width:10.9258%;height:96px;">Array</td><td style="width:18.7039%;height:96px;"><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">\[\]</span></span></div></div></td><td style="width:47.716%;height:96px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">With this configuration you can override the default icons used for modules enabled in the customer portal. </span><span style="vertical-align:inherit;">The default form icons are found in app/layouts/PortalLayout.php. </span><span style="vertical-align:inherit;">The names of the icons can be found here https://fonts.google.com/icons.</span></span></td></tr><tr style="height:29px;"><td style="width:22.6544%;height:29px;"><div><div><div><div>sdk_languages</div></div></div></div></td><td style="width:10.9258%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Array</span></span></td><td style="width:18.7039%;height:29px;"><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">\[\]</span></span></div></div></td><td style="width:47.716%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">With this configuration you can add new labels or modify existing ones. </span></span>  
<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">You need to create a new file in the sdk folder.</span></span></td></tr><tr style="height:29px;"><td style="width:22.6544%;height:29px;"><div><div><div><div>sdk_global_php</div></div></div></div></td><td style="width:10.9258%;height:29px;">Array</td><td style="width:18.7039%;height:29px;"><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">\[\]</span></span></div></div></td><td style="width:47.716%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">With this configuration you can add php files to load on each page (they should contain classes/functions). </span></span>  
<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">You need to create a new file in the sdk folder.</span></span></td></tr><tr style="height:29px;"><td style="width:22.6544%;height:29px;"><div><div><div><div>sdk_global_js</div></div></div></div></td><td style="width:10.9258%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Array</span></span></td><td style="width:18.7039%;height:29px;"><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">\[\]</span></span></div></div></td><td style="width:47.716%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">With this configuration you can add js to load globally. </span></span>  
<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">You need to create a new file in the public/assets/sdk folder.</span></span></td></tr><tr style="height:29px;"><td style="width:22.6544%;height:29px;"><div><div><div><div>sdk_module_js</div></div></div></div></td><td style="width:10.9258%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Array</span></span></td><td style="width:18.7039%;height:29px;"><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">\[\]</span></span></div></div></td><td style="width:47.716%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">With this configuration you can add js to load for a specific module. </span></span>  
<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">You need to create a new file in the public/assets/sdk folder.</span></span></td></tr><tr style="height:29px;"><td style="width:22.6544%;height:29px;"><div><div><div><div>sdk_global_css</div></div></div></div></td><td style="width:10.9258%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Array</span></span></td><td style="width:18.7039%;height:29px;"><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">\[\]</span></span></div></div></td><td style="width:47.716%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">With this configuration you can add css to load globally. </span></span>  
<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">You need to create a new file in the public/assets/sdk folder.</span></span></td></tr><tr><td style="width:22.6544%;"><div><div><div><div><div><div>sdk_controllers</div></div></div></div></div></div></td><td style="width:10.9258%;">Array</td><td style="width:18.7039%;"><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">\[\]</span></span></div></div></td><td style="width:47.716%;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">With this configuration you can add custom actions ("action" field in the URL). </span></span>  
<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">The array represents an association between the action name and the file that contains the controller class to handle the request. </span></span>  
<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">You need to create a new file in the sdk folder.</span></span></td></tr><tr><td style="width:22.6544%;"><div><div><div><div><div><div>sdk_module</div></div></div></div></div></div></td><td style="width:10.9258%;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Array</span></span></td><td style="width:18.7039%;"><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">\[\]</span></span></div></div></td><td style="width:47.716%;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">With this configuration you can add customizations on a specific module. </span></span>  
<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">The array represents an association between the module name and the file that contains the module's extended class. </span></span>  
<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">You need to create a new file in the sdk folder.</span></span></td></tr><tr><td style="width:22.6544%;"><div><div><div><div><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">sdk\_menu</span></span></div></div></div></div></div></div></td><td style="width:10.9258%;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Array</span></span></td><td style="width:18.7039%;"><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">\[\]</span></span></div></div></td><td style="width:47.716%;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">With this configuration you can add custom menu items in the sidebar. </span></span>  
<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">You need to create a new file in the sdk folder.</span></span></td></tr></tbody></table>

#### <span style="vertical-align:inherit;"><span style="vertical-align:inherit;">API Reference - Main classes, methods, functions and variables</span></span>

---

##### \\app\\Request

<table id="bkmrk-metodo-descrizione" style="border-collapse:collapse;width:100%;height:290px;"><tbody><tr style="height:29px;"><td style="width:25%;height:29px;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Method</span></span>**</td><td style="width:25%;height:29px;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Arguments</span></span>**</td><td style="width:50%;height:29px;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Description</span></span>**</td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>get()</div></div></td><td style="width:25%;height:29px;"><div><div>$keys = null, $purify = false</div></div></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to get one or more parameters from the global variable $\_GET. </span><span style="vertical-align:inherit;">If the second argument is set to "true", the parameters are purified through the HTML Purifier library. </span><span style="vertical-align:inherit;">Example:</span></span>

```PHP
$request->get('foo');
$request->get(['foo', 'bar']);
$request->get('foo', true);
```

</td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div><div><div>post()</div></div></div></div></td><td style="width:25%;height:29px;"><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">$keys = null, $purify = false</span></span></div></div></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to get one or more parameters from the global variable $\_POST. </span><span style="vertical-align:inherit;">If the second argument is set to "true", the parameters are purified through the HTML Purifier library. </span><span style="vertical-align:inherit;">Example:</span></span>

```PHP
$request->post('foo');
$request->post(['foo', 'bar']);
$request->post('foo', true);
```

</td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div><div><div><div><div>files()</div></div></div></div></div></div></td><td style="width:25%;height:29px;"><div><div><div><div>$key</div></div></div></div></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to get files uploaded via the HTTP POST method and organized via the global variable $\_FILES.</span></span>

```PHP
$request->files('attachments');
```

</td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div>cookie()</div></div></div></div></div></div></div></div></td><td style="width:25%;height:29px;"><div><div>$keys = null, $purify = false</div></div></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to obtain one or more parameters from the global variable $\_COOKIE. </span><span style="vertical-align:inherit;">If the second argument is set to "true", the parameters are purified through the HTML Purifier library. </span><span style="vertical-align:inherit;">Example:</span></span>

```PHP
$request->cookie('foo');
```

</td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div>server()</div></div></div></div></div></div></div></div></td><td style="width:25%;height:29px;"><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">$keys = null, $purify = false</span></span></div></div></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to get one or more parameters from the global variable $\_SERVER. </span><span style="vertical-align:inherit;">If the second argument is set to "true", the parameters are purified through the HTML Purifier library. </span><span style="vertical-align:inherit;">Example:</span></span>

```PHP
$request->server('');
```

</td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>isGet()</div></div></td><td style="width:25%;height:29px;"> </td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Returns true if the request method is GET.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">isPost()</span></span></div></div></td><td style="width:25%;height:29px;"> </td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Returns true if the request method is POST.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>isAjax()</div></div></td><td style="width:25%;height:29px;"> </td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Returns true if the request is AJAX.​</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>purify()</div></div></td><td style="width:25%;height:29px;"><div><div><div><div>$input</div></div></div></div></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Purify the $input variable through the HTML Purifier library.</span></span></td></tr></tbody></table>

##### \\app\\Response

<table id="bkmrk-metodo-argomenti-des" style="border-collapse:collapse;width:100%;height:290px;"><tbody><tr style="height:29px;"><td style="width:25%;height:29px;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Method</span></span>**</td><td style="width:25%;height:29px;">**<span style="vertical-align:inherit;">Arguments</span>**</td><td style="width:50%;height:29px;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Description</span></span>**</td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>__construct()</div></div></td><td style="width:25%;height:29px;"><div><div>$content = '', $statusCode = 200, $headers = []</div></div></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Initializes a new \\app\\Response() object with the response content ($content), return code ($statusCode), and default headers ($headers).</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>setContent()</div></div></td><td style="width:25%;height:29px;"><div><div>$content</div></div></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Set the content of the response.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">setStatusCode()</span></span></div></div></td><td style="width:25%;height:29px;"><div><div>$statusCode</div></div></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Set the response return code.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>setHeader()</div></div></td><td style="width:25%;height:29px;"><div><div>$header, $replace = true</div></div></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Set a new header in the response. </span><span style="vertical-align:inherit;">With the second argument it is possible to indicate whether or not the header must replace a previous header already set.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>setMimeType()</div></div></td><td style="width:25%;height:29px;"><div><div>$mimeType = 'text/html'</div></div></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Sets the response content mime.</span></span>

</td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>json()</div></div></td><td style="width:25%;height:29px;"><div><div>$data</div></div></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Set response content with $data converted to JSON format and 'application/json' mime.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>redirect()</div></div></td><td style="width:25%;height:29px;"><div><div>$page</div></div></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Performs a redirect to $page.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>downloadFile()</div></div></td><td style="width:25%;height:29px;"><div><div>$fullpath</div></div></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows downloading of a file located in $fullpath.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>output()</div></div></td><td style="width:25%;height:29px;"> </td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Outputs the content, response code, and headers you set.</span></span></td></tr></tbody></table>

##### \\app\\Session

<table id="bkmrk-metodo-argomenti-des-0" style="border-collapse:collapse;width:100%;height:261px;"><tbody><tr style="height:29px;"><td style="width:25%;height:29px;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Method</span></span>**</td><td style="width:25%;height:29px;">**<span style="vertical-align:inherit;">Arguments</span>**</td><td style="width:50%;height:29px;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Description</span></span>**</td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>set()</div></div></td><td style="width:25%;height:29px;"><div><div>$key, $value = ''</div></div></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Set the value $value with key $key in $\_SESSION.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>get()</div></div></td><td style="width:25%;height:29px;"><div><div>$key</div></div></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to get the value with key $key from $\_SESSION.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>flash()</div></div></td><td style="width:25%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">$key</span></span></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to get the value with key $key from $\_SESSION. </span><span style="vertical-align:inherit;">Next, the $key will be deleted from $\_SESSION.​</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>remove()</div></div></td><td style="width:25%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">$key</span></span></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Delete the $key from $\_SESSION.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>hasKey()</div></div></td><td style="width:25%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">$key</span></span></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Returns true if $key exists in $\_SESSION.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">setMulti()</span></span></div></div></td><td style="width:25%;height:29px;">$keys</td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to write multiple values ​​to $\_SESSION.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>removeMulti()</div></div></td><td style="width:25%;height:29px;"><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">$keys</span></span></div></div></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Delete multiple values ​​in $\_SESSION.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>append()</div></div></td><td style="width:25%;height:29px;"><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">$key, $value = ''</span></span></div></div></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Set the $key as an array in $\_SESSION and the value $value is added to it.</span></span></td></tr></tbody></table>

##### \\app\\Config

<table id="bkmrk-metodo-argomenti-des-1" style="border-collapse:collapse;width:100%;height:261px;"><tbody><tr style="height:29px;"><td style="width:25%;height:29px;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Method</span></span>**</td><td style="width:25%;height:29px;">**<span style="vertical-align:inherit;">Arguments</span>**</td><td style="width:50%;height:29px;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Description</span></span>**</td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>has()</div></div></td><td style="width:25%;height:29px;"><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">$key</span></span></div></div></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Returns true if $key exists in the global configuration.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">get()</span></span></div></div></td><td style="width:25%;height:29px;"><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">$key</span></span></div></div></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to get the value with key $key from the global configuration.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>set()</div></div></td><td style="width:25%;height:29px;"><div><div>$key, $value</div></div></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Set the value $value with key $key in the global configuration.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>getAll()</div></div></td><td style="width:25%;height:29px;"> </td><td><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">It allows you to obtain a key-value list with all the global configuration.</span></span></td></tr><tr><td style="width:25%;"><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">setMulti()</span></span></div></div></td><td style="width:25%;"><div><div>$values</div></div></td><td><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to write multiple values ​​into the global configuration.</span></span></td></tr><tr><td style="width:25%;"><div><div><div><div>clear()</div></div></div></div></td><td style="width:25%;"><div><div><div><div>$key</div></div></div></div></td><td><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Delete the $key from the global configuration.</span></span></td></tr><tr><td style="width:25%;"><div><div><div><div><div><div>clearAll()</div></div></div></div></div></div></td><td style="width:25%;"> </td><td><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Delete all values ​​from the global configuration.</span></span></td></tr></tbody></table>

##### \\app\\PortalModule

<table id="bkmrk-variabile-default-de" style="border-collapse:collapse;width:100%;height:307px;"><tbody><tr style="height:29px;"><td style="width:25%;height:29px;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Variable</span></span>**</td><td style="width:25%;height:29px;">**Default**</td><td style="width:50%;height:29px;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Description</span></span>**</td></tr><tr style="height:29px;"><td style="width:25%;height:29px;">$hasComments</td><td style="width:25%;height:29px;"><div><div>false</div></div></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Indicates whether the module supports comments.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>$hasAttachments</div></div></td><td style="width:25%;height:29px;"><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">false</span></span></div></div></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Indicates whether the module supports attachments.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>$enableEdit</div></div></td><td style="width:25%;height:29px;"><div><div>true</div></div></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Indicates whether the module can be modified (edit mode).</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>$formColumns</div></div></td><td style="width:25%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">3</span></span></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Indicates the number of columns to use for displaying fields in Create, Edit and Detail.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>$listTemplate</div></div></td><td style="width:25%;height:29px;"><div><div>List.tpl</div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Indicates the template used for displaying a list (action List).</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;">$referenceListTemplate</td><td style="width:25%;height:29px;"><div><div>sections/ReferenceList.tpl</div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Indicates the template used for displaying a related list (uitype 10).</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>$detailTemplate</div></div></td><td style="width:25%;height:29px;"><div><div>Detail.tpl</div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Indicates the template used to display the detail of a record (action Detail).</span></span></td></tr><tr style="height:46px;"><td style="width:25%;height:46px;"><div><div><div><div>$editTemplate</div></div></div></div></td><td style="width:25%;height:46px;"><div><div><div><div>Edit.tpl</div></div></div></div></td><td style="height:46px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Indicates the template used to display the edit of a record (action Edit).</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div><div><div><div><div>$notAuthorizedTemplate</div></div></div></div></div></div></td><td style="width:25%;height:29px;"><div><div><div><div><div><div>PageNotAuthorized.tpl</div></div></div></div></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Indicates the template used to display a permission error (e.g. record not found or permission errors).</span></span></td></tr></tbody></table>

<table id="bkmrk-metodo-argomenti-des-2" style="width:100%;border-collapse:collapse;height:353px;"><tbody><tr style="height:29px;"><td style="width:25%;height:29px;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Method</span></span>**</td><td style="width:25%;height:29px;">**<span style="vertical-align:inherit;">Arguments</span>**</td><td style="width:50%;height:29px;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Description</span></span>**</td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>__construct()</div></div></td><td style="width:25%;height:29px;"><div><div>$module</div></div></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">$module indicates the name of the module to obtain an instance of the class. </span><span style="vertical-align:inherit;">If the module has been extended via SDK then an instance of the extended class will be returned.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>prepareList()</div></div></td><td style="width:25%;height:29px;"><div><div>$viewer, $request</div></div></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Method used for displaying a list (action List).</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>postProcessList()</div></div></td><td style="width:25%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">$viewer, $request</span></span></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">This method can be used by extended classes to insert/modify data set in prepareList().</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>prepareEdit()</div></div></td><td style="width:25%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">$viewer, $request</span></span></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Method used to display the edit of a record (action Edit).</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>postProcessEdit()</div></div></td><td style="width:25%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">$viewer, $request</span></span></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">This method can be used by extended classes to insert/modify the data set in prepareEdit().</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>prepareDetail()</div></div></td><td style="width:25%;height:29px;"><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">$viewer, $request</span></span></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Method used to display the detail of a record (action Detail).</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>postProcessDetail()</div></div></td><td style="width:25%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">$viewer, $request</span></span></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">This method can be used by extended classes to insert/modify data set in prepareDetail().</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>saveRecord()</div></div></td><td style="width:25%;height:29px;"><div><div>$request</div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Method used to save a record (action Save).</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>postProcessSaveValues()</div></div></td><td style="width:25%;height:29px;"><div><div>$request, &amp;$values</div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">This method can be used by extended classes to insert/modify data set in saveRecord().</span></span></td></tr><tr style="height:63px;"><td style="width:25%;height:63px;"><div><div><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">isPermitted()</span></span></div></div></div></div></td><td style="width:25%;height:63px;"><div><div><div><div>$module, $action = self::ACTION_LIST, $recordValues = []</div></div></div></div></td><td style="height:63px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Returns if the contact has permission to perform a certain action.</span></span>

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">List of supported actions:</span></span>

<div><div>. ACTION_LIST</div><div>. ACTION_CREATE</div><div>. ACTION_EDIT</div><div>. ACTION_DETAIL</div><div>. ACTION_DELETE</div><div>. ACTION_SAVE</div><div>. ACTION_ADD_COMMENTS</div><div>. ACTION_UPLOAD_ATTACHMENTS</div><div>. ACTION_CHANGE_PWD</div><div>. ACTION_SOLVE_TICKET</div></div></td></tr></tbody></table>

##### \\app\\clients\\PortalRestClient

<table id="bkmrk-metodo-argomenti-des-3" style="border-collapse:collapse;width:100%;height:266px;"><tbody><tr style="height:29px;"><td style="width:25%;height:29px;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Method</span></span>**</td><td style="width:25%;height:29px;">**<span style="vertical-align:inherit;">Arguments</span>**</td><td style="width:50%;height:29px;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Description</span></span>**</td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>get()</div></div></td><td style="width:25%;height:29px;">$restName, $queryParameters = \[\], $headers = \[\]</td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to perform a GET request to vtenext.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>post()</div></div></td><td style="width:25%;height:29px;">$restName, $queryParameters = \[\], $bodyParameters = \[\], $headers = \[\]</td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to perform a POST request to vtenext.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">postMultipart()</span></span></div></div></td><td style="width:25%;height:29px;">$restName, $queryParameters = \[\], $bodyParameters = \[\], $fileParameters = \[\], $headers = \[\]</td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to perform a multipart POST request to vtenext.</span></span>

</td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>patch()</div></div></td><td style="width:25%;height:29px;"><div><div>$restName, $queryParameters = [], $bodyParameters = [], $headers = []</div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to execute a PATCH request to vtenext.</span></span></td></tr><tr style="height:63px;"><td style="width:25%;height:63px;"><div><div>delete()</div></div></td><td style="width:25%;height:63px;"><div><div>$restName, $queryParameters = [], $headers = []</div></div></td><td style="height:63px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to execute a DELETE request to vtenext.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>postDownload()</div></div></td><td style="width:25%;height:29px;"><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">$restName, $queryParameters = \[\], $bodyParameters = \[\], $headers = \[\]</span></span></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to perform a POST request to download a vtenext file in stream mode.</span></span></td></tr></tbody></table>

##### Helpers

<table id="bkmrk-funzione-argomenti-d" style="border-collapse:collapse;width:100%;height:1310px;"><tbody><tr style="height:29px;"><td style="width:25%;height:29px;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Function</span></span>**</td><td style="width:25%;height:29px;">**<span style="vertical-align:inherit;">Arguments</span>**</td><td style="width:50%;height:29px;">**<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Description</span></span>**</td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>preprint()</div></div></td><td style="width:25%;height:29px;"><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">$var</span></span></div></div></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">print\_r formatted with &lt;pre&gt; tag</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">predump()</span></span></div></div></td><td style="width:25%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">$var</span></span></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">var\_dump formatted with &lt;pre&gt; tags</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>encodeForHtml()</div></div></td><td style="width:25%;height:29px;"><div><div>$value, $charset = 'UTF-8'</div></div></td><td style="width:50%;height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Encode the $value value to be inserted into an HTML page.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>encodeForHtmlAttr()</div></div></td><td style="width:25%;height:29px;"><div><div>$value, $enclosing = '"'</div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Encode the $value value to be placed inside an HTML tag attribute.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>encodeForJs()</div></div></td><td style="width:25%;height:29px;"><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">$value, $enclosing = '"'</span></span></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Encode the $value value to be placed inside a javascript &lt;script&gt;.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>htmlAttr()</div></div></td><td style="width:25%;height:29px;"><div><div>$attributes</div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Encode a list of attributes to be placed inside an HTML tag.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>config()</div></div></td><td style="width:25%;height:29px;"><div><div>$key</div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to get the value with key $key from the global configuration.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div><div><div>trans()</div></div></div></div></td><td style="width:25%;height:29px;"><div><div><div><div>$key, $args = []</div></div></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Translate the label $key.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div><div><div><div><div>listUrl()</div></div></div></div></div></div></td><td style="width:25%;height:29px;"><div><div><div><div><div><div>$module, $extraParams = []</div></div></div></div></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Generate a link to open a list.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div>createUrl()</div></div></div></div></div></div></div></div></td><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">$module, $extraParams = \[\]</span></span></div></div></div></div></div></div></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Generates a link to open the creation of a record.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div>createDocUrl()</div></div></div></div></div></div></div></div></div></div></td><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div>$module, $folderId, $extraParams = []</div></div></div></div></div></div></div></div></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Generates a link to open the creation of a document.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div><div><div>detailUrl()</div></div></div></div></div></div></div></div></div></div></div></div></td><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div><div><div>$module, $record, $extraParams = []</div></div></div></div></div></div></div></div></div></div></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Generates a link to open the detail of a record.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">editUrl()</span></span></div></div></div></div></div></div></div></div></div></div></div></div></div></div></td><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">$module, $record, $extraParams = \[\]</span></span></div></div></div></div></div></div></div></div></div></div></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Generates a link to open the editing of a record.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div>downloadUrl()</div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></td><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div><div><div><div><div>$record, $documentId, $extraParams = []</div></div></div></div></div></div></div></div></div></div></div></div></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Generates a link to download a document.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div>docFolderUrl()</div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></td><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">$module, $folderId, $extraParams = \[\]</span></span></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Generates a link to open a specific folder in the documents module.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div>returnUrl()</div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></td><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div>$request</div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Generate a return link (e.g. "Cancel" action).</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div>portalLanguage()</div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></td><td style="width:25%;height:29px;"> </td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Returns the language used in the portal.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>setPortalLanguage()</div></div></td><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div>$language</div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Set the $language in the portal.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div><div><div>getBrowserTitle()</div></div></div></div></td><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div>$title</div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Returns the title to be set in an HTML page of the portal.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div><div><div><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">getModuleLabel()</span></span></div></div></div></div></div></div></td><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div>$module</div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Returns the translation of the module $module.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">getSingleModuleLabel()</span></span></div></div></div></div></div></div></div></div></td><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">$module</span></span></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Returns the singular translation of the module $module.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div>getMaxUploadSize()</div></div></div></div></div></div></div></div></div></div></td><td style="width:25%;height:29px;"> </td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Returns the maximum upload size in the portal.</span></span></td></tr><tr style="height:63px;"><td style="width:25%;height:63px;"><div><div>setPortalCookie()</div></div></td><td style="width:25%;height:63px;"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div>$name, $value = "", $expires_or_options, $httponly = false</div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></td><td style="height:63px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Set a cookie in the portal.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">unsetPortalCookie()</span></span></div></div></div></div></td><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div>$name, $httponly = false</div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Removes a cookie from the portal.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div><div><div><div><div>csrfToken()</div></div></div></div></div></div></td><td style="width:25%;height:29px;"> </td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Returns the csrf token.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>csrfInputName()</div></div></td><td style="width:25%;height:29px;"> </td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Returns the name of the input for sending the csrf token.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>flashPortalError()</div></div></td><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div>$error</div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to display a timed error message.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div><div><div>flashPortalMessage()</div></div></div></div></td><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div>$message</div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows you to display a timed message.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div><div><div><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">customerId()</span></span></div></div></div></div></div></div></td><td style="width:25%;height:29px;"> </td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Returns the ID of the authenticated contact.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>customerEmail()</div></div></td><td style="width:25%;height:29px;"> </td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Returns the email of the authenticated contact.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>customerUsername()</div></div></td><td style="width:25%;height:29px;"> </td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Returns the name and surname of the authenticated contact.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>resourcever()</div></div></td><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div>$filename</div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Allows the versioning of css and javascript files.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>basePath()</div></div></td><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div>$path = ''</div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Returns the path to the portal base folder. </span><span style="vertical-align:inherit;">If $path is specified, a path is created and returned starting from the base folder.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>appPath()</div></div></td><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">$path = ''</span></span></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Returns the path to the portal's "app" folder. </span><span style="vertical-align:inherit;">If $path is specified, a path is created and returned starting from the "app" folder.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>configPath()</div></div></td><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">$path = ''</span></span></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Returns the path to the portal's "config" folder. </span><span style="vertical-align:inherit;">If $path is specified, a path is created and returned starting from the "config" folder.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>resourcesPath()</div></div></td><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div>$path = ''</div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Returns the path to the portal's "resources" folder. </span><span style="vertical-align:inherit;">If $path is specified, a path is created and returned starting from the "resources" folder.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>langPath()</div></div></td><td style="width:25%;height:29px;"><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">only $</span></span></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Returns the path to the indicated language file $lang.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>storagePath()</div></div></td><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">$path = ''</span></span></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Returns the path to the portal's "storage" folder. </span><span style="vertical-align:inherit;">If $path is specified, a path is created and returned starting from the "storage" folder.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>publicPath()</div></div></td><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">$path = ''</span></span></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Returns the path to the portal's "public" folder. </span><span style="vertical-align:inherit;">If $path is specified, a path is created and returned starting from the "public" folder.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>vtePath()</div></div></td><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">$path = ''</span></span></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Returns the path to the vtenext folder. </span><span style="vertical-align:inherit;">If $path is indicated, a path is created and returned starting from the vtenext folder.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>sdkPath()</div></div></td><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div>$path = ''</div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Returns the path to the portal's "sdk" folder. </span><span style="vertical-align:inherit;">If $path is indicated, a path is created and returned starting from the "sdk" folder.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>sdkAssetsPath()</div></div></td><td style="width:25%;height:29px;"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">$path = ''</span></span></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Returns the path to the portal's "public/assets/sdk" folder. </span><span style="vertical-align:inherit;">If $path is specified, a path is created and returned starting from the "public/assets/sdk" folder.</span></span></td></tr><tr style="height:29px;"><td style="width:25%;height:29px;"><div><div>publicRelPath()</div></div></td><td style="width:25%;height:29px;"><div><div>$assetPath</div></div></td><td style="height:29px;"><span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Returns a relative path starting from the "public" folder.</span></span></td></tr></tbody></table>

#### <span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Examples</span></span>

---

##### SDK view

Here is an example of how to change the visibility of portal fields via view SDK.

```PHP
<?php
require('../../config.inc.php');
chdir($root_directory);
require_once('include/utils/utils.php');
require_once('vtlib/Vtecrm/Module.php');
$Vtiger_Utils_Log = true;
global $adb, $table_prefix;
VteSession::start();

$module = '';
$src = '';
$mode = 'constrain';
$success = 'continue';
SDK::addView($module, $src, $mode, $success);
```

```php
<?php

global $sdk_mode, $table_prefix;

switch ($sdk_mode) {
	case 'portal.create':
		$readonly = 100;
		$success = true;
		break;
	case 'portal.edit':
	case 'portal.detail':
		if ($col_fields['field3'] !== 'Open') {
			$readonly = 99;
			$success = true;
		}
		if (in_array($fieldname, ['field1', 'field2'])) {
			$readonly = 100;
			$success = true;
		}
		break;
}

```


##### <span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Creating a new controller</span></span>

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Here is an example of how to create a new controller to manage a custom action.</span></span>

- <span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Edit "config/sdk.config.php" to insert a new controller</span></span>

```PHP
return [
	'sdk_controllers' => [
		'SampleVte' => 'controllers/SampleVteController.php',
	]
];
```

- <span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Implement the SampleVteController class in "sdk/controllers/SampleVteController.php"</span></span>

```PHP
<?php

class SampleVteController extends \app\controllers\BaseController {
	
	public function index($request) {
		return $this->displaySomething($request);
	}

	protected function displaySomething($request) {
		$parameter1 = $request->get('parameter1', true);
		$parameter2 = $request->get('parameter2', true);

		$this->viewer->assign('PARAMETER1', $parameter1);
		$this->viewer->assign('PARAMETER2', $parameter2);

		$layout = \app\LayoutFactory::getPortalLayout($this->viewer, $this->client, $request);
		$output = $this->fetchWithLayout('sdk/SampleVte.tpl', $layout);

		return new \app\Response($output);
	}
	
}

```

- <span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Create a new template in "resources/templates/sdk/SampleVte.tpl"</span></span>

```PHP
{extends file='layouts/PortalLayout.tpl'}

{block name=content}
	<h1>Sample Vte</h1>
{/block}
```

- <span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Modify "config/sdk.config.php" indicating the sdk file for inserting the new entries in the side menu</span></span>

```PHP
return [
	'sdk_controllers' => [
		'SampleVte' => 'controllers/SampleVteController.php',
	],
  	'sdk_menu' => [
		'samplevte-menu.php',
	]
];
```

- <span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Edit the "sdk/samplevte-menu.php" file</span></span>

```PHP
<?php

return [
	[
		'text' => trans('SampleVte'),
		'active' => false,
		'prefix' => [
			'type' => 'icon',
			'icon_style' => 'material',
			'icon_name' => 'pie_chart',
		],
		'action' => [
			'type' => 'link',
			'link_href' => "index.php?action=SampleVte",
		],
	],
];

```

[![image-1694533951130.48.16.png](https://usermanual.vtenext.com/uploads/images/gallery/2023-09/scaled-1680-/image-1694533951130-48-16.png)](https://usermanual.vtenext.com/uploads/images/gallery/2023-09/image-1694533951130-48-16.png)

##### <span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Extending a module</span></span>

<span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Here is an example of how to extend the functionality of a module.</span></span>

- <span style="vertical-align:inherit;">Edit</span> "config/sdk.config.php"

```PHP
return [
	'sdk_module' => [
		'Contacts' => 'modules/ContactsModule.php',
	]
];
```

- <span style="vertical-align:inherit;"><span style="vertical-align:inherit;">Implement the ContactsModule class in "sdk/modules/ContactsModule.php"</span></span>

```PHP
<?php

class ContactsModule extends \app\PortalModule {
	
	public $hasComments = true;

	public $formColumns = 2;

	public function canAddComments() {
		return true;
	}
	
}

```

[![image-1694596916198.20.03.png](https://usermanual.vtenext.com/uploads/images/gallery/2023-09/scaled-1680-/image-1694596916198-20-03.png)](https://usermanual.vtenext.com/uploads/images/gallery/2023-09/image-1694596916198-20-03.png)

# Migration to PHP ≥ 8.3

# What's new in PHP 8.3+

#### **Minimum PHP version updated**

The application now requires **PHP 8.3** as the minimum version.




#### **Database updates**

Starting from version 26.01 of vtenext, some important database changes took place, such as:

**Charset and Collation** Database is now using by default the charset **utf8mb4** and collation **utf8mb4\_general\_ci**, which allows to store and display all possible UTF-8 characters, including emojis and special symbols.

**BIGINT columns**  
All columns containing crmids are now BIGINT (for new installations). When creating columns that store crmid, always use BIGINT (or **I8** when using adodb datadict). This is necessary to support a large number of records (more than ~2 billion).

**TINYINT columns**  
All columns containing only boolean integers (e.g., 0/1 for checkbox fields uitype 56, deleted=0/1, or various status columns with only small integers) are now TINYINT (or **I1** when using adodb datadict). This change will slightly reduce the database size and query execution time in certain cases.

During the update (step 3019 - 3020) a script to automatically convert the charset and the integer columns is executed (modules/Update/changes/bigint\_utf8mb4\_alter.php). Since this change can take a very long time, bigger tables (modules with more than 100k records, or tables bigger than 5GB) are excluded and should be converted manually at the end of the update (or at an appropriate time to minimize downtime, like at night). The script will output if this is necessary or if all tables have been converted. In case the manual conversion is necessary, it can be done in 2 ways:

- By running the queries in file: modules/Update/changes/bigint\_alter\_big.sql
- For extremely large tables, by executing the script: modules/Update/changes/percona\_alter.sh, to use the pt-online-schema-change tool (installation to be done separately) to avoid locking the tables. It is advised to review this script and manually add the necessary command line option to the Percona command.

**Licence tag**

A new SPDX license tag is now **mandatory in all vtenext core files** (php, js and tpl) that are not external libraries

```php
/*************************************
 * SPDX-FileCopyrightText: 2009-present Vtenext S.r.l. Società Benefit 
 * SPDX-License-Identifier: LicenseRef-vtenext-business-license 
 ************************************/
```



#### **New configuration files**

All new custom configuration files (e.g., custom api token) must be placed in the `/config` folder. If some configuration is different from dev/prod environments, use the corresponding config file in `/config/`.

- `config.$envType.php`: Overrides of PHP configuration and global variables for a specific environment type
- `phpstan/*`: Configuration to use when running phpstan
- `smarty/*`: Configuration directory for Smarty library
- `logging.php`: Configuration for the legacy logs (old log4php)
- `request.config.php`: See [Configuration - RequestHandler](https://usermanual.vtenext.com/books/developers/page/requesthandler#bkmrk-configuration)




#### **New config variables**

The following global config variables have been added to `config.inc.php`:

##### **`$enableLegacyLogs`**

Activate the legacy logs (the old log4php), which logs a lot of stuff in `logs/vtenext.log`. The usefulness of this log is not certain. Configuration of this log is in `/config/logging.php`.

##### **`$envType`**

Specify the environment type. Possible values:

- `"prod"` (default)
- `"preprod"`
- `"dev"`
- `"debug"`

Each level activates more error messages. The default configuration is in `config.inc.php`, variable `$php_config`, overridden by the specific `/config/config.$envType.php` file.

##### **`$php_config`**

Used to specify the default PHP configuration used by `$envType`.

##### **`$smarty_warn_superglobals`**

<div id="bkmrk-a-global-configurati"><div>A global configuration flag that enables warnings when PHP superglobals are used in Smarty templates. When enabled, the system registers a Smarty POST filter that scans compiled template code for superglobal usage ($_GET, $_POST, $_REQUEST, $_COOKIE, $_SERVER, $_ENV). <div><div>By default it is enabled only in development environments.</div></div></div></div>


#### **New functions and methods**

##### **RequestHandler (RH)**

`RequestHandler` provides secure, centralized access to HTTP request data. It automatically sanitizes input using `vtlib_purify()` and applies type-safe filters from `F::` enum. This replaces direct access to superglobals like `$_REQUEST`, `$_GET`, `$_POST`, etc.

For details, see: [RequestHandler](https://usermanual.vtenext.com/books/developers/page/requesthandler)

##### **BaseAction**

We have moved the route resolution logic to the new `IndexRouter` class and introduced the `BaseAction` class to handle actions.

For details, see: [Routing system](https://usermanual.vtenext.com/books/developers/page/routing-system).

##### **CRMEntity methods update**

The old `retrieve_entity_info` has been renamed to `retrieve_html` and `retrieve_entity_info_no_html` to `retrieve`. This is to make more explicit that there is a HTML conversion going on. The old functions are deprecated, but still working.

- `CRMEntity::retrieve_html()`: Retrieves entity with HTML conversion
- `CRMEntity::retrieve()`: Retrieves entity without HTML conversion

##### **Translation aliases**

New convenient aliases for translation functions:

- `trans()`: Alias for `getTranslatedString()`
- `trans_app()`: Alias for `getTranslatedString($str, "APP_STRINGS")`
- `trans_js()`: Alias for `getTranslatedString($str, "ALERT_ARR")`




#### **New database query methods**

New database query methods are available. For details, see: [Database Best Practices](https://usermanual.vtenext.com/books/developers/page/database-best-practices)




#### **<span data-teams="true">Autoescaping of all Smarty variable</span>**

<span data-teams="true">All variables outputted by Smarty templates (for example `{$VARIABLE}`) by default have all applicable characters are converted to html `&...;` notation.</span>

<span data-teams="true">The reason of this change is to reduce as much as possible exposure to XSS attacks, since it was extremely difficult to track every possible variable in templates and ensure it was properly escaped.</span>

For details, see: [Escape](https://usermanual.vtenext.com/books/developers/page/introduction).




#### **New tools**

##### **`tools/change-env`**

Allows to easily change the `$envType` variable in `config.inc.php` and automatically execute scripts afterwards. Scripts to execute are located in `plugins/envs/make.$env/*`.

##### **`tools/activate`**

<div id="bkmrk-prefix-the-absolute-"><div>Prefix the absolute path of `tools/` to `PATH`.</div></div>##### **`tools/analyze`**

<div id="bkmrk-install-and-run-phps"><div>Install and run phpstan on the vte root directory, or a custom one.</div></div>##### **`tools/tests`**

<div id="bkmrk-install-and-run-phpu"><div id="bkmrk-install-and-run-phpu-1"><div id="bkmrk-install-and-run-phpu-2"><div><div>Install and run phpunit with vte unit and functional tests.</div></div></div></div></div>##### **`tools/change-color`**

<div id="bkmrk-simple-script-to-cha"><div><div>Simple script to change color of vtenext using some standard values. The purpose is to quickly change color of an environment to differentiate it from the production.</div></div></div>##### **`tools/compile-themes`**

<div id="bkmrk-compile-scss-sources"><div id="bkmrk-compile-scss-sources-1"><div><div>Compile scss sources of all (or some) known vte themes. Can monitor for file changes and trigger a compilation automatically (`inotifywait` must be installed).</div><div>  
</div></div></div></div>

#### **Important files and folders**

##### **`cache_local/`**

This folder should contain cache files to be stored on the same host as the webserver. It is useful in case of cluster deployment, where the `cache/` folder is normally shared, but `cache_local/` is only local.

##### **`plugins/envs/`**

Contains the scripts to be executed when changing the environment type (e.g., after cloning the prod in the dev host).




#### **GDPR portal update**

The GDPR portal has been significantly updated with the following improvements:

- Migration of libraries to Composer for better dependency management
- API calls switched to vtenext RestClient
- Smarty templating engine upgraded to the latest version (^5.4)




#### **Libraries**

All third party libraries now must be imported via composer (living in the `/vendor/` folder), or placed in the `/vendor-extra/` folder. Libraries in vendor-extra are automatically included in the class path.

<p class="callout info">**Important note** Whenever a new library is added, either via composer or manually, the following command must be executed to refresh the classmap: `composer optimize` → alias of `composer dump-autoload -o`</p>

##### **Libraries updated**

<table id="bkmrk-library-previous-ver" style="width: 84.7619%; height: 510.344px;"><thead><tr style="height: 46.5938px;"><th style="width: 50.4923%; height: 46.5938px;">**Library**</th><th style="width: 27.8481%; height: 46.5938px;">**Previous version**</th><th style="width: 21.6596%; height: 46.5938px;">**New version**</th></tr></thead><tbody><tr style="height: 29.7969px;"><td style="width: 50.4923%; height: 29.7969px;">`phpmailer/phpmailer`</td><td style="width: 27.8481%; height: 29.7969px;">6.6.5</td><td style="width: 21.6596%; height: 29.7969px;">6.9.3</td></tr><tr style="height: 29.7969px;"><td style="width: 50.4923%; height: 29.7969px;">`ezyang/htmlpurifier`</td><td style="width: 27.8481%; height: 29.7969px;">4.13.0</td><td style="width: 21.6596%; height: 29.7969px;">4.18.0</td></tr><tr style="height: 29.7969px;"><td style="width: 50.4923%; height: 29.7969px;">`mpdf/mpdf`</td><td style="width: 27.8481%; height: 29.7969px;">8.0.12</td><td style="width: 21.6596%; height: 29.7969px;">8.2.5</td></tr><tr style="height: 29.7969px;"><td style="width: 50.4923%; height: 29.7969px;">`league/iso3166`</td><td style="width: 27.8481%; height: 29.7969px;">2.1.5</td><td style="width: 21.6596%; height: 29.7969px;">4.3.2</td></tr><tr style="height: 29.7969px;"><td style="width: 50.4923%; height: 29.7969px;">`league/oauth2-client`</td><td style="width: 27.8481%; height: 29.7969px;">2.7.0</td><td style="width: 21.6596%; height: 29.7969px;">2.8.1</td></tr><tr style="height: 29.7969px;"><td style="width: 50.4923%; height: 29.7969px;">`league/oauth2-google`</td><td style="width: 27.8481%; height: 29.7969px;">3.0.4</td><td style="width: 21.6596%; height: 29.7969px;">4.0.1</td></tr><tr style="height: 29.7969px;"><td style="width: 50.4923%; height: 29.7969px;">`spomky-labs/otphp`</td><td style="width: 27.8481%; height: 29.7969px;">10.0.3</td><td style="width: 21.6596%; height: 29.7969px;">11.3.0</td></tr><tr style="height: 29.7969px;"><td style="width: 50.4923%; height: 29.7969px;">`joypixels/emoji-toolkit`</td><td style="width: 27.8481%; height: 29.7969px;">6.6.0</td><td style="width: 21.6596%; height: 29.7969px;">9.0.1</td></tr><tr style="height: 29.7969px;"><td style="width: 50.4923%; height: 29.7969px;">`phpoffice/phpspreadsheet`</td><td style="width: 27.8481%; height: 29.7969px;">1.8.2</td><td style="width: 21.6596%; height: 29.7969px;">4.1.0</td></tr><tr style="height: 29.7969px;"><td style="width: 50.4923%; height: 29.7969px;">`phpseclib/phpseclib`</td><td style="width: 27.8481%; height: 29.7969px;">3.0.42</td><td style="width: 21.6596%; height: 29.7969px;">3.0.43</td></tr><tr style="height: 29.7969px;"><td style="width: 50.4923%; height: 29.7969px;">`jaybizzle/crawler-detect`</td><td style="width: 27.8481%; height: 29.7969px;">1.3.0</td><td style="width: 21.6596%; height: 29.7969px;">1.3.4</td></tr><tr style="height: 29.7969px;"><td style="width: 50.4923%; height: 29.7969px;">`hubspot/hubspot-php`</td><td style="width: 27.8481%; height: 29.7969px;">2.0</td><td style="width: 21.6596%; height: 29.7969px;">5.3.0</td></tr><tr style="height: 29.7969px;"><td style="width: 50.4923%; height: 29.7969px;">`slince/shopify-api-php`</td><td style="width: 27.8481%; height: 29.7969px;">2.5</td><td style="width: 21.6596%; height: 29.7969px;">3.1.0</td></tr><tr style="height: 46.5938px;"><td style="width: 50.4923%; height: 46.5938px;">`zendesk/zendesk_api_client_php`</td><td style="width: 27.8481%; height: 46.5938px;">2.29</td><td style="width: 21.6596%; height: 46.5938px;">4.1.0</td></tr><tr style="height: 29.7969px;"><td style="width: 50.4923%; height: 29.7969px;">`lesstif/php-jira-rest-client`</td><td style="width: 27.8481%; height: 29.7969px;">1.41</td><td style="width: 21.6596%; height: 29.7969px;">5.9.0</td></tr></tbody></table>

##### **Libraries added**

- `web-token/jwt-library` 4.0.4
- `php-amqplib/php-amqplib` 3.7.3
- `econea/nusoap` 0.9.18
- `dragonmantank/cron-expression` 3.4.0
- `qualityunit/tnef-decoder` 1.2.9
- `sabre/vobject` 4.5.6
- `flipboxdigital/oauth2-hubspot` 1.0.1
- `stevenmaguire/oauth2-salesforce` 2.0.1
- `stevenmaguire/oauth2-zendesk` 2.1.0
- `automattic/woocommerce` 3.1.0
- `monolog` (replaces log4php) 3.8.1

##### **Libraries moved to vendor**

The following libraries have been moved from various locations to `/vendor/` or `/vendor-extra/`:

- `include/nusoap` → moved to vendor
- `modules/Settings/ProcessMaker/thirdparty/cron-expression` → moved to vendor
- `modules/PDFMaker/classes/simple_html_dom.php` → moved to vendor-extra
- `include/magpierss` → moved to vendor-extra
- `modules/Morphsuit/utils/RSA` → replaced with phpseclib in vendor
- `modules/Newsletter/bounce_driver.class.php` → moved to vendor-extra
- `modules/Messages/src/attachment_tnef` → moved to vendor
- `modules/Messages/src/Squirrelmail.php` → moved to vendor-extra
- `smartoptimizer` → moved to vendor-extra (updated minifier, added brotli and zstandard support)
- `portal/nusoap` → moved to vendor
- `portal/include/htmlpurifier` → removed
- `include/pChart` → patched and moved to vendor-extra
- `include/freetag` → fixed and moved to vendor-extra
- `modules/VteSync/vendor` → moved to main vendor
- `modules/VteSync/VteSyncLib/src/Connector/Jira/vendor` → moved to main vendor

##### **Removed libraries**

- `log4php` - Replaced with monolog
- `vtlib/SimplePie` - No longer used
- `include/antlr` - Not working, grammar unknown and never used
- `modules/SDK/examples/intellisense` - Removed
- `modules/Calendar/iCal/ical-parser-class.php` - Not used
- `modules/Calendar/iCal/iCalendar_*.php` (Bennu) - Replaced with sabre/vobject
- `modules/Calendar/iCal/iCalcreator` - Replaced with sabre/vobject
- `include/Zend` - Simplified
- `gdpr/include/vtwsclib/lib/Zend` - Removed
- `portal/include/Zend` - Removed
- `videlalvaro/php-amqplib` - Replaced with php-amqplib/php-amqplib
- `web-token/jwt-easy`, `web-token/jwt-key-mgmt`, `web-token/signature-pack` - Replaced with web-token/jwt-library

##### **Zend framework replaced with Laminas**

The Zend framework (a discontinued project) has been completely removed and replaced with **Laminas**, its official successor. All Zend dependencies have been migrated to the Laminas equivalents.




#### **Removed config variables**

The following config variables have been removed:

##### **`$display_empty_home_blocks`**

Never used.

##### **`$php_max_execution_time`**

Misleading, since it was applied only in a few cases, and it was always 0, setting the limit to unlimited. Replaced by the `$php_config` and specific overrides when needed (e.g., Report export, PDF generation).




#### **Removed files and folders**

##### **`modules/Dashboard`**

Removed this module, already deprecated and inactive.

##### **`PEAR.php`**

Used only by Dashboard and by obsolete libraries.

##### **`Image/*`**

Used only by Dashboard module.

##### **`include/db_backup`**

Not used anymore (and also a bad idea to make a full backup on admin logout).

##### **`Smarty/templates_c`**

Moved to `cache_local/smarty` to keep the number of writable folders limited.

##### **`plugins/erpconnectorDir`**

This folder was used in the past to perform one/two-way synchronizations with external systems. It has now been replaced by the [Data Import](https://usermanual.vtenext.com/books/user-manual-vtenext-2502/page/1716-data-import).



##### **Old portal (v1)**

The previous version of the portal is **no longer supported**. You can choose one of the following options:

1. **Switch to the new Business Portal**
    
    Go to *Settings → Business Portal* to start using the updated version.
2. **Restore the portal from the upgrade backup**
    
    You may restore the previous version from the upgrade backup and update it to ensure compatibility with the new system. Please note that the old portal is <span class="s1">**not compatible with PHP 8**</span>. For more information and assistance, contact support.

# New developers features in vtenext 26.04

In version 26.04 some additional changes have been made to the code to simplify the development of custom functionalities in certain areas.

<span>View the </span>[developer release notes](https://usermanual.vtenext.com/link/5751#bkmrk-developers).

# Mailscanner (Mail Converter)

Several Mailscanner classes are now exendable via `SDK::setClass`:

- MailScanner
- MailScannerAction
- MailScannerInfo
- MailScannerRule
- MailScannerSpam
- MailScannerMailBox
- MailScannerMailBoxZend

This allows for easier extensibility using our standard SDK.

Moreover, the list of actions (for example: Create Ticket, Update Ticket, ...) for each rule is now stored in the database and not hardcoded in various files. So adding a new a new action is much easier now:

```php
require_once 'modules/Settings/MailScanner/core/MailScannerAction.php';

// extend the class to implement custom actions
SDK::setClass('MailScannerAction', 'MailScannerActionCustom', 'modules/SDK/src/CUSTOMER/MailScannerActionCustom.php');

// add an action to create a task (custom module, or even Calendar)
MailScannerAction::addActionType('CREATE,Task,FROM', 'LBL_CREATE_MS_TASK', 'createTask');

// add the label
SDK::setLanguageEntries('Settings', 'LBL_CREATE_MS_TASK', ['it_it' => 'Crea compito', 'en_us' => 'Create task']);

```

And the extended class in `MailScannerActionCustom.php`:

```php
<?php

class MailScannerActionCustom extends MailScannerAction {
	
	/**
	 * Example of a custom mailscanner action
	 */
	function createTask($mailscanner, $mailrecord, $regexMatchInfo, $compare_parentid, $match_field) {
		
		$subject = $mailrecord->_subject;
		$description = $mailrecord->getBodyText();
		
		// create a record "CustomTask"
		$inst = \CRMEntity::getInstance('CustomTask');
		$inst->mode = '';
		
		// populate some fields
		$inst->column_fields['taskname'] = $subject;
		$inst->column_fields['description'] = $description;
		$inst->column_fields['date'] = date('Y-m-d');

		// save it
		$inst->save('CustomTask');
		
		// Associate any attachement of the email to the record
		$this->__SaveAttachements($mailrecord, $inst->modulename, $inst, $inst);
		
		// create the Messages record
		$this->__CreateNewEmail($mailrecord, $this->module, $inst);

		// return the record id, to signal the correct application of the action
		return $inst->id;
		
	}
	
}

```

Will produce a new action in the rule:

[![image.png](https://usermanual.vtenext.com/uploads/images/gallery/2026-03/scaled-1680-/NU1image.png)](https://usermanual.vtenext.com/uploads/images/gallery/2026-03/NU1image.png)

# Record conversion

In the latest version the handling of record conversion (converting a Quote to a SalesOrder, or a SalesOrder to an Invoice...) has been generalized and centralized in a single place.

The list of available conversion modes is now in the table `vte_convertmodes`, which is managed by the class `ConvertModesUtils` which gives the possibility to add new conversion modes between standard or custom modules.

For example, to add a new conversion mode from module Quote, to a custom module (with products) PreOrders:

```php
// instantiate the utils class
$CMU = ConvertModesUtils::getInstance();

// define the mapping for the fields, key is destination field, value is the source
$mapping = [
    // dest => source
	'vcf_1_1' => 'subject',
	'description' => 'description'
];

// add the convert mode
$CMU->addConvertMode(
	'quotetopreorder', 			// unique name for the mode, can be any string
	'Quotes',					// starting module
	'PreOrders',				// second module
	'record',					// the parameter in request that will pass the record id. by default "record"
	'quoteid',					// name of the field in the destination module with a reference to the first module, can be null
	true,						// if true, show the button in Quotes
	5,							// sequence of the button, among other conversion buttons
	'handleRecordConversion',	// the method to handle the conversion. You can specify a different one in the PreOrders class if you wish to do something different
	$mapping					// the fields mapping. products block is automatically copied
);
```

The button will appear automatically:

[![image.png](https://usermanual.vtenext.com/uploads/images/gallery/2026-03/scaled-1680-/goiimage.png)](https://usermanual.vtenext.com/uploads/images/gallery/2026-03/goiimage.png)

# CSV Import

One of the classes used by the standard CSV Import (the one available from any module's ListView) has been made extendable via `SDK::setClass` and highly refactored to split the main method into smaller ones, to ease modification of specific behaviours:

- `Import_Data_Controller`: This class now can be extended

For example, if you need to modify a value of a specific field before being saved to the database:

```php
// register the class
SDK::setClass('Import_Data_Controller', 'Import_Data_ControllerCustom', 'modules/SDK/src/CUSTOMER/ImportCustom.php');
```

And the class:

```php
<?php

require_once('modules/Import/controllers/Import_Data_Controller.php');

class Import_Data_ControllerCustom extends Import_Data_Controller {
	
	/**
	 * Transform a single field value to a format suitable to vte
	 */
	protected function transformFieldValue($fieldName, $fieldValue, $fieldInstance, $moduleMeta) {
		$fieldValue = parent::transformFieldValue($fieldName, $fieldValue, $fieldInstance, $moduleMeta);
		
		if ('Accounts' === $this->module && 'accountname' === $fieldName) {
			// append "IMPORTED" to the accountname
			$fieldValue .= ' - IMPORTED';
		}
		
		return $fieldValue;
	}
}

```

# VteSync (synchronizations)

VteSync connectors are now extendable via `SDK::setClass` so it's easier to add new functionalities or changing the field mapping.

For example, to extend the WooCommerce connector:

```php
// classes are namespaced
SDK::setClass('VteSyncLib\Connector\WooCommerce', 'WooCommerceCustom', 'modules/SDK/src/CUSTOMER/WooCommerceCustom.php');
```

The extended connector:

```php
<?php

// namespace the class, so it's easier to work with it
namespace VteSyncLib\Connector;

class WooCommerceCustom extends WooCommerce {

	// extend the constructor to alter the standard models
	public function __construct($config = array(), $storage = null) {
		parent::__construct($config, $storage);

		// in this case, the class is in the same folder, so no need to include it
		$this->classes['Accounts'] = array(
          'module' => 'Accounts',
          'commonClass' => 'VteSyncLib\Model\CommonRecord', 
          'class' => 'VteSyncLib\Connector\WooCommerce\Model\AccountCustom'
        );
	}
	
	// or it's possible to redefine any existing method
}

```

And the custom model:

```php
<?php

// include parent class
require_once(__DIR__.'/Account.php');

namespace VteSyncLib\Connector\WooCommerce\Model;

class AccountCustom extends Account {

	// here I redefine the mapping, removing or adding fields
    protected static $fieldMap = array(
        // WooCommerce => CommonRecord
        'email' => 'email',
        'username' => 'name',
        //'phone' => 'phone', // THIS IS COMMENTED

        // billing address
        'address' => 'billingstreet',
        'city' => 'billingcity',
        'postcode' => 'billingpostalcode',
        'state' => 'billingstate',
        'country' => 'billingcountry',
        'companybill' => 'companybill',
        'firstnamebill' => 'firstnamebill',
        'lastnamebill' => 'lastnamebill',
        // shipping address
        'address_shipping' => 'shippingstreet',
        'city_shipping' => 'shippingcity',
        'postcode_shipping' => 'shippingpostalcode',
        'state_shipping' => 'shippingstate',
        'country_shipping' => 'shippingcountry',
        'companyship' => 'companyship',
        'firstnameship' => 'firstnameship',
        'lastnameship' => 'lastnameship',
		
		'otherfield' => 'otherfield', // THIS IS CUSTOM!
		// you probably also need to alter the VTE models, to connect this mapping to vte's one
    );
    
	// this function is called to prepare the array to be sent to woocommerce
    public function toRawData($mode) {
		$raw = parent::toRawData($mode);
		
		// when sendind data to woo, hardcode this additional field:
		$row['somefield'] = 'somevalue';
		
		return $raw;
	}

}

```

# ListView extendability

The `ListViewController` class has been refactored to be more perfomant and easily extendable.

For example, now it's much easier to add new icons into the *Actions* column:

```php
SDK::setClass('ListViewController', 'ListViewControllerCustom', 'modules/SDK/src/CUSTOMER/ListViewControllerCustom.php');
```

And the class:

```php
<?php

require_once('include/ListView/ListViewController.php');

class ListViewControllerCustom extends ListViewController {
	
	/**
	 * Generate an array of strings to be concatenated and set as the "action" column
	 */
	public function generateActions($focus, $recordId, array $sqlrow = [], $navigationInfo = []) : array {
		$actionLinkInfo = parent::generateActions($focus, $recordId, $sqlrow, $navigationInfo);
		
		$module = $focus->modulename;
		if ($module === 'Leads') {
			// add an icon to each lead to open the record in the erp:
			$actionLinkInfo[] = "<a href=\"https://myerp.example.com/lead/$recordId\" target=\"_blank\"><i class=\"vteicon\"'>open_in_browser</i></a>";
		}
		
		return $actionLinkInfo;
	}
	
}

```

Resulting in:

[![image.png](https://usermanual.vtenext.com/uploads/images/gallery/2026-03/scaled-1680-/GMVimage.png)](https://usermanual.vtenext.com/uploads/images/gallery/2026-03/GMVimage.png)

# Webforms

The class WebformCapture is now extendable via `SDK::setClass` and the main method `captureNow` has been split up in several methods to facilitate modification of specific behaviours.

For example, to force the value of a field with a dynamic value upon Lead creation:

First we have to register the extension:

```php
SDK::setClass('WebformCapture', 'WebformCaptureCustom', 'modules/SDK/src/CUSTOMER/WebformCaptureCustom.php');
```

And then extend the prepareParameters method:

```php
<?php

require_once('modules/Webforms/WebformCapture.php');

class WebformCaptureCustom extends WebformCapture {
	
	/**
	 * Read data from request and populate the necessary fields
	 */
	protected function prepareParameters(array $request, Webforms_Model $webform) : array {
        // call the parent method to fill the standard values
		$parameters = parent::prepareParameters($request, $webform);

        // populate the field with a random value (makeRandomString is just an example here)
		$parameters['my_field_random'] = makeRandomString();
		
		return $parameters;
	}
}

```

# Code Review Skill

There is a **skill** available for use with **AI agents** built into your IDE that can help you adapt your code to the new release.  
More details available in the [documentation](https://usermanual.vtenext.com/books/developers/page/skill-review-sdk-files).

Once the analysis is complete, the files are checked and the original ones are overwritten, run `tools/check-requests` to check for any further references to the superglobal variable `$_REQUEST`. If none are found, the `config/request.config.override.php` file will be removed; otherwise, it will be updated, leaving only the new occurrences.

<p class="callout info">If you use a cloud agent, make sure your files do not contain sensitive data or credentials.</p>

# Using AI

<span>Tips on how to use LLM to help writing/refactoring vte code</span>

# Skill - Review SDK files

Skill to (try to) port all custom files to vtenext 26 and make them compatible with vtenext 26.

#### Install to Codex

Create the folder `review-sdk-26` in the skills folder of your IDE example: \[...\]/.codex/skills/ with the structure:

<div id="bkmrk-review-plugin-26%2F%E2%94%9C%E2%94%80%E2%94%80"><div>review-sdk-26/  
├── [SKILL.md](#bkmrk-skill.md)  
└── agents/  
 └── [openai.yaml](#bkmrk-openai.yaml)</div></div>##### SKILL.md

```markdown
---
name: review-sdk-26
description: Revise the SDK code to be compatible with VTENEXT version 26.*
---

# review-sdk-26

Review and update PHP files (>=7.x) to make them:
* Compatible with PHP 8.3
* Aligned with the new project guidelines
* More robust, typed, and modern

You are working on existing VTENEXT application code. DO NOT rewrite everything from scratch.
Keep the original logic, improving it only where necessary.
The project's root directory is specified in the config.inc.php file in `$root_directory`. If that directory is not accessible, use the directory containing the file config.inc.php.

## When to use

This skill should be used after the update to VTENEXT 26.* to ensure that all custom PHP files are compatible with PHP 8.3 and follow the new project guidelines.

## Input

* List of PHP files, if they are not provided execute `tools/skills/review-sdk-26/get_php_customizations.php` to get the list of files to scan and review. The list of files should be provided in batches of 10 if there are too many files to review at once. Do not analyze, process or propose changes to files that are not in the list obtained.

## Instructions

### 1. Prepare the environment

* Create folder modules/SDK/src/review_sdk_26, if already exists empty it
* If there are too many files, run the skill on 10 files at a time (batch)
* If you are running a batch, do not empty the folder
* Also copies unpatched files to the review_sdk_26 folder to maintain the same structure and allow manual review of all files
* If you find any hardcoded credentials or sensitive data while analyzing the code, stop everything and let me know to move this data to a separate configuration file


### 2. PHP 8.3 Compatibility Analysis

* Fix weak comparisons as described [here](https://usermanual.vtenext.com/books/developers/page/weak-comparisons)
* Add type casts where necessary to functions that require a string as parameter, for example `trim()` `strpos()` and `stripos()`
* Do not replace functions if the existing ones work on PHP 8.3
* For empty string comparisons, replace with the `empty()` function
* For comparisons with `$mode` or `$sdk_mode`, the creation mode is usually checked and the empty string comparison is fine. `=== ''`

#### example

old code:
```php
if ($recordid == '') {
```

new one:
```php
if (empty($recordid)) {
```

### 3. Refactoring

#### RequestHandler

* As described [here](https://usermanual.vtenext.com/books/developers/page/requesthandler), access to the superglobal variables `$_REQUEST`, `$_GET`, and `$_POST` is no longer allowed.
* Replace reading their values ​​with the `RH::r`, `RH::g`, and `RH::p` methods of the `RequestHandler` class found in include/utils/RequestHandler.php.
* When writing to superglobals, use the `RH::push_*` and `RH::pop_*` methods as described in the link above.
* Remove calls to `vtlib_purify()`; RequestHandler already handles them internally.

#### Database best practices and escaping

* Apply the new best practices as described [here](https://usermanual.vtenext.com/books/developers/page/escaping-rules)
* In particular, replace `query_result` with `query_result_no_html` and `fetchByAssoc(...)` with `fetchByAssoc(..., -1, false)`
* If HTML is generated in PHP and assigned to a smarty variable, use the `\Vtenext\Types\HtmlString` class to enclose the HTML string
* In custom reports the method `getSDKBlock()` should be return a `\Vtenext\Types\HtmlString` object instead of a string

#### Other substitutions
* Remove include/require of the file `Smarty_setup.php` that no longer exists
* Replace references to the `vtigerCRM_Smarty` class with `VteSmarty`
* Replace `session_start()` con `VteSession::start()`
* For reading and writing in session use the methods of the `VteSession` class found in `include/VteSession.php`

### 4. Coding style

* Apply the guidelines described [here](https://usermanual.vtenext.com/books/developers/page/coding-style)
* Add license header to the top of the file or update it if already present as described in the link above
* Remove the closing tag `?>` at the end of PHP files

### 5. Other examples
If you are modifying a method called get_dependents_list or get_related list and towards the end there is a piece of code similar to `$return_value['CUSTOM_BUTTON'] = $button;`, ensure that the $button variable is of type `\Vtenext\Types\HtmlString`, for example in this way:
```
$return_value['CUSTOM_BUTTON'] = new \Vtenext\Types\HtmlString($button);
```

### 6. Important Rules

* Do NOT change the functional behavior
* Do NOT introduce external dependencies
* Maintain backward compatibility if possible
* Clear code > "smart" code
* Do NOT directly modify the original files
* Do NOT return complete updated code
* Perform a final check on the files with `php -l` or with `php8.3 -l` if the former is not at version 8.3
* When you have finished analyzing all the files suggest to re-check the files copied into the folder

## Output

* List modified files
* Notify the user that modified files will be found in that folder so they can manually verify them and overwrite the originals after their review
```

##### openai.yaml

```yaml
interface:
  display_name: "Review SDK files"
  short_description: "Review the code of SDK customizations and make it compatible with vtenext 26"
  default_prompt: "Use $review-sdk-26 to review code of SDK customizations and make it compatible with vtenext 26."
```

#### Prompt

Select the skill by digit $review-sdk... and indicate the path to the folder.  
example:

```markdown
[$review-sdk-26]([...]/.codex/skills/review-sdk-26/SKILL.md) in the folder [vtenext folder].
The skill contains links to web pages with details and usage examples; read those pages.
```

# Skill - Review plugin

Skill to review the code of a specific folder (plugin) and make it compatible with vtenext 26.

#### Install to Codex

Create the folder `review-plugin-26` in the skills folder of your IDE example: \[...\]/.codex/skills/ with the structure:

<div id="bkmrk-review-plugin-26%2F%E2%94%9C%E2%94%80%E2%94%80"><div>review-plugin-26/  
├── [SKILL.md](#bkmrk-skill.md)  
└── agents/  
 └── [openai.yaml](#bkmrk-openai.yaml)</div></div>##### SKILL.md

```markdown
---
name: review-plugin-26
description: Revise the php code to be compatible with VTENEXT version 26.*
---

# review-plugin-26

Review and update PHP files (>=7.x) to make them:
* Compatible with PHP 8.3
* Aligned with the new project guidelines
* More robust, typed, and modern

You are working on a plugin for VTENEXT application which needs to be reviewed to the new version. DO NOT rewrite everything from scratch.
Keep the original logic, improving it only where necessary.

## Instructions

### 1. Prepare the environment

* Duplicate the folder
* If there are too many files, run the skill on 10 files at a time (batch)
* If you are running a batch, do not empty the folder
* Also copies unpatched files to the new folder to maintain the same structure and allow manual review of all files
* If you find any hardcoded credentials or sensitive data while analyzing the code, stop everything and let me know to move this data to a separate configuration file

### 2. PHP 8.3 Compatibility Analysis

* Fix weak comparisons as described [here](https://usermanual.vtenext.com/books/developers/page/weak-comparisons)
* Add type casts where necessary to functions that require a string as parameter, for example `trim()` `strpos()` and `stripos()`
* Do not replace functions if the existing ones work on PHP 8.3
* For empty string comparisons, replace with the `empty()` function
* For comparisons with `$mode` or `$sdk_mode`, the creation mode is usually checked and the empty string comparison is fine. `=== ''`

#### example

old code:
```php
if ($recordid == '') {
```

new one:
```php
if (empty($recordid)) {
```

### 3. Refactoring

#### RequestHandler

* As described [here](https://usermanual.vtenext.com/books/developers/page/requesthandler), access to the superglobal variables `$_REQUEST`, `$_GET`, and `$_POST` is no longer allowed.
* Replace reading their values ​​with the `RH::r`, `RH::g`, and `RH::p` methods of the `RequestHandler` class found in include/utils/RequestHandler.php.
* When writing to superglobals, use the `RH::push_*` and `RH::pop_*` methods as described in the link above.
* Remove calls to `vtlib_purify()`; RequestHandler already handles them internally.

#### Database best practices and escaping

* Apply the new best practices as described [here](https://usermanual.vtenext.com/books/developers/page/escaping-rules)
* In particular, replace `query_result` with `query_result_no_html` and `fetchByAssoc(...)` with `fetchByAssoc(..., -1, false)`
* If HTML is generated in PHP and assigned to a smarty variable, use the `\Vtenext\Types\HtmlString` class to enclose the HTML string
* In custom reports the method `getSDKBlock()` should be return a `\Vtenext\Types\HtmlString` object instead of a string

#### Other substitutions
* Remove include/require of the file `Smarty_setup.php` that no longer exists
* Replace references to the `vtigerCRM_Smarty` class with `VteSmarty`
* Replace `session_start()` con `VteSession::start()`
* For reading and writing in session use the methods of the `VteSession` class found in `include/VteSession.php`

### 4. Coding style

* Apply the guidelines described [here](https://usermanual.vtenext.com/books/developers/page/coding-style)
* Add license header to the top of the file or update it if already present as described in the link above
* Remove the closing tag `?>` at the end of PHP files

### 5. Other examples
If you are modifying a method called get_dependents_list or get_related list and towards the end there is a piece of code similar to `$return_value['CUSTOM_BUTTON'] = $button;`, ensure that the $button variable is of type `\Vtenext\Types\HtmlString`, for example in this way:
```
$return_value['CUSTOM_BUTTON'] = new \Vtenext\Types\HtmlString($button);
```

### 6. Important Rules

* Do NOT change the functional behavior
* Do NOT introduce external dependencies
* Maintain backward compatibility if possible
* Clear code > "smart" code
* Do NOT directly modify the original files
* Do NOT return complete updated code
* Perform a final check on the files with `php -l` or with `php8.3 -l` if the former is not at version 8.3
* When you have finished analyzing all the files suggest to re-check the files copied into the folder

## Output

* List modified files
* Notify the user that modified files will be found in that folder so they can manually verify them and overwrite the originals after their review
```

##### openai.yaml

```yaml
interface:
  display_name: "Review Plugin"
  short_description: "Review the code of a specific folder (plugin) and make it compatible with vtenext 26"
  default_prompt: "Use $review-plugin-26 to review code of a specific folder (plugin) and make it compatible with vtenext 26."
```

#### Prompt

Select the skill by digit $review-plugin... and indicate the path to the folder.  
example:

```markdown
[$review-plugin-26]([...]/.codex/skills/review-plugin-26/SKILL.md) on the folder [your folder].
The skill contains links to web pages with details and usage examples; read those pages.
```

# Help us improve

**Developer documentation is constantly evolving!**

We are constantly committed to improving the quality and completeness of technical documentation to make your work simpler and more efficient. Your feedback is essential to us.

#### **How you can help us**

There are several ways you can contribute to improving the documentation and code:

- **Report missing or incomplete documentation** - If you can't find the information you're looking for
- **Request clarifications** - If a section is unclear or ambiguous
- **Propose improvements** - Do you have ideas on how to make the documentation more useful?
- **Suggest code optimizations** - Have you found a better way to implement something?
- **Report errors or issues** - Have you found a bug or inconsistency?
- **Share best practices** - Have you developed interesting patterns or solutions?

#### **Contact us via email**

Send your requests, suggestions, or questions to:

**Email:** <support@vtenext.com>

In the email subject line, specify:

- `[DOC]` for documentation requests
- `[BUG]` for bug reports
- `[SUGG]` for new feature suggestions
- `[HELP]` for assistance requests

Your contribution makes a difference! Every report, suggestion, or idea helps us create better documentation for developers.

**Thank you for your support and collaboration!**