Add first valid expression ruler. Still not linked to scheduleds.
This commit is contained in:
parent
f59f7bd757
commit
f4bbfa0152
|
@ -3,6 +3,7 @@
|
|||
"ingenerator/tokenista": "^1.1",
|
||||
"ajani/flash-message": "^2.0",
|
||||
"giggsey/libphonenumber-for-php": "^8.10",
|
||||
"twig/twig": "^3.0"
|
||||
"twig/twig": "^3.0",
|
||||
"symfony/expression-language": "^5.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "645ff432da0c81453a7f3f1df765195d",
|
||||
"content-hash": "5c471b1b5ba3135917d74c53558a6259",
|
||||
"packages": [
|
||||
{
|
||||
"name": "ajani/flash-message",
|
||||
|
@ -212,6 +212,336 @@
|
|||
],
|
||||
"time": "2018-02-26T14:16:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/cache",
|
||||
"version": "1.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/cache.git",
|
||||
"reference": "d11b50ad223250cf17b86e38383413f5a6764bf8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8",
|
||||
"reference": "d11b50ad223250cf17b86e38383413f5a6764bf8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Cache\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for caching libraries",
|
||||
"keywords": [
|
||||
"cache",
|
||||
"psr",
|
||||
"psr-6"
|
||||
],
|
||||
"time": "2016-08-06T20:24:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/container",
|
||||
"version": "1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/container.git",
|
||||
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
|
||||
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Container\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common Container Interface (PHP FIG PSR-11)",
|
||||
"homepage": "https://github.com/php-fig/container",
|
||||
"keywords": [
|
||||
"PSR-11",
|
||||
"container",
|
||||
"container-interface",
|
||||
"container-interop",
|
||||
"psr"
|
||||
],
|
||||
"time": "2017-02-14T16:28:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/log",
|
||||
"version": "1.1.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/log.git",
|
||||
"reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/log/zipball/446d54b4cb6bf489fc9d75f55843658e6f25d801",
|
||||
"reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Log\\": "Psr/Log/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for logging libraries",
|
||||
"homepage": "https://github.com/php-fig/log",
|
||||
"keywords": [
|
||||
"log",
|
||||
"psr",
|
||||
"psr-3"
|
||||
],
|
||||
"time": "2019-11-01T11:05:21+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/cache",
|
||||
"version": "v5.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/cache.git",
|
||||
"reference": "32bd1f9be1684bba768a6834037706cf0950843c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/cache/zipball/32bd1f9be1684bba768a6834037706cf0950843c",
|
||||
"reference": "32bd1f9be1684bba768a6834037706cf0950843c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2.5",
|
||||
"psr/cache": "~1.0",
|
||||
"psr/log": "~1.0",
|
||||
"symfony/cache-contracts": "^1.1.7|^2",
|
||||
"symfony/service-contracts": "^1.1|^2",
|
||||
"symfony/var-exporter": "^4.4|^5.0"
|
||||
},
|
||||
"conflict": {
|
||||
"doctrine/dbal": "<2.5",
|
||||
"symfony/dependency-injection": "<4.4",
|
||||
"symfony/http-kernel": "<4.4",
|
||||
"symfony/var-dumper": "<4.4"
|
||||
},
|
||||
"provide": {
|
||||
"psr/cache-implementation": "1.0",
|
||||
"psr/simple-cache-implementation": "1.0",
|
||||
"symfony/cache-implementation": "1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"cache/integration-tests": "dev-master",
|
||||
"doctrine/cache": "~1.6",
|
||||
"doctrine/dbal": "~2.5",
|
||||
"predis/predis": "~1.1",
|
||||
"psr/simple-cache": "^1.0",
|
||||
"symfony/config": "^4.4|^5.0",
|
||||
"symfony/dependency-injection": "^4.4|^5.0",
|
||||
"symfony/var-dumper": "^4.4|^5.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "5.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\Cache\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony Cache component with PSR-6, PSR-16, and tags",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"caching",
|
||||
"psr6"
|
||||
],
|
||||
"time": "2019-11-18T17:27:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/cache-contracts",
|
||||
"version": "v2.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/cache-contracts.git",
|
||||
"reference": "a91281de82119a7a07481b892f709d88da592cd3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/cache-contracts/zipball/a91281de82119a7a07481b892f709d88da592cd3",
|
||||
"reference": "a91281de82119a7a07481b892f709d88da592cd3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2.9",
|
||||
"psr/cache": "^1.0"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/cache-implementation": ""
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Contracts\\Cache\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Generic abstractions related to caching",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"abstractions",
|
||||
"contracts",
|
||||
"decoupling",
|
||||
"interfaces",
|
||||
"interoperability",
|
||||
"standards"
|
||||
],
|
||||
"time": "2019-11-09T09:18:34+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/expression-language",
|
||||
"version": "v5.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/expression-language.git",
|
||||
"reference": "121ece2d8c52777db0809525526ba9875f5a483a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/expression-language/zipball/121ece2d8c52777db0809525526ba9875f5a483a",
|
||||
"reference": "121ece2d8c52777db0809525526ba9875f5a483a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2.5",
|
||||
"symfony/cache": "^4.4|^5.0",
|
||||
"symfony/service-contracts": "^1.1|^2"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "5.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\ExpressionLanguage\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony ExpressionLanguage Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2019-11-18T17:27:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.12.0",
|
||||
|
@ -329,6 +659,124 @@
|
|||
],
|
||||
"time": "2019-08-06T08:03:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/service-contracts",
|
||||
"version": "v2.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/service-contracts.git",
|
||||
"reference": "9d99e1556417bf227a62e14856d630672bf10eaf"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/9d99e1556417bf227a62e14856d630672bf10eaf",
|
||||
"reference": "9d99e1556417bf227a62e14856d630672bf10eaf",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2.9",
|
||||
"psr/container": "^1.0"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/service-implementation": ""
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Contracts\\Service\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Generic abstractions related to writing services",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"abstractions",
|
||||
"contracts",
|
||||
"decoupling",
|
||||
"interfaces",
|
||||
"interoperability",
|
||||
"standards"
|
||||
],
|
||||
"time": "2019-11-09T09:18:34+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/var-exporter",
|
||||
"version": "v5.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/var-exporter.git",
|
||||
"reference": "e2f1eeb12edacf744c4b359a859204578fdf8549"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/var-exporter/zipball/e2f1eeb12edacf744c4b359a859204578fdf8549",
|
||||
"reference": "e2f1eeb12edacf744c4b359a859204578fdf8549",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/var-dumper": "^4.4|^5.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "5.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\VarExporter\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "A blend of var_export() + serialize() to turn any serializable data structure to plain PHP code",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"clone",
|
||||
"construct",
|
||||
"export",
|
||||
"hydrate",
|
||||
"instantiate",
|
||||
"serialize"
|
||||
],
|
||||
"time": "2019-11-18T17:27:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "twig/twig",
|
||||
"version": "v3.0.0",
|
||||
|
|
|
@ -41,7 +41,9 @@ namespace controllers\internals;
|
|||
'condition' => $condition,
|
||||
];
|
||||
|
||||
if (!$this->validate_condition($condition))
|
||||
$internal_ruler = new Ruler();
|
||||
$valid_condition = $internal_ruler->validate_condition($condition, ['contact' => (object) ['datas' => (object) null]]);
|
||||
if (!$valid_condition)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -73,8 +75,10 @@ namespace controllers\internals;
|
|||
'name' => $name,
|
||||
'condition' => $condition,
|
||||
];
|
||||
|
||||
if (!$this->validate_condition($condition))
|
||||
|
||||
$internal_ruler = new Ruler();
|
||||
$valid_condition = $internal_ruler->validate_condition($condition, ['contact' => (object) ['datas' => (object) null]]);
|
||||
if (!$valid_condition)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -102,11 +106,39 @@ namespace controllers\internals;
|
|||
|
||||
|
||||
/**
|
||||
* Verify if a condition string is valid (i.e we can parse it without error)
|
||||
* Gets the user's contacts that respects a condition
|
||||
* @param int $id_user : User id
|
||||
* @param string $condition : Condition string to verify
|
||||
* @return bool
|
||||
*/
|
||||
public function validate_condition (string $condition) : bool
|
||||
public function get_contacts_for_condition_and_user (int $id_user, string $condition) : bool
|
||||
{
|
||||
$internal_contacts = new Contacts($this->bdd);
|
||||
$contacts = $internal_contacts->gets_for_user($id_user);
|
||||
|
||||
$ruler = new Ruler();
|
||||
|
||||
foreach ($contacts as $key => $contact)
|
||||
{
|
||||
if ($contact['datas'] != null)
|
||||
{
|
||||
$contact['datas'] = json_decode($contact['datas']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$contact['datas'] = new \stdClass();
|
||||
}
|
||||
|
||||
$contact = (object) $contact;
|
||||
|
||||
$datas = ['contact' => $contact];
|
||||
$is_valid = $ruler->evaluate_condition($condition, $datas);
|
||||
if (!$is_valid)
|
||||
{
|
||||
unset($contacts[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return $contacts;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ namespace controllers\internals;
|
|||
* @param string $datas : Contact datas
|
||||
* @return mixed bool|int : False if cannot create contact, id of the new contact else
|
||||
*/
|
||||
public function create($id_user, $number, $name, $datas)
|
||||
public function create($id_user, $number, $name, ?string $datas = null)
|
||||
{
|
||||
$contact = [
|
||||
'id_user' => $id_user,
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace controllers\internals;
|
||||
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionFunction;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
|
||||
|
||||
class ExpressionProvider implements ExpressionFunctionProviderInterface
|
||||
{
|
||||
public function getFunctions()
|
||||
{
|
||||
return [
|
||||
ExpressionFunction::fromPhp('is_null', 'exists'),
|
||||
ExpressionFunction::fromPhp('mb_strtolower', 'lower'),
|
||||
ExpressionFunction::fromPhp('mb_strtoupper', 'upper'),
|
||||
ExpressionFunction::fromPhp('mb_substr', 'substr'),
|
||||
ExpressionFunction::fromPhp('abs', 'abs'),
|
||||
ExpressionFunction::fromPhp('strtotime', 'date'),
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of RaspiSMS.
|
||||
*
|
||||
* (c) Pierre-Lin Bonnemaison <plebwebsas@gmail.com>
|
||||
*
|
||||
* This source file is subject to the GPL-3.0 license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace controllers\internals;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionFunction;
|
||||
|
||||
/**
|
||||
* Class to analyse rules used by conditional groups
|
||||
*/
|
||||
class Ruler extends \descartes\InternalController
|
||||
{
|
||||
private $expression_language;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct ()
|
||||
{
|
||||
$this->expression_language = new ExpressionLanguage();
|
||||
|
||||
//Add custom functions
|
||||
$this->expression_language->registerProvider(new ExpressionProvider());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Verify if a condition is valid. i.e we can evaluate it without error.
|
||||
* @param string $condition : The condition to evaluate.
|
||||
* @param array $datas : The datas to made available to condition
|
||||
* @return bool : false if invalid, true else
|
||||
*/
|
||||
public function validate_condition (string $condition, array $datas = []) : bool
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->expression_language->evaluate($condition, $datas);
|
||||
return true;
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
echo "Error : ";
|
||||
echo $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Evaluate a condition
|
||||
* @param string $condition : The condition to evaluate.
|
||||
* @param array $datas : The datas to made available to condition
|
||||
* @return ?bool : false if invalid, true else, null only on error
|
||||
*/
|
||||
public function evaluate_condition (string $condition, array $datas = []) : ?bool
|
||||
{
|
||||
try
|
||||
{
|
||||
$result = $this->expression_language->evaluate($condition, $datas);
|
||||
return (bool) $result;
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,181 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of RaspiSMS.
|
||||
*
|
||||
* (c) Pierre-Lin Bonnemaison <plebwebsas@gmail.com>
|
||||
*
|
||||
* This source file is subject to the GPL-3.0 license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace controllers\publics;
|
||||
|
||||
/**
|
||||
* Page des groups.
|
||||
*/
|
||||
class ConditionalGroup extends \descartes\Controller
|
||||
{
|
||||
private $internal_conditional_group;
|
||||
private $internal_contact;
|
||||
private $internal_ruler;
|
||||
private $internal_event;
|
||||
|
||||
/**
|
||||
* Cette fonction est appelée avant toute les autres :
|
||||
* Elle vérifie que l'utilisateur est bien connecté.
|
||||
*
|
||||
* @return void;
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$bdd = \descartes\Model::_connect(DATABASE_HOST, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD);
|
||||
|
||||
$this->internal_conditional_group = new \controllers\internals\ConditionalGroup($bdd);
|
||||
$this->internal_contact = new \controllers\internals\Contact($bdd);
|
||||
$this->internal_event = new \controllers\internals\Event($bdd);
|
||||
$this->internal_ruler = new \controllers\internals\Ruler($bdd);
|
||||
|
||||
\controllers\internals\Tool::verifyconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all conditionnals groups for administration
|
||||
*
|
||||
* @param mixed $page
|
||||
*/
|
||||
public function list($page = 0)
|
||||
{
|
||||
$page = (int) $page;
|
||||
|
||||
|
||||
$groups = $this->internal_conditional_group->list_for_user($_SESSION['user']['id'], 25, $page);
|
||||
$this->render('conditional_group/list', ['groups' => $groups]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cette fonction va supprimer une liste de groups.
|
||||
*
|
||||
* @param array int $_GET['ids'] : Les id des groups à supprimer
|
||||
* @param mixed $csrf
|
||||
*
|
||||
* @return boolean;
|
||||
*/
|
||||
public function delete($csrf)
|
||||
{
|
||||
if (!$this->verify_csrf($csrf))
|
||||
{
|
||||
\FlashMessage\FlashMessage::push('danger', 'Jeton CSRF invalid !');
|
||||
|
||||
return $this->redirect(\descartes\Router::url('ConditionalGroup', 'list'));
|
||||
}
|
||||
|
||||
$ids = $_GET['ids'] ?? [];
|
||||
foreach ($ids as $id)
|
||||
{
|
||||
$this->internal_conditional_group->delete_for_user($_SESSION['user']['id'], $id);
|
||||
}
|
||||
|
||||
return $this->redirect(\descartes\Router::url('ConditionalGroup', 'list'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Cette fonction retourne la page d'ajout d'un group.
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
$this->render('conditional_group/add');
|
||||
}
|
||||
|
||||
/**
|
||||
* Cette fonction retourne la page d'édition des groups.
|
||||
*
|
||||
* @param int... $ids : Les id des groups à supprimer
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
$ids = $_GET['ids'] ?? [];
|
||||
|
||||
$groups = $this->internal_conditional_group->gets_in_for_user($_SESSION['user']['id'], $ids);
|
||||
|
||||
$this->render('conditional_group/edit', [
|
||||
'groups' => $groups,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cette fonction insert un nouveau group.
|
||||
*
|
||||
* @param $csrf : Le jeton CSRF
|
||||
* @param string $_POST['name'] : Le nom du group
|
||||
* @param array $_POST['condition'] : The condition to used
|
||||
*/
|
||||
public function create($csrf)
|
||||
{
|
||||
if (!$this->verify_csrf($csrf))
|
||||
{
|
||||
\FlashMessage\FlashMessage::push('danger', 'Jeton CSRF invalid !');
|
||||
|
||||
return $this->redirect(\descartes\Router::url('ConditionalGroup', 'add'));
|
||||
}
|
||||
|
||||
$name = $_POST['name'] ?? false;
|
||||
$condition = $_POST['condition'] ?? false;
|
||||
|
||||
if (!$name || !$condition)
|
||||
{
|
||||
\FlashMessage\FlashMessage::push('danger', 'Des champs sont manquants !');
|
||||
|
||||
return $this->redirect(\descartes\Router::url('ConditionalGroup', 'add'));
|
||||
}
|
||||
|
||||
$id_group = $this->internal_conditional_group->create($_SESSION['user']['id'], $name, $condition);
|
||||
if (!$id_group)
|
||||
{
|
||||
\FlashMessage\FlashMessage::push('danger', 'Impossible de créer ce groupe.');
|
||||
|
||||
return $this->redirect(\descartes\Router::url('ConditionalGroup', 'add'));
|
||||
}
|
||||
|
||||
\FlashMessage\FlashMessage::push('success', 'Le groupe a bien été créé.');
|
||||
|
||||
return $this->redirect(\descartes\Router::url('ConditionalGroup', 'list'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Cette fonction met à jour une group.
|
||||
*
|
||||
* @param $csrf : Le jeton CSRF
|
||||
* @param array $_POST['groups'] : Un tableau des groups avec leur nouvelle valeurs & une entrée 'contacts_id' avec les ids des contacts pour chaque group
|
||||
*
|
||||
* @return boolean;
|
||||
*/
|
||||
public function update($csrf)
|
||||
{
|
||||
if (!$this->verify_csrf($csrf))
|
||||
{
|
||||
\FlashMessage\FlashMessage::push('danger', 'Jeton CSRF invalid !');
|
||||
|
||||
return $this->redirect(\descartes\Router::url('ConditionalGroup', 'list'));
|
||||
}
|
||||
|
||||
$groups = $_POST['groups'] ?? [];
|
||||
|
||||
$nb_groups_update = 0;
|
||||
foreach ($groups as $id => $group)
|
||||
{
|
||||
$nb_groups_update += (int) $this->internal_conditional_group->update_for_user($_SESSION['user']['id'], $id, $group['name'], $group['condition']);
|
||||
}
|
||||
|
||||
if ($nb_groups_update !== \count($groups))
|
||||
{
|
||||
\FlashMessage\FlashMessage::push('danger', 'Certains groupes n\'ont pas pu êtres mis à jour.');
|
||||
|
||||
return $this->redirect(\descartes\Router::url('ConditionalGroup', 'list'));
|
||||
}
|
||||
|
||||
\FlashMessage\FlashMessage::push('success', 'Tous les groupes ont été modifiés avec succès.');
|
||||
|
||||
return $this->redirect(\descartes\Router::url('ConditionalGroup', 'list'));
|
||||
}
|
||||
}
|
|
@ -131,7 +131,7 @@ namespace controllers\publics;
|
|||
$name = $_POST['name'] ?? false;
|
||||
$number = $_POST['number'] ?? false;
|
||||
$id_user = $_SESSION['user']['id'];
|
||||
$datas = empty($_POST['datas']) ? null : $_POST['datas'];
|
||||
$datas = $_POST['datas'] ?? [];
|
||||
|
||||
if (!$name || !$number)
|
||||
{
|
||||
|
@ -162,7 +162,11 @@ namespace controllers\publics;
|
|||
$key = mb_ereg_replace('[\W]', '', $key);
|
||||
$clean_datas[$key] = (string) $value;
|
||||
}
|
||||
|
||||
}
|
||||
$clean_datas = $clean_datas ?: null;
|
||||
|
||||
if ($clean_datas)
|
||||
{
|
||||
$clean_datas = json_encode($clean_datas);
|
||||
}
|
||||
|
||||
|
@ -206,7 +210,7 @@ namespace controllers\publics;
|
|||
$name = $contact['name'] ?? false;
|
||||
$number = $contact['number'] ?? false;
|
||||
$id_user = $_SESSION['user']['id'];
|
||||
$datas = empty($contact['datas']) ? null : $contact['datas'];
|
||||
$datas = $contact['datas'] ?? null;
|
||||
|
||||
if (!$name || !$number)
|
||||
{
|
||||
|
@ -218,7 +222,7 @@ namespace controllers\publics;
|
|||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
$clean_datas = null;
|
||||
if ($datas)
|
||||
{
|
||||
|
@ -233,9 +237,14 @@ namespace controllers\publics;
|
|||
$key = mb_ereg_replace('[\W]', '', $key);
|
||||
$clean_datas[$key] = (string) $value;
|
||||
}
|
||||
|
||||
}
|
||||
$clean_datas = $clean_datas ?: null;
|
||||
|
||||
if ($clean_datas)
|
||||
{
|
||||
$clean_datas = json_encode($clean_datas);
|
||||
}
|
||||
|
||||
|
||||
$nb_contacts_update += (int) $this->internal_contact->update_for_user($id_user, $id_contact, $number, $name, $clean_datas);
|
||||
}
|
||||
|
|
|
@ -32,7 +32,6 @@ CREATE TABLE IF NOT EXISTS scheduled
|
|||
origin VARCHAR(25) DEFAULT NULL,
|
||||
at DATETIME NOT NULL,
|
||||
text VARCHAR(1000) NOT NULL,
|
||||
condition VARCHAR(1000) DEFAULT NULL,
|
||||
flash BOOLEAN NOT NULL DEFAULT 0,
|
||||
FOREIGN KEY (id_user) REFERENCES user (id) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
PRIMARY KEY (id)
|
||||
|
@ -89,7 +88,7 @@ CREATE TABLE IF NOT EXISTS `conditional_group`
|
|||
id INT NOT NULL AUTO_INCREMENT,
|
||||
id_user INT NOT NULL,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
condition TEXT NOT NULL,
|
||||
`condition` TEXT NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
FOREIGN KEY (id_user) REFERENCES user (id) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
UNIQUE (id_user, name)
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
namespace models;
|
||||
|
||||
class CondiditionalGroup extends StandardModel
|
||||
class ConditionalGroup extends StandardModel
|
||||
{
|
||||
/**
|
||||
* Return table name
|
||||
|
|
12
routes.php
12
routes.php
|
@ -77,6 +77,18 @@
|
|||
'update' => '/group/update/{csrf}/',
|
||||
'json_list' => '/groups.json/',
|
||||
],
|
||||
|
||||
'ConditionalGroup' => [
|
||||
'list' => [
|
||||
'/conditional_group/',
|
||||
'/conditional_group/p/{page}/',
|
||||
],
|
||||
'add' => '/conditional_group/add/',
|
||||
'create' => '/conditional_group/create/{csrf}/',
|
||||
'delete' => '/conditional_group/delete/{csrf}/',
|
||||
'edit' => '/conditional_group/edit/',
|
||||
'update' => '/conditional_group/update/{csrf}/',
|
||||
],
|
||||
|
||||
'Received' => [
|
||||
'list' => [
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
//Template dashboard
|
||||
|
||||
$this->render('incs/head', ['title' => 'Groupes Conditionnels - Add'])
|
||||
?>
|
||||
<div id="wrapper">
|
||||
<?php
|
||||
$this->render('incs/nav', ['page' => 'conditional_groupes'])
|
||||
?>
|
||||
<div id="page-wrapper">
|
||||
<div class="container-fluid">
|
||||
<!-- Page Heading -->
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<h1 class="page-header">
|
||||
Nouveau groupe conditionnel
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li>
|
||||
<i class="fa fa-dashboard"></i> <a href="<?php echo \descartes\Router::url('Dashboard', 'show'); ?>">Dashboard</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-random"></i> <a href="<?php echo \descartes\Router::url('ConditionalGroup', 'list'); ?>">Groupes Conditionnels</a>
|
||||
</li>
|
||||
<li class="active">
|
||||
<i class="fa fa-plus"></i> Nouveau
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><i class="fa fa-random fa-fw"></i> Ajout d'un groupe conditionnel</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form action="<?php echo \descartes\Router::url('ConditionalGroup', 'create', ['csrf' => $_SESSION['csrf']]);?>" method="POST">
|
||||
<div class="form-group">
|
||||
<label>Nom du groupe conditionnel</label>
|
||||
<div class="form-group input-group">
|
||||
<span class="input-group-addon"><span class="fa fa-users"></span></span>
|
||||
<input name="name" class="form-control" type="text" placeholder="Nom groupe" autofocus required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Condition</label>
|
||||
<p class="italic small help">
|
||||
Les conditions vous permettent de définir dynamiquement les contacts qui appartiennent au groupe en utilisant leurs données additionnelles. Pour plus d'informations consultez la documentation relative à <a href="#">l'utilisation des groupes conditionnels.</a>
|
||||
</p>
|
||||
<input class="form-control" name="condition" placeholder="Ex : contact.datas.gender == 'male'"/>
|
||||
</div>
|
||||
<a class="btn btn-danger" href="<?php echo \descartes\Router::url('ConditionalGroup', 'list'); ?>">Annuler</a>
|
||||
<input type="submit" class="btn btn-success" value="Enregistrer le groupe" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
$this->render('incs/footer');
|
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
//Template dashboard
|
||||
|
||||
$this->render('incs/head', ['title' => 'Groupes Conditionnels - Edit'])
|
||||
?>
|
||||
<div id="wrapper">
|
||||
<?php
|
||||
$this->render('incs/nav', ['page' => 'conditional_groupes'])
|
||||
?>
|
||||
<div id="page-wrapper">
|
||||
<div class="container-fluid">
|
||||
<!-- Page Heading -->
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<h1 class="page-header">
|
||||
Modification groupes conditionnels
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li>
|
||||
<i class="fa fa-dashboard"></i> <a href="<?php echo \descartes\Router::url('Dashboard', 'show'); ?>">Dashboard</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-random"></i> <a href="<?php echo \descartes\Router::url('ConditionalGroup', 'list'); ?>">Groupes Conditionnels</a>
|
||||
</li>
|
||||
<li class="active">
|
||||
<i class="fa fa-edit"></i> Modifier
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><i class="fa fa-edit fa-fw"></i> Modification des groupes conditionnels</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form action="<?php echo \descartes\Router::url('ConditionalGroup', 'update', ['csrf' => $_SESSION['csrf']]);?>" method="POST">
|
||||
<?php foreach ($groups as $group) { ?>
|
||||
<input name="groups[<?php $this->s($group['id']); ?>][group][id]" type="hidden" value="<?php $this->s($group['id']); ?>">
|
||||
<div class="form-group">
|
||||
<label>Nom du groupe conditionnel</label>
|
||||
<div class="form-group input-group">
|
||||
<span class="input-group-addon"><span class="fa fa-user"></span></span>
|
||||
<input name="groups[<?php $this->s($group['id']); ?>][name]" class="form-control" type="text" placeholder="Nom groupe" autofocus required value="<?php $this->s($group['name']); ?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Condition</label>
|
||||
<p class="italic small help">
|
||||
Les conditions vous permettent de définir dynamiquement les contacts qui appartiennent au groupe en utilisant leurs données additionnelles. Pour plus d'informations consultez la documentation relative à <a href="#">l'utilisation des groupes conditionnels.</a>
|
||||
</p>
|
||||
<input class="form-control" name="groups[<?php $this->s($group['id']); ?>][condition]" value="<?php $this->s($group['condition']); ?>"/>
|
||||
</div>
|
||||
<hr/>
|
||||
<?php } ?>
|
||||
<a class="btn btn-danger" href="<?php echo \descartes\Router::url('ConditionalGroup', 'list'); ?>">Annuler</a>
|
||||
<input type="submit" class="btn btn-success" value="Enregistrer le groupe" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
jQuery(document).ready(function()
|
||||
{
|
||||
jQuery('.add-contacts').each(function()
|
||||
{
|
||||
jQuery(this).magicSuggest({
|
||||
data: '<?php echo \descartes\Router::url('Contact', 'json_list'); ?>',
|
||||
valueField: 'id',
|
||||
displayField: 'name',
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
$this->render('incs/footer');
|
|
@ -0,0 +1,101 @@
|
|||
<?php
|
||||
//Template dashboard
|
||||
|
||||
$this->render('incs/head', ['title' => 'ConditionalGroupes Conditionnels - Show All'])
|
||||
?>
|
||||
<div id="wrapper">
|
||||
<?php
|
||||
$this->render('incs/nav', ['page' => 'conditional_groupes'])
|
||||
?>
|
||||
<div id="page-wrapper">
|
||||
<div class="container-fluid">
|
||||
<!-- Page Heading -->
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<h1 class="page-header">
|
||||
Dashboard <small>Groupes Conditionnels</small>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li>
|
||||
<i class="fa fa-dashboard"></i> <a href="<?php echo \descartes\Router::url('Dashboard', 'show'); ?>">Dashboard</a>
|
||||
</li>
|
||||
<li class="active">
|
||||
<i class="fa fa-random"></i> Groupes Conditionnels
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><i class="fa fa-random fa-fw"></i> Liste des groupes conditionnels</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form method="GET">
|
||||
<?php if (!$groups) { ?>
|
||||
<p>Aucun groupe n'a été formé pour le moment.</p>
|
||||
<?php } else { ?>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-hover table-striped" id="table-groupes">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Nom</th>
|
||||
<th>Condition</th>
|
||||
<th style="width:5%;">Sélectionner</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($groups as $group) { ?>
|
||||
<tr>
|
||||
<td><?php $this->s($group['id']); ?></td>
|
||||
<td><?php $this->s($group['name']); ?></td>
|
||||
<td><?php $this->s($group['condition']); ?></td>
|
||||
<td><input type="checkbox" name="ids[]" value="<?php $this->s($group['id']); ?>"></td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php } ?>
|
||||
<div>
|
||||
<div class="col-xs-6 no-padding">
|
||||
<a class="btn btn-success" href="<?php echo \descartes\Router::url('ConditionalGroup', 'add'); ?>"><span class="fa fa-plus"></span> Ajouter un groupe conditionnel</a>
|
||||
</div>
|
||||
<?php if ($groups) { ?>
|
||||
<div class="text-right col-xs-6 no-padding">
|
||||
<strong>Action pour la séléction :</strong>
|
||||
<button class="btn btn-default" type="submit" formaction="<?php echo \descartes\Router::url('ConditionalGroup', 'edit'); ?>"><span class="fa fa-edit"></span> Modifier</button>
|
||||
<button class="btn btn-default" type="submit" formaction="<?php echo \descartes\Router::url('ConditionalGroup', 'delete', ['csrf' => $_SESSION['csrf']]); ?>"><span class="fa fa-trash-o"></span> Supprimer</button>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
jQuery(document).ready(function ()
|
||||
{
|
||||
jQuery('.action-dropdown a').on('click', function (e)
|
||||
{
|
||||
e.preventDefault();
|
||||
var destination = jQuery(this).parents('.action-dropdown').attr('destination');
|
||||
var url = jQuery(this).attr('href');
|
||||
jQuery(destination).find('input:checked').each(function ()
|
||||
{
|
||||
url += '/' + jQuery(this).val();
|
||||
});
|
||||
window.location = url;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
$this->render('incs/footer');
|
|
@ -19,7 +19,7 @@
|
|||
</script>
|
||||
<?php } ?>
|
||||
|
||||
<?php if (!$_SESSION['user']['settings']['display_help']) { ?>
|
||||
<?php if (!$_SESSION['user']['settings']['display_help'] ?? false) { ?>
|
||||
<style>.help {display: none;}</style>
|
||||
<?php } ?>
|
||||
|
||||
|
|
|
@ -55,13 +55,16 @@
|
|||
</li>
|
||||
<li>
|
||||
<a href="javascript:;" data-toggle="collapse" data-target="#repertoire"><i class="fa fa-fw fa-book"></i> Répertoire <i class="fa fa-fw fa-caret-down"></i></a>
|
||||
<ul id="repertoire" class="collapse <?php echo in_array($page, array('contacts', 'groupes')) ? 'in' : ''; ?>">
|
||||
<ul id="repertoire" class="collapse <?php echo in_array($page, array('contacts', 'groupes', 'conditional_groupes')) ? 'in' : ''; ?>">
|
||||
<li <?php echo $page == 'contacts' ? 'class="active"' : ''; ?>>
|
||||
<a href="<?php echo \descartes\Router::url('Contact', 'list'); ?>"><i class="fa fa-fw fa-user"></i> Contacts</a>
|
||||
</li>
|
||||
<li <?php echo $page == 'groupes' ? 'class="active"' : ''; ?>>
|
||||
<a href="<?php echo \descartes\Router::url('Group', 'list'); ?>"><i class="fa fa-fw fa-group"></i> Groupes</a>
|
||||
</li>
|
||||
<li <?php echo $page == 'conditional_groupes' ? 'class="active"' : ''; ?>>
|
||||
<a href="<?php echo \descartes\Router::url('ConditionalGroup', 'list'); ?>"><i class="fa fa-fw fa-random"></i> Groupes Conditionnels</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
|
|
Loading…
Reference in New Issue