Fixes #1 Minicrud + Cookies viemo youtube + recaptcha + Matomo

This commit is contained in:
Jérémy Munsch 2023-05-27 16:15:42 +02:00
parent f93196020d
commit 97a7fdc36b
No known key found for this signature in database
GPG key ID: 18361AF3BFEC0FEC
34 changed files with 823 additions and 68 deletions

View file

@ -34,4 +34,15 @@ MAIL_FROM_NAME="${APP_NAME}"
# Sentry
SENTRY_LARAVEL_DSN=
# MATOMO
MATOMO_SITE_ID=
# GOOGLE
# - ANALYTICS
ANALYTICS_GOOGLE_ID=
# - RECAPTCHA
GOOGLE_RECAPTCHA_PUBLIC=6LeQxHoUAAAAAGryIFfrnyvdqVm0KNBaIUCQDdf9
GOOGLE_RECAPTCHA_PRIVATE=6LeQxHoUAAAAAOudkLY90DerM_ZtlaFfzTlJJvIo
GOOGLE_RECAPTCHA_VERSION=2
# - MAPS
GOOGLE_MAPS_KEY=

View file

@ -22,3 +22,4 @@ sentry:test
- Add ldc/web-admin-secure-laravel & ldc/web-status-checker-laravel after bump laravel-10
- Api pour storefinder cassée rules.wo-gilt-was.de
- passer le script en TS et le style pas en inline
- Meettre le bump cookie notice pour Matomo

View file

@ -21,6 +21,10 @@ enum InputType: int
case string = 11;
case text = 12;
case url = 13;
/** Vimeo only */
case videoVimeo = 14;
/** Youtube only */
case videoYoutube = 15;
/** Youtube or Vimeo. */
case video = 16;
}

View file

@ -48,22 +48,24 @@ class SecureHeaders
$response->headers->set('X-Frame-Options', 'SAMEORIGIN');
$response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains;');
$cspPolicies = [
"default-src 'none';",
"manifest-src 'self';",
"prefetch-src 'self' https://cloud.ccm19.de;",
"prefetch-src 'self';",
// * CKEDITOR Specific https://ckeditor.com/docs/ckeditor5/latest/installation/advanced/csp.html
"connect-src 'self' blob: " . \implode(' ', [
'https://player.vimeo.com',
'https://rules.wo-gilt-was.de',
'https://cloud.ccm19.de',
// * MATOMO
'https://yl.s-web.fr',
'https://region1.google-analytics.com',
'https://maps.googleapis.com',
'https://maps.googleapis.com'
]) . ';',
"img-src * data: blob:;",
'frame-src ' . implode(' ', [
"frame-src 'self' " . implode(' ', [
'https://www.youtube.com',
'https://www.google.com/recaptcha/',
'https://recaptcha.google.com/recaptcha/',
'https://www.google.com/maps/'
'https://www.google.com/maps/',
'https://player.vimeo.com'
]) . ';',
"font-src 'self' data: " . \implode(' ', [
'https://fonts.gstatic.com',
@ -75,10 +77,8 @@ class SecureHeaders
'https://cdnjs.cloudflare.com',
'https://use.typekit.net',
'https://p.typekit.net',
'https://cloud.ccm19.de',
// * File Manager
'https://fonts.googleapis.com',
'https://cdn.jsdelivr.net',
'https://use.fontawesome.com',
'https://fonts.googleapis.com'
]) . ';',
@ -89,7 +89,6 @@ class SecureHeaders
// phpcs:ignore Generic.Files.LineLength.TooLong
"script-src 'self' 'unsafe-eval' {$jsDev} " . \implode(' ', [
'https: blob:',
'https://cloud.ccm19.de',
'https://www.googletagmanager.com',
'https://cdnjs.cloudflare.com',
"$scheme://www.google-analytics.com",

View file

@ -50,6 +50,18 @@ abstract class UpdateMiniCrudRequest extends FormRequest
"data.$iptName.value" => $this->compileValidate('regex:%([^"&?\/\s]{11})%i', $input)
]);
break;
case InputType::videoVimeo->value:
$rules = array_merge($rules, [
"data.$iptName.value" => $this->compileValidate('regex:%([0-9]{8,10})%i', $input)
]);
break;
case InputType::video->value:
$rules = array_merge($rules, [
"data.$iptName.value" => $this->compileValidate([
'regex:%([0-9]{8,10}|[^"&?\/\s]{11})%i'
], $input)
]);
break;
case InputType::string->value:
$rules = array_merge($rules, [
"data.$iptName.value" => $this->compileValidate('required|string|min:3', $input)
@ -57,7 +69,7 @@ abstract class UpdateMiniCrudRequest extends FormRequest
break;
case InputType::text->value:
$mergeRules = $this->compileValidate('required|string|min:15', $input);
$mergeRules = collect(\explode('|', $mergeRules))
$mergeRules = collect($mergeRules)
->map(function ($rule) {
if (\strpos($rule, 'max:') !== false) {
$max = \intval(str_replace('max:', '', $rule));
@ -155,24 +167,54 @@ abstract class UpdateMiniCrudRequest extends FormRequest
/**
* Undocumented function
*
* @param string $inputTypeValidate
* @param string|array $inputTypeValidate
* @param array $input
* @return string
* @return array
*/
public function compileValidate(string $inputTypeValidate, array $input): string
public function compileValidate(string|array $inputTypeValidate, array $input): array
{
if (!isset($input['validate']) or !\is_string($input['validate'])) {
return $inputTypeValidate;
if (\is_string($inputTypeValidate)) {
$inputTypeValidate = explode('|', $inputTypeValidate);
}
/** @var array */
$inputValidate = explode('|', $input['validate']);
$out = explode('|', $inputTypeValidate);
/** @var array|string */
$inputValidate = \collect($input)->get('validate', $input);
$inputValidate = \is_string($inputValidate) ? explode('|', $inputValidate) : $inputValidate;
foreach ($inputValidate as $validate) {
if (!\in_array($validate, $out)) {
$out[] = $validate;
if (!\in_array($validate, $inputTypeValidate)) {
$inputTypeValidate[] = $validate;
}
}
return \implode('|', $out);
$test = \collect($inputTypeValidate)->all();
$test = \collect($inputTypeValidate)->sort(function ($valueA, $valueB) {
$valueA = $this->ruleHasToComeFirst($valueA) ? 0 : 2;
$valueB = $this->ruleHasToComeFirst($valueB) ? 1 : 2;
return $valueA - $valueB;
})->unique()->all();
return \collect($inputTypeValidate)->unique()->sort(function ($valueA, $valueB) {
$valueA = $this->ruleHasToComeFirst($valueA) ? 0 : 2;
$valueB = $this->ruleHasToComeFirst($valueB) ? 1 : 2;
return $valueA - $valueB;
})->all();
}
/**
* Does the rulle need to come first ?
*
* @param string $rule
* @return boolean
*/
private function ruleHasToComeFirst(string $rule): bool
{
$rule = \collect(\explode(':', $rule, 1))->filter()->first(null, '');
return \collect([
'nullable', 'present', 'required', 'required_if',
'required_unless', 'required_with', 'required_with_all',
'required_without', 'required_without_all', 'required_with_all',
'missing', 'missing_if', 'missing_unless',
'missing_with', 'missing_with_all',
])->first(fn (string $firstRule) => \strpos($rule, $firstRule) === 0) ? true : false;
}
/**

View file

@ -2,7 +2,9 @@
namespace App\Http\Requests\Fo;
use App\Lib\Helpers\ToolboxHelper;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\ValidationException;
class ContactRequest extends FormRequest
{
@ -41,6 +43,14 @@ class ContactRequest extends FormRequest
'email' => 'required|email:rfc,spoof,dns,strict',
'message' => 'required|string|min:10',
'terms' => 'accepted',
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
'g-recaptcha-response' => function (string $attribute, mixed $value, \Closure $fail) {
try {
ToolboxHelper::validateRecaptcha($this, \config('services.google.recaptcha.version'));
} catch (ValidationException $e) {
$fail($e->getMessage());
}
}
];
}
}

View file

@ -2,10 +2,12 @@
namespace App\Lib\Helpers;
use Carbon\Carbon;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
@ -35,6 +37,60 @@ class ToolboxHelper
return $merged;
}
/**
* Validate recaptcha
*
* @param \Illuminate\Http\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']);
}
/**
* Strip scheme (https) on oembed urls
* Only embed allowed are for MP4 self hosted videos right now

View file

@ -229,11 +229,16 @@ class Concept extends Model
'validate' => "required_if:data.bloc_{$i}_has_video.value,false",
];
$data["bloc_{$i}_video_youtube"] = [
'label' => "Bloc {$i} - Video",
'inputType' => InputType::videoYoutube->value,
$data["bloc_{$i}_video"] = [
'label' => "Bloc {$i} - Video (Youtube or Vimeo)",
'inputType' => InputType::video->value,
'value' => '',
'validate' => "required_if:data.bloc_{$i}_has_video.value,true|nullable|regex:%([^\"&?\/\s]{11})%i",
// phpcs:ignore Generic.Files.LineLength.TooLong
'validate' => [
'required_if:data.bloc_{$i}_has_video.value,true',
'nullable',
'regex:%([0-9]{8,10}|[^\"&?\/\s]{11})%i'
],
'required' => false
];

View file

@ -161,6 +161,7 @@ trait MiniCrudModel
case InputType::number->value:
case InputType::videoVimeo->value:
case InputType::videoYoutube->value:
case InputType::video->value:
break;
case InputType::string->value:
case InputType::text->value:

View file

@ -15,13 +15,15 @@ return [
|
*/
'paths' => ['api/*', 'sanctum/csrf-cookie', '*/storefinder'],
// 'paths' => ['api/*', 'sanctum/csrf-cookie', '*/storefinder'],
'paths' => ['*'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [
'https:\/\//player\.vimeo\.com',
'https:\/\/[a-z0-9\-\.]*\.' . \str_replace('.', '\.', 'wo-gilt-was.de')
],

View file

@ -34,7 +34,16 @@ return [
'google' => [
'maps' => [
'key' => env('GOOGLE_MAPS_KEY'),
],
'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')
],
];

12
package-lock.json generated
View file

@ -1,5 +1,5 @@
{
"name": "srcs",
"name": "wenn-dann-mit-respekt",
"lockfileVersion": 3,
"requires": true,
"packages": {
@ -11,6 +11,7 @@
"@fortawesome/free-regular-svg-icons": "^6.1.2",
"@fortawesome/free-solid-svg-icons": "^6.1.2",
"@fortawesome/vue-fontawesome": "^3.0.1",
"@ldc-web/cookie-notice": "^3.0.3",
"@rushstack/eslint-patch": "^1.1.4",
"@sipec/vue3-tags-input": "^3.0.4",
"@splidejs/splide": "^4.1.4",
@ -2348,6 +2349,15 @@
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
"dev": true
},
"node_modules/@ldc-web/cookie-notice": {
"version": "3.0.3",
"resolved": "https://lab.s-web.fr/api/v4/projects/50/packages/npm/@ldc-web/cookie-notice/-/@ldc-web/cookie-notice-3.0.3.tgz",
"integrity": "sha1-9zBm0372Tl+Q+p2FxG073/wHApQ=",
"dev": true,
"dependencies": {
"core-js": "^3.30.0"
}
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",

View file

@ -18,6 +18,7 @@
"@fortawesome/free-regular-svg-icons": "^6.1.2",
"@fortawesome/free-solid-svg-icons": "^6.1.2",
"@fortawesome/vue-fontawesome": "^3.0.1",
"@ldc-web/cookie-notice": "^3.0.3",
"@rushstack/eslint-patch": "^1.1.4",
"@sipec/vue3-tags-input": "^3.0.4",
"@splidejs/splide": "^4.1.4",

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

View file

@ -0,0 +1,48 @@
<?php
// * Version 3.0.3
// phpcs:disable
return [
'button_customize_cookies' => 'Cookie-Einstellungen',
'button_forbid' => 'Verbieten',
'button_allow' => 'Zulassen',
'button_forbid_all' => 'Alle ablehnen',
'button_allow_all' => 'Alle akzeptieren',
'button_customize' => 'Einstellungen',
'button_back' => 'Rückkehr',
'banner_title' => 'Die Website :site_name nutzt Cookies!',
'banner_text_open' => 'Diese Website speichert Cookies auf Ihrem Computer. :site_name verwendet diese Informationen zur Verbesserung und Personalisierung Ihres Surferlebnisses sowie zur Analyse und Kennzahlen über unsere Besucher sowohl auf dieser Website als auch anderswo. Sie können auf „Akzeptieren“ klicken, wenn Sie der Verwendung dieser Cookies zustimmen.',
'banner_text' => 'Wir verwenden Cookies, um das ordnungsgemäße Funktionieren unserer Website sicherzustellen und für analytische Zwecke. Indem Sie diese Website weiterhin durchsuchen oder durch Wenn Sie dieses Banner schließen, stimmen Sie der Verwendung dieser Cookies zu.',
'banner_details' => 'Wenn Sie die Verwendung von Cookies ablehnen, werden Ihre Daten nicht erfasst. während Ihres Besuchs auf dieser Website. In Ihrem Browser wird ein Cookie verwendet, um Ihre Präferenzen nicht zu verfolgen. Auf einige Funktionen kann nicht zugegriffen werden. Sie können Ihre Meinung jederzeit ändern und Ihre Einwilligung ändern, indem Sie zu dieser Website zurückkehren.',
'banner_details_link' =>
'Weitere Informationen zu den von uns verwendeten Cookies finden Sie in unserer Datenschutzerklärung.',
'banner_details_link_title' => 'Datenschutz-Bestimmungen',
'banner_more_details' => 'Mehr Erfahren...',
'banner_less_details' => 'Weniger Details...',
'Panel_title' => 'Panel zur Verwaltung von Cookies',
'Panel_subtitle' => 'DerSchutz Ihrer Privatsphäre ist unsere Priorität',
'Panel_details' => 'Die Funktionalitäten dieser Website basieren auf Diensten, die von Dritten angeboten werden. Wenn Sie zustimmen, werden diese Drittanbieter Cookies platzieren, die Ihnen die Anzeige ermöglichen auf der Website der von diesen Dritten gehosteten Inhalte. Durch diese Cookies sammeln diese Dritten und wird Ihre Browserdaten für eigene Zwecke gemäß verwenden zu deren Datenschutzerklärung (Link unten). Auf dieser Seite können Sie Ihre Einwilligung entweder global oder für jeden einzelnen Dienst erteilen oder widerrufen.',
'custom' => [
'frame' => 'Sie müssen :type-Cookies autorisieren, um dieses Element anzuzeigen',
'recaptcha' => [
'name' => 'API - reCAPTACHA',
'description' => 'Die Cookies, die über das Recaptcha auf den Formularen hinterlegt werden, sollen verhindern, dass diese durch Roboter übermittelt werden.',
'warning' => 'Um das Formular nutzen zu können, müssen Sie Recaptcha-Cookies akzeptieren'
],
'matomo' => [
'name' => 'PUBLIKUMSMESSUNGEN - MATOMO',
'description' => 'Zielgruppenanalysedienst.'
],
'youtube' => [
'name' => 'VIDEOS - Youtube',
'description' => 'Über Video-Sharing-Dienste platzierte Cookies sollen es dem Benutzer ermöglichen, Multimedia-Inhalte direkt auf der Website anzuzeigen.',
'labelLink' => 'Erfahren Sie mehr'
],
'vimeo' => [
'name' => 'VIDEOS - Vimeo',
'description' => 'Über Video-Sharing-Dienste platzierte Cookies sollen es dem Benutzer ermöglichen, Multimedia-Inhalte direkt auf der Website anzuzeigen.',
'labelLink' => 'Erfahren Sie mehr'
],
]
];

View file

@ -0,0 +1,48 @@
<?php
// * Version 3.0.3
// phpcs:disable
return [
'button_customize_cookies' => 'Customize cookies',
'button_forbid' => 'Deny',
'button_allow' => 'Accept',
'button_forbid_all' => 'Deny all',
'button_allow_all' => 'Accept all',
'button_customize' => 'Customize',
'button_back' => 'Back',
'banner_title' => 'The website :site_name uses cookies!',
'banner_text_open' =>
'This website stores cookies on your computer. :site_name uses this information to improve and personalize your browsing experience, as well as for analysis and metrics about our visitors both on this website and elsewhere. You can click on "Accept" if you consent to the use of these cookies.',
'banner_text' => 'We use cookies to ensure the proper functioning of our site and for analytical purposes. By continuing to browse this site or by closing this banner, you consent to the use of these cookies.',
'banner_details' => 'If you refuse the use of cookies, your information will not be tracked. during your visit to this site. A cookie will be used in your browser to remember not to track your preferences. Some features will not be accessible. You can change your mind and modify your consent at any time by returning to this site.',
'banner_details_link' => 'To find out more about the cookies we use, see our privacy policy.',
'banner_details_link_title' => 'Privacy Policy',
'banner_more_details' => 'More details...',
'banner_less_details' => 'Less details...',
'Panel_title' => 'Cookie management panel',
'Panel_subtitle' => 'Your privacy is our priority',
'Panel_details' => 'The functionalities of this site rely on services offered by third parties. If you agree, these third parties will place cookies that will allow you to view on the site of the content hosted by these third parties. Through these cookies, these third parties will collect and will use your browsing data for their own purposes, in accordance with to their privacy policy (link below). This page allows you to give or withdraw your consent, either globally or on a service-by-service basis.',
'custom' => [
'frame' => 'You must autorize :type cookies to view this element',
'recaptcha' => [
'name' => 'API - reCAPTACHA',
'description' => 'The cookies that are deposited via the recaptcha on the forms are intended to prevent them from being submitted by robots.',
'warning' => 'To use the form you must accept Recaptcha cookies'
],
'matomo' => [
'name' => 'AUDIENCE MEASUREMENTS - MATOMO',
'description' => 'Audience analysis service.'
],
'youtube' => [
'name' => 'VIDEOS - Youtube',
'description' => 'Cookies placed via video sharing services are intended to allow the user to view multimedia content directly on the site.',
'labelLink' => 'Learn more'
],
'vimeo' => [
'name' => 'VIDEOS - Vimeo',
'description' => 'Cookies placed via video sharing services are intended to allow the user to view multimedia content directly on the site.',
'labelLink' => 'Learn more'
],
]
];

View file

@ -1,10 +1,29 @@
@charset "UTF-8";
// * UTILITIES
@import "./utilities/variables";
// * VENDOR
@import "./vendor/tailwind";
@import "./vendor/ckEditorContent";
@import "@ldc-web/cookie-notice";
// * BASE
@import "./base/fonts";
@import "./base/common";
@import "./app";
.video.frame {
display: none;
}
#cn-wrapper #cn-cookie-btn {
padding: 0;
background-color: transparent;
box-shadow: 0 0 0;
img {
width: 100%;
filter: drop-shadow(0 0 0.75rem #888);
}
}

View file

@ -6,7 +6,7 @@ $color-success: #198754;
$color-danger: #dc3545;
$color-info: #0dcaf0;
$color-warning: #ffc107;
$color-primary: #0d6efd;
$color-primary: #b4be00;
$color-secondary: #6c757d;
// * BACKGROUNDS COLORS
@ -22,3 +22,16 @@ $color-default-texts: $color-dark;
$font-default-family-base: "Imprima Regular", sans-serif; // - The default site font family.
$font-default-size-base: 1rem; // Assumes the browser default, typically `16px`
$line-default-height-base: 1.65;
// * COOKIES
$ck-primary-color: $color-primary;
$ck-secondary-color: $color-secondary;
$ck-background-color: $color-white;
$ck-shadow-color: 0 0.1rem 1rem rgba($color-dark, 35%);
$ck-title-font: inherit;
$ck-title-line-height: 2.5rem;
$ck-title-size: 2rem;
$ck-text-font: inherit;
$ck-text-size: 1rem;
$ck-accept-color: $color-success;
$ck-deny-color: $color-danger;

View file

@ -10,3 +10,7 @@ import "./utilities/fontawesome";
// CUSTOM
import "./fo/common";
import "./fo/vue-fo";
// Cookies
import CookieNotice from "@ldc-web/cookie-notice";
window.CookieNotice = CookieNotice;

View file

@ -21,3 +21,48 @@ window.addEventListener("scroll", () => {
document.querySelector("header")?.classList.remove("small");
}
});
// * Video frames.
document.addEventListener("DOMContentLoaded", function () {
const videoFrameList = document.querySelectorAll(
".video-frame"
) as NodeListOf<HTMLLinkElement>;
for (const videoLink of videoFrameList) {
if (!videoLink.dataset.type) {
throw new Error("Missing dataset type");
}
if (!videoLink.dataset.frame) {
throw new Error("Missing dataset frame");
}
// @ts-ignore
window.__CN_VIDEO = window.__CN_VIDEO ?? {};
const newIframe = document.createElement("iframe");
newIframe.frameBorder = "0";
newIframe.allow = "fullscreen";
newIframe.width = "1920px";
newIframe.height = "1080px";
newIframe.setAttribute("allowfullscreen", "allowfullscreen");
// @ts-ignore
const instance = new FsLightbox();
instance.props.sources = [newIframe];
setTimeout(() => {
videoLink.addEventListener("click", (e) => {
e.preventDefault();
// * This comes fslightbox lib
// @ts-ignore
instance.open();
setTimeout(function () {
// @ts-ignore
newIframe.src = window.__CN_VIDEO[videoLink.dataset.type]
? String(videoLink.href)
: String(videoLink.dataset.frame);
}, 50);
return false;
});
}, 300);
}
});

View file

@ -1,6 +1,7 @@
export {};
import { AxiosStatic } from "axios";
import type VueTagsInput from "@sipec/vue3-tags-input";
import CookieNotice from "@ldc-web/cookie-notice";
// * Enum
interface EnumInterface {
@ -74,6 +75,7 @@ declare global {
axios: AxiosStatic;
Swal: Function;
vueDebug: boolean | undefined;
CookieNotice: typeof CookieNotice;
__SYSTEM: {
_asset: string;
_locale: string;

View file

@ -7,6 +7,8 @@ $data = (isset($model->data)) ? $model->getEmptyData() : $conceptData;
$input = json_decode(json_encode($input), true);
@endphp
@switch(intval($input['inputType']))
@case(intval(App\Enums\InputType::video->value))
@case(intval(App\Enums\InputType::videoVimeo->value))
@case(intval(App\Enums\InputType::videoYoutube->value))
<div class="col-12 col-md-6 mb-3">
<fieldset class="border h-100 bg-white p-3">
@ -21,7 +23,11 @@ $data = (isset($model->data)) ? $model->getEmptyData() : $conceptData;
'value' => old("data.{$k}.value", $model->data[$k]['value'] ?? null),
'required' => isset($input['required']) ? !empty($input['required']) : true,
'model' => $model ?? null,
'helper' => __('video id eg: keZURbOo4-M')
'helper' => match(intval($input['inputType'])) {
intval(App\Enums\InputType::videoYoutube->value) => __('video id eg: keZURbOo4-M'),
intval(App\Enums\InputType::videoVimeo->value) => __('video id eg: 74918975'),
intval(App\Enums\InputType::video->value) => __('video id eg: keZURbOo4-M or 74918975'),
}
])
@include('modules.input-error', [
'inputName' => "data.{$k}.value"

View file

@ -4,7 +4,6 @@
<head>
@include('fo.layouts.head')
<meta name="p:domain_verify" content="5f89c5d6c41555aa17ff8d26cc6661f2" />
<script nonce="{{ $nonce }}" src="https://cloud.ccm19.de/app.js?apiKey=0533121ec6c6d41612c50e16a537909e86a43e15b7d38cd1&amp;domain=62b2cf81de22ed179e5eede8" referrerpolicy="origin"></script>
</head>
<body class="relative w-full min-h-screen font-roboto bg-hellgrau text-p lg:text-p-lg mt-[87px] scroll-smooth">
@ -17,11 +16,12 @@
@include('fo.layouts.footer')
@include('fo.layouts.navigation')
@include('fo.vendor/ldcweb-cookies')
@stack('scripts-constants')
@include('modules.script-constants')
@stack('scripts')
@vite(['resources/ts/fo.ts'])
<script nonce="{{ $nonce }}" defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.12.0/dist/cdn.min.js"></script>
<script nonce="{{ $nonce }}">
let mobileMenuOpen = false;

View file

@ -58,6 +58,7 @@
<a href="{{ route('legals') }}" class="block mt-16">{{ __('Legal notice') }}</a>
<a href="{{ route('privacy') }}" class="block mt-2">{{ __('Privacy') }}</a>
<a href="#" class="cn-cookie-link block mt-2">{{ __('cookie-notice.button_customize_cookies') }}</a>
<a href="https://masthuhn-initiative.de/" target="_blank"><img class="mt-16 max-w-[200px]" src="{{ Vite::asset('resources/assets/images/Bilder/ECC-Logo-green.svg') }}"></a>
</div>

View file

@ -0,0 +1,14 @@
@php $uniqueID = uniqid(); @endphp
<a
class="video-frame relative w-full"
href="{{ $link }}"
data-type="{{ $type }}"
data-frame="{{ route('front.frame', ['frame' => $type]) }}"
target="_blank">
<img
class="relative w-full"
src="{{ $picture }}"
alt="{{ $alt }}"
title="{{ $title }}">
<i class="youtube-play-button absolute top-50 left-50 fa-regular fa-circle-play"></i>
</a>

View file

@ -1,32 +1,52 @@
@php
$color = \App\Enums\Concepts\ColorsEnum::make($conceptModel->data["bloc_{$number}_color"]['value']);
$video_value = $conceptModel->data["bloc_{$number}_video"]['value'] ?? null;
$player = 'UNDEFINED';
$link = null;
$picture = !empty($conceptModel->data["bloc_{$number}_image"]['value']) ?
asset($conceptModel->data["bloc_{$number}_image"]['value']) : null;
$alt = $conceptModel->data["bloc_{$number}_image"]['alt'];
$title = $conceptModel->data["bloc_{$number}_image"]['title'];
switch (true) {
// VIMEO
case preg_match('%([0-9]{8,10})%i', $conceptModel->data["bloc_{$number}_video"]['value']):
$player = 'vimeo';
$link = "https://player.vimeo.com/video/{$video_value}";
$picture = "https://vumbnail.com/{$video_value}.jpg";
break;
// YOUTUBE
case preg_match('%([^"&?\/\s]{11})%i', $conceptModel->data["bloc_{$number}_video"]['value']):
$player = 'youtube';
$link = "https://www.youtube.com/embed/{$video_value}";
$picture = "https://i.ytimg.com/vi/{$video_value}/hqdefault.jpg";
break;
// IGNORE
case is_null($video_value):
break;
default:
throw new \Error("Unhandled video id {$video_value}");
}
@endphp
<div class="flex flex-wrap flex-row my-3 sm:my-0 sm:even:flex-row sm:odd:flex-row-reverse">
@if(!empty($conceptModel->data["bloc_{$number}_has_video"]['value']))
@if(!empty($video_value))
<div class="basis-full sm:basis-1/2 flex">
<a
class="relative w-full"
data-fslightbox="https://www.youtube.com/watch?v={{ $conceptModel->data["bloc_{$number}_video_youtube"]['value'] }}"
href="https://www.youtube.com/watch?v={{ $conceptModel->data["bloc_{$number}_video_youtube"]['value'] }}"
target="_blank"
class="position-absolute top-50 start-50 translate-middle"
data-fancybox>
<img
class="w-full"
@if(!empty($conceptModel->data["bloc_{$number}_image"]['value']))
src="{{ asset($conceptModel->data["bloc_{$number}_image"]['value']) }}"
@else
src="{{ 'https://i.ytimg.com/vi/' . $conceptModel->data["bloc_{$number}_video_youtube"]['value'] . '/hqdefault.jpg' }}"
@endif
alt="{{ $conceptModel->data["bloc_{$number}_image"]['alt'] }}"
title="{{ $conceptModel->data["bloc_{$number}_image"]['title'] }}">
<i class="youtube-play-button absolute top-50 left-50 fa-regular fa-circle-play"></i>
</a>
@include('fo.modules.video-frame', [
'type' => $player,
'title' => $title,
'link' => $link,
'picture' => $picture,
'alt' => $alt,
])
</div>
@else
<div class="basis-full sm:basis-1/2 flex">
<img src="{{ asset($conceptModel->data["bloc_{$number}_image"]['value']) }}" alt="{{ $conceptModel->data["bloc_{$number}_image"]['alt'] }}" title="{{ $conceptModel->data["bloc_{$number}_image"]['title'] }}">
<img src="{{ $picture }}" alt="{{ $alt }}" title="{{ $title }}">
</div>
@endif
<div class="basis-full sm:basis-1/2 order-2 text-weiss flex items-center" style="background-color: {{ \App\Enums\Concepts\ColorsEnum::make($color)->color() }}">

View file

@ -27,13 +27,13 @@
<p class="mr-10 pb-3 font-semibold">{{ Session::get('success') }}</p>
</div>
@else
<form method="POST" action="{{ route('contact-form.send') }}" class="sm:grid sm:grid-cols-3 sm:gap-4" xnovalidate>
<form id="form-submit" method="POST" action="{{ route('contact-form.send') }}" class="sm:grid sm:grid-cols-3 sm:gap-4" xnovalidate inert>
@csrf
<div class="mb-6">
<input type="text" id="first_name" name="first_name" class="input bordered white placeholder-weiss bg-hellgruen block w-full px-3 py-2" placeholder="{{ __('validation.attributes.first_name') }}*" value="{{ old('first_name') }}" required>
@if ($errors->has('first_name'))
<div class="mb-6">
<span class="text-danger">{{ $errors->first('first_name') }}</span>
<span class="text-red-600">{{ $errors->first('first_name') }}</span>
</div>
@endif
</div>
@ -42,7 +42,7 @@
<input type="text" id="last_name" name="last_name" class="input bordered white placeholder-weiss bg-hellgruen block w-full px-3 py-2" placeholder="{{ __('validation.attributes.last_name') }}*" value="{{ old('last_name') }}" required>
@if ($errors->has('last_name'))
<div class="mb-6">
<span class="text-danger">{{ $errors->first('last_name') }}</span>
<span class="text-red-600">{{ $errors->first('last_name') }}</span>
</div>
@endif
</div>
@ -51,7 +51,7 @@
<input type="email" id="email" name="email" class="input bordered white placeholder-weiss bg-hellgruen block w-full px-3 py-2" placeholder="{{ __('validation.attributes.email') }}*" value="{{ old('email') }}" required>
@if ($errors->has('email'))
<div class="mb-6">
<span class="text-danger">{{ $errors->first('email') }}</span>
<span class="text-red-600">{{ $errors->first('email') }}</span>
</div>
@endif
</div>
@ -61,7 +61,7 @@
</div>
@if ($errors->has('message'))
<div class="mb-6">
<span class="text-danger">{{ $errors->first('message') }}</span>
<span class="text-red-600">{{ $errors->first('message') }}</span>
</div>
@endif
@ -81,7 +81,7 @@
</div>
@if ($errors->has('terms'))
<div class="mb-6">
<span class="text-danger">{{ $errors->first('terms') }}</span>
<span class="text-red-600">{{ $errors->first('terms') }}</span>
</div>
@endif
@ -89,9 +89,28 @@
<p>*{{ __('Required fields') }}</p>
</div>
<button type="submit" class="button brown inline items-center px-4 my-2 mt-2 mr-6 text-sm text-white w-40 uppercase">{{ __('Send') }}</button>
<div class="mb-6 sm:col-span-3 text-center">
<div id="r_captcha" class="flex justify-center"></div>
@if ($errors->has('g-recaptcha-response'))
<div class="mb-6">
<span class="text-red-600">{{ $errors->first('g-recaptcha-response') }}</span>
</div>
@endif
<button
type="submit"
data-sitekey="{{ config('services.google.recaptcha.public') }}"
class="g-recaptcha button brown inline items-center px-4 my-2 mt-2 mr-6 text-sm text-white w-40 uppercase"
>{{ __('Send') }}</button>
</div>
</form>
<a id="recaptcha-warning" href="" class="cn-cookie-link" rel="nofollow">
<strong class="text-red-600 text-underline text-bold text-normal">
{{ __('cookie-notice.custom.recaptcha.warning') }}
</strong>
</a>
@php
Session::forget('success');
@endphp
@ -101,3 +120,25 @@
</div>
@endsection
@push('scripts')
<script nonce="{{ $nonce }}">
document.addEventListener('DOMContentLoaded', function () {
window.__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?render=explicit&onload=__CN_Recaptcha_Loaded';
s.parentNode.insertBefore(g, s)
}(document, 'script'));
};
window.__CN_Recaptcha_Loaded = function() {
grecaptcha.render('r_captcha', {
'sitekey' : '{{ config('services.google.recaptcha.public') }}'
});
document.getElementById('recaptcha-warning').remove();
document.getElementById('form-submit').inert = false;
};
});
</script>
@endpush

View file

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ __('Autorisation nécessaire') }}</title>
</head>
<body>
<h1 style="padding: 1rem; background-color: rgb(153, 153, 153); color: #FFF; position: absolute; top:50%; left: 50%; transform:translateX(-50%) translateY(-50%);">
{{ __('cookie-notice.custom.frame', [
'type' => $frame
]) }}
</h1>
</body>
</html>

View file

@ -171,7 +171,6 @@
@endsection
@push('scripts')
<script nonce="{{ $nonce }}" src="https://cdn.jsdelivr.net/npm/@splidejs/splide@4.1.4/dist/js/splide.min.js"></script>
<script nonce="{{ $nonce }}">
document.addEventListener('DOMContentLoaded', function() {
new Splide('.splide', {

View file

@ -0,0 +1,92 @@
<div id="cn-wrapper" tabindex="-1" role="dialog">
<button id="cn-cookie-btn" title="{{ $strings->button_customize_cookies }}">
{{-- COOKIE SVG --}}
<svg viewBox="0 0 28 28" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M14.0001 2.93383C14.0001 4.16622 14.6159 5.25477 15.5567 5.90849C15.3526 6.46423 15.2412 7.06473 15.2412 7.69126C15.2412 10.5472 17.5564 12.8624 20.4123 12.8624C21.0807 12.8624 21.7195 12.7356 22.3059 12.5047C22.9456 13.527 24.0817 14.2069 25.3766 14.2069C26.1872 14.2069 26.9357 13.9404 27.539 13.4903C27.5453 13.6594 27.5484 13.8294 27.5484 14C27.5484 21.4826 21.4826 27.5484 14 27.5484C6.51748 27.5484 0.45166 21.4826 0.45166 14C0.45166 6.51748 6.51748 0.45166 14 0.45166C14.3209 0.45166 14.6391 0.462811 14.9543 0.484747C14.3618 1.12929 14.0001 1.98931 14.0001 2.93383ZM21.2176 2.53212C21.2195 2.54914 21.2213 2.56619 21.2229 2.58328C21.2569 2.58864 21.2909 2.59433 21.3247 2.60035C21.2891 2.57745 21.2534 2.5547 21.2176 2.53212ZM25.5171 6.86104C25.523 6.89745 25.5285 6.93399 25.5336 6.97066C25.5512 6.97141 25.5687 6.97228 25.5861 6.97328C25.5633 6.93574 25.5403 6.89833 25.5171 6.86104ZM12.5247 2.09283C12.4748 2.38291 12.4487 2.68118 12.4487 2.98553C12.4487 4.33316 12.9591 5.56158 13.7972 6.48802C13.7266 6.87842 13.6898 7.28054 13.6898 7.69126C13.6898 11.404 16.6996 14.4137 20.4123 14.4137C20.8725 14.4137 21.3219 14.3675 21.7562 14.2794C22.689 15.1941 23.9669 15.7582 25.3766 15.7582C25.5439 15.7582 25.7094 15.7503 25.8726 15.7347C25.0321 21.5385 20.0368 25.9971 14.0001 25.9971C7.37431 25.9971 2.00305 20.6258 2.00305 14.0001C2.00305 7.87378 6.59498 2.82 12.5247 2.09283Z"
fill="#FF4E4E" />
<circle cx="7.58784" cy="12.8624" r="1.96503" fill="#FF4E4E" />
<circle cx="8.82887" cy="19.2745" r="1.3445" fill="#FF4E4E" />
<circle cx="13.6897" cy="14.5171" r="1.03423" fill="#FF4E4E" />
<circle cx="11.4144" cy="7.48439" r="0.930805" fill="#FF4E4E" />
<circle cx="17.5164" cy="20.3089" r="1.86161" fill="#FF4E4E" />
<circle cx="21.3432" cy="6.7604" r="0.930805" fill="#FF4E4E" />
<circle cx="22.1706" cy="1.89958" r="1.44792" fill="#FF4E4E" />
<circle cx="26.8245" cy="8.31185" r="0.72396" fill="#FF4E4E" />
</svg>
</button>
{{-- BANNER --}}
<div id="cn-banner" tabindex="-1">
<div class="cn-modal-top-line">
<button id="cn-close-modal">X</button>
</div>
{{-- CONTENT VIEW --}}
<div class="cn-content">
{{-- 1ST PAGE BANNER --}}
<div class="cn-banner-content">
<div class="cn-banner-img">
<img src="{{ $logo_picture_url }}" alt="{{ $logo_alt_text }}">
</div>
<div class="cn-banner-text">
<div class="cn-banner-title">
<span>{!! nl2br(e($strings->banner_title)) !!}</span>
</div>
<div class="cn-banner-para-p">
<p class="cn-banner-para-p-close">{!! nl2br(e($strings->banner_text)) !!}</p>
<p class="cn-banner-para-p-open d-none">{!! nl2br(e($strings->banner_text_open)) !!}</p>
</div>
</div>
<div class="cn-banner-details">
<p>
{!! nl2br(e($strings->banner_details)) !!}<br>
<a href="{{ $confidentiality_page_url }}" title="{{ $strings->banner_details_link_title }}">{{ $strings->banner_details_link }}</a>
</p>
</div>
<div class="cn-banner-btn-details">
<button class="cn-banner-btn-details-p" data-open="{{ $strings->banner_less_details }}" data-close="{{ $strings->banner_more_details }}">{!!
nl2br(e($strings->banner_more_details))
!!}</button>
</div>
</div>
{{-- 2ND PAGE CUSTOMIZE PANEL --}}
<div class="cn-modal-container d-none">
<p class="cn-modal-title" aria-level="h2">{{ $strings->Panel_title }}</p>
<p class="cn-modal-subtitle">{!! nl2br(e($strings->Panel_subtitle)) !!}</p>
<p class="cn-modal-description">{!! nl2br(e($strings->Panel_details)) !!}</p>
<div class="cn-modal-wrapper">
{{-- COOKIES LIST --}}
<ul id="cn-modal-list"></ul>
</div>
</div>
</div>
{{-- NAV BAR --}}
<div class="cn-nav">
<button id="cn-deny-banner" class="cn-banner-btn cn-deny">{{ $strings->button_forbid_all }}</button>
<button id="cn-accept-banner" class="cn-banner-btn cn-accept">{{ $strings->button_allow_all }}</button>
<button id="cn-settings-banner" class="cn-banner-btn cn-custom" data-open-modal="{{ $strings->button_back }}" data-close-modal="{{ $strings->button_customize }}">{{
$strings->button_customize
}}</button>
</div>
{{-- COOKIE LIST LI TEMPLATE --}}
<div id="cn-li-template">
<div class="cn-cookie-wrapper">
<div class="cn-cookie-infos">
<span class="cn-cookie-name">#name</span>
<span class="cn-cookie-description">#description</span>
<a class="cn-cookie-link">#link</a>
</div>
<div class="cn-cookie-buttons">
<div class="deny">
<input type="radio" id="" name="" data-name="" data-status="false">
<label for="" role="button" tabindex="0">{{ $strings->button_forbid }}</label>
</div>
<div class="accept">
<input type="radio" id="" name="" data-name="" data-status="true">
<label for="" role="button" tabindex="0">{{ $strings->button_allow }}</label>
</div>
</div>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,80 @@
<div id="cn-wrapper" tabindex="-1" role="dialog">
<button id="cn-cookie-btn" title="{{ $strings->button_customize_cookies }}">
{{-- COOKIE SVG --}}
<img src="{{ Vite::asset('resources/assets/images/cookie-icon.png') }}" alt="{{ __('Cookie Icon') }}">
</button>
{{-- BANNER --}}
<div id="cn-banner" tabindex="-1">
<div class="cn-modal-top-line">
<button id="cn-close-modal">X</button>
</div>
{{-- CONTENT VIEW --}}
<div class="cn-content">
{{-- 1ST PAGE BANNER --}}
<div class="cn-banner-content">
<div class="cn-banner-img">
<img src="{{ $logo_picture_url }}" alt="{{ $logo_alt_text }}">
</div>
<div class="cn-banner-text">
<div class="cn-banner-title">
<span>{!! nl2br(e($strings->banner_title)) !!}</span>
</div>
<div class="cn-banner-para-p">
<p class="cn-banner-para-p-close">{!! nl2br(e($strings->banner_text)) !!}</p>
<p class="cn-banner-para-p-open d-none">{!! nl2br(e($strings->banner_text_open)) !!}</p>
</div>
</div>
<div class="cn-banner-details">
<p>
{!! nl2br(e($strings->banner_details)) !!}<br>
<a href="{{ $confidentiality_page_url }}" title="{{ $strings->banner_details_link_title }}">{{ $strings->banner_details_link }}</a>
</p>
</div>
<div class="cn-banner-btn-details">
<button class="cn-banner-btn-details-p" data-open="{{ $strings->banner_less_details }}" data-close="{{ $strings->banner_more_details }}">{!!
nl2br(e($strings->banner_more_details))
!!}</button>
</div>
</div>
{{-- 2ND PAGE CUSTOMIZE PANEL --}}
<div class="cn-modal-container d-none">
<p class="cn-modal-title" aria-level="h2">{{ $strings->Panel_title }}</p>
<p class="cn-modal-subtitle">{!! nl2br(e($strings->Panel_subtitle)) !!}</p>
<p class="cn-modal-description">{!! nl2br(e($strings->Panel_details)) !!}</p>
<div class="cn-modal-wrapper">
{{-- COOKIES LIST --}}
<ul id="cn-modal-list"></ul>
</div>
</div>
</div>
{{-- NAV BAR --}}
<div class="cn-nav">
<button id="cn-deny-banner" class="cn-banner-btn cn-deny">{{ $strings->button_forbid_all }}</button>
<button id="cn-accept-banner" class="cn-banner-btn cn-accept">{{ $strings->button_allow_all }}</button>
<button id="cn-settings-banner" class="cn-banner-btn cn-custom" data-open-modal="{{ $strings->button_back }}" data-close-modal="{{ $strings->button_customize }}">{{
$strings->button_customize
}}</button>
</div>
{{-- COOKIE LIST LI TEMPLATE --}}
<div id="cn-li-template">
<div class="cn-cookie-wrapper">
<div class="cn-cookie-infos">
<span class="cn-cookie-name">#name</span>
<span class="cn-cookie-description">#description</span>
<a class="cn-cookie-link">#link</a>
</div>
<div class="cn-cookie-buttons">
<div class="deny">
<input type="radio" id="" name="" data-name="" data-status="false">
<label for="" role="button" tabindex="0">{{ $strings->button_forbid }}</label>
</div>
<div class="accept">
<input type="radio" id="" name="" data-name="" data-status="true">
<label for="" role="button" tabindex="0">{{ $strings->button_allow }}</label>
</div>
</div>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,152 @@
@php
$site_name = Str::ucfirst(config('app.name'));
$logo_picture_url = Vite::asset('resources/assets/images/Icons/logo-nature-respect.svg');
$logo_alt_text = "Logo {$site_name}";
$confidentiality_page_url = route('legals');
$strings = (object)[
'button_customize_cookies' => __('cookie-notice.button_customize_cookies'),
'button_forbid' => __('cookie-notice.button_forbid'),
'button_allow' => __('cookie-notice.button_allow'),
'button_forbid_all' => Str::upper(__('cookie-notice.button_forbid_all')),
'button_allow_all' => Str::upper(__('cookie-notice.button_allow_all')),
'button_customize' => Str::upper(__('cookie-notice.button_customize')),
'button_back' => Str::upper(__('cookie-notice.button_back')),
'banner_title' => __('cookie-notice.banner_title', ['site_name' => $site_name]),
'banner_text_open' => __('cookie-notice.banner_text_open', ['site_name' => $site_name]),
'banner_text' => __('cookie-notice.banner_text'),
'banner_details' => __('cookie-notice.banner_details'),
'banner_details_link' => __('cookie-notice.banner_details_link'),
'banner_details_link_title' => __('cookie-notice.banner_details_link_title'),
'banner_more_details' => __('cookie-notice.banner_more_details'),
'banner_less_details' => __('cookie-notice.banner_less_details'),
'Panel_title' => __('cookie-notice.Panel_title'),
'Panel_subtitle' => __('cookie-notice.Panel_subtitle'),
'Panel_details' => __('cookie-notice.Panel_details'),
];
$cookies = [];
// YOUTUBE
$cookies['youtube'] = [
'name' => __('cookie-notice.custom.youtube.name'),
'description' => __('cookie-notice.custom.youtube.name'),
'labelLink' => __('cookie-notice.custom.youtube.labelLink'),
'link' => 'https://policies.google.com/technologies/cookies?hl=fr&utm_source=ucb',
'cookies' => ['NID', 'VISITOR_INFO1_LIVE', 'YSC']
];
$cookies['youtube']['script'] = <<<SCRIPT
function () {
window.__CN_VIDEO = window.__CN_VIDEO ?? {};
window.__CN_VIDEO.youtube = true;
}
SCRIPT;
$cookies['youtube']['fallback'] = <<<FALLBACK
function () {
window.__CN_VIDEO = window.__CN_VIDEO ?? {};
window.__CN_VIDEO.youtube = false;
}
FALLBACK;
// VIMEO
$cookies['vimeo'] = [
'name' => __('cookie-notice.custom.youtube.name'),
'description' => __('cookie-notice.custom.youtube.description'),
'labelLink' => __('cookie-notice.custom.youtube.labelLink'),
'link' => 'https://vimeo.com/cookie_policy',
'cookies' => []
];
$cookies['vimeo']['script'] = <<<SCRIPT
function () {
window.__CN_VIDEO = window.__CN_VIDEO ?? {};
window.__CN_VIDEO.vimeo = true;
}
SCRIPT;
$cookies['vimeo']['fallback'] = <<<FALLBACK
function () {
window.__CN_VIDEO = window.__CN_VIDEO ?? {};
window.__CN_VIDEO.vimeo = false;
}
FALLBACK;
// RECAPTCHA
$cookies['recaptcha'] = [
'name' => __('cookie-notice.custom.recaptcha.name'),
'description' => __('cookie-notice.custom.recaptcha.description'),
'link' => 'https://policies.google.com/privacy',
'labelLink' => __('En savoir +'),
'cookies' => ['NID', '__Secure-3PAPISID', '__Secure-3PSID', '__Secure-3PSIDCC']
];
$cookies['recaptcha']['script'] = <<<SCRIPT
function () {
if (window.__CN_Recaptcha) {
window.__CN_Recaptcha();
}
}
SCRIPT;
// MATOMO
$cookies['matomo'] = [
'name' => __('cookie-notice.custom.matomo.name'),
'description' => __('cookie-notice.custom.matomo.name'),
'link' => 'https://fr.matomo.org/gdpr-analytics/',
'labelLink' => __('En savoir +'),
'cookies' => ['_pk']
];
$cookies['matomo']['script'] = <<<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', '
SCRIPT;
$cookies['matomo']['script'] .= config("services.matomo.site_id");
$cookies['matomo']['script'] .= <<<SCRIPT
']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true; g.src=u+'unicorn'; s.parentNode.insertBefore(g,s);
})();
}
SCRIPT;
@endphp
{{-- INCLUDE TEMPLATE --}}
@include('fo.vendor.cookie-notice-templates/wenndanntruc', compact(
'site_name', 'logo_picture_url', 'logo_alt_text',
'confidentiality_page_url', 'strings'
))
@push('scripts')
<script nonce="{{ $nonce ?? '' }}">
document.addEventListener('DOMContentLoaded', function () {
window.WebCookieNotice = {};
@foreach($cookies as $index_name => $cookie)
window.WebCookieNotice['{{ $index_name }}'] = {};
window.WebCookieNotice['{{ $index_name }}'].tmp = {};
// SCRIPT
window.WebCookieNotice['{{ $index_name }}'].tmp.script = {!! strip_tags($cookie['script']) !!};
@php unset($cookie['script']); @endphp
// FALLBACK
@if(isset($cookie['fallback']))
window.WebCookieNotice['{{ $index_name }}'].tmp.fallback = {!! strip_tags($cookie['fallback']) !!};
@php unset($cookie['fallback']); @endphp
@endif
window.WebCookieNotice['{{ $index_name }}'].conf = @json($cookie);
window.WebCookieNotice['{{ $index_name }}'].conf.script = window.WebCookieNotice['{{ $index_name }}'].tmp.script;
window.WebCookieNotice['{{ $index_name }}'].conf.fallback = window.WebCookieNotice['{{ $index_name }}'].tmp.fallback;
window.WebCookieNotice['{{ $index_name }}'] = window.WebCookieNotice['{{ $index_name }}'].conf;
@endforeach
window.WebCookieNotice = new CookieNotice(window.WebCookieNotice);
});
</script>
@endpush

View file

@ -13,6 +13,10 @@ Route::namespace('\App\Http\Controllers\Fo')->middleware(['throttle:credentials'
// * API Route.
Route::get('stores', [StoreController::class, 'city'])->name('stores.city');
// * Iframe cookies
Route::get('/frame/{frame}', fn (string $frame) => view('fo.pages.frame', ['frame' => $frame]))
->where('frame', '[a-z]+')->name('front.frame');
Route::localized(function () {
Route::get('/', [HomeController::class, 'index'])->name('home');