Merge branch 'develop' into trunk

This commit is contained in:
Vincent BRIDIER 2023-03-20 14:20:37 +01:00
commit e4656628f7
No known key found for this signature in database
GPG key ID: 8571BFA20420D512
17 changed files with 276 additions and 56 deletions

View file

@ -70,6 +70,14 @@ MESBR_COUPON_3_CHARCUT=""
ANALYTICS_GOOGLE_ID=
GOOGLE_MAP_API_KEY=
# Google Recaptcha
GOOGLE_RECAPTCHA_PUBLIC=
GOOGLE_RECAPTCHA_PRIVATE=
GOOGLE_RECAPTCHA_VERSION=
# Matomo
MATOMO_SITE_ID=
## MERCANET
MERCANET_SECRET=
MERCANET_MERCHAND_ID=

View file

@ -3,19 +3,38 @@
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\View;
class SecureHeaders
{
// Enumerate headers which you do not want in your application's responses.
// Great starting point would be to go check out @Scott_Helme's:
// https://securityheaders.com/
/**
* Enumerate headers which you do not want in your application's responses.
* Great starting point would be to go check out @Scott_Helme's:
* https://securityheaders.com/
*
* @var array
*/
private $unwantedHeaderList = [
'X-Powered-By',
'Server',
];
public function handle($request, Closure $next)
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next)
{
View::share('nonce', $nonce = \uniqid());
// * For VueJS dev tool to execute
$jsDev = config('app.debug') ? "'unsafe-eval' 'unsafe-inline'" : "'nonce-$nonce'";
$reportUri = Route::has('cspReportUri') ?
\sprintf('report-uri %s', \parse_url(route('cspReportUri'), \PHP_URL_PATH)) : '';
$this->removeUnwantedHeaders($this->unwantedHeaderList);
$response = $next($request);
$response->headers->set('Referrer-Policy', 'no-referrer-when-downgrade');
@ -24,19 +43,41 @@ class SecureHeaders
$response->headers->set('X-Frame-Options', 'SAMEORIGIN');
$response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains;');
$cspPolicies = [
'frame-src ' . implode(" ", [
'https://www.google.com/recaptcha/',
'https://recaptcha.google.com/recaptcha/'
]) . ';',
"style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com https://fonts.googleapis.com;",
// phpcs:ignore Generic.Files.LineLength.TooLong
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdnjs.cloudflare.com https://www.googletagmanager.com https://www.google-analytics.com https://maps.googleapis.com https://fonts.googleapis.com;"
'script-src ' . implode(" ", [
'\'self\'',
'\'unsafe-inline\'',
'\'unsafe-eval\'',
'https://cdnjs.cloudflare.com',
'https://*.googletagmanager.com',
'https://www.google-analytics.com',
'https://maps.googleapis.com',
'https://fonts.googleapis.com',
'https://www.google.com/recaptcha/',
'https://www.gstatic.com/recaptcha/',
'https://yl.s-web.fr',
]) . ' ' . $jsDev . ';'
];
$response->headers->set(
'Content-Security-Policy',
implode(' ', $cspPolicies)
implode(' ', $cspPolicies) . $reportUri
);
$response->headers->set('Feature-Policy', 'fullscreen \'self\';');
return $response;
}
private function removeUnwantedHeaders($headerList)
/**
* Remove unwanted headers
*
* @param array $headerList
* @return void
*/
private function removeUnwantedHeaders(array $headerList)
{
foreach ($headerList as $header) {
header_remove($header);

View file

@ -4,6 +4,7 @@ namespace App\Http\Requests;
use App\Enums\ContactFormCivility;
use App\Enums\ContactFormType;
use App\Lib\Helpers\ToolboxHelper;
use Illuminate\Foundation\Http\FormRequest;
use Kwaadpepper\Enum\Rules\EnumIsValidRule;
@ -26,6 +27,8 @@ class ContactFormRequest extends FormRequest
*/
public function rules()
{
ToolboxHelper::validateRecaptcha($this, \config('services.google.recaptcha.version'));
if ($this->type == ContactFormType::onepage()->value) {
app()->setLocale('nl');
}

View file

@ -2,6 +2,7 @@
namespace App\Http\Requests;
use App\Lib\Helpers\ToolboxHelper;
use Carbon\Carbon;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Password;
@ -36,6 +37,8 @@ class CustomerRegisterRequest extends FormRequest
*/
public function rules()
{
ToolboxHelper::validateRecaptcha($this, \config('services.google.recaptcha.version'));
return [
'email' => 'required|email:rfc,dns,spoof,filter|max:255|unique:customers,email',
'username' => 'required|string|min:1|max:255|unique:customers,username',
@ -78,6 +81,10 @@ class CustomerRegisterRequest extends FormRequest
];
}
/**
*
* @return array
*/
public function attributes()
{
return [
@ -85,6 +92,10 @@ class CustomerRegisterRequest extends FormRequest
];
}
/**
*
* @return array
*/
public function messages()
{
return [

View file

@ -129,9 +129,14 @@ class UpdateEngagement extends FormRequest
}//end if
break;
case InputType::url()->value:
if ($iptName === "link") {
$ruleAtt = "sometimes";
} else {
$ruleAtt = "required";
}
$rules = array_merge($rules, [
"content.$i.content.$iptName.value" => [
'required', 'url',
$ruleAtt, 'url',
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
try {
@ -172,10 +177,10 @@ class UpdateEngagement extends FormRequest
"content.$i.content.$iptName.value" => 'boolean'
]);
break;
}//end switch
}//end foreach
}//end foreach
}//end if
} //end switch
} //end foreach
} //end foreach
} //end if
return $rules;
}
}

View file

@ -64,7 +64,7 @@ class XmlTracaReader extends XmlJobs
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function (\SimpleXMLElement $xmlContent, array &$line) {
$abatage_date = \explode('.', $line['abatage_date']);
// * Inverser année et jours a cause du flux de merde
// * Inverser année et jours a cause du flux
$line['abatage_date'] = \sprintf(
'20%s/%s/%s',
$abatage_date[2],

View file

@ -0,0 +1,65 @@
<?php
namespace App\Lib\Helpers;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Validator;
class ToolboxHelper
{
/**
* Validate recaptcha
*
* @param Request $request
* @param boolean $isV3
* @return void
*/
public static function validateRecaptcha(Request $request, bool $isV3 = true)
{
Validator::validate($request->all(), [
'g-recaptcha-response' => [
'required', 'string',
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed, Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
function ($attribute, $value, $fail) use ($isV3) {
try {
$response = Http::asForm()->post('https://www.google.com/recaptcha/api/siteverify', [
'secret' => config('services.google.recaptcha.private'),
'response' => \strval($value),
'remoteip' => app('clientIp')
]);
if ($e = $response->toException()) {
throw $e;
}
if ($isV3) {
if (
!$response->json('success') or
!$response->json('challenge_ts') or
Carbon::parse($response->json('challenge_ts'))->lessThan(\now('utc')->subMinutes(5)) or
$response->json('hostname') !== \parse_url(config('app.url'), \PHP_URL_HOST) or
!$response->json('score')
) {
throw new \RuntimeException();
}
if (\floatval($response->json('score')) < 0.8) {
$fail("Vous n'avez pas obtenu un score assez élevé lors de la validation du recaptcha");
}
return;
}
if (
!$response->json('success') or
!$response->json('challenge_ts') or
Carbon::parse($response->json('challenge_ts'))->lessThan(\now('utc')->subMinutes(5)) or
$response->json('hostname') !== \parse_url(config('app.url'), \PHP_URL_HOST)
) {
throw new \RuntimeException();
}
} catch (\Exception $e) {
$fail('Erreur lors de la validation du formulaire veuillez réssayer');
} //end try
}
]
], [], ['g-recaptcha-response' => 'reCaptcha']);
}
}

View file

@ -30,4 +30,16 @@ return [
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
],
'google' => [
'recaptcha' => [
'public' => env('GOOGLE_RECAPTCHA_PUBLIC'),
'private' => env('GOOGLE_RECAPTCHA_PRIVATE'),
'version' => env('GOOGLE_RECAPTCHA_VERSION', "3") === "3" ? true : false
]
],
'matomo' => [
'site_id' => env('MATOMO_SITE_ID')
],
];

View file

@ -32,8 +32,13 @@ $content = $engagement->content;
@case(InputType::url()->value)
@case(InputType::string()->value)
<div class="form-group">
<label for="{{ $i.$k }}"><small>{{ $input['label'] }}*</small></label>
<input id="{{ $i.$k }}" type="text" class="form-control" name="content[{{$i}}][content][{{$k}}][value]" placeholder="{{ $input['label'] }}" value="{{ old("content.$i.content.$k.value") ?? $input['value'] ?? '' }}" required>
@if($k === 'link')
<label for="{{ $i.$k }}"><small>{{ $input['label'] }}</small></label>
<input id="{{ $i.$k }}" type="text" class="form-control" name="content[{{$i}}][content][{{$k}}][value]" placeholder="{{ $input['label'] }}" value="{{ old("content.$i.content.$k.value") ?? $input['value'] ?? '' }}">
@else
<label for="{{ $i.$k }}"><small>{{ $input['label'] }}*</small></label>
<input id="{{ $i.$k }}" type="text" class="form-control" name="content[{{$i}}][content][{{$k}}][value]" placeholder="{{ $input['label'] }}" value="{{ old("content.$i.content.$k.value") ?? $input['value'] ?? '' }}" required>
@endif
</div>
@break
@case(InputType::text()->value)

View file

@ -8,7 +8,7 @@
@section('content')
<section class="content-register">
<div class="content">
<form action="{{ route('inscription') }}" method="POST" class="form-register" name="form-register">
<form action="{{ route('inscription') }}" method="POST" id="register-form-id" class="form-register" name="form-register">
@csrf
<div class="bloc-left">
@if ($errors->any())
@ -79,20 +79,38 @@
<div>
<label>
<input type="checkbox" name="opt_in" @if(old('opt_in') !== null)checked @endif>
{{ __('J’accepte de recevoir les actualités et bons plans Le Gaulois') }}
{{ __('J\'accepte de recevoir les actualités et bons plans Le Gaulois') }}
</label>
</div>
<br>
<div>
<label>
<input class="changeLength" type="checkbox" name="agree" required>
{{ __('J’accepte les conditions générales d’utilisation du site et autorise LDC Sablé à traiter mes données personnelles conformément à la Charte de confidentialité.') }}
{{ __('J\'accepte les conditions générales d\'utilisation du site et autorise LDC Sablé à traiter mes données personnelles conformément à la Charte de confidentialité.') }}
</label>
</div>
<button type="submit" class="btn-red" value="Enregistrer">{{ __('Enregistrer') }}</button>
</div>
<div class="completion g-recaptcha" data-sitekey="{{ config('services.google.recaptcha.public') }}"></div>
</div>
</form>
</div>
</section>
@endsection
@push('scripts')
<script nonce="{{ $nonce }}">
window.__LDC_CN_Recaptcha = function() {
(function(d, t) {
let g = d.createElement(t),
s = d.getElementsByTagName(t)[0];
g.src = 'https://www.google.com/recaptcha/api.js';
s.parentNode.insertBefore(g, s)
}(document, 'script'));
};
function onSubmit(token) {
document.getElementById("register-form-id").submit();
}
</script>
@endpush

View file

@ -1,4 +1,4 @@
<form action="{{ $type->equals(ContactFormType::complain()) ? route('product-request.submit') : route('contact-us.submit') }}" class="form-contact" method="post" enctype="multipart/form-data">
<form action="{{ $type->equals(ContactFormType::complain()) ? route('product-request.submit') : route('contact-us.submit') }}" id="contact-form-id" class="form-contact" method="post" enctype="multipart/form-data">
@csrf
<p id="contactFormError" @if(count($errors))class="display" @endif>
@ -137,10 +137,11 @@
@if($errors->default->has('message'))<small class="text-danger">{{ implode('<br>', $errors->default->get('message')) }}</small>@endif
</p>
<p class="envoyer">
<div class="envoyer g-recaptcha" data-sitekey="{{ config('services.google.recaptcha.public') }}"></div>
<div class="envoyer mt-1">
<span><sup>*</sup>{{ __('mandatory_fields') }}</span>
<button title="Envoyer" class="btn btn-red btn-arronded-20 text-uppercase">{{ __('send') }}</button>
</p>
<button class="btn btn-red btn-arronded-20 text-uppercase" title="Envoyer" type="submit">{{ __('send') }}</button>
</div>
</form>
@push('scripts')
@ -169,3 +170,20 @@
</script>
@endpush
@push('scripts')
<script nonce="{{ $nonce }}">
window.__LDC_CN_Recaptcha = function() {
(function(d, t) {
let g = d.createElement(t),
s = d.getElementsByTagName(t)[0];
g.src = 'https://www.google.com/recaptcha/api.js';
s.parentNode.insertBefore(g, s)
}(document, 'script'));
};
function onSubmit(token) {
document.getElementById("contact-form-id").submit();
}
</script>
@endpush

View file

@ -5,7 +5,7 @@
<div class="text ck-content">
{!! clean($bloc['content']['content']['value']) !!}
</div>
@if($bloc['content']['link']['status'])
@if($bloc['content']['link']['status'] && !empty($bloc['content']['link']['value']))
<div class="link @if(!count($bloc['content']['labels']['value'])) noborder @endif">
<a href="{{ $bloc['content']['link']['value'] }}" @if($bloc['content']['link']['newtab']) target="_blank" @endif>{{ $bloc['content']['link']['text'] ?? __('En Savoir +') }}</a>
</div>

View file

@ -7,7 +7,7 @@
<div class="text ck-content">
{!! clean($bloc['content']['content']['value']) !!}
</div>
@if($bloc['content']['link']['status'])
@if($bloc['content']['link']['status'] && !empty($bloc['content']['link']['value']))
<div class="link">
<a href="{{ $bloc['content']['link']['value'] }}" @if($bloc['content']['link']['newtab']) target="_blank" @endif>{{ $bloc['content']['link']['text'] ?? __('En Savoir +') }}</a>
</div>

View file

@ -5,7 +5,7 @@
<div class="text ck-content">
{!! clean($bloc['content']['content']['value']) !!}
</div>
@if($bloc['content']['link']['status'])
@if($bloc['content']['link']['status'] && !empty($bloc['content']['link']['value']))
<div class="link @if(!count($bloc['content']['labels']['value'])) noborder @endif">
<a href="{{ $bloc['content']['link']['value'] }}" @if($bloc['content']['link']['newtab']) target="_blank" @endif>{{ $bloc['content']['link']['text'] ?? __('En Savoir +') }}</a>
</div>

View file

@ -5,7 +5,7 @@
<div class="text ck-content">
{!! clean($bloc['content']['content']['value']) !!}
</div>
@if($bloc['content']['link']['status'])
@if($bloc['content']['link']['status'] && !empty($bloc['content']['link']['value']))
<div class="link noborder">
<a href="{{ $bloc['content']['link']['value'] }}" @if($bloc['content']['link']['newtab']) target="_blank" @endif>{{ $bloc['content']['link']['text'] ?? __('En Savoir +') }}</a>
</div>

View file

@ -21,18 +21,18 @@
<div class="ldcweb-banner-title">
<p aria-level="h1">Le site internet Le Gaulois utilise des cookies !</p>
</div>
<p class="ldcweb-banner-para-p" data-open="Ce site web stocke les cookies sur votre ordinateur. Le Gaulois utilise ces informations
afin d’améliorer et de personnaliser votre expérience de navigation, ainsi que pour l'analyse et les indicateurs concernant nos
visiteurs à la fois sur ce site web et sur d'autres supports. Vous pouvez cliquer sur « Accepter » si vous consentez à l’utilisation
de ces cookies." data-close="Nous utilisons des cookies pour nous assurer du bon fonctionnement de notre site et à des fins analytiques.
En poursuivant votre navigation sur ce site ou en fermant ce bandeau, vous consentez à l’utilisation de ces
cookies.">Nous utilisons des cookies pour nous assurer du bon fonctionnement de notre site et à des fins analytiques.
En poursuivant votre navigation sur ce site ou en fermant ce bandeau, vous consentez à l’utilisation de ces
<p class="ldcweb-banner-para-p" data-open="Ce site web stocke les cookies sur votre ordinateur. Le Gaulois utilise ces informations
afin d’améliorer et de personnaliser votre expérience de navigation, ainsi que pour l'analyse et les indicateurs concernant nos
visiteurs à la fois sur ce site web et sur d'autres supports. Vous pouvez cliquer sur « Accepter » si vous consentez à l’utilisation
de ces cookies." data-close="Nous utilisons des cookies pour nous assurer du bon fonctionnement de notre site et à des fins analytiques.
En poursuivant votre navigation sur ce site ou en fermant ce bandeau, vous consentez à l’utilisation de ces
cookies.">Nous utilisons des cookies pour nous assurer du bon fonctionnement de notre site et à des fins analytiques.
En poursuivant votre navigation sur ce site ou en fermant ce bandeau, vous consentez à l’utilisation de ces
cookies.</p>
</div>
</div>
<div class="ldcweb-banner-details">
<p>Si vous refusez l'utilisation des cookies, vos informations ne seront pas suivies lors de votre visite sur ce site. Un cookie sera utilisé
<p>Si vous refusez l'utilisation des cookies, vos informations ne seront pas suivies lors de votre visite sur ce site. Un cookie sera utilisé
dans votre navigateur afin de se souvenir de ne pas suivre vos préférences. Certaines fonctionnalités ne seront pas accessibles.
Vous pouvez changer d’avis et modifier votre consentement à tout moment en revenant sur ce site.
Pour en savoir plus sur les cookies que nous utilisons, consultez notre <a href="{{ route('chart-conf') }}">charte de confidentialité</a>.</p>
@ -47,8 +47,8 @@
<p class="subTitle">{{ __('Le respect de votre vie privée est notre priorité') }}</p>
<p class="description">Les fonctionnalités de ce site s’appuient sur des services proposés par des tiers.
Si vous acceptez, ces tiers déposeront des cookies qui vous permettront de visualiser sur le site du contenu hébergé par ces tiers.
Via ces cookies, ces tiers collecteront et utiliseront vos données de navigation pour des finalités qui leur sont propres, conformément
à leur politique de confidentialité (lien ci-dessous). Cette page vous permet de donner ou de retirer votre consentement, soit globalement
Via ces cookies, ces tiers collecteront et utiliseront vos données de navigation pour des finalités qui leur sont propres, conformément
à leur politique de confidentialité (lien ci-dessous). Cette page vous permet de donner ou de retirer votre consentement, soit globalement
soit service par service.</p>
</div>
<div class="modal-details">
@ -95,6 +95,18 @@
@push('scripts')
<script>
window.LDCWebCN = new LdcwebCn({
'recaptcha': {
'name': "API - reCAPTACHA",
'description': 'Les cookies qui sont déposés via le recaptcha sur les formulaires ont pour finalité d\'empêcher que ceux-ci ne soient soumis par des robots.',
'link': 'https://policies.google.com/privacy',
'labelLink': 'En savoir plus',
'cookies': ['NID', '__Secure-3PAPISID', '__Secure-3PSID', '__Secure-3PSIDCC'],
'script': function() {
if (window.__LDC_CN_Recaptcha) {
window.__LDC_CN_Recaptcha();
}
}
},
'analytics': {
'name': "Google Analytics",
'description': 'Service d\'analyse d\'audience',
@ -112,6 +124,25 @@
gtag('config', '{{ config('google.analyticsKey') }}');
}
},
'matomo':{
'name': "Matomo",
'description': 'Service d\'analyse d\'audience',
'link': 'https://fr.matomo.org/gdpr-analytics/',
'cookies': ['_pk'],
'script': function () {
var _paq = window._paq = window._paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="//yl.s-web.fr/";
_paq.push(['setTrackerUrl', u+'rainbow']);
_paq.push(['setSiteId', '{{ config("services.matomo.site_id") }}']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true; g.src=u+'unicorn'; s.parentNode.insertBefore(g,s);
})();
}
},
'vimeo': {
'name': "VIDEOS - Vimeo",
'description': 'Les cookies déposés via les services de partage de vidéo ont pour finalité de permettre à l’utilisateur de visionner directement sur le site le contenu multimédia.',

View file

@ -1,28 +1,31 @@
<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
@include('layouts.app-head')
@include('front.breadcrumb.breadcrumb-head')
@yield('microdata')
@if(config('app.env') === 'local')
<script>window.vueDebug = true;</script>
@endif
@include('layouts.app-head')
@include('front.breadcrumb.breadcrumb-head')
@yield('microdata')
@if(config('app.env') === 'local')
<script>
window.vueDebug = true;
</script>
@endif
</head>
<body>
<div id="app" class="@yield('bg-color')">
@include('layouts.app-header')
@include('front.breadcrumb.breadcrumb-body')
@yield('sub_header')
@yield('content')
@include('layouts.app-footer')
</div>
@if(config('google.analyticsKey'))
<div id="app" class="@yield('bg-color')">
@include('layouts.app-header')
@include('front.breadcrumb.breadcrumb-body')
@yield('sub_header')
@yield('content')
@include('layouts.app-footer')
</div>
@include('front.webldc-cn')
@include('front.ck-videos')
@endif
@stack('scriptsConstants')
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/js/all.min.js" integrity="sha512-RXf+QSDCUQs5uwRKaDoXt55jygZZm2V++WUZduaU/Ui/9EGp3f/2KZVahFZBKGH0s774sd3HmrhUy+SgOFQLVQ==" crossorigin="anonymous"></script>
<script src="{{ mix('js/preload-main.js') }}"></script>
@stack('scripts')
@stack('scriptsConstants')
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/js/all.min.js" integrity="sha512-RXf+QSDCUQs5uwRKaDoXt55jygZZm2V++WUZduaU/Ui/9EGp3f/2KZVahFZBKGH0s774sd3HmrhUy+SgOFQLVQ==" crossorigin="anonymous"></script>
<script src="{{ mix('js/preload-main.js') }}"></script>
@stack('scripts')
</body>
</html>
</html>