Closes #32 Add youtube video on Homepage

+ remove Unused video Frame
+ FIX  Video Inlaid in Cookie Notice
This commit is contained in:
Jérémy Munsch 2023-12-21 15:42:36 +01:00
parent f06e87daf5
commit 608629307f
No known key found for this signature in database
GPG key ID: 18361AF3BFEC0FEC
11 changed files with 218 additions and 94 deletions

View file

@ -2,6 +2,7 @@
namespace App\Http\Requests\Bo\Homepage;
use App\Rules\IsValidYoutubeId;
use App\Traits\Requests\HasPicture;
use Illuminate\Foundation\Http\FormRequest;
@ -26,6 +27,9 @@ class UpdateHomepageRequest extends FormRequest
*/
protected function prepareForValidation()
{
$this->merge([
'display_video' => $this->boolean('display_video')
]);
$this->mergePicture('picture');
}
@ -44,10 +48,15 @@ class UpdateHomepageRequest extends FormRequest
'picture_title' => 'required|string|between:3,255',
'presentation_title' => 'required|string|between:3,255',
'presentation_content' => 'required|string',
'display_video' => 'present|boolean',
'youtube_video_id' => [
'nullable', 'required_if:display_video,true',
new IsValidYoutubeId()
],
];
return \array_merge(
$rules,
$this->pictureRules('picture', 640, 390, 640, 390, false),
$this->pictureRules('picture', 640, 390, 640, 390, true, requiredIf: 'display_video,false'),
);
}

View file

@ -11,9 +11,11 @@ use Illuminate\Database\Eloquent\Model;
* @property string $title_part1 Title part 1.
* @property string $title_part2 Title part 2.
* @property string $title_part3 Title part 3.
* @property string $picture Path of the picture (640px * 390px).
* @property string|null $picture Path of the picture (640px * 390px).
* @property string $picture_alt Picture alt attribute.
* @property string $picture_title Picture title attribute.
* @property string $youtube_video_id The youtube video Id.
* @property boolean $display_video Wether to display video or picture.
* @property string $presentation_title Presentation title.
* @property string $presentation_content WYSIWYG - Presentation content.
* @property-read \Carbon\Carbon $created_at Created date.
@ -38,6 +40,17 @@ class Homepage extends Model
'picture_title',
'presentation_title',
'presentation_content',
'youtube_video_id',
'display_video',
];
/**
* The attributes that should be cast.
*
* @var string[]
*/
protected $casts = [
'display_video' => 'bool'
];
/**

View file

@ -0,0 +1,33 @@
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Str;
/**
* Validate a youtube ID regex
*
* @url https://webapps.stackexchange.com/questions/54443/format-for-id-of-youtube-video
* @package App\Rules
*/
class IsValidYoutubeId implements ValidationRule
{
/**
* Run the validation rule.
*
* @param string $attribute
* @param mixed $value
* @param \Closure(string):\Illuminate\Translation\PotentiallyTranslatedString $fail
* @return void
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceBeforeLastUsed
* @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint
*/
public function validate(string $attribute, mixed $value, \Closure $fail): void
{
// phpcs:enable
if (!Str::of($value)->isMatch('/[0-9A-Za-z_-]{10}[048AEIMQUYcgkosw]/')) {
$fail(trans('validation.youtube_id'));
}
}
}

View file

@ -0,0 +1,43 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up(): void
{
Schema::table('homepages', function (Blueprint $table) {
$table->string('picture')->nullable()->change()
->comment('Path de l\'image de la page d\'accueil (640px * 390px)');
$table->string('youtube_video_id')->after('picture_title')
->nullable()
->default(null)
->comment('L\ídentifiant de la vidéo Youtube');
$table->boolean('display_video')->after('youtube_video_id')
->default(false)
->comment('Afficher la video ou l\'image');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down(): void
{
Schema::table('homepages', function (Blueprint $table) {
$table->string('picture')->change()
->comment('Path de l\'image de la page d\'accueil (640px * 390px)');
$table->dropColumn('youtube_video_id');
$table->dropColumn('display_video');
});
}
};

View file

@ -215,6 +215,9 @@ return [
'year' => 'year',
'url' => 'URL',
],
'youtube_id' => ':attribute must be a valid Youtube video ID',
'custom' => [
'of_the' => [
'male' => 'of the ',

View file

@ -217,6 +217,9 @@ return [
'year' => 'année',
'url' => 'URL',
],
'youtube_id' => ':attribute doit être un ID de vidéo Youtube valide',
'custom' => [
'of_the' => [
'male' => 'du ',
@ -287,5 +290,7 @@ return [
'unpublish_at' => 'date de fin de publication',
'unpublish_at' => 'date de fin de publication',
'video_id' => 'ID de la vidéo',
'youtube_video_id' => 'ID de la vidéo youtube',
'display_video' => 'affichage de la vidéo',
],
];

View file

@ -2,74 +2,6 @@ import Swiper from "swiper";
import { Navigation } from "swiper/modules";
document.addEventListener("DOMContentLoaded", function () {
// * Video frames.
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);
}
// * Video inlaid.
const videoInlaidList = document.querySelectorAll(
".video-inlaid"
) as NodeListOf<HTMLLinkElement>;
for (const videoLink of videoInlaidList) {
if (!videoLink.getAttribute("type")) {
throw new Error("Missing attribute type");
}
if (!videoLink.getAttribute("href")) {
throw new Error("Missing attribute href");
}
// @ts-ignore
if (window.__CN_VIDEO[videoLink.getAttribute("type")]) {
videoLink.innerHTML = "";
const newIframe = document.createElement("iframe");
newIframe.frameBorder = "0";
newIframe.allow = "fullscreen";
newIframe.width = "1920px";
newIframe.height = "1080px";
newIframe.classList.add("w-100");
newIframe.classList.add("h-100");
newIframe.setAttribute("allowfullscreen", "allowfullscreen");
newIframe.src = String(videoLink.getAttribute("href"));
videoLink.appendChild(newIframe);
}
}
// SWIPER
const swiperConfig = {
"swiper-homepage-news": {

View file

@ -162,13 +162,67 @@
'widthpx' => '640px',
'heightpx' => '390px',
]),
'required' => true,
'required' => false,
'preview' => true,
])
</div>
</div>
</fieldset>
</div>
{{-- YOUTUBE VIDEO ID --}}
<div class="col-12 col-md-6 mb-3">
<fieldset class="h-100 border p-3">
<legend>{{ Str::ucfirst(__('validation.custom.youtube_video_id')) }}</legend>
<div class="row">
<div class="col-12">
@include('bo.partials.inputs.varchar', [
'id' => 'homepageVideoUrl',
'label' => __(':field :inter:model', [
'field' => Str::ucfirst(__('validation.custom.youtube_video_id')),
'inter' => __('validation.custom.of_the.female'),
'model' => __('models.classes.homepage'),
]),
'hasWordCounter' => true,
'name' => 'youtube_video_id',
'required' => false,
'model' => $homepageModel ?? null,
'helper' =>
__('validation.between.string', [
'attribute' => __(':field :inter:model', [
'field' => __('validation.custom.youtube_video_id'),
'inter' => __('validation.custom.of_the.female'),
'model' => __('models.classes.homepage'),
]),
'min' => 11,
'max' => 12,
]) . __("Doit être l\'id de la vidéo Youtube eg:\u{00A0}(MH19CxWqluY)."),
'placeholder' => 'MH19CxWqluY',
])
</div>
</div>
</fieldset>
</div>
{{-- DISPLAY VIDEO --}}
<div class="col-12 col-md-6 mb-3">
<fieldset class="h-100 border p-3">
<legend>{{ Str::ucfirst(__('validation.custom.display_video')) }}</legend>
<div class="row">
<div class="col-12">
@include('bo.partials.inputs.tinyint-boolean', [
'id' => 'homepageDisplayVideo',
'name' => 'display_video',
'model' => $homepageModel ?? null,
'label' => __('validation.custom.display_video'),
'label' => __('crud.minicrud.visibility_label'),
'helper' => __('crud.minicrud.visibility_helper'),
])
</div>
</div>
</fieldset>
</div>
{{-- PRESENTATION TITLE --}}
<div class="col-12 col-md-6 mb-3">
<fieldset class="h-100 border p-3">

View file

@ -1,14 +0,0 @@
@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

@ -9,8 +9,8 @@
<section class="homepage">
<!-- TITLE CARD -->
<div class="title-card container-fluid my-xl-5 my-0 px-4">
<div class="d-flex flex-column flex-xl-row justify-content-center align-items-center w-100 flex-wrap">
<div class="bg-agis-yellow p-xl-5 p-4 px-5">
<div class="row justify-content-center align-items-center">
<div class="col-12 col-md-6 col-lg-5 col-xl-4 bg-agis-yellow p-xl-5 g-0 p-4 px-5">
<h1 class="my-2 text-center">
<p class="fs-title-xxl f-sheldon text-uppercase m-0">{{ $homepageModel->title_part1 }}</p>
<span class="divide d-block w-100 mt-xl-3 my-2"></span>
@ -19,9 +19,22 @@
<p class="fs-title-l f-rufina fw-semibold m-0">{{ Str::of($homepageModel->title_part3)->ucFirst()->value() }}</p>
</h1>
</div>
<div class="m-xl-0 my-4 mb-5">
<img src="{{ asset($homepageModel->picture) }}" title="{{ $homepageModel->picture_title }}"
alt="{{ $homepageModel->picture_alt }}">
<div class="col-12 col-md-6 col-lg-5 col-xl-4 m-xl-0 g-0 my-4">
@if ($homepageModel->display_video)
<div class="ratio ratio-16x9 y-video">
<div class="video-inlaid" type="youtube"
href="https://www.youtube.com/embed/{{ $homepageModel->youtube_video_id }}">
<a class="cn-cookie-link" id="youtube-warning" href="" rel="nofollow">
<strong class="text-underline text-bold text-normal text-danger">
{{ __('cookie-notice.custom.youtube.warning') }}
</strong>
</a>
</div>
</div>
@else
<img class="img-fluid" src="{{ asset($homepageModel->picture) }}" title="{{ $homepageModel->picture_title }}"
alt="{{ $homepageModel->picture_alt }}">
@endif
</div>
</div>
</div>

View file

@ -43,14 +43,47 @@ $cookies['youtube'] = [
$cookies['youtube']['script'] = <<<SCRIPT
function () {
window.__CN_VIDEO = window.__CN_VIDEO ?? {};
window.__CN_VIDEO.youtube = true;
// * Video inlaid.
const videoInlaidList = document.querySelectorAll(".video-inlaid");
for (const videoLink of videoInlaidList) {
if (!videoLink.getAttribute("type")) {
throw new Error("Missing attribute type");
}
if (!videoLink.getAttribute("href")) {
throw new Error("Missing attribute href");
}
videoLink.innerHTML = "";
const newIframe = document.createElement("iframe");
newIframe.frameBorder = "0";
newIframe.allow = "fullscreen";
newIframe.width = "1920px";
newIframe.height = "1080px";
newIframe.classList.add("w-100");
newIframe.classList.add("h-100");
newIframe.setAttribute("allowfullscreen", "allowfullscreen");
newIframe.src = String(videoLink.getAttribute("href"));
videoLink.appendChild(newIframe);
}
}
SCRIPT;
$cookies['youtube']['fallback'] = <<<FALLBACK
function () {
window.__CN_VIDEO = window.__CN_VIDEO ?? {};
window.__CN_VIDEO.youtube = false;
// * Video inlaid.
const videoInlaidList = document.querySelectorAll(".video-inlaid");
for (const item of videoInlaidList) {
let a = document.createElement('a');
a.innerText = 'Autoriser';
a.classList.add('cn-cookie-link');
a.setAttribute('role', 'button');
a.onclick = function () {
WebCookieNotice.openCookieModalToCookiePage();
};
let e = document.createElement('p');
e.innerHTML = 'Youtube est désactivé. ';
item.innerHTML = "";
e.appendChild(a);
item.appendChild(e);
}
}
FALLBACK;