Add SlidersController

This commit is contained in:
Sebastien Hattry 2021-12-07 23:35:14 +01:00
parent 400ebc164b
commit 960ad7bc2f
36 changed files with 1015 additions and 164 deletions

20
app/Enums/Role.php Normal file
View file

@ -0,0 +1,20 @@
<?php
namespace App\Enums;
use Kwaadpepper\Enum\BaseEnumRoutable;
/**
* @method static self admin()
* @method static self user()
*/
class Role extends BaseEnumRoutable
{
protected static function values(): array
{
return [
'admin' => 0,
'user' => 1,
];
}
}

View file

@ -4,8 +4,8 @@ namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\StoreSliderRequest;
use App\Http\Requests\UpdateSliderRequest;
use App\Models\Slider;
use Illuminate\Support\Str;
class SlidersController extends Controller
{
@ -38,18 +38,11 @@ class SlidersController extends Controller
*/
public function store(StoreSliderRequest $request)
{
$label = $request->label;
$slug = $request->slug;
if (empty($slug)) {
$slug = Str::slug($label);
}
$commentaire = $request->commentaire;
$slider = new Slider($request->validated());
$slider = new Slider();
$slider->label = $label;
$slider->slug = $slug;
$slider->commentaire = $commentaire;
$slider->save();
if (!$slider->saveOrFail()) {
return redirect()->route('bo.sliders.edit', $slider->id)->with('error', 'Le slider n\'a pas été crée');
}
return redirect()->route('bo.sliders.edit', $slider->id)->with('success', 'Slider créé !');
}
@ -70,9 +63,8 @@ class SlidersController extends Controller
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
public function edit(Slider $slider)
{
$slider = Slider::findOrFail($id);
return view('back.sliders.edit', compact('slider'));
}
@ -83,17 +75,13 @@ class SlidersController extends Controller
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(StoreSliderRequest $request, $id)
public function update(UpdateSliderRequest $request, Slider $slider)
{
$slider = Slider::findOrFail($id);
$label = $request->label;
$slug = $request->slug;
$commentaire = $request->commentaire;
$slider->fill($request->validated());
$slider->label = $label;
$slider->slug = $slug;
$slider->commentaire = $commentaire;
$slider->save();
if (!$slider->saveOrFail()) {
return redirect()->route('bo.sliders.edit', $slider->id)->with('error', 'La modification a échoué');
}
return redirect()->route('bo.sliders.edit', $slider->id)->with('success', 'Modifications enregistrées !');
}
@ -104,10 +92,8 @@ class SlidersController extends Controller
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
public function destroy(Slider $slider)
{
$slider = Slider::findOrFail($id);
$count = Slider::count();
if ($count === 1) {
@ -115,7 +101,9 @@ class SlidersController extends Controller
}
// @todo : controller les slides liés
$slider->delete();
return redirect()->route('bo.sliders.index');
if (!$slider->delete()) {
return redirect()->route('bo.sliders.index')->with('error', trans('La suppression a échoué'));
}
return redirect()->route('bo.sliders.index')->with('success', trans('Suppression faite avec succès'));
}
}

View file

@ -4,6 +4,7 @@ namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\StoreSlideRequest;
use App\Lib\Utils;
use App\Models\Slide;
use App\Models\Slider;
use Illuminate\Support\Str;
@ -40,18 +41,11 @@ class SlidesController extends Controller
*/
public function store(StoreSlideRequest $request)
{
$picture = $request->picture;
$slide = new Slide($request->validated());
$slide->order = $this->getLastOrder();
$slide->save();
if ($picture !== null) {
$imageName = $slide->id . '_' . Str::random(5) . '.' . $picture->extension();
$picture->move(public_path('images/slides'), $imageName);
$slide->picture = $imageName;
$slide->save();
}
$slide->saveOrFail();
return redirect()->route('bo.slides.edit', $slide->id)->with('success', 'Slide créé !');
}
@ -88,19 +82,8 @@ class SlidesController extends Controller
*/
public function update(StoreSlideRequest $request, Slide $slide)
{
$picture = $request->picture;
$slide->fill($request->validated());
if ($picture !== null) {
if (file_exists(public_path() . 'images/slides/' . $slide->picture)) {
unlink(public_path() . 'images/slides/' . $slide->picture);
}
$imageName = $slide->id . '_' . Str::random(5) . '.' . $picture->extension();
$picture->move(public_path('images/slides'), $imageName);
$slide->picture = $imageName;
}
if (!$slide->saveOrFail()) {
return redirect()->route('bo.slides.edit', $slide->id)->with('error', trans('La modification a échoué'));
}

View file

@ -17,8 +17,8 @@ class StoreSlideRequest extends FormRequest
{
try {
$this->merge([
'date_start' => Carbon::parse("$this->date_start_date $this->date_start_time:00"),
'date_end' => Carbon::parse("$this->date_end_date $this->date_end_time:00"),
'date_start' => Carbon::parse("$this->date_start_date $this->date_start_time:00")->toDateTimeString(),
'date_end' => Carbon::parse("$this->date_end_date $this->date_end_time:00")->toDateTimeString(),
]);
// phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
} catch (InvalidFormatException $e) {
@ -43,7 +43,7 @@ class StoreSlideRequest extends FormRequest
return [
'title' => 'required|max:255',
'text' => 'sometimes|max:255',
'picture' => 'sometimes|image|mimes:jpeg,png,jpg|max:2048',
'picture' => 'sometimes|image|mimes:jpeg,png,jpg|max:2048|dimensions:width=1920,height=812',
'link_label' => 'sometimes|max:255',
'link' => 'sometimes|max:255',
'link_new_tab' => 'required|boolean',

View file

@ -3,6 +3,7 @@
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Str;
class StoreSliderRequest extends FormRequest
{
@ -13,7 +14,12 @@ class StoreSliderRequest extends FormRequest
*/
public function authorize()
{
return false;
$this->merge([
'slug' => Str::slug($this->label),
'published' => $this->published ? true : false
]);
return auth()->check();
}
/**
@ -25,7 +31,31 @@ class StoreSliderRequest extends FormRequest
{
return [
'label' => 'required|string|max:255',
'slug' => 'sometimes|unique:sliders',
'slug' => 'required|unique:sliders',
'commentaire' => 'sometimes|max:255',
'published' => 'required|boolean',
];
}
/**
* Get custom messages for validator errors.
*
* @return array
*/
public function messages()
{
return [];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes()
{
return [
'slug' => trans('label')
];
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace App\Http\Requests;
class UpdateSliderRequest extends StoreSliderRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return array_merge(
parent::rules(),
[
'slug' => 'required',
]
);
}
}

328
app/Lib/Utils.php Normal file
View file

@ -0,0 +1,328 @@
<?php
namespace App\Lib;
use App\Models\Customers;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use SplFileInfo;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Throwable;
/**
* Some utilities methods
*
* @author Jérémy Munsch <jeremy.munsch@ldc.fr>
*/
// phpcs:disable Generic.Files.LineLength.TooLong
class Utils
{
/**
* Replace stange accents
*
* https://stackoverflow.com/questions/3371697/replacing-accented-characters-php
* @author Jérémy Munsch <jeremy.munsch@jeremydev.ovh>
* @param string $subject
* @return string
*/
public static function replaceSpecChar(string $subject): string
{
$char_map = array(
"ъ" => "-", "ь" => "-", "Ъ" => "-", "Ь" => "-",
"А" => "A", "Ă" => "A", "Ǎ" => "A", "Ą" => "A", "À" => "A", "Ã" => "A", "Á" => "A", "Æ" => "A", "Â" => "A", "Å" => "A", "Ǻ" => "A", "Ā" => "A", "א" => "A",
"Б" => "B", "ב" => "B", "Þ" => "B",
"Ĉ" => "C", "Ć" => "C", "Ç" => "C", "Ц" => "C", "צ" => "C", "Ċ" => "C", "Č" => "C", "©" => "C", "ץ" => "C",
"Д" => "D", "Ď" => "D", "Đ" => "D", "ד" => "D", "Ð" => "D",
"È" => "E", "Ę" => "E", "É" => "E", "Ë" => "E", "Ê" => "E", "Е" => "E", "Ē" => "E", "Ė" => "E", "Ě" => "E", "Ĕ" => "E", "Є" => "E", "Ə" => "E", "ע" => "E",
"Ф" => "F", "Ƒ" => "F",
"Ğ" => "G", "Ġ" => "G", "Ģ" => "G", "Ĝ" => "G", "Г" => "G", "ג" => "G", "Ґ" => "G",
"ח" => "H", "Ħ" => "H", "Х" => "H", "Ĥ" => "H", "ה" => "H",
"I" => "I", "Ï" => "I", "Î" => "I", "Í" => "I", "Ì" => "I", "Į" => "I", "Ĭ" => "I", "I" => "I", "И" => "I", "Ĩ" => "I", "Ǐ" => "I", "י" => "I", "Ї" => "I", "Ī" => "I", "І" => "I",
"Й" => "J", "Ĵ" => "J",
"ĸ" => "K", "כ" => "K", "Ķ" => "K", "К" => "K", "ך" => "K",
"Ł" => "L", "Ŀ" => "L", "Л" => "L", "Ļ" => "L", "Ĺ" => "L", "Ľ" => "L", "ל" => "L",
"מ" => "M", "М" => "M", "ם" => "M",
"Ñ" => "N", "Ń" => "N", "Н" => "N", "Ņ" => "N", "ן" => "N", "Ŋ" => "N", "נ" => "N", "ʼn" => "N", "Ň" => "N",
"Ø" => "O", "Ó" => "O", "Ò" => "O", "Ô" => "O", "Õ" => "O", "О" => "O", "Ő" => "O", "Ŏ" => "O", "Ō" => "O", "Ǿ" => "O", "Ǒ" => "O", "Ơ" => "O",
"פ" => "P", "ף" => "P", "П" => "P",
"ק" => "Q",
"Ŕ" => "R", "Ř" => "R", "Ŗ" => "R", "ר" => "R", "Р" => "R", "®" => "R",
"Ş" => "S", "Ś" => "S", "Ș" => "S", "Š" => "S", "С" => "S", "Ŝ" => "S", "ס" => "S",
"Т" => "T", "Ț" => "T", "ט" => "T", "Ŧ" => "T", "ת" => "T", "Ť" => "T", "Ţ" => "T",
"Ù" => "U", "Û" => "U", "Ú" => "U", "Ū" => "U", "У" => "U", "Ũ" => "U", "Ư" => "U", "Ǔ" => "U", "Ų" => "U", "Ŭ" => "U", "Ů" => "U", "Ű" => "U", "Ǖ" => "U", "Ǜ" => "U", "Ǚ" => "U", "Ǘ" => "U",
"В" => "V", "ו" => "V",
"Ý" => "Y", "Ы" => "Y", "Ŷ" => "Y", "Ÿ" => "Y",
"Ź" => "Z", "Ž" => "Z", "Ż" => "Z", "З" => "Z", "ז" => "Z",
"а" => "a", "ă" => "a", "ǎ" => "a", "ą" => "a", "à" => "a", "ã" => "a", "á" => "a", "æ" => "a", "â" => "a", "å" => "a", "ǻ" => "a", "ā" => "a", "א" => "a",
"б" => "b", "ב" => "b", "þ" => "b",
"ĉ" => "c", "ć" => "c", "ç" => "c", "ц" => "c", "צ" => "c", "ċ" => "c", "č" => "c", "©" => "c", "ץ" => "c",
"Ч" => "ch", "ч" => "ch",
"д" => "d", "ď" => "d", "đ" => "d", "ד" => "d", "ð" => "d",
"è" => "e", "ę" => "e", "é" => "e", "ë" => "e", "ê" => "e", "е" => "e", "ē" => "e", "ė" => "e", "ě" => "e", "ĕ" => "e", "є" => "e", "ə" => "e", "ע" => "e",
"ф" => "f", "ƒ" => "f",
"ğ" => "g", "ġ" => "g", "ģ" => "g", "ĝ" => "g", "г" => "g", "ג" => "g", "ґ" => "g",
"ח" => "h", "ħ" => "h", "х" => "h", "ĥ" => "h", "ה" => "h",
"i" => "i", "ï" => "i", "î" => "i", "í" => "i", "ì" => "i", "į" => "i", "ĭ" => "i", "ı" => "i", "и" => "i", "ĩ" => "i", "ǐ" => "i", "י" => "i", "ї" => "i", "ī" => "i", "і" => "i",
"й" => "j", "Й" => "j", "Ĵ" => "j", "ĵ" => "j",
"ĸ" => "k", "כ" => "k", "ķ" => "k", "к" => "k", "ך" => "k",
"ł" => "l", "ŀ" => "l", "л" => "l", "ļ" => "l", "ĺ" => "l", "ľ" => "l", "ל" => "l",
"מ" => "m", "м" => "m", "ם" => "m",
"ñ" => "n", "ń" => "n", "н" => "n", "ņ" => "n", "ן" => "n", "ŋ" => "n", "נ" => "n", "ʼn" => "n", "ň" => "n",
"ø" => "o", "ó" => "o", "ò" => "o", "ô" => "o", "õ" => "o", "о" => "o", "ő" => "o", "ŏ" => "o", "ō" => "o", "ǿ" => "o", "ǒ" => "o", "ơ" => "o",
"פ" => "p", "ף" => "p", "п" => "p",
"ק" => "q",
"ŕ" => "r", "ř" => "r", "ŗ" => "r", "ר" => "r", "р" => "r", "®" => "r",
"ş" => "s", "ś" => "s", "ș" => "s", "š" => "s", "с" => "s", "ŝ" => "s", "ס" => "s",
"т" => "t", "ț" => "t", "ט" => "t", "ŧ" => "t", "ת" => "t", "ť" => "t", "ţ" => "t",
"ù" => "u", "û" => "u", "ú" => "u", "ū" => "u", "у" => "u", "ũ" => "u", "ư" => "u", "ǔ" => "u", "ų" => "u", "ŭ" => "u", "ů" => "u", "ű" => "u", "ǖ" => "u", "ǜ" => "u", "ǚ" => "u", "ǘ" => "u",
"в" => "v", "ו" => "v",
"ý" => "y", "ы" => "y", "ŷ" => "y", "ÿ" => "y",
"ź" => "z", "ž" => "z", "ż" => "z", "з" => "z", "ז" => "z", "ſ" => "z",
"™" => "tm",
"@" => "at",
"Ä" => "ae", "Ǽ" => "ae", "ä" => "ae", "æ" => "ae", "ǽ" => "ae",
"ij" => "ij", "IJ" => "ij",
"я" => "ja", "Я" => "ja",
"Э" => "je", "э" => "je",
"ё" => "jo", "Ё" => "jo",
"ю" => "ju", "Ю" => "ju",
"œ" => "oe", "Œ" => "oe", "ö" => "oe", "Ö" => "oe",
"щ" => "sch", "Щ" => "sch",
"ш" => "sh", "Ш" => "sh",
"ß" => "ss",
"Ü" => "ue",
"Ж" => "zh", "ж" => "zh",
);
return self::mbStrtr($subject, $char_map);
}
/**
* Multibyte strtr
*
* @param string $str
* @param string|array $from
* @param string|null $to
* @return void
*/
public static function mbStrtr($str, $from, $to = null): string
{
if (is_array($from)) {
$from = array_map('utf8_decode', $from);
$from = array_map('utf8_decode', array_flip($from));
return utf8_encode(strtr(utf8_decode($str), array_flip($from)));
}
return utf8_encode(strtr(utf8_decode($str), utf8_decode($from), utf8_decode($to)));
}
/**
* Assert That no Back Office url is present on txt
*
* @param string $field
* @param string $text
* @return string
* @throws \Illuminate\Validation\ValidationException
*/
public static function assertNoInternalUrls(string $field, string $text): string
{
$routes = Route::getRoutes();
// url replace and email replace
$text = preg_replace_callback(
'/(?P<url>(?:http[s]?:\/\/.)?(?:www\.)?[-a-zA-Z0-9@%._\+~#=]{2,256}\.[a-z]{2,6}\b(?:[-a-zA-Z0-9@:%_\+.~#?&\/\/=]*))/',
function ($matches) use ($routes, $field) {
$match = collect($matches)->first() ?? '';
$url = $match;
try {
$request = Request::create($url);
// if a route is match then it do not throws exception
if ($routes->match($request)->getPrefix() === 'bo') {
// removes the matched url
throw ValidationException::withMessages([
$field => trans($field) . " : Il est interdit de coller une url du Back Office Le Gaulois dans ce champ"
]);
}
return $match;
} catch (NotFoundHttpException $e) {
return $match;
}
},
$text
);
return $text;
}
public static function dotStringToNestedArray($path, $value, $separator = '.')
{
$arr = $value;
$keys = array_reverse(explode($separator, $path));
foreach ($keys as $key) {
$arr = [$key => $arr];
}
return $arr;
}
/**
* Store an image for a model
* if a string is passed then the store action is ignored and the strieng is returned
*
* @param \Illuminate\Http\UploadedFile|\SplFileInfo|string $image
* @param string $folderName
* @return string The storage path
*/
public static function storeImage($image, string $folderName): string
{
if (\is_object($image) and get_class($image) === UploadedFile::class) {
/** @var UploadedFile */
$image = $image;
$newPath = self::prepareMoveFile($image->getClientOriginalName(), $folderName);
$newPath = str_replace(storage_path('app'), '', $newPath);
$image->storePubliclyAs(pathinfo($newPath, \PATHINFO_DIRNAME), pathinfo($newPath, \PATHINFO_BASENAME));
return '/storage' . str_replace('/public', '', $newPath);
}
if (\is_object($image) and get_class($image) === SplFileInfo::class) {
/** @var SplFileInfo */
$image = $image;
$newPath = self::prepareMoveFile($image->getBaseName(), $folderName);
if (!File::copy($image->getRealPath(), $newPath)) {
throw new \Exception(sprintf(
'Failed to copy file %s to %s',
$image->getRealPath(),
$newPath
));
}
return str_replace(\storage_path('app/public'), '/storage', $newPath);
}
if (\is_string($image)) {
return $image;
}
throw new Exception(sprintf('Cannot handle image %s', \gettype($image)));
}
private static function prepareMoveFile(string $filename, string $folderName): string
{
$pathFolder = \storage_path("app/public/images/$folderName");
if (!File::exists($pathFolder)) {
File::makeDirectory($pathFolder, 0755, true);
}
$filename = sprintf('%s-%s', Str::uuid(), $filename);
$fullPath = sprintf('%s/%s', $pathFolder, $filename);
if (File::exists($fullPath)) {
File::delete($fullPath);
}
return $fullPath;
}
/**
* Manually send an exception to team instead of going trough the error handler
* (requires package kwaadpepper/exception-handler)
*
* @param Throwable $e
* @return void
*/
public static function sendEmailErrorManually(Throwable $e)
{
Mail::send([
'html' => 'exception-handler::emails.exception',
'text' => 'exception-handler::emails.exception-plain'
], [
'appName' => config('app.name'),
'code' => $e->getCode(),
'line' => $e->getLine(),
'ename' => \get_class($e),
'emessage' => self::getAnonymizedMessage($e->getMessage()),
'file' => str_replace(\realpath(\sprintf('%s/../', \app_path())), '', $e->getFile()),
'stackTrace' => self::getAnonymizedStackTrace($e),
'uri' => app()->runningInConsole() ?
'CONSOLE' : (
(request() and request()->fullUrl()) ?
request()->fullUrl() : 'none')
], function ($m) {
/** @var \Illuminate\Mail\Message $m */
$m->to(
config('mail.contactsList.serviceweb'),
sprintf('%s Exception Handler', config('app.name'))
)->subject('Need help !, an error occured in your application, please check Logs ASAP');
});
Log::alert(sprintf('Envoie d\'une erreur par mail : %s', json_encode($e)));
}
/**
* Generate an unique pseudo
*
* @param string $firstname
* @param string $lastname
* @param integer $i
* @return void
*/
public static function getUniquePseudo($firstname, $lastname, int $i = 0)
{
$pseudo = $firstname . '.' . substr($lastname, 0, 1);
if ($i > 0) {
$pseudo .= "-" . Str::random(8);
}
$customer = Customers::where('username', $pseudo)->first();
if ($customer) {
return self::getUniquePseudo($firstname, $lastname, $i + 1);
}
return $pseudo;
}
/**
* Anonymize at least Sql queries
*
* @param string $message
* @return string
*/
private static function getAnonymizedMessage(string $message): string
{
$message = \preg_replace('/^(.*)( \(SQL:.*\))$/', '$1', $message) ?? $message;
return $message;
}
/**
* Anonymize stack trace
*
* @param \Exception $e
* @return string
*/
private static function getAnonymizedStackTrace(Throwable $e): string
{
$projectPath = \realpath(\sprintf('%s/../', \app_path()));
$traceStack = $e->getTrace();
$traceStr = '';
$maxStackStage = 0;
foreach ($traceStack as $stackStage => $stack) {
$maxStackStage = $stackStage;
$traceStr .= \sprintf(
'#%s %s(%d): ',
$stackStage,
str_replace($projectPath, '', $stack['file']),
$stack['line']
);
$traceStr .= "{$stack['class']}->{$stack['function']}(";
$traceArgs = '';
if (isset($stack['args'])) {
$argsCount = count($stack['args']);
for ($i = 0; $i < $argsCount; $i++) {
$type = \gettype($stack['args'][$i]);
switch ($type) {
case 'object':
$traceArgs .= sprintf('Object(%s)', \get_class($stack['args'][$i]));
break;
case 'array':
$traceArgs .= sprintf('Array(%d)', \count($stack['args'][$i]));
break;
case 'string':
$traceArgs .= sprintf('String(%d)', \strlen($stack['args'][$i]));
break;
default:
$traceArgs .= \gettype($stack['args'][$i]);
}
$traceArgs .= ($i !== ($argsCount - 1) ? ',' : '');
}
$traceStr .= $traceArgs;
}
$traceStr .= ")\n";
}
$maxStackStage++;
$traceStr .= "#$maxStackStage {main}";
return $traceStr;
}
}

View file

@ -2,6 +2,7 @@
namespace App\Models;
use App\Lib\Utils;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
@ -26,10 +27,27 @@ class Slide extends Model
'link',
'link_new_tab',
'slider_id',
'picture',
'date_start',
'date_end'
];
protected static function boot()
{
static::creating(function (Slide $slide) {
if ($slide->picture) {
$slide->picture = Utils::storeImage($slide->picture, $slide->getTable());
}
});
static::updating(function (Slide $slide) {
if ($slide->picture) {
$slide->picture = Utils::storeImage($slide->picture, $slide->getTable());
}
});
parent::boot();
}
public function slider()
{
return $this->belongsTo(Slider::class);

View file

@ -10,6 +10,13 @@ class Slider extends Model
{
use HasFactory;
protected $fillable = [
'label',
'slug',
'commentaire',
'published'
];
public function slides()
{
return $this->hasMany(Slide::class, 'slider_id')->orderBy('order', 'ASC');

View file

@ -2,15 +2,22 @@
namespace App\Models;
use App\Enums\Role;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Hash;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
use HasFactory;
use Notifiable;
public $castEnums = [
'role' => Role::class
];
/**
* The attributes that are mass assignable.
@ -41,4 +48,20 @@ class User extends Authenticatable
protected $casts = [
'email_verified_at' => 'datetime',
];
protected static function boot()
{
static::creating(function (User $user) {
if (Hash::needsRehash($user->password)) {
$user->password = Hash::make($user->password);
}
});
static::updating(function (User $user) {
if (Hash::needsRehash($user->password)) {
$user->password = Hash::make($user->password);
}
});
parent::boot();
}
}

View file

@ -8,6 +8,7 @@
"php": "^7.3|^8.0",
"fruitcake/laravel-cors": "^2.0",
"guzzlehttp/guzzle": "^7.0.1",
"kwaadpepper/enum": "^2.1",
"laravel/framework": "^8.65",
"laravel/sanctum": "^2.11",
"laravel/tinker": "^2.5",

62
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "4e38664dc2e1313269ea0f10ec8cbadf",
"content-hash": "8f7837eb85b0725169b90fb56c8b1646",
"packages": [
{
"name": "asm89/stack-cors",
@ -883,6 +883,66 @@
],
"time": "2021-10-06T17:43:30+00:00"
},
{
"name": "kwaadpepper/enum",
"version": "2.1.2",
"source": {
"type": "git",
"url": "https://github.com/Kwaadpepper/enum.git",
"reference": "bbd08723895cad5a41bc0d57e565fe64fa13ca41"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Kwaadpepper/enum/zipball/bbd08723895cad5a41bc0d57e565fe64fa13ca41",
"reference": "bbd08723895cad5a41bc0d57e565fe64fa13ca41",
"shasum": ""
},
"require": {
"php": "^7.3"
},
"require-dev": {
"orchestra/testbench": "^5",
"phpunit/php-code-coverage": "^9.2",
"phpunit/phpunit": "~9.0",
"slevomat/coding-standard": "^7.0",
"squizlabs/php_codesniffer": "^3.5"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Kwaadpepper\\Enum\\EnumServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Kwaadpepper\\Enum\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jérémy Munsch",
"email": "github@jeremydev.ovh",
"homepage": "https://jeremydev.ovh"
}
],
"description": "Enums to use for any project",
"homepage": "https://github.com/kwaadpepper/enum",
"keywords": [
"enum",
"laravel"
],
"support": {
"issues": "https://github.com/Kwaadpepper/enum/issues",
"source": "https://github.com/Kwaadpepper/enum/tree/2.1.2"
},
"time": "2021-11-18T16:52:08+00:00"
},
{
"name": "laravel/framework",
"version": "v8.69.0",

View file

@ -67,7 +67,7 @@ return [
|
*/
'timezone' => 'UTC',
'timezone' => 'Europe/Paris',
/*
|--------------------------------------------------------------------------
@ -80,7 +80,7 @@ return [
|
*/
'locale' => 'en',
'locale' => 'fr',
/*
|--------------------------------------------------------------------------
@ -106,7 +106,7 @@ return [
|
*/
'faker_locale' => 'en_US',
'faker_locale' => 'fr_FR',
/*
|--------------------------------------------------------------------------

View file

@ -0,0 +1,38 @@
<?php
namespace Database\Factories;
use App\Models\Slide;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Carbon;
use SplFileInfo;
class SlideFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = Slide::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
'title' => $this->faker->unique()->word(),
'text' => $this->faker->realText(25),
'link_label' => $this->faker->text(128),
'link' => route('wasup'),
'link_new_tab' => rand(0, 1),
'date_start' => Carbon::now(),
'date_end' => Carbon::now()->addDays(2),
'picture' => new SplFileInfo(resource_path('assets/images/image-sample.jpg'))
];
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace Database\Factories;
use App\Models\Slider;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
class SliderFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = Slider::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
$label = $this->faker->unique()->word();
return [
'label' => $label,
'slug' => Str::slug($label),
'commentaire' => $this->faker->realText(128),
'published' => rand(0, 1)
];
}
}

View file

@ -1,6 +1,5 @@
<?php
use Database\Seeders\SlidersSeeder;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
@ -17,14 +16,11 @@ class CreateSlidersTable extends Migration
Schema::create('sliders', function (Blueprint $table) {
$table->id();
$table->string('label');
$table->string('slug');
$table->unique('slug');
$table->string('slug')->unique();
$table->string('commentaire')->nullable();
$table->boolean('published')->nullable();
$table->timestamps();
});
$seeder = new SlidersSeeder();
$seeder->run();
}
/**

View file

@ -13,6 +13,11 @@ class DatabaseSeeder extends Seeder
*/
public function run()
{
// \App\Models\User::factory(10)->create();
$this->call([
UserSeeder::class,
SlidersSeeder::class
]);
}
}

View file

@ -2,6 +2,7 @@
namespace Database\Seeders;
use App\Models\Slide;
use App\Models\Slider;
use Illuminate\Support\Str;
use Illuminate\Database\Seeder;
@ -15,10 +16,17 @@ class SlidersSeeder extends Seeder
*/
public function run()
{
$homeslider = new Slider();
$homeslider->label = "Home";
$homeslider->slug = Str::slug($homeslider->label);
$homeslider->commentaire = 'Slider de la home';
$homeslider->save();
Slider::factory(2)->create()
->each(function (Slider $slider) {
$i = 0;
Slide::factory(3)->make()
->each(function (Slide $slide) use ($slider, &$i) {
$slide->order = $i++;
$slide->slider()->associate($slider);
$slide->save();
});
});
}
}

View file

@ -0,0 +1,26 @@
<?php
namespace Database\Seeders;
use App\Enums\Role;
use App\Models\User;
use Illuminate\Database\Seeder;
class UserSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$user = new User();
$user->role = Role::admin();
$user->name = 'Seb';
$user->email = 'seb.hattry@gmail.com';
$user->password = 'pwd00895';
$user->save();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

View file

@ -0,0 +1,18 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used during authentication for various
| messages that we need to display to the user. You are free to modify
| these language lines according to your application's requirements.
|
*/
'failed' => 'Ces identifiants ne correspondent pas à nos enregistrements',
'password' => 'Le mot de passe fourni est incorrect.',
'throttle' => 'Tentatives de connexion trop nombreuses. Veuillez essayer de nouveau dans :seconds secondes.',
];

View file

@ -0,0 +1,19 @@
<?php
return [
'kid' => 'enfant',
'balanced_menu_type' => [
'kid' => 'enfant',
'teenager' => 'adolescent',
'woman' => 'femme',
'man' => 'homme',
'senior' => 'senior',
'withPrefix' => [
'kid' => 'un enfant',
'teenager' => 'un adolescent',
'woman' => 'une femme',
'man' => 'un homme',
'senior' => 'un senior'
]
]
];

View file

@ -0,0 +1,17 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Pagination Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used by the paginator library to build
| the simple pagination links. You are free to change them to anything
| you want to customize your views to better match your application.
|
*/
'previous' => '&laquo; Précédent',
'next' => 'Suivant &raquo;',
];

View file

@ -0,0 +1,20 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Password Reminder Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are the default lines which match reasons
| that are given by the password broker for a password update attempt
| has failed, such as for an invalid token or invalid new password.
|
*/
'reset' => 'Votre mot de passe a été réinitialisé !',
'sent' => 'Nous vous avons envoyé par email le lien de réinitialisation du mot de passe !',
'throttled' => 'Veuillez patienter avant de réessayer.',
'token' => "Ce jeton de réinitialisation du mot de passe n'est pas valide.",
'user' => "Aucun utilisateur n'a été trouvé avec cette adresse email.",
];

View file

@ -0,0 +1,187 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Validation Language Lines
|--------------------------------------------------------------------------
|
| The following language lines contain the default error messages used by
| the validator class. Some of these rules have multiple versions such
| as the size rules. Feel free to tweak each of these messages.
|
*/
'accepted' => 'Le champ :attribute doit être accepté.',
'active_url' => "Le champ :attribute n'est pas une URL valide.",
'after' => 'Le champ :attribute doit être une date postérieure au :date.',
'after_or_equal' => 'Le champ :attribute doit être une date postérieure ou égale au :date.',
'alpha' => 'Le champ :attribute doit contenir uniquement des lettres.',
'alpha_dash' => 'Le champ :attribute doit contenir uniquement des lettres, des chiffres et des tirets.',
'alpha_num' => 'Le champ :attribute doit contenir uniquement des chiffres et des lettres.',
'array' => 'Le champ :attribute doit être un tableau.',
'before' => 'Le champ :attribute doit être une date antérieure au :date.',
'before_or_equal' => 'Le champ :attribute doit être une date antérieure ou égale au :date.',
'between' => [
'numeric' => 'La valeur de :attribute doit être comprise entre :min et :max.',
'file' => 'La taille du fichier de :attribute doit être comprise entre :min et :max kilo-octets.',
'string' => 'Le texte :attribute doit contenir entre :min et :max caractères.',
'array' => 'Le tableau :attribute doit contenir entre :min et :max éléments.',
],
'boolean' => 'Le champ :attribute doit être vrai ou faux.',
'confirmed' => 'Le champ de confirmation :attribute ne correspond pas.',
'date' => "Le champ :attribute n'est pas une date valide.",
'date_equals' => 'Le champ :attribute doit être une date égale à :date.',
'date_format' => 'Le champ :attribute ne correspond pas au format :format.',
'different' => 'Les champs :attribute et :other doivent être différents.',
'digits' => 'Le champ :attribute doit contenir :digits chiffres.',
'digits_between' => 'Le champ :attribute doit contenir entre :min et :max chiffres.',
'dimensions' => "La taille de l'image :attribute n'est pas conforme.",
'distinct' => 'Le champ :attribute a une valeur en double.',
'email' => 'Le champ :attribute doit être une adresse email valide.',
'ends_with' => 'Le champ :attribute doit se terminer par une des valeurs suivantes : :values',
'exists' => 'Le champ :attribute sélectionné est invalide.',
'file' => 'Le champ :attribute doit être un fichier.',
'filled' => 'Le champ :attribute doit avoir une valeur.',
'gt' => [
'numeric' => 'La valeur de :attribute doit être supérieure à :value.',
'file' => 'La taille du fichier de :attribute doit être supérieure à :value kilo-octets.',
'string' => 'Le texte :attribute doit contenir plus de :value caractères.',
'array' => 'Le tableau :attribute doit contenir plus de :value éléments.',
],
'gte' => [
'numeric' => 'La valeur de :attribute doit être supérieure ou égale à :value.',
'file' => 'La taille du fichier de :attribute doit être supérieure ou égale à :value kilo-octets.',
'string' => 'Le texte :attribute doit contenir au moins :value caractères.',
'array' => 'Le tableau :attribute doit contenir au moins :value éléments.',
],
'image' => 'Le champ :attribute doit être une image.',
'in' => 'Le champ :attribute est invalide.',
'in_array' => "Le champ :attribute n'existe pas dans :other.",
'integer' => 'Le champ :attribute doit être un entier.',
'ip' => 'Le champ :attribute doit être une adresse IP valide.',
'ipv4' => 'Le champ :attribute doit être une adresse IPv4 valide.',
'ipv6' => 'Le champ :attribute doit être une adresse IPv6 valide.',
'json' => 'Le champ :attribute doit être un document JSON valide.',
'lt' => [
'numeric' => 'La valeur de :attribute doit être inférieure à :value.',
'file' => 'La taille du fichier de :attribute doit être inférieure à :value kilo-octets.',
'string' => 'Le texte :attribute doit contenir moins de :value caractères.',
'array' => 'Le tableau :attribute doit contenir moins de :value éléments.',
],
'lte' => [
'numeric' => 'La valeur de :attribute doit être inférieure ou égale à :value.',
'file' => 'La taille du fichier de :attribute doit être inférieure ou égale à :value kilo-octets.',
'string' => 'Le texte :attribute doit contenir au plus :value caractères.',
'array' => 'Le tableau :attribute doit contenir au plus :value éléments.',
],
'max' => [
'numeric' => 'La valeur de :attribute ne peut être supérieure à :max.',
'file' => 'La taille du fichier de :attribute ne peut pas dépasser :max kilo-octets.',
'string' => 'Le texte de :attribute ne peut contenir plus de :max caractères.',
'array' => 'Le tableau :attribute ne peut contenir plus de :max éléments.',
],
'mimes' => 'Le champ :attribute doit être un fichier de type : :values.',
'mimetypes' => 'Le champ :attribute doit être un fichier de type : :values.',
'min' => [
'numeric' => 'La valeur de :attribute doit être supérieure ou égale à :min.',
'file' => 'La taille du fichier de :attribute doit être supérieure à :min kilo-octets.',
'string' => 'Le texte :attribute doit contenir au moins :min caractères.',
'array' => 'Le tableau :attribute doit contenir au moins :min éléments.',
],
'multiple_of' => 'La valeur de :attribute doit être un multiple de :value',
'not_in' => "Le champ :attribute sélectionné n'est pas valide.",
'not_regex' => "Le format du champ :attribute n'est pas valide.",
'numeric' => 'Le champ :attribute doit contenir un nombre.',
'password' => 'Le mot de passe est incorrect',
'present' => 'Le champ :attribute doit être présent.',
'regex' => 'Le format du champ :attribute est invalide.',
'required' => 'Le champ :attribute est obligatoire.',
'required_if' => 'Le champ :attribute est obligatoire quand la valeur de :other est :value.',
'required_unless' => 'Le champ :attribute est obligatoire sauf si :other est :values.',
'required_with' => 'Le champ :attribute est obligatoire quand :values est présent.',
'required_with_all' => 'Le champ :attribute est obligatoire quand :values sont présents.',
'required_without' => "Le champ :attribute est obligatoire quand :values n'est pas présent.",
'required_without_all' => "Le champ :attribute est requis quand aucun de :values n'est présent.",
'same' => 'Les champs :attribute et :other doivent être identiques.',
'size' => [
'numeric' => 'La valeur de :attribute doit être :size.',
'file' => 'La taille du fichier de :attribute doit être de :size kilo-octets.',
'string' => 'Le texte de :attribute doit contenir :size caractères.',
'array' => 'Le tableau :attribute doit contenir :size éléments.',
],
'starts_with' => 'Le champ :attribute doit commencer avec une des valeurs suivantes : :values',
'string' => 'Le champ :attribute doit être une chaîne de caractères.',
'timezone' => 'Le champ :attribute doit être un fuseau horaire valide.',
'unique' => 'La valeur du champ :attribute est déjà utilisée.',
'uploaded' => "Le fichier du champ :attribute n'a pu être téléversé.",
'url' => "Le format de l'URL de :attribute n'est pas valide.",
'uuid' => 'Le champ :attribute doit être un UUID valide',
/*
|--------------------------------------------------------------------------
| Custom Validation Language Lines
|--------------------------------------------------------------------------
|
| Here you may specify custom validation messages for attributes using the
| convention "attribute.rule" to name the lines. This makes it quick to
| specify a specific custom language line for a given attribute rule.
|
*/
'phone' => 'Le champ :attribute n\'est pas un numéro de téléphone valide.',
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
/*
|--------------------------------------------------------------------------
| Custom Validation Attributes
|--------------------------------------------------------------------------
|
| The following language lines are used to swap attribute place-holders
| with something more reader friendly such as E-Mail Address instead
| of "email". This simply helps us make messages a little cleaner.
|
*/
'attributes' => [
'name' => 'nom',
'username' => "nom d'utilisateur",
'email' => 'adresse email',
'first_name' => 'prénom',
'last_name' => 'nom',
'password' => 'mot de passe',
'password_confirmation' => 'confirmation du mot de passe',
'city' => 'ville',
'country' => 'pays',
'address' => 'adresse',
'phone' => 'téléphone',
'mobile' => 'portable',
'age' => 'âge',
'sex' => 'sexe',
'gender' => 'genre',
'day' => 'jour',
'month' => 'mois',
'year' => 'année',
'hour' => 'heure',
'minute' => 'minute',
'second' => 'seconde',
'title' => 'titre',
'content' => 'contenu',
'description' => 'description',
'excerpt' => 'extrait',
'date' => 'date',
'time' => 'heure',
'available' => 'disponible',
'size' => 'taille',
'answers' => 'réponses',
'answers.*' => 'réponse',
'postal_code' => 'code postal',
'picture' => 'image',
'breeder_code' => 'code éleveur'
],
];

View file

@ -5,33 +5,11 @@
@csrf
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2"><a href="{{route('bo.sliders.index')}}" class="h2 text-decoration-none">
< </a> {{ __('Sliders') }} <small class="text-muted">{{ __('Création') }}</small></h1>
< </a> {{ __('Slider') }} <small class="text-muted">{{ __('Création') }}</small></h1>
<div class="btn-toolbar mb-2 mb-md-0">
<button type="submit" class="btn btn-sm btn-primary">{{ __('Enregistrer') }}</a>
</div>
</div>
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<div class="form-group">
<label for="name">{{ __('Label') }} *</label>
<input type="text" id="label" name="label" class="form-control" placeholder="{{ __('Label') }} * " value="{{ old('label') }}" required>
</div>
<div class="form-group">
<label for="name">{{ __('Slug') }}</label>
<input type="text" id="slug" name="slug" class="form-control" placeholder="{{ __('Slug') }}" value="{{ old('slug') }}">
<small class="text-muted"> {{ __('laisser vide pour une génération automatique') }} </small>
</div>
<div class="form-group">
<label for="name">{{ __('Commentaire') }}</label>
<input type="text" id="commentaire" name="commentaire" class="form-control" placeholder="{{ __('Commentaire') }}" value="{{ old('commentaire') }}">
</div>
@include('back.sliders.form-inputs')
</form>
@endsection

View file

@ -6,52 +6,30 @@
@csrf
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2"><a href="{{route('bo.sliders.index')}}" class="h2 text-decoration-none">
< </a> Slider <small class="text-muted">Edition</small></h1>
< </a> {{ __('Slider') }} <small class="text-muted">{{ __('Édition') }}</small></h1>
@if (session('success'))
<span class="float-center text-success">{{ session('success') }}</span>
@endif
<div class="btn-toolbar mb-2 mb-md-0">
<button type="submit" class="btn btn-sm btn-primary">Enregistrer</a>
<button type="submit" class="btn btn-sm btn-primary">{{ __('Enregistrer') }}</a>
</div>
</div>
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<div class="form-row">
<div class="form-group col">
<label for="name">Label *</label>
<input type="text" id="label" name="label" class="form-control" placeholder="Label * " value="{{ old('label',$slider->label) }}" required>
</div>
<div class="form-group col">
<label for="name">Slug</label>
<input type="text" id="slug" name="slug" class="form-control" placeholder="Slug" value="{{ old('slug',$slider->slug) }}" required>
</div>
</div>
<div class="form-group">
<label for="name">Commentaire</label>
<input type="text" id="commentaire" name="commentaire" class="form-control" placeholder="Commentaire" value="{{ old('commentaire',$slider->commentaire) }}">
</div>
@include('back.sliders.form-inputs')
</form>
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h2><small class="text-muted">Slides</small></h2>
<h2><small class="text-muted">{{ __('Slides') }}</small></h2>
</div>
<table class="table">
<thead class="thead-dark">
<tr>
<th scope="col">Titre</th>
<th scope="col">Texte</th>
<th scope="col">Image</th>
<th scope="col">Lien</th>
<th scope="col">Début</th>
<th scope="col">Fin</th>
<th scope="col">Slider de regroupement</th>
<th scope="col">Ordre</th>
<th scope="col">{{ __('Titre') }}</th>
<th scope="col">{{ __('Texte') }}</th>
<th scope="col">{{ __('Image') }}</th>
<th scope="col">{{ __('Lien') }}</th>
<th scope="col">{{ __('Début') }}</th>
<th scope="col">{{ __('Fin') }}</th>
<th scope="col">{{ __('Slider de regroupement') }}</th>
<th scope="col">{{ __('Ordre') }}</th>
<th scope="col"></th>
</tr>
</thead>
@ -60,30 +38,30 @@
<tr>
<td>{{$slide->title}}</td>
<td>{!! $slide->text !!}</td>
<td><img src="{{ asset('/images/slides/' . $slide->picture) }}" height="50px"></td>
<td><img src="{{ asset($slide->picture) }}" height="50px"></td>
<td><a href="{{$slide->link}}" target="_slide">{{ $slide->link_label }}</a></td>
<td>{{$slide->date_start}}</td>
<td>{{$slide->date_end}}</td>
<td>{{$slide->slider->label}}</td>
<td>
@if ($loop->first)
<a href="{{route('bo.slides.change-order', ['id' => $slide->id, 'action' => 'down'])}}" class="btn-link text-decoration-none">
<i name="down_arrow" class="icon-size far fa-arrow-alt-circle-down fa-2x"></i>
<a href="{{route('bo.slides.change-order', ['slide' => $slide->id, 'action' => 'down'])}}" class="btn-link text-decoration-none">
>
</a>
@elseif($loop->last)
<a href="{{route('bo.slides.change-order', ['id' => $slide->id, 'action' => 'up'])}}" class="btn-link text-decoration-none">
<i name="up_arrow" class="icon-size far fa-arrow-alt-circle-up fa-2x"></i>
<a href="{{route('bo.slides.change-order', ['slide' => $slide->id, 'action' => 'up'])}}" class="btn-link text-decoration-none">
<
</a>
@else
<a href="{{route('bo.slides.change-order', ['id' => $slide->id, 'action' => 'up'])}}" class="btn-link text-decoration-none">
<i name="up_arrow" class="icon-size far fa-arrow-alt-circle-up fa-2x"></i>
<a href="{{route('bo.slides.change-order', ['slide' => $slide->id, 'action' => 'up'])}}" class="btn-link text-decoration-none">
<
</a>
<a href="{{route('bo.slides.change-order', ['id' => $slide->id, 'action' => 'down'])}}" class="btn-link text-decoration-none">
<i name="down_arrow" class="icon-size far fa-arrow-alt-circle-down fa-2x"></i>
<a href="{{route('bo.slides.change-order', ['slide' => $slide->id, 'action' => 'down'])}}" class="btn-link text-decoration-none">
>
</a>
@endif
</td>
<td><a href="{{route('bo.slides.edit', ['slide' => $slide->id])}}" class="btn btn-sm btn-primary">Éditer</a></td>
<td><a href="{{route('bo.slides.edit', ['slide' => $slide->id])}}" class="btn btn-sm btn-primary">{{ __('Éditer') }}</a></td>
</tr>
@endforeach
</tbody>

View file

@ -0,0 +1,16 @@
<div class="form-group">
<label for="name">{{ __('Label') }} *</label>
<input type="text" id="label" name="label" class="form-control" placeholder="{{ __('Label') }} * " value="{{ old('label', $slider->label ?? '') }}" required>
@include('back.modules.input-error', ['inputName'=>'label'])
@include('back.modules.input-error', ['inputName'=>'slug'])
</div>
<div class="form-group">
<label for="name">{{ __('Commentaire') }}</label>
<input type="text" id="commentaire" name="commentaire" class="form-control" placeholder="{{ __('Commentaire') }}" value="{{ old('commentaire', $slider->commentaire ?? '') }}">
@include('back.modules.input-error', ['inputName'=>'commentaire'])
</div>
<div class="form-group">
<input class="form-check-input" type="checkbox" id="publish" name="published" @if (old('published', $slider->published) !== 0)checked @endif>
<label class="form-check-label" for="publish">{{ __('Publié') }}</label>
@include('back.modules.input-error', ['inputName'=>'published'])
</div>

View file

@ -2,30 +2,30 @@
@section('content')
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Sliders <small class="text-muted">Liste</small></h1>
<h1 class="h2">{{ __('Sliders') }} <small class="text-muted">{{ __('Liste') }}</small></h1>
@if ($errors->any())
@foreach($errors->all() as $error)
<span class="float-center text-danger">{{ $error }}</span>
@endforeach
@endif
<a href="{{route('bo.sliders.create')}}" class="btn btn-primary float-right">Créer un slider</a>
<a href="{{route('bo.sliders.create')}}" class="btn btn-primary float-right">{{ __('Créer un slider') }}</a>
</div>
<table class="table">
<thead>
<th scope="col">Label</th>
<th scope="col">Slug</th>
<th scope="col">Commentaire</th>
<th scope="col">{{ __('Label') }}</th>
<th scope="col">{{ __('Commentaire') }}</th>
<th scope="col">{{ __('Publié') }}</th>
<th></th>
</thead>
<tbody>
@foreach ($sliders as $slider)
<tr>
<td>{{$slider->label}}</td>
<td>{{$slider->slug}}</td>
<td>{{$slider->commentaire}}</td>
<td>{{$slider->published}}</td>
<td class="text-center">
<a href="{{route('bo.sliders.edit', $slider->id)}}" class="btn btn-sm btn-primary">Éditer</a>
<a href="{{route('bo.sliders.edit', $slider->id)}}" class="btn btn-sm btn-primary">{{ __('Éditer') }}</a>
<form action="{{route('bo.sliders.destroy', $slider->id)}}" method="POST" class="d-inline">
@method('DELETE')
@csrf

View file

@ -6,7 +6,7 @@
@csrf
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2"><a href="{{route('bo.slides.index')}}" class="h2 text-decoration-none">
< </a> {{ __('Slides') }} <small class="text-muted">{{ __('Création') }}</small></h1>
< </a> {{ __('Slide') }} <small class="text-muted">{{ __('Edition') }}</small></h1>
<div class="btn-toolbar mb-2 mb-md-0">
<button type="submit" class="btn btn-sm btn-primary">{{ __('Enregistrer') }}</a>
</div>

View file

@ -6,6 +6,7 @@
<div class="form-group">
<label for="name">{{ __('Texte') }}</label>
<textarea class="form-control" id="text" name="text" placeholder="{{ __('Texte') }}">{{ old('text', $slide->text) }}</textarea>
@include('back.modules.input-error', ['inputName'=>'text'])
</div>
<div class="form-group">
<label for="picture">{{ __('Image') }} <small class="text-muted">- JPEG/PNG, 2Mo max. Dimensions {{ Config::get('theme.slide_image_maxwidth') }} px par {{ Config::get('theme.slide_image_maxheight') }} px</small></label></br>
@ -13,8 +14,9 @@
@if ($slide->picture !== null)
<br />
<br />
<img src="/images/slides/{{$slide->picture}}" alt="{{$slide->title}}" style="max-height: 100px;">
<img src="{{$slide->picture}}" alt="{{$slide->title}}" style="max-height: 100px;">
@endif
@include('back.modules.input-error', ['inputName'=>'picture'])
</div>
<div class="form-row">
@ -29,6 +31,7 @@
<div class="form-group col"></div>
<div class="form-group col"></div>
<div class="form-group col"></div>
@include('back.modules.input-error', ['inputName'=>'slider_id'])
</div>
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
@ -45,6 +48,7 @@
<input type="time" id="date_start_time" name="date_start_time" class="form-control" value="{{ old('date_start_time') }}">
@endif
<small class="text-muted">{{ __('Laisser vide pour un affichage immédiat') }}</small>
@include('back.modules.input-error', ['inputName'=>'date_start'])
</div>
<div class="form-group col">
<label for="date_end_date">{{ __('Fin') }} <small class="text-muted">{{ __('de l\'affichage') }}</small></label>
@ -56,6 +60,7 @@
<input type="time" id="date_end_time" name="date_end_time" class="form-control" value="{{ old('date_end_time') }}">
@endif
<small class="text-muted">{{ __('Laisser vide pour un affichage permanent') }}</small>
@include('back.modules.input-error', ['inputName'=>'date_end'])
</div>
</div>
@ -65,12 +70,15 @@
<div class="form-group">
<label for="name">{{ __('Texte du lien') }}</label>
<input type="text" class="form-control" name="link_label" placeholder="{{ __('Texte du lien') }}" value="{{ old('link_label', $slide->link_label) }}">
@include('back.modules.input-error', ['inputName'=>'link_label'])
</div>
<div class="form-group">
<label for="name">{{ __('Cible du lien') }}</label>
<input type="text" class="form-control" name="link" placeholder="{{ __('Cible du lien') }}" value="{{ old('link', $slide->link) }}">
@include('back.modules.input-error', ['inputName'=>'link'])
</div>
<div class="form-group text-center">
<input class="form-check-input" type="checkbox" id="new_tab_cb" name="link_new_tab" @if (old('link_new_tab', $slide->link_new_tab) !== 0)checked @endif>
<label class="form-check-label" for="new_tab_cb">{{ __('Ouvrir dans un nouvel onglet') }}</label>
@include('back.modules.input-error', ['inputName'=>'link_new_tab'])
</div>

View file

@ -30,7 +30,7 @@
<tr>
<td>{{$slide->title}}</td>
<td>{!! $slide->text !!}</td>
<td><img src="{{ asset('/images/slides/' . $slide->picture) }}" height="50px"></td>
<td><img src="{{ asset($slide->picture) }}" height="50px"></td>
<td><a href="{{$slide->link}}" target="_slide">{{ $slide->link_label }}</a></td>
<td>{{$slide->date_start}}</td>
<td>{{$slide->date_end}}</td>
@ -38,18 +38,18 @@
<td>
@if ($loop->first)
<a href="{{route('bo.slides.change-order', ['slide' => $slide->id, 'action' => 'down'])}}" class="btn-link text-decoration-none">
<i name="down_arrow" class="icon-size far fa-arrow-alt-circle-down fa-2x"></i>
> </a>
>
</a>
@elseif($loop->last)
<a href="{{route('bo.slides.change-order', ['slide' => $slide->id, 'action' => 'up'])}}" class="btn-link text-decoration-none">
<i name="up_arrow" class="icon-size far fa-arrow-alt-circle-up fa-2x"></i><
<
</a>
@else
<a href="{{route('bo.slides.change-order', ['slide' => $slide->id, 'action' => 'up'])}}" class="btn-link text-decoration-none">
<i name="up_arrow" class="icon-size far fa-arrow-alt-circle-up fa-2x"></i>
< </a>
<
</a>
<a href="{{route('bo.slides.change-order', ['slide' => $slide->id, 'action' => 'down'])}}" class="btn-link text-decoration-none">
<i name="down_arrow" class="icon-size far fa-arrow-alt-circle-down fa-2x"></i>>
>
</a>
@endif
</td>

View file

@ -19,10 +19,7 @@
@endif
<!-- Favicon -->
{{-- <link rel="icon" href="{{ asset('~/assets/images/favicon.png') }}" type="image/png">
<link rel="icon" sizes="16x16" href="{{ asset('~/assets/images/LG-favicons_16x16.png') }}" type="image/png">
<link rel="icon" sizes="32x32" href="{{ asset('~/assets/images/LG-favicons_32x32.png') }}" type="image/png">
<link rel="icon" sizes="48x48" href="{{ asset('~/assets/images/LG-favicons_48x48.png') }}" type="image/png"> --}}
<link rel="icon" href="/favicon.png" type="image/png">
@preload('mix-manifest.json')

View file

@ -1,11 +1,37 @@
<nav id="sidebarMenu" class="col-md-3 col-lg-2 min-height-100vh d-md-block bg-light sidebar collapse">
<div class="sidebar-sticky pt-3 d-flex flex-column justify-content-between align-items-center">
<div class="col-12 mt-4">
<h6>sous-titre</h6>
<ul class="nav flex-column mb-2">
<li class="nav-item"><a class="nav-link" href="{{route('bo.sliders.index')}}">Slider</a></li>
<li class="nav-item"><a class="nav-link" href="{{route('bo.slides.index')}}">Slide</a></li>
</ul>
<div class="accordion" id="accordionHome">
<div class="accordion-item mb-1">
<h2 class="accordion-header" id="headingOne">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="false" aria-controls="collapseOne">
{{ __('Page d\'accueil') }}
</button>
</h2>
<div id="collapseOne" class="accordion-collapse collapse" aria-labelledby="headingOne">
<div class="accordion-body">
<div class="mb-1">
<h6 class="pb-1 mb-1 text-dark border-bottom">SLIDER</h6>
<a class="nav-link py-0 ms-4" href="{{route('bo.sliders.index')}}">{{ __('Slider') }}</a>
<a class="nav-link py-0 ms-4" href="{{route('bo.slides.index')}}">{{ __('Slide') }}</a>
</div>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header" id="headingTwo">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
Accordion Item #2
</button>
</h2>
<div id="collapseTwo" class="accordion-collapse collapse" aria-labelledby="headingTwo">
<div class="accordion-body">
<strong>This is the second item's accordion body.</strong> It is hidden by default, until the collapse plugin adds the appropriate classes that we use to style each element. These classes control the overall appearance, as well as the showing and hiding via CSS transitions. You can modify any of this with custom CSS or overriding our default variables. It's also worth noting that just about any HTML can go within the <code>.accordion-body</code>, though the transition does limit overflow.
</div>
</div>
</div>
</div>
{{-- @foreach($modelsClasses as $crudModel)
@if(
auth()->user()->can('viewAny', $crudModel) or
@ -29,12 +55,12 @@
@endcan
@endforeach --}}
</div>
<div class="col-12">
<div class="col-12 mt-5">
<ul class="nav flex-column mb-2">
<li class="nav-item">
<form action="{{ route('bo.logout') }}" method="POST">
@csrf
<button class="btn btn-primary">Se déconnecter</button>
<button class="btn btn-primary">{{ __('Se déconnecter') }}</button>
</form>
</li>
</ul>