diff --git a/app/Http/Controllers/Bo/NewsController.php b/app/Http/Controllers/Bo/NewsController.php new file mode 100644 index 00000000..c61944b3 --- /dev/null +++ b/app/Http/Controllers/Bo/NewsController.php @@ -0,0 +1,202 @@ +authorizeResource(News::class); + } + /** + * Display a listing of the resource. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Contracts\View\View + */ + public function index(Request $request): \Illuminate\Contracts\View\View + { + /** @var \Illuminate\Database\Eloquent\Builder $newsModels */ + $newsModels = News::query()->with('newsCategory'); + + /** @var string $search Search field */ + $search = $request->search; + if ($search) { + $this->searchQuery( + $newsModels, + $search, + null, + 'title', + 'content', + 'news_category_id' + ); + } + $searchFields = \implode(', ', [ + trans('validation.attributes.title'), + trans('validation.attributes.content'), + trans('validation.custom.category'), + ]); + + /** Sort columns with a query */ + $this->sortQuery($newsModels); + + /** Custom pagination */ + $newsModels = $this->paginate($newsModels); + + return view('bo.pages.news.index', compact('newsModels', 'search', 'searchFields')); + } + + /** + * Show the form for creating a new resource. + * + * @param \App\Models\News $news + * @return \Illuminate\Contracts\View\View + */ + public function create(News $news): \Illuminate\Contracts\View\View + { + $newsCategoryModels = NewsCategory::query()->where('published', true)->orderBy('order')->get(); + return view('bo.pages.news.create', ['newsModel' => $news, 'newsCategoryModels' => $newsCategoryModels]); + } + + /** + * Store a newly created resource in storage. + * + * @param \App\Http\Requests\Bo\News\StoreNewsRequest $request + * @return \Illuminate\Http\RedirectResponse + */ + public function store(StoreNewsRequest $request): \Illuminate\Http\RedirectResponse + { + return DB::transaction(function () use ($request) { + $news = new News(); + $news->fill($request->validated()); + $oldestNewsWithHighlight = News::query()->where('highlight', true)->orderBy('created_at')->get(); + if ($news->saveOrFail()) { + if ($news->highlight === true) { + if ($oldestNewsWithHighlight->count() >= 2) { + /** @var \App\Models\News $firstNews */ + $firstNews = $oldestNewsWithHighlight->first(); + $firstNews->highlight = false; + $firstNews->save(); + } + } + return redirect()->route('bo.news.edit', $news) + ->with('success', __('crud.messages.has_been_created', [ + 'model' => Str::ucfirst(__('models.classes.news')) + ])); + } + return redirect()->back() + ->with('error', __('crud.messages.cannot_be_created', [ + 'model' => Str::ucfirst(__('models.classes.news')) + ])); + }); + } + + /** + * Display the specified resource. + * + * @param \App\Models\News $news + * @return \Illuminate\Contracts\View\View + */ + public function show(News $news): \Illuminate\Contracts\View\View + { + return view('bo.pages.news.show', ['newsModel' => $news]); + } + + /** + * Show the form for editing the specified resource. + * + * @param \App\Models\News $news + * @return \Illuminate\Contracts\View\View + */ + public function edit(News $news): \Illuminate\Contracts\View\View + { + $newsCategoryModels = NewsCategory::query()->where('published', true)->orderBy('order')->get(); + return view('bo.pages.news.edit', ['newsModel' => $news, 'newsCategoryModels' => $newsCategoryModels]); + } + + /** + * Update the specified resource in storage. + * + * @param \App\Http\Requests\Bo\News\UpdateNewsRequest $request + * @param \App\Models\News $news + * @return \Illuminate\Http\RedirectResponse + */ + public function update( + UpdateNewsRequest $request, + News $news + ): \Illuminate\Http\RedirectResponse { + return DB::transaction(function () use ($request, $news) { + $news->fill($request->validated()); + $oldestNewsWithHighlight = News::query()->where('highlight', true)->orderBy('created_at')->get(); + if ($news->saveOrFail()) { + if ($news->highlight === true) { + if ($oldestNewsWithHighlight->count() >= 2) { + /** @var \App\Models\News $firstNews */ + $firstNews = $oldestNewsWithHighlight->first(); + $firstNews->highlight = false; + $firstNews->save(); + } + } + return redirect()->route('bo.news.edit', $news) + ->with('success', trans('crud.messages.has_been_updated', [ + 'model' => Str::ucfirst(trans('models.classes.news')) + ])); + } + return redirect()->route('bo.news.edit', $news) + ->with('error', trans('crud.messages.cannot_be_updated', [ + 'model' => Str::ucfirst(trans('models.classes.news')) + ])); + }); + } + + /** + * Remove the specified resource from storage. + * + * @param \App\Models\News $news + * @return \Illuminate\Http\RedirectResponse + */ + public function destroy(News $news): \Illuminate\Http\RedirectResponse + { + /** @var array */ + $previousQueries = []; + \parse_str(\parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries); + if ($news->deleteOrFail()) { + return redirect()->route('bo.news.index', $previousQueries) + ->with('success', trans('crud.messages.has_been_deleted', [ + 'model' => Str::ucfirst(trans('models.classes.news')) + ])); + } + return redirect()->back() + ->with('error', trans('crud.messages.cannot_be_deleted', [ + 'model' => Str::ucfirst(trans('models.classes.news')) + ])); + } + + /** + * Duplicate the specified resource. + * + * @param \App\Models\News $news + * @return \Illuminate\Contracts\View\View + */ + public function duplicate(News $news): \Illuminate\Contracts\View\View + { + return $this->create($news->replicate()); + } +} diff --git a/app/Http/Controllers/Bo/UploadController.php b/app/Http/Controllers/Bo/UploadController.php new file mode 100644 index 00000000..28f54ea0 --- /dev/null +++ b/app/Http/Controllers/Bo/UploadController.php @@ -0,0 +1,30 @@ +upload; + + $path = FileStorageHelper::storeFile(new HistoricalMilestone(), $image, true); + + return response()->json([ + 'url' => "/{$path}" + ]); + } +} diff --git a/app/Http/Requests/Bo/News/StoreNewsRequest.php b/app/Http/Requests/Bo/News/StoreNewsRequest.php new file mode 100644 index 00000000..a90d35c9 --- /dev/null +++ b/app/Http/Requests/Bo/News/StoreNewsRequest.php @@ -0,0 +1,95 @@ +mergePicture('picture'); + $this->mergePicture('thumbnail'); + $this->merge([ + 'published' => $this->boolean('published'), + 'highlight' => $this->boolean('highlight'), + ]); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules(): array + { + $rules = [ + 'seo_title' => 'required|string|between:45,70', + 'seo_description' => 'required|string|between:50,160', + 'title' => 'required|string|between:3,255', + 'subtitle' => 'sometimes|nullable|string|between:3,255', + 'picture_alt' => 'required|string|between:3,255', + 'picture_title' => 'required|string|between:3,255', + 'thumbnail_alt' => 'required|string|between:3,255', + 'thumbnail_title' => 'required|string|between:3,255', + 'news_category_id' => 'required|integer', + 'content' => ['required', 'string', new WysiwygLength()], + 'highlight' => 'required|boolean', + 'published' => 'required|boolean', + 'publish_at' => 'sometimes|nullable|date', + 'unpublish_at' => 'sometimes|nullable|date|after:publish_at', + ]; + return \array_merge( + $rules, + $this->pictureRules('picture', 700, 400, 700, 400), + $this->pictureRules('thumbnail', 360, 250, 360, 250), + ); + } + + /** + * Get custom attributes for validator errors. + * + * @return array + */ + public function attributes(): array + { + return [ + 'seo_title' => trans('validation.attributes.title'), + 'seo_description' => trans('validation.attributes.title'), + 'title' => trans('validation.attributes.title'), + 'subtitle' => trans('validation.attributes.subtitle'), + 'content' => trans('validation.attributes.content'), + 'news_category_id' => trans('validation.custom.category'), + 'picture' => trans('validation.attributes.image'), + 'picture_alt' => trans('validation.custom.attribute_alt'), + 'picture_title' => trans('validation.custom.attribute_title'), + 'thumbnail_alt' => trans('validation.custom.attribute_alt'), + 'thumbnail_title' => trans('validation.custom.attribute_title'), + 'published' => trans('validation.attributes.published'), + 'highlight' => trans('validation.attributes.highlight'), + 'publish_at' => trans('validation.custom.publish_at'), + 'unpublish_at' => trans('validation.custom.unpublish_at'), + ]; + } +} diff --git a/app/Http/Requests/Bo/News/UpdateNewsRequest.php b/app/Http/Requests/Bo/News/UpdateNewsRequest.php new file mode 100644 index 00000000..debb4ea5 --- /dev/null +++ b/app/Http/Requests/Bo/News/UpdateNewsRequest.php @@ -0,0 +1,18 @@ +route('news')); + } +} diff --git a/app/Http/Requests/Bo/UploadFileRequest.php b/app/Http/Requests/Bo/UploadFileRequest.php new file mode 100644 index 00000000..b6b63479 --- /dev/null +++ b/app/Http/Requests/Bo/UploadFileRequest.php @@ -0,0 +1,30 @@ +check() and $this->ajax(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules(): array + { + return [ + 'upload' => 'required|image|mimes:jpeg,png,jpg|max:2048' + ]; + } +} diff --git a/app/Models/News.php b/app/Models/News.php new file mode 100644 index 00000000..698d03ff --- /dev/null +++ b/app/Models/News.php @@ -0,0 +1,241 @@ + + */ + protected $fillable = [ + 'seo_title', + 'seo_description', + 'title', + 'subtitle', + 'slug', + 'picture', + 'picture_alt', + 'picture_title', + 'thumbnail', + 'thumbnail_alt', + 'thumbnail_title', + 'news_category_id', + 'content', + 'highlight', + 'published', + 'publish_at', + 'unpublish_at', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'publish_at' => 'datetime', + 'unpublish_at' => 'datetime', + 'published' => 'boolean', + 'highlight' => 'boolean', + ]; + + /** + * Perform any actions required after the model boots. + * + * @return void + */ + protected static function booted(): void + { + static::creating(function (self $news) { + self::makeStatusSense($news); + self::assertTitleIsUnique($news->title); + self::manageFields($news); + }); + static::updating(function (self $news) { + self::makeStatusSense($news); + self::assertTitleIsUnique($news->title, $news->getKey()); + self::manageFields($news); + }); + static::updated(function (self $news) { + FileStorageHelper::removeOldFile($news, 'picture'); + FileStorageHelper::removeOldFile($news, 'thumbnail'); + }); + static::deleted(function (self $news) { + FileStorageHelper::removeFile($news, 'picture'); + FileStorageHelper::removeFile($news, 'thumbnail'); + }); + } + + // * METHODS + + /** + * Adjusts news status using current status and publish dates + * + * @param self $news + * @return void + */ + private static function makeStatusSense(self $news): void + { + /** @var \Carbon\Carbon|null */ + $publish_at = $news->publish_at; + /** @var \Carbon\Carbon|null */ + $unpublish_at = $news->unpublish_at; + $wantedStatus = $news->published; + if ($publish_at or $unpublish_at) { + $news->published = $news->planificated_status; + } + if ($wantedStatus !== $news->published) { + session()->flash('bo.info', \trans( + 'La publication est modifiée en concordance avec les dates enregistrées. + Pour modifier l\'état de publication, veuillez modifier ou enlever les dates correspondantes.' + )); + } + } + + /** + * Getter the current status following planification dates planificated_status + * + * @return boolean + */ + public function getPlanificatedStatusAttribute(): bool + { + /** @var \Carbon\Carbon|null */ + $publish_at = $this->publish_at; + /** @var \Carbon\Carbon|null */ + $unpublish_at = $this->unpublish_at; + if ($publish_at and $unpublish_at) { + return $unpublish_at->greaterThanOrEqualTo(now()) and $publish_at->lessThan(now()); + } + if ($publish_at) { + return $publish_at->lessThan(now()); + } + if ($unpublish_at) { + return $unpublish_at->greaterThanOrEqualTo(now()); + } + return false; + } + + /** + * Manage model fields. + * + * @param self $model + * @return void + */ + private static function manageFields(self $model): void + { + $model->slug = str($model->title)->slug(); + $model->thumbnail = FileStorageHelper::storeFile($model, $model->thumbnail, true); + $model->picture = FileStorageHelper::storeFile($model, $model->picture, true); + } + + /** + * Asserts using validation that the title is unique + * + * @param string|null $title + * @param integer|null $id + * @return void + * @throws \Illuminate\Validation\ValidationException If an actu title already exists. + */ + private static function assertTitleIsUnique(string|null $title, ?int $id = null): void + { + $table = (new self())->getTable(); + ToolboxHelper::assertFieldIsUnique($table, 'title', $title, $id); + ToolboxHelper::assertFieldIsUnique($table, 'slug', str($title)->slug(), $id); + } + + /** + * Get News views by News category. + * + * @param string|null $slug + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator + */ + public static function getNewsViewsByNewsCategory(string|null $slug = null): \Illuminate\Contracts\Pagination\LengthAwarePaginator + { + /** + * @var Detection\MobileDetect + */ + $detect = new MobileDetect(); + /** @var \App\Models\NewsCategory|null */ + $newsCategoryModel = !is_null($slug) && !empty($slug) ? NewsCategory::query()->where('slug', $slug)->first() : null; + return News::query() + ->with('newsCategory') + ->when(!is_null($newsCategoryModel), function (Builder $q) use ($newsCategoryModel) { + $q->whereHas('newsCategory', function (Builder $q) use ($newsCategoryModel) { + $q->where('id', $newsCategoryModel->getKey()); + }); + }) + ->where('published', true) + ->orderByDesc('created_at') + ->paginate($detect->isMobile() ? 3 : 6) + ->through(function ($newsModel) { + return view('fo.partials.news-card', ['newsModel' => $newsModel])->render(); + }); + } + + /** + * Get Social news views by filter social category. + * + * @param string|null $slug + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator + */ + public static function getSocialViewsByFilter(string|null $slug = null): \Illuminate\Contracts\Pagination\LengthAwarePaginator + { + /** + * @var Detection\MobileDetect + */ + $detect = new MobileDetect(); + /** @var \App\Models\NewsCategory|null */ + $socialCategory = !is_null($slug) && !empty($slug) ? NewsCategory::query()->where('slug', $slug)->first() : null; + return News::query() + ->with('newsCategory') + ->when(!is_null($socialCategory), function (Builder $q) use ($socialCategory) { + $q->whereHas('newsCategory', function (Builder $q) use ($socialCategory) { + $q->where('id', $socialCategory->getKey()); + }); + }) + ->where('published', true) + ->orderByDesc('created_at') + ->paginate($detect->isMobile() ? 4 : 12) + ->through(function ($socialModel) { + return view('fo.partials.news-card', ['newsModel' => $socialModel])->render(); + }); + } + + // * RELATIONS + + /** + * Get the category that owns the news. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function newsCategory(): \Illuminate\Database\Eloquent\Relations\BelongsTo + { + return $this->belongsTo(NewsCategory::class); + } +} diff --git a/app/Models/Product.php b/app/Models/Product.php index 9e6dde8a..2800564a 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -160,7 +160,7 @@ class Product extends Model implements SeoInterface { $rangeModel = !is_null($slug) ? Range::query()->where('slug', $slug)->first() : - Range::query()->orderBy('slug', 'ASC')->first(); + Range::query()->orderBy('slug', 'ASC')->where('published', true)->first(); return Product::query()->whereHas('range', function (Builder $q) use ($rangeModel) { $q->where('id', $rangeModel->getKey()); })->inRandomOrder() diff --git a/app/Policies/NewsPolicy.php b/app/Policies/NewsPolicy.php new file mode 100644 index 00000000..c2ce2f16 --- /dev/null +++ b/app/Policies/NewsPolicy.php @@ -0,0 +1,97 @@ + + */ +class NewsFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + $publishedStatus = fake()->boolean(75); + $publish_at = Carbon::now()->addMonth(); + $title = fake()->unique()->words(rand(1, 2), true); + return [ + 'seo_title' => fake()->sentence(), + 'seo_description' => fake()->sentence(), + 'title' => $title, + 'subtitle' => fake()->sentence(), + 'content' => fake()->text(), + 'slug' => Str::slug($title), + 'picture_alt' => fake()->sentence(), + 'picture_title' => fake()->sentence(), + 'thumbnail_alt' => fake()->sentence(), + 'thumbnail_title' => fake()->sentence(), + 'highlight' => false, + 'news_category_id' => 1, + 'published' => $publishedStatus, + 'publish_at' => !$publishedStatus ? $publish_at : null, + 'unpublish_at' => !$publishedStatus ? $publish_at->copy()->addDays(rand(10, 20)) : null, + ]; + } +} diff --git a/database/factories/assets/news/default-700x400.png b/database/factories/assets/news/default-700x400.png new file mode 100644 index 00000000..5e99d244 Binary files /dev/null and b/database/factories/assets/news/default-700x400.png differ diff --git a/database/factories/assets/news/thumbnail-360x250.png b/database/factories/assets/news/thumbnail-360x250.png new file mode 100644 index 00000000..c233712e Binary files /dev/null and b/database/factories/assets/news/thumbnail-360x250.png differ diff --git a/database/migrations/2024_08_10_153246_create_news_table.php b/database/migrations/2024_08_10_153246_create_news_table.php new file mode 100644 index 00000000..5df454e7 --- /dev/null +++ b/database/migrations/2024_08_10_153246_create_news_table.php @@ -0,0 +1,44 @@ +id(); + $table->string('seo_title')->comment('The seo title of the news.'); + $table->string('seo_description')->comment('The seo description of the news.'); + $table->string('title')->comment('The title of the news.'); + $table->string('subtitle')->nullable()->comment('The title of the news.'); + $table->string('picture')->comment('Picture path.'); + $table->string('picture_alt')->comment('Picture alt attribute.'); + $table->string('picture_title')->comment('Picture title attribute.'); + $table->string('thumbnail')->comment('Thumbnail path.'); + $table->string('thumbnail_alt')->comment('Thumbnail alt attribute.'); + $table->string('thumbnail_title')->comment('Thumbnail title attribute.'); + $table->string('slug')->unique()->comment('Unique title slug'); + $table->text('content')->comment('The content of the news.'); + $table->foreignId('news_category_id')->constrained('news_categories')->restrictOnDelete()->comment('News category id'); + $table->boolean('highlight')->comment('Highlight status.'); + $table->boolean('published')->comment('News publish status.'); + $table->timestamp('publish_at')->nullable()->comment('News publishment date.'); + $table->timestamp('unpublish_at')->nullable()->comment('News unpublishment date.'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('news'); + } +}; diff --git a/database/seeders/Development/NewsSeeder.php b/database/seeders/Development/NewsSeeder.php new file mode 100644 index 00000000..c9ac91b9 --- /dev/null +++ b/database/seeders/Development/NewsSeeder.php @@ -0,0 +1,41 @@ +make()->each(function (News $news) { + $news->picture = FileStorageHelper::storeFile( + $news, + new \SplFileInfo(\resource_path( + '../database/factories/assets/news/default-700x400.png' + )) + ); + $news->thumbnail = FileStorageHelper::storeFile( + $news, + new \SplFileInfo(\resource_path( + '../database/factories/assets/news/thumbnail-360x250.png' + )) + ); + $news->saveQuietly(); + }); + } +} diff --git a/database/seeders/DevelopmentSeeder.php b/database/seeders/DevelopmentSeeder.php index 2e52de1c..74999c40 100644 --- a/database/seeders/DevelopmentSeeder.php +++ b/database/seeders/DevelopmentSeeder.php @@ -23,6 +23,7 @@ use Database\Seeders\Development\SubrangeSeeder; use Database\Seeders\Development\TestimonialSeeder; use Database\Seeders\Development\UserSeeder; use Database\Seeders\Development\NewsCorporateSeeder; +use Database\Seeders\Development\NewsSeeder; use Illuminate\Database\Seeder; class DevelopmentSeeder extends Seeder @@ -56,6 +57,7 @@ class DevelopmentSeeder extends Seeder NewsCorporateSeeder::class, TestimonialSeeder::class, NewsCategorySeeder::class, + NewsSeeder::class, ]); } } diff --git a/resources/js/ckUploadAdapter.js b/resources/js/ckUploadAdapter.js new file mode 100644 index 00000000..19ae8ed0 --- /dev/null +++ b/resources/js/ckUploadAdapter.js @@ -0,0 +1,67 @@ +import axios from "axios"; +import route from "../ts/modules/route"; + +class ckUploadAdapter { + constructor(loader) { + // The file loader instance to use during the upload. + this.loader = loader; + } + + // Starts the upload process. + upload() { + return this.loader.file.then( + (file) => + new Promise((resolve, reject) => { + this._initRequest(); + this._sendRequest(file, resolve, reject); + }), + ); + } + + // Aborts the upload process. + abort() { + if (this.cancel) { + this.cancel.abort(); + } + } + + // Initializes the XMLHttpRequest object using the URL passed to the constructor. + _initRequest() { + this.cancel = axios.CancelToken.source(); + this.route = route.methods.route("bo.upload"); + } + + // Prepares the data and sends the request. + _sendRequest(file, resolve, reject) { + // Prepare the form data. + const data = new FormData(); + data.append("upload", file); + axios + .post(this.route, data, { + headers: { + "Content-Type": "multipart/form-data", + }, + onUploadProgress: (evt) => { + this.loader.uploadTotal = evt.total; + this.loader.uploaded = evt.loaded; + }, + }) + .then((response) => { + resolve({ + default: response.data.url, + }); + }) + .catch((error) => { + console.error(error); + reject("Le fichier à été marbré"); + }); + } +} + +function CkUploadAdapterPlugin(editor) { + editor.plugins.get("FileRepository").createUploadAdapter = (loader) => { + return new ckUploadAdapter(loader); + }; +} + +export default CkUploadAdapterPlugin; diff --git a/resources/lang/en/crud.php b/resources/lang/en/crud.php index 81978fc6..06160129 100644 --- a/resources/lang/en/crud.php +++ b/resources/lang/en/crud.php @@ -62,7 +62,9 @@ return [ 'minicrud' => [ 'visibility_label' => 'Visibility', + 'highlight_label' => 'Highlight', 'visibility_helper' => 'Check to make it visible.', + 'highlight_helper' => 'Check to make it highlight.', 'select_entity' => 'Select :entity from the list.', 'associated_entity' => 'Associated :entity', 'dimensions_must_be' => 'Must be :widthpx (width) by :heightpx (height).', diff --git a/resources/lang/en/models.php b/resources/lang/en/models.php index 57939b40..a3ad9ac4 100644 --- a/resources/lang/en/models.php +++ b/resources/lang/en/models.php @@ -27,6 +27,7 @@ return [ 'claim' => 'claim', 'historical_milestone' => 'historical milestone|historical milestones', 'offer' => 'offer', + 'news' => 'news', 'news_corporate' => 'news of the compagny', 'news_category' => 'category of the news', ] diff --git a/resources/lang/en/validation.php b/resources/lang/en/validation.php index 032ea3ff..2368225c 100644 --- a/resources/lang/en/validation.php +++ b/resources/lang/en/validation.php @@ -289,6 +289,7 @@ return [ 'sustitle' => 'sustitle', 'target' => 'target', 'tips' => 'tips', + 'thumbnail' => 'thumbnail', 'type' => 'type', 'unit' => 'unit', 'unpublish_at' => 'unpublish at', diff --git a/resources/lang/fr/crud.php b/resources/lang/fr/crud.php index 66226f2d..4ef24d27 100644 --- a/resources/lang/fr/crud.php +++ b/resources/lang/fr/crud.php @@ -63,7 +63,11 @@ return [ 'minicrud' => [ 'visibility_label' => 'Visibilité', + 'highlight_label' => 'Mise en avant', + 'category_label' => 'Catégorie de l\'article', 'visibility_helper' => 'Cocher pour rendre visible.', + 'highlight_helper' => 'Cocher pour mettre en avant.', + 'category_helper' => 'Séléctionner la categorie.', 'select_entity' => 'Sélectionnez :entity dans la liste.', 'associated_entity' => ':entity associé(e)', 'dimensions_must_be' => 'Doit être :widthpx (largeur) par :heightpx (hauteur).', diff --git a/resources/lang/fr/models.php b/resources/lang/fr/models.php index 25941eb7..04719ec4 100644 --- a/resources/lang/fr/models.php +++ b/resources/lang/fr/models.php @@ -27,6 +27,7 @@ return [ 'claim' => 'allégations', 'historical_milestone' => 'jalon historique|jalons historique', 'offer' => 'offre', + 'news' => 'actualités', 'news_corporate' => 'actualités de l\'entreprise', 'news_category' => 'catégorie d\'une actualité', ] diff --git a/resources/lang/fr/validation.php b/resources/lang/fr/validation.php index 62115e60..30d88a4a 100644 --- a/resources/lang/fr/validation.php +++ b/resources/lang/fr/validation.php @@ -291,6 +291,7 @@ return [ 'sustitle' => 'sous-titre', 'target' => 'cible', 'tips' => 'astuce', + 'thumbnail' => 'vignette', 'type' => 'type', 'unit' => 'unité', 'unpublish_at' => 'date de fin de publication', diff --git a/resources/ts/bo/components/CkEditorComponent.vue b/resources/ts/bo/components/CkEditorComponent.vue index 72cd7591..65a1261e 100644 --- a/resources/ts/bo/components/CkEditorComponent.vue +++ b/resources/ts/bo/components/CkEditorComponent.vue @@ -20,6 +20,8 @@ import { computed, defineOptions, onMounted, ref, useAttrs, type PropType } from "vue"; import parseLaravelErrorMessages from "../../modules/parseLaravelErrorMessages"; import "./../../../js/vendor/ckeditor"; +// @ts-ignore +import CkUploadAdapterPlugin from "./../../../js/ckUploadAdapter"; defineOptions({ name: "CkEditorComponent" @@ -184,6 +186,7 @@ onMounted(() => { // * Differents CkEditor configs. const minimalConfig = { + extraPlugins: [], wordCount: ckEditorGlobals.wordCount, ui: ckEditorGlobals.ui, placeholder: placeholder.value, @@ -227,6 +230,9 @@ onMounted(() => { }; const basicConfig = { + extraPlugins: [ + CkUploadAdapterPlugin, + ], wordCount: ckEditorGlobals.wordCount, ui: ckEditorGlobals.ui, placeholder: placeholder.value, @@ -277,6 +283,8 @@ onMounted(() => { "removeFormat", "|", "sourceEditing", + "|", + "imageUpload", ], }, image: { diff --git a/resources/views/bo/layouts/sidemenu.blade.php b/resources/views/bo/layouts/sidemenu.blade.php index 6dd1d3f7..5adee578 100644 --- a/resources/views/bo/layouts/sidemenu.blade.php +++ b/resources/views/bo/layouts/sidemenu.blade.php @@ -821,7 +821,8 @@ + +
+ @csrf + @include('bo.pages.news.form') + @include('bo.partials.script-button-clone') +
+
+ +
+
+
+@endsection diff --git a/resources/views/bo/pages/news/edit.blade.php b/resources/views/bo/pages/news/edit.blade.php new file mode 100644 index 00000000..a42336f3 --- /dev/null +++ b/resources/views/bo/pages/news/edit.blade.php @@ -0,0 +1,58 @@ +@extends('bo.layout', ['brParam' => $newsModel]) + +@section('title', __('Édition d\'une :model', ['model' => __('models.classes.news')])) +@section('description', __('Édition d\'une :model déjà existant', ['model' => __('models.classes.news')])) +@section('breadcrumb', request()->route()->getName()) + +@section('content') +
+
+ + + + @include('modules.breadcrumbs.breadcrumb-body', ['brParam' => $newsModel]) +
+
+
+ @can('view', $newsModel) + + + + @endcan + @can('duplicate', $newsModel) + + + + @endcan + + @can('delete', $newsModel) + @csrf + @method('DELETE') + + @endcan +
+
+
+
+ @csrf + @method('PUT') + @include('bo.pages.news.form') + @include('bo.partials.script-button-clone') +
+
+ +
+
+
+@endsection diff --git a/resources/views/bo/pages/news/form.blade.php b/resources/views/bo/pages/news/form.blade.php new file mode 100644 index 00000000..d2187619 --- /dev/null +++ b/resources/views/bo/pages/news/form.blade.php @@ -0,0 +1,403 @@ +
+ {{-- SEO_TITLE --}} +
+
+ {{ str(__('validation.custom.seo_title'))->ucfirst() }} +
+
+ @include('bo.partials.inputs.varchar', [ + 'id' => 'newsSeoTitle', + 'label' => __(':field :inter:model', [ + 'field' => str(__('validation.custom.seo_title'))->ucfirst(), + 'inter' => __('validation.custom.of_the.female'), + 'model' => trans('models.classes.news'), + ]), + 'hasWordCounter' => true, + 'name' => 'seo_title', + 'required' => true, + 'model' => $newsModel ?? null, + 'helper' => sprintf( + '%s %s', + __('crud.helpers.seo_title'), + __('validation.between.string', [ + 'attribute' => __(':field :inter:model', [ + 'field' => __('validation.custom.seo_title'), + 'inter' => __('validation.custom.of_the.female'), + 'model' => trans('models.classes.news'), + ]), + 'min' => 45, + 'max' => 70, + ])), + 'placeholder' => __(':field :inter:model', [ + 'field' => str(__('validation.custom.seo_title'))->ucfirst(), + 'inter' => __('validation.custom.of_the.female'), + 'model' => trans('models.classes.news'), + ]), + ]) +
+
+
+
+ {{-- SEO_DESCRIPTION --}} +
+
+ {{ str(__('validation.custom.seo_description'))->ucfirst() }} +
+
+ @include('bo.partials.inputs.varchar-multiline', [ + 'id' => 'newsSeoDescription', + 'label' => __(':field :inter:model', [ + 'field' => str(__('validation.custom.seo_description'))->ucfirst(), + 'inter' => __('validation.custom.of_the.female'), + 'model' => trans('models.classes.news'), + ]), + 'name' => 'seo_description', + 'model' => $newsModel ?? null, + 'required' => true, + 'helper' => sprintf( + '%s %s', + __('crud.helpers.seo_description'), + __('validation.between.string', [ + 'attribute' => __(':field :inter:model', [ + 'field' => __('validation.custom.seo_description'), + 'inter' => __('validation.custom.of_the.female'), + 'model' => trans('models.classes.news'), + ]), + 'min' => 50, + 'max' => 160, + ])), + 'placeholder' => __(':field :inter:model', [ + 'field' => str(__('validation.custom.seo_description'))->ucfirst(), + 'inter' => __('validation.custom.of_the.female'), + 'model' => trans('models.classes.news'), + ]), + ]) +
+
+
+
+ {{-- TITLE --}} +
+
+ {{ str(__('validation.attributes.title'))->ucFirst() }} +
+
+ @include('bo.partials.inputs.varchar', [ + 'id' => 'newsTitle', + 'label' => __(':field :inter:model', [ + 'field' => str(__('validation.attributes.title'))->ucFirst(), + 'inter' => __('validation.custom.of_the.vowel'), + 'model' => __('models.classes.news'), + ]), + 'hasWordCounter' => true, + 'name' => 'title', + 'required' => true, + 'model' => $newsModel ?? null, + 'helper' => __('validation.between.string', [ + 'attribute' => __(':field :inter:model', [ + 'field' => __('validation.attributes.title'), + 'inter' => __('validation.custom.of_the.vowel'), + 'model' => __('models.classes.news'), + ]), + 'min' => 3, + 'max' => 255, + ]), + 'placeholder' => __(':field :inter:model', [ + 'field' => str(__('validation.attributes.title'))->ucFirst(), + 'inter' => __('validation.custom.of_the.vowel'), + 'model' => __('models.classes.news'), + ]), + ]) +
+
+
+
+ {{-- SUBTITLE --}} +
+
+ {{ str(__('validation.custom.subtitle'))->ucFirst() }} +
+
+ @include('bo.partials.inputs.varchar', [ + 'id' => 'newsSubtitle', + 'label' => __(':field :inter:model', [ + 'field' => str(__('validation.custom.subtitle'))->ucFirst(), + 'inter' => __('validation.custom.of_the.vowel'), + 'model' => __('models.classes.news'), + ]), + 'hasWordCounter' => true, + 'name' => 'subtitle', + 'required' => false, + 'model' => $newsModel ?? null, + 'helper' => __('validation.between.string', [ + 'attribute' => __(':field :inter:model', [ + 'field' => __('validation.custom.subtitle'), + 'inter' => __('validation.custom.of_the.vowel'), + 'model' => __('models.classes.news'), + ]), + 'min' => 3, + 'max' => 255, + ]), + 'placeholder' => __(':field :inter:model', [ + 'field' => str(__('validation.custom.subtitle'))->ucFirst(), + 'inter' => __('validation.custom.of_the.vowel'), + 'model' => __('models.classes.news'), + ]), + ]) +
+
+
+
+ {{-- PICTURE --}} +
+
+ {{ str(__('validation.attributes.image'))->ucFirst() }} +
+ {{-- PICTURE_ALT --}} +
+ @include('bo.partials.inputs.varchar', [ + 'id' => 'newsPictureAlt', + 'label' => __(':field1 :inter1:field2 :inter2:model', [ + 'field1' => str(__('validation.custom.attribute_alt'))->ucFirst(), + 'inter1' => __('validation.custom.of_the.vowel'), + 'field2' => __('validation.attributes.image'), + 'inter2' => __('validation.custom.of_the.vowel'), + 'model' => __('models.classes.news'), + ]), + 'hasWordCounter' => true, + 'name' => 'picture_alt', + 'required' => true, + 'model' => $newsModel ?? null, + 'helper' => __('crud.minicrud.picture_alt_helper'), + 'placeholder' => __(':field1 :inter1:field2 :inter2:model', [ + 'field1' => str(__('validation.custom.attribute_alt'))->ucFirst(), + 'inter1' => __('validation.custom.of_the.vowel'), + 'field2' => __('validation.attributes.image'), + 'inter2' => __('validation.custom.of_the.vowel'), + 'model' => __('models.classes.news'), + ]), + ]) +
+ {{-- PICTURE_TITLE --}} +
+ @include('bo.partials.inputs.varchar', [ + 'id' => 'newsPictureTitle', + 'label' => __(':field1 :inter1:field2 :inter2:model', [ + 'field1' => str(__('validation.custom.attribute_title'))->ucFirst(), + 'inter1' => __('validation.custom.of_the.vowel'), + 'field2' => __('validation.attributes.image'), + 'inter2' => __('validation.custom.of_the.vowel'), + 'model' => __('models.classes.news'), + ]), + 'hasWordCounter' => true, + 'name' => 'picture_title', + 'required' => true, + 'model' => $newsModel ?? null, + 'helper' => __('crud.minicrud.picture_title_helper'), + 'placeholder' => __(':field1 :inter1:field2 :inter2:model', [ + 'field1' => str(__('validation.custom.attribute_title'))->ucFirst(), + 'inter1' => __('validation.custom.of_the.vowel'), + 'field2' => __('validation.attributes.image'), + 'inter2' => __('validation.custom.of_the.vowel'), + 'model' => __('models.classes.news'), + ]), + ]) +
+ {{-- PICTURE --}} +
+ @include('bo.partials.inputs.varchar-picture', [ + 'id' => 'newsPicture', + 'name' => 'picture', + 'width' => 700, + 'height' => 400, + 'model' => $newsModel ?? null, + 'required' => true, + 'preview' => true, + ]) +
+
+
+
+ {{-- THUMBNAIL --}} +
+
+ {{ str(__('validation.custom.thumbnail'))->ucFirst() }} +
+ {{-- THUMBNAIL_ALT --}} +
+ @include('bo.partials.inputs.varchar', [ + 'id' => 'newsThumbnailAlt', + 'label' => __(':field1 :inter1:field2 :inter2:model', [ + 'field1' => str(__('validation.custom.attribute_alt'))->ucFirst(), + 'inter1' => __('validation.custom.of_the.female'), + 'field2' => __('validation.custom.thumbnail'), + 'inter2' => __('validation.custom.of_the.vowel'), + 'model' => __('models.classes.news'), + ]), + 'hasWordCounter' => true, + 'name' => 'thumbnail_alt', + 'required' => true, + 'model' => $newsModel ?? null, + 'helper' => __('crud.minicrud.picture_alt_helper'), + 'placeholder' => __(':field1 :inter1:field2 :inter2:model', [ + 'field1' => str(__('validation.custom.attribute_alt'))->ucFirst(), + 'inter1' => __('validation.custom.of_the.female'), + 'field2' => __('validation.custom.thumbnail'), + 'inter2' => __('validation.custom.of_the.vowel'), + 'model' => __('models.classes.news'), + ]), + ]) +
+ {{-- THUMBNAIL_TITLE --}} +
+ @include('bo.partials.inputs.varchar', [ + 'id' => 'newsThumbnailTitle', + 'label' => __(':field1 :inter1:field2 :inter2:model', [ + 'field1' => str(__('validation.custom.attribute_title'))->ucFirst(), + 'inter1' => __('validation.custom.of_the.female'), + 'field2' => __('validation.custom.thumbnail'), + 'inter2' => __('validation.custom.of_the.vowel'), + 'model' => __('models.classes.news'), + ]), + 'hasWordCounter' => true, + 'name' => 'thumbnail_title', + 'required' => true, + 'model' => $newsModel ?? null, + 'helper' => __('crud.minicrud.picture_title_helper'), + 'placeholder' => __(':field1 :inter1:field2 :inter2:model', [ + 'field1' => str(__('validation.custom.attribute_title'))->ucFirst(), + 'inter1' => __('validation.custom.of_the.female'), + 'field2' => __('validation.custom.thumbnail'), + 'inter2' => __('validation.custom.of_the.vowel'), + 'model' => __('models.classes.news'), + ]), + ]) +
+ {{-- THUMBNAIL --}} +
+ @include('bo.partials.inputs.varchar-picture', [ + 'id' => 'newsThumbnail', + 'name' => 'thumbnail', + 'width' => 360, + 'height' => 250, + 'model' => $newsModel ?? null, + 'required' => true, + 'preview' => true, + ]) +
+
+
+
+ {{-- CONTENT --}} +
+
+ {{ str(__('validation.attributes.content'))->ucfirst() }} +
+
+ @include('bo.partials.inputs.text-html', [ + 'id' => 'newsContent', + 'label' => __(':field :inter:model', [ + 'field' => str(__('validation.attributes.content'))->ucfirst(), + 'inter' => __('validation.custom.of_the.vowel'), + 'model' => trans('models.classes.news'), + ]), + 'name' => 'content', + 'model' => $newsModel ?? null, + 'required' => true, + 'helper' => __('crud.minicrud.text_warning'), + ]) +
+
+
+
+ {{-- HIGHLIGHT --}} +
+
+ {{ str(__('validation.custom.highlight'))->ucFirst() }} +
+
+ @include('bo.partials.inputs.tinyint-boolean', [ + 'id' => 'newsHighlight ', + 'name' => 'highlight', + 'model' => $newsModel ?? null, + 'label' => __('crud.minicrud.highlight_label'), + 'helper' => __('crud.minicrud.highlight_helper'), + ]) +
+
+
+
+ {{-- CATEGORY --}} +
+
+ {{ str(__('validation.custom.category'))->ucFirst() }} +
+
+ @include('bo.partials.inputs.bigint-foreign-id', [ + 'id' => 'newsCategoryId', + 'name' => 'news_category_id', + 'model' => $newsModel ?? null, + 'associatedModels' => $newsCategoryModels, + 'foreignLabel' => 'title', + 'label' => __('crud.minicrud.category_label'), + 'helper' => __('crud.minicrud.category_helper'), + ]) +
+
+
+
+ {{-- PUBLICATION --}} +
+
+ {{ str(__('crud.minicrud.visibility_label'))->ucFirst() }} +
+ {{-- PUBLISHED --}} +
+ @include('bo.partials.inputs.tinyint-boolean', [ + 'id' => 'newsPublished', + 'name' => 'published', + 'model' => $newsModel ?? null, + 'label' => __('crud.minicrud.visibility_label'), + 'helper' => sprintf('%s %s', __('crud.minicrud.visibility_helper'), __('crud.helpers.visibility_with_dates')), + ]) +
+ {{-- PUBLICATION DATES --}} +
+
+ {{-- PUBLISH_AT --}} +
+
+ @include('bo.partials.inputs.timestamp-datetime', [ + 'label' => str(__('validation.custom.publish_at'))->ucFirst() . __(' (Europe/Paris)'), + 'id' => 'newsPublishedAt', + 'name' => 'publish_at', + 'model' => $newsModel ?? null, + 'helper' => __('crud.helpers.publish_at', [ + 'model' => str(__('models.classes.news'))->ucFirst(), + ]), + ]) +
+
+ {{-- UNPUBLISH_AT --}} +
+
+ @include('bo.partials.inputs.timestamp-datetime', [ + 'label' => str(__('validation.custom.unpublish_at'))->ucfirst() . __(' (Europe/Paris)'), + 'id' => 'newsUnPublishedAt', + 'name' => 'unpublish_at', + 'model' => $newsModel ?? null, + 'helper' => __('crud.helpers.unpublish_at', [ + 'model' => str(__('models.classes.news'))->ucFirst(), + ]), + ]) +
+
+
+
+
+
+
+
+

{{ __('* Champs obligatoires') }}

+
+
diff --git a/resources/views/bo/pages/news/index-list.blade.php b/resources/views/bo/pages/news/index-list.blade.php new file mode 100644 index 00000000..bf785c26 --- /dev/null +++ b/resources/views/bo/pages/news/index-list.blade.php @@ -0,0 +1,80 @@ + + @if (count($newsModels) > 0) + + @include('bo.modules.table-col-sorter', [ + 'cols' => [ + 'title' => str(__('validation.attributes.title'))->ucfirst(), + 'news_category_id' => str(__('validation.custom.category'))->ucfirst(), + 'picture' => str(__('validation.attributes.image'))->ucfirst(), + 'published' => str(__('validation.custom.publication'))->ucfirst(), + 'updated_at' => str(__('validation.attributes.updated_at'))->ucfirst(), + ], + ]) + + + @foreach ($newsModels as $newsModel) + + + + + @include('bo.modules.change-published-status', [ + 'routeName' => 'news', + 'model' => $newsModel, + ]) + + + + @endforeach + + @else + + + + @endif +
{{ $newsModel->title }}{{ $newsModel->newsCategory->title }} + {{ $newsModel->picture_alt }} + + {{ $newsModel->updated_at->isoFormat('LLLL') }} + + @canAny(['show', 'duplicate', 'update', 'delete'], $newsModel) +
+ @can('view', $newsModel) + + + + @endcan + @can('duplicate', $newsModel) + + + + @endcan + @can('update', $newsModel) + + + + @endcan + @can('delete', $newsModel) + @csrf + @method('DELETE') + + @endcan +
+ @else + + + + @endcan +
+ {{ trans_choice('crud.other.no_model_found', 1, ['model' => __('models.classes.news')]) }} +
diff --git a/resources/views/bo/pages/news/index.blade.php b/resources/views/bo/pages/news/index.blade.php new file mode 100644 index 00000000..cf16d3f6 --- /dev/null +++ b/resources/views/bo/pages/news/index.blade.php @@ -0,0 +1,21 @@ +@extends('bo.layout') + +@section('title', str(__('models.classes.news'))->ucfirst()->plural()) +@section('description', __('Liste de tous les :model', ['model' => str(__('models.classes.news'))->plural()])) + +@section('content') +
+ @include('modules.breadcrumbs.breadcrumb-body') + @can('create', \App\Models\News::class) + + + + @endcan +
+ @include('bo.modules.search-bar') +
+ @include('bo.pages.news.index-list') +
+ {{ $newsModels->links() }} +@endsection diff --git a/resources/views/bo/pages/news/show.blade.php b/resources/views/bo/pages/news/show.blade.php new file mode 100644 index 00000000..caba3afe --- /dev/null +++ b/resources/views/bo/pages/news/show.blade.php @@ -0,0 +1,131 @@ +@extends('bo.layout', ['brParam' => $newsModel]) + +@section('title', __('Aperçu d\'un :model', ['model' => __('models.classes.news')])) +@section('description', __('Aperçu d\'un :model déjà existante', ['model' => __('models.classes.news')])) +@section('breadcrumb', request()->route()->getName()) + +@section('content') +
+
+ + + + @include('modules.breadcrumbs.breadcrumb-body', ['brParam' => $newsModel]) +
+ @canAny(['duplicate', 'update', 'delete'], $newsModel) +
+ @can('duplicate', $newsModel) + + + + @endcan + @can('update', $newsModel) + + + + @endcan + @can('delete', $newsModel) + @csrf + @method('DELETE') + + @endcan +
+ @endcan +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{{ str(__('validation.attributes.title'))->ucFirst() }} + {{ $newsModel->title }} +
{{ str(__('validation.custom.seo_title'))->ucFirst() }} + {{ $newsModel->seo_title }} +
{{ str(__('validation.custom.seo_description'))->ucFirst() }} + {{ $newsModel->seo_description }} +
{{ str(__('validation.attributes.image'))->ucFirst() }} + {{ $newsModel->picture_alt }} +
{{ str(__('validation.custom.thumbnail'))->ucFirst() }} + {{ $newsModel->thumbnail_alt }} +
{{ str(__('validation.attributes.content'))->ucFirst() }} + {{ $newsModel->content }} +
+ {{ str(__('validation.custom.publication'))->ucFirst() }} + + {{ $newsModel->published + ? str($newsModel->created_at->isoFormat('LLLL'))->ucFirst() + : __('crud.other.model_not_published') }} +
{{ str(__('validation.custom.publish_at'))->ucFirst() }} + {{ $newsModel->publish_at + ? str($newsModel->publish_at->isoFormat('LLLL'))->ucFirst() + : __('crud.other.model_not_published') }} +
{{ str(__('validation.custom.unpublish_at'))->ucFirst() }} + {{ $newsModel->unpublish_at + ? str($newsModel->unpublish_at->isoFormat('LLLL'))->ucFirst() + : __('crud.other.model_not_published') }} +
{{ str(__('validation.attributes.created_at'))->ucFirst() }} + {{ str($newsModel->created_at->isoFormat('LLLL'))->ucFirst() }} +
{{ str(__('validation.attributes.updated_at'))->ucFirst() }} + {{ str($newsModel->updated_at->isoFormat('LLLL'))->ucFirst() }} +
+
+
+
+@endsection diff --git a/resources/views/bo/pages/static_pages/form.blade.php b/resources/views/bo/pages/static_pages/form.blade.php index fe3a3999..1db6ea07 100644 --- a/resources/views/bo/pages/static_pages/form.blade.php +++ b/resources/views/bo/pages/static_pages/form.blade.php @@ -77,8 +77,8 @@ ]) - - + + @endif @if (!$staticPageModel->type->equals(StaticPageType::recipe) && !$staticPageModel->type->equals(StaticPageType::product) && @@ -179,6 +179,7 @@ 'model' => trans_choice('models.classes.static_page', 1), ]), 'name' => 'content', + 'editorConfig' => \App\Enums\CkEditorConfigName::basic->label(), 'model' => $staticPageModel ?? null, 'required' => false, 'helper' => __('crud.minicrud.text_warning'), diff --git a/resources/views/bo/partials/inputs/text-html.blade.php b/resources/views/bo/partials/inputs/text-html.blade.php index 0f775653..828cc255 100644 --- a/resources/views/bo/partials/inputs/text-html.blade.php +++ b/resources/views/bo/partials/inputs/text-html.blade.php @@ -23,6 +23,7 @@ 'editorConfig' => $editorConfig ?? \App\Enums\CkEditorConfigName::basic->label(), 'placeholder' => $placeholder ?? __('Un text riche'), 'errors' => $errors->getBag('default')->getMessages() ?? [], + 'editorConfig' => $editorConfig ?? \App\Enums\CkEditorConfigName::basic->label(), ]; @endphp
diff --git a/resources/views/modules/script-routes.blade.php b/resources/views/modules/script-routes.blade.php index 7bdd355c..cc508ce6 100644 --- a/resources/views/modules/script-routes.blade.php +++ b/resources/views/modules/script-routes.blade.php @@ -8,6 +8,7 @@ // FIXME: Add missing routes for MenuNodeTypeEnum usage in settings vue component // FIXME: Add missing routes for RangesSliderComponent.vue // BO + window.__SYSTEM._routes['bo.upload'] = '{{ route('bo.upload') }}'; window.__SYSTEM._routes['bo.menu_node_data.index'] = '{{ route('bo.menu_node_data.index') }}'; window.__SYSTEM._routes['bo.menu_node_data.update'] = '{{ route('bo.menu_node_data.update', ['menu_node_data' => 'MENU_NODE_DATA']) }}'; window.__SYSTEM._routes['bo.approaches.json-paginate'] = '{{ route('bo.approaches.json-paginate') }}'; diff --git a/routes/breadcrumbs-bo.php b/routes/breadcrumbs-bo.php index 39686035..ae8a056c 100644 --- a/routes/breadcrumbs-bo.php +++ b/routes/breadcrumbs-bo.php @@ -13,6 +13,7 @@ use App\Models\MainSlide; use App\Models\GeneralSetting; use App\Models\HistoricalMilestone; use App\Models\Meat; +use App\Models\News; use App\Models\NewsCategory; use App\Models\NewsCorporate; use App\Models\Offer; @@ -582,6 +583,30 @@ Breadcrumbs::for('bo.historical_milestones.duplicate', function (Generator $trai $trail->push(Str::ucfirst(trans('crud.actions.duplicate'))); }); +// * NEWS +Breadcrumbs::for('bo.news.index', function (Generator $trail) { + $trail->push( + Str::of(trans('models.classes.news'))->plural()->ucfirst(), + route('bo.news.index') + ); +}); +Breadcrumbs::for('bo.news.show', function (Generator $trail, News $news) { + $trail->parent('bo.news.index'); + $trail->push(Str::ucfirst(trans('crud.actions.view')), route('bo.news.show', $news)); +}); +Breadcrumbs::for('bo.news.create', function (Generator $trail) { + $trail->parent('bo.news.index'); + $trail->push(Str::ucfirst(trans('crud.actions.create')), route('bo.news.create')); +}); +Breadcrumbs::for('bo.news.edit', function (Generator $trail, News $news) { + $trail->parent('bo.news.index'); + $trail->push(Str::ucfirst(trans('crud.actions.edit')), route('bo.news.edit', $news)); +}); +Breadcrumbs::for('bo.news.duplicate', function (Generator $trail) { + $trail->parent('bo.news.index'); + $trail->push(Str::ucfirst(trans('crud.actions.duplicate'))); +}); + // * NEWS CORPORATE Breadcrumbs::for('bo.news_corporates.index', function (Generator $trail) { $trail->push( diff --git a/routes/web-bo.php b/routes/web-bo.php index 6941373d..1b1db4e4 100644 --- a/routes/web-bo.php +++ b/routes/web-bo.php @@ -14,6 +14,7 @@ use App\Http\Controllers\Bo\MainSlideController; use App\Http\Controllers\Bo\MenuNodeDataController; use App\Http\Controllers\Bo\MeatController; use App\Http\Controllers\Bo\NewsCategoryController; +use App\Http\Controllers\Bo\NewsController; use App\Http\Controllers\Bo\NewsCorporateController; use App\Http\Controllers\Bo\OfferController; use App\Http\Controllers\Bo\ProductController; @@ -25,6 +26,7 @@ use App\Http\Controllers\Bo\StaticPageController; use App\Http\Controllers\Bo\SubmeatController; use App\Http\Controllers\Bo\SubrangeController; use App\Http\Controllers\Bo\TestimonialController; +use App\Http\Controllers\Bo\UploadController; use App\Http\Controllers\Bo\UserController; use App\Http\Controllers\Bo\VideoController; use App\Http\Controllers\Controller; @@ -431,6 +433,17 @@ Route::middleware(['throttle:web', 'admin-secure'])->prefix('bo')->name('bo.')-> 'duplicate' ])->name('news_corporates.duplicate'); + // * NEWS. + Route::resource('news', NewsController::class) + ->withoutMiddleware('htmlsanitize') + ->middleware('htmlsanitize:content'); + Route::patch('/news/{news}/change-published', [ + NewsController::class, 'changePublished' + ])->name('news.change-published'); + Route::get('/news/{news}/duplicate', [ + NewsController::class, 'duplicate' + ])->name('news.duplicate'); + // * NEWS CATEGORIES. Route::resource('news_categories', NewsCategoryController::class); Route::patch('/news_categories/{news_category}/change-published', [ @@ -452,4 +465,7 @@ Route::middleware(['throttle:web', 'admin-secure'])->prefix('bo')->name('bo.')-> }); // * Others routes Route::post('theme/set', [Controller::class, 'setTheme'])->name('theme.set'); + + // * UPLOAD (USED FOR CKEDITOR) + Route::post('/upload', [UploadController::class, 'uploadFile'])->name('upload'); });