Merge branch 'master' into _bootstrap_modal

This commit is contained in:
2021-04-14 09:49:46 +02:00
146 changed files with 11043 additions and 649 deletions

View File

@@ -14,6 +14,7 @@ use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Security\Core\Role\RoleHierarchy;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Form\Type\ComposedRoleScopeType;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
@@ -43,6 +44,11 @@ class PermissionsGroupController extends AbstractController
*/
private $translator;
/**
* @var ValidatorInterface
*/
private $validator;
/**
* PermissionsGroupController constructor.
*
@@ -50,18 +56,21 @@ class PermissionsGroupController extends AbstractController
* @param RoleProvider $roleProvider
* @param RoleHierarchy $roleHierarchy
* @param TranslatorInterface $translator
* @param ValidatorInterface $validator
*/
public function __construct(
TranslatableStringHelper $translatableStringHelper,
RoleProvider $roleProvider,
RoleHierarchy $roleHierarchy,
TranslatorInterface $translator
TranslatorInterface $translator,
ValidatorInterface $validator
)
{
$this->translatableStringHelper = $translatableStringHelper;
$this->roleProvider = $roleProvider;
$this->roleHierarchy = $roleHierarchy;
$this->translator = $translator;
$this->validator = $validator;
}
/**
@@ -452,7 +461,7 @@ class PermissionsGroupController extends AbstractController
);
$permissionsGroup->addRoleScope($roleScope);
$violations = $this->get('validator')->validate($permissionsGroup);
$violations = $this->validator->validate($permissionsGroup);
if ($violations->count() === 0) {
$em->flush();

View File

@@ -11,6 +11,7 @@ use Chill\MainBundle\Form\UserType;
use Chill\MainBundle\Entity\GroupCenter;
use Chill\MainBundle\Form\Type\ComposedGroupCenterType;
use Chill\MainBundle\Form\UserPasswordType;
use Symfony\Component\Validator\Validator\ValidatorInterface;
/**
@@ -28,14 +29,21 @@ class UserController extends AbstractController
*/
private $logger;
/**
* @var ValidatorInterface
*/
private $validator;
/**
* UserController constructor.
*
* @param LoggerInterface $logger
* @param ValidatorInterface $validator
*/
public function __construct(LoggerInterface $logger)
public function __construct(LoggerInterface $logger, ValidatorInterface $validator)
{
$this->logger = $logger;
$this->validator = $validator;
}
/**
@@ -258,7 +266,7 @@ class UserController extends AbstractController
$form[self::FORM_GROUP_CENTER_COMPOSED]->getData());
$user->addGroupCenter($groupCenter);
if ($this->get('validator')->validate($user)->count() === 0) {
if ($this->validator->validate($user)->count() === 0) {
$em->flush();
$this->addFlash('success', $this->get('translator')->trans('The '
@@ -267,7 +275,7 @@ class UserController extends AbstractController
return $this->redirect($this->generateUrl('admin_user_edit',
array('id' => $uid)));
} else {
foreach($this->get('validator')->validate($user) as $error)
foreach($this->validator->validate($user) as $error)
$this->addFlash('error', $error->getMessage());
}
}
@@ -380,7 +388,7 @@ class UserController extends AbstractController
// logging for prod
$this->logger->info('update password for an user', [
'by' => $this->getUser()->getUsername(),
'by' => $this->getUser()->getUsername(),
'user' => $user->getUsername()
]);

View File

@@ -1,38 +0,0 @@
<?php
namespace Chill\MainBundle\Form\Extension;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* Class CKEditorTypeExtension
*
* @package Chill\MainBundle\Form\Extension
* @author Mathieu Jaumotte mathieu.jaumotte@champs-libres.coop
*/
class CKEditorExtension extends AbstractTypeExtension
{
/**
* Return the class of the Textarea Type being extended.
* @return iterable
*/
public static function getExtendedTypes(): iterable
{
return [TextareaType::class];
}
/**
* @param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'attr' => [
'class' => 'ckeditor snippet-markdown'
]
]);
}
}

View File

@@ -0,0 +1,57 @@
<?php
/*
* Copyright (C) 2017 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\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormInterface;
/**
* Create a Textarea
*
* By default, add a WYSIWYG editor.
*
* Options:
*
* * `disable_editor`: set true to disable editor
*
*/
final class ChillTextareaType extends AbstractType
{
public function getParent()
{
return TextareaType::class;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver
->setDefined('disable_editor')
->setDefault('disable_editor', false)
->setAllowedTypes('disable_editor', 'bool');
}
public function buildView(FormView $view, FormInterface $form, array $options)
{
if (!$options['disable_editor']) {
$view->vars['attr']['ckeditor'] = true;
}
}
}

View File

@@ -20,7 +20,7 @@ namespace Chill\MainBundle\Form\Type;
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Chill\MainBundle\Form\Type\ChillTextareaType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
@@ -44,8 +44,8 @@ class CommentType extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('comment', TextareaType::class, [
->add('comment', ChillTextareaType::class, [
'disable_editor' => $options['disable_editor']
])
;
@@ -72,8 +72,12 @@ class CommentType extends AbstractType
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => CommentEmbeddable::class,
]);
$resolver
->setDefined('disable_editor')
->setAllowedTypes('disable_editor', 'bool')
->setDefaults([
'data_class' => CommentEmbeddable::class,
'disable_editor' => false
]);
}
}

View File

@@ -26,146 +26,168 @@ use Psr\Cache\CacheItemPoolInterface;
/**
* Helper to some task linked to phonenumber.
*
* Currently, only Twilio is supported (https://www.twilio.com/lookup). A method
*
* Currently, only Twilio is supported (https://www.twilio.com/lookup). A method
* allow to check if the helper is configured for validation. This should be used
* before doing some validation.
*
*
*
*/
class PhonenumberHelper
{
/**
*
* @var Client
* Twilio client
*/
protected $twilioClient;
protected Client $twilioClient;
/**
*
* @var LoggerInterface
* TRUE if the client is properly configured
*/
protected $logger;
/**
*
* @var CacheItemPoolInterface
*/
protected $cachePool;
protected bool $isConfigured = false;
protected LoggerInterface $logger;
protected CacheItemPoolInterface $cachePool;
const LOOKUP_URI = 'https://lookups.twilio.com/v1/PhoneNumbers/%s';
const FORMAT_URI = 'https://lookups.twilio.com/v1/PhoneNumbers/%s';
public function __construct(
CacheItemPoolInterface $cachePool,
$config,
$config,
LoggerInterface $logger
) {
$this->logger = $logger;
$this->cachePool = $cachePool;
if (\array_key_exists('twilio_sid', $config)
if (\array_key_exists('twilio_sid', $config)
&& !empty($config['twilio_sid'])
&& \array_key_exists('twilio_secret', $config)
&& !empty($config['twilio_secret'])) {
&& strlen($config['twilio_sid']) > 2
&& \array_key_exists('twilio_secret', $config)
&& !empty($config['twilio_secret'])
&& strlen($config['twilio_secret']) > 2
) {
$this->twilioClient = new Client([
'auth' => [ $config['twilio_sid'], $config['twilio_secret'] ]
]);
}
$this->isConfigured = TRUE;
}
}
/**
* Return true if the validation is configured and available.
*
*
* @return bool
*/
public function isPhonenumberValidationConfigured() : bool
{
return NULL !== $this->twilioClient;
return $this->isConfigured;
}
/**
* REturn true if the phoennumber is a mobile phone. Return always false
* REturn true if the phoennumber is a mobile phone. Return always true
* if the validation is not configured.
*
*
* @param string $phonenumber
* @return bool
*/
public function isValidPhonenumberMobile($phonenumber) : bool
{
$validation = $this->performTwilioLookup($phonenumber);
if (NULL === $validation) {
return false;
if (FALSE === $this->isPhonenumberValidationConfigured()) {
return true;
}
$validation = $this->performTwilioLookup($phonenumber);
if (NULL === $validation) {
return true;
}
return $validation === 'mobile';
}
/**
* Return true if the phonenumber is a landline or voip phone. Return always false
* Return true if the phonenumber is a landline or voip phone. Return always true
* if the validation is not configured.
*
*
* @param string $phonenumber
* @return bool
*/
public function isValidPhonenumberLandOrVoip($phonenumber) : bool
{
$validation = $this->performTwilioLookup($phonenumber);
if (NULL === $validation) {
return false;
if (FALSE === $this->isPhonenumberValidationConfigured()) {
return true;
}
$validation = $this->performTwilioLookup($phonenumber);
if (NULL === $validation) {
return true;
}
return \in_array($validation, [ 'landline', 'voip' ]);
}
/**
* Return true if the phonenumber is a landline or voip phone. Return always false
* Return true if the phonenumber is a landline or voip phone. Return always true
* if the validation is not configured.
*
*
* @param string $phonenumber
* @return bool
*/
public function isValidPhonenumberAny($phonenumber) : bool
{
if (FALSE === $this->isPhonenumberValidationConfigured()) {
return true;
}
$validation = $this->performTwilioLookup($phonenumber);
;
if (NULL === $validation) {
return false;
}
return \in_array($validation, [ 'landline', 'voip', 'mobile' ]);
}
/**
* Get type (mobile, landline, ...) for phone number
*
* @param string $phonenumber
*
* @return string
*/
public function getType(string $phonenumber): string
{
return $this->performTwilioLookup($phonenumber) ?? 'unknown';
}
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) {
$response = $e->getResponse();
$this->logger->error("[phonenumber helper] Could not format number "
@@ -174,7 +196,7 @@ class PhonenumberHelper
"status_code" => $response->getStatusCode(),
"phonenumber" => $phonenumber
]);
return $phonenumber;
} catch (ServerException $e) {
$response = $e->getResponse();
@@ -184,7 +206,7 @@ class PhonenumberHelper
"status_code" => $response->getStatusCode(),
"phonenumber" => $phonenumber
]);
return null;
} catch (ConnectException $e) {
$this->logger->error("[phonenumber helper] Could not format number "
@@ -192,38 +214,38 @@ class PhonenumberHelper
"message" => $e->getMessage(),
"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()) {
return null;
}
// filter only number
$filtered = \preg_replace("/[^0-9]/", "", $phonenumber);
$item = $this->cachePool->getItem('pnum_'.$filtered);
if ($item->isHit()) {
return $item->get();
//return $item->get();
}
try {
$response = $this->twilioClient->get(sprintf(self::LOOKUP_URI, '+'.$filtered), [
'http_errors' => true,
@@ -241,7 +263,7 @@ class PhonenumberHelper
"status_code" => $response->getStatusCode(),
"phonenumber" => $phonenumber
]);
return null;
} catch (ConnectException $e) {
$this->logger->error("[phonenumber helper] Could not format number "
@@ -249,20 +271,20 @@ class PhonenumberHelper
"message" => $e->getMessage(),
"phonenumber" => $phonenumber
]);
return null;
}
$validation = \json_decode($response->getBody())->carrier->type;
$item
->set($validation)
// expires after 12h
->expiresAfter(3600 * 12)
;
$this->cachePool->save($item);
return $validation;
}
}

View File

@@ -33,6 +33,7 @@ require('./modules/breadcrumb/index.js');
require('./modules/download-report/index.js');
require('./modules/select_interactive_loading/index.js');
require('./modules/export-list/export-list.scss');
require('./modules/entity/index.js');
/*
* load img

View File

@@ -1,39 +1,24 @@
import ClassicEditorBase from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
import EssentialsPlugin from '@ckeditor/ckeditor5-essentials/src/essentials';
import MarkdownPlugin from '@ckeditor/ckeditor5-markdown-gfm/src/markdown';
//import UploadAdapterPlugin from '@ckeditor/ckeditor5-adapter-ckfinder/src/uploadadapter';
//import AutoformatPlugin from '@ckeditor/ckeditor5-autoformat/src/autoformat';
import BoldPlugin from '@ckeditor/ckeditor5-basic-styles/src/bold';
import ItalicPlugin from '@ckeditor/ckeditor5-basic-styles/src/italic';
import BlockQuotePlugin from '@ckeditor/ckeditor5-block-quote/src/blockquote';
//import EasyImagePlugin from '@ckeditor/ckeditor5-easy-image/src/easyimage';
import HeadingPlugin from '@ckeditor/ckeditor5-heading/src/heading';
//import ImagePlugin from '@ckeditor/ckeditor5-image/src/image';
//import ImageCaptionPlugin from '@ckeditor/ckeditor5-image/src/imagecaption';
//import ImageStylePlugin from '@ckeditor/ckeditor5-image/src/imagestyle';
//import ImageToolbarPlugin from '@ckeditor/ckeditor5-image/src/imagetoolbar';
//import ImageUploadPlugin from '@ckeditor/ckeditor5-image/src/imageupload';
import LinkPlugin from '@ckeditor/ckeditor5-link/src/link';
import ListPlugin from '@ckeditor/ckeditor5-list/src/list';
import ParagraphPlugin from '@ckeditor/ckeditor5-paragraph/src/paragraph';
import "./index.scss";
export default class ClassicEditor extends ClassicEditorBase {}
ClassicEditor.builtinPlugins = [
EssentialsPlugin,
MarkdownPlugin,
// UploadAdapterPlugin,
// AutoformatPlugin,
BoldPlugin,
ItalicPlugin,
BlockQuotePlugin,
// EasyImagePlugin,
HeadingPlugin,
// ImagePlugin,
// ImageCaptionPlugin,
// ImageStylePlugin,
// ImageToolbarPlugin,
// ImageUploadPlugin,
LinkPlugin,
ListPlugin,
ParagraphPlugin
@@ -49,27 +34,19 @@ ClassicEditor.defaultConfig = {
'link',
'bulletedList',
'numberedList',
// 'uploadImage',
'blockQuote',
'undo',
'redo'
]
},
// image: {
// toolbar: [
// 'imageStyle:full',
// 'imageStyle:side',
// '|',
// 'imageTextAlternative'
// ]
// },
language: 'fr'
};
let Fields = [];
Fields.push.apply(Fields, document.querySelectorAll('textarea.ckeditor'));
Fields.push.apply(Fields, document.querySelectorAll('.cf-fields textarea'));
Fields.push.apply(Fields, document.querySelectorAll('textarea[ckeditor]'));
// enable for custom fields
//Fields.push.apply(Fields, document.querySelectorAll('.cf-fields textarea'));
Fields.forEach(function(field) {
ClassicEditor
@@ -81,4 +58,4 @@ Fields.forEach(function(field) {
console.error( error.stack );
})
;
})
});

View File

@@ -0,0 +1,8 @@
// set min height for ckeditor
.ck-editor__editable {
min-height: 150px;
}
.ck-editor.ck-reset {
margin-bottom: 1.5em;
}

View File

@@ -0,0 +1,6 @@
.chill-entity__comment-embeddable {
.chill-entity__comment-embeddable__metadata {
font-size: smaller;
color: var(--chill-light-gray);
}
}

View File

@@ -0,0 +1,2 @@
// css classes to render entities
require('./comment_embeddable.scss');

View File

@@ -157,16 +157,27 @@ dl.chill_view_data {
}
blockquote.chill-user-quote {
blockquote.chill-user-quote,
div.chill-user-quote {
border-left: 10px solid $chill-yellow;
margin: 1.5em 10px;
padding: 0.5em 10px;
quotes: "\201C""\201D""\2018""\2019";
background-color: $chill-llight-gray;
p { display: inline; }
blockquote {
margin: 1.5em 10px;
padding: 0.5em 10px;
}
blockquote:before {
color: #ccc;
content: open-quote;
font-size: 4em;
line-height: 0.1em;
margin-right: 0.25em;
vertical-align: -0.4em;
}
}
.chill-no-data-statement {

View File

@@ -148,7 +148,6 @@ form {
li {
label {
display: inline-block;
font-weight: normal;
}
}
}

View File

@@ -0,0 +1,36 @@
{{ opening_box|raw }}
<div class="">
<div class="comment-embeddable_comment">
{%- if options['limit_lines'] is not null -%}
{% set content = comment.comment|split('\n')|slice(0, options['limit_lines'])|join('\n') %}
{%- else -%}
{% set content = comment.comment %}
{%- endif -%}
<blockquote class="chill-user-quote">
{% if options['disable_markdown'] %}
{{ content|nl2br }}
{% else %}
{{ content|chill_markdown_to_html }}
{% endif %}
</blockquote>
</div>
</div>
{% if options['metadata'] %}
<div class="chill-entity__comment-embeddable__metadata">
{% if user is not empty %}
<span class="chill-entity__comment-embeddable__user">
{{ 'Last updated by'| trans }} {{ user|chill_entity_render_box(options['user']) }}
</span>';
{% endif %}
{% if comment.date is not empty %}
<span class="chill-entity__comment-embeddable__date">
{% if user is empty %}{{ 'Last updated on'|trans ~ ' ' }}{% else %}{{ 'on'|trans ~ ' ' }}{% endif %}
{{ comment.date|format_datetime("medium", "short") }}
</span>
{% endif %}
</div>
{% endif %}
{{ closing_box|raw }}

View File

@@ -0,0 +1,54 @@
<?php
/*
* Copyright (C) 2021 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\Templating;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Parsedown;
/**
* Render markdown
*/
final class ChillMarkdownRenderExtension extends AbstractExtension
{
/**
* @var Parsedown
*/
protected $parsedown;
public function __construct()
{
$this->parsedown = new Parsedown();
$this->parsedown->setSafeMode(true);
}
public function getFilters(): array
{
return [
new TwigFilter('chill_markdown_to_html', [$this, 'renderMarkdownToHtml'], [
'is_safe' => [ 'html' ]
])
];
}
public function renderMarkdownToHtml(string $var): string
{
return $this->parsedown->parse($var);
}
}

View File

@@ -21,9 +21,9 @@
namespace Chill\MainBundle\Templating\Entity;
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Repository\UserRepository;
use Chill\MainBundle\Templating\Entity\AbstractChillEntityRender;
use Symfony\Component\Templating\EngineInterface;
class CommentRender extends AbstractChillEntityRender
{
@@ -31,10 +31,19 @@ class CommentRender extends AbstractChillEntityRender
* @var \Chill\MainBundle\Repository\UserRepository
*/
private $userRepository;
/**
*
* @var EngineInterface
*/
private $engine;
public function __construct(UserRepository $userRepository)
{
public function __construct(
UserRepository $userRepository,
EngineInterface $engine
) {
$this->userRepository = $userRepository;
$this->engine = $engine;
}
/**
@@ -45,35 +54,29 @@ class CommentRender extends AbstractChillEntityRender
*/
public function renderBox($entity, array $options): string
{
$username = '';
// default options
$options = \array_merge([
'user' => [],
'disable_markdown' => false,
'limit_lines' => null,
'metadata' => true
], $options);
if ($entity->getUserId()) {
$user = $this->userRepository->find($entity->getUserId());
if ($user instanceof User) {
$username = $user->getUsername();
}
}
$str = $this->getDefaultOpeningBox('comment-embeddable').
'<span class="comment_comment">'.
nl2br($entity->getComment()).
'</span>';
if ($entity->getDate() instanceof \DateTime) {
$str .= '<span class="comment_date">'.
$entity->getDate()->format('d/m/Y H:i');
'</span>';
}
if (strlen($username) > 0) {
$str .= '<span class="comment_user">'.
$username.
'</span>';
}
$str .= $this->getDefaultClosingBox();
return $str;
return $this->engine
->render(
'@ChillMain/Entity/CommentEmbeddable.html.twig',
[
'opening_box' => $this->getDefaultOpeningBox('comment-embeddable'),
'closing_box' => $this->getDefaultClosingBox(),
'user' => $user ?? NULL,
'comment' => $entity,
'options' => $options
]
);
}
/**

View File

@@ -0,0 +1,76 @@
<?php
/*
* Chill is a software for social workers
*
* Copyright (C) 2014-2021, Champs Libres Cooperative SCRLFS,
* <http://www.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\Tests\Templating;
use PHPUnit\Framework\TestCase;
use Chill\MainBundle\Templating\ChillMarkdownRenderExtension;
/**
* Test the service ChillMarkdownRenderExtension
*
* @internal we do not want to test the markdown transformation. We just want to
* test that the markdown is correctly transformed into html, and that the html
* is safe.
*/
class ChillMarkdownRenderExtensionTest extends TestCase
{
private const SIMPLE_MARKDOWN = <<<MD
# test
Text.
MD;
private const SIMPLE_HTML = <<<HTML
<h1>test</h1>
<p>Text.</p>
HTML;
private const UNAUTHORIZED_MARKDOWN = <<<MD
<script>alert("ok");</script>
MD;
private const UNAUTHORIZED_HTML = <<<HTML
<p>&lt;script&gt;alert(&quot;ok&quot;);&lt;/script&gt;</p>
HTML;
/**
* Test that the markdown input is transformed into html
*/
public function testRendering()
{
$extension = new ChillMarkdownRenderExtension();
$this->assertEquals(self::SIMPLE_HTML,
$extension->renderMarkdownToHtml(self::SIMPLE_MARKDOWN));
}
/**
* Test that the output of the markdown content is sanitized
*/
public function testSecurity()
{
$extension = new ChillMarkdownRenderExtension();
$this->assertEquals(self::UNAUTHORIZED_HTML,
$extension->renderMarkdownToHtml(self::UNAUTHORIZED_MARKDOWN));
}
}

View File

@@ -7,9 +7,8 @@ buildCKEditor = function(encore)
.addPlugin( new CKEditorWebpackPlugin( {
language: 'fr',
addMainLanguageTranslationsToAllAssets: true,
verbose: true,
strict: true,
//additionalLanguages: ['en', 'nl', 'es'],
verbose: !encore.isProduction(),
strict: true
} ) )
// Use raw-loader for CKEditor 5 SVG files.
@@ -35,7 +34,7 @@ buildCKEditor = function(encore)
} )
} )
;
}
};
// Compile and loads all assets from the Chill Main Bundle
module.exports = function(encore, entries)
@@ -61,8 +60,8 @@ module.exports = function(encore, entries)
encore.addEntry('bootstrap', __dirname + '/Resources/public/modules/bootstrap/index.js');
// CKEditor5
//buildCKEditor(encore);
//encore.addEntry('ckeditor5', __dirname + '/Resources/public/modules/ckeditor5/index.js');
buildCKEditor(encore);
encore.addEntry('ckeditor5', __dirname + '/Resources/public/modules/ckeditor5/index.js');
};
};

View File

@@ -1,16 +1,16 @@
services:
Chill\MainBundle\Controller\:
autowire: true
resource: '../../Controller'
tags: ['controller.service_arguments']
Chill\MainBundle\Controller\PasswordController:
autowire: true
arguments:
$chillLogger: '@monolog.logger.chill'
tags: ['controller.service_arguments']
Chill\MainBundle\Controller\SearchController:
arguments:
$searchProvider: '@chill_main.search_provider'
@@ -24,9 +24,11 @@ services:
$roleProvider: '@chill.main.role_provider'
$roleHierarchy: '@security.role_hierarchy'
$translator: '@Symfony\Contracts\Translation\TranslatorInterface'
$validator: '@Symfony\Component\Validator\Validator\ValidatorInterface'
tags: ['controller.service_arguments']
Chill\MainBundle\Controller\UserController:
arguments:
$logger: '@Psr\Log\LoggerInterface'
tags: ['controller.service_arguments']
$validator: '@Symfony\Component\Validator\Validator\ValidatorInterface'
tags: ['controller.service_arguments']

View File

@@ -138,10 +138,6 @@ services:
tags:
- { name: form.type }
Chill\MainBundle\Form\Extension\CKEditorExtension:
tags:
- { name: form.type_extension, extended_type: Symfony\Component\Form\Extension\Core\Type\TextareaType }
chill.main.form.type.comment:
class: Chill\MainBundle\Form\Type\CommentType
arguments:

View File

@@ -34,5 +34,10 @@ services:
Chill\MainBundle\Templating\Entity\CommentRender:
arguments:
- '@chill.main.user_repository'
- '@Symfony\Component\Templating\EngineInterface'
tags:
- { name: 'chill.render_entity' }
Chill\MainBundle\Templating\ChillMarkdownRenderExtension:
tags:
- { name: twig.extension }

View File

@@ -35,6 +35,9 @@ This form contains errors: Ce formulaire contient des erreurs
Choose an user: Choisir un utilisateur
'You are going to leave a page with unsubmitted data. Are you sure you want to leave ?': "Vous allez quitter la page alors que des données n'ont pas été enregistrées. Êtes vous sûr de vouloir partir ?"
No value: Aucune information
Last updated by: Dernière mise à jour par
Last updated on: Dernière mise à jour le
on: le
Edit: Modifier
Update: Mettre à jour