Merge branch 'master' into CRUD-init

This commit is contained in:
Julien Fastré 2019-12-04 21:50:21 +01:00
commit 547439d9ef
20 changed files with 263 additions and 17 deletions

View File

@ -65,10 +65,34 @@ Version 1.5.10
- allow to group export in UI
Branch CRUD-Init
================
Version 1.5.11
==============
- improve return path functions and filters ;
Version 1.5.12
==============
- make the redirection to admin temporarily: some admin experienced cache problems (403 error) when they switched from one admin account to a non-admin one ;
Version 1.5.13
==============
- allow to customize logo on login screen and main layout ;
- remove desert background image on page, handle it from cache in login screen;
Version 1.5.14
==============
- fix errors in pagination
- fix search: usage of parenthesis
- add DQL function REPLACE for replacing in strings: "REPLACE(string, 'from', 'to')"
- add function to format phonenumber
- improve `chill_print_or_message` to support date time;
- add a module `show_hide` for javascript;
- load assets using functions ;
- load a `runtime.js` assets for objects shared by webpack ;
Branch CRUD-Init
================

View File

@ -10,7 +10,7 @@ class DefaultController extends Controller
{
if ($this->isGranted('ROLE_ADMIN')) {
return $this->redirectToRoute('chill_main_admin_central');
return $this->redirectToRoute('chill_main_admin_central', [], 302);
}
return $this->render('ChillMainBundle::layout.html.twig');

View File

@ -34,6 +34,7 @@ use Chill\MainBundle\Doctrine\DQL\Similarity;
use Chill\MainBundle\Doctrine\DQL\OverlapsI;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Chill\MainBundle\Doctrine\DQL\Replace;
/**
* This class load config for chillMainExtension.
@ -164,7 +165,8 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
'string_functions' => array(
'unaccent' => Unaccent::class,
'GET_JSON_FIELD_BY_KEY' => GetJsonFieldByKey::class,
'AGGREGATE' => JsonAggregate::class
'AGGREGATE' => JsonAggregate::class,
'REPLACE' => Replace::class,
),
'numeric_functions' => [
'JSONB_EXISTS_IN_ARRAY' => JsonbExistsInArray::class,

64
Doctrine/DQL/Replace.php Normal file
View File

@ -0,0 +1,64 @@
<?php
/*
* Chill is a software for social workers
* Copyright (C) 2019 Champs-Libres Coopérative <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\Doctrine\DQL;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\Lexer;
/**
*
*
*/
class Replace extends FunctionNode
{
protected $string;
protected $from;
protected $to;
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker): string
{
return 'REPLACE('.
$this->string->dispatch($sqlWalker).
', '.
$this->from->dispatch($sqlWalker).
', '.
$this->to->dispatch($sqlWalker).
')';
}
public function parse(\Doctrine\ORM\Query\Parser $parser): void
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->string = $parser->StringPrimary();
$parser->match(Lexer::T_COMMA);
$this->from = $parser->StringPrimary();
$parser->match(Lexer::T_COMMA);
$this->to = $parser->StringPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}

View File

@ -63,6 +63,13 @@ class Page implements PageInterface
* @var int
*/
protected $itemPerPage;
/**
* The number of items in the whole iteration
*
* @var int
*/
protected $totalItems;
public function __construct(
@ -70,13 +77,15 @@ class Page implements PageInterface
$itemPerPage,
UrlGeneratorInterface $urlGenerator,
$route,
array $routeParameters
array $routeParameters,
$totalItems
) {
$this->urlGenerator = $urlGenerator;
$this->number = $number;
$this->itemPerPage = $itemPerPage;
$this->route = $route;
$this->routeParameters = $routeParameters;
$this->totalItems = $totalItems;
}
public function generateUrl()
@ -91,7 +100,9 @@ class Page implements PageInterface
public function getLastItemNumber()
{
return $this->number * $this->itemPerPage - 1;
$last = $this->number * $this->itemPerPage - 1;
return $last < $this->totalItems ? $last : $this->totalItems;
}
public function getNumber()

View File

@ -232,7 +232,8 @@ class Paginator implements PaginatorInterface
array_merge($this->routeParameters, array(
$this->pageKey => $number,
$this->itemPerPageKey => $this->itemPerPage
))
)),
$this->totalItems
);
}

View File

@ -53,6 +53,7 @@ class PhonenumberHelper
protected $cachePool;
const LOOKUP_URI = 'https://lookups.twilio.com/v1/PhoneNumbers/%s';
const FORMAT_URI = 'https://lookups.twilio.com/v1/PhoneNumbers/%s';
public function __construct(
@ -121,6 +122,64 @@ class PhonenumberHelper
return \in_array($validation, [ 'landline', 'voip' ]);
}
public function format($phonenumber)
{
return $this->performTwilioFormat($phonenumber);
}
protected function performTwilioFormat($phonenumber)
{
if (FALSE === $this->isPhonenumberValidationConfigured()) {
return $phonenumber;
}
// filter only number
$filtered = \preg_replace("/[^0-9]/", "", $phonenumber);
$item = $this->cachePool->getItem('pnum_format_nat_'.$filtered);
if ($item->isHit()) {
return $item->get();
}
try {
$response = $this->twilioClient->get(sprintf(self::FORMAT_URI, '+'.$filtered), [
'http_errors' => true,
]);
} catch (ClientException $e) {
$this->logger->error("[phonenumber helper] Could not format number "
. "due to client error", [
"message" => $e->getResponseBodySummary($e->getResponse()),
"status_code" => $e->getResponse()->getStatusCode(),
"phonenumber" => $phonenumber
]);
return $phonenumber;
} catch (ServerException $e) {
$this->logger->error("[phonenumber helper] Could not format number "
. "due to server error", [
"message" => $e->getResponseBodySummary($e->getResponse()),
"status_code" => $e->getResponse()->getStatusCode(),
"phonenumber" => $phonenumber
]);
return null;
}
$format = \json_decode($response->getBody())->national_format;
$item
->set($format)
// expires after 3d
->expiresAfter(3600 * 24 * 3)
;
$this->cachePool->save($item);
return $format;
}
protected function performTwilioLookup($phonenumber)
{
if (FALSE === $this->isPhonenumberValidationConfigured()) {

View File

@ -0,0 +1,52 @@
<?php
/*
* Copyright (C) 2019 Champs Libres Cooperative <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\Phonenumber;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Chill\MainBundle\Phonenumber\PhonenumberHelper;
/**
*
*
*/
class Templating extends AbstractExtension
{
/**
*
* @var PhonenumberHelper
*/
protected $phonenumberHelper;
public function __construct(PhonenumberHelper $phonenumberHelper)
{
$this->phonenumberHelper = $phonenumberHelper;
}
public function getFilters()
{
return [
new TwigFilter('chill_format_phonenumber', [$this, 'formatPhonenumber'])
];
}
public function formatPhonenumber($phonenumber)
{
return $this->phonenumberHelper->format($phonenumber);
}
}

View File

@ -35,7 +35,7 @@ root:
defaults:
_controller: FrameworkBundle:Redirect:urlRedirect
path: /homepage
permanent: true
permanent: false
chill_main_homepage_without_locale:
path: /homepage

View File

@ -4,6 +4,12 @@ services:
$logger: '@Psr\Log\LoggerInterface'
$config: '%chill_main.phone_helper%'
$cachePool: '@cache.user_data'
Chill\MainBundle\Phonenumber\Templating:
arguments:
$phonenumberHelper: '@Chill\MainBundle\Phonenumber\PhonenumberHelper'
tags:
- { name: twig.extension }
Chill\MainBundle\Validation\Validator\ValidPhonenumber:
arguments:

View File

@ -27,8 +27,6 @@ require('./modules/download-report/index.js');
require('select2/dist/css/select2.css');
require('./modules/select_interactive_loading/index.js');
require('./modules/export-list/export-list.scss');
//import {ChillShowHide} from './modules/show_hide/index.js';
//global.ChillShowHide = ChillShowHide;
// img
require('./img/favicon.ico');

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

View File

@ -28,7 +28,7 @@ body {
right: 0;
top: 0;
z-index: -1;
background-image: url('./../../img/background/desert.jpg');
background-image: url('./desert.jpg');
background-attachment: fixed;
background-repeat: no-repeat;
background-size: cover;

View File

@ -0,0 +1 @@
<img class="logo" src="{{ asset('build/images/logo-chill-sans-slogan_white.png') }}">

View File

@ -0,0 +1 @@
<img class="logo" src="{{ asset('build/images/logo-chill-outil-accompagnement_white.png') }}">

View File

@ -27,8 +27,8 @@
</head>
<body>
<div id="content">
<img class="logo" src="{{ asset('build/images/logo-chill-outil-accompagnement_white.png') }}">
{{ include('@ChillMain/Login/_login-logo.html.twig') }}
{% if error is not null %}
<p>{{ error.message|trans }}</p>
{% endif %}

View File

@ -37,7 +37,7 @@
<div class="grid-4 hide-tablet hide-mobile parent">
<div class="grid-10 push-2 grid-tablet-12 grid-mobile-12 push-tablet-0 grid-mobile-0 logo-container">
<a href="{{ path('chill_main_homepage') }}">
<img class="logo" src="{{ asset('build/images/logo-chill-sans-slogan_white.png') }}">
{{ include('@ChillMain/Layout/_header-logo.html.twig') }}
</a>
</div>
</div>

View File

@ -76,7 +76,14 @@ abstract class AbstractSearch implements SearchInterface
foreach ($supportedTerms as $term) {
if (array_key_exists($term, $terms) && $term !== '_default') {
$recomposed .= ' '.$term.':';
$containsSpace = \strpos($terms[$term], " ") !== false;
if ($containsSpace) {
$recomposed .= "(";
}
$recomposed .= (mb_stristr(' ', $terms[$term]) === FALSE) ? $terms[$term] : '('.$terms[$term].')';
if ($containsSpace) {
$recomposed .= ")";
}
}
}

View File

@ -5,6 +5,7 @@ namespace Chill\MainBundle\Templating;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
use Twig\TwigFilter;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Bridge\Twig\Extension\RoutingExtension;
@ -42,7 +43,15 @@ class ChillTwigRoutingHelper extends AbstractExtension
return [
new TwigFunction('chill_return_path_or', [$this, 'getReturnPathOr'], ['is_safe_callback' => [$this, 'isUrlGenerationSafe']] ),
new TwigFunction('chill_path_add_return_path', [$this, 'getPathAddReturnPath'], ['is_safe_callback' => [$this, 'isUrlGenerationSafe']] ),
new TwigFunction('chill_path_forward_return_path', [$this, 'getPathForwardReturnPath'], ['is_safe_callback' => [$this, 'isUrlGenerationSafe']] )
new TwigFunction('chill_path_forward_return_path', [$this, 'getPathForwardReturnPath'], ['is_safe_callback' => [$this, 'isUrlGenerationSafe']] ),
];
}
public function getFilters()
{
return [
new TwigFilter('chill_return_path_label', [$this, 'getLabelReturnPath']),
];
}
@ -51,6 +60,13 @@ class ChillTwigRoutingHelper extends AbstractExtension
return $this->originalExtension->isUrlGenerationSafe($argsNode);
}
public function getLabelReturnPath($default)
{
$request = $this->requestStack->getCurrentRequest();
return $request->query->get('returnPathLabel', null) ?? $default;
}
/**
* Return the return path if it exists, or generate the path if not.
*
@ -78,12 +94,16 @@ class ChillTwigRoutingHelper extends AbstractExtension
* @param bool $relative
* @return string
*/
public function getPathAddReturnPath($name, $parameters = [], $relative = false)
public function getPathAddReturnPath($name, $parameters = [], $relative = false, $label = null)
{
$request = $this->requestStack->getCurrentRequest();
$parameters['returnPath'] = $request->getRequestUri();
if ($label) {
$parameters['returnPathLabel'] = $label;
}
return $this->originalExtension->getPath($name, $parameters, $relative);
}

View File

@ -5,4 +5,4 @@ module.exports = function(encore, entries) {
encore.addAliases({
ShowHide: __dirname + '/Resources/public/modules/show_hide/'
});
};
};